/[svn]/jsampler/trunk/src/org/jsampler/view/fantasia/Channel.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/view/fantasia/Channel.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1467 - (show annotations) (download)
Sat Nov 3 13:14:31 2007 UTC (16 years, 5 months ago) by iliev
File size: 46885 byte(s)
* bugfix: The parameter changes were
  discarded when creating new audio/MIDI device
* bugfix: The orchestras changes were not saved for the next session
  when orchestras.xml does not exist in the JSampler's home directory
* bugfix: In some cases the sampler configuration was not exported
  properly to LSCP script
* Some minor bugfixes and enhancements

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2007 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.fantasia;
24
25 import java.awt.Container;
26 import java.awt.Cursor;
27 import java.awt.Dimension;
28 import java.awt.Graphics;
29 import java.awt.Insets;
30 import java.awt.Rectangle;
31
32 import java.awt.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.event.HierarchyEvent;
35 import java.awt.event.HierarchyListener;
36 import java.awt.event.MouseAdapter;
37 import java.awt.event.MouseEvent;
38
39 import java.beans.PropertyChangeEvent;
40 import java.beans.PropertyChangeListener;
41
42 import java.util.logging.Level;
43
44 import javax.swing.Action;
45 import javax.swing.BorderFactory;
46 import javax.swing.Box;
47 import javax.swing.BoxLayout;
48 import javax.swing.DefaultListCellRenderer;
49 import javax.swing.JButton;
50 import javax.swing.JComboBox;
51 import javax.swing.JLabel;
52 import javax.swing.JMenuItem;
53 import javax.swing.JPanel;
54 import javax.swing.JPopupMenu;
55 import javax.swing.JScrollPane;
56 import javax.swing.JToggleButton;
57 import javax.swing.JToolBar;
58 import javax.swing.SwingConstants;
59 import javax.swing.Timer;
60
61 import javax.swing.event.ChangeEvent;
62 import javax.swing.event.ChangeListener;
63
64 import net.sf.juife.Dial;
65 import net.sf.juife.InformationDialog;
66 import net.sf.juife.JuifeUtils;
67 import net.sf.juife.TitleBar;
68
69 import org.jdesktop.swingx.JXCollapsiblePane;
70
71 import org.jsampler.AudioDeviceModel;
72 import org.jsampler.CC;
73 import org.jsampler.MidiDeviceModel;
74 import org.jsampler.MidiInstrumentMap;
75 import org.jsampler.SamplerChannelModel;
76 import org.jsampler.SamplerModel;
77
78 import org.jsampler.event.ListEvent;
79 import org.jsampler.event.ListListener;
80 import org.jsampler.event.MidiDeviceEvent;
81 import org.jsampler.event.MidiDeviceListEvent;
82 import org.jsampler.event.MidiDeviceListListener;
83 import org.jsampler.event.MidiDeviceListener;
84 import org.jsampler.event.SamplerAdapter;
85 import org.jsampler.event.SamplerChannelAdapter;
86 import org.jsampler.event.SamplerChannelEvent;
87 import org.jsampler.event.SamplerChannelListEvent;
88 import org.jsampler.event.SamplerChannelListListener;
89 import org.jsampler.event.SamplerChannelListener;
90 import org.jsampler.event.SamplerEvent;
91 import org.jsampler.event.SamplerListener;
92
93 import org.jsampler.view.std.JSChannelOutputRoutingDlg;
94 import org.jsampler.view.std.JSFxSendsPane;
95 import org.jsampler.view.std.JSInstrumentChooser;
96
97 import org.jvnet.lafwidget.animation.FadeConfigurationManager;
98 import org.jvnet.lafwidget.animation.FadeKind;
99
100 import org.jvnet.substance.SubstanceImageCreator;
101 import org.jvnet.substance.SubstanceLookAndFeel;
102
103 import org.linuxsampler.lscp.AudioOutputDevice;
104 import org.linuxsampler.lscp.MidiInputDevice;
105 import org.linuxsampler.lscp.MidiPort;
106 import org.linuxsampler.lscp.SamplerChannel;
107 import org.linuxsampler.lscp.SamplerEngine;
108
109 import static org.jsampler.view.fantasia.FantasiaI18n.i18n;
110 import static org.jsampler.view.fantasia.FantasiaPrefs.*;
111
112
113 /**
114 *
115 * @author Grigor Iliev
116 */
117 public class Channel extends org.jsampler.view.JSChannel {
118 private final JXCollapsiblePane mainPane;
119 private final ChannelScreen screen = new ChannelScreen(this);
120 private final ChannelOptions optionsPane = new ChannelOptions(this);
121
122 private final PowerButton btnPower = new PowerButton();
123 private final MuteButton btnMute = new MuteButton();
124 private final SoloButton btnSolo = new SoloButton();
125 private final OptionsButton btnOptions = new OptionsButton();
126
127 private final EnhancedDial dialVolume = new EnhancedDial();
128
129 private boolean selected = false;
130
131 /**
132 * Creates a new instance of <code>Channel</code> using the specified
133 * non-<code>null</code> channel model.
134 * @param model The model to be used by this channel.
135 * @throws IllegalArgumentException If the model is <code>null</code>.
136 */
137 public
138 Channel(SamplerChannelModel model) {
139 this(model, null);
140 }
141
142 /**
143 * Creates a new instance of <code>Channel</code> using the specified
144 * non-<code>null</code> channel model.
145 * @param model The model to be used by this channel.
146 * @param listener A listener which is notified when the newly created
147 * channel is fully expanded on the screen.
148 * @throws IllegalArgumentException If the model is <code>null</code>.
149 */
150 public
151 Channel(SamplerChannelModel model, final ActionListener listener) {
152 super(model);
153
154 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
155 ChannelPane p = new ChannelPane();
156 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
157
158 //p.add(Box.createRigidArea(new Dimension(3, 0)));
159
160 btnPower.setAlignmentY(JPanel.TOP_ALIGNMENT);
161
162 JPanel tb = new JPanel();
163 tb.setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 4));
164 tb.setLayout(new BoxLayout(tb, BoxLayout.X_AXIS));
165 tb.setOpaque(false);
166 tb.setAlignmentY(JPanel.TOP_ALIGNMENT);
167 tb.add(btnPower);
168 tb.setPreferredSize(new Dimension(tb.getPreferredSize().width, 58));
169 tb.setMinimumSize(tb.getPreferredSize());
170 tb.setMaximumSize(tb.getPreferredSize());
171 p.add(tb);
172
173 //p.add(Box.createRigidArea(new Dimension(4, 0)));
174
175 p.add(createVSeparator());
176
177 //p.add(Box.createRigidArea(new Dimension(3, 0)));
178
179 JPanel p2 = new JPanel();
180 p2.setOpaque(false);
181 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
182 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
183 p2.setBorder(BorderFactory.createEmptyBorder(5, 3, 0, 2));
184 p2.add(screen);
185 p.add(p2);
186
187 p.add(createVSeparator());
188
189 p2 = new JPanel();
190 p2.setOpaque(false);
191 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
192 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
193 p2.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
194 p2.add(new JLabel(Res.gfxMuteTitle));
195 p2.add(btnMute);
196 p2.add(new JLabel(Res.gfxSoloTitle));
197 p2.add(btnSolo);
198
199 p.add(p2);
200
201 p.add(createVSeparator());
202
203 p2 = new JPanel();
204 p2.setOpaque(false);
205 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
206 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
207 p2.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
208 JLabel l = new JLabel(Res.gfxVolumeTitle);
209 l.setAlignmentX(JPanel.CENTER_ALIGNMENT);
210 l.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
211 p2.add(l);
212 dialVolume.setDialPixmap(Res.gfxVolumeDial, 30, 330);
213 dialVolume.setAlignmentX(JPanel.CENTER_ALIGNMENT);
214 p2.add(dialVolume);
215 p.add(p2);
216
217 p.add(createVSeparator());
218
219 p2 = new JPanel();
220 p2.setOpaque(false);
221 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
222 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
223 p2.setBorder(BorderFactory.createEmptyBorder(27, 0, 0, 0));
224 l = new JLabel(Res.gfxOptionsTitle);
225 l.setAlignmentX(JPanel.CENTER_ALIGNMENT);
226 l.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
227 p2.add(l);
228
229 p2.add(Box.createRigidArea(new Dimension(0, 3)));
230
231 btnOptions.setAlignmentX(JPanel.CENTER_ALIGNMENT);
232 p2.add(btnOptions);
233 p.add(p2);
234
235
236 p.setPreferredSize(new Dimension(420, 60));
237 p.setMinimumSize(p.getPreferredSize());
238 p.setMaximumSize(p.getPreferredSize());
239 //p.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));
240
241 p.setAlignmentX(JPanel.CENTER_ALIGNMENT);
242 optionsPane.setAlignmentX(JPanel.CENTER_ALIGNMENT);
243
244 mainPane = new JXCollapsiblePane();
245 mainPane.getContentPane().setLayout (
246 new BoxLayout(mainPane.getContentPane(), BoxLayout.Y_AXIS)
247 );
248
249 mainPane.add(p);
250 mainPane.add(optionsPane);
251
252 setOpaque(false);
253
254 int i = preferences().getIntProperty(MAXIMUM_CHANNEL_VOLUME);
255 dialVolume.setMaximum(i);
256 String mcv = MAXIMUM_CHANNEL_VOLUME;
257 preferences().addPropertyChangeListener(mcv, new PropertyChangeListener() {
258 public void
259 propertyChange(PropertyChangeEvent e) {
260 int j = preferences().getIntProperty(MAXIMUM_CHANNEL_VOLUME);
261 dialVolume.setMaximum(j);
262 }
263 });
264
265 getModel().addSamplerChannelListener(getHandler());
266
267 updateChannelInfo();
268
269 add(mainPane);
270
271 if(listener != null) {
272 final String s = JXCollapsiblePane.ANIMATION_STATE_KEY;
273 mainPane.addPropertyChangeListener(s, new PropertyChangeListener() {
274 public void
275 propertyChange(PropertyChangeEvent e) {
276 if(e.getNewValue() == "expanded") {
277 // TODO: this should be done regardles the listener != null?
278 mainPane.removePropertyChangeListener(s, this);
279 ///////
280 listener.actionPerformed(null);
281 ensureChannelIsVisible();
282 } else if(e.getNewValue() == "expanding/collapsing") {
283 ensureChannelIsVisible();
284 }
285 }
286 });
287 }
288
289 mainPane.setAnimated(false);
290 mainPane.setCollapsed(true);
291 mainPane.setAnimated(preferences().getBoolProperty(ANIMATED));
292 mainPane.setCollapsed(false);
293
294 preferences().addPropertyChangeListener(ANIMATED, new PropertyChangeListener() {
295 public void
296 propertyChange(PropertyChangeEvent e) {
297 mainPane.setAnimated(preferences().getBoolProperty(ANIMATED));
298 }
299 });
300
301 if(listener != null) {
302 javax.swing.SwingUtilities.invokeLater(new Runnable() {
303 public void
304 run() { listener.actionPerformed(null); }
305 });
306 }
307
308 CC.getSamplerModel().addSamplerChannelListListener(getHandler());
309 }
310
311 private void
312 ensureChannelIsVisible() {
313 Container p = getParent();
314 JScrollPane sp = null;
315 while(p != null) {
316 if(p instanceof JScrollPane) {
317 sp = (JScrollPane)p;
318 break;
319 }
320 p = p.getParent();
321 }
322 if(sp == null) return;
323 int h = sp.getViewport().getView().getHeight();
324 sp.getViewport().scrollRectToVisible(new Rectangle(0, h - 2, 1, 1));
325 }
326
327 private JPanel
328 createVSeparator() {
329 PixmapPane p = new PixmapPane(Res.gfxVLine);
330 p.setAlignmentY(JPanel.TOP_ALIGNMENT);
331 p.setPreferredSize(new Dimension(2, 60));
332 p.setMinimumSize(p.getPreferredSize());
333 p.setMaximumSize(p.getPreferredSize());
334 return p;
335 }
336
337 /**
338 * Determines whether the channel is selected.
339 * @return <code>true</code> if the channel is selected, <code>false</code> otherwise.
340 */
341 public boolean isSelected() { return selected; }
342
343 /**
344 * Sets the selection state of this channel.
345 * This method is invoked when the selection state of the channel has changed.
346 * @param select Specifies the new selection state of this channel;
347 * <code>true</code> to select the channel, <code>false</code> otherwise.
348 */
349 public void
350 setSelected(boolean select) {
351
352 selected = select;
353 }
354
355 /** Shows the channel properties. */
356 public void
357 expandChannel() { expandChannel(optionsPane.isAnimated()); }
358
359 /** Shows the channel properties. */
360 public void
361 expandChannel(boolean animated) {
362 if(btnOptions.isSelected()) return;
363
364 boolean b = optionsPane.isAnimated();
365 optionsPane.setAnimated(animated);
366 btnOptions.doClick();
367 optionsPane.setAnimated(b);
368 }
369
370
371 /** Invoked when the user changes the volume */
372 private void
373 setVolume() {
374 screen.updateVolumeInfo(dialVolume.getValue());
375
376 if(dialVolume.getValueIsAdjusting()) return;
377
378 int vol = (int)(getChannelInfo().getVolume() * 100);
379
380 if(vol == dialVolume.getValue()) return;
381
382
383 /*
384 * If the model's volume is not equal to the dial knob
385 * value we assume that the change is due to user input.
386 * So we must update the volume at the backend too.
387 */
388 float volume = dialVolume.getValue();
389 volume /= 100;
390 getModel().setBackendVolume(volume);
391 }
392
393 /**
394 * Updates the channel settings. This method is invoked when changes to the
395 * channel were made.
396 */
397 private void
398 updateChannelInfo() {
399 SamplerChannel sc = getChannelInfo();
400
401 screen.updateScreenInfo(sc);
402 updateMuteIcon(sc);
403
404 if(sc.isSoloChannel()) btnSolo.setIcon(Res.gfxSoloOn);
405 else btnSolo.setIcon(Res.gfxSoloOff);
406
407 dialVolume.setValue((int)(sc.getVolume() * 100));
408
409 boolean b = sc.getEngine() != null;
410 dialVolume.setEnabled(b);
411 btnSolo.setEnabled(b);
412 btnMute.setEnabled(b);
413 }
414
415 /**
416 * Updates the mute button with the proper icon regarding to information obtained
417 * from <code>channel</code>.
418 * @param channel A <code>SamplerChannel</code> instance containing the new settings
419 * for this channel.
420 */
421 private void
422 updateMuteIcon(SamplerChannel channel) {
423 if(channel.isMutedBySolo()) btnMute.setIcon(Res.gfxMutedBySolo);
424 else if(channel.isMuted()) btnMute.setIcon(Res.gfxMuteOn);
425 else btnMute.setIcon(Res.gfxMuteOff);
426 }
427
428 private class EnhancedDial extends Dial {
429 EnhancedDial() {
430 super(0, 100);
431
432 setMouseHandlerMode(MouseHandlerMode.LEFT_TO_RIGHT_AND_DOWN_TO_UP);
433
434 addMouseListener(new MouseAdapter() {
435 public void
436 mouseClicked(MouseEvent e) {
437 if(e.getButton() == e.BUTTON3) {
438 setValue(getMaximum() / 2);
439 return;
440 }
441
442 if(e.getButton() != e.BUTTON1) return;
443
444 if(e.getClickCount() < 2) return;
445 setValue(getValueByPoint(e.getPoint()));
446 }
447 });
448
449 addChangeListener(new ChangeListener() {
450 public void
451 stateChanged(ChangeEvent e) { setVolume(); }
452 });
453 }
454 }
455
456 protected void
457 onDestroy() {
458 CC.getSamplerModel().removeSamplerChannelListListener(getHandler());
459
460 screen.onDestroy();
461 optionsPane.onDestroy();
462 }
463
464 private final EventHandler eventHandler = new EventHandler();
465
466 private EventHandler
467 getHandler() { return eventHandler; }
468
469 private class EventHandler implements SamplerChannelListener, SamplerChannelListListener {
470 /**
471 * Invoked when changes are made to a sampler channel.
472 * @param e A <code>SamplerChannelEvent</code> instance
473 * containing event information.
474 */
475 public void
476 channelChanged(SamplerChannelEvent e) { updateChannelInfo(); }
477
478 /**
479 * Invoked when the number of active disk streams has changed.
480 * @param e A <code>SamplerChannelEvent</code> instance
481 * containing event information.
482 */
483 public void
484 streamCountChanged(SamplerChannelEvent e) {
485 screen.updateStreamCount(getModel().getStreamCount());
486 }
487
488 /**
489 * Invoked when the number of active voices has changed.
490 * @param e A <code>SamplerChannelEvent</code> instance
491 * containing event information.
492 */
493 public void
494 voiceCountChanged(SamplerChannelEvent e) {
495 screen.updateVoiceCount(getModel().getVoiceCount());
496 }
497
498 /**
499 * Invoked when a new sampler channel is created.
500 * @param e A <code>SamplerChannelListEvent</code>
501 * instance providing the event information.
502 */
503 public void
504 channelAdded(SamplerChannelListEvent e) { }
505
506 /**
507 * Invoked when a sampler channel is removed.
508 * @param e A <code>SamplerChannelListEvent</code>
509 * instance providing the event information.
510 */
511 public void
512 channelRemoved(SamplerChannelListEvent e) {
513 // Some cleanup when the channel is removed.
514 if(e.getChannelModel().getChannelId() == getChannelId()) {
515 onDestroy();
516 }
517 }
518 }
519
520
521 private class PowerButton extends PixmapToggleButton
522 implements ActionListener, PropertyChangeListener {
523
524 PowerButton() {
525 super(Res.gfxPowerOff, Res.gfxPowerOn);
526
527 setSelected(true);
528 addActionListener(this);
529 }
530
531 public void
532 actionPerformed(ActionEvent e) {
533 if(!mainPane.isAnimated()) {
534 CC.getSamplerModel().removeBackendChannel(getChannelId());
535 return;
536 }
537
538 String s = JXCollapsiblePane.ANIMATION_STATE_KEY;
539 mainPane.addPropertyChangeListener(s, this);
540 mainPane.setCollapsed(true);
541 }
542
543 public void
544 propertyChange(PropertyChangeEvent e) {
545 if(e.getNewValue() == "collapsed") {
546 CC.getSamplerModel().removeBackendChannel(getChannelId());
547 }
548 }
549
550 public boolean
551 contains(int x, int y) { return (x - 11)*(x - 11) + (y - 11)*(y - 11) < 71; }
552 }
553
554 private class MuteButton extends PixmapButton implements ActionListener {
555 MuteButton() {
556 super(Res.gfxMuteOff);
557 //setDisabledIcon(Res.gfxMuteSoloDisabled);
558 setDisabledIcon (
559 SubstanceImageCreator.makeTransparent(this, Res.gfxMuteOff, 0.4)
560 );
561 addActionListener(this);
562 }
563
564 public void
565 actionPerformed(ActionEvent e) {
566 SamplerChannel sc = getChannelInfo();
567 boolean b = true;
568
569 /*
570 * Changing the mute button icon now instead of
571 * leaving the work to the notification mechanism of the LinuxSampler.
572 */
573 if(sc.isMuted() && !sc.isMutedBySolo()) {
574 b = false;
575 boolean hasSolo = CC.getSamplerModel().hasSoloChannel();
576
577 if(sc.isSoloChannel() || !hasSolo) setIcon(Res.gfxMuteOff);
578 else setIcon(Res.gfxMutedBySolo);
579 } else setIcon(Res.gfxMuteOn);
580
581 Channel.this.getModel().setBackendMute(b);
582 }
583
584 public boolean
585 contains(int x, int y) { return (x > 5 && x < 23) && (y > 5 && y < 16); }
586 }
587
588 private class SoloButton extends PixmapButton implements ActionListener {
589 SoloButton() {
590 super(Res.gfxSoloOff);
591 //setDisabledIcon(Res.gfxMuteSoloDisabled);
592 setDisabledIcon (
593 SubstanceImageCreator.makeTransparent(this, Res.gfxSoloOff, 0.4)
594 );
595 addActionListener(this);
596 }
597
598 public void
599 actionPerformed(ActionEvent e) {
600 SamplerChannel sc = getChannelInfo();
601 boolean b = !sc.isSoloChannel();
602
603 /*
604 * Changing the solo button icon (and related) now instead of
605 * leaving the work to the notification mechanism of the LinuxSampler.
606 */
607 if(b) {
608 setIcon(Res.gfxSoloOn);
609 if(sc.isMutedBySolo()) btnMute.setIcon(Res.gfxMuteOff);
610 } else {
611 setIcon(Res.gfxSoloOff);
612 if(!sc.isMuted() && CC.getSamplerModel().getSoloChannelCount() > 1)
613 btnMute.setIcon(Res.gfxMutedBySolo);
614 }
615
616 Channel.this.getModel().setBackendSolo(b);
617 }
618
619 public boolean
620 contains(int x, int y) { return (x > 5 && x < 23) && (y > 5 && y < 16); }
621 }
622
623 private class OptionsButton extends PixmapToggleButton implements ActionListener {
624 OptionsButton() {
625 super(Res.gfxOptionsOff, Res.gfxOptionsOn);
626 setRolloverIcon(Res.gfxOptionsOffRO);
627 this.setRolloverSelectedIcon(Res.gfxOptionsOnRO);
628 addActionListener(this);
629 }
630
631 public void
632 actionPerformed(ActionEvent e) {
633 showOptionsPane(isSelected());
634
635 String s;
636 if(isSelected()) s = i18n.getButtonLabel("OptionsButton.ttHideOptions");
637 else s = i18n.getButtonLabel("OptionsButton.ttShowOptions");
638
639 setToolTipText(s);
640 }
641
642 private void
643 showOptionsPane(boolean show) {
644 optionsPane.setCollapsed(!show);
645 }
646
647 public boolean
648 contains(int x, int y) { return super.contains(x, y) & y < 13; }
649 }
650 }
651
652 class ChannelPane extends PixmapPane {
653 ChannelPane() {
654 super(Res.gfxChannel);
655 setPixmapInsets(new Insets(3, 3, 3, 3));
656 }
657 }
658
659 class ChannelScreen extends PixmapPane {
660 private final Channel channel;
661
662 private final InstrumentPane instrumentPane;
663 private JButton btnInstr = new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnInstr"));
664
665 private final JButton btnEditInstr =
666 new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnEditInstr"));
667 private final ScreenButtonBg sbbEditInstr = new ScreenButtonBg(btnEditInstr);
668
669 private final JButton btnFxSends =
670 new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnFxSends"));
671
672 private final JButton btnEngine
673 = new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnEngine"));
674
675 private final JPopupMenu menuEngines = new JPopupMenu();
676
677 private final JLabel lVolume = new Label();
678 private final JLabel lStreams = new Label("--");
679 private final JLabel lVoices = new Label("--");
680
681 private InformationDialog fxSendsDlg = null;
682
683 private Dimension dimVolume;
684
685 private Timer timer;
686
687 class Label extends JLabel {
688 Label() { this(""); }
689
690 Label(String s) {
691 super(s);
692 setFont(Res.fontScreen);
693 setForeground(new java.awt.Color(0xFFA300));
694 }
695 }
696
697 ChannelScreen(final Channel channel) {
698 super(Res.gfxChannelScreen);
699 setPixmapInsets(new Insets(6, 6, 6, 6));
700 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
701
702 this.channel = channel;
703
704 setOpaque(false);
705
706 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
707
708 btnInstr.setAlignmentX(CENTER_ALIGNMENT);
709 btnInstr.setRolloverEnabled(false);
710 btnInstr.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
711
712 instrumentPane = new InstrumentPane();
713 add(instrumentPane);
714
715 JPanel p = new JPanel();
716 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
717 p.setAlignmentX(CENTER_ALIGNMENT);
718 p.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
719
720 btnFxSends.setToolTipText(i18n.getButtonLabel("ChannelScreen.btnFxSends.tt"));
721 btnFxSends.addActionListener(new ActionListener() {
722 public void
723 actionPerformed(ActionEvent e) {
724 if(fxSendsDlg != null && fxSendsDlg.isVisible()) {
725 fxSendsDlg.toFront();
726 return;
727 }
728 FxSendsPane p = new FxSendsPane(channel.getModel());
729 int id = channel.getModel().getChannelId();
730 fxSendsDlg = new InformationDialog(CC.getMainFrame(), p);
731 fxSendsDlg.setTitle(i18n.getLabel("FxSendsDlg.title", id));
732 fxSendsDlg.setModal(false);
733 fxSendsDlg.showCloseButton(false);
734 fxSendsDlg.setVisible(true);
735 }
736 });
737
738 p.add(btnFxSends);
739
740 p.add(Box.createRigidArea(new Dimension(6, 0)));
741
742 btnEngine.setIcon(Res.iconEngine12);
743 p.add(btnEngine);
744 //p.add(new Label("|"));
745
746 //p.add(Box.createRigidArea(new Dimension(6, 0)));
747
748 //p.add(btnReset);
749
750 p.add(Box.createGlue());
751
752 p.add(lStreams);
753 p.add(new Label("/"));
754 p.add(lVoices);
755
756 p.add(Box.createRigidArea(new Dimension(12, 0)));
757
758 lVolume.setIcon(Res.iconVolume14);
759 lVolume.setAlignmentX(RIGHT_ALIGNMENT);
760 updateVolumeInfo(100);
761 dimVolume = lVolume.getPreferredSize();
762 p.add(lVolume);
763 p.setPreferredSize(new Dimension(250, p.getPreferredSize().height));
764 p.setMinimumSize(p.getPreferredSize());
765 p.setMaximumSize(p.getPreferredSize());
766
767 //btnInstr.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
768 p.setOpaque(false);
769 add(p);
770
771
772 setPreferredSize(new Dimension(270, 48));
773 setMinimumSize(getPreferredSize());
774 setMaximumSize(getPreferredSize());
775
776 createEngineMenu();
777 installListeners();
778 }
779
780 protected void
781 onDestroy() { timer.stop(); }
782
783 private void
784 createEngineMenu() {
785 for(final SamplerEngine engine : CC.getSamplerModel().getEngines()) {
786 JMenuItem mi = new JMenuItem(engine.getDescription());
787
788 mi.addActionListener(new ActionListener() {
789 public void
790 actionPerformed(ActionEvent e) {
791 channel.getModel().setBackendEngineType(engine.getName());
792 }
793 });
794
795 menuEngines.add(mi);
796 }
797 }
798
799 private void
800 installListeners() {
801 btnInstr.addActionListener(new ActionListener() {
802 public void
803 actionPerformed(ActionEvent e) { loadInstrument(); }
804 });
805
806 btnEditInstr.addActionListener(new ActionListener() {
807 public void
808 actionPerformed(ActionEvent e) {
809 CC.getSamplerModel().editBackendInstrument(channel.getChannelId());
810 }
811 });
812
813 btnEngine.addActionListener(new ActionListener() {
814 public void
815 actionPerformed(ActionEvent e) {
816 int y = btnEngine.getHeight() + 1;
817 menuEngines.show(btnEngine, 0, y);
818 }
819 });
820
821 addMouseListener(getHandler());
822 addHierarchyListener(getHandler());
823
824 ActionListener l = new ActionListener() {
825 public void
826 actionPerformed(ActionEvent e) {
827 if(getMousePosition(true) != null) {
828 getHandler().mouseEntered(null);
829 } else {
830 getHandler().mouseExited(null);
831 }
832 }
833 };
834 timer = new Timer(1000, l);
835 timer.start();
836 }
837
838 private void
839 loadInstrument() {
840 JSInstrumentChooser dlg = FantasiaUtils.createInstrumentChooser(CC.getMainFrame());
841 dlg.setVisible(true);
842
843 if(!dlg.isCancelled()) {
844 SamplerChannelModel m = channel.getModel();
845 m.loadBackendInstrument(dlg.getInstrumentFile(), dlg.getInstrumentIndex());
846 }
847 }
848
849 protected void
850 updateScreenInfo(SamplerChannel sc) {
851 int status = sc.getInstrumentStatus();
852 if(status >= 0 && status < 100) {
853 btnInstr.setText(i18n.getLabel("ChannelScreen.loadingInstrument", status));
854 } else if(status == -1) {
855 btnInstr.setText(i18n.getButtonLabel("ChannelScreen.btnInstr"));
856 } else if(status < -1) {
857 btnInstr.setText(i18n.getLabel("ChannelScreen.errorLoadingInstrument"));
858 } else {
859 if(sc.getInstrumentName() != null) btnInstr.setText(sc.getInstrumentName());
860 else btnInstr.setText(i18n.getButtonLabel("ChannelScreen.btnInstr"));
861 }
862
863 instrumentPane.update();
864
865 if(sc.getEngine() != null) {
866 String s = sc.getEngine().getDescription();
867 if(!s.equals(btnEngine.getText())) btnEngine.setText(s);
868 }
869
870 }
871
872 protected void
873 updateVolumeInfo(int volume) {
874 lVolume.setText(i18n.getLabel("ChannelScreen.volume", volume));
875 lVolume.setMinimumSize(dimVolume);
876 lVolume.setPreferredSize(dimVolume);
877
878 }
879
880 /**
881 * Updates the number of active disk streams.
882 * @param count The new number of active disk streams.
883 */
884 protected void
885 updateStreamCount(int count) {
886 Dimension d = lStreams.getPreferredSize();
887 lStreams.setText(count == 0 ? "--" : String.valueOf(count));
888 d = JuifeUtils.getUnionSize(d, lStreams.getPreferredSize());
889 lStreams.setMinimumSize(d);
890 lStreams.setPreferredSize(d);
891 lStreams.setMaximumSize(d);
892 }
893
894 /**
895 * Updates the number of active voices.
896 * @param count The new number of active voices.
897 */
898 protected void
899 updateVoiceCount(int count) {
900 Dimension d = lVoices.getPreferredSize();
901 lVoices.setText(count == 0 ? "--" : String.valueOf(count));
902 d = JuifeUtils.getUnionSize(d, lVoices.getPreferredSize());
903 lVoices.setMinimumSize(d);
904 lVoices.setPreferredSize(d);
905 lVoices.setMaximumSize(d);
906 }
907
908 class InstrumentPane extends JPanel {
909 private final JPanel leftPane = new JPanel();
910 private final JPanel rightPane = new JPanel();
911
912 InstrumentPane() {
913 setOpaque(false);
914 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
915 add(leftPane);
916 add(btnInstr);
917 add(rightPane);
918 add(sbbEditInstr);
919 btnEditInstr.setToolTipText(i18n.getLabel("ChannelScreen.btnEditInstr.tt"));
920 sbbEditInstr.setVisible(false);
921 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 6));
922
923 update();
924 }
925
926 public void
927 update() {
928 int a = btnInstr.getMinimumSize().width;
929 int b = 0;
930 if(sbbEditInstr.isVisible()) b = sbbEditInstr.getPreferredSize().width;
931
932 int max = 254 - b;
933 if(a > max) a = max;
934
935 int h = btnInstr.getPreferredSize().height;
936 btnInstr.setPreferredSize(new Dimension(a, h));
937 h = btnInstr.getMaximumSize().height;
938 btnInstr.setMaximumSize(new Dimension(a, h));
939
940
941 int i = (254 - btnInstr.getPreferredSize().width) / 2;
942
943 int j = i;
944 if(sbbEditInstr.isVisible()) j -= sbbEditInstr.getPreferredSize().width;
945 if(i < 0 || j < 0) i = j = 0;
946
947 Dimension d = new Dimension(i, 1);
948 leftPane.setMinimumSize(d);
949 leftPane.setPreferredSize(d);
950 leftPane.setMaximumSize(d);
951
952 d = new Dimension(j, 1);
953 rightPane.setMinimumSize(d);
954 rightPane.setPreferredSize(d);
955 rightPane.setMaximumSize(d);
956
957 validate();
958 }
959 }
960
961 class FxSendsPane extends JSFxSendsPane {
962 FxSendsPane(SamplerChannelModel model) {
963 super(model);
964
965 actionAddFxSend.putValue(Action.SMALL_ICON, Res.iconNew16);
966 actionRemoveFxSend.putValue(Action.SMALL_ICON, Res.iconDelete16);
967 }
968
969 protected JToolBar
970 createToolBar() {
971 JToolBar tb = new JToolBar();
972 Dimension d = new Dimension(Short.MAX_VALUE, tb.getPreferredSize().height);
973 tb.setMaximumSize(d);
974 tb.setFloatable(false);
975 tb.setAlignmentX(JPanel.RIGHT_ALIGNMENT);
976
977 tb.add(new ToolbarButton(actionAddFxSend));
978 tb.add(new ToolbarButton(actionRemoveFxSend));
979
980 return tb;
981 }
982 }
983
984 static class ScreenButton extends JButton {
985 ScreenButton(String s) {
986 super(s);
987 setContentAreaFilled(false);
988 setFocusPainted(false);
989 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
990 setMargin(new Insets(0, 0, 0, 0));
991
992 putClientProperty (
993 SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY, Boolean.TRUE
994 );
995
996 putClientProperty (
997 SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, Boolean.TRUE
998 );
999
1000 putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
1001
1002 FadeConfigurationManager.getInstance().disallowFades(FadeKind.ROLLOVER, this);
1003
1004 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
1005 setFont(Res.fontScreen);
1006 setForeground(new java.awt.Color(0xFFA300));
1007 }
1008 }
1009
1010 static class ScreenButtonBg extends PixmapPane {
1011 ScreenButtonBg(JButton btn) {
1012 super(Res.gfxScreenBtnBg);
1013 setPixmapInsets(new Insets(4, 4, 4, 4));
1014 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
1015 setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 7));
1016 add(btn);
1017 setPreferredSize(new Dimension(getPreferredSize().width, 13));
1018 }
1019
1020 public Dimension
1021 getPreferredSize() {
1022 return new Dimension(super.getPreferredSize().width, 13);
1023 }
1024 }
1025
1026 private final EventHandler eventHandler = new EventHandler();
1027
1028 private EventHandler
1029 getHandler() { return eventHandler; }
1030
1031 private class EventHandler extends MouseAdapter implements HierarchyListener {
1032 public void
1033 mouseEntered(MouseEvent e) {
1034 if(channel.getChannelInfo().getInstrumentStatus() != 100) return;
1035
1036 if(!sbbEditInstr.isVisible()) {
1037 sbbEditInstr.setVisible(true);
1038 instrumentPane.update();
1039 }
1040 }
1041
1042 public void
1043 mouseExited(MouseEvent e) {
1044 if(getMousePosition(true) != null) return;
1045 if(sbbEditInstr.isVisible()) {
1046 sbbEditInstr.setVisible(false);
1047 instrumentPane.update();
1048 }
1049 }
1050
1051 /** Called when the hierarchy has been changed. */
1052 public void
1053 hierarchyChanged(HierarchyEvent e) {
1054 if((e.getChangeFlags() & e.SHOWING_CHANGED) == e.SHOWING_CHANGED) {
1055 if(getMousePosition() == null) mouseExited(null);
1056 else mouseEntered(null);
1057 }
1058 }
1059 }
1060 }
1061
1062 class ChannelOptions extends JXCollapsiblePane {
1063 private final Channel channel;
1064 private MidiDeviceModel midiDevice = null;
1065
1066 private final JComboBox cbMidiDevice = new FantasiaComboBox();
1067 private final JComboBox cbMidiPort = new FantasiaComboBox();
1068 private final JComboBox cbMidiChannel = new FantasiaComboBox();
1069 private final JComboBox cbInstrumentMap = new FantasiaComboBox();
1070 private final JComboBox cbAudioDevice = new FantasiaComboBox();
1071
1072 private final PixmapButton btnChannelRouting;
1073
1074 private boolean update = false;
1075
1076 private final SamplerListener samplerListener;
1077 private final MapListListener mapListListener = new MapListListener();
1078
1079 private class NoMap {
1080 public String
1081 toString() { return "[None]"; }
1082 }
1083
1084 private NoMap noMap = new NoMap();
1085
1086 private class DefaultMap {
1087 public String
1088 toString() { return "[Default]"; }
1089 }
1090
1091 private DefaultMap defaultMap = new DefaultMap();
1092
1093 ChannelOptions(final Channel channel) {
1094 setAnimated(false);
1095 setCollapsed(true);
1096 setAnimated(preferences().getBoolProperty(ANIMATED));
1097
1098 preferences().addPropertyChangeListener(ANIMATED, new PropertyChangeListener() {
1099 public void
1100 propertyChange(PropertyChangeEvent e) {
1101 setAnimated(preferences().getBoolProperty(ANIMATED));
1102 }
1103 });
1104
1105 PixmapPane bgp = new PixmapPane(Res.gfxChannelOptions);
1106 bgp.setPixmapInsets(new Insets(1, 1, 1, 1));
1107
1108 this.channel = channel;
1109
1110 bgp.setBorder(BorderFactory.createEmptyBorder(5, 4, 5, 4));
1111 bgp.setLayout(new BoxLayout(bgp, BoxLayout.X_AXIS));
1112
1113 bgp.setPreferredSize(new Dimension(420, 44));
1114 bgp.setMinimumSize(getPreferredSize());
1115 bgp.setMaximumSize(getPreferredSize());
1116
1117 JPanel p = new JPanel();
1118 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1119 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1120 JLabel l = new JLabel(Res.gfxMidiInputTitle);
1121 l.setAlignmentX(LEFT_ALIGNMENT);
1122 p.add(l);
1123
1124 JPanel p2 = new JPanel();
1125 p2.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
1126 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
1127
1128 Object o = cbMidiDevice.getRenderer();
1129 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1130
1131 cbMidiDevice.setPreferredSize(new Dimension(40, 18));
1132 cbMidiDevice.setMinimumSize(cbMidiDevice.getPreferredSize());
1133 cbMidiDevice.setMaximumSize(cbMidiDevice.getPreferredSize());
1134 p2.add(cbMidiDevice);
1135
1136 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1137
1138 o = cbMidiPort.getRenderer();
1139 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1140
1141 cbMidiPort.setPreferredSize(new Dimension(62, 18));
1142 cbMidiPort.setMinimumSize(cbMidiPort.getPreferredSize());
1143 cbMidiPort.setMaximumSize(cbMidiPort.getPreferredSize());
1144 p2.add(cbMidiPort);
1145
1146 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1147
1148 o = cbMidiChannel.getRenderer();
1149 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1150
1151 cbMidiChannel.addItem("All");
1152 for(int i = 1; i <= 16; i++) cbMidiChannel.addItem("Channel " + String.valueOf(i));
1153 cbMidiChannel.setPreferredSize(new Dimension(80, 18));
1154 cbMidiChannel.setMinimumSize(cbMidiChannel.getPreferredSize());
1155 cbMidiChannel.setMaximumSize(cbMidiChannel.getPreferredSize());
1156
1157 p2.add(cbMidiChannel);
1158 p2.setAlignmentX(LEFT_ALIGNMENT);
1159 p2.setOpaque(false);
1160 p.add(p2);
1161 p.setBackground(new java.awt.Color(0x818181));
1162
1163 bgp.add(p);
1164
1165 bgp.add(Box.createRigidArea(new Dimension(4, 0)));
1166
1167 p = new JPanel();
1168 p.setOpaque(true);
1169 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1170 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1171 l = new JLabel(Res.gfxInstrumentMapTitle);
1172 l.setAlignmentX(LEFT_ALIGNMENT);
1173 l.setAlignmentX(LEFT_ALIGNMENT);
1174 p.add(l);
1175
1176 p.add(Box.createRigidArea(new Dimension(0, 3)));
1177
1178 //o = cbInstrumentMap.getRenderer();
1179 //if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1180
1181 cbInstrumentMap.setPreferredSize(new Dimension(126, 18));
1182 cbInstrumentMap.setMinimumSize(cbInstrumentMap.getPreferredSize());
1183 cbInstrumentMap.setMaximumSize(cbInstrumentMap.getPreferredSize());
1184 cbInstrumentMap.setAlignmentX(LEFT_ALIGNMENT);
1185 p.add(cbInstrumentMap);
1186 p.setBackground(new java.awt.Color(0x818181));
1187 bgp.add(p);
1188
1189 bgp.add(Box.createRigidArea(new Dimension(4, 0)));
1190
1191 p = new JPanel();
1192 p.setOpaque(true);
1193 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1194 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1195 l = new JLabel(Res.gfxAudioOutputTitle);
1196 l.setAlignmentX(LEFT_ALIGNMENT);
1197 p.add(l);
1198
1199 //p.add(Box.createRigidArea(new Dimension(0, 3)));
1200
1201 p2 = new JPanel();
1202 p2.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
1203 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
1204 p2.setOpaque(false);
1205 p2.setAlignmentX(LEFT_ALIGNMENT);
1206
1207 o = cbAudioDevice.getRenderer();
1208 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.RIGHT);
1209
1210 cbAudioDevice.setPreferredSize(new Dimension(40, 18));
1211 cbAudioDevice.setMinimumSize(cbAudioDevice.getPreferredSize());
1212 cbAudioDevice.setMaximumSize(cbAudioDevice.getPreferredSize());
1213
1214 p2.add(cbAudioDevice);
1215 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1216 btnChannelRouting = new PixmapButton(Res.gfxBtnCr, Res.gfxBtnCrRO);
1217 btnChannelRouting.setPressedIcon(Res.gfxBtnCrRO);
1218 btnChannelRouting.setEnabled(false);
1219 btnChannelRouting.setToolTipText(i18n.getLabel("ChannelOptions.routing"));
1220
1221 btnChannelRouting.addActionListener(new ActionListener() {
1222 public void
1223 actionPerformed(ActionEvent e) {
1224 SamplerChannel c = channel.getChannelInfo();
1225 new JSChannelOutputRoutingDlg(CC.getMainFrame(), c).setVisible(true);
1226
1227 }
1228 });
1229
1230 p2.add(btnChannelRouting);
1231
1232 p.add(p2);
1233 p.setBackground(new java.awt.Color(0x818181));
1234 p2 = new JPanel();
1235 p2.setLayout(new java.awt.BorderLayout());
1236 p.add(p2);
1237 bgp.add(p);
1238
1239 setContentPane(bgp);
1240
1241 cbMidiDevice.addActionListener(new ActionListener() {
1242 public void
1243 actionPerformed(ActionEvent e) { setMidiDevice(); }
1244 });
1245
1246 cbMidiPort.addActionListener(new ActionListener() {
1247 public void
1248 actionPerformed(ActionEvent e) { setMidiPort(); }
1249 });
1250
1251 cbMidiChannel.addActionListener(new ActionListener() {
1252 public void
1253 actionPerformed(ActionEvent e) { setMidiChannel(); }
1254 });
1255
1256 samplerListener = new SamplerAdapter() {
1257 /** Invoked when the default MIDI instrument map is changed. */
1258 public void
1259 defaultMapChanged(SamplerEvent e) {
1260 updateCbInstrumentMapToolTipText();
1261
1262 }
1263 };
1264
1265 CC.getSamplerModel().addSamplerListener(samplerListener);
1266
1267 cbInstrumentMap.addItem(noMap);
1268 cbInstrumentMap.addItem(defaultMap);
1269 for(MidiInstrumentMap map : CC.getSamplerModel().getMidiInstrumentMaps()) {
1270 cbInstrumentMap.addItem(map);
1271 }
1272
1273 int map = channel.getModel().getChannelInfo().getMidiInstrumentMapId();
1274 cbInstrumentMap.setSelectedItem(CC.getSamplerModel().getMidiInstrumentMapById(map));
1275 if(cbInstrumentMap.getSelectedItem() == null) {
1276 if(map == -1) cbInstrumentMap.setSelectedItem(noMap);
1277 else if(map == -2) {
1278 cbInstrumentMap.setSelectedItem(defaultMap);
1279 }
1280 }
1281
1282 updateCbInstrumentMapToolTipText();
1283
1284 if(channel.getModel().getChannelInfo().getEngine() == null) {
1285 cbInstrumentMap.setEnabled(false);
1286 }
1287
1288 cbInstrumentMap.addActionListener(new ActionListener() {
1289 public void
1290 actionPerformed(ActionEvent e) { updateInstrumentMap(); }
1291 });
1292
1293 CC.getSamplerModel().addMidiInstrumentMapListListener(mapListListener);
1294
1295 cbAudioDevice.addActionListener(new ActionListener() {
1296 public void
1297 actionPerformed(ActionEvent e) { setBackendAudioDevice(); }
1298 });
1299
1300 channel.getModel().addSamplerChannelListener(new SamplerChannelAdapter() {
1301 public void
1302 channelChanged(SamplerChannelEvent e) { updateChannelProperties(); }
1303 });
1304
1305 CC.getSamplerModel().addMidiDeviceListListener(getHandler());
1306 CC.getSamplerModel().addAudioDeviceListListener(getHandler());
1307
1308 updateMidiDevices();
1309 updateAudioDevices();
1310 updateChannelProperties();
1311 }
1312
1313 /**
1314 * Updates the channel settings. This method is invoked when changes to the
1315 * channel were made.
1316 */
1317 private void
1318 updateChannelProperties() {
1319 SamplerModel sm = CC.getSamplerModel();
1320 SamplerChannel sc = channel.getModel().getChannelInfo();
1321
1322 MidiDeviceModel mm = sm.getMidiDeviceById(sc.getMidiInputDevice());
1323 AudioDeviceModel am = sm.getAudioDeviceById(sc.getAudioOutputDevice());
1324
1325 if(isUpdate()) CC.getLogger().warning("Unexpected update state!");
1326
1327 setUpdate(true);
1328
1329 try {
1330 cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo());
1331
1332 cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo());
1333 btnChannelRouting.setEnabled(am != null);
1334 } catch(Exception x) {
1335 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1336 }
1337
1338 if(sc.getEngine() != null) {
1339 cbInstrumentMap.setEnabled(true);
1340 int id = sc.getMidiInstrumentMapId();
1341 Object o;
1342 if(id == -2) o = defaultMap;
1343 else if(id == -1) o = noMap;
1344 else o = CC.getSamplerModel().getMidiInstrumentMapById(id);
1345
1346 if(cbInstrumentMap.getSelectedItem() != o) {
1347 cbInstrumentMap.setSelectedItem(o);
1348 }
1349 } else {
1350 cbInstrumentMap.setSelectedItem(noMap);
1351 cbInstrumentMap.setEnabled(false);
1352 }
1353
1354 setUpdate(false);
1355 }
1356
1357 /**
1358 * Updates the MIDI device list.
1359 */
1360 private void
1361 updateMidiDevices() {
1362 SamplerModel sm = CC.getSamplerModel();
1363 SamplerChannel sc = channel.getModel().getChannelInfo();
1364
1365 setUpdate(true);
1366
1367 try {
1368 cbMidiDevice.removeAllItems();
1369
1370 for(MidiDeviceModel m : sm.getMidiDevices())
1371 cbMidiDevice.addItem(m.getDeviceInfo());
1372
1373 MidiDeviceModel mm = sm.getMidiDeviceById(sc.getMidiInputDevice());
1374 cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo());
1375 } catch(Exception x) {
1376 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1377 }
1378
1379 setUpdate(false);
1380 }
1381
1382
1383 private void
1384 updateInstrumentMap() {
1385 updateCbInstrumentMapToolTipText();
1386
1387 int id = channel.getModel().getChannelInfo().getMidiInstrumentMapId();
1388 Object o = cbInstrumentMap.getSelectedItem();
1389 if(o == null && id == -1) return;
1390
1391 int cbId;
1392 if(o == null || o == noMap) cbId = -1;
1393 else if(o == defaultMap) cbId = -2;
1394 else cbId = ((MidiInstrumentMap)o).getMapId();
1395
1396 if(cbId == id) return;
1397
1398 channel.getModel().setBackendMidiInstrumentMap(cbId);
1399 }
1400
1401 private void
1402 updateCbInstrumentMapToolTipText() {
1403 if(cbInstrumentMap.getSelectedItem() != defaultMap) {
1404 cbInstrumentMap.setToolTipText(null);
1405 return;
1406 }
1407
1408 MidiInstrumentMap m = CC.getSamplerModel().getDefaultMidiInstrumentMap();
1409 if(m != null) {
1410 String s = i18n.getLabel("Channel.ttDefault", m.getName());
1411 cbInstrumentMap.setToolTipText(s);
1412 } else {
1413 cbInstrumentMap.setToolTipText(null);
1414 }
1415 }
1416
1417 /**
1418 * Updates the audio device list.
1419 */
1420 private void
1421 updateAudioDevices() {
1422 SamplerModel sm = CC.getSamplerModel();
1423 SamplerChannel sc = channel.getModel().getChannelInfo();
1424
1425 setUpdate(true);
1426
1427 try {
1428 cbAudioDevice.removeAllItems();
1429
1430 for(AudioDeviceModel m : sm.getAudioDevices())
1431 cbAudioDevice.addItem(m.getDeviceInfo());
1432
1433 AudioDeviceModel am = sm.getAudioDeviceById(sc.getAudioOutputDevice());
1434 cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo());
1435 } catch(Exception x) {
1436 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1437 }
1438
1439 setUpdate(false);
1440 }
1441
1442 private void
1443 setMidiDevice() {
1444 MidiInputDevice mid = (MidiInputDevice)cbMidiDevice.getSelectedItem();
1445
1446 if(!isUpdate()) {
1447 if(mid != null) {
1448 channel.getModel().setBackendMidiInputDevice(mid.getDeviceId());
1449 }
1450
1451 return;
1452 }
1453
1454 if(midiDevice != null) midiDevice.removeMidiDeviceListener(getHandler());
1455
1456 cbMidiPort.removeAllItems();
1457
1458 if(mid == null) {
1459 midiDevice = null;
1460 cbMidiPort.setEnabled(false);
1461
1462 cbMidiChannel.setSelectedItem(null);
1463 cbMidiChannel.setEnabled(false);
1464 } else {
1465 midiDevice = CC.getSamplerModel().getMidiDeviceById(mid.getDeviceId());
1466 if(midiDevice != null) midiDevice.addMidiDeviceListener(getHandler());
1467
1468 cbMidiPort.setEnabled(true);
1469
1470 MidiPort[] ports = mid.getMidiPorts();
1471 for(MidiPort port : ports) cbMidiPort.addItem(port);
1472
1473 int p = channel.getModel().getChannelInfo().getMidiInputPort();
1474 cbMidiPort.setSelectedItem(p >= 0 && p < ports.length ? ports[p] : null);
1475
1476 cbMidiChannel.setEnabled(true);
1477 int c = channel.getModel().getChannelInfo().getMidiInputChannel();
1478 cbMidiChannel.setSelectedItem(c == -1 ? "All" : "Channel " + (c + 1));
1479 }
1480 }
1481
1482 private void
1483 setMidiPort() {
1484 if(isUpdate()) return;
1485
1486 channel.getModel().setBackendMidiInputPort(cbMidiPort.getSelectedIndex());
1487 }
1488
1489 private void
1490 setMidiChannel() {
1491 if(isUpdate()) return;
1492
1493 Object o = cbMidiChannel.getSelectedItem();
1494 if(o == null) return;
1495 String s = o.toString();
1496
1497 int c = s.equals("All") ? -1 : Integer.parseInt(s.substring(8)) - 1;
1498
1499 channel.getModel().setBackendMidiInputChannel(c);
1500 }
1501
1502 private void
1503 setBackendAudioDevice() {
1504 if(isUpdate()) return;
1505 AudioOutputDevice dev = (AudioOutputDevice)cbAudioDevice.getSelectedItem();
1506 if(dev != null) channel.getModel().setBackendAudioOutputDevice(dev.getDeviceId());
1507 }
1508
1509 /**
1510 * Determines whether the currently processed changes are due to update.
1511 * @return <code>true</code> if the currently processed changes are due to update and
1512 * <code>false</code> if the currently processed changes are due to user input.
1513 */
1514 private boolean
1515 isUpdate() { return update; }
1516
1517 /**
1518 * Sets whether the currently processed changes are due to update.
1519 * @param b Specify <code>true</code> to indicate that the currently
1520 * processed changes are due to update; <code>false</code>
1521 * indicates that the currently processed changes are due to user input.
1522 */
1523 private void
1524 setUpdate(boolean b) { update = b; }
1525
1526 protected void
1527 onDestroy() {
1528 SamplerModel sm = CC.getSamplerModel();
1529
1530 sm.removeMidiDeviceListListener(getHandler());
1531 sm.removeAudioDeviceListListener(getHandler());
1532 sm.removeMidiInstrumentMapListListener(mapListListener);
1533 sm.removeSamplerListener(samplerListener);
1534
1535 if(midiDevice != null) {
1536 midiDevice.removeMidiDeviceListener(getHandler());
1537 }
1538 }
1539
1540 private final Handler handler = new Handler();
1541
1542 private Handler
1543 getHandler() { return handler; }
1544
1545 private class Handler implements MidiDeviceListListener, ListListener<AudioDeviceModel>,
1546 MidiDeviceListener {
1547 /**
1548 * Invoked when a new MIDI device is created.
1549 * @param e A <code>MidiDeviceListEvent</code>
1550 * instance providing the event information.
1551 */
1552 public void
1553 deviceAdded(MidiDeviceListEvent e) {
1554 cbMidiDevice.addItem(e.getMidiDeviceModel().getDeviceInfo());
1555 }
1556
1557 /**
1558 * Invoked when a MIDI device is removed.
1559 * @param e A <code>MidiDeviceListEvent</code>
1560 * instance providing the event information.
1561 */
1562 public void
1563 deviceRemoved(MidiDeviceListEvent e) {
1564 cbMidiDevice.removeItem(e.getMidiDeviceModel().getDeviceInfo());
1565 }
1566
1567 /**
1568 * Invoked when a new audio device is created.
1569 * @param e An <code>AudioDeviceListEvent</code>
1570 * instance providing the event information.
1571 */
1572 public void
1573 entryAdded(ListEvent<AudioDeviceModel> e) {
1574 cbAudioDevice.addItem(e.getEntry().getDeviceInfo());
1575 }
1576
1577 /**
1578 * Invoked when an audio device is removed.
1579 * @param e An <code>AudioDeviceListEvent</code>
1580 * instance providing the event information.
1581 */
1582 public void
1583 entryRemoved(ListEvent<AudioDeviceModel> e) {
1584 cbAudioDevice.removeItem(e.getEntry().getDeviceInfo());
1585 }
1586
1587 public void
1588 settingsChanged(MidiDeviceEvent e) {
1589 if(isUpdate()) {
1590 CC.getLogger().warning("Invalid update state");
1591 return;
1592 }
1593
1594 setUpdate(true);
1595 int idx = cbMidiPort.getSelectedIndex();
1596 MidiInputDevice d = e.getMidiDeviceModel().getDeviceInfo();
1597
1598 cbMidiPort.removeAllItems();
1599 for(MidiPort port : d.getMidiPorts()) cbMidiPort.addItem(port);
1600
1601 if(idx >= cbMidiPort.getModel().getSize()) idx = 0;
1602
1603 setUpdate(false);
1604
1605 if(cbMidiPort.getModel().getSize() > 0) cbMidiPort.setSelectedIndex(idx);
1606 }
1607 }
1608
1609 private class MapListListener implements ListListener<MidiInstrumentMap> {
1610 /** Invoked when a new MIDI instrument map is added to a list. */
1611 public void
1612 entryAdded(ListEvent<MidiInstrumentMap> e) {
1613 cbInstrumentMap.insertItemAt(e.getEntry(), cbInstrumentMap.getItemCount());
1614 boolean b = channel.getModel().getChannelInfo().getEngine() != null;
1615 if(b && !cbInstrumentMap.isEnabled()) cbInstrumentMap.setEnabled(true);
1616 }
1617
1618 /** Invoked when a new MIDI instrument map is removed from a list. */
1619 public void
1620 entryRemoved(ListEvent<MidiInstrumentMap> e) {
1621 cbInstrumentMap.removeItem(e.getEntry());
1622 if(cbInstrumentMap.getItemCount() == 0) { // TODO: ?
1623 cbInstrumentMap.setSelectedItem(noMap);
1624 cbInstrumentMap.setEnabled(false);
1625 }
1626 }
1627 }
1628 }

  ViewVC Help
Powered by ViewVC