aboutsummaryrefslogtreecommitdiffstats
path: root/tests/com/jsyn/examples/NotesToTone.java
diff options
context:
space:
mode:
authorPhil Burk <[email protected]>2014-12-30 16:53:03 -0800
committerPhil Burk <[email protected]>2014-12-30 16:53:03 -0800
commit534969d42ca5168d645678345cd21242fe41f389 (patch)
treee8f5d1cba1ec57685e76ceb923d8da25a7846cfb /tests/com/jsyn/examples/NotesToTone.java
parenta4d8ca95178d2e3acfc3299a4b73e84c2646d24e (diff)
Initial commit of code.
Diffstat (limited to 'tests/com/jsyn/examples/NotesToTone.java')
-rw-r--r--tests/com/jsyn/examples/NotesToTone.java214
1 files changed, 214 insertions, 0 deletions
diff --git a/tests/com/jsyn/examples/NotesToTone.java b/tests/com/jsyn/examples/NotesToTone.java
new file mode 100644
index 0000000..9186087
--- /dev/null
+++ b/tests/com/jsyn/examples/NotesToTone.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2010 Phil Burk, Mobileer Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * If you play notes fast enough they become a tone.
+ *
+ * Play a sine wave modulated by an envelope.
+ * Speed up the envelope until it is playing at audio rate.
+ * Slow down the oscillator until it becomes an LFO amp modulator.
+ * Use a LatchZeroCrossing to stop at the end of a sine wave cycle when we are finished.
+ *
+ * @author Phil Burk, (C) 2010 Mobileer Inc
+ */
+
+package com.jsyn.examples;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import com.jsyn.data.SegmentedEnvelope;
+import com.jsyn.unitgen.ExponentialRamp;
+import com.jsyn.unitgen.LatchZeroCrossing;
+import com.jsyn.unitgen.LineOut;
+import com.jsyn.unitgen.SineOscillator;
+import com.jsyn.unitgen.UnitOscillator;
+import com.jsyn.unitgen.VariableRateDataReader;
+import com.jsyn.unitgen.VariableRateMonoReader;
+import com.jsyn.util.WaveRecorder;
+
+/**
+ * When notes speed up they can become a new tone. <br>
+ * Multiply an oscillator and an envelope. Speed up the envelope until it becomes a tone. Slow down
+ * the oscillator until it acts like an envelope. Write the resulting audio to a WAV file.
+ *
+ * @author Phil Burk (C) 2011 Mobileer Inc
+ */
+
+public class NotesToTone {
+ private final static double SONG_AMPLITUDE = 0.7;
+ private final static double INTRO_DURATION = 2.0;
+ private final static double OUTRO_DURATION = 2.0;
+ private final static double RAMP_DURATION = 20.0;
+ private final static double LOW_FREQUENCY = 1.0;
+ private final static double HIGH_FREQUENCY = 800.0;
+
+ private final static boolean useRecorder = true;
+ private WaveRecorder recorder;
+
+ private Synthesizer synth;
+ private ExponentialRamp envSweeper;
+ private ExponentialRamp oscSweeper;
+ private VariableRateDataReader envelopePlayer;
+ private UnitOscillator osc;
+ private LatchZeroCrossing latch;
+ private LineOut lineOut;
+ private SegmentedEnvelope envelope;
+
+ private void play() throws IOException {
+ synth = JSyn.createSynthesizer();
+ synth.setRealTime(true);
+
+ if (useRecorder) {
+ File waveFile = new File("notes_to_tone.wav");
+ // Default is stereo, 16 bits.
+ recorder = new WaveRecorder(synth, waveFile, 1);
+ System.out.println("Writing to WAV file " + waveFile.getAbsolutePath());
+ }
+
+ createUnits();
+
+ connectUnits();
+
+ setupEnvelope();
+
+ osc.amplitude.set(SONG_AMPLITUDE);
+
+ // Ramp the rate of the envelope up until it becomes an audible tone.
+ envSweeper.current.set(LOW_FREQUENCY);
+ envSweeper.input.set(LOW_FREQUENCY);
+ envSweeper.time.set(RAMP_DURATION);
+
+ // Ramp the rate of the oscillator down until it becomes an LFO.
+ oscSweeper.current.set(HIGH_FREQUENCY);
+ oscSweeper.input.set(HIGH_FREQUENCY);
+ oscSweeper.time.set(RAMP_DURATION);
+
+ // Start synthesizer using default stereo output at 44100 Hz.
+ synth.start();
+
+ // When we start the recorder it will pull data from the oscillator and
+ // sweeper.
+ if (recorder != null) {
+ recorder.start();
+ }
+
+ // We also need to start the LineOut if we want to hear it now.
+ lineOut.start();
+
+ // Get synthesizer time in seconds.
+ double timeNow = synth.getCurrentTime();
+
+ // Schedule start of ramps.
+ double songDuration = INTRO_DURATION + RAMP_DURATION + OUTRO_DURATION;
+ envSweeper.input.set(HIGH_FREQUENCY, timeNow + INTRO_DURATION);
+ oscSweeper.input.set(LOW_FREQUENCY, timeNow + INTRO_DURATION);
+
+ // Arm zero crossing latch
+ latch.gate.set(0.0, timeNow + songDuration);
+
+ // Sleep while the sound is being generated in the background thread.
+ try {
+ synth.sleepUntil(timeNow + songDuration + 2.0);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if (recorder != null) {
+ recorder.stop();
+ recorder.close();
+ }
+ // Stop everything.
+ synth.stop();
+ }
+
+ private void createUnits() {
+ // Add a tone generators.
+ synth.add(osc = new SineOscillator());
+ // Add a controller that will sweep the envelope rate up.
+ synth.add(envSweeper = new ExponentialRamp());
+ // Add a controller that will sweep the oscillator down.
+ synth.add(oscSweeper = new ExponentialRamp());
+
+ synth.add(latch = new LatchZeroCrossing());
+ // Add an output unit.
+ synth.add(lineOut = new LineOut());
+
+ // Add an envelope player.
+ synth.add(envelopePlayer = new VariableRateMonoReader());
+ }
+
+ private void connectUnits() {
+ oscSweeper.output.connect(osc.frequency);
+ osc.output.connect(latch.input);
+ // Latch when sine LFO crosses zero.
+ latch.output.connect(envelopePlayer.amplitude);
+
+ envSweeper.output.connect(envelopePlayer.rate);
+
+ // Connect the envelope player to the audio output.
+ envelopePlayer.output.connect(0, lineOut.input, 0);
+ // crossFade.output.connect( 0, lineOut.input, 1 );
+
+ if (recorder != null) {
+ envelopePlayer.output.connect(0, recorder.getInput(), 0);
+ // crossFade.output.connect( 0, recorder.getInput(), 1 );
+ }
+ }
+
+ private void setupEnvelope() {
+ // Setup envelope. The envelope has a total duration of 1.0 seconds.
+ // Values are (duration,target) pairs.
+ double[] pairs = new double[5 * 2 * 2];
+ int i = 0;
+ // duration, target for delay
+ pairs[i++] = 0.15;
+ pairs[i++] = 0.0;
+ // duration, target for attack
+ pairs[i++] = 0.05;
+ pairs[i++] = 1.0;
+ // duration, target for release
+ pairs[i++] = 0.1;
+ pairs[i++] = 0.6;
+ // duration, target for sustain
+ pairs[i++] = 0.1;
+ pairs[i++] = 0.6;
+ // duration, target for release
+ pairs[i++] = 0.1;
+ pairs[i++] = 0.0;
+ // Create mirror image of this envelope.
+ int halfLength = i;
+ while (i < pairs.length) {
+ pairs[i] = pairs[i - halfLength];
+ i++;
+ pairs[i] = pairs[i - halfLength] * -1.0;
+ i++;
+ }
+ envelope = new SegmentedEnvelope(pairs);
+
+ envelopePlayer.dataQueue.queueLoop(envelope, 0, envelope.getNumFrames());
+ }
+
+ public static void main(String[] args) {
+ try {
+ new NotesToTone().play();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}