package jmslexamples; import java.awt.*; import java.awt.event.*; import com.softsynth.jmsl.*; import com.softsynth.jmsl.midi.MidiIO_MidiShare; import com.softsynth.jmsl.midi.MidiInstrument; import com.softsynth.jmsl.view.JMSLScrollbar; import com.softsynth.jmsl.view.JMSLScrollbarProcessor; /** Control the speed of a ParallelCollection. The collection has two MusicShapes in it: one plays a riff twice as fast as the other. In order to stay in synch, the faster one has a repeat count of 2, against a repeat count of 1 for the slower MusicShape. Note that when you change the timestretch in the second half of the pattern (ie during the seocnd repeat of the faster MusicShape, the collection stays in synch. If you change timeStretch in the first half (ie during the first repeat of the faster riff), the collection will play out-of-synch, though accurately. Why? This is because the faster MusicShape gets the new timeStretch value on its second repeat, and plays this secoind repeat at the new timeStretch speed, while the slower one is still playing through its current repeat at the old timeStretch speed. @author Nick Didkovsky, 1/21/99 4:54AM CalArts, copyright (c) 1999, Nick Didkovsky */ public class ParallelMidiShapes extends Frame implements ActionListener, JMSLScrollbarProcessor { private Button startButton; private Button stopButton; private ParallelCollection parCol; private JMSLScrollbar speedyScrollbar; private Label speedyLabel; private Button speedyButton; private double dur = 1.0; private int numShapes = 2; public ParallelMidiShapes() { setLayout(new BorderLayout()); Panel p = new Panel(); p.setLayout(new GridLayout(0, 1)); speedyLabel = new Label(durString()); speedyScrollbar = new JMSLScrollbar(this, (int) dur * 1000, 10, 6000); // starting value, min, max // set the size of the scrollbar speedyScrollbar.setSize(320, 25); // set the increment that the value will jump when user clicks inside scrollbar speedyScrollbar.setPageIncrement(100); speedyButton = new Button("Set Time Stretch"); p.add(speedyLabel); p.add(speedyScrollbar); p.add(speedyButton); add("North", p); p = new Panel(); p.add(startButton = new Button("Start")); p.add(stopButton = new Button("Stop")); add("South", p); startButton.addActionListener(this); stopButton.addActionListener(this); speedyButton.addActionListener(this); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { JMSL.closeMusicDevices(); System.exit(0); } }); pack(); buildParallelMidiShapes(numShapes); } /** Build a parallel collection of shapes played by midi, each new shape twice as fast as previous. */ void buildParallelMidiShapes(int numShapes) { parCol = new ParallelCollection(); for (int i = 0; i < numShapes; i++) { MusicShape s = new MusicShape(4); fillShape(s, i); s.setInstrument(new MidiInstrument(1)); parCol.add(s); } } void fillShape(MusicShape s, int i) { double dur = Math.pow(0.5, i); s.add(dur, 55 + 3 * i, 120, dur * 0.75); s.add(dur, 58 + 3 * i, 110, dur * 0.75); s.add(dur, 63 + 3 * i, 100, dur * 0.75); s.add(dur, 69 + 3 * i, 95, dur * 0.75); s.setRepeats(100 * (int) (1.0 / dur)); } String durString() { return "Time Stretch: " + dur; } void handleSpeedyChange() { dur = speedyScrollbar.getValue() / 1000.0; speedyLabel.setText(durString()); } /** Any class using a JMSLScrollbar must implement JMSLScrollbarProcessor by defining this method */ public void JMSLScrollbarValueChanged(JMSLScrollbar jsb) { if (jsb == speedyScrollbar) { handleSpeedyChange(); } } void handleSpeedyButton() { parCol.setTimeStretch(dur); } void handleStart() { parCol.finishAll(); parCol.launch(JMSL.now()); } void handleStop() { parCol.finishAll(); } public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source == speedyButton) handleSpeedyButton(); if (source == startButton) handleStart(); if (source == stopButton) handleStop(); } public static void main(String args[]) { ParallelMidiShapes gui = new ParallelMidiShapes(); MusicDevice dev = MidiIO_MidiShare.instance(); dev.edit(gui); dev.open(); gui.setVisible(true); } }