diff options
author | Phil Burk <[email protected]> | 2016-08-02 07:52:17 -0700 |
---|---|---|
committer | Phil Burk <[email protected]> | 2016-10-24 08:29:20 -0700 |
commit | 580fea450ec0982d0bd8be589f00566267e7b0d1 (patch) | |
tree | 0420f768fc7c63208b1720232c447e17af9017af /tests/com | |
parent | a6583e89166f7477a675cf3094a91b303ba7850a (diff) |
Instruments: add better synth, pitch control
Diffstat (limited to 'tests/com')
-rw-r--r-- | tests/com/jsyn/examples/CircuitTester.java | 13 | ||||
-rw-r--r-- | tests/com/jsyn/examples/HearMoogFilter.java | 32 | ||||
-rw-r--r-- | tests/com/jsyn/examples/PlayMIDI.java | 241 | ||||
-rw-r--r-- | tests/com/jsyn/examples/SeeOscillators.java | 51 | ||||
-rw-r--r-- | tests/com/jsyn/examples/UseMidiKeyboard.java | 127 | ||||
-rw-r--r-- | tests/com/jsyn/research/lambdas/LambdaUnits.java | 22 | ||||
-rw-r--r-- | tests/com/jsyn/unitgen/TestMath.java | 25 |
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); + } + } } |