gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

PmDefaultsFrame.java (15543B)


      1 // PmDefaults -- a small application to set PortMIDI default input/output
      2 
      3 /* Implementation notes:
      4 
      5 This program uses PortMidi to enumerate input and output devices and also
      6 to send output messages to test output devices and 
      7 to receive input messages to test input devices.
      8 
      9 */
     10 
     11 package pmdefaults;
     12 
     13 import javax.swing.*;
     14 import java.awt.*;
     15 import java.awt.event.*;
     16 import java.lang.Math.*;
     17 import jportmidi.*;
     18 import jportmidi.JPortMidiApi.*;
     19 import java.util.ArrayList;
     20 import java.util.prefs.*;
     21 import java.net.*;
     22 
     23 public class PmDefaultsFrame extends JFrame 
     24         implements ActionListener, ComponentListener {
     25 
     26     // This class extends JPortMidi in order to override midi input handling
     27     // In this case, midi input simply blinks the activity light
     28     public class JPM extends JPortMidi {
     29         ActivityLight light;
     30         PmDefaultsFrame frame;
     31         int lightTime;
     32         boolean lightState;
     33         int now; // current time in ms
     34         final int HALF_BLINK_PERIOD = 250; // ms
     35 
     36         public JPM(ActivityLight al, PmDefaultsFrame df) 
     37                 throws JPortMidiException {
     38             light = al;
     39             frame = df;
     40             lightTime = 0;
     41             lightState = false; // meaning "off"
     42             now = 0;
     43         }
     44         
     45         public void poll(int ms) throws JPortMidiException {
     46             // blink the light. lightState is initially false (off).
     47             // to blink the light, set lightState to true and lightTime
     48             // to now + 0.25s; turn on light
     49             // now > ligntTime && lightState => set lightTime = now + 0.25s;
     50             //                                  set lightState = false
     51             //                                  turn off light
     52             // light can be blinked again when now > lightTime && !lightState
     53             now = ms;
     54             if (now > lightTime && lightState) {
     55                 lightTime = now + HALF_BLINK_PERIOD;
     56                 lightState = false;
     57                 light.setState(false);
     58             }
     59             super.poll();
     60         }
     61 
     62         public void handleMidiIn(PmEvent buffer) {
     63             System.out.println("midi in: now " + now + 
     64                                " lightTime " + lightTime +
     65                                " lightState " + lightState);
     66             if (now > lightTime && !lightState) {
     67                 lightState = true;
     68                 lightTime = now + HALF_BLINK_PERIOD;
     69                 light.setState(true);
     70             }
     71         }
     72     }
     73 
     74     public class ActivityLight extends JPanel {
     75         Color color;
     76         final Color OFF_COLOR = Color.BLACK;
     77         final Color ON_COLOR = Color.GREEN;
     78 
     79         ActivityLight() {
     80             super();
     81             Dimension size = new Dimension(50, 25);
     82             setMaximumSize(size);
     83             setPreferredSize(size);
     84             setMinimumSize(size);
     85             color = OFF_COLOR;
     86             System.out.println("ActivityLight " + getSize());
     87         }
     88 
     89         public void setState(boolean on) {
     90             color = (on ? ON_COLOR : OFF_COLOR);
     91             repaint();
     92         }
     93 
     94         public void paintComponent(Graphics g) {
     95             super.paintComponent(g); // paint background
     96             g.setColor(color);
     97             int x = (getWidth() / 2) - 5;
     98             int y = (getHeight() / 2) - 5;
     99             g.fillOval(x, y, 10, 10);
    100             g.setColor(OFF_COLOR);
    101             g.drawOval(x, y, 10, 10);
    102         }
    103     }
    104 
    105     private JLabel inputLabel;
    106     private JComboBox inputSelection;
    107     // inputIds maps from the index of an item in inputSelection to the
    108     // device ID. Used to open the selected device.
    109     private ArrayList<Integer> inputIds;
    110     private ActivityLight inputActivity;
    111     private JLabel outputLabel;
    112     private JComboBox outputSelection;
    113     // analogous to inputIds, outputIds maps selection index to device ID
    114     private ArrayList<Integer> outputIds;
    115     private JButton testButton;
    116     private JButton refreshButton;
    117     private JButton updateButton;
    118     private JButton closeButton;
    119     private JLabel logo;
    120 
    121     private JPM jpm;
    122     private Timer timer;
    123 
    124     public void componentResized(ComponentEvent e) {
    125         System.out.println(e);
    126         if (e.getComponent() == this) {
    127             Insets insets = getInsets();
    128             Dimension dim = getSize();
    129             layoutComponents(dim.width - insets.left - insets.right,
    130                              dim.height - insets.top - insets.bottom);
    131         }
    132     }
    133     public void componentMoved(ComponentEvent e) {
    134         System.out.println(e);
    135     }
    136     public void componentHidden(ComponentEvent e) {
    137         System.out.println(e);
    138     }
    139     public void componentShown(ComponentEvent e) {
    140         System.out.println(e);
    141     }
    142 
    143     
    144     PmDefaultsFrame(String title) {
    145         super(title);
    146         initComponents();
    147         System.out.println("initComponents returned\n");
    148         pack(); // necessary before calling getInsets();
    149         // initially, only width matters to layout:
    150         layoutComponents(550, 300);
    151         System.out.println("after layout, pref " + getPreferredSize());
    152         // now, based on layout-computed preferredSize, set the Frame size
    153         Insets insets = getInsets();
    154         Dimension dim = getPreferredSize();
    155         dim.width += (insets.left + insets.right);
    156         dim.height += (insets.top + insets.bottom);
    157         setSize(dim);
    158         System.out.println("size" + getPreferredSize());
    159         addComponentListener(this);
    160 
    161         timer = new Timer(50 /* ms */, this);
    162         timer.addActionListener(this);
    163         try {
    164             jpm = new JPM(inputActivity, this);
    165             jpm.setTrace(true);
    166             loadDeviceChoices();
    167             timer.start(); // don't start timer if there's an error
    168         } catch(JPortMidiException e) {
    169             System.out.println(e);
    170         }
    171     }
    172 
    173     void openInputSelection() {
    174         int id = inputSelection.getSelectedIndex();
    175         if (id < 0) return; // nothing selected
    176         id = (Integer) (inputIds.get(id)); // map to device ID
    177         // openInput will close any previously open input stream
    178         try {
    179             jpm.openInput(id, 100); // buffer size hopes to avoid overflow
    180         } catch(JPortMidiException e) {
    181             System.out.println(e);
    182         }
    183     }
    184 
    185     // make a string to put into preferences describing this device
    186     String makePrefName(int id) {
    187         String name = jpm.getDeviceName(id);
    188         String interf = jpm.getDeviceInterf(id);
    189         // the syntax requires comma-space separator (see portmidi.h)
    190         return interf + ", " + name;
    191     }
    192 
    193 
    194     public void savePreferences() {
    195         Preferences prefs = Preferences.userRoot().node("/PortMidi");
    196         int id = outputSelection.getSelectedIndex();
    197         if (id >= 0) {
    198             String prefName = makePrefName(outputIds.get(id));
    199             System.out.println("output pref: " + prefName);
    200             prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName);
    201         }
    202         id = inputSelection.getSelectedIndex();
    203         if (id >= 0) {
    204             String prefName = makePrefName(inputIds.get(id));
    205             System.out.println("input pref: " + prefName);
    206             prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName);
    207         }
    208         try {
    209             prefs.flush();
    210         } catch(BackingStoreException e) {
    211             System.out.println(e);
    212         }
    213     }
    214 
    215     public void actionPerformed(ActionEvent e) {
    216         Object source = e.getSource();
    217         try {
    218             if (source == timer) {
    219                 jpm.poll(jpm.timeGet());
    220             } else if (source == refreshButton) {
    221                 if (jpm.isOpenInput()) jpm.closeInput();
    222                 if (jpm.isOpenOutput()) jpm.closeOutput();
    223                 jpm.refreshDeviceLists();
    224                 loadDeviceChoices();
    225             } else if (source == updateButton) {
    226                 savePreferences();
    227             } else if (source == closeButton) {
    228                 if (jpm.isOpenInput()) jpm.closeInput();
    229                 if (jpm.isOpenOutput()) jpm.closeOutput();
    230             } else if (source == testButton) {
    231                 sendTestMessages();
    232             } else if (source == inputSelection) {
    233                 // close previous selection and open new one
    234                 openInputSelection();
    235             } else if (source == outputSelection) {
    236                 jpm.closeOutput(); // remains closed until Test button reopens
    237             }
    238         } catch(JPortMidiException ex) {
    239             System.out.println(ex);
    240         }
    241     };
    242 
    243     private void layoutComponents(int width, int height) {
    244         // I tried to do this with various layout managers, but failed
    245         // It seems pretty straightforward to just compute locations and
    246         // sizes.
    247 
    248         int gap = 2; // pixel separation between components
    249         int indent = 20;
    250         int y = gap;
    251 
    252         // inputLabel goes in upper left
    253         inputLabel.setLocation(0, y);
    254         inputLabel.setSize(inputLabel.getPreferredSize());
    255 
    256         // inputSelection goes below and indented, width based on panel
    257         y += inputLabel.getHeight() + gap;
    258         inputSelection.setLocation(indent, y);
    259         // size of inputSelection must leave room at right for inputButton
    260         // (in fact, inputActivity goes there, but we'll make inputSelection
    261         // and outputSelection the same size, based on leaving room for
    262         // testButton, which is larger than inputActivity.)
    263         Dimension dim = inputSelection.getPreferredSize();
    264         Dimension dimButton = testButton.getPreferredSize();
    265         // make button and selection the same height so they align
    266         dim.height = dimButton.height = Math.max(dim.height, dimButton.height);
    267         // make selection width as wide as possible
    268         dim.width = width - indent - dimButton.width - gap;
    269         inputSelection.setSize(dim);
    270 
    271         // inputActivity goes to the right of inputSelection
    272         inputActivity.setLocation(indent + dim.width + gap, y);
    273         // square size to match the height of inputSelection
    274         inputActivity.setSize(dim.height, dim.height);
    275 
    276         // outputLabel goes below
    277         y += dim.height + gap;
    278         outputLabel.setLocation(0, y);
    279         outputLabel.setSize(outputLabel.getPreferredSize());
    280        
    281         // outputSelection is like inputSelection
    282         y += outputLabel.getHeight() + gap;
    283         outputSelection.setLocation(indent, y);
    284         outputSelection.setSize(dim);
    285 
    286         // testButton is like inputActivity
    287         testButton.setLocation(indent + dim.width + gap, y);
    288         testButton.setSize(dimButton);
    289         System.out.println("button " + dimButton + " selection " + dim);
    290 
    291         // refreshButton is below
    292         y += dim.height + gap;
    293         dim = refreshButton.getPreferredSize();
    294         refreshButton.setLocation(indent, y);
    295         refreshButton.setSize(dim);
    296 
    297         // updateButton to right of refreshButton
    298         int x = indent + dim.width + gap;
    299         updateButton.setLocation(x, y);
    300         dim = updateButton.getPreferredSize();
    301         updateButton.setSize(dim);
    302 
    303         // closeButton to right of updateButton
    304         x += dim.width + gap;
    305         closeButton.setLocation(x, y);
    306         dim = closeButton.getPreferredSize();
    307         closeButton.setSize(dim);
    308 
    309         // place logo centered at bottom
    310         y += dim.height + gap;
    311         logo.setLocation((width - logo.getWidth()) / 2, 
    312                          height - gap - logo.getHeight());
    313 
    314         // set overall size
    315         y += logo.getHeight() + gap;
    316         System.out.println("computed best size " + width + ", " + y);
    317         setPreferredSize(new Dimension(width, y));
    318     }
    319 
    320     private void initComponents() {
    321         Container wholePanel = getContentPane();
    322         wholePanel.setLayout(null);
    323         setLayout(null);
    324 
    325         inputLabel = new JLabel();
    326         inputLabel.setText("Default Input");
    327         wholePanel.add(inputLabel);
    328 
    329         inputSelection = new JComboBox();
    330         inputSelection.addActionListener(this);
    331         inputSelection.setLocation(20, 30);
    332         inputSelection.setSize(inputSelection.getPreferredSize());
    333         System.out.println("Adding inputSelection to panel");
    334         wholePanel.add(inputSelection);
    335         inputIds = new ArrayList<Integer>();
    336 
    337         inputActivity = new ActivityLight();
    338         wholePanel.add(inputActivity);
    339 
    340         outputLabel = new JLabel();
    341         outputLabel.setText("Default Output");
    342         wholePanel.add(outputLabel);
    343 
    344         outputSelection = new JComboBox();
    345         outputSelection.addActionListener(this);
    346         wholePanel.add(outputSelection);
    347         testButton = new JButton();
    348         testButton.setText("Test");
    349         testButton.addActionListener(this);
    350         wholePanel.add(testButton);
    351         outputIds = new ArrayList<Integer>();
    352 
    353         refreshButton = new JButton();
    354         refreshButton.setText("Refresh Device Lists");
    355         System.out.println("refresh " + refreshButton.getPreferredSize());
    356         System.out.println(getLayout());
    357         refreshButton.addActionListener(this);
    358         wholePanel.add(refreshButton);
    359 
    360         updateButton = new JButton();
    361         updateButton.setText("Update Preferences");
    362         updateButton.setSize(refreshButton.getPreferredSize());
    363         updateButton.addActionListener(this);
    364         wholePanel.add(updateButton);
    365 
    366         closeButton = new JButton();
    367         closeButton.setText("Close/Release Ports");
    368         closeButton.setSize(refreshButton.getPreferredSize());
    369         closeButton.addActionListener(this);
    370         wholePanel.add(closeButton);
    371 
    372 	// load the logo from the jar file (on Linux and Windows)
    373 	ClassLoader cldr = this.getClass().getClassLoader();
    374         ImageIcon icon;
    375         URL logoURL = cldr.getResource("portmusic_logo.png");
    376         if (logoURL == null) {
    377             // on Mac, load from bundle
    378             icon = new ImageIcon("portmusic_logo.png");
    379         } else {
    380             icon = new ImageIcon(logoURL);
    381         }
    382         logo = new JLabel(icon);
    383         logo.setSize(logo.getPreferredSize());
    384         wholePanel.add(logo);
    385 
    386         setVisible(true);
    387         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    388     }
    389 
    390     void loadDeviceChoices() throws JPortMidiException {
    391         // initialize and load combo boxes with device descriptions
    392         int n = jpm.countDevices();
    393         inputSelection.removeAllItems();
    394         inputIds.clear();
    395         outputSelection.removeAllItems();
    396         outputIds.clear();
    397         for (int i = 0; i < n; i++) {
    398             String interf = jpm.getDeviceInterf(i);
    399             String name = jpm.getDeviceName(i);
    400 	    System.out.println("name " + name);
    401             String selection = name + " [" + interf + "]";
    402             if (jpm.getDeviceInput(i)) {
    403                 inputIds.add(i);
    404                 inputSelection.addItem(selection);
    405             } else {
    406                 outputIds.add(i);
    407                 outputSelection.addItem(selection);
    408             }
    409         }
    410     }
    411 
    412     void sendTestMessages() {
    413         try {
    414             if (!jpm.isOpenOutput()) {
    415                 int id = outputSelection.getSelectedIndex();
    416                 if (id < 0) return; // nothing selected
    417                 id = (Integer) (outputIds.get(id));
    418                 System.out.println("calling openOutput");
    419                 jpm.openOutput(id, 10, 10);
    420             }
    421             jpm.midiNote(0, 67, 100); // send an A (440)
    422             jpm.midiNote(0, 67, 0, jpm.timeGet() + 500);
    423         } catch(JPortMidiException e) {
    424             System.out.println(e);
    425         }
    426     }
    427 }
    428