aboutsummaryrefslogtreecommitdiffstats
path: root/tests/com
diff options
context:
space:
mode:
authorPhil Burk <[email protected]>2016-08-02 07:52:17 -0700
committerPhil Burk <[email protected]>2016-10-24 08:29:20 -0700
commit580fea450ec0982d0bd8be589f00566267e7b0d1 (patch)
tree0420f768fc7c63208b1720232c447e17af9017af /tests/com
parenta6583e89166f7477a675cf3094a91b303ba7850a (diff)
Instruments: add better synth, pitch control
Diffstat (limited to 'tests/com')
-rw-r--r--tests/com/jsyn/examples/CircuitTester.java13
-rw-r--r--tests/com/jsyn/examples/HearMoogFilter.java32
-rw-r--r--tests/com/jsyn/examples/PlayMIDI.java241
-rw-r--r--tests/com/jsyn/examples/SeeOscillators.java51
-rw-r--r--tests/com/jsyn/examples/UseMidiKeyboard.java127
-rw-r--r--tests/com/jsyn/research/lambdas/LambdaUnits.java22
-rw-r--r--tests/com/jsyn/unitgen/TestMath.java25
7 files changed, 384 insertions, 127 deletions
diff --git a/tests/com/jsyn/examples/CircuitTester.java b/tests/com/jsyn/examples/CircuitTester.java
index 1a307f2..948e8a0 100644
--- a/tests/com/jsyn/examples/CircuitTester.java
+++ b/tests/com/jsyn/examples/CircuitTester.java
@@ -4,9 +4,9 @@
* 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.
@@ -22,6 +22,7 @@ import javax.swing.JApplet;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
+import com.jsyn.instruments.DualOscillatorSynthVoice;
import com.jsyn.instruments.SubtractiveSynthVoice;
import com.jsyn.scope.AudioScope;
import com.jsyn.swing.JAppletFrame;
@@ -31,7 +32,7 @@ import com.jsyn.unitgen.UnitSource;
/**
* Listen to a circuit while tweaking it knobs. Show output in a scope.
- *
+ *
* @author Phil Burk (C) 2012 Mobileer Inc
*/
public class CircuitTester extends JApplet {
@@ -63,7 +64,7 @@ public class CircuitTester extends JApplet {
// Use a scope to see the output.
scope = new AudioScope(synth);
scope.addProbe(unitSource.getOutput());
- scope.setTriggerMode(AudioScope.TriggerMode.NORMAL);
+ scope.setTriggerMode(AudioScope.TriggerMode.AUTO);
scope.getView().setControlsVisible(false);
add(BorderLayout.SOUTH, scope.getView());
@@ -72,13 +73,13 @@ public class CircuitTester extends JApplet {
/**
* Override this to test your own circuits.
- *
+ *
* @return
*/
public UnitSource createUnitSource() {
//return new SampleHoldNoteBlaster();
//return new com.syntona.exported.FMVoice();
- return new SubtractiveSynthVoice();
+ return new DualOscillatorSynthVoice();
//return new WindCircuit();
//return new WhiteNoise();
//return new BrownNoise();
diff --git a/tests/com/jsyn/examples/HearMoogFilter.java b/tests/com/jsyn/examples/HearMoogFilter.java
index dfe3bec..4ec4811 100644
--- a/tests/com/jsyn/examples/HearMoogFilter.java
+++ b/tests/com/jsyn/examples/HearMoogFilter.java
@@ -4,9 +4,9 @@
* 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.
@@ -48,7 +48,7 @@ import com.jsyn.unitgen.UnitOscillator;
/**
* Play a sawtooth through a 4-pole filter.
- *
+ *
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class HearMoogFilter extends JApplet {
@@ -58,11 +58,12 @@ public class HearMoogFilter extends JApplet {
private FilterLowPass filterBiquad;
private LinearRamp rampCutoff;
private PassThrough tieQ;
+ private PassThrough tieCutoff;
private PassThrough mixer;
private LineOut lineOut;
private AudioScope scope;
- private AudioScopeProbe moogProbe;
+ private boolean useCutoffRamp = false;
@Override
public void init() {
@@ -70,6 +71,7 @@ public class HearMoogFilter extends JApplet {
synth.add(oscillator = new SawtoothOscillatorBL());
synth.add(rampCutoff = new LinearRamp());
synth.add(tieQ = new PassThrough());
+ synth.add(tieCutoff = new PassThrough());
synth.add(filterMoog = new FilterFourPoles());
synth.add(filterBiquad = new FilterLowPass());
synth.add(mixer = new PassThrough());
@@ -77,9 +79,14 @@ public class HearMoogFilter extends JApplet {
oscillator.output.connect(filterMoog.input);
oscillator.output.connect(filterBiquad.input);
- rampCutoff.output.connect(filterMoog.frequency);
- rampCutoff.output.connect(filterBiquad.frequency);
- rampCutoff.time.set(0.050);
+ if (useCutoffRamp) {
+ rampCutoff.output.connect(filterMoog.frequency);
+ rampCutoff.output.connect(filterBiquad.frequency);
+ rampCutoff.time.set(0.000);
+ } else {
+ tieCutoff.output.connect(filterMoog.frequency);
+ tieCutoff.output.connect(filterBiquad.frequency);
+ }
tieQ.output.connect(filterMoog.Q);
tieQ.output.connect(filterBiquad.Q);
filterMoog.output.connect(mixer.input);
@@ -89,7 +96,8 @@ public class HearMoogFilter extends JApplet {
filterBiquad.amplitude.set(0.1);
oscillator.frequency.setup(50.0, 130.0, 3000.0);
oscillator.amplitude.setup(0.0, 0.336, 1.0);
- rampCutoff.input.setup(50.0, 400.0, 4000.0);
+ rampCutoff.input.setup(filterMoog.frequency);
+ tieCutoff.input.setup(filterMoog.frequency);
tieQ.input.setup(0.1, 0.7, 10.0);
setupGUI();
}
@@ -144,14 +152,18 @@ public class HearMoogFilter extends JApplet {
knobPanel.add(setupPortKnob(oscillator.frequency, "OscFreq"));
knobPanel.add(setupPortKnob(oscillator.amplitude, "OscAmp"));
- knobPanel.add(setupPortKnob(rampCutoff.input, "Cutoff"));
+ if (useCutoffRamp) {
+ knobPanel.add(setupPortKnob(rampCutoff.input, "Cutoff"));
+ } else {
+ knobPanel.add(setupPortKnob(tieCutoff.input, "Cutoff"));
+ }
knobPanel.add(setupPortKnob(tieQ.input, "Q"));
rackPanel.add(knobPanel);
add(rackPanel, BorderLayout.SOUTH);
scope = new AudioScope(synth);
scope.addProbe(oscillator.output);
- moogProbe = scope.addProbe(filterMoog.output);
+ scope.addProbe(filterMoog.output);
scope.addProbe(filterBiquad.output);
scope.setTriggerMode(AudioScope.TriggerMode.NORMAL);
scope.getView().setControlsVisible(false);
diff --git a/tests/com/jsyn/examples/PlayMIDI.java b/tests/com/jsyn/examples/PlayMIDI.java
new file mode 100644
index 0000000..04c6b9b
--- /dev/null
+++ b/tests/com/jsyn/examples/PlayMIDI.java
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ */
+
+package com.jsyn.examples;
+
+import java.io.IOException;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import com.jsyn.devices.javasound.MidiDeviceTools;
+import com.jsyn.instruments.DualOscillatorSynthVoice;
+import com.jsyn.instruments.SubtractiveSynthVoice;
+import com.jsyn.midi.MessageParser;
+import com.jsyn.midi.MidiConstants;
+import com.jsyn.midi.MidiSynthesizer;
+import com.jsyn.unitgen.LineOut;
+import com.jsyn.unitgen.PowerOfTwo;
+import com.jsyn.unitgen.SineOscillator;
+import com.jsyn.unitgen.UnitOscillator;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.MultiChannelSynthesizer;
+import com.jsyn.util.VoiceAllocator;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.math.AudioMath;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Send MIDI messages to JSyn based MIDI synthesizer.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class PlayMIDI {
+ private static final int NUM_CHANNELS = 16;
+ private static final int VOICES_PER_CHANNEL = 6;
+ private Synthesizer synth;
+ private MidiSynthesizer midiSynthesizer;
+ private LineOut lineOut;
+
+ private VoiceDescription voiceDescription;
+ private MultiChannelSynthesizer multiSynth;
+
+ public static void main(String[] args) {
+ PlayMIDI app = new PlayMIDI();
+ try {
+ VoiceDescription description = DualOscillatorSynthVoice.getVoiceDescription();
+ app.test(description);
+ System.out.println("Test complete");
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.exit(0);
+ }
+
+ public void sendMidiMessage(byte[] bytes) {
+ midiSynthesizer.onReceive(bytes, 0, bytes.length);
+ }
+
+ public void sendNoteOff(int channel, int pitch, int velocity) {
+ midiCommand(MidiConstants.NOTE_OFF + channel, pitch, velocity);
+ }
+
+ public void sendNoteOn(int channel, int pitch, int velocity) {
+ midiCommand(MidiConstants.NOTE_ON + channel, pitch, velocity);
+ }
+
+ public void sendControlChange(int channel, int index, int value) {
+ midiCommand(MidiConstants.CONTROL_CHANGE + channel, index, value);
+ }
+
+ /**
+ * @param channel
+ * @param program starts at zero
+ */
+ private void sendProgramChange(int channel, int program) {
+ midiCommand(MidiConstants.PROGRAM_CHANGE + channel, program);
+
+ }
+
+ /**
+ * Send either RPN or NRPN.
+ */
+ public void sendParameter(int channel, int index14, int value14, int controllerXPN) {
+ int indexLsb = index14 & 0x07F;
+ int indexMsb = (index14 >> 7) & 0x07F;
+ int valueLsb = value14 & 0x07F;
+ int valueMsb = (value14 >> 7) & 0x07F;
+ sendControlChange(channel, controllerXPN + 1, indexMsb);
+ sendControlChange(channel, controllerXPN, indexLsb);
+ sendControlChange(channel, MidiConstants.CONTROLLER_DATA_ENTRY, valueMsb);
+ sendControlChange(channel, MidiConstants.CONTROLLER_DATA_ENTRY_LSB, valueLsb);
+ sendControlChange(channel, controllerXPN + 1, 0x7F); // NULL RPN index
+ sendControlChange(channel, controllerXPN, 0x7F); // to deactivate RPN
+ }
+
+ public void sendRPN(int channel, int index14, int value14) {
+ sendParameter(channel, index14, value14, MidiConstants.CONTROLLER_RPN_LSB);
+ }
+
+ public void sendNRPN(int channel, int index14, int value14) {
+ sendParameter(channel, index14, value14, MidiConstants.CONTROLLER_NRPN_LSB);
+ }
+
+ private void midiCommand(int status, int data1, int data2) {
+ byte[] buffer = new byte[3];
+ buffer[0] = (byte) status;
+ buffer[1] = (byte) data1;
+ buffer[2] = (byte) data2;
+ sendMidiMessage(buffer);
+ }
+
+ private void midiCommand(int status, int data1) {
+ byte[] buffer = new byte[2];
+ buffer[0] = (byte) status;
+ buffer[1] = (byte) data1;
+ sendMidiMessage(buffer);
+ }
+
+ public int test(VoiceDescription description) throws IOException, InterruptedException {
+ setupSynth(description);
+
+ //playOctaveUsingBend();
+ playSameNotesBent();
+
+ // Setup all the channels.
+ int maxChannels = 8;
+ for (int channel = 0; channel < maxChannels; channel++) {
+ int program = channel;
+ sendProgramChange(channel, program);
+ }
+ playNotePerChannel(maxChannels);
+
+ return 0;
+ }
+
+ private void playOctaveUsingBend() throws InterruptedException {
+ sendProgramChange(0, 0);
+ float range0 = 12.0f;
+ sendPitchBendRange(0, range0);
+ for(int i = 0; i < 13; i++) {
+ System.out.println("Bend to pitch " + i);
+ sendPitchBend(0, i / range0);
+ sendNoteOn(0, 60, 100);
+ synth.sleepFor(0.5);
+ sendNoteOff(0, 60, 100);
+ synth.sleepFor(0.5);
+ }
+ }
+
+ private void playSameNotesBent() throws InterruptedException {
+ sendProgramChange(0, 0);
+ sendProgramChange(1, 0);
+ float range0 = 2.3f;
+ float range1 = 6.8f;
+ sendPitchBendRange(0, range0);
+ sendPitchBendRange(1, range1);
+ sendPitchBend(0, 0.0f / range0); // bend by 0 semitones
+ sendPitchBend(1, 1.0f / range1); // bend by 1 semitones
+
+ System.out.println("These two notes should play at the same pitch.");
+ sendNoteOn(0, 61, 100);
+ synth.sleepFor(0.5);
+ sendNoteOff(0, 61, 100);
+
+ sendNoteOn(1, 60, 100);
+ synth.sleepFor(0.5);
+ sendNoteOff(1, 60, 100);
+
+ synth.sleepFor(2.0);
+ System.out.println("------ done ---------------");
+ }
+
+ /**
+ *
+ * @param channel
+ * @param normalizedBend between -1 and +1
+ */
+ private void sendPitchBend(int channel, float normalizedBend) {
+ final int BEND_MIN = 0x0000;
+ final int BEND_CENTER = 0x2000;
+ final int BEND_MAX = 0x3FFF;
+ int bend = BEND_CENTER + (int)(BEND_CENTER * normalizedBend);
+ if (bend < BEND_MIN) bend = BEND_MIN;
+ else if (bend > BEND_MAX) bend = BEND_MAX;
+ int lsb = bend & 0x07F;
+ int msb = (bend >> 7) & 0x07F;
+ midiCommand(MidiConstants.PITCH_BEND + channel, lsb, msb);
+ }
+
+ private void sendPitchBendRange(int channel, float range0) {
+ int semitones = (int)range0;
+ int cents = (int) (100 * (range0 - semitones));
+ int value = (semitones << 7) + cents;
+ sendRPN(channel, MidiConstants.RPN_BEND_RANGE, value);
+ }
+
+ private void playNotePerChannel(int maxChannels) throws InterruptedException {
+ // Play notes on those channels.
+ for (int channel = 0; channel < maxChannels; channel++) {
+ sendNoteOn(channel, 60 + channel, 100);
+ synth.sleepFor(0.5);
+ sendNoteOff(channel, 60 + channel, 100);
+ synth.sleepFor(0.5);
+ }
+ }
+
+ private void setupSynth(VoiceDescription description) {
+ synth = JSyn.createSynthesizer();
+
+ // Add an output.
+ synth.add(lineOut = new LineOut());
+
+ voiceDescription = description;
+ multiSynth = new MultiChannelSynthesizer();
+ final int startChannel = 0;
+ multiSynth.setup(synth, startChannel, NUM_CHANNELS, VOICES_PER_CHANNEL, voiceDescription);
+ midiSynthesizer = new MidiSynthesizer(multiSynth);
+
+ multiSynth.getOutput().connect(0,lineOut.input, 0);
+ multiSynth.getOutput().connect(1,lineOut.input, 1);
+
+ // Start synthesizer using default stereo output at 44100 Hz.
+ synth.start();
+ lineOut.start();
+ }
+
+}
diff --git a/tests/com/jsyn/examples/SeeOscillators.java b/tests/com/jsyn/examples/SeeOscillators.java
index b01e3a9..b8088c4 100644
--- a/tests/com/jsyn/examples/SeeOscillators.java
+++ b/tests/com/jsyn/examples/SeeOscillators.java
@@ -4,9 +4,9 @@
* 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.
@@ -31,12 +31,15 @@ import javax.swing.JRadioButton;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.scope.AudioScope;
+import com.jsyn.scope.AudioScopeProbe;
+import com.jsyn.swing.DoubleBoundedRangeSlider;
import com.jsyn.swing.JAppletFrame;
import com.jsyn.swing.PortControllerFactory;
import com.jsyn.unitgen.ImpulseOscillator;
import com.jsyn.unitgen.ImpulseOscillatorBL;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.LinearRamp;
+import com.jsyn.unitgen.MorphingOscillatorBL;
import com.jsyn.unitgen.Multiply;
import com.jsyn.unitgen.PulseOscillator;
import com.jsyn.unitgen.PulseOscillatorBL;
@@ -51,9 +54,9 @@ import com.jsyn.unitgen.TriangleOscillator;
import com.jsyn.unitgen.UnitOscillator;
/**
- * Display each oscillators waveform using the AudioScope. This is a reimplementation of the
+ * Display each oscillator's waveform using the AudioScope. This is a re-implementation of the
* TJ_SeeOsc Applet from the old API.
- *
+ *
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class SeeOscillators extends JApplet {
@@ -66,6 +69,10 @@ public class SeeOscillators extends JApplet {
private Multiply oscGain;
private ButtonGroup buttonGroup;
private LinearRamp freqRamp;
+ private LinearRamp widthRamp;
+ private LinearRamp shapeRamp;
+ private DoubleBoundedRangeSlider widthSlider;
+ private DoubleBoundedRangeSlider shapeSlider;
/* Can be run as either an application or as an applet. */
public static void main(String args[]) {
@@ -83,11 +90,13 @@ public class SeeOscillators extends JApplet {
add(BorderLayout.NORTH, new JLabel("Show Oscillators in an AudioScope"));
scope = new AudioScope(synth);
- scope.addProbe(oscGain.output);
+ AudioScopeProbe probe = scope.addProbe(oscGain.output);
+ probe.setAutoScaleEnabled(false);
+ probe.setVerticalScale(1.1);
scope.setTriggerMode(AudioScope.TriggerMode.NORMAL);
// scope.getModel().getTriggerModel().getLevelModel().setDoubleValue( 0.0001 );
// Turn off the gain and trigger control GUI.
- scope.getView().setShowControls(false);
+ scope.getView().setControlsVisible(false);
scope.start();
add(BorderLayout.CENTER, scope.getView());
@@ -101,6 +110,10 @@ public class SeeOscillators extends JApplet {
southPanel.add(PortControllerFactory.createExponentialPortSlider(freqRamp.input));
southPanel.add(PortControllerFactory.createExponentialPortSlider(oscGain.inputB));
+ southPanel.add(widthSlider = PortControllerFactory.createPortSlider(widthRamp.input));
+ widthSlider.setEnabled(false);
+ southPanel.add(shapeSlider = PortControllerFactory.createPortSlider(shapeRamp.input));
+ shapeSlider.setEnabled(false);
oscPanel.validate();
validate();
@@ -121,10 +134,21 @@ public class SeeOscillators extends JApplet {
freqRamp.input.setName("Frequency");
freqRamp.time.set(0.1);
+ synth.add(widthRamp = new LinearRamp());
+ widthRamp.input.setup(-1.0, 0.0, 1.0);
+ widthRamp.input.setName("Width");
+ widthRamp.time.set(0.1);
+
+ synth.add(shapeRamp = new LinearRamp());
+ shapeRamp.input.setup(-1.0, 0.0, 1.0);
+ shapeRamp.input.setName("Shape");
+ shapeRamp.time.set(0.1);
+
// Add an output so we can hear the oscillators.
synth.add(lineOut = new LineOut());
- oscGain.output.connect(lineOut.input);
+ oscGain.output.connect(0, lineOut.input, 0);
+ oscGain.output.connect(0, lineOut.input, 1);
setupGUI();
@@ -141,6 +165,7 @@ public class SeeOscillators extends JApplet {
addOscillator(new SquareOscillatorBL(), "SquareBL");
addOscillator(new PulseOscillator(), "Pulse");
addOscillator(new PulseOscillatorBL(), "PulseBL");
+ addOscillator(new MorphingOscillatorBL(), "MorphBL");
addOscillator(new ImpulseOscillator(), "Impulse");
addOscillator(new ImpulseOscillatorBL(), "ImpulseBL");
@@ -159,6 +184,15 @@ public class SeeOscillators extends JApplet {
oscillators.add(osc);
synth.add(osc);
freqRamp.output.connect(osc.frequency);
+ if (osc instanceof PulseOscillatorBL) {
+ widthRamp.output.connect(((PulseOscillatorBL)osc).width);
+ }
+ if (osc instanceof PulseOscillator) {
+ widthRamp.output.connect(((PulseOscillator)osc).width);
+ }
+ if (osc instanceof MorphingOscillatorBL) {
+ shapeRamp.output.connect(((MorphingOscillatorBL)osc).shape);
+ }
osc.amplitude.set(1.0);
JRadioButton checkBox = new JRadioButton(label);
buttonGroup.add(checkBox);
@@ -169,6 +203,9 @@ public class SeeOscillators extends JApplet {
oscGain.inputA.disconnectAll(0);
// Connect this one.
osc.output.connect(oscGain.inputA);
+ widthSlider.setEnabled(osc instanceof PulseOscillator
+ || osc instanceof PulseOscillatorBL);
+ shapeSlider.setEnabled(osc instanceof MorphingOscillatorBL);
}
});
oscPanel.add(checkBox);
diff --git a/tests/com/jsyn/examples/UseMidiKeyboard.java b/tests/com/jsyn/examples/UseMidiKeyboard.java
index 196f13c..0efa039 100644
--- a/tests/com/jsyn/examples/UseMidiKeyboard.java
+++ b/tests/com/jsyn/examples/UseMidiKeyboard.java
@@ -4,9 +4,9 @@
* 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.
@@ -26,33 +26,26 @@ import javax.sound.midi.Receiver;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.devices.javasound.MidiDeviceTools;
-import com.jsyn.instruments.SubtractiveSynthVoice;
-import com.jsyn.midi.MessageParser;
-import com.jsyn.midi.MidiConstants;
+import com.jsyn.instruments.DualOscillatorSynthVoice;
+import com.jsyn.midi.MidiSynthesizer;
import com.jsyn.unitgen.LineOut;
-import com.jsyn.unitgen.PowerOfTwo;
-import com.jsyn.unitgen.SineOscillator;
-import com.jsyn.unitgen.UnitOscillator;
-import com.jsyn.util.VoiceAllocator;
-import com.softsynth.shared.time.TimeStamp;
+import com.jsyn.util.MultiChannelSynthesizer;
+import com.jsyn.util.VoiceDescription;
/**
* Connect a USB MIDI Keyboard to the internal MIDI Synthesizer using JavaSound.
- *
+ *
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class UseMidiKeyboard {
- private static final int MAX_VOICES = 8;
+ private static final int NUM_CHANNELS = 16;
+ private static final int VOICES_PER_CHANNEL = 3;
+
private Synthesizer synth;
- private VoiceAllocator allocator;
private LineOut lineOut;
- private double vibratoRate = 5.0;
- private double vibratoDepth = 0.0;
-
- private UnitOscillator lfo;
- private PowerOfTwo powerOfTwo;
- private MessageParser messageParser;
- private SubtractiveSynthVoice[] voices;
+ private MidiSynthesizer midiSynthesizer;
+ private VoiceDescription voiceDescription;
+ private MultiChannelSynthesizer multiSynth;
public static void main(String[] args) {
UseMidiKeyboard app = new UseMidiKeyboard();
@@ -77,15 +70,13 @@ public class UseMidiKeyboard {
@Override
public void send(MidiMessage message, long timeStamp) {
byte[] bytes = message.getMessage();
- messageParser.parse(bytes);
+ midiSynthesizer.onReceive(bytes, 0, bytes.length);
}
}
public int test() throws MidiUnavailableException, IOException, InterruptedException {
setupSynth();
- messageParser = new MyParser();
-
int result = 2;
MidiDevice keyboard = MidiDeviceTools.findKeyboard();
Receiver receiver = new CustomReceiver();
@@ -104,95 +95,27 @@ public class UseMidiKeyboard {
return result;
}
- class MyParser extends MessageParser {
- @Override
- public void controlChange(int channel, int index, int value) {
- // Mod Wheel
- if (index == 1) {
- vibratoDepth = 0.1 * value / 128.0;
- // System.out.println( "vibratoDepth = " + vibratoDepth );
- lfo.amplitude.set(vibratoDepth);
- }
- // 102 is the index of the first knob on my Axiom 25
- else if (index == 102) {
- final double bump = 0.95;
- if (value < 64) {
- vibratoRate *= bump;
- } else {
- vibratoRate *= 1.0 / bump;
- }
- System.out.println("vibratoRate = " + vibratoRate);
- lfo.frequency.set(vibratoRate);
- }
-
- }
-
- @Override
- public void noteOff(int channel, int noteNumber, int velocity) {
- allocator.noteOff(noteNumber, synth.createTimeStamp());
- }
-
- @Override
- public void noteOn(int channel, int noteNumber, int velocity) {
- double frequency = convertPitchToFrequency(noteNumber);
- double amplitude = velocity / (4 * 128.0);
- TimeStamp timeStamp = synth.createTimeStamp();
- allocator.noteOn(noteNumber, frequency, amplitude, timeStamp);
- }
-
- @Override
- public void pitchBend(int channel, int bend) {
- double fraction = (bend - MidiConstants.PITCH_BEND_CENTER)
- / ((double) MidiConstants.PITCH_BEND_CENTER);
- System.out.println("bend = " + bend + ", fraction = " + fraction);
- }
- }
-
- /**
- * 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)));
- }
private void setupSynth() {
synth = JSyn.createSynthesizer();
- // Add an output.
- synth.add(lineOut = new LineOut());
+ voiceDescription = DualOscillatorSynthVoice.getVoiceDescription();
+// voiceDescription = SubtractiveSynthVoice.getVoiceDescription();
- synth.add(powerOfTwo = new PowerOfTwo());
- synth.add(lfo = new SineOscillator());
- // Sums pitch modulation.
- lfo.output.connect(powerOfTwo.input);
- lfo.amplitude.set(vibratoDepth);
- lfo.frequency.set(vibratoRate);
-
- voices = new SubtractiveSynthVoice[MAX_VOICES];
- for (int i = 0; i < MAX_VOICES; i++) {
- SubtractiveSynthVoice voice = new SubtractiveSynthVoice();
- synth.add(voice);
- powerOfTwo.output.connect(voice.pitchModulation);
- voice.getOutput().connect(0, lineOut.input, 0);
- voice.getOutput().connect(0, lineOut.input, 1);
- voices[i] = voice;
- }
- allocator = new VoiceAllocator(voices);
+ multiSynth = new MultiChannelSynthesizer();
+ final int startChannel = 0;
+ multiSynth.setup(synth, startChannel, NUM_CHANNELS, VOICES_PER_CHANNEL, voiceDescription);
+ midiSynthesizer = new MidiSynthesizer(multiSynth);
+
+ // Create a LineOut for the entire synthesizer.
+ synth.add(lineOut = new LineOut());
+ multiSynth.getOutput().connect(0,lineOut.input, 0);
+ multiSynth.getOutput().connect(1,lineOut.input, 1);
// Start synthesizer using default stereo output at 44100 Hz.
synth.start();
- // We only need to start the LineOut. It will pull data from the
- // oscillator.
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 + 0.5;
-
}
}
diff --git a/tests/com/jsyn/research/lambdas/LambdaUnits.java b/tests/com/jsyn/research/lambdas/LambdaUnits.java
new file mode 100644
index 0000000..42807ac
--- /dev/null
+++ b/tests/com/jsyn/research/lambdas/LambdaUnits.java
@@ -0,0 +1,22 @@
+package com.jsyn.research.lambdas;
+
+import java.util.function.BinaryOperator;
+
+public class LambdaUnits {
+
+
+ public static void main(String[] args) {
+ test();
+ }
+
+ void tryLambda(BinaryOperator<Double> op) {
+ double result = op.apply(3.0, 4.0);
+ System.out.println("result = " + result);
+ }
+
+ private static void test() {
+ System.out.println("Test Lambdas");
+ // Need Java 8! tryLambda((x, y) -> (x * y));
+ }
+
+}
diff --git a/tests/com/jsyn/unitgen/TestMath.java b/tests/com/jsyn/unitgen/TestMath.java
index 0fde9b5..cae1dea 100644
--- a/tests/com/jsyn/unitgen/TestMath.java
+++ b/tests/com/jsyn/unitgen/TestMath.java
@@ -4,9 +4,9 @@
* 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.
@@ -19,6 +19,7 @@ package com.jsyn.unitgen;
import junit.framework.TestCase;
import com.jsyn.engine.SynthesisEngine;
+import com.softsynth.math.AudioMath;
/**
* @author Phil Burk, (C) 2009 Mobileer Inc
@@ -388,5 +389,25 @@ public class TestMath extends TestCase {
assertEquals("PowerOfTwo", Math.pow(2.0, in), powerOfTwo.output.getValue(), 0.001);
}
}
+ public void testPitchToFrequency() {
+ PitchToFrequency ugen = new PitchToFrequency();
+ ugen.setSynthesisEngine(synthesisEngine);
+ final double smallValue = -1.5308084989341915E-17;
+ double values[] = {
+ 49.0, 49.5, 50.0 + smallValue,
+ 60.0 -smallValue,
+ 79.2, 12.9, 118.973
+ };
+ // Sanity check AudioMath
+ assertEquals("PitchToFrequency", 440.0, AudioMath.pitchToFrequency(69), 0.001);
+ assertEquals("PitchToFrequency", 660.0, AudioMath.pitchToFrequency(69+7.02), 0.1);
+
+ for (double pitch : values) {
+ ugen.input.setValueInternal(pitch);
+ ugen.generate();
+ assertEquals("PitchToFrequency", AudioMath.pitchToFrequency(pitch),
+ ugen.output.getValue(), 0.001);
+ }
+ }
}