aboutsummaryrefslogtreecommitdiffstats
path: root/tests/com/jsyn/examples/PlayChords.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/com/jsyn/examples/PlayChords.java')
-rw-r--r--tests/com/jsyn/examples/PlayChords.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/tests/com/jsyn/examples/PlayChords.java b/tests/com/jsyn/examples/PlayChords.java
new file mode 100644
index 0000000..0b1ae2e
--- /dev/null
+++ b/tests/com/jsyn/examples/PlayChords.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009 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.
+ */
+
+package com.jsyn.examples;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import com.jsyn.instruments.SubtractiveSynthVoice;
+import com.jsyn.unitgen.LineOut;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceAllocator;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Play chords and melody using the VoiceAllocator.
+ *
+ * @author Phil Burk (C) 2009 Mobileer Inc
+ */
+public class PlayChords {
+ private static final int MAX_VOICES = 8;
+ private Synthesizer synth;
+ private VoiceAllocator allocator;
+ private LineOut lineOut;
+ /** Number of seconds to generate music in advance of presentation-time. */
+ private double advance = 0.2;
+ private double secondsPerBeat = 0.6;
+ // on time over note duration
+ private double dutyCycle = 0.8;
+ private double measure = secondsPerBeat * 4.0;
+ private UnitVoice[] voices;
+
+ private void test() {
+ synth = JSyn.createSynthesizer();
+
+ // Add an output.
+ synth.add(lineOut = new LineOut());
+
+ voices = new UnitVoice[MAX_VOICES];
+ for (int i = 0; i < MAX_VOICES; i++) {
+ SubtractiveSynthVoice voice = new SubtractiveSynthVoice();
+ synth.add(voice);
+ voice.getOutput().connect(0, lineOut.input, 0);
+ voice.getOutput().connect(0, lineOut.input, 1);
+ voices[i] = voice;
+ }
+ allocator = new VoiceAllocator(voices);
+
+ // Start synthesizer using default stereo output at 44100 Hz.
+ synth.start();
+ // We only need to start the LineOut. It will pull data from the
+ // voices.
+ lineOut.start();
+
+ // Get synthesizer time in seconds.
+ double timeNow = synth.getCurrentTime();
+
+ // Advance to a near future time so we have a clean start.
+ double time = timeNow + 1.0;
+
+ try {
+ int tonic = 60 - 12;
+ for (int i = 0; i < 4; i++) {
+ playMajorMeasure1(time, tonic);
+ time += measure;
+ catchUp(time);
+ playMajorMeasure1(time, tonic + 4);
+ time += measure;
+ catchUp(time);
+ playMajorMeasure1(time, tonic + 7);
+ time += measure;
+ catchUp(time);
+ playMinorMeasure1(time, tonic + 2); // minor chord
+ time += measure;
+ catchUp(time);
+ }
+ time += secondsPerBeat;
+ catchUp(time);
+
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // Stop everything.
+ synth.stop();
+ }
+
+ private void playMinorMeasure1(double time, int base) throws InterruptedException {
+ int p1 = base;
+ int p2 = base + 3;
+ int p3 = base + 7;
+ playChord1(time, p1, p2, p3);
+ playNoodle1(time, p1 + 24, p2 + 24, p3 + 24);
+ }
+
+ private void playMajorMeasure1(double time, int base) throws InterruptedException {
+ int p1 = base;
+ int p2 = base + 4;
+ int p3 = base + 7;
+ playChord1(time, p1, p2, p3);
+ playNoodle1(time, p1 + 24, p2 + 24, p3 + 24);
+ }
+
+ private void playNoodle1(double time, int p1, int p2, int p3) {
+ double secondsPerNote = secondsPerBeat * 0.5;
+ for (int i = 0; i < 8; i++) {
+ int p = pickFromThree(p1, p2, p3);
+ noteOn(time, p);
+ noteOff(time + dutyCycle * secondsPerNote, p);
+ time += secondsPerNote;
+ }
+ }
+
+ private int pickFromThree(int p1, int p2, int p3) {
+ int r = (int) (Math.random() * 3.0);
+ if (r < 1)
+ return p1;
+ else if (r < 2)
+ return p2;
+ else
+ return p3;
+ }
+
+ private void playChord1(double time, int p1, int p2, int p3) throws InterruptedException {
+ double dur = dutyCycle * secondsPerBeat;
+ playTriad(time, dur, p1, p2, p3);
+ time += secondsPerBeat;
+ playTriad(time, dur, p1, p2, p3);
+ time += secondsPerBeat;
+ playTriad(time, dur * 0.25, p1, p2, p3);
+ time += secondsPerBeat * 0.25;
+ playTriad(time, dur * 0.25, p1, p2, p3);
+ time += secondsPerBeat * 0.75;
+ playTriad(time, dur, p1, p2, p3);
+ time += secondsPerBeat;
+ }
+
+ private void playTriad(double time, double dur, int p1, int p2, int p3)
+ throws InterruptedException {
+ noteOn(time, p1);
+ noteOn(time, p2);
+ noteOn(time, p3);
+ double offTime = time + dur;
+ noteOff(offTime, p1);
+ noteOff(offTime, p2);
+ noteOff(offTime, p3);
+ }
+
+ private void catchUp(double time) throws InterruptedException {
+ synth.sleepUntil(time - advance);
+ }
+
+ private void noteOff(double time, int noteNumber) {
+ allocator.noteOff(noteNumber, new TimeStamp(time));
+ }
+
+ private void noteOn(double time, int noteNumber) {
+ double frequency = convertPitchToFrequency(noteNumber);
+ double amplitude = 0.2;
+ TimeStamp timeStamp = new TimeStamp(time);
+ allocator.noteOn(noteNumber, frequency, amplitude, timeStamp);
+ }
+
+ /**
+ * Calculate frequency in Hertz based on MIDI pitch. Middle C is 60.0. You can use fractional
+ * pitches so 60.5 would give you a pitch half way between C and C#.
+ */
+ double convertPitchToFrequency(double pitch) {
+ final double concertA = 440.0;
+ return concertA * Math.pow(2.0, ((pitch - 69) * (1.0 / 12.0)));
+ }
+
+ public static void main(String[] args) {
+ new PlayChords().test();
+ }
+}