/* * JSampler - a java front-end for LinuxSampler * * Copyright (C) 2005-2007 Grigor Iliev * * This file is part of JSampler. * * JSampler is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * JSampler is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JSampler; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package org.jsampler.view.classic; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.Vector; import java.util.logging.Level; import javax.swing.Action; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.JToggleButton; import javax.swing.TransferHandler; import javax.swing.border.Border; import javax.swing.border.LineBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.sf.juife.InformationDialog; import net.sf.juife.JuifeUtils; import org.jsampler.AudioDeviceModel; import org.jsampler.CC; import org.jsampler.HF; import org.jsampler.Instrument; import org.jsampler.MidiDeviceModel; import org.jsampler.MidiInstrumentMap; import org.jsampler.SamplerChannelModel; import org.jsampler.SamplerModel; import org.jsampler.event.ListEvent; import org.jsampler.event.ListListener; import org.jsampler.event.MidiDeviceListEvent; import org.jsampler.event.MidiDeviceListListener; import org.jsampler.event.SamplerAdapter; import org.jsampler.event.SamplerChannelAdapter; import org.jsampler.event.SamplerChannelEvent; import org.jsampler.event.SamplerChannelListEvent; import org.jsampler.event.SamplerChannelListListener; import org.jsampler.event.SamplerChannelListener; import org.jsampler.event.SamplerEvent; import org.linuxsampler.lscp.AudioOutputDevice; import org.linuxsampler.lscp.MidiInputDevice; import org.linuxsampler.lscp.MidiPort; import org.linuxsampler.lscp.SamplerChannel; import org.linuxsampler.lscp.SamplerEngine; import static org.jsampler.view.classic.ClassicI18n.i18n; /** * * @author Grigor Iliev */ public class Channel extends org.jsampler.view.JSChannel { private final static ImageIcon iconMuteOn; private final static ImageIcon iconMuteOff; private final static ImageIcon iconMutedBySolo; private final static ImageIcon iconSoloOn; private final static ImageIcon iconSoloOff; private final static ImageIcon iconShowProperties; private final static ImageIcon iconHideProperties; private static Border borderSelected; private static Border borderHighlighted; private static Border borderDeselected; private static Color chnColor; private static Color borderColor; private static Color borderHighlightedColor; private static Color chnSelectedColor; private static Color chnHighlightedColor; private final static Vector propertyChangeListeners = new Vector(); static { String path = "org/jsampler/view/classic/res/icons/"; URL url = ClassLoader.getSystemClassLoader().getResource(path + "mute_on.png"); iconMuteOn = new ImageIcon(url); url = ClassLoader.getSystemClassLoader().getResource(path + "mute_off.png"); iconMuteOff = new ImageIcon(url); url = ClassLoader.getSystemClassLoader().getResource(path + "muted_by_solo.png"); iconMutedBySolo = new ImageIcon(url); url = ClassLoader.getSystemClassLoader().getResource(path + "solo_on.png"); iconSoloOn = new ImageIcon(url); url = ClassLoader.getSystemClassLoader().getResource(path + "solo_off.png"); iconSoloOff = new ImageIcon(url); url = ClassLoader.getSystemClassLoader().getResource(path + "Back16.gif"); iconShowProperties = new ImageIcon(url); iconHideProperties = Res.iconDown16; if(ClassicPrefs.getCustomChannelBorderColor()) setBorderColor(ClassicPrefs.getChannelBorderColor()); else setBorderColor(ClassicPrefs.getDefaultChannelBorderColor()); if(ClassicPrefs.getCustomChannelBorderHlColor()) setBorderHighlightedColor(ClassicPrefs.getChannelBorderHlColor()); else setBorderHighlightedColor(ClassicPrefs.getDefaultChannelBorderHlColor()); borderSelected = new LineBorder(getBorderColor(), 2, true); borderHighlighted = new LineBorder(getBorderHighlightedColor(), 2, true); borderDeselected = BorderFactory.createEmptyBorder(2, 2, 2, 2); chnColor = new JPanel().getBackground(); if(ClassicPrefs.getCustomSelectedChannelBgColor()) { chnSelectedColor = ClassicPrefs.getSelectedChannelBgColor(); } else { int r = chnColor.getRed() - 14 < 0 ? 0 : chnColor.getRed() - 14; int g = chnColor.getGreen() - 8 < 0 ? 0 : chnColor.getGreen() - 8; int b = chnColor.getBlue() - 3 < 0 ? 0 : chnColor.getBlue() - 3; chnSelectedColor = new Color(r, g, b); } /*r = r + 5 > 255 ? 255 : r + 5; g = g + 4 > 255 ? 255 : g + 4; b = b + 1 > 255 ? 255 : b + 1;*/ chnHighlightedColor = new Color(chnColor.getRGB()); } /** * Registers the specified listener for receiving property change events. * @param l The PropertyChangeListener to register. */ public void addPropertyChangeListener(PropertyChangeListener l) { propertyChangeListeners.add(l); } /** * Removes the specified listener. * @param l The PropertyChangeListener to remove. */ public void removePropertyChangeListener(PropertyChangeListener l) { propertyChangeListeners.remove(l); } /** * Gets the border color that is used when the channel is selected. * @return The border color that is used when the channel is selected. */ public static Color getBorderColor() { return borderColor; } /** * Sets the border color to be used when the channel is selected. * @param c The border color to be used when the channel is selected. */ public static void setBorderColor(Color c) { if(borderColor != null && borderColor.getRGB() == c.getRGB()) return; Color oldColor = borderColor; borderColor = c; borderSelected = new LineBorder(getBorderColor(), 2, true); firePropertyChanged("borderColor", oldColor, borderColor); } /** * Gets the border color that is used when the mouse pointer is over a channel. * @return The border color that is used when the mouse pointer is over a channel. */ public static Color getBorderHighlightedColor() { return borderHighlightedColor; } /** * Sets the border color to be used when the mouse pointer is over a channel. * @param c The border color to be used when the mouse pointer is over a channel. */ public static void setBorderHighlightedColor(Color c) { Color oldColor = borderHighlightedColor; if(oldColor != null && oldColor.getRGB() == c.getRGB()) return; borderHighlightedColor = c; borderHighlighted = new LineBorder(getBorderHighlightedColor(), 2, true); firePropertyChanged("borderHighlightedColor", oldColor, borderHighlightedColor); } /** * Gets the background color that is used when a channel is selected. * @return The background color that is used when a channel is selected. */ public static Color getSelectedChannelBgColor() { return chnSelectedColor; } /** * Sets the background color that is used when a channel is selected. * @param c The background color to be used when a channel is selected. */ public static void setSelectedChannelBgColor(Color c) { Color oldColor = chnSelectedColor; if(oldColor != null && oldColor.getRGB() == c.getRGB()) return; chnSelectedColor = c; firePropertyChanged("selectedChannelBgColor", oldColor, chnSelectedColor); } /** * Gets the background color that is used when the mouse pointer is over a channel. * @return The background color that is used when the mouse pointer is over a channel. */ public static Color getHighlightedChannelBgColor() { return chnHighlightedColor; } /** * Sets the background color to be used when the mouse pointer is over a channel. * @param c The background color to be used when the mouse pointer is over a channel. */ public static void setHighlightedChannelBgColor(Color c) { Color oldColor = chnHighlightedColor; if(oldColor != null && oldColor.getRGB() == c.getRGB()) return; chnHighlightedColor = c; firePropertyChanged("highlightedChannelBgColor", oldColor, chnHighlightedColor); } private static void firePropertyChanged(String propertyName, Object oldValue, Object newValue) { PropertyChangeEvent e = new PropertyChangeEvent(Channel.class, propertyName, oldValue, newValue); for(PropertyChangeListener l : propertyChangeListeners) l.propertyChange(e); } private final JPanel mainPane = new JPanel(); private final ChannelProperties propertiesPane; private final JButton btnInstr = new InstrumentButton(i18n.getLabel("Channel.btnInstr")); private final Action actInstr; private final JButton btnMute = new JButton(); private final JButton btnSolo = new JButton(); private final JSlider slVolume = new JSlider(0, 100); private final JLabel lVolume = new JLabel(); private final JLabel lVolImg = new JLabel(Res.iconVolume16); private final JLabel lStreams = new JLabel("--"); private final JLabel lVoices = new JLabel("--"); private final JToggleButton btnProperties = new JToggleButton(); private static int count = 2; private boolean selected = false; private boolean mouseOver = false; /** * Creates a new instance of Channel using the specified * non-null channel model. * @param model The model to be used by this channel. * @throws IllegalArgumentException If the model is null. */ public Channel(SamplerChannelModel model) { super(model); setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS)); addMouseListener(getHandler()); JPanel p = new JPanel(); p.setOpaque(false); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); p.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); setToolTipText(i18n.getLabel("Channel.tt", getModel().getChannelId())); Dimension d = btnInstr.getPreferredSize(); btnInstr.setMaximumSize(new Dimension(Short.MAX_VALUE, d.height)); p.add(btnInstr); p.add(Box.createRigidArea(new Dimension(6, 0))); lStreams.setHorizontalAlignment(JLabel.CENTER); lVoices.setHorizontalAlignment(JLabel.CENTER); JPanel statPane = new JPanel(); statPane.setOpaque(false); statPane.setBorder(BorderFactory.createLoweredBevelBorder()); statPane.setLayout(new BoxLayout(statPane, BoxLayout.X_AXIS)); statPane.add(Box.createRigidArea(new Dimension(6, 0))); statPane.add(lStreams); statPane.add(new JLabel("/")); statPane.add(lVoices); statPane.add(Box.createRigidArea(new Dimension(6, 0))); p.add(statPane); p.add(Box.createRigidArea(new Dimension(6, 0))); btnMute.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); p.add(btnMute); p.add(Box.createRigidArea(new Dimension(6, 0))); btnSolo.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); p.add(btnSolo); p.add(Box.createRigidArea(new Dimension(6, 0))); JPanel volumePane = new JPanel(); volumePane.setOpaque(false); volumePane.setBorder(BorderFactory.createLoweredBevelBorder()); volumePane.setLayout(new BoxLayout(volumePane, BoxLayout.X_AXIS)); volumePane.add(Box.createRigidArea(new Dimension(6, 0))); volumePane.add(lVolImg); volumePane.add(Box.createRigidArea(new Dimension(1, 0))); d = slVolume.getPreferredSize(); slVolume.setMaximumSize(new Dimension(d.width > 300 ? d.width : 300, d.height)); slVolume.setOpaque(false); volumePane.add(slVolume); lVolume.setBorder(BorderFactory.createEmptyBorder(3, 6, 3, 6)); lVolume.setHorizontalAlignment(lVolume.RIGHT); // We use this to set the size of the lVolume that will be used in setVolume() // to prevent the frequent resizing of lVolume lVolume.setText("100%"); volumePane.add(lVolume); p.add(volumePane); p.add(Box.createRigidArea(new Dimension(6, 0))); btnProperties.setContentAreaFilled(false); btnProperties.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); btnProperties.setIcon(iconShowProperties); btnProperties.setSelectedIcon(iconHideProperties); p.add(btnProperties); mainPane.add(p); propertiesPane = new ChannelProperties(model); propertiesPane.setBorder(BorderFactory.createEmptyBorder(0, 3, 3, 3)); propertiesPane.setVisible(false); mainPane.add(propertiesPane); add(mainPane); d = getPreferredSize(); setMaximumSize(new Dimension(getMaximumSize().width, d.height)); getModel().addSamplerChannelListener(getHandler()); actInstr = new AbstractAction() { public void actionPerformed(ActionEvent e) { if(actInstr.isEnabled()) loadInstrument(); } }; btnInstr.addActionListener(actInstr); btnMute.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { changeMute(); } }); btnSolo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { changeSolo(); } }); slVolume.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { setVolume(); } }); btnProperties.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showProperties(btnProperties.isSelected()); String s; if(btnProperties.isSelected()) { s = i18n.getButtonLabel("Channel.ttHideProps"); } else { s = i18n.getButtonLabel("Channel.ttShowProps"); } btnProperties.setToolTipText(s); } }); btnProperties.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); String s; if(btnProperties.isSelected()) s = i18n.getButtonLabel("Channel.ttHideProps"); else s = i18n.getButtonLabel("Channel.ttShowProps"); btnProperties.setToolTipText(s); addPropertyChangeListener(getHandler()); updateChannelInfo(); } public class InstrumentButton extends JButton { private boolean dragging = false; InstrumentButton(String s) { super(s); setTransferHandler(new TransferHandler("instrument")); addMouseListener(new MouseAdapter() { public void mouseExited(MouseEvent e) { if(!dragging) return; int b1 = e.BUTTON1_DOWN_MASK; if((e.getModifiersEx() & b1) != b1) return; actInstr.setEnabled(false); doClick(0); actInstr.setEnabled(true); JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } public void mouseReleased(MouseEvent e) { dragging = false; } }); addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { dragging = true; } }); } public String getInstrument() { SamplerChannel sc = Channel.this.getChannelInfo(); if(sc.getInstrumentName() == null || sc.getInstrumentStatus() < 0) return null; Instrument instr = new Instrument(); instr.setName(sc.getInstrumentName()); instr.setInstrumentIndex(sc.getInstrumentIndex()); instr.setPath(sc.getInstrumentFile()); return instr.getDnDString(); } public void setInstrument(String instr) { if(!Instrument.isDnDString(instr)) return; String[] args = instr.split("\n"); if(args.length < 6) return; try { int idx = Integer.parseInt(args[5]); Channel.this.getModel().loadBackendInstrument(args[4], idx); } catch(Exception x) { CC.getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } } } private final EventHandler eventHandler = new EventHandler(); private EventHandler getHandler() { return eventHandler; } private class EventHandler extends MouseAdapter implements SamplerChannelListener, PropertyChangeListener, HierarchyListener { /** * Invoked when changes are made to a sampler channel. * @param e A SamplerChannelEvent instance * containing event information. */ public void channelChanged(SamplerChannelEvent e) { updateChannelInfo(); } /** * Invoked when the number of active disk streams has changed. * @param e A SamplerChannelEvent instance * containing event information. */ public void streamCountChanged(SamplerChannelEvent e) { updateStreamCount(getModel().getStreamCount()); } /** * Invoked when the number of active voices has changed. * @param e A SamplerChannelEvent instance * containing event information. */ public void voiceCountChanged(SamplerChannelEvent e) { updateVoiceCount(getModel().getVoiceCount()); } public void propertyChange(PropertyChangeEvent e) { if ( e.getPropertyName() == "borderColor" || e.getPropertyName() == "borderHighlightedColor" || e.getPropertyName() == "selectedChannelBgColor" || e.getPropertyName() == "highlightedChannelBgColor" ) { updateColors(isSelected()); } } public void mouseEntered(MouseEvent e) { mouseOver = true; updateColors(isSelected()); } public void mouseExited(MouseEvent e) { if(getMousePosition(true) != null) return; mouseOver = false; updateColors(isSelected()); } /** Called when the hierarchy has been changed. */ public void hierarchyChanged(HierarchyEvent e) { if((e.getChangeFlags() & e.SHOWING_CHANGED) == e.SHOWING_CHANGED) { if(getMousePosition() == null) mouseExited(null); else mouseEntered(null); } } } /** * Determines whether the channel is selected. * @return true if the channel is selected, false otherwise. */ public boolean isSelected() { return selected; } /** * Sets the selection state of this channel. * This method is invoked when the selection state of the channel has changed. * @param select Specifies the new selection state of this channel; * true to select the channel, false otherwise. */ public void setSelected(boolean select) { updateColors(select); selected = select; } /** * Updates the channel background and border colors. * @param selected Specifies the selection state of this channel. */ private void updateColors(boolean selected) { if(selected) { mainPane.setBorder(borderSelected); mainPane.setBackground(chnSelectedColor); } else { if(mouseOver) { mainPane.setBorder(borderHighlighted); mainPane.setBackground(chnHighlightedColor); } else { mainPane.setBorder(borderDeselected); mainPane.setBackground(chnColor); } } } /** Hides the channel properties. */ public void collapseChannel() { if(btnProperties.isSelected()) btnProperties.doClick(); } /** Shows the channel properties. */ public void expandChannel() { if(!btnProperties.isSelected()) btnProperties.doClick(); } /** * Updates the channel settings. This method is invoked when changes to the * channel were made. */ private void updateChannelInfo() { SamplerChannel sc = getChannelInfo(); int status = sc.getInstrumentStatus(); if(status >= 0 && status < 100) { btnInstr.setText(i18n.getLabel("Channel.loadingInstrument", status)); } else if(status == -1) { btnInstr.setText(i18n.getLabel("Channel.btnInstr")); } else if(status < -1) { btnInstr.setText(i18n.getLabel("Channel.errorLoadingInstrument")); } else { if(sc.getInstrumentName() != null) btnInstr.setText(sc.getInstrumentName()); else btnInstr.setText(i18n.getLabel("Channel.btnInstr")); } updateMuteIcon(sc); if(sc.isSoloChannel()) btnSolo.setIcon(iconSoloOn); else btnSolo.setIcon(iconSoloOff); slVolume.setValue((int)(sc.getVolume() * 100)); boolean b = sc.getEngine() != null; slVolume.setEnabled(b); btnSolo.setEnabled(b); btnMute.setEnabled(b); } /** Invoked when the user clicks the mute button. */ private void changeMute() { SamplerChannel sc = getChannelInfo(); boolean b = true; /* * Changing the mute button icon now instead of * leaving the work to the notification mechanism of the LinuxSampler. */ if(sc.isMuted() && !sc.isMutedBySolo()) { b = false; boolean hasSolo = CC.getSamplerModel().hasSoloChannel(); if(sc.isSoloChannel() || !hasSolo) btnMute.setIcon(iconMuteOff); else btnMute.setIcon(iconMutedBySolo); } else btnMute.setIcon(iconMuteOn); getModel().setBackendMute(b); } /** Invoked when the user clicks the solo button. */ private void changeSolo() { SamplerChannel sc = getChannelInfo(); boolean b = !sc.isSoloChannel(); /* * Changing the solo button icon (and related) now instead of * leaving the work to the notification mechanism of the LinuxSampler. */ if(b) { btnSolo.setIcon(iconSoloOn); if(sc.isMutedBySolo()) btnMute.setIcon(iconMuteOff); } else { btnSolo.setIcon(iconSoloOff); if(!sc.isMuted() && CC.getSamplerModel().getSoloChannelCount() > 1) btnMute.setIcon(iconMutedBySolo); } getModel().setBackendSolo(b); } /** Invoked when the user changes the volume */ private void setVolume() { updateVolume(); if(slVolume.getValueIsAdjusting()) return; int vol = (int)(getChannelInfo().getVolume() * 100); if(vol == slVolume.getValue()) return; /* * If the model's volume is not equal to the slider * value we assume that the change is due to user input. * So we must update the volume at the backend too. */ float volume = slVolume.getValue(); volume /= 100; getModel().setBackendVolume(volume); } private void updateVolume() { int volume = slVolume.getValue(); slVolume.setToolTipText(i18n.getLabel("Channel.volume", volume)); lVolImg.setToolTipText(i18n.getLabel("Channel.volume", volume)); setVolumeLabel(volume); } private void setVolumeLabel(int volume) { Dimension d = lVolume.getPreferredSize(); lVolume.setText(String.valueOf(volume) + '%'); d = JuifeUtils.getUnionSize(d, lVolume.getPreferredSize()); lVolume.setMinimumSize(d); lVolume.setPreferredSize(d); lVolume.setMaximumSize(d); } /** * Updates the mute button with the proper icon regarding to information obtained * from channel. * @param channel A SamplerChannel instance containing the new settings * for this channel. */ private void updateMuteIcon(SamplerChannel channel) { if(channel.isMutedBySolo()) btnMute.setIcon(iconMutedBySolo); else if(channel.isMuted()) btnMute.setIcon(iconMuteOn); else btnMute.setIcon(iconMuteOff); } /** * Updates the number of active disk streams. * @param count The new number of active disk streams. */ private void updateStreamCount(int count) { Dimension d = lStreams.getPreferredSize(); lStreams.setText(count == 0 ? "--" : String.valueOf(count)); d = JuifeUtils.getUnionSize(d, lStreams.getPreferredSize()); lStreams.setMinimumSize(d); lStreams.setPreferredSize(d); lStreams.setMaximumSize(d); } /** * Updates the number of active voices. * @param count The new number of active voices. */ private void updateVoiceCount(int count) { Dimension d = lVoices.getPreferredSize(); lVoices.setText(count == 0 ? "--" : String.valueOf(count)); d = JuifeUtils.getUnionSize(d, lVoices.getPreferredSize()); lVoices.setMinimumSize(d); lVoices.setPreferredSize(d); lVoices.setMaximumSize(d); } private void showProperties(boolean show) {propertiesPane.setVisible(show); } private void loadInstrument() { InstrumentChooser dlg = new InstrumentChooser(CC.getMainFrame()); dlg.setVisible(true); if(!dlg.isCancelled()) { int idx = dlg.getInstrumentIndex(); getModel().loadBackendInstrument(dlg.getFileName(), idx); } } } class ChannelProperties extends JPanel { private final JLabel lMidiDevice = new JLabel(i18n.getLabel("ChannelProperties.lMidiDevice")); private final JLabel lMidiPort = new JLabel(i18n.getLabel("ChannelProperties.lMidiPort")); private final JLabel lMidiChannel = new JLabel(i18n.getLabel("ChannelProperties.lMidiChannel")); private final JLabel lInstrumentMap = new JLabel(i18n.getLabel("ChannelProperties.lInstrumentMap")); private final JLabel lAudioDevice = new JLabel(i18n.getLabel("ChannelProperties.lAudioDevice")); private final JComboBox cbEngines = new JComboBox(); private final JComboBox cbInstrumentMap = new JComboBox(); private final JComboBox cbMidiDevice = new JComboBox(); private final JComboBox cbMidiPort = new JComboBox(); private final JComboBox cbMidiChannel = new JComboBox(); private final JComboBox cbAudioDevice = new JComboBox(); private final JButton btnFxSends = new JButton(Res.iconFxSends22); private final JButton btnAudioProps = new JButton(Res.iconAudioProps16); private InformationDialog fxSendsDlg = null; private SamplerChannelModel channelModel = null; private boolean update = false; private class NoMap { public String toString() { return "[None]"; } } private NoMap noMap = new NoMap(); private class DefaultMap { public String toString() { return "[Default]"; } } private DefaultMap defaultMap = new DefaultMap(); /** * Creates a new instance of ChannelProperties using the specified non-null * channel model. * @param model The model to be used by this channel properties pane. */ ChannelProperties(SamplerChannelModel model) { channelModel = model; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setOpaque(false); add(new JSeparator()); JPanel enginesPane = createEnginePane(); JPanel devicesPane = new JPanel(); devicesPane.setOpaque(false); devicesPane.setLayout(new BoxLayout(devicesPane, BoxLayout.X_AXIS)); devicesPane.add(Box.createRigidArea(new Dimension(3, 0))); devicesPane.add(createMidiPane()); devicesPane.add(Box.createRigidArea(new Dimension(3, 0))); devicesPane.add(enginesPane); devicesPane.add(Box.createRigidArea(new Dimension(3, 0))); JPanel audioPane = createAudioPane(); Dimension d = audioPane.getPreferredSize(); d.height = Short.MAX_VALUE; audioPane.setMaximumSize(d); devicesPane.add(audioPane); add(devicesPane); add(Box.createRigidArea(new Dimension(0, 6))); add(new JSeparator()); cbMidiChannel.addItem("All"); for(int i = 1; i <= 16; i++) cbMidiChannel.addItem(String.valueOf(i)); cbMidiDevice.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setMidiDevice(); } }); cbMidiPort.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setMidiPort(); } }); cbMidiChannel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setMidiChannel(); } }); cbEngines.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setEngineType(); } }); cbAudioDevice.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setAudioDevice(); } }); getModel().addSamplerChannelListener(new SamplerChannelAdapter() { public void channelChanged(SamplerChannelEvent e) { updateChannelProperties(); } }); CC.getSamplerModel().addSamplerListener(new SamplerAdapter() { /** Invoked when the default MIDI instrument map is changed. */ public void defaultMapChanged(SamplerEvent e) { updateCbInstrumentMapToolTipText(); } }); cbInstrumentMap.addItem(noMap); cbInstrumentMap.addItem(defaultMap); for(MidiInstrumentMap map : CC.getSamplerModel().getMidiInstrumentMaps()) { cbInstrumentMap.addItem(map); } int map = getModel().getChannelInfo().getMidiInstrumentMapId(); cbInstrumentMap.setSelectedItem(CC.getSamplerModel().getMidiInstrumentMapById(map)); if(cbInstrumentMap.getSelectedItem() == null) { if(map == -1) cbInstrumentMap.setSelectedItem(noMap); else if(map == -2) { cbInstrumentMap.setSelectedItem(defaultMap); } } updateCbInstrumentMapToolTipText(); if(getModel().getChannelInfo().getEngine() == null) { cbInstrumentMap.setEnabled(false); } cbInstrumentMap.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { updateInstrumentMap(); } }); CC.getSamplerModel().addMidiDeviceListListener(getHandler()); CC.getSamplerModel().addAudioDeviceListListener(getHandler()); CC.getSamplerModel().addSamplerChannelListListener(getHandler()); CC.getSamplerModel().addMidiInstrumentMapListListener(new MapListListener()); btnAudioProps.setToolTipText(i18n.getLabel("ChannelProperties.routing")); btnAudioProps.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { SamplerChannel c = getModel().getChannelInfo(); new ChannelOutputRoutingDlg(CC.getMainFrame(), c).setVisible(true); } }); btnFxSends.setToolTipText(i18n.getButtonLabel("ChannelProperties.btnFxSends")); btnFxSends.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(fxSendsDlg != null && fxSendsDlg.isVisible()) { fxSendsDlg.toFront(); return; } FxSendsPane p = new FxSendsPane(getModel()); int id = getModel().getChannelId(); fxSendsDlg = new InformationDialog(CC.getMainFrame(), p); fxSendsDlg.setTitle(i18n.getLabel("FxSendsPane.title", id)); fxSendsDlg.setModal(false); fxSendsDlg.showCloseButton(false); fxSendsDlg.setVisible(true); } }); updateMidiDevices(); updateAudioDevices(); updateChannelProperties(); } private JPanel createEnginePane() { for(SamplerEngine e : CC.getSamplerModel().getEngines()) cbEngines.addItem(e); cbEngines.setMaximumSize(cbEngines.getPreferredSize()); JPanel p = new JPanel(); p.setOpaque(false); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.add(cbEngines); p.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); //enginesPane.add(Box.createGlue()); JPanel enginesPane = new JPanel(); enginesPane.setOpaque(false); enginesPane.setLayout(new BorderLayout()); enginesPane.add(p, BorderLayout.SOUTH); //enginesPane.add(Box.createRigidArea(new Dimension(0, 3))); String s = i18n.getLabel("ChannelProperties.enginesPane"); enginesPane.setBorder(BorderFactory.createTitledBorder(s)); Dimension d = new Dimension(enginesPane.getPreferredSize().width, Short.MAX_VALUE); enginesPane.setMaximumSize(d); return enginesPane; } private JPanel createMidiPane() { JPanel midiPane = new JPanel(); midiPane.setOpaque(false); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); midiPane.setLayout(gridbag); c.gridx = 1; c.gridy = 0; c.insets = new Insets(0, 3, 3, 3); gridbag.setConstraints(lMidiDevice, c); midiPane.add(lMidiDevice); c.gridx = 2; c.gridy = 0; gridbag.setConstraints(lMidiPort, c); midiPane.add(lMidiPort); c.gridx = 3; c.gridy = 0; gridbag.setConstraints(lMidiChannel, c); midiPane.add(lMidiChannel); c.gridx = 4; c.gridy = 0; c.insets = new Insets(0, 10, 3, 3); gridbag.setConstraints(lInstrumentMap, c); midiPane.add(lInstrumentMap); btnFxSends.setMargin(new Insets(0, 0, 0, 0)); c.gridx = 0; c.gridy = 0; c.gridheight = 2; c.insets = new Insets(0, 5, 0, 8); gridbag.setConstraints(btnFxSends, c); midiPane.add(btnFxSends); c.gridx = 1; c.gridy = 1; c.gridheight = 1; c.insets = new Insets(0, 4, 4, 3); c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(cbMidiDevice, c); midiPane.add(cbMidiDevice); c.gridx = 3; c.gridy = 1; gridbag.setConstraints(cbMidiChannel, c); midiPane.add(cbMidiChannel); c.gridx = 2; c.gridy = 1; gridbag.setConstraints(cbMidiPort, c); midiPane.add(cbMidiPort); c.gridx = 4; c.gridy = 1; c.weightx = 1.0; c.insets = new Insets(0, 10, 3, 3); gridbag.setConstraints(cbInstrumentMap, c); midiPane.add(cbInstrumentMap); String s = i18n.getLabel("ChannelProperties.midiPane"); TitledBorder border = BorderFactory.createTitledBorder(s); //border.setTitlePosition(border.TOP); midiPane.setBorder(border); return midiPane; } private JPanel createAudioPane() { JPanel audioPane = new JPanel(); audioPane.setOpaque(false); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); audioPane.setLayout(gridbag); c.gridx = 0; c.gridy = 0; c.insets = new Insets(0, 3, 3, 3); gridbag.setConstraints(lAudioDevice, c); audioPane.add(lAudioDevice); c.gridx = 0; c.gridy = 1; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(cbAudioDevice, c); audioPane.add(cbAudioDevice); btnAudioProps.setMargin(new Insets(0, 0, 0, 0)); c.gridx = 1; c.gridy = 1; c.fill = GridBagConstraints.NONE; gridbag.setConstraints(btnAudioProps, c); audioPane.add(btnAudioProps); String s = i18n.getLabel("ChannelProperties.audioPane"); audioPane.setBorder(BorderFactory.createTitledBorder(s)); return audioPane; } /** * Gets the model that is currently used by this channel properties pane. * @return model The SamplerChannelModel instance * that provides information about the channel whose settings are * represented by this channel properties pane. */ public SamplerChannelModel getModel() { return channelModel; } private void updateInstrumentMap() { updateCbInstrumentMapToolTipText(); int id = getModel().getChannelInfo().getMidiInstrumentMapId(); Object o = cbInstrumentMap.getSelectedItem(); if(o == null && id == -1) return; int cbId; if(o == null || o == noMap) cbId = -1; else if(o == defaultMap) cbId = -2; else cbId = ((MidiInstrumentMap)o).getMapId(); if(cbId == id) return; channelModel.setBackendMidiInstrumentMap(cbId); } private void updateCbInstrumentMapToolTipText() { if(cbInstrumentMap.getSelectedItem() != defaultMap) { cbInstrumentMap.setToolTipText(null); return; } MidiInstrumentMap m = CC.getSamplerModel().getDefaultMidiInstrumentMap(); if(m != null) { String s = i18n.getLabel("Channel.ttDefault", m.getName()); cbInstrumentMap.setToolTipText(s); } else { cbInstrumentMap.setToolTipText(null); } } /** * Updates the channel settings. This method is invoked when changes to the * channel were made. */ private void updateChannelProperties() { SamplerModel sm = CC.getSamplerModel(); SamplerChannel sc = getModel().getChannelInfo(); MidiDeviceModel mm = sm.getMidiDeviceModel(sc.getMidiInputDevice()); AudioDeviceModel am = sm.getAudioDeviceModel(sc.getAudioOutputDevice()); if(isUpdate()) CC.getLogger().warning("Unexpected update state!"); setUpdate(true); try { cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo()); cbEngines.setSelectedItem(sc.getEngine()); cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo()); } catch(Exception x) { CC.getLogger().log(Level.WARNING, "Unkown error", x); } if(sc.getEngine() != null) { if(cbInstrumentMap.getItemCount() > 0) cbInstrumentMap.setEnabled(true); } else { cbInstrumentMap.setSelectedItem(noMap); cbInstrumentMap.setEnabled(false); } setUpdate(false); } /** * Updates the MIDI device list. */ private void updateMidiDevices() { SamplerModel sm = CC.getSamplerModel(); SamplerChannel sc = getModel().getChannelInfo(); setUpdate(true); try { cbMidiDevice.removeAllItems(); for(MidiDeviceModel m : sm.getMidiDeviceModels()) cbMidiDevice.addItem(m.getDeviceInfo()); MidiDeviceModel mm = sm.getMidiDeviceModel(sc.getMidiInputDevice()); cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo()); } catch(Exception x) { CC.getLogger().log(Level.WARNING, "Unkown error", x); } setUpdate(false); } /** * Updates the audio device list. */ private void updateAudioDevices() { SamplerModel sm = CC.getSamplerModel(); SamplerChannel sc = getModel().getChannelInfo(); setUpdate(true); try { cbAudioDevice.removeAllItems(); for(AudioDeviceModel m : sm.getAudioDeviceModels()) cbAudioDevice.addItem(m.getDeviceInfo()); AudioDeviceModel am = sm.getAudioDeviceModel(sc.getAudioOutputDevice()); cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo()); } catch(Exception x) { CC.getLogger().log(Level.WARNING, "Unkown error", x); } setUpdate(false); } private void setMidiDevice() { MidiInputDevice mid = (MidiInputDevice)cbMidiDevice.getSelectedItem(); if(!isUpdate()) { if(mid != null) getModel().setBackendMidiInputDevice(mid.getDeviceId()); return; } cbMidiPort.removeAllItems(); if(mid == null) { cbMidiPort.setEnabled(false); cbMidiChannel.setSelectedItem(null); cbMidiChannel.setEnabled(false); } else { cbMidiPort.setEnabled(true); MidiPort[] ports = mid.getMidiPorts(); for(MidiPort port : ports) cbMidiPort.addItem(port); int p = getModel().getChannelInfo().getMidiInputPort(); cbMidiPort.setSelectedItem(p >= 0 && p < ports.length ? ports[p] : null); cbMidiChannel.setEnabled(true); int c = getModel().getChannelInfo().getMidiInputChannel(); cbMidiChannel.setSelectedItem(c == -1 ? "All" : String.valueOf(c + 1)); } } private void setMidiPort() { if(isUpdate()) return; getModel().setBackendMidiInputPort(cbMidiPort.getSelectedIndex()); } private void setMidiChannel() { if(isUpdate()) return; Object o = cbMidiChannel.getSelectedItem(); if(o == null) return; int c = o.toString().equals("All") ? -1 : Integer.parseInt(o.toString()) - 1; getModel().setBackendMidiInputChannel(c); } /** Invoked when the user selects an engine. */ private void setEngineType() { Object oldEngine = getModel().getChannelInfo().getEngine(); SamplerEngine newEngine = (SamplerEngine)cbEngines.getSelectedItem(); if(oldEngine != null) { if(oldEngine.equals(newEngine)) return; } else if(newEngine == null) return; getModel().setBackendEngineType(newEngine.getName()); } private void setAudioDevice() { if(isUpdate()) return; AudioOutputDevice dev = (AudioOutputDevice)cbAudioDevice.getSelectedItem(); if(dev != null) getModel().setBackendAudioOutputDevice(dev.getDeviceId()); } /** * Determines whether the currently processed changes are due to update. * @return true if the currently processed changes are due to update and * false if the currently processed changes are due to user input. */ private boolean isUpdate() { return update; } /** * Sets whether the currently processed changes are due to update. * @param b Specify true to indicate that the currently * processed changes are due to update; false * indicates that the currently processed changes are due to user input. */ private void setUpdate(boolean b) { update = b; } private final Handler handler = new Handler(); private Handler getHandler() { return handler; } private class Handler implements MidiDeviceListListener, ListListener, SamplerChannelListListener { /** * Invoked when a new MIDI device is created. * @param e A MidiDeviceListEvent * instance providing the event information. */ public void deviceAdded(MidiDeviceListEvent e) { cbMidiDevice.addItem(e.getMidiDeviceModel().getDeviceInfo()); } /** * Invoked when a MIDI device is removed. * @param e A MidiDeviceListEvent * instance providing the event information. */ public void deviceRemoved(MidiDeviceListEvent e) { cbMidiDevice.removeItem(e.getMidiDeviceModel().getDeviceInfo()); } /** * Invoked when a new audio device is created. * @param e An AudioDeviceListEvent * instance providing the event information. */ public void entryAdded(ListEvent e) { cbAudioDevice.addItem(e.getEntry().getDeviceInfo()); } /** * Invoked when an audio device is removed. * @param e An AudioDeviceListEvent * instance providing the event information. */ public void entryRemoved(ListEvent e) { cbAudioDevice.removeItem(e.getEntry().getDeviceInfo()); } /** * Invoked when a new sampler channel is created. * @param e A SamplerChannelListEvent * instance providing the event information. */ public void channelAdded(SamplerChannelListEvent e) { } /** * Invoked when a sampler channel is removed. * @param e A SamplerChannelListEvent * instance providing the event information. */ public void channelRemoved(SamplerChannelListEvent e) { // Some cleanup when the channel is removed. if(e.getChannelModel().getChannelId() == channelModel.getChannelId()) { CC.getSamplerModel().removeMidiDeviceListListener(getHandler()); CC.getSamplerModel().removeAudioDeviceListListener(getHandler()); } } } private class MapListListener implements ListListener { /** Invoked when a new MIDI instrument map is added to a list. */ public void entryAdded(ListEvent e) { //cbInstrumentMap.addItem(e.getEntry()); cbInstrumentMap.insertItemAt(e.getEntry(), cbInstrumentMap.getItemCount()); boolean b = getModel().getChannelInfo().getEngine() != null; if(b && !cbInstrumentMap.isEnabled()) cbInstrumentMap.setEnabled(true); } /** Invoked when a new MIDI instrument map is removed from a list. */ public void entryRemoved(ListEvent e) { cbInstrumentMap.removeItem(e.getEntry()); if(cbInstrumentMap.getItemCount() == 0) { cbInstrumentMap.setSelectedItem(noMap); cbInstrumentMap.setEnabled(false); } } } }