/[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 1545 - (show annotations) (download)
Tue Dec 4 18:28:29 2007 UTC (16 years, 4 months ago) by iliev
File size: 47993 byte(s)
* Added support for monitoring the total number of active disk streams

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

  ViewVC Help
Powered by ViewVC