aboutsummaryrefslogtreecommitdiffstats
path: root/src/test/java/com/jsyn/unitgen
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/jsyn/unitgen')
-rw-r--r--src/test/java/com/jsyn/unitgen/CalibrateMoogFilter.java141
-rw-r--r--src/test/java/com/jsyn/unitgen/EnablingGate.java51
-rw-r--r--src/test/java/com/jsyn/unitgen/NonRealTimeTestCase.java42
-rw-r--r--src/test/java/com/jsyn/unitgen/RecordMoogFilter.java158
-rw-r--r--src/test/java/com/jsyn/unitgen/TestConnections.java111
-rw-r--r--src/test/java/com/jsyn/unitgen/TestDelay.java81
-rw-r--r--src/test/java/com/jsyn/unitgen/TestEnable.java81
-rw-r--r--src/test/java/com/jsyn/unitgen/TestEnvelopeAttackDecay.java130
-rw-r--r--src/test/java/com/jsyn/unitgen/TestEnvelopeDAHDSR.java355
-rw-r--r--src/test/java/com/jsyn/unitgen/TestFunction.java77
-rw-r--r--src/test/java/com/jsyn/unitgen/TestMath.java420
-rw-r--r--src/test/java/com/jsyn/unitgen/TestRamps.java205
-rw-r--r--src/test/java/com/jsyn/unitgen/TestUnitGate.java81
13 files changed, 1933 insertions, 0 deletions
diff --git a/src/test/java/com/jsyn/unitgen/CalibrateMoogFilter.java b/src/test/java/com/jsyn/unitgen/CalibrateMoogFilter.java
new file mode 100644
index 0000000..1e74aa8
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/CalibrateMoogFilter.java
@@ -0,0 +1,141 @@
+/*
+ * 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.unitgen;
+
+import javax.swing.JApplet;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+
+/**
+ * Play a sawtooth through a 4-pole filter.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class CalibrateMoogFilter extends JApplet {
+ private Synthesizer synth;
+ private UnitOscillator oscillator;
+ private SineOscillator reference;
+ ZeroCrossingCounter zeroCounter;
+ PitchDetector pitchDetector;
+ ZeroCrossingCounter sineZeroCounter;
+ PitchDetector sinePitchDetector;
+ private FilterFourPoles filterMoog;
+ private LineOut lineOut;
+
+ @Override
+ public void init() {
+ synth = JSyn.createSynthesizer();
+ synth.setRealTime(false);
+ synth.add(oscillator = new SawtoothOscillatorBL());
+ synth.add(reference = new SineOscillator());
+ synth.add(filterMoog = new FilterFourPoles());
+ synth.add(pitchDetector = new PitchDetector());
+ synth.add(sinePitchDetector = new PitchDetector());
+ synth.add(zeroCounter = new ZeroCrossingCounter());
+ synth.add(sineZeroCounter = new ZeroCrossingCounter());
+ synth.add(lineOut = new LineOut());
+
+ oscillator.output.connect(filterMoog.input);
+ filterMoog.output.connect(zeroCounter.input);
+ zeroCounter.output.connect(pitchDetector.input);
+ reference.output.connect(0, lineOut.input, 0);
+ filterMoog.output.connect(0, lineOut.input, 1);
+
+ reference.output.connect(sineZeroCounter.input);
+ sineZeroCounter.output.connect(sinePitchDetector.input);
+
+ oscillator.frequency.set(130.0);
+ oscillator.amplitude.set(0.001);
+ filterMoog.frequency.set(440.0);
+ filterMoog.Q.set(4.1);
+ }
+
+ @Override
+ public void start() {
+ // Start synthesizer using default stereo output at 44100 Hz.
+ synth.start();
+ pitchDetector.start();
+ sinePitchDetector.start();
+ lineOut.start();
+ }
+
+ @Override
+ public void stop() {
+ pitchDetector.stop();
+ sinePitchDetector.stop();
+ lineOut.stop();
+ synth.stop();
+ }
+
+ public void test() {
+ init();
+ start();
+ try {
+ calibrate();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ stop();
+ }
+
+ private void calibrate() throws InterruptedException {
+ synth.sleepFor(2.0);
+ double freq = 100.0;
+ System.out
+ .printf("freq, moogFreq, ratio, moogConf, sineFreq, sineConf, moogZRate, sineZRate\n");
+ long startingFrameCount = synth.getFrameCount();
+ long startingMoogZeroCount = zeroCounter.getCount();
+ long startingSineZeroCount = sineZeroCounter.getCount();
+ for (int i = 0; i < 50; i++) {
+ reference.frequency.set(freq);
+ filterMoog.frequency.set(freq);
+ synth.sleepFor(2.0);
+
+ long endingFrameCount = synth.getFrameCount();
+ long elapsedFrames = endingFrameCount - startingFrameCount;
+ startingFrameCount = endingFrameCount;
+
+ long endingMoogZeroCount = zeroCounter.getCount();
+ long elapsedMoogZeros = endingMoogZeroCount - startingMoogZeroCount;
+ startingMoogZeroCount = endingMoogZeroCount;
+
+ long endingSineZeroCount = sineZeroCounter.getCount();
+ long elapsedSineZeros = endingSineZeroCount - startingSineZeroCount;
+ startingSineZeroCount = endingSineZeroCount;
+
+ double moogZeroRate = elapsedMoogZeros * (double) synth.getFrameRate() / elapsedFrames;
+ double sineZeroRate = elapsedSineZeros * (double) synth.getFrameRate() / elapsedFrames;
+
+ double moogMeasuredFreq = pitchDetector.frequency.get();
+ double moogConfidence = pitchDetector.confidence.get();
+ double sineMeasuredFreq = sinePitchDetector.frequency.get();
+ double sineConfidence = sinePitchDetector.confidence.get();
+ double ratio = freq / moogMeasuredFreq;
+ System.out.printf("%7.2f, %8.5f, %7.5f, %4.2f, %8.5f, %4.2f, %8.4f, %8.4f\n", freq,
+ moogMeasuredFreq, ratio, moogConfidence, sineMeasuredFreq, sineConfidence,
+ moogZeroRate, sineZeroRate);
+
+ freq *= 1.1;
+ }
+ }
+
+ public static void main(String[] args) {
+ new CalibrateMoogFilter().test();
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/EnablingGate.java b/src/test/java/com/jsyn/unitgen/EnablingGate.java
new file mode 100644
index 0000000..daf36be
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/EnablingGate.java
@@ -0,0 +1,51 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.ports.UnitInputPort;
+
+/**
+ * This can be used to block the execution of upstream units. It can be placed at the output of a
+ * circuit and driven with an amplitude envelope.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class EnablingGate extends UnitFilter {
+ public UnitInputPort gate;
+
+ /* Define Unit Ports used by connect() and set(). */
+ public EnablingGate() {
+ super();
+ addPort(gate = new UnitInputPort("Gate"));
+ }
+
+ @Override
+ public void generate(int start, int limit) {
+ double[] aValues = input.getValues();
+ double[] bValues = gate.getValues();
+ double[] outputs = output.getValues();
+ for (int i = start; i < limit; i++) {
+ outputs[i] = aValues[i] * bValues[i];
+ }
+ // If we end up at zero then disable pulling of data.
+ // We do this at the end so that envelope can get started.
+ if (outputs[limit - 1] <= 0.0) {
+ setEnabled(false);
+ }
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/NonRealTimeTestCase.java b/src/test/java/com/jsyn/unitgen/NonRealTimeTestCase.java
new file mode 100644
index 0000000..bec5762
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/NonRealTimeTestCase.java
@@ -0,0 +1,42 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+public abstract class NonRealTimeTestCase {
+
+ protected SynthesisEngine synthesisEngine;
+
+ public NonRealTimeTestCase() {
+ super();
+ }
+
+ @BeforeEach
+ private void setUp() {
+ synthesisEngine = new SynthesisEngine();
+ synthesisEngine.setRealTime(false);
+ }
+
+ @AfterEach
+ private void tearDown() {
+ synthesisEngine.stop();
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/RecordMoogFilter.java b/src/test/java/com/jsyn/unitgen/RecordMoogFilter.java
new file mode 100644
index 0000000..31a86be
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/RecordMoogFilter.java
@@ -0,0 +1,158 @@
+/*
+ * 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.unitgen;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.swing.JApplet;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import com.jsyn.util.WaveRecorder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Measure actual frequency as a function of input frequency and Q.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class RecordMoogFilter extends JApplet {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RecordMoogFilter.class);
+
+ private final static boolean SWEEP_Q = false;
+ private final static boolean SWEEP_FREQUENCY = true;
+ private final static int NUM_STEPS = 11;
+
+ private final static double MIN_Q = 0.0;
+ private final static double DEFAULT_Q = 9.0;
+ private final static double MAX_Q = 10.0;
+
+ private final static double MIN_FREQUENCY = 100.0;
+ private final static double DEFAULT_FREQUENCY = 500.0;
+ private final static double MAX_FREQUENCY = 4000.0;
+
+ private Synthesizer synth;
+ private WhiteNoise source;
+ private SineOscillator reference;
+ private FilterFourPoles filterMoog;
+ private LineOut lineOut;
+ private WaveRecorder recorder;
+
+ @Override
+ public void init() {
+ synth = JSyn.createSynthesizer();
+ synth.setRealTime(false);
+ synth.add(source = new WhiteNoise());
+ synth.add(filterMoog = new FilterFourPoles());
+ synth.add(reference = new SineOscillator());
+ synth.add(lineOut = new LineOut());
+
+ source.output.connect(filterMoog.input);
+ reference.output.connect(0, lineOut.input, 0);
+ filterMoog.output.connect(0, lineOut.input, 1);
+
+ reference.amplitude.set(0.5);
+ source.amplitude.set(0.5);
+ filterMoog.frequency.set(DEFAULT_FREQUENCY);
+ filterMoog.Q.set(DEFAULT_Q);
+
+ File waveFile = new File("temp_recording.wav");
+ // Default is stereo, 16 bits.
+ try {
+ recorder = new WaveRecorder(synth, waveFile);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ LOGGER.debug("Writing to WAV file " + waveFile.getAbsolutePath());
+ }
+
+ @Override
+ public void start() {
+ // Start synthesizer using default stereo output at 44100 Hz.
+ synth.start();
+ lineOut.start();
+
+ reference.output.connect(0, recorder.getInput(), 0);
+ filterMoog.output.connect(0, recorder.getInput(), 1);
+ recorder.start();
+ }
+
+ @Override
+ public void stop() {
+ if (recorder != null) {
+ recorder.stop();
+ try {
+ recorder.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ lineOut.stop();
+ synth.stop();
+ }
+
+ public void test() {
+ init();
+ start();
+ try {
+ calibrate();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ stop();
+ }
+
+ private void calibrate() throws InterruptedException {
+ synth.sleepFor(0.2);
+ double freq = SWEEP_FREQUENCY ? MIN_FREQUENCY : DEFAULT_FREQUENCY;
+ double q = SWEEP_Q ? MIN_Q : DEFAULT_Q;
+ double stepQ = (MAX_Q - MIN_Q) / (NUM_STEPS - 1);
+ double scaleFrequency = Math.pow((MAX_FREQUENCY / MIN_FREQUENCY), (1.0 / (NUM_STEPS - 1)));
+ System.out.printf("freq, q, measured\n");
+ for (int i = 0; i < NUM_STEPS; i++) {
+ double refAmp = reference.amplitude.get();
+ reference.amplitude.set(0.0);
+ synth.sleepFor(0.1);
+ reference.amplitude.set(refAmp);
+
+ System.out.printf("%8.2f, %6.3f, \n", freq, q);
+ filterMoog.frequency.set(freq);
+ reference.frequency.set(freq);
+ filterMoog.Q.set(q);
+
+ synth.sleepFor(2.0);
+
+ if (SWEEP_FREQUENCY) {
+ freq *= scaleFrequency;
+ }
+ if (SWEEP_Q) {
+ q += stepQ;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ new RecordMoogFilter().test();
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestConnections.java b/src/test/java/com/jsyn/unitgen/TestConnections.java
new file mode 100644
index 0000000..9aee32f
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestConnections.java
@@ -0,0 +1,111 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestConnections {
+ private Synthesizer synth;
+
+ private Add add1;
+ private Add add2;
+ private Add add3;
+
+ @BeforeEach
+ private void beforeEach() {
+ synth = JSyn.createSynthesizer();
+
+ synth.add(add1 = new Add());
+ synth.add(add2 = new Add());
+ synth.add(add3 = new Add());
+
+ add1.start();
+ add2.start();
+ add3.start();
+
+ add1.inputA.set(0.1);
+ add1.inputB.set(0.2);
+
+ add2.inputA.set(0.4);
+ add2.inputB.set(0.8);
+
+ add3.inputA.set(1.6);
+ add3.inputB.set(3.2);
+ }
+
+ @Test
+ public void testSet() throws InterruptedException {
+ synth.sleepFor(0.01);
+ assertEquals(0.3, add1.output.getValue(), 0.0001, "set inputs of adder");
+ }
+
+ @Test
+ public void testConnect() throws InterruptedException {
+ synth.sleepFor(0.01);
+ assertEquals(0.3, add1.output.getValue(), 0.0001, "set inputs of adder");
+ assertEquals(1.2, add2.output.getValue(), 0.0001, "set inputs of adder");
+
+ // Test different ways of connecting.
+ add1.output.connect(add2.inputB);
+ checkConnection();
+
+ add1.output.connect(0, add2.inputB, 0);
+ checkConnection();
+
+ add1.output.connect(add2.inputB.getConnectablePart(0));
+ checkConnection();
+
+ add1.output.getConnectablePart(0).connect(add2.inputB);
+ checkConnection();
+
+ add1.output.getConnectablePart(0).connect(add2.inputB.getConnectablePart(0));
+ checkConnection();
+
+ add2.inputB.connect(add1.output);
+ checkConnection();
+
+ add2.inputB.connect(0, add1.output, 0);
+ checkConnection();
+
+ add2.inputB.connect(add1.output.getConnectablePart(0));
+ checkConnection();
+
+ add2.inputB.getConnectablePart(0).connect(add1.output);
+ checkConnection();
+
+ add2.inputB.getConnectablePart(0).connect(add1.output.getConnectablePart(0));
+ checkConnection();
+ }
+
+ private void checkConnection() throws InterruptedException {
+ synth.sleepFor(0.01);
+ assertEquals(0.3, add1.output.getValue(), 0.0001, "connection should not change output");
+ assertEquals(0.7, add2.output.getValue(), 0.0001, "replace set value with output");
+
+ // Revert to set value after disconnection.
+ add1.output.disconnectAll();
+ synth.sleepFor(0.01);
+ assertEquals(0.3, add1.output.getValue(), 0.0001, "still the same");
+ assertEquals(1.2, add2.output.getValue(), 0.0001, "should revert to original set() value");
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestDelay.java b/src/test/java/com/jsyn/unitgen/TestDelay.java
new file mode 100644
index 0000000..7e1a0b1
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestDelay.java
@@ -0,0 +1,81 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.util.AudioStreamReader;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestDelay extends NonRealTimeTestCase {
+
+ @Test
+ public void testFloor() {
+ double x = -7.3;
+ int n = (int) Math.floor(x);
+ assertEquals(-8, n, "int");
+ }
+
+ public void checkInterpolatingDelay(int maxFrames, double delayFrames)
+ throws InterruptedException {
+ synthesisEngine.start();
+
+ System.out.printf("test delayFrames = %7.5f\n", delayFrames);
+ InterpolatingDelay delay = new InterpolatingDelay();
+ synthesisEngine.add(delay);
+ delay.allocate(maxFrames);
+ delay.delay.set(delayFrames / 44100.0);
+ SawtoothOscillator osc = new SawtoothOscillator();
+ synthesisEngine.add(osc);
+ osc.frequency.set(synthesisEngine.getFrameRate() / 4.0);
+ osc.amplitude.set(1.0);
+ osc.output.connect(delay.input);
+
+ int samplesPerFrame = 1;
+ AudioStreamReader reader = new AudioStreamReader(synthesisEngine, samplesPerFrame);
+ delay.output.connect(reader.getInput());
+
+ delay.start();
+ for (int i = 0; i < (3 * maxFrames); i++) {
+ if (reader.available() == 0) {
+ synthesisEngine.sleepFor(0.01);
+ }
+ double actual = reader.read();
+ double expected = 1 + i - delayFrames;
+ if (expected < 0.0) {
+ expected = 0.0;
+ }
+ // System.out.printf( "[%d] expected = %7.3f, delayed = %7.3f\n", i, expected, actual );
+ // assertEquals(expected, actual, 0.00001, "delayed output");
+ }
+ }
+
+ @Test
+ public void testSmall() throws InterruptedException {
+ checkInterpolatingDelay(40, 7.0);
+ }
+
+ @Test
+ public void testEven() throws InterruptedException {
+ checkInterpolatingDelay(44100, 13671.0);
+ }
+
+ @Test
+ public void testInterpolatingDelay() throws InterruptedException {
+ checkInterpolatingDelay(44100, 13671.4);
+ }
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestEnable.java b/src/test/java/com/jsyn/unitgen/TestEnable.java
new file mode 100644
index 0000000..a244c61
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestEnable.java
@@ -0,0 +1,81 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class TestEnable {
+ private SynthesisEngine synthesisEngine;
+
+ @BeforeEach
+ protected void beforeEach() {
+ synthesisEngine = new SynthesisEngine();
+ synthesisEngine.setRealTime(false);
+ }
+
+ @AfterEach
+ protected void afterEach() {
+ synthesisEngine.stop();
+ }
+
+ @Test
+ public void testEnablingGate() throws InterruptedException {
+ LinearRamp ramp = new LinearRamp();
+ synthesisEngine.add(ramp);
+ EnablingGate enabler = new EnablingGate();
+ synthesisEngine.add(enabler);
+ Add adder = new Add();
+ synthesisEngine.add(adder);
+
+ ramp.output.connect(enabler.input);
+ enabler.output.connect(adder.inputA);
+
+ // set up so ramp should equal time
+ ramp.current.set(0.0);
+ ramp.input.set(1.0);
+ ramp.time.set(1.0);
+ enabler.gate.set(1.0);
+
+ synthesisEngine.start();
+ double startTime = synthesisEngine.getCurrentTime();
+ // pull from final adder
+ adder.start();
+ synthesisEngine.sleepUntil(startTime + 0.1);
+ double tolerance = 0.002;
+ assertEquals(0.1, ramp.output.getValue(), tolerance, "ramp going up");
+ assertEquals(0.1, enabler.output.getValue(), tolerance, "enabler going up");
+ assertEquals(0.1, adder.output.getValue(), tolerance, "adder going up");
+ synthesisEngine.sleepUntil(startTime + 0.2);
+ assertEquals(0.2, adder.output.getValue(), tolerance, "start enabled");
+
+ // disable everything upstream
+ enabler.gate.set(0.0);
+
+ synthesisEngine.sleepUntil(startTime + 0.3);
+ assertEquals(0.2, ramp.output.getValue(), tolerance, "should not be pulled");
+ assertFalse(enabler.isEnabled(), "should be disabled");
+ assertEquals(0.0, enabler.output.getValue(), tolerance, "should be zero");
+ assertEquals(0.0, adder.output.getValue(), tolerance, "zero");
+
+ }
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestEnvelopeAttackDecay.java b/src/test/java/com/jsyn/unitgen/TestEnvelopeAttackDecay.java
new file mode 100644
index 0000000..8328bb7
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestEnvelopeAttackDecay.java
@@ -0,0 +1,130 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestEnvelopeAttackDecay extends TestUnitGate {
+ double attackTime;
+ double decayTime;
+
+ @BeforeEach
+ protected void beforeEach() {
+ synthesisEngine = new SynthesisEngine();
+ synthesisEngine.setRealTime(false);
+ attackTime = 0.2;
+ decayTime = 0.4;
+ }
+
+ @AfterEach
+ protected void afterEach() {
+ synthesisEngine.stop();
+ }
+
+ @Test
+ public void testOnOff() throws InterruptedException {
+ var envelope = new EnvelopeAttackDecay();
+ synthesisEngine.add(envelope);
+
+ envelope.attack.set(0.1);
+ envelope.decay.set(0.2);
+
+ synthesisEngine.start();
+ envelope.start();
+ time = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(time + 0.1);
+ assertEquals(0.0, envelope.output.getValue(), "still idling");
+
+ // Trigger the envelope using on/off
+ envelope.input.on();
+ time = synthesisEngine.getCurrentTime();
+ // Check end of attack cycle.
+ synthesisEngine.sleepUntil(time + 0.1);
+ assertTrue(envelope.output.getValue() > 0.8, "at peak");
+ envelope.input.off();
+ // Check end of decay cycle.
+ synthesisEngine.sleepUntil(time + 0.3);
+ assertTrue(envelope.output.getValue() < 0.1, "at peak");
+
+ synthesisEngine.sleepFor(0.1);
+
+ // Trigger the envelope using trigger()
+ envelope.input.trigger();
+ time = synthesisEngine.getCurrentTime();
+ // Check end of attack cycle.
+ synthesisEngine.sleepUntil(time + 0.1);
+ assertTrue(envelope.output.getValue() > 0.8, "at peak");
+ // Check end of decay cycle.
+ synthesisEngine.sleepUntil(time + 0.3);
+ assertTrue(envelope.output.getValue() < 0.1, "at peak");
+ }
+
+ @Test
+ public void testRetrigger() throws InterruptedException {
+ var envelope = new EnvelopeAttackDecay();
+ synthesisEngine.add(envelope);
+
+ envelope.attack.set(0.1);
+ envelope.decay.set(0.2);
+
+ synthesisEngine.start();
+ envelope.start();
+ time = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(time + 0.1);
+ assertEquals(0.0, envelope.output.getValue(), "still idling");
+
+ // Trigger the envelope using trigger()
+ envelope.input.trigger();
+ // Check end of attack cycle.
+ synthesisEngine.sleepFor(0.1);
+ assertEquals(1.0, envelope.output.getValue(), 0.1, "at peak");
+
+ // Decay half way.
+ synthesisEngine.sleepFor(0.1);
+ assertTrue(envelope.output.getValue() < 0.7, "at peak");
+
+ // Retrigger while decaying
+ envelope.input.trigger();
+ // Will get to top faster.
+ synthesisEngine.sleepFor(0.1);
+ assertEquals(1.0, envelope.output.getValue(), 0.1, "at peak");
+
+ // Check end of decay cycle.
+ synthesisEngine.sleepFor(0.2);
+ assertTrue(envelope.output.getValue() < 0.1, "at peak");
+
+ }
+
+ @Test
+ public void testAutoDisable() throws InterruptedException {
+ var ramp = new LinearRamp();
+ synthesisEngine.add(ramp);
+ var envelope = new EnvelopeAttackDecay();
+ envelope.attack.set(0.1);
+ envelope.decay.set(0.1);
+ synthesisEngine.add(envelope);
+ ramp.output.connect(envelope.amplitude);
+
+ checkAutoDisable(ramp, envelope);
+ }
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestEnvelopeDAHDSR.java b/src/test/java/com/jsyn/unitgen/TestEnvelopeDAHDSR.java
new file mode 100644
index 0000000..618e823
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestEnvelopeDAHDSR.java
@@ -0,0 +1,355 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestEnvelopeDAHDSR extends TestUnitGate {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TestEnvelopeDAHDSR.class);
+
+ double delayTime;
+ double attackTime;
+ double holdTime;
+ double decayTime;
+ double sustainLevel;
+ double releaseTime;
+
+ @BeforeEach
+ protected void beforeEach() {
+ synthesisEngine = new SynthesisEngine();
+ synthesisEngine.setRealTime(false);
+ delayTime = 0.1;
+ attackTime = 0.2;
+ holdTime = 0.3;
+ decayTime = 0.4;
+ sustainLevel = 0.5;
+ releaseTime = 0.6;
+ }
+
+ @AfterEach
+ protected void afterEach() {
+ synthesisEngine.stop();
+ }
+
+ @Test
+ public void testStages() throws InterruptedException {
+ EnvelopeDAHDSR ramp = checkToSustain();
+
+ // Change sustain level to simulate tremolo sustain.
+ sustainLevel = 0.7;
+ ramp.sustain.set(sustainLevel);
+ time += 0.01;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(sustainLevel, ramp.output.getValue(), 0.01, "sustain moving delaying");
+
+ // Gate off to let envelope release.
+ ramp.input.set(0.0);
+ synthesisEngine.sleepUntil(time + (releaseTime * 0.1));
+ double releaseValue = ramp.output.getValue();
+ assertEquals(sustainLevel * 0.36, releaseValue, 0.01, "partway down release");
+ }
+
+ private EnvelopeDAHDSR checkToSustain() throws InterruptedException {
+ EnvelopeDAHDSR ramp = new EnvelopeDAHDSR();
+ synthesisEngine.add(ramp);
+
+ ramp.delay.set(delayTime);
+ ramp.attack.set(attackTime);
+ ramp.hold.set(holdTime);
+ ramp.decay.set(decayTime);
+ ramp.sustain.set(sustainLevel);
+ ramp.release.set(releaseTime);
+
+ synthesisEngine.start();
+ ramp.start();
+ time = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(time + (2.0 * delayTime));
+ assertEquals(0.0, ramp.output.getValue(), "still idling");
+
+ // Trigger the envelope.
+ ramp.input.set(1.0);
+ time = synthesisEngine.getCurrentTime();
+ // Check end of delay cycle.
+ synthesisEngine.sleepUntil(time + (delayTime * 0.9));
+ assertEquals(0.0, ramp.output.getValue(), 0.01, "still delaying");
+ // Half way up attack ramp.
+ synthesisEngine.sleepUntil(time + delayTime + (attackTime * 0.5));
+ assertEquals(0.5, ramp.output.getValue(), 0.01, "half attack");
+ // Holding after attack.
+ synthesisEngine.sleepUntil(time + delayTime + attackTime + (holdTime * 0.1));
+ assertEquals(1.0, ramp.output.getValue(), 0.01, "holding");
+ synthesisEngine.sleepUntil(time + delayTime + attackTime + (holdTime * 0.9));
+ assertEquals(1.0, ramp.output.getValue(), 0.01, "still holding");
+ synthesisEngine.sleepUntil(time + delayTime + attackTime + holdTime + decayTime);
+ time = synthesisEngine.getCurrentTime();
+ assertEquals(sustainLevel, ramp.output.getValue(), 0.01, "at sustain");
+ return ramp;
+ }
+
+ @Test
+ public void testRetrigger() throws InterruptedException {
+ EnvelopeDAHDSR ramp = checkToSustain();
+
+ // Gate off to let envelope release.
+ ramp.input.set(0.0);
+ synthesisEngine.sleepUntil(time + (releaseTime * 0.1));
+ double releaseValue = ramp.output.getValue();
+ assertEquals(sustainLevel * 0.36, releaseValue, 0.01, "partway down release");
+
+ // Retrigger during release phase.
+ time = synthesisEngine.getCurrentTime();
+ ramp.input.set(1.0);
+ // Check end of delay cycle.
+ synthesisEngine.sleepUntil(time + (delayTime * 0.9));
+ assertEquals(releaseValue, ramp.output.getValue(), 0.01, "still delaying");
+ // Half way up attack ramp from where it started.
+ synthesisEngine.sleepUntil(time + delayTime + (attackTime * 0.5));
+ assertEquals(releaseValue + 0.5, ramp.output.getValue(), 0.01, "half attack");
+
+ }
+
+ // I noticed a hang while playing with knobs.
+ @Test
+ public void testHang() throws InterruptedException {
+
+ delayTime = 0.0;
+ attackTime = 0.0;
+ holdTime = 0.0;
+ decayTime = 0.0;
+ sustainLevel = 0.3;
+ releaseTime = 3.0;
+
+ EnvelopeDAHDSR ramp = new EnvelopeDAHDSR();
+ synthesisEngine.add(ramp);
+
+ ramp.delay.set(delayTime);
+ ramp.attack.set(attackTime);
+ ramp.hold.set(holdTime);
+ ramp.decay.set(decayTime);
+ ramp.sustain.set(sustainLevel);
+ ramp.release.set(releaseTime);
+
+ synthesisEngine.start();
+ ramp.start();
+ // Trigger the envelope.
+ ramp.input.set(1.0);
+ time = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(time + 0.01);
+ assertEquals(sustainLevel, ramp.output.getValue(), "should jump to sustain level");
+
+ // Gate off to let envelope release.
+ ramp.input.set(0.0);
+ synthesisEngine.sleepUntil(time + 1.0);
+ double releaseValue = ramp.output.getValue();
+ assertTrue(sustainLevel > releaseValue, "partway down release");
+
+ holdTime = 0.5;
+ ramp.hold.set(holdTime);
+ decayTime = 0.5;
+ ramp.decay.set(decayTime);
+
+ // Retrigger during release phase and try to catch it at top of hold
+ time = synthesisEngine.getCurrentTime();
+ ramp.input.set(1.0);
+ // Check end of delay cycle.
+ synthesisEngine.sleepUntil(time + (holdTime * 0.1));
+ assertEquals(1.0, ramp.output.getValue(), 0.01, "should jump to hold");
+ }
+
+ @Test
+ public void testNegative() throws InterruptedException {
+ delayTime = -0.1;
+ attackTime = -0.2;
+ holdTime = -0.3;
+ decayTime = -0.4;
+ sustainLevel = 0.3;
+ releaseTime = -0.5;
+
+ EnvelopeDAHDSR ramp = new EnvelopeDAHDSR();
+ synthesisEngine.add(ramp);
+
+ ramp.delay.set(delayTime);
+ ramp.attack.set(attackTime);
+ ramp.hold.set(holdTime);
+ ramp.decay.set(decayTime);
+ ramp.sustain.set(sustainLevel);
+ ramp.release.set(releaseTime);
+
+ synthesisEngine.start();
+ ramp.start();
+ // Trigger the envelope.
+ ramp.input.set(1.0);
+ time = synthesisEngine.getCurrentTime();
+ time += 0.1;
+ synthesisEngine.sleepUntil(time + 0.01);
+ assertEquals(sustainLevel, ramp.output.getValue(), "should jump to sustain level");
+
+ ramp.sustain.set(sustainLevel = -0.4);
+ time += 0.1;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(sustainLevel, ramp.output.getValue(), "sustain should clip at zero");
+
+ ramp.sustain.set(sustainLevel = 0.4);
+ time += 0.1;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(sustainLevel, ramp.output.getValue(), "sustain should come back");
+
+ // Gate off to let envelope release.
+ ramp.input.set(0.0);
+ time += 0.1;
+ synthesisEngine.sleepUntil(time);
+ double releaseValue = ramp.output.getValue();
+ assertEquals(0.0, releaseValue, "release quickly");
+ }
+
+ @Test
+ public void testOnOff() throws InterruptedException {
+ EnvelopeDAHDSR ramp = new EnvelopeDAHDSR();
+ synthesisEngine.add(ramp);
+
+ ramp.delay.set(0.0);
+ ramp.attack.set(0.1);
+ ramp.hold.set(0.0);
+ ramp.decay.set(0.0);
+ ramp.sustain.set(0.9);
+ ramp.release.set(0.1);
+
+ synthesisEngine.start();
+ ramp.start();
+ time = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(time + 0.2);
+ assertEquals(0.0, ramp.output.getValue(), "still idling");
+
+ // Trigger the envelope.
+ ramp.input.on();
+ time = synthesisEngine.getCurrentTime();
+ // Check end of delay cycle.
+ synthesisEngine.sleepUntil(time + 0.2);
+ assertEquals(0.9, ramp.output.getValue(), 0.01, "at sustain");
+
+ // Release the envelope.
+ ramp.input.off();
+ time = synthesisEngine.getCurrentTime();
+ // Check end of delay cycle.
+ synthesisEngine.sleepUntil(time + 0.2);
+ assertEquals(0.0, ramp.output.getValue(), 0.01, "after release");
+ }
+
+ @Test
+ public void testAutoDisable() throws InterruptedException {
+
+ LinearRamp ramp = new LinearRamp();
+ synthesisEngine.add(ramp);
+ EnvelopeDAHDSR envelope = new EnvelopeDAHDSR();
+ synthesisEngine.add(envelope);
+ envelope.attack.set(0.1);
+ envelope.decay.set(0.1);
+ envelope.release.set(0.1);
+ envelope.sustain.set(0.1);
+ ramp.output.connect(envelope.amplitude);
+
+ checkAutoDisable(ramp, envelope);
+ }
+
+ static class GatedRampCircuit extends Circuit {
+ LinearRamp ramp;
+ EnvelopeDAHDSR envelope;
+
+ GatedRampCircuit() {
+ add(ramp = new LinearRamp());
+ add(envelope = new EnvelopeDAHDSR());
+ envelope.attack.set(0.1);
+ envelope.decay.set(0.1);
+ envelope.release.set(0.1);
+ envelope.sustain.set(0.1);
+
+ envelope.setupAutoDisable(this);
+ ramp.output.connect(envelope.amplitude);
+ }
+ }
+
+ @Test
+ public void testAutoDisableCircuit() throws InterruptedException {
+ GatedRampCircuit circuit = new GatedRampCircuit();
+ synthesisEngine.add(circuit);
+ checkAutoDisable(circuit.ramp, circuit.envelope);
+ }
+
+ public void checkReleaseTiming(double releaseTime, double tolerance)
+ throws InterruptedException {
+ delayTime = 0.0;
+ attackTime = 0.2;
+ holdTime = 0.0;
+ decayTime = 10.0;
+ sustainLevel = 1.0;
+
+ EnvelopeDAHDSR ramp = new EnvelopeDAHDSR();
+ synthesisEngine.add(ramp);
+
+ ramp.delay.set(delayTime);
+ ramp.attack.set(attackTime);
+ ramp.hold.set(holdTime);
+ ramp.decay.set(decayTime);
+ ramp.sustain.set(sustainLevel);
+ ramp.release.set(releaseTime);
+
+ synthesisEngine.start();
+ ramp.start();
+ // Trigger the envelope.
+ ramp.input.set(1.0);
+ time = synthesisEngine.getCurrentTime();
+ time += attackTime * 2;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(sustainLevel, ramp.output.getValue(), "should be at to sustain level");
+
+ // Start envelope release.
+ ramp.input.set(0.0);
+ final double db90 = 20.0 * Math.log(1.0 / 32768.0) / Math.log(10.0);
+ LOGGER.debug("JSyns DB90 is actually " + db90);
+ int numSteps = 10;
+ for (int i = 0; i < 10; i++) {
+ time += releaseTime / numSteps;
+ synthesisEngine.sleepUntil(time);
+ double expectedDB = db90 * (i + 1) / numSteps;
+ double expectedAmplitude = sustainLevel * Math.pow(10.0, expectedDB / 20.0);
+ double releaseValue = ramp.output.getValue();
+ assertEquals(expectedAmplitude, releaseValue, tolerance, "release " + i + " at");
+ }
+ time += releaseTime / numSteps;
+ synthesisEngine.sleepUntil(time);
+ double releaseValue = ramp.output.getValue();
+ assertEquals(0.0, releaseValue, 0.0001, "env after release time should go to zero");
+ }
+
+ @Test
+ public void testReleaseTiming() throws InterruptedException {
+ checkReleaseTiming(0.1, 0.004);
+ checkReleaseTiming(1.0, 0.002);
+ checkReleaseTiming(2.5, 0.001);
+ checkReleaseTiming(10.0, 0.001);
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestFunction.java b/src/test/java/com/jsyn/unitgen/TestFunction.java
new file mode 100644
index 0000000..65953cd
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestFunction.java
@@ -0,0 +1,77 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.JSyn;
+import com.jsyn.Synthesizer;
+import com.jsyn.data.DoubleTable;
+import com.jsyn.data.Function;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Phil Burk, (C) 2009 Mobileer Inc
+ */
+public class TestFunction {
+ Synthesizer synth;
+
+ @BeforeEach
+ protected void beforeEach() {
+ synth = JSyn.createSynthesizer();
+ synth.setRealTime(false);
+ synth.start();
+ }
+
+ @AfterEach
+ protected void afterEach() {
+ synth.stop();
+ }
+
+ @Test
+ public void testDoubleTable() {
+ double[] data = {
+ 2.0, 0.0, 3.0
+ };
+ DoubleTable table = new DoubleTable(data);
+ assertEquals(2.0, table.evaluate(-1.4), "DoubleTable below");
+ assertEquals(2.0, table.evaluate(-1.0), "DoubleTable edge");
+ assertEquals(1.0, table.evaluate(-0.5), "DoubleTable mid");
+ assertEquals(0.0, table.evaluate(0.0), "DoubleTable zero");
+ assertEquals(0.75, table.evaluate(0.25), "DoubleTable mid");
+ assertEquals(3.0, table.evaluate(1.3), "DoubleTable above");
+
+ }
+
+ @Test
+ public void testFunctionEvaluator() throws InterruptedException {
+ FunctionEvaluator shaper = new FunctionEvaluator();
+ synth.add(shaper);
+ shaper.start();
+
+ Function cuber = x -> x * x * x;
+ shaper.function.set(cuber);
+
+ shaper.input.set(0.5);
+ synth.sleepFor(0.001);
+
+ assertEquals((0.5 * 0.5 * 0.5), shaper.output.getValue(), "Cuber");
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestMath.java b/src/test/java/com/jsyn/unitgen/TestMath.java
new file mode 100644
index 0000000..7c33223
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestMath.java
@@ -0,0 +1,420 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+import com.softsynth.math.AudioMath;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Phil Burk, (C) 2009 Mobileer Inc
+ */
+public class TestMath {
+ SynthesisEngine synthesisEngine;
+
+ @BeforeEach
+ protected void beforeEach() {
+ synthesisEngine = new SynthesisEngine();
+ }
+
+ @Test
+ public void testAdd() {
+ Add add = new Add();
+ add.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ add.inputA.setValueInternal(x);
+ add.inputB.setValueInternal(y);
+
+ add.generate();
+
+ assertEquals(x + y, add.output.getValue(), 0.001, "Add");
+ }
+
+ @Test
+ public void testPartialAdd() {
+ Add add = new Add();
+ add.setSynthesisEngine(synthesisEngine);
+
+ double x = 2.5;
+ double y = 9.7;
+ add.inputA.setValueInternal(x);
+ add.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ add.generate(2, 5);
+
+ assertEquals(0.0, add.output.getValues()[0], 0.001, "Add partial");
+ assertEquals(0.0, add.output.getValues()[1], 0.001, "Add partial");
+ assertEquals(x + y, add.output.getValues()[2], 0.001, "Add partial");
+ assertEquals(x + y, add.output.getValues()[3], 0.001, "Add partial");
+ assertEquals(x + y, add.output.getValues()[4], 0.001, "Add partial");
+ assertEquals(0.0, add.output.getValues()[5], 0.001, "Add partial");
+ assertEquals(0.0, add.output.getValues()[6], 0.001, "Add partial");
+ assertEquals(0.0, add.output.getValues()[7], 0.001, "Add partial");
+
+ }
+
+ /**
+ * Unit test for Subtract.java - added by Lisa Tolentino 06/17/2009
+ */
+ @Test
+ public void testSubtract() {
+ Subtract sub = new Subtract();
+ sub.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ sub.inputA.setValueInternal(x);
+ sub.inputB.setValueInternal(y);
+
+ sub.generate();
+
+ assertEquals(x - y, sub.output.getValue(), 0.001, "Subtract");
+ }
+
+ @Test
+ public void testPartialSubtract() {
+ Subtract sub = new Subtract();
+ sub.setSynthesisEngine(synthesisEngine);
+
+ double x = 2.5;
+ double y = 9.7;
+ sub.inputA.setValueInternal(x);
+ sub.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ sub.generate(2, 5);
+
+ assertEquals(0.0, sub.output.getValues()[0], 0.001, "Subtract partial");
+ assertEquals(0.0, sub.output.getValues()[1], 0.001, "Subtract partial");
+ assertEquals(x - y, sub.output.getValues()[2], 0.001, "Subtract partial");
+ assertEquals(x - y, sub.output.getValues()[3], 0.001, "Subtract partial");
+ assertEquals(x - y, sub.output.getValues()[4], 0.001, "Subtract partial");
+ assertEquals(0.0, sub.output.getValues()[5], 0.001, "Subtract partial");
+ assertEquals(0.0, sub.output.getValues()[6], 0.001, "Subtract partial");
+ assertEquals(0.0, sub.output.getValues()[7], 0.001, "Subtract partial");
+ }
+
+ /**
+ * Unit test for Multiply.java - added by Lisa Tolentino 06/19/2009
+ */
+ @Test
+ public void testMultiply() {
+ Multiply mult = new Multiply();
+ mult.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ mult.inputA.setValueInternal(x);
+ mult.inputB.setValueInternal(y);
+
+ mult.generate();
+
+ assertEquals(x * y, mult.output.getValue(), 0.001, "Multiply");
+ }
+
+ @Test
+ public void testPartialMultiply() {
+ Multiply mult = new Multiply();
+ mult.setSynthesisEngine(synthesisEngine);
+
+ double x = 2.5;
+ double y = 9.7;
+ mult.inputA.setValueInternal(x);
+ mult.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ mult.generate(2, 5);
+
+ assertEquals(0.0, mult.output.getValues()[0], 0.001, "Multiply partial");
+ assertEquals(0.0, mult.output.getValues()[1], 0.001, "Multiply partial");
+ assertEquals(x * y, mult.output.getValues()[2], 0.001, "Multiply partial");
+ assertEquals(x * y, mult.output.getValues()[3], 0.001, "Multiply partial");
+ assertEquals(x * y, mult.output.getValues()[4], 0.001, "Multiply partial");
+ assertEquals(0.0, mult.output.getValues()[5], 0.001, "Multiply partial");
+ assertEquals(0.0, mult.output.getValues()[6], 0.001, "Multiply partial");
+ assertEquals(0.0, mult.output.getValues()[7], 0.001, "Multiply partial");
+ }
+
+ /**
+ * Unit test for Divide.java - added by Lisa Tolentino 06/19/2009
+ */
+ @Test
+ public void testDivide() {
+ Divide divide = new Divide();
+ divide.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ divide.inputA.setValueInternal(x);
+ divide.inputB.setValueInternal(y);
+
+ divide.generate();
+
+ assertEquals(x / y, divide.output.getValue(), 0.001, "Divide");
+ }
+
+ @Test
+ public void testPartialDivide() {
+ Divide divide = new Divide();
+ divide.setSynthesisEngine(synthesisEngine);
+
+ double x = 2.5;
+ double y = 9.7;
+ divide.inputA.setValueInternal(x);
+ divide.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ divide.generate(2, 5);
+
+ assertEquals(0.0, divide.output.getValues()[0], 0.001, "Divide partial");
+ assertEquals(0.0, divide.output.getValues()[1], 0.001, "Divide partial");
+ assertEquals(x / y, divide.output.getValues()[2], 0.001, "Divide partial");
+ assertEquals(x / y, divide.output.getValues()[3], 0.001, "Divide partial");
+ assertEquals(x / y, divide.output.getValues()[4], 0.001, "Divide partial");
+ assertEquals(0.0, divide.output.getValues()[5], 0.001, "Divide partial");
+ assertEquals(0.0, divide.output.getValues()[6], 0.001, "Divide partial");
+ assertEquals(0.0, divide.output.getValues()[7], 0.001, "Divide partial");
+ }
+
+ /**
+ * Unit test for MultiplyAdd.java - added by Lisa Tolentino 06/19/2009
+ */
+ @Test
+ public void testMultiplyAdd() {
+ MultiplyAdd multAdd = new MultiplyAdd();
+ multAdd.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ double z = 2.28;
+ multAdd.inputA.setValueInternal(x);
+ multAdd.inputB.setValueInternal(y);
+ multAdd.inputC.setValueInternal(z);
+
+ multAdd.generate();
+
+ assertEquals((x * y) + z, multAdd.output.getValue(), 0.001, "MultiplyAdd");
+ }
+
+ @Test
+ public void testPartialMultiplyAdd() {
+ MultiplyAdd multAdd = new MultiplyAdd();
+ multAdd.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ double z = 2.28;
+ multAdd.inputA.setValueInternal(x);
+ multAdd.inputB.setValueInternal(y);
+ multAdd.inputC.setValueInternal(z);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ multAdd.generate(2, 5);
+
+ assertEquals(0.0, multAdd.output.getValues()[0], 0.001, "MultiplyAdd partial");
+ assertEquals(0.0, multAdd.output.getValues()[1], 0.001, "MultiplyAdd partial");
+ assertEquals((x * y) + z, multAdd.output.getValues()[2], 0.001, "MultiplyAdd partial");
+ assertEquals((x * y) + z, multAdd.output.getValues()[3], 0.001, "MultiplyAdd partial");
+ assertEquals((x * y) + z, multAdd.output.getValues()[4], 0.001, "MultiplyAdd partial");
+ assertEquals(0.0, multAdd.output.getValues()[5], 0.001, "MultiplyAdd partial");
+ assertEquals(0.0, multAdd.output.getValues()[6], 0.001, "MultiplyAdd partial");
+ assertEquals(0.0, multAdd.output.getValues()[7], 0.001, "MultiplyAdd partial");
+ }
+
+ /**
+ * Unit test for Compare.java - added by Lisa Tolentino 06/19/2009
+ */
+ @Test
+ public void testCompare() {
+ UnitBinaryOperator compare = new Compare();
+ compare.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ compare.inputA.setValueInternal(x);
+ compare.inputB.setValueInternal(y);
+
+ compare.generate();
+
+ assertEquals((x > y ? 1 : 0), compare.output.getValue(), 0.001, "Compare");
+ }
+
+ @Test
+ public void testPartialCompare() {
+ UnitBinaryOperator compare = new Compare();
+ compare.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ compare.inputA.setValueInternal(x);
+ compare.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ compare.generate(2, 5);
+
+ assertEquals(0.0, compare.output.getValues()[0], 0.001, "Compare partial");
+ assertEquals(0.0, compare.output.getValues()[1], 0.001, "Compare partial");
+ assertEquals((x > y ? 1 : 0), compare.output.getValues()[2], 0.001, "Compare partial");
+ assertEquals((x > y ? 1 : 0), compare.output.getValues()[3], 0.001, "Compare partial");
+ assertEquals((x > y ? 1 : 0), compare.output.getValues()[4], 0.001, "Compare partial");
+ assertEquals(0.0, compare.output.getValues()[5], 0.001, "Compare partial");
+ assertEquals(0.0, compare.output.getValues()[6], 0.001, "Compare partial");
+ assertEquals(0.0, compare.output.getValues()[7], 0.001, "Compare partial");
+ }
+
+ /**
+ * Unit test for Maximum.java - added by Lisa Tolentino 06/20/2009
+ */
+ @Test
+ public void testMaximum() {
+ Maximum max = new Maximum();
+ max.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ max.inputA.setValueInternal(x);
+ max.inputB.setValueInternal(y);
+
+ max.generate();
+
+ assertEquals((x > y ? x : y), max.output.getValue(), 0.001, "Maximum");
+ }
+
+ @Test
+ public void testPartialMaximum() {
+ Maximum max = new Maximum();
+ max.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ max.inputA.setValueInternal(x);
+ max.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ max.generate(2, 5);
+
+ assertEquals(0.0, max.output.getValues()[0], 0.001, "Maximum partial");
+ assertEquals(0.0, max.output.getValues()[1], 0.001, "Maximum partial");
+ assertEquals((x > y ? x : y), max.output.getValues()[2], 0.001, "Maximum partial");
+ assertEquals((x > y ? x : y), max.output.getValues()[3], 0.001, "Maximum partial");
+ assertEquals((x > y ? x : y), max.output.getValues()[4], 0.001, "Maximum partial");
+ assertEquals(0.0, max.output.getValues()[5], 0.001, "Maximum partial");
+ assertEquals(0.0, max.output.getValues()[6], 0.001, "Maximum partial");
+ assertEquals(0.0, max.output.getValues()[7], 0.001, "Maximum partial");
+ }
+
+ /**
+ * Unit test for Minimum.java - added by Lisa Tolentino 06/20/2009
+ */
+ @Test
+ public void testMinimum() {
+ Minimum min = new Minimum();
+ min.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ min.inputA.setValueInternal(x);
+ min.inputB.setValueInternal(y);
+
+ min.generate();
+
+ assertEquals((x < y ? x : y), min.output.getValue(), 0.001, "Minimum");
+ }
+
+ @Test
+ public void testPartialMinimum() {
+ Minimum min = new Minimum();
+ min.setSynthesisEngine(synthesisEngine);
+
+ double x = 33.99;
+ double y = 8.31;
+ min.inputA.setValueInternal(x);
+ min.inputB.setValueInternal(y);
+
+ // Only generate a few values in the middle.
+ // This is to test low latency feedback loops.
+ // Only generate values for 2,3,4
+ min.generate(2, 5);
+
+ assertEquals(0.0, min.output.getValues()[0], 0.001, "Maximum partial");
+ assertEquals(0.0, min.output.getValues()[1], 0.001, "Maximum partial");
+ assertEquals((x < y ? x : y), min.output.getValues()[2], 0.001, "Maximum partial");
+ assertEquals((x < y ? x : y), min.output.getValues()[3], 0.001, "Maximum partial");
+ assertEquals((x < y ? x : y), min.output.getValues()[4], 0.001, "Maximum partial");
+ assertEquals(0.0, min.output.getValues()[5], 0.001, "Maximum partial");
+ assertEquals(0.0, min.output.getValues()[6], 0.001, "Maximum partial");
+ assertEquals(0.0, min.output.getValues()[7], 0.001, "Maximum partial");
+ }
+
+ @Test
+ public void testPowerOfTwo() {
+ PowerOfTwo powerOfTwo = new PowerOfTwo();
+ powerOfTwo.setSynthesisEngine(synthesisEngine);
+ final double smallValue = -1.5308084989341915E-17;
+ double[] values = {
+ 0.0, 1.3, 4.5, -0.5, -1.0, -2.8, smallValue, -smallValue, 1.0 - smallValue,
+ 1.0 + smallValue
+ };
+ for (double in : values) {
+ powerOfTwo.input.setValueInternal(in);
+ powerOfTwo.generate();
+ assertEquals(Math.pow(2.0, in), powerOfTwo.output.getValue(), 0.001, "PowerOfTwo");
+ }
+ }
+
+ @Test
+ 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(440.0, AudioMath.pitchToFrequency(69), 0.001, "PitchToFrequency");
+ assertEquals(660.0, AudioMath.pitchToFrequency(69+7.02), 0.1, "PitchToFrequency");
+
+ for (double pitch : values) {
+ ugen.input.setValueInternal(pitch);
+ ugen.generate();
+ assertEquals(ugen.output.getValue(), 0.001, "PitchToFrequency: " + AudioMath.pitchToFrequency(pitch));
+ }
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestRamps.java b/src/test/java/com/jsyn/unitgen/TestRamps.java
new file mode 100644
index 0000000..40d968c
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestRamps.java
@@ -0,0 +1,205 @@
+/*
+ * 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.unitgen;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestRamps extends NonRealTimeTestCase {
+
+ public void viewContinuousRamp(double duration, double startValue, double targetValue)
+ throws InterruptedException {
+ ContinuousRamp ramp = new ContinuousRamp();
+ synthesisEngine.add(ramp);
+
+ ramp.current.set(startValue);
+ ramp.input.set(startValue);
+ ramp.time.set(duration);
+
+ synthesisEngine.setRealTime(false);
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ ramp.input.set(targetValue);
+
+ double time = synthesisEngine.getCurrentTime();
+ int numLoops = 20;
+ double increment = duration / numLoops;
+ for (int i = 0; i < (numLoops + 1); i++) {
+ double value = ramp.output.getValue();
+ System.out.printf("i = %d, t = %9.5f, value = %8.4f\n", i, time, value);
+ time += increment;
+ synthesisEngine.sleepUntil(time);
+ }
+
+ synthesisEngine.stop();
+ }
+
+ public void checkContinuousRamp(double duration, double startValue, double targetValue)
+ throws InterruptedException {
+ ContinuousRamp ramp = new ContinuousRamp();
+ synthesisEngine.add(ramp);
+
+ ramp.current.set(startValue);
+ ramp.input.set(startValue);
+ ramp.time.set(duration);
+
+ synthesisEngine.setRealTime(false);
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ assertEquals(ramp.input.getValue(), ramp.output.getValue(), "start flat");
+
+ ramp.input.set(targetValue);
+ double startTime = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(startTime + (duration / 2));
+ assertEquals((targetValue + startValue) / 2.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + duration);
+ assertEquals(targetValue, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + duration + 0.1);
+ assertEquals(targetValue, ramp.output.getValue(), "flat again");
+
+ synthesisEngine.stop();
+ }
+
+ @Test
+ public void testContinuousRamp() throws InterruptedException {
+ viewContinuousRamp(4.0, 0.0, 1.0);
+ }
+
+ @Test
+ public void testExponentialRamp() throws InterruptedException {
+ ExponentialRamp ramp = new ExponentialRamp();
+ synthesisEngine.add(ramp);
+
+ double duration = 0.3;
+ ramp.current.set(1.0);
+ ramp.input.set(1.0);
+ ramp.time.set(duration);
+
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ assertEquals(ramp.input.getValue(), ramp.output.getValue(), "start flat");
+
+ ramp.input.set(8.0);
+ double startTime = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(startTime + 0.1);
+ assertEquals(2.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.2);
+ assertEquals(4.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.3);
+ assertEquals(8.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.4);
+ assertEquals(8.0, ramp.output.getValue(), "flat again");
+ }
+
+ @Test
+ public void testLinearRamp() throws InterruptedException {
+ LinearRamp ramp = new LinearRamp();
+ synthesisEngine.add(ramp);
+
+ double duration = 0.4;
+ ramp.current.set(0.0);
+ ramp.input.set(0.0);
+ ramp.time.set(duration);
+
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ assertEquals(ramp.input.getValue(), ramp.output.getValue(), "start flat");
+
+ ramp.input.set(8.0);
+ double startTime = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(startTime + 0.1);
+ assertEquals(2.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.2);
+ assertEquals(4.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.3);
+ assertEquals(6.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.4);
+ assertEquals(8.0, ramp.output.getValue(), "flat again");
+ }
+
+ @Test
+ public void testExponentialRampConnected() throws InterruptedException {
+ ExponentialRamp ramp = new ExponentialRamp();
+ PassThrough pass = new PassThrough();
+ synthesisEngine.add(ramp);
+ synthesisEngine.add(pass);
+
+ double duration = 0.3;
+ ramp.current.set(1.0);
+ pass.input.set(1.0);
+ ramp.time.set(duration);
+
+ // Send value through a connected unit.
+ pass.input.set(1.0);
+ pass.output.connect(ramp.input);
+
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ assertEquals(ramp.input.getValue(), ramp.output.getValue(), "start flat");
+
+ pass.input.set(8.0);
+ double startTime = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(startTime + 0.1);
+ assertEquals(2.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.2);
+ assertEquals(4.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.3);
+ assertEquals(8.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.4);
+ assertEquals(8.0, ramp.output.getValue(), "flat again");
+ }
+
+ @Test
+ public void testLinearRampConnected() throws InterruptedException {
+ LinearRamp ramp = new LinearRamp();
+ PassThrough pass = new PassThrough();
+ synthesisEngine.add(ramp);
+ synthesisEngine.add(pass);
+
+ double duration = 0.4;
+ ramp.current.set(0.0);
+ pass.input.set(0.0);
+ ramp.time.set(duration);
+
+ // Send value through a connected unit.
+ pass.input.set(0.0);
+ pass.output.connect(ramp.input);
+
+ synthesisEngine.start();
+ ramp.start();
+ synthesisEngine.sleepUntil(synthesisEngine.getCurrentTime() + 0.01);
+ assertEquals(ramp.input.getValue(), ramp.output.getValue(), "start flat");
+
+ pass.input.set(8.0);
+ double startTime = synthesisEngine.getCurrentTime();
+ synthesisEngine.sleepUntil(startTime + 0.1);
+ assertEquals(2.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.2);
+ assertEquals(4.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.3);
+ assertEquals(6.0, ramp.output.getValue(), 0.01, "ramping up");
+ synthesisEngine.sleepUntil(startTime + 0.4);
+ assertEquals(8.0, ramp.output.getValue(), "flat again");
+ }
+
+}
diff --git a/src/test/java/com/jsyn/unitgen/TestUnitGate.java b/src/test/java/com/jsyn/unitgen/TestUnitGate.java
new file mode 100644
index 0000000..cba03e7
--- /dev/null
+++ b/src/test/java/com/jsyn/unitgen/TestUnitGate.java
@@ -0,0 +1,81 @@
+/*
+ * 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.unitgen;
+
+import com.jsyn.engine.SynthesisEngine;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestUnitGate {
+
+ protected SynthesisEngine synthesisEngine;
+ protected double time;
+
+ public void checkAutoDisable(LinearRamp ramp, UnitGate envelope) throws InterruptedException {
+ double tolerance = 0.01;
+ Add adder = new Add();
+ synthesisEngine.add(adder);
+
+ envelope.output.connect(adder.inputA);
+ if (ramp.getCircuit() != null) {
+ ramp.output.connect(adder.inputB);
+ }
+
+ envelope.input.setAutoDisableEnabled(true);
+ envelope.setEnabled(false);
+
+ // set up so ramp value should equal time
+ ramp.current.set(0.0);
+ ramp.input.set(1.0);
+ ramp.time.set(1.0);
+
+ synthesisEngine.start();
+ // pull from final adder
+ adder.start();
+
+ time = synthesisEngine.getCurrentTime();
+ time += 0.1;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(0.0, envelope.output.getValue(), "still idling");
+ assertEquals(0.0, ramp.output.getValue(), tolerance, "ramp frozen at beginning");
+
+ // run multiple times to make sure we can retrigger the envelope.
+ for (int i = 0; i < 3; i++) {
+ double level = ramp.output.getValue();
+ // Trigger the envelope using trigger()
+ envelope.input.on();
+ time += 0.1;
+ level += 0.1;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(level, ramp.output.getValue(), tolerance, "ramp going up " + i);
+ assertTrue(envelope.isEnabled(), "enabled at peak");
+
+ envelope.input.off();
+ time += 0.1;
+ level += 0.1;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(level, ramp.output.getValue(), tolerance, "ramp going up more " + i);
+ assertEquals(0.0, envelope.output.getValue(), 0.1, "at bottom");
+
+ time += 0.2;
+ synthesisEngine.sleepUntil(time);
+ assertEquals(level, ramp.output.getValue(), tolerance, "ramp frozen " + i);
+ }
+ }
+
+}