aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/jsyn/instruments
diff options
context:
space:
mode:
authorRubbaBoy <[email protected]>2020-07-06 02:33:28 -0400
committerPhil Burk <[email protected]>2020-10-30 11:19:34 -0700
commit46888fae6eb7b1dd386f7af7d101ead99ae61981 (patch)
tree8969bbfd68d2fb5c0d8b86da49ec2eca230a72ab /src/main/java/com/jsyn/instruments
parentc51e92e813dd481603de078f0778e1f75db2ab05 (diff)
Restructured project, added gradle, JUnit, logger, and more
Added Gradle (and removed ant), modernized testing via the JUnit framework, moved standalone examples from the tests directory to a separate module, removed sparsely used Java logger and replaced it with SLF4J. More work could be done, however this is a great start to greatly improving the health of the codebase.
Diffstat (limited to 'src/main/java/com/jsyn/instruments')
-rw-r--r--src/main/java/com/jsyn/instruments/DrumWoodFM.java159
-rw-r--r--src/main/java/com/jsyn/instruments/DualOscillatorSynthVoice.java301
-rw-r--r--src/main/java/com/jsyn/instruments/JSynInstrumentLibrary.java48
-rw-r--r--src/main/java/com/jsyn/instruments/NoiseHit.java114
-rw-r--r--src/main/java/com/jsyn/instruments/SubtractiveSynthVoice.java182
-rw-r--r--src/main/java/com/jsyn/instruments/WaveShapingVoice.java187
6 files changed, 991 insertions, 0 deletions
diff --git a/src/main/java/com/jsyn/instruments/DrumWoodFM.java b/src/main/java/com/jsyn/instruments/DrumWoodFM.java
new file mode 100644
index 0000000..ba6cd1b
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/DrumWoodFM.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011 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.instruments;
+
+import com.jsyn.ports.UnitInputPort;
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.unitgen.Add;
+import com.jsyn.unitgen.Circuit;
+import com.jsyn.unitgen.EnvelopeAttackDecay;
+import com.jsyn.unitgen.Multiply;
+import com.jsyn.unitgen.PassThrough;
+import com.jsyn.unitgen.SineOscillator;
+import com.jsyn.unitgen.SineOscillatorPhaseModulated;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Drum instruments using 2 Operator FM.
+ *
+ * @author Phil Burk (C) 2011 Mobileer Inc
+ */
+public class DrumWoodFM extends Circuit implements UnitVoice {
+ private static final int NUM_PRESETS = 3;
+ // Declare units and ports.
+ EnvelopeAttackDecay ampEnv;
+ SineOscillatorPhaseModulated carrierOsc;
+ EnvelopeAttackDecay modEnv;
+ SineOscillator modOsc;
+ PassThrough freqDistributor;
+ Add modSummer;
+ Multiply frequencyMultiplier;
+
+ public UnitInputPort mcratio;
+ public UnitInputPort index;
+ public UnitInputPort modRange;
+ public UnitInputPort frequency;
+
+ public DrumWoodFM() {
+ // Create unit generators.
+ add(carrierOsc = new SineOscillatorPhaseModulated());
+ add(freqDistributor = new PassThrough());
+ add(modSummer = new Add());
+ add(ampEnv = new EnvelopeAttackDecay());
+ add(modEnv = new EnvelopeAttackDecay());
+ add(modOsc = new SineOscillator());
+ add(frequencyMultiplier = new Multiply());
+
+ addPort(mcratio = frequencyMultiplier.inputB, "MCRatio");
+ addPort(index = modSummer.inputA, "Index");
+ addPort(modRange = modEnv.amplitude, "ModRange");
+ addPort(frequency = freqDistributor.input, "Frequency");
+
+ ampEnv.export(this, "Amp");
+ modEnv.export(this, "Mod");
+
+ freqDistributor.output.connect(carrierOsc.frequency);
+ freqDistributor.output.connect(frequencyMultiplier.inputA);
+
+ carrierOsc.output.connect(ampEnv.amplitude);
+ modEnv.output.connect(modSummer.inputB);
+ modSummer.output.connect(modOsc.amplitude);
+ modOsc.output.connect(carrierOsc.modulation);
+ frequencyMultiplier.output.connect(modOsc.frequency);
+
+ // Make the circuit turn off when the envelope finishes to reduce CPU load.
+ ampEnv.setupAutoDisable(this);
+
+ usePreset(0);
+ }
+
+ @Override
+ public void noteOff(TimeStamp timeStamp) {
+ }
+
+ @Override
+ public void noteOn(double freq, double ampl, TimeStamp timeStamp) {
+ carrierOsc.amplitude.set(ampl, timeStamp);
+ ampEnv.input.trigger(timeStamp);
+ modEnv.input.trigger(timeStamp);
+ }
+
+ @Override
+ public UnitOutputPort getOutput() {
+ return ampEnv.output;
+ }
+
+ @Override
+ public void usePreset(int presetIndex) {
+ mcratio.setup(0.001, 0.6875, 20.0);
+ ampEnv.attack.setup(0.001, 0.005, 8.0);
+ modEnv.attack.setup(0.001, 0.005, 8.0);
+
+ int n = presetIndex % NUM_PRESETS;
+ switch (n) {
+ case 0:
+ ampEnv.decay.setup(0.001, 0.293, 8.0);
+ modEnv.decay.setup(0.001, 0.07, 8.0);
+ frequency.setup(0.0, 349.0, 3000.0);
+ index.setup(0.001, 0.05, 10.0);
+ modRange.setup(0.001, 0.4, 10.0);
+ break;
+ case 1:
+ default:
+ ampEnv.decay.setup(0.001, 0.12, 8.0);
+ modEnv.decay.setup(0.001, 0.06, 8.0);
+ frequency.setup(0.0, 1400.0, 3000.0);
+ index.setup(0.001, 0.16, 10.0);
+ modRange.setup(0.001, 0.17, 10.0);
+ break;
+ }
+ }
+
+ static class MyVoiceDescription extends VoiceDescription {
+ static String[] presetNames = {
+ "WoodBlockFM", "ClaveFM"
+ };
+ static String[] tags = {
+ "electronic", "drum"
+ };
+
+ public MyVoiceDescription() {
+ super("DrumWoodFM", presetNames);
+ }
+
+ @Override
+ public UnitVoice createUnitVoice() {
+ return new DrumWoodFM();
+ }
+
+ @Override
+ public String[] getTags(int presetIndex) {
+ return tags;
+ }
+
+ @Override
+ public String getVoiceClassName() {
+ return DrumWoodFM.class.getName();
+ }
+ }
+
+ public static VoiceDescription getVoiceDescription() {
+ return new MyVoiceDescription();
+ }
+}
diff --git a/src/main/java/com/jsyn/instruments/DualOscillatorSynthVoice.java b/src/main/java/com/jsyn/instruments/DualOscillatorSynthVoice.java
new file mode 100644
index 0000000..c81041f
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/DualOscillatorSynthVoice.java
@@ -0,0 +1,301 @@
+/*
+ * 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.instruments;
+
+import com.jsyn.ports.UnitInputPort;
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.unitgen.Add;
+import com.jsyn.unitgen.Circuit;
+import com.jsyn.unitgen.EnvelopeDAHDSR;
+import com.jsyn.unitgen.FilterFourPoles;
+import com.jsyn.unitgen.MorphingOscillatorBL;
+import com.jsyn.unitgen.Multiply;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.math.AudioMath;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Synthesizer voice with two morphing oscillators and a four-pole resonant filter.
+ * Modulate the amplitude and filter using DAHDSR envelopes.
+ */
+public class DualOscillatorSynthVoice extends Circuit implements UnitVoice {
+ private Multiply frequencyMultiplier;
+ private Multiply amplitudeMultiplier;
+ private Multiply detuneScaler1;
+ private Multiply detuneScaler2;
+ private Multiply amplitudeBoost;
+ private MorphingOscillatorBL osc1;
+ private MorphingOscillatorBL osc2;
+ private FilterFourPoles filter;
+ private EnvelopeDAHDSR ampEnv;
+ private EnvelopeDAHDSR filterEnv;
+ private Add cutoffAdder;
+
+ private static MyVoiceDescription voiceDescription;
+
+ public UnitInputPort amplitude;
+ public UnitInputPort frequency;
+ /**
+ * This scales the frequency value. You can use this to modulate a group of instruments using a
+ * shared LFO and they will stay in tune. Set to 1.0 for no modulation.
+ */
+ public UnitInputPort frequencyScaler;
+ public UnitInputPort oscShape1;
+ public UnitInputPort oscShape2;
+// public UnitInputPort oscDetune1;
+// public UnitInputPort oscDetune2;
+ public UnitInputPort cutoff;
+ public UnitInputPort filterEnvDepth;
+ public UnitInputPort Q;
+
+ public DualOscillatorSynthVoice() {
+ add(frequencyMultiplier = new Multiply());
+ add(amplitudeMultiplier = new Multiply());
+ add(amplitudeBoost = new Multiply());
+ add(detuneScaler1 = new Multiply());
+ add(detuneScaler2 = new Multiply());
+ // Add tone generators.
+ add(osc1 = new MorphingOscillatorBL());
+ add(osc2 = new MorphingOscillatorBL());
+
+ // Use an envelope to control the amplitude.
+ add(ampEnv = new EnvelopeDAHDSR());
+
+ // Use an envelope to control the filter cutoff.
+ add(filterEnv = new EnvelopeDAHDSR());
+ add(filter = new FilterFourPoles());
+ add(cutoffAdder = new Add());
+
+ filterEnv.output.connect(cutoffAdder.inputA);
+ cutoffAdder.output.connect(filter.frequency);
+ frequencyMultiplier.output.connect(detuneScaler1.inputA);
+ frequencyMultiplier.output.connect(detuneScaler2.inputA);
+ detuneScaler1.output.connect(osc1.frequency);
+ detuneScaler2.output.connect(osc2.frequency);
+ osc1.output.connect(amplitudeMultiplier.inputA); // mix oscillators
+ osc2.output.connect(amplitudeMultiplier.inputA);
+ amplitudeMultiplier.output.connect(filter.input);
+ filter.output.connect(amplitudeBoost.inputA);
+ amplitudeBoost.output.connect(ampEnv.amplitude);
+
+ addPort(amplitude = amplitudeMultiplier.inputB, PORT_NAME_AMPLITUDE);
+ addPort(frequency = frequencyMultiplier.inputA, PORT_NAME_FREQUENCY);
+ addPort(oscShape1 = osc1.shape, "OscShape1");
+ addPort(oscShape2 = osc2.shape, "OscShape2");
+// addPort(oscDetune1 = osc1.shape, "OscDetune1");
+// addPort(oscDetune2 = osc2.shape, "OscDetune2");
+ addPort(cutoff = cutoffAdder.inputB, PORT_NAME_CUTOFF);
+ addPortAlias(cutoff, PORT_NAME_TIMBRE);
+ addPort(Q = filter.Q);
+ addPort(frequencyScaler = frequencyMultiplier.inputB, PORT_NAME_FREQUENCY_SCALER);
+ addPort(filterEnvDepth = filterEnv.amplitude, "FilterEnvDepth");
+
+ filterEnv.export(this, "Filter");
+ ampEnv.export(this, "Amp");
+
+ frequency.setup(osc1.frequency);
+ frequencyScaler.setup(0.2, 1.0, 4.0);
+ cutoff.setup(filter.frequency);
+ // Allow negative filter sweeps
+ filterEnvDepth.setup(-4000.0, 2000.0, 4000.0);
+
+ // set amplitudes slightly different so that they never entirely cancel
+ osc1.amplitude.set(0.5);
+ osc2.amplitude.set(0.4);
+ // Make the circuit turn off when the envelope finishes to reduce CPU load.
+ ampEnv.setupAutoDisable(this);
+ // Add named port for mapping pressure.
+ amplitudeBoost.inputB.setup(1.0, 1.0, 4.0);
+ addPortAlias(amplitudeBoost.inputB, PORT_NAME_PRESSURE);
+
+ usePreset(0);
+ }
+
+ /**
+ * The first oscillator will be tuned UP by semitoneOffset/2.
+ * The second oscillator will be tuned DOWN by semitoneOffset/2.
+ * @param semitoneOffset
+ */
+ private void setDetunePitch(double semitoneOffset) {
+ double halfOffset = semitoneOffset * 0.5;
+ setDetunePitch1(halfOffset);
+ setDetunePitch2(-halfOffset);
+ }
+
+ /**
+ * Set the detuning for osc1 in semitones.
+ * @param semitoneOffset
+ */
+ private void setDetunePitch1(double semitoneOffset) {
+ double scale = AudioMath.semitonesToFrequencyScaler(semitoneOffset);
+ detuneScaler1.inputB.set(scale);
+ }
+
+ /**
+ * Set the detuning for osc2 in semitones.
+ * @param semitoneOffset
+ */
+ private void setDetunePitch2(double semitoneOffset) {
+ double scale = AudioMath.semitonesToFrequencyScaler(semitoneOffset);
+ detuneScaler2.inputB.set(scale);
+ }
+
+ @Override
+ public void noteOff(TimeStamp timeStamp) {
+ ampEnv.input.off(timeStamp);
+ filterEnv.input.off(timeStamp);
+ }
+
+ @Override
+ public void noteOn(double freq, double ampl, TimeStamp timeStamp) {
+ frequency.set(freq, timeStamp);
+ amplitude.set(ampl, timeStamp);
+ ampEnv.input.on(timeStamp);
+ filterEnv.input.on(timeStamp);
+ }
+
+ @Override
+ public UnitOutputPort getOutput() {
+ return ampEnv.output;
+ }
+
+ // Reset to basic voice.
+ public void reset() {
+ osc1.shape.set(0.0);
+ osc2.shape.set(0.0);
+ ampEnv.attack.set(0.005);
+ ampEnv.decay.set(0.2);
+ ampEnv.sustain.set(0.5);
+ ampEnv.release.set(1.0);
+ filterEnv.attack.set(0.01);
+ filterEnv.decay.set(0.6);
+ filterEnv.sustain.set(0.4);
+ filterEnv.release.set(1.0);
+ cutoff.set(500.0);
+ filterEnvDepth.set(3000.0);
+ filter.reset();
+ filter.Q.set(3.9);
+ setDetunePitch(0.02);
+ }
+
+ @Override
+ public void usePreset(int presetIndex) {
+ reset(); // start from known configuration
+ int n = presetIndex % presetNames.length;
+ switch (n) {
+ case 0:
+ break;
+ case 1:
+ ampEnv.attack.set(0.1);
+ ampEnv.decay.set(0.9);
+ ampEnv.sustain.set(0.1);
+ ampEnv.release.set(0.1);
+ cutoff.set(500.0);
+ filterEnvDepth.set(500.0);
+ filter.Q.set(3.0);
+ break;
+ case 2:
+ ampEnv.attack.set(0.1);
+ ampEnv.decay.set(0.3);
+ ampEnv.release.set(0.5);
+ cutoff.set(2000.0);
+ filterEnvDepth.set(500.0);
+ filter.Q.set(2.0);
+ break;
+ case 3:
+ osc1.shape.set(-0.9);
+ osc2.shape.set(-0.8);
+ ampEnv.attack.set(0.3);
+ ampEnv.decay.set(0.8);
+ ampEnv.release.set(0.2);
+ filterEnv.sustain.set(0.7);
+ cutoff.set(500.0);
+ filterEnvDepth.set(500.0);
+ filter.Q.set(3.0);
+ break;
+ case 4:
+ osc1.shape.set(1.0);
+ osc2.shape.set(0.0);
+ break;
+ case 5:
+ osc1.shape.set(1.0);
+ setDetunePitch1(0.0);
+ osc2.shape.set(0.9);
+ setDetunePitch1(7.0);
+ break;
+ case 6:
+ osc1.shape.set(0.6);
+ osc2.shape.set(-0.2);
+ setDetunePitch1(0.01);
+ ampEnv.attack.set(0.005);
+ ampEnv.decay.set(0.09);
+ ampEnv.sustain.set(0.0);
+ ampEnv.release.set(1.0);
+ filterEnv.attack.set(0.005);
+ filterEnv.decay.set(0.1);
+ filterEnv.sustain.set(0.4);
+ filterEnv.release.set(1.0);
+ cutoff.set(2000.0);
+ filterEnvDepth.set(5000.0);
+ filter.Q.set(7.02);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static final String[] presetNames = {
+ "FastSaw", "SlowSaw", "BrightSaw",
+ "SoftSine", "SquareSaw", "SquareFifth",
+ "Blip"
+ };
+
+ static class MyVoiceDescription extends VoiceDescription {
+ String[] tags = {
+ "electronic", "filter", "analog", "subtractive"
+ };
+
+ public MyVoiceDescription() {
+ super(DualOscillatorSynthVoice.class.getName(), presetNames);
+ }
+
+ @Override
+ public UnitVoice createUnitVoice() {
+ return new DualOscillatorSynthVoice();
+ }
+
+ @Override
+ public String[] getTags(int presetIndex) {
+ return tags;
+ }
+
+ @Override
+ public String getVoiceClassName() {
+ return DualOscillatorSynthVoice.class.getName();
+ }
+ }
+
+ public static VoiceDescription getVoiceDescription() {
+ if (voiceDescription == null) {
+ voiceDescription = new MyVoiceDescription();
+ }
+ return voiceDescription;
+ }
+
+
+}
diff --git a/src/main/java/com/jsyn/instruments/JSynInstrumentLibrary.java b/src/main/java/com/jsyn/instruments/JSynInstrumentLibrary.java
new file mode 100644
index 0000000..9f111c3
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/JSynInstrumentLibrary.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 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.instruments;
+
+import com.jsyn.swing.InstrumentBrowser;
+import com.jsyn.util.InstrumentLibrary;
+import com.jsyn.util.VoiceDescription;
+
+/**
+ * Stock instruments provided with the JSyn distribution.
+ *
+ * @author Phil Burk (C) 2011 Mobileer Inc
+ * @see InstrumentBrowser
+ */
+
+public class JSynInstrumentLibrary implements InstrumentLibrary {
+ static VoiceDescription[] descriptions = {
+ WaveShapingVoice.getVoiceDescription(),
+ SubtractiveSynthVoice.getVoiceDescription(),
+ DualOscillatorSynthVoice.getVoiceDescription(),
+ NoiseHit.getVoiceDescription(),
+ DrumWoodFM.getVoiceDescription()
+ };
+
+ @Override
+ public VoiceDescription[] getVoiceDescriptions() {
+ return descriptions;
+ }
+
+ @Override
+ public String getName() {
+ return "JSynInstruments";
+ }
+}
diff --git a/src/main/java/com/jsyn/instruments/NoiseHit.java b/src/main/java/com/jsyn/instruments/NoiseHit.java
new file mode 100644
index 0000000..b8714fc
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/NoiseHit.java
@@ -0,0 +1,114 @@
+/*
+ * 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.instruments;
+
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.unitgen.Circuit;
+import com.jsyn.unitgen.EnvelopeAttackDecay;
+import com.jsyn.unitgen.PinkNoise;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Cheap synthetic cymbal sound.
+ */
+public class NoiseHit extends Circuit implements UnitVoice {
+ EnvelopeAttackDecay ampEnv;
+ PinkNoise noise;
+ private static final int NUM_PRESETS = 3;
+
+ public NoiseHit() {
+ // Create unit generators.
+ add(noise = new PinkNoise());
+ add(ampEnv = new EnvelopeAttackDecay());
+ noise.output.connect(ampEnv.amplitude);
+
+ ampEnv.export(this, "Amp");
+
+ // Make the circuit turn off when the envelope finishes to reduce CPU load.
+ ampEnv.setupAutoDisable(this);
+
+ usePreset(0);
+ }
+
+ @Override
+ public void noteOff(TimeStamp timeStamp) {
+ }
+
+ @Override
+ public void noteOn(double freq, double ampl, TimeStamp timeStamp) {
+ noise.amplitude.set(ampl, timeStamp);
+ ampEnv.input.trigger();
+ }
+
+ @Override
+ public UnitOutputPort getOutput() {
+ return ampEnv.output;
+ }
+
+ @Override
+ public void usePreset(int presetIndex) {
+ int n = presetIndex % NUM_PRESETS;
+ switch (n) {
+ case 0:
+ ampEnv.attack.set(0.001);
+ ampEnv.decay.set(0.1);
+ break;
+ case 1:
+ ampEnv.attack.set(0.03);
+ ampEnv.decay.set(1.4);
+ break;
+ default:
+ ampEnv.attack.set(0.9);
+ ampEnv.decay.set(0.3);
+ break;
+ }
+ }
+
+ static class MyVoiceDescription extends VoiceDescription {
+ static String[] presetNames = {
+ "ShortNoiseHit", "LongNoiseHit", "SlowNoiseHit"
+ };
+ static String[] tags = {
+ "electronic", "noise"
+ };
+
+ public MyVoiceDescription() {
+ super("NoiseHit", presetNames);
+ }
+
+ @Override
+ public UnitVoice createUnitVoice() {
+ return new NoiseHit();
+ }
+
+ @Override
+ public String[] getTags(int presetIndex) {
+ return tags;
+ }
+
+ @Override
+ public String getVoiceClassName() {
+ return NoiseHit.class.getName();
+ }
+ }
+
+ public static VoiceDescription getVoiceDescription() {
+ return new MyVoiceDescription();
+ }
+}
diff --git a/src/main/java/com/jsyn/instruments/SubtractiveSynthVoice.java b/src/main/java/com/jsyn/instruments/SubtractiveSynthVoice.java
new file mode 100644
index 0000000..5cfc4b9
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/SubtractiveSynthVoice.java
@@ -0,0 +1,182 @@
+/*
+ * 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.instruments;
+
+import com.jsyn.ports.UnitInputPort;
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.unitgen.Add;
+import com.jsyn.unitgen.Circuit;
+import com.jsyn.unitgen.EnvelopeDAHDSR;
+import com.jsyn.unitgen.FilterLowPass;
+import com.jsyn.unitgen.Multiply;
+import com.jsyn.unitgen.SawtoothOscillatorBL;
+import com.jsyn.unitgen.UnitOscillator;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Typical synthesizer voice with one oscillator and a biquad resonant filter. Modulate the amplitude and
+ * filter using DAHDSR envelopes.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class SubtractiveSynthVoice extends Circuit implements UnitVoice {
+ private UnitOscillator osc;
+ private FilterLowPass filter;
+ private EnvelopeDAHDSR ampEnv;
+ private EnvelopeDAHDSR filterEnv;
+ private Add cutoffAdder;
+ private Multiply frequencyScaler;
+
+ public UnitInputPort amplitude;
+ public UnitInputPort frequency;
+ /**
+ * This scales the frequency value. You can use this to modulate a group of instruments using a
+ * shared LFO and they will stay in tune.
+ */
+ public UnitInputPort pitchModulation;
+ public UnitInputPort cutoff;
+ public UnitInputPort cutoffRange;
+ public UnitInputPort Q;
+
+ public SubtractiveSynthVoice() {
+ add(frequencyScaler = new Multiply());
+ // Add a tone generator.
+ add(osc = new SawtoothOscillatorBL());
+
+ // Use an envelope to control the amplitude.
+ add(ampEnv = new EnvelopeDAHDSR());
+
+ // Use an envelope to control the filter cutoff.
+ add(filterEnv = new EnvelopeDAHDSR());
+ add(filter = new FilterLowPass());
+ add(cutoffAdder = new Add());
+
+ filterEnv.output.connect(cutoffAdder.inputA);
+ cutoffAdder.output.connect(filter.frequency);
+ frequencyScaler.output.connect(osc.frequency);
+ osc.output.connect(filter.input);
+ filter.output.connect(ampEnv.amplitude);
+
+ addPort(amplitude = osc.amplitude, "Amplitude");
+ addPort(frequency = frequencyScaler.inputA, "Frequency");
+ addPort(pitchModulation = frequencyScaler.inputB, "PitchMod");
+ addPort(cutoff = cutoffAdder.inputB, "Cutoff");
+ addPort(cutoffRange = filterEnv.amplitude, "CutoffRange");
+ addPort(Q = filter.Q);
+
+ ampEnv.export(this, "Amp");
+ filterEnv.export(this, "Filter");
+
+ frequency.setup(osc.frequency);
+ pitchModulation.setup(0.2, 1.0, 4.0);
+ cutoff.setup(filter.frequency);
+ cutoffRange.setup(filter.frequency);
+
+ // Make the circuit turn off when the envelope finishes to reduce CPU load.
+ ampEnv.setupAutoDisable(this);
+
+ usePreset(0);
+ }
+
+ @Override
+ public void noteOff(TimeStamp timeStamp) {
+ ampEnv.input.off(timeStamp);
+ filterEnv.input.off(timeStamp);
+ }
+
+ @Override
+ public void noteOn(double freq, double ampl, TimeStamp timeStamp) {
+ frequency.set(freq, timeStamp);
+ amplitude.set(ampl, timeStamp);
+
+ ampEnv.input.on(timeStamp);
+ filterEnv.input.on(timeStamp);
+ }
+
+ @Override
+ public UnitOutputPort getOutput() {
+ return ampEnv.output;
+ }
+
+ @Override
+ public void usePreset(int presetIndex) {
+ int n = presetIndex % presetNames.length;
+ switch (n) {
+ case 0:
+ ampEnv.attack.set(0.01);
+ ampEnv.decay.set(0.2);
+ ampEnv.release.set(1.0);
+ cutoff.set(500.0);
+ cutoffRange.set(500.0);
+ filter.Q.set(1.0);
+ break;
+ case 1:
+ ampEnv.attack.set(0.5);
+ ampEnv.decay.set(0.3);
+ ampEnv.release.set(0.2);
+ cutoff.set(500.0);
+ cutoffRange.set(500.0);
+ filter.Q.set(3.0);
+ break;
+ case 2:
+ default:
+ ampEnv.attack.set(0.1);
+ ampEnv.decay.set(0.3);
+ ampEnv.release.set(0.5);
+ cutoff.set(2000.0);
+ cutoffRange.set(500.0);
+ filter.Q.set(2.0);
+ break;
+ }
+ }
+
+ static String[] presetNames = {
+ "FastSaw", "SlowSaw", "BrightSaw"
+ };
+
+ static class MyVoiceDescription extends VoiceDescription {
+ String[] tags = {
+ "electronic", "filter", "clean"
+ };
+
+ public MyVoiceDescription() {
+ super("SubtractiveSynth", presetNames);
+ }
+
+ @Override
+ public UnitVoice createUnitVoice() {
+ return new SubtractiveSynthVoice();
+ }
+
+ @Override
+ public String[] getTags(int presetIndex) {
+ return tags;
+ }
+
+ @Override
+ public String getVoiceClassName() {
+ return SubtractiveSynthVoice.class.getName();
+ }
+ }
+
+ public static VoiceDescription getVoiceDescription() {
+ return new MyVoiceDescription();
+ }
+
+}
diff --git a/src/main/java/com/jsyn/instruments/WaveShapingVoice.java b/src/main/java/com/jsyn/instruments/WaveShapingVoice.java
new file mode 100644
index 0000000..5044f21
--- /dev/null
+++ b/src/main/java/com/jsyn/instruments/WaveShapingVoice.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2011 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.instruments;
+
+import com.jsyn.data.DoubleTable;
+import com.jsyn.ports.UnitFunctionPort;
+import com.jsyn.ports.UnitInputPort;
+import com.jsyn.ports.UnitOutputPort;
+import com.jsyn.unitgen.Circuit;
+import com.jsyn.unitgen.EnvelopeDAHDSR;
+import com.jsyn.unitgen.FunctionEvaluator;
+import com.jsyn.unitgen.Multiply;
+import com.jsyn.unitgen.SineOscillator;
+import com.jsyn.unitgen.UnitOscillator;
+import com.jsyn.unitgen.UnitVoice;
+import com.jsyn.util.VoiceDescription;
+import com.softsynth.math.ChebyshevPolynomial;
+import com.softsynth.math.PolynomialTableData;
+import com.softsynth.shared.time.TimeStamp;
+
+/**
+ * Waveshaping oscillator with envelopes.
+ *
+ * @author Phil Burk (C) 2011 Mobileer Inc
+ */
+public class WaveShapingVoice extends Circuit implements UnitVoice {
+ private static final long serialVersionUID = -2704222221111608377L;
+ private static final int NUM_PRESETS = 3;
+ private UnitOscillator osc;
+ private FunctionEvaluator waveShaper;
+ private EnvelopeDAHDSR ampEnv;
+ private EnvelopeDAHDSR rangeEnv;
+ private Multiply frequencyScaler;
+
+ public UnitInputPort range;
+ public UnitInputPort frequency;
+ public UnitInputPort amplitude;
+ public UnitFunctionPort function;
+ public UnitInputPort pitchModulation;
+
+ // default Chebyshev polynomial table to share.
+ private static DoubleTable chebyshevTable;
+ private final static int CHEBYSHEV_ORDER = 11;
+
+ static {
+ // Make table with Chebyshev polynomial to share among voices
+ PolynomialTableData chebData = new PolynomialTableData(
+ ChebyshevPolynomial.T(CHEBYSHEV_ORDER), 1024);
+ chebyshevTable = new DoubleTable(chebData.getData());
+ }
+
+ public WaveShapingVoice() {
+ add(frequencyScaler = new Multiply());
+ add(osc = new SineOscillator());
+ add(waveShaper = new FunctionEvaluator());
+ add(rangeEnv = new EnvelopeDAHDSR());
+ add(ampEnv = new EnvelopeDAHDSR());
+
+ addPort(amplitude = ampEnv.amplitude);
+ addPort(range = osc.amplitude, "Range");
+ addPort(function = waveShaper.function);
+ addPort(frequency = frequencyScaler.inputA, "Frequency");
+ addPort(pitchModulation = frequencyScaler.inputB, "PitchMod");
+
+ ampEnv.export(this, "Amp");
+ rangeEnv.export(this, "Range");
+
+ function.set(chebyshevTable);
+
+ // Connect units.
+ osc.output.connect(rangeEnv.amplitude);
+ rangeEnv.output.connect(waveShaper.input);
+ ampEnv.output.connect(waveShaper.amplitude);
+ frequencyScaler.output.connect(osc.frequency);
+
+ // Set reasonable defaults for the ports.
+ pitchModulation.setup(0.1, 1.0, 10.0);
+ range.setup(0.1, 0.8, 1.0);
+ frequency.setup(osc.frequency);
+ amplitude.setup(0.0, 0.5, 1.0);
+
+ // Make the circuit turn off when the envelope finishes to reduce CPU load.
+ ampEnv.setupAutoDisable(this);
+
+ usePreset(2);
+ }
+
+ @Override
+ public UnitOutputPort getOutput() {
+ return waveShaper.output;
+ }
+
+ @Override
+ public void noteOn(double freq, double amp, TimeStamp timeStamp) {
+ frequency.set(freq, timeStamp);
+ amplitude.set(amp, timeStamp);
+ ampEnv.input.on(timeStamp);
+ rangeEnv.input.on(timeStamp);
+ }
+
+ @Override
+ public void noteOff(TimeStamp timeStamp) {
+ ampEnv.input.off(timeStamp);
+ rangeEnv.input.off(timeStamp);
+ }
+
+ @Override
+ public void usePreset(int presetIndex) {
+ int n = presetIndex % NUM_PRESETS;
+ switch (n) {
+ case 0:
+ ampEnv.attack.set(0.01);
+ ampEnv.decay.set(0.2);
+ ampEnv.release.set(1.0);
+ rangeEnv.attack.set(0.01);
+ rangeEnv.decay.set(0.2);
+ rangeEnv.sustain.set(0.4);
+ rangeEnv.release.set(1.0);
+ break;
+ case 1:
+ ampEnv.attack.set(0.5);
+ ampEnv.decay.set(0.3);
+ ampEnv.release.set(0.2);
+ rangeEnv.attack.set(0.03);
+ rangeEnv.decay.set(0.2);
+ rangeEnv.sustain.set(0.5);
+ rangeEnv.release.set(1.0);
+ break;
+ default:
+ ampEnv.attack.set(0.1);
+ ampEnv.decay.set(0.3);
+ ampEnv.release.set(0.5);
+ rangeEnv.attack.set(0.01);
+ rangeEnv.decay.set(0.2);
+ rangeEnv.sustain.set(0.9);
+ rangeEnv.release.set(1.0);
+ break;
+ }
+ }
+
+ static class MyVoiceDescription extends VoiceDescription {
+ static String[] presetNames = {
+ "FastChebyshev", "SlowChebyshev", "BrightChebyshev"
+ };
+ static String[] tags = {
+ "electronic", "waveshaping", "clean"
+ };
+
+ public MyVoiceDescription() {
+ super("Waveshaping", presetNames);
+ }
+
+ @Override
+ public UnitVoice createUnitVoice() {
+ return new WaveShapingVoice();
+ }
+
+ @Override
+ public String[] getTags(int presetIndex) {
+ return tags;
+ }
+
+ @Override
+ public String getVoiceClassName() {
+ return WaveShapingVoice.class.getName();
+ }
+ }
+
+ public static VoiceDescription getVoiceDescription() {
+ return new MyVoiceDescription();
+ }
+
+}