package jmslexamples.jsyn2; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import com.softsynth.jmsl.JMSL; import com.softsynth.jmsl.JMSLMixerContainer; import com.softsynth.jmsl.JMSLRandom; import com.softsynth.jmsl.MusicDevice; import com.softsynth.jmsl.MusicJob; import com.softsynth.jmsl.MusicShape; import com.softsynth.jmsl.jsyn2.JSynMusicDevice; import com.softsynth.jmsl.jsyn2.JSynUnitVoiceInstrument; import com.softsynth.jmsl.jsyn2.unitvoices.SmoothFilteredSawtoothBL; /** * This examples shows how to use Instrument on(), update() and off() to start a * sound, update its timbre over time, then shut it down. Uses a MusicJob * subclass to implement this by calling on() in start(), update() in repeat(), * and off() in stop() * * Notice that this sounds smooth even when update()s sends abruptly changing * values because the circuit has exponential lags before some of its input * ports. * * This JSyn2 version uses the unique voice token returned by on() to properly * reference the allocated voice for update() and off(). Updated to JSyn2 Dec 2016 * * @author Nick Didkovsky, (c) 2004 All rights reserved, Email: * nick@didkovsky.com * */ public class SimpleTimbralEvolution extends JFrame { JMSLMixerContainer mixer; public void build() { JMSL.clock.setAdvance(0.3); MusicDevice dev = JSynMusicDevice.instance(); dev.edit(new java.awt.Frame()); dev.open(); mixer = new JMSLMixerContainer(); mixer.start(); makeAndLaunchJob(30); makeAndLaunchJob(30.5); add(mixer.getPanAmpControlPanel()); } int alternatingPan = 0; private void makeAndLaunchJob(double pitch) { JSynUnitVoiceInstrument ins = new JSynUnitVoiceInstrument(1, SmoothFilteredSawtoothBL.class.getName()); mixer.addInstrument(ins, alternatingPan, 0.9); TimbreJob job = new TimbreJob(); job.setInstrument(ins); job.setPitch(pitch); job.setRepeats(100); job.setRepeatPause(1.0); job.launch(JMSL.now() + JMSLRandom.choose(2.0)); alternatingPan = alternatingPan == 0 ? 1: 0; } public void stop() { JMSL.closeMusicDevices(); } public static void main(String[] args) { SimpleTimbralEvolution jf = new SimpleTimbralEvolution(); jf.setSize(600, 200); jf.build(); jf.setVisible(true); jf.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { jf.stop(); jf.setVisible(false); System.exit(0); } }); } } class TimbreJob extends MusicJob { // hold a copy of data to change on each repeat() double[] data; double pitch = 60; int myVoiceToken; public void setPitch(double pitch) { this.pitch = pitch; } public double start(double playTime) { System.out.print(getName() + " start() calling instrument.on() " + playTime + " with data: "); if (getInstrument() != null && getInstrument().getDimensionNameSpace() != null) { data = MusicShape.getDefaultArray(getInstrument().getDimensionNameSpace()); int indexOfPitch = getInstrument().getDimensionNameSpace().getDimension("pitch"); data[indexOfPitch] = pitch; Object obj = getInstrument().on(playTime, 1.0, data); myVoiceToken = ((Integer) obj).intValue(); JMSL.printDoubleArray(data); System.out.println("My unique voice token is " + myVoiceToken); } return playTime; } public double repeat(double playTime) { System.out.print(getName() + " repeat() calling instrument.update() at playtime=" + playTime + ", data: "); if (data != null) { // update timbral dimensions only, which start at 4 for (int i = 4; i < data.length; i++) { double lowLimit = getInstrument().getDimensionNameSpace().getLowLimit(i); double highLimit = getInstrument().getDimensionNameSpace().getHighLimit(i); data[i] = JMSLRandom.choose(lowLimit, highLimit); } ((JSynUnitVoiceInstrument) getInstrument()).update(playTime, 1.0, data, myVoiceToken); JMSL.printDoubleArray(data); } return playTime; } public double stop(double playTime) { System.out.print(getName() + " stop() calling instrument.off() at playtime " + playTime); if (getInstrument() != null) { ((JSynUnitVoiceInstrument) getInstrument()).off(playTime, myVoiceToken); } return playTime; } }