/* * Created on June 26, 2004 * */ package jmslexamples.jsyn; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import jmslexamples.MidiNoteSimulatorCheckbox; import com.softsynth.jmsl.*; import com.softsynth.jmsl.jsyn.JSynMusicDevice; import com.softsynth.jmsl.jsyn.SynthNoteAllPortsInstrument; import com.softsynth.jmsl.midi.*; import com.softsynth.jsyn.circuits.FilteredSawtoothBL; /** * Play a JSyn with MIDI . * * NoteOn's play a SynthNoteAllPortsInstrument * * MIDI Control ID's assigned to DimensionNameSpace for timbral control. * * Play a note or a chord on your MIDI synth and move your MIDI hardware's control faders to hear changes. * * Substitute your own SynthNote for FilteredSawtoothBL, recompile and run. * * Have fun. * * @author Nick Didkovsky, email: didkovn@mail.rockefeller.edu, (c) 2004 Nick Didkovsky, all rights reserved. * */ public class JSynMIDIPlayer extends Panel implements MidiListener { // prints debug data private boolean DBUG = false; // keep track on notes played to send update() to all sounding notes when control fader is moved private boolean[] notesOn = new boolean[128]; // common data to be changed by incoming notes and control changes private double[] data; // SynthNoteAllPortsInstrumet private Instrument instrument; // The instrument's dimension name space private DimensionNameSpace dimensionNameSpace; // provide user with gui to assign control id's to dimensions private TextField[] dimensionControlTextFields; // labels to report dimension value changes as MIDI controls change private Label[] dimensionValueLabels; public JSynMIDIPlayer() { setLayout(new BorderLayout()); } /** This method clones data[] field and then sets the pitch. This clone is passed to instrument.on() and update() * Cannot just send the one and only data[] array to instrument.play() because JSyn Instruments * use a hashtable of double[] to look up which allocated synthnote to turn off or update() later. * So each double[] must be a new object . */ private double[] cloneDataSetPitch(double pitch) { double[] dataClone = cloneData(); dataClone[1] = pitch; return dataClone; } /** @return copy of data[] */ private double[] cloneData() { double[] dataClone = new double[data.length]; System.arraycopy(data, 0, dataClone, 0, data.length); return dataClone; } /** Set Instrument * Follow this with a call to buildFromInstrument */ public void setInstrument(Instrument instrument) { this.instrument = instrument; } /** Store instrument's dimension name space, build compatible double[], build compatible labels and textfields */ public void buildFromInstrument() { dimensionNameSpace = instrument.getDimensionNameSpace(); data = new double[dimensionNameSpace.dimension()]; for (int i = 0; i < data.length; i++) { data[i] = dimensionNameSpace.getDefault(i); } // provide MIDI control assignments for dimensions higher than the first standard 4 if (dimensionNameSpace.dimension() > 4) { int numUpperDimensions = dimensionNameSpace.dimension() - 4; dimensionControlTextFields = new TextField[numUpperDimensions]; dimensionValueLabels = new Label[numUpperDimensions]; Panel p = new Panel(); p.setLayout(new GridLayout(0, 3)); p.add(new Label("Dimension")); p.add(new Label("Current Value")); p.add(new Label("MIDI Control ID")); for (int i = 0; i < dimensionControlTextFields.length; i++) { int dimension = 4 + i; double defaultValue = dimensionNameSpace.getDefault(dimension); String dimName = dimensionNameSpace.getDimensionName(dimension); dimensionControlTextFields[i] = new TextField(i + 1 + ""); dimensionValueLabels[i] = new Label(defaultValue + ""); p.add(new Label(dimName)); p.add(dimensionValueLabels[i]); p.add(dimensionControlTextFields[i]); } add(BorderLayout.NORTH, p); } } public void handleNoteOn(double timeStamp, int channel, int pitch, int velocity) { if (instrument != null) { double[] dataClone = cloneDataSetPitch(pitch); if (velocity == 0) { handleNoteOff(timeStamp, channel, pitch, 0); } else { setAmp(dataClone, velocity / 127.0); instrument.on(JMSL.realTime(), 1.0, dataClone); notesOn[pitch] = true; } } } public void handleNoteOff(double timeStamp, int channel, int pitch, int velocity) { if (pitch != 0) { double[] dataClone = cloneDataSetPitch(pitch); setAmp(dataClone, 0); instrument.off(JMSL.realTime(), 1.0, dataClone); notesOn[pitch] = false; } } private void setAmp(double[] dataClone, double amp) { dataClone[2] = amp; } public void handlePolyphonicAftertouch(double timeStamp, int channel, int pitch, int pressure) { } public void handleControlChange(double timeStamp, int channel, int id, int value) { if (DBUG) System.out.println("MIDI Control id=" + id); boolean anyChanges = false; for (int i = 0; i < dimensionControlTextFields.length; i++) { try { int controlID = new Integer(dimensionControlTextFields[i].getText()).intValue(); if (DBUG) System.out.println(controlID); if (controlID == id) { int dimension = i + 4; double valueScaledToDimension = value / 127.0 * (dimensionNameSpace.getHighLimit(dimension) - dimensionNameSpace.getLowLimit(dimension)) + dimensionNameSpace.getLowLimit(dimension); if (DBUG) System.out.println(dimension + ", " + valueScaledToDimension); data[dimension] = valueScaledToDimension; dimensionValueLabels[i].setText(valueScaledToDimension + ""); anyChanges = true; } } catch (NumberFormatException e) { System.err.println("Bad control number " + e); } } if (anyChanges) { for (int i = 0; i < notesOn.length; i++) { if (notesOn[i]) { instrument.update(JMSL.realTime(), 1.0, cloneDataSetPitch(i)); } } } else { if (DBUG) System.out.println("no changes"); } } public void handleProgramChange(double timeStamp, int channel, int program) { } public void handleChannelAftertouch(double timeStamp, int channel, int pressure) { } public void handlePitchBend(double timeStamp, int channel, int lsb, int msb) { } public void handleSysEx(double timeStamp, byte[] data) { } public static void main(String args[]) { Frame f = new Frame("Play JSyn with MIDI, (c) 2004 Nick Didkovsky "); f.setLayout(new BorderLayout(10, 20)); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { JMSL.closeMusicDevices(); System.exit(0); } }); JSynMIDIPlayer jsynMIDIPlayer = new JSynMIDIPlayer(); f.add(BorderLayout.NORTH, jsynMIDIPlayer); // open JSyn music device JSynMusicDevice dev = JSynMusicDevice.instance(); dev.edit(f); dev.open(); // open midi music device JMSL.midi = MidiIO_MidiShare.instance(); JMSL.midi.edit(f); JMSL.midi.open(); MidiParser parser = new MidiParser(); JMSL.midi.addMidiParser(parser); parser.addMidiListener(jsynMIDIPlayer); // CHANGE THIS TO YOUR OWN SYNTHNOTE CLASSNAME String synthNoteClassName = FilteredSawtoothBL.class.getName(); SynthNoteAllPortsInstrument ins1 = new SynthNoteAllPortsInstrument(8, synthNoteClassName); jsynMIDIPlayer.setInstrument(ins1); jsynMIDIPlayer.buildFromInstrument(); JMSLMixerContainer mixer = new JMSLMixerContainer(); mixer.start(); mixer.addInstrument(ins1, 0.5, 0.6); f.add(BorderLayout.SOUTH, mixer.getPanAmpControlPanel()); f.pack(); f.setVisible(true); Frame midiSimFrame = new Frame("Simulate MIDI notes"); midiSimFrame.setLayout(new GridLayout(16, 0)); for (int i = 1; i < 128; i++) { midiSimFrame.add(new MidiNoteSimulatorCheckbox(i + "", i, jsynMIDIPlayer)); } midiSimFrame.pack(); midiSimFrame.setLocation(new Point(f.getLocation().x + f.getWidth(), f.getLocation().y)); midiSimFrame.setVisible(true); } }