/* * Created on June 26, 2004 * */ package jmslexamples.jsyn2; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Point; import java.awt.TextField; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.util.Hashtable; import com.softsynth.jmsl.DimensionNameSpace; import com.softsynth.jmsl.Instrument; import com.softsynth.jmsl.JMSL; import com.softsynth.jmsl.JMSLMixerContainer; import com.softsynth.jmsl.jsyn2.JSynMusicDevice; import com.softsynth.jmsl.jsyn2.JSynUnitVoiceInstrument; import com.softsynth.jmsl.jsyn2.unitvoices.FilteredSawtoothBL; import com.softsynth.jmsl.midi.MidiIOFactory; import com.softsynth.jmsl.midi.MidiListener; import com.softsynth.jmsl.midi.MidiParser; import jmslexamples.MidiNoteSimulatorCheckbox; /** * Play a JSyn UnitVoice with MIDI . * * NoteOn's play a JSynUnitVoiceInstrument * * 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 UnitVoice for FilteredSawtoothBL, recompile and run. * * Have fun. * * upgraded to JSyn2 API Dec 2016 * * * @author Nick Didkovsky, email: nick@didkovsky.com, (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; 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); } } Hashtable midiNoteNumberToVoiceTokenHash = new Hashtable(); 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); Integer voiceToken = (Integer) instrument.on(JMSL.realTime(), 1.0, dataClone); midiNoteNumberToVoiceTokenHash.put(new Integer(pitch), voiceToken); notesOn[pitch] = true; } } } public void handleNoteOff(double timeStamp, int channel, int pitch, int velocity) { if (pitch != 0) { if (midiNoteNumberToVoiceTokenHash.containsKey(new Integer(pitch))) { int voiceToken = midiNoteNumberToVoiceTokenHash.get(new Integer(pitch)).intValue(); ((JSynUnitVoiceInstrument) instrument).off(JMSL.realTime(), voiceToken); } 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 = Integer.parseInt(dimensionControlTextFields[i].getText()); 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 try { JMSL.midi = new MidiIOFactory().getMidiIO(MidiIOFactory.MidiIO_JAVASOUND); 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 unitVoiceClassName = FilteredSawtoothBL.class.getName(); JSynUnitVoiceInstrument ins1 = new JSynUnitVoiceInstrument(8, unitVoiceClassName); 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); } catch (IOException e1) { e1.printStackTrace(); } } }