/[svn]/jsampler/trunk/src/org/jsampler/view/std/JSFxSendsPane.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/view/std/JSFxSendsPane.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1866 - (show annotations) (download)
Sun Mar 15 19:40:29 2009 UTC (15 years, 1 month ago) by iliev
File size: 19615 byte(s)
* show controller names in fx sends dialogs
* save send levels when exporting sampler configuration
* ask when removing nonempty map
* fixed compilation error

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2009 Grigor Iliev <grigor@grigoriliev.com>
5 *
6 * This file is part of JSampler.
7 *
8 * JSampler is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * JSampler is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with JSampler; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23 package org.jsampler.view.std;
24
25 import java.awt.BorderLayout;
26 import java.awt.Dimension;
27
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30
31 import java.util.Vector;
32
33 import javax.swing.AbstractAction;
34 import javax.swing.Action;
35 import javax.swing.BorderFactory;
36 import javax.swing.Box;
37 import javax.swing.BoxLayout;
38 import javax.swing.DefaultCellEditor;
39 import javax.swing.JButton;
40 import javax.swing.JComboBox;
41 import javax.swing.JLabel;
42 import javax.swing.JPanel;
43 import javax.swing.JScrollPane;
44 import javax.swing.JSlider;
45 import javax.swing.JSplitPane;
46 import javax.swing.JTable;
47 import javax.swing.JToolBar;
48
49 import javax.swing.event.ChangeEvent;
50 import javax.swing.event.ChangeListener;
51 import javax.swing.event.ListSelectionEvent;
52 import javax.swing.event.ListSelectionListener;
53
54 import javax.swing.table.AbstractTableModel;
55 import javax.swing.table.TableColumn;
56 import javax.swing.table.JTableHeader;
57
58 import net.sf.juife.event.TaskEvent;
59 import net.sf.juife.event.TaskListener;
60
61 import org.jsampler.AudioDeviceModel;
62 import org.jsampler.CC;
63 import org.jsampler.JSPrefs;
64 import org.jsampler.SamplerChannelModel;
65
66 import org.jsampler.event.EffectSendsAdapter;
67 import org.jsampler.event.EffectSendsEvent;
68 import org.jsampler.event.EffectSendsListener;
69
70
71 import org.jsampler.task.Channel;
72
73 import org.jsampler.view.FxSendTable;
74
75 import org.linuxsampler.lscp.FxSend;
76
77 import static org.jsampler.view.std.StdI18n.i18n;
78
79
80 /**
81 *
82 * @author Grigor Iliev
83 */
84 public class JSFxSendsPane extends JPanel implements ListSelectionListener {
85 private SamplerChannelModel channelModel;
86 private final FxSendTable fxSendsTable;
87
88 protected final Action actionAddFxSend = new AddFxSendAction();
89 protected final Action actionRemoveFxSend = new RemoveFxSendAction();
90
91 private final JComboBox cbMidiCtrls = new JComboBox();
92 private final JSlider slVolume = StdUtils.createVolumeSlider();
93
94 private final JLabel lMidiCtrl = new JLabel(i18n.getLabel("JSFxSendsPane.lMidiCtrl"));
95 private final JLabel lVolume = new JLabel(i18n.getLabel("JSFxSendsPane.lVolume"));
96
97 private final ChannelRoutingTable channelRoutingTable;
98
99 private FxSend fxSend = null;
100
101 private final int[] undefinedControllers = {
102 3, 9, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 85, 86, 87, 88, 89, 90,
103 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119
104 };
105
106 private int lastUsedController = 0;
107
108 /**
109 * Creates a new instance of <code>JSFxSendsPane</code>.
110 *
111 * @throws IllegalArgumentException If <code>model</code> is <code>null</code>.
112 */
113 public
114 JSFxSendsPane(SamplerChannelModel model) {
115 if(model == null)
116 throw new IllegalArgumentException("model should be non-null!");
117
118 channelModel = model;
119 fxSendsTable = new FxSendTable(channelModel);
120 channelRoutingTable = new ChannelRoutingTable();
121
122 setLayout(new BorderLayout());
123
124
125
126 JPanel rightPane = createRightPane();
127
128
129 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
130 splitPane.setLeftComponent(createLeftPane());
131 splitPane.setRightComponent(rightPane);
132 splitPane.setContinuousLayout(true);
133 add(splitPane);
134
135 channelModel.addEffectSendsListener(getHandler());
136
137 fxSendsTable.getSelectionModel().addListSelectionListener(this);
138
139 if(channelModel.getChannelInfo().getEngine() == null) {
140 actionAddFxSend.setEnabled(false);
141 }
142 if(channelModel.getFxSendCount() == 0) {
143 actionRemoveFxSend.setEnabled(false);
144 } else {
145 fxSendsTable.setSelectedFxSend(channelModel.getFxSend(0));
146 }
147 updateFxSend();
148
149 Dimension d = getMinimumSize();
150 int w = d.width > 600 ? d.width : 600;
151 int h = d.height > 300 ? d.height : 300;
152 setPreferredSize(new Dimension(w, h));
153 splitPane.setDividerLocation(200);
154 }
155
156 protected JSPrefs
157 preferences() { return CC.getViewConfig().preferences(); }
158
159 public SamplerChannelModel
160 getChannelModel() { return channelModel; }
161
162 protected JToolBar
163 createToolBar() {
164 JToolBar tb = new JToolBar();
165 tb.setMaximumSize(new Dimension(Short.MAX_VALUE, tb.getPreferredSize().height));
166 tb.setFloatable(false);
167 tb.setAlignmentX(JPanel.RIGHT_ALIGNMENT);
168
169 tb.add(new JButton(actionAddFxSend));
170 tb.add(new JButton(actionRemoveFxSend));
171
172 return tb;
173 }
174
175 protected JPanel
176 createLeftPane() {
177 JPanel leftPane = new JPanel();
178 leftPane.setLayout(new BorderLayout());
179
180 leftPane.add(createToolBar(), BorderLayout.NORTH);
181 leftPane.add(new JScrollPane(fxSendsTable));
182
183 return leftPane;
184 }
185
186 protected JPanel
187 createRightPane() {
188 for(int i = 0; i < 128; i++) {
189 String s = "[" + i + "] " + getMidiControllerName(i);
190 cbMidiCtrls.addItem(s);
191 }
192
193 cbMidiCtrls.setMinimumSize(new Dimension(100, cbMidiCtrls.getMinimumSize().height));
194
195 cbMidiCtrls.addActionListener(new ActionListener() {
196 public void
197 actionPerformed(ActionEvent e) {
198 if(fxSend == null) return;
199
200 int fxs = fxSend.getFxSendId();
201 int ctrl = cbMidiCtrls.getSelectedIndex();
202
203 if(ctrl == fxSend.getMidiController()) {
204 return;
205 }
206
207 lastUsedController = ctrl;
208 channelModel.setBackendFxSendMidiController(fxs, ctrl);
209 }
210 });
211
212 slVolume.addChangeListener(new ChangeListener() {
213 public void
214 stateChanged(ChangeEvent e) { setVolume(); }
215 });
216
217 JPanel rightPane = new JPanel();
218 rightPane.setLayout(new BorderLayout());
219
220 JPanel p = new JPanel();
221 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
222
223 JPanel p2 = new JPanel();
224 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
225
226 p2.add(lVolume);
227 p2.add(Box.createRigidArea(new Dimension(6, 0)));
228 slVolume.setMinimumSize(slVolume.getPreferredSize());
229 p2.add(slVolume);
230 p2.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
231 p.add(p2);
232
233 p2 = new JPanel();
234
235 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
236 p2.add(lMidiCtrl);
237 p2.add(Box.createRigidArea(new Dimension(6, 0)));
238 p2.add(cbMidiCtrls);
239 p2.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
240 //p2.setAlignmentX(LEFT_ALIGNMENT);
241 p.add(p2);
242
243 rightPane.add(p, BorderLayout.NORTH);
244 rightPane.add(new JScrollPane(channelRoutingTable));
245
246 return rightPane;
247 }
248
249 private String
250 getMidiControllerName(int i) {
251 switch(i) {
252 case 0: return "Bank Select";
253 case 1: return "Modulation Wheel or Lever";
254 case 2: return "Breath Controller";
255 case 4: return "Foot Controller";
256 case 5: return "Portamento Time";
257 case 6: return "Data Entry MSB";
258 case 7: return "Channel Volume";
259 case 8: return "Balance";
260 case 10: return "Pan";
261 case 11: return "Expression Controller";
262 case 12: return "Effect Control 1";
263 case 13: return "Effect Control 2";
264 case 16: return "General Purpose Controller 1";
265 case 17: return "General Purpose Controller 2";
266 case 18: return "General Purpose Controller 3";
267 case 19: return "General Purpose Controller 4";
268 case 32: return "LSB for Control 0 (Bank Select)";
269 case 33: return "LSB for Control 1 (Modulation Wheel or Lever)";
270 case 34: return "LSB for Control 2 (Breath Controller)";
271 case 35: return "LSB for Control 3 (Undefined)";
272 case 36: return "LSB for Control 4 (Foot Controller)";
273 case 37: return "LSB for Control 5 (Portamento Time)";
274 case 38: return "LSB for Control 6 (Data Entry)";
275 case 39: return "LSB for Control 7 (Channel Volume)";
276 case 40: return "LSB for Control 8 (Balance)";
277 case 41: return "LSB for Control 9 (Undefined)";
278 case 42: return "LSB for Control 10 (Pan)";
279 case 43: return "LSB for Control 11 (Expression Controller)";
280 case 44: return "LSB for Control 12 (Effect control 1)";
281 case 45: return "LSB for Control 13 (Effect control 2)";
282 case 46: return "LSB for Control 14 (Undefined)";
283 case 47: return "LSB for Control 15 (Undefined)";
284 case 48: return "LSB for Control 16 (General Purpose Controller 1)";
285 case 49: return "LSB for Control 17 (General Purpose Controller 2)";
286 case 50: return "LSB for Control 18 (General Purpose Controller 3)";
287 case 51: return "LSB for Control 19 (General Purpose Controller 4)";
288 case 52: return "LSB for Control 20 (Undefined)";
289 case 53: return "LSB for Control 21 (Undefined)";
290 case 54: return "LSB for Control 22 (Undefined)";
291 case 55: return "LSB for Control 23 (Undefined)";
292 case 56: return "LSB for Control 24 (Undefined)";
293 case 57: return "LSB for Control 25 (Undefined)";
294 case 58: return "LSB for Control 26 (Undefined)";
295 case 59: return "LSB for Control 27 (Undefined)";
296 case 60: return "LSB for Control 28 (Undefined)";
297 case 61: return "LSB for Control 29 (Undefined)";
298 case 62: return "LSB for Control 30 (Undefined)";
299 case 63: return "LSB for Control 31 (Undefined)";
300 case 64: return "Damper Pedal on/off (Sustain)";
301 case 65: return "Portamento On/Off";
302 case 66: return "Sostenuto On/Off";
303 case 67: return "Soft Pedal On/Off";
304 case 68: return "Legato Footswitch";
305 case 69: return "Hold 2";
306 case 70: return "Sound Controller 1 (default: Sound Variation)";
307 case 71: return "Sound Controller 2 (default: Timbre/Harmonic Intens.)";
308 case 72: return "Sound Controller 3 (default: Release Time)";
309 case 73: return "Sound Controller 4 (default: Attack Time)";
310 case 74: return "Sound Controller 5 (default: Brightness)";
311 case 75: return "Sound Controller 6 (default: Decay Time";
312 case 76: return "Sound Controller 7 (default: Vibrato Rate)";
313 case 77: return "Sound Controller 8 (default: Vibrato Depth)";
314 case 78: return "Sound Controller 9 (default: Vibrato Delay)";
315 case 79: return "Sound Controller 10 (default: undefined)";
316 case 80: return "General Purpose Controller 5";
317 case 81: return "General Purpose Controller 6";
318 case 82: return "General Purpose Controller 7";
319 case 83: return "General Purpose Controller 8";
320 case 84: return "Portamento Control";
321 case 91: return "Effects 1 Depth (default: Reverb Send Level)";
322 case 92: return "Effects 2 Depth (formerly Tremolo Depth)";
323 case 93: return "Effects 3 Depth(default: Chorus Send Level)";
324 case 94: return "Effects 4 Depth (formerly Celeste [Detune] Depth)";
325 case 95: return "Effects 5 Depth (formerly Phaser Depth)";
326 case 96: return "Data Increment (Data Entry +1)";
327 case 97: return "Data Decrement (Data Entry -1)";
328 case 98: return "Non-Registered Parameter Number (NRPN) - LSB";
329 case 99: return "Non-Registered Parameter Number (NRPN) - MSB";
330 case 100: return "Registered Parameter Number (RPN) - LSB";
331 case 101: return "Registered Parameter Number (RPN) - MSB";
332 case 120: return "All Sound Off (Channel Mode Message)";
333 case 121: return "Reset All Controllers (Channel Mode Message)";
334 case 122: return "Local Control On/Off (Channel Mode Message)";
335 case 123: return "All Notes Off (Channel Mode Message)";
336 case 124: return "Omni Mode Off (Channel Mode Message)";
337 case 125: return "Omni Mode On (Channel Mode Message)";
338 case 126: return "Mono Mode On (Channel Mode Message)";
339 case 127: return "Poly Mode On (Channel Mode Message)";
340 default: return "Undefined";
341 }
342 }
343
344 public boolean
345 isUndefinedController(int ctrl) {
346 for(int i = 0; i < undefinedControllers.length; i++) {
347 if(ctrl == undefinedControllers[i]) return true;
348 }
349
350 return false;
351 }
352
353 private int
354 findUndefinedController(int ctrl, Vector<Integer> excludes) {
355 for(int i = 0; i < undefinedControllers.length; i++) {
356 int c = undefinedControllers[i];
357 if(c > ctrl) { if(!excludes.contains(c)) return c; }
358 }
359
360 for(int i = 0; i < undefinedControllers.length; i++) {
361 int c = undefinedControllers[i];
362 if(c < ctrl) { if(!excludes.contains(c)) return c; }
363 else break;
364 }
365
366 return -1;
367 }
368
369 public int
370 getUndefinedController() {
371 int c = lastUsedController;
372 if(c > 119) c = 0;
373 FxSend[] fxSends = getChannelModel().getFxSends();
374 Vector<Integer> v = new Vector<Integer>();
375
376 for(int i = fxSends.length - 1; i >= 0; i--) {
377 int c2 = fxSends[i].getMidiController();
378 if(isUndefinedController(c2)) v.add(c2);
379 }
380
381 int ctrl = findUndefinedController(c, v);
382
383 return ctrl != -1 ? ctrl : 3;
384 }
385
386 private void
387 setVolume() {
388 if(fxSend == null || slVolume.getValueIsAdjusting()) return;
389
390 int i = (int)(fxSend.getLevel() * 100);
391 if(slVolume.getValue() == i) return;
392
393 float vol = slVolume.getValue();
394 vol /= 100;
395 channelModel.setBackendFxSendLevel(fxSend.getFxSendId(), vol);
396 }
397
398 @Override
399 public void
400 valueChanged(ListSelectionEvent e) {
401 if(e.getValueIsAdjusting()) return;
402
403 fxSend = fxSendsTable.getSelectedFxSend();
404 actionRemoveFxSend.setEnabled(fxSend != null);
405 updateFxSend();
406 }
407
408 private void
409 updateFxSend() {
410 boolean b = (fxSend != null);
411 cbMidiCtrls.setEnabled(b);
412 slVolume.setEnabled(b);
413 channelRoutingTable.setEnabled(b);
414 if(!b) {
415 slVolume.setValue(0);
416 cbMidiCtrls.setSelectedIndex(0);
417 return;
418 }
419
420 cbMidiCtrls.setSelectedIndex(fxSend.getMidiController());
421 slVolume.setValue((int)(fxSend.getLevel() * 100));
422 }
423
424 class AddFxSendAction extends AbstractAction {
425 private int fxSendId = -1;
426
427 AddFxSendAction() {
428 super(i18n.getLabel("JSFxSendsPane.AddFxSendAction"));
429
430 String s = i18n.getLabel("JSFxSendsPane.AddFxSendAction.tt");
431 putValue(SHORT_DESCRIPTION, s);
432 }
433
434 @Override
435 public void
436 actionPerformed(ActionEvent e) {
437 int id = channelModel.getChannelId();
438 int c = getUndefinedController();
439 lastUsedController = c;
440 final Channel.AddFxSend t = new Channel.AddFxSend(id, c, "New effect send");
441
442 t.addTaskListener(new TaskListener() {
443 public void
444 taskPerformed(TaskEvent e) {
445 if(t.doneWithErrors()) {
446 fxSendId = -1;
447 return;
448 }
449 setFxSendId(t.getResult());
450 }
451 });
452 CC.getTaskQueue().add(t);
453 }
454
455 public int
456 getFxSendId() { return fxSendId; }
457
458 public void
459 setFxSendId(int id) { fxSendId = id; }
460 }
461
462 class RemoveFxSendAction extends AbstractAction {
463 RemoveFxSendAction() {
464 super(i18n.getLabel("JSFxSendsPane.RemoveFxSendAction"));
465
466 String s = i18n.getLabel("JSFxSendsPane.RemoveFxSendAction.tt");
467 putValue(SHORT_DESCRIPTION, s);
468 }
469
470 @Override
471 public void
472 actionPerformed(ActionEvent e) {
473 FxSend fxs = fxSendsTable.getSelectedFxSend();
474 if(fxs == null) return;
475 channelModel.removeBackendFxSend(fxs.getFxSendId());
476 }
477 }
478
479 class ChannelRoutingTable extends JTable {
480 private String[] columnToolTips = {
481 i18n.getLabel("JSFxSendsPane.ttAudioSrc"),
482 i18n.getLabel("JSFxSendsPane.ttAudioDst"),
483 };
484
485 ChannelRoutingTable() {
486 super(new ChannelRoutingTableModel());
487
488 JComboBox cb = new JComboBox();
489 int devId = channelModel.getChannelInfo().getAudioOutputDevice();
490 AudioDeviceModel adm = CC.getSamplerModel().getAudioDeviceById(devId);
491
492 int chns;
493 if(adm == null) {
494 chns = channelModel.getChannelInfo().getAudioOutputChannels();
495 } else {
496 chns = adm.getDeviceInfo().getAudioChannelCount();
497 }
498
499 for(Integer i = 0; i < chns; i++) cb.addItem(i);
500
501 TableColumn column = getColumnModel().getColumn(1);
502 column.setCellEditor(new DefaultCellEditor(cb));
503 }
504
505 @Override
506 protected JTableHeader
507 createDefaultTableHeader() {
508 return new JTableHeader(columnModel) {
509 public String getToolTipText(java.awt.event.MouseEvent e) {
510 java.awt.Point p = e.getPoint();
511 int i = columnModel.getColumnIndexAtX(p.x);
512 i = columnModel.getColumn(i).getModelIndex();
513 return columnToolTips[i];
514 }
515 };
516 }
517 }
518
519 class ChannelRoutingTableModel extends AbstractTableModel implements ListSelectionListener {
520 private String[] columnNames = {
521 i18n.getLabel("JSFxSendsPane.audioSrc"),
522 i18n.getLabel("JSFxSendsPane.audioDst")
523 };
524
525 ChannelRoutingTableModel() {
526 channelModel.addEffectSendsListener(new EffectSendsAdapter() {
527 /** Invoked when an effect send's setting are changed. */
528 public void
529 effectSendChanged(EffectSendsEvent e) {
530 if(fxSend == null) {
531 fireTableDataChanged();
532 return;
533 }
534
535 if(fxSend.equals(e.getFxSend())) {
536 int l;
537 l = e.getFxSend().getAudioOutputRouting().length;
538 fireTableRowsUpdated(0, l - 1);
539 }
540 }
541 });
542
543 fxSendsTable.getSelectionModel().addListSelectionListener(this);
544 }
545
546 @Override
547 public int
548 getColumnCount() { return columnNames.length; }
549
550 @Override
551 public String
552 getColumnName(int column) { return columnNames[column]; }
553
554 @Override
555 public int
556 getRowCount() {
557 if(fxSend == null) return 0;
558 return fxSend.getAudioOutputRouting().length;
559 }
560
561 @Override
562 public Object
563 getValueAt(int row, int column) {
564 switch(column) {
565 case 0:
566 return row;
567 case 1:
568 return fxSend.getAudioOutputRouting()[row];
569 default: return null;
570 }
571
572 }
573
574 @Override
575 public boolean
576 isCellEditable(int row, int column) {
577 switch(column) {
578 case 0:
579 return false;
580 case 1:
581 return true;
582 default: return false;
583 }
584 }
585
586 @Override
587 public void
588 setValueAt(Object value, int row, int column) {
589 if(column == 0) return;
590 int id = fxSend.getFxSendId();
591 int src = (Integer)getValueAt(row, 0);
592 int dst = (Integer)value;
593 channelModel.setBackendFxSendAudioOutputChannel(id, src, dst);
594 fxSend.getAudioOutputRouting()[row] = dst;
595
596 fireTableCellUpdated(row, column);
597 }
598
599 @Override
600 public void
601 valueChanged(ListSelectionEvent e) {
602 if(e.getValueIsAdjusting()) return;
603
604 fireTableDataChanged();
605 }
606 }
607
608 private final Handler eventHandler = new Handler();
609
610 private Handler
611 getHandler() { return eventHandler; }
612
613 private class Handler implements EffectSendsListener {
614 /** Invoked when a new effect send is added to a sampler channel. */
615 @Override
616 public void
617 effectSendAdded(EffectSendsEvent e) {
618 FxSend fxs = fxSendsTable.getSelectedFxSend();
619 if(fxs == null) return;
620 AddFxSendAction a = (AddFxSendAction)actionAddFxSend;
621 if(fxs.getFxSendId() != a.getFxSendId()) return;
622
623 fxSendsTable.requestFocus();
624 fxSendsTable.editSelectedFxSend();
625 a.setFxSendId(-1);
626 }
627
628 /** Invoked when an effect send is removed from a sampler channel. */
629 @Override
630 public void
631 effectSendRemoved(EffectSendsEvent e) {
632 if(channelModel.getFxSendCount() == 0) return;
633 int id = e.getFxSend().getFxSendId();
634 for(FxSend fxs : channelModel.getFxSends()) {
635 if(fxs.getFxSendId() > id) {
636 fxSendsTable.setSelectedFxSend(fxs);
637 return;
638 }
639 }
640 FxSend fxs = channelModel.getFxSend(channelModel.getFxSendCount() - 1);
641 fxSendsTable.setSelectedFxSend(fxs);
642 }
643
644 /** Invoked when an effect send's setting are changed. */
645 @Override
646 public void
647 effectSendChanged(EffectSendsEvent e) {
648 if(fxSend == null) return;
649 if(fxSend.equals(e.getFxSend())) {
650 fxSend = e.getFxSend();
651 updateFxSend();
652 }
653 }
654 }
655 }

  ViewVC Help
Powered by ViewVC