package jmslexamples.jsyn; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import com.didkovsky.portview.PVButton; import com.didkovsky.portview.PVPanel; import com.softsynth.jmsl.*; import com.softsynth.jmsl.jsyn.JSynMusicDevice; import com.softsynth.jmsl.jsyn.SynthNoteAllPortsInstrument; import com.softsynth.jmsl.view.PVFrameAdapter; import com.softsynth.jmsl.view.PVPanelAdapter; /** * * VERY IMPORTANT EXAMPLE FOR JMSL/JSyn interaction!!!! * * Create a MusicShape with an Instrument that controls the various input ports * on some JSyn SynthNote. * * SynthNoteAllPortsInstrument sniffs out all the SynthNote's input ports and * creates a DimensionNameSpace for itself. * * A DimensionNameSapce is a mapping between Dimension indexes 0, 1, 2, 3... and * names like "duration", "pitch", "amplitude", "hold", "modIndex", "cutoff", * etc... * * You can build a MusicShape and add data to it by finding out information * about this DimensionNameSpace * * @author Nick Didkovsky, Dec 15, 2002 */ public class AlgorithmicTimbreDemo extends PVFrameAdapter implements ActionListener { JMSLMixerContainer mixer; SynthNoteAllPortsInstrument ins; // Named "naive" because it gets its dimension and dimension names from ins MusicShape naiveMusicShape; com.softsynth.jmsl.view.MusicShapeEditor shapeEditor; PVButton startButton; PVButton stopButton; public AlgorithmicTimbreDemo() { super("Algorithmic Timbre Demo (c) 2002 Nick Didkovsky, Closebox to quit"); setFrameLayout(new java.awt.BorderLayout()); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { JMSL.closeMusicDevices(); System.exit(0); } }); } void buildGUI() { PVPanel p = new PVPanelAdapter(); p.setLayout(new FlowLayout()); p.add((startButton = JMSL.getViewFactory().createButton("Start")).getComponent()); p.add((stopButton = JMSL.getViewFactory().createButton("Stop")).getComponent()); startButton.addActionListener(this); stopButton.addActionListener(this); stopButton.setEnabled(false); add(BorderLayout.SOUTH, p.getComponent()); add(BorderLayout.NORTH, mixer.getPanAmpControlPanel()); add(BorderLayout.CENTER, shapeEditor.getComponent()); } void initMusicDevices() { JMSL.clock.setAdvance(0.1); JSynMusicDevice.instance().edit((Frame) this.getComponent()); JSynMusicDevice.instance().open(); } void buildInstruments() { // Name the SynthNote you want String synthNoteClassName = com.softsynth.jsyn.circuits.FilteredSawtoothBL.class.getName(); ins = new SynthNoteAllPortsInstrument(); ins.setMaxVoices(8); ins.setSynthNoteClassName(synthNoteClassName); ins.buildFromAttributes(); // at this point, ins has a DimensionNameSpace derived from the // synthnote } void buildMixer() { mixer = new JMSLMixerContainer(); mixer.start(); mixer.addInstrument(ins); } void buildMusicShape() { // Now build a MusicShape with DimensionNameSpace taken from instrument naiveMusicShape = new MusicShape(ins.getDimensionNameSpace()); naiveMusicShape.setInstrument(ins); // Now let's add data to the MusicShape for the Instrument to play() // We use one set of algorithms for the "standard" dimenions 0..3, which // stand for // duration, pitch, amplitude, and hold. Use another algorithm to // generate data // for non-standard dimensions 4 and higher... for (int i = 0; i < 17; i++) { // this array of double will be added to the MusicShape. Allocate // and fill with data double[] dar = new double[naiveMusicShape.dimension()]; double duration = JMSLRandom.choose(0.125, 1.5); double pitch = JMSLRandom.choose(60.0 + i, 72.0 + i); double amplitude = JMSLRandom.choose(0.2, 0.7); double hold = JMSLRandom.choose(2.0) * duration; // We know dimensions 0, 1, 2, 3 are for duration, note, amplitude, // and hold. // These 4 are invariants that must be respected in order to use // SynthNoteAllPortsInstrument // but we take advantage of this opportunity to show that you could // query for these indexes by name! dar[ins.getDimensionNameSpace().getDimension("duration")] = duration; dar[ins.getDimensionNameSpace().getDimension("pitch")] = pitch; dar[ins.getDimensionNameSpace().getDimension("amplitude")] = amplitude; dar[ins.getDimensionNameSpace().getDimension("hold")] = hold; // Now randomize data for dimensions 4 and higher for (int j = 4; j < naiveMusicShape.dimension(); j++) { // without even knowing the meanings of these higher dimensions, // we can enter random values by // randomizing between their low and high limits double lowLimit = ins.getDimensionNameSpace().getLowLimit(j); double highLimit = ins.getDimensionNameSpace().getHighLimit(j); double value = JMSLRandom.choose(lowLimit, highLimit); dar[j] = value; } naiveMusicShape.add(dar); } naiveMusicShape.setRepeats(1000); } void buildMusicShapeEditor() { shapeEditor = new com.softsynth.jmsl.view.MusicShapeEditor(); shapeEditor.addMusicShape(naiveMusicShape); } public void actionPerformed(ActionEvent ev) { Object source = ev.getSource(); if (source == startButton) { naiveMusicShape.launch(JMSL.now()); stopButton.setEnabled(true); startButton.setEnabled(false); } if (source == stopButton) { naiveMusicShape.finishAll(); stopButton.setEnabled(false); startButton.setEnabled(true); } } public static void main(String args[]) { JMSL.setViewFactory(new com.didkovsky.portview.swing.ViewFactorySwing()); AlgorithmicTimbreDemo algorithmicTimbreDemo = new AlgorithmicTimbreDemo(); algorithmicTimbreDemo.initMusicDevices(); algorithmicTimbreDemo.buildInstruments(); algorithmicTimbreDemo.buildMixer(); algorithmicTimbreDemo.buildMusicShape(); algorithmicTimbreDemo.buildMusicShapeEditor(); algorithmicTimbreDemo.buildGUI(); algorithmicTimbreDemo.pack(); algorithmicTimbreDemo.setVisible(true); } }