diff options
Diffstat (limited to 'src/test/java/com/jsyn/unitgen')
-rw-r--r-- | src/test/java/com/jsyn/unitgen/CalibrateMoogFilter.java | 141 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/EnablingGate.java | 51 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/NonRealTimeTestCase.java | 42 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/RecordMoogFilter.java | 158 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestConnections.java | 111 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestDelay.java | 81 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestEnable.java | 81 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestEnvelopeAttackDecay.java | 130 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestEnvelopeDAHDSR.java | 355 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestFunction.java | 77 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestMath.java | 420 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestRamps.java | 205 | ||||
-rw-r--r-- | src/test/java/com/jsyn/unitgen/TestUnitGate.java | 81 |
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); + } + } + +} |