diff options
author | Phil Burk <[email protected]> | 2014-12-30 16:53:03 -0800 |
---|---|---|
committer | Phil Burk <[email protected]> | 2014-12-30 16:53:03 -0800 |
commit | 534969d42ca5168d645678345cd21242fe41f389 (patch) | |
tree | e8f5d1cba1ec57685e76ceb923d8da25a7846cfb /tests/com/jsyn/engine | |
parent | a4d8ca95178d2e3acfc3299a4b73e84c2646d24e (diff) |
Initial commit of code.
Diffstat (limited to 'tests/com/jsyn/engine')
-rw-r--r-- | tests/com/jsyn/engine/TestAudioOutput.java | 86 | ||||
-rw-r--r-- | tests/com/jsyn/engine/TestDevices.java | 69 | ||||
-rw-r--r-- | tests/com/jsyn/engine/TestEngine.java | 180 | ||||
-rw-r--r-- | tests/com/jsyn/engine/TestFifo.java | 219 | ||||
-rw-r--r-- | tests/com/jsyn/engine/TestWaveFileReadWrite.java | 107 |
5 files changed, 661 insertions, 0 deletions
diff --git a/tests/com/jsyn/engine/TestAudioOutput.java b/tests/com/jsyn/engine/TestAudioOutput.java new file mode 100644 index 0000000..a95d426 --- /dev/null +++ b/tests/com/jsyn/engine/TestAudioOutput.java @@ -0,0 +1,86 @@ +/* + * 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.engine; + +import java.io.IOException; + +import junit.framework.TestCase; + +import com.jsyn.devices.AudioDeviceManager; +import com.jsyn.devices.AudioDeviceOutputStream; +import com.jsyn.devices.javasound.JavaSoundAudioDevice; + +/** + * @author Phil Burk, (C) 2009 Mobileer Inc + */ +public class TestAudioOutput extends TestCase { + + SynthesisEngine synthesisEngine; + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testMonoSine() throws IOException { + System.out.println("Test mono output."); + final int FRAMES_PER_BUFFER = 128; + final int SAMPLES_PER_FRAME = 1; + double[] buffer = new double[FRAMES_PER_BUFFER * SAMPLES_PER_FRAME]; + AudioDeviceManager audioDevice = new JavaSoundAudioDevice(); + AudioDeviceOutputStream audioOutput = audioDevice.createOutputStream( + audioDevice.getDefaultOutputDeviceID(), 44100, SAMPLES_PER_FRAME); + for (int i = 0; i < FRAMES_PER_BUFFER; i++) { + double angle = (i * Math.PI * 2.0) / FRAMES_PER_BUFFER; + buffer[i] = Math.sin(angle); + } + audioOutput.start(); + for (int i = 0; i < 1000; i++) { + audioOutput.write(buffer); + } + audioOutput.stop(); + + } + + public void testStereoSine() throws IOException { + System.out.println("Test stereo output."); + final int FRAMES_PER_BUFFER = 128; + final int SAMPLES_PER_FRAME = 2; + double[] buffer = new double[FRAMES_PER_BUFFER * SAMPLES_PER_FRAME]; + AudioDeviceManager audioDevice = new JavaSoundAudioDevice(); + AudioDeviceOutputStream audioOutput = audioDevice.createOutputStream( + audioDevice.getDefaultOutputDeviceID(), 44100, SAMPLES_PER_FRAME); + int bi = 0; + for (int i = 0; i < FRAMES_PER_BUFFER; i++) { + double angle = (i * Math.PI * 2.0) / FRAMES_PER_BUFFER; + buffer[bi++] = Math.sin(angle); + buffer[bi++] = Math.sin(angle); + } + audioOutput.start(); + for (int i = 0; i < 1000; i++) { + audioOutput.write(buffer); + } + audioOutput.stop(); + + } + +} diff --git a/tests/com/jsyn/engine/TestDevices.java b/tests/com/jsyn/engine/TestDevices.java new file mode 100644 index 0000000..56f2c1f --- /dev/null +++ b/tests/com/jsyn/engine/TestDevices.java @@ -0,0 +1,69 @@ +/* + * 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.engine; + +import junit.framework.TestCase; + +import com.jsyn.JSyn; +import com.jsyn.Synthesizer; +import com.jsyn.devices.AudioDeviceFactory; +import com.jsyn.devices.AudioDeviceManager; +import com.jsyn.unitgen.LineIn; +import com.jsyn.unitgen.LineOut; + +public class TestDevices extends TestCase { + // Test audio input and output simultaneously. + public void testPassThrough() { + Synthesizer synth; + LineIn lineIn; + LineOut lineOut; + // Create a context for the synthesizer. + synth = JSyn.createSynthesizer(AudioDeviceFactory.createAudioDeviceManager(true)); + // Add an audio input. + synth.add(lineIn = new LineIn()); + // Add an audio output. + synth.add(lineOut = new LineOut()); + // Connect the input to the output. + lineIn.output.connect(0, lineOut.input, 0); + lineIn.output.connect(1, lineOut.input, 1); + + // Both stereo. + int numInputChannels = 2; + int numOutputChannels = 2; + synth.start(44100, AudioDeviceManager.USE_DEFAULT_DEVICE, numInputChannels, + AudioDeviceManager.USE_DEFAULT_DEVICE, numOutputChannels); + + // We only need to start the LineOut. It will pull data from the LineIn. + lineOut.start(); + System.out.println("Audio passthrough started."); + // Sleep a while. + double sleepTime = 2.0; + try { + double time = synth.getCurrentTime(); + // Sleep for a few seconds. + synth.sleepUntil(time + sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + double synthTime = synth.getCurrentTime(); + assertEquals("Time has advanced. " + synthTime, sleepTime, synthTime, 0.2); + // Stop everything. + synth.stop(); + System.out.println("All done."); + + } +} diff --git a/tests/com/jsyn/engine/TestEngine.java b/tests/com/jsyn/engine/TestEngine.java new file mode 100644 index 0000000..b633bc1 --- /dev/null +++ b/tests/com/jsyn/engine/TestEngine.java @@ -0,0 +1,180 @@ +/* + * 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.engine; + +import junit.framework.TestCase; + +import com.jsyn.unitgen.LineOut; +import com.jsyn.unitgen.PitchDetector; +import com.jsyn.unitgen.SineOscillator; +import com.jsyn.unitgen.ZeroCrossingCounter; + +public class TestEngine extends TestCase { + + public void testInitialization() { + final int DEFAULT_FRAME_RATE = 44100; + SynthesisEngine synthesisEngine = new SynthesisEngine(); + assertEquals("frameCount zero before starting", 0, synthesisEngine.getFrameCount()); + assertEquals("default frameRate", DEFAULT_FRAME_RATE, synthesisEngine.getFrameRate()); + assertEquals("default pullData", true, synthesisEngine.isPullDataEnabled()); + } + + public void checkPullData(boolean pullData) { + SynthesisEngine synthesisEngine = new SynthesisEngine(); + assertEquals("default realTime", true, synthesisEngine.isRealTime()); + synthesisEngine.setRealTime(false); + + assertEquals("default pullData", true, synthesisEngine.isPullDataEnabled()); + synthesisEngine.setPullDataEnabled(pullData); + + SineOscillator sineOscillator = new SineOscillator(); + synthesisEngine.add(sineOscillator); + + LineOut lineOut = new LineOut(); + synthesisEngine.add(lineOut); + sineOscillator.output.connect(0, lineOut.input, 0); + + assertEquals("initial sine value", 0.0, sineOscillator.output.getValue()); + + synthesisEngine.start(); + if (!pullData) { + sineOscillator.start(); + } + // We always have to start the LineOut. + lineOut.start(); + synthesisEngine.generateNextBuffer(); + synthesisEngine.generateNextBuffer(); + + double value = sineOscillator.output.getValue(); + assertTrue("sine value after generation = " + value, (value > 0.0)); + } + + public void testPullDataFalse() { + checkPullData(false); + } + + public void testPullDataTrue() { + checkPullData(true); + } + + public void testMixedAdding() { + boolean gotCaught = false; + SynthesisEngine synthesisEngine1 = new SynthesisEngine(); + synthesisEngine1.setRealTime(false); + synthesisEngine1.setPullDataEnabled(true); + SynthesisEngine synthesisEngine2 = new SynthesisEngine(); + synthesisEngine2.setRealTime(false); + synthesisEngine2.setPullDataEnabled(true); + + // Create a sineOscillator but do not add it to the synth! + SineOscillator sineOscillator = new SineOscillator(); + LineOut lineOut = new LineOut(); + + synthesisEngine1.add(lineOut); + synthesisEngine2.add(sineOscillator); + try { + sineOscillator.output.connect(0, lineOut.input, 0); + } catch (RuntimeException e) { + gotCaught = true; + assertTrue("informative MPE message", e.getMessage().contains("different synths")); + } + + assertTrue("caught NPE caused by forgetting synth.add", gotCaught); + } + + public void testNotAdding() { + SynthesisEngine synthesisEngine = new SynthesisEngine(); + synthesisEngine.setRealTime(false); + synthesisEngine.setPullDataEnabled(true); + + // Create a sineOscillator but do not add it to the synth! + SineOscillator sineOscillator = new SineOscillator(); + + LineOut lineOut = new LineOut(); + sineOscillator.output.connect(0, lineOut.input, 0); + synthesisEngine.add(lineOut); + + assertEquals("initial sine value", 0.0, sineOscillator.output.getValue()); + + synthesisEngine.start(); + // We always have to start the LineOut. + lineOut.start(); + boolean gotCaught = false; + try { + synthesisEngine.generateNextBuffer(); + synthesisEngine.generateNextBuffer(); + } catch (NullPointerException e) { + gotCaught = true; + assertTrue("informative MPE message", e.getMessage().contains("forgot to add")); + } + + assertTrue("caught NPE caused by forgetting synth.add", gotCaught); + } + + public void testMultipleStarts() throws InterruptedException { + SynthesisEngine synth = new SynthesisEngine(); + + // Create a sineOscillator but do not add it to the synth! + SineOscillator osc = new SineOscillator(); + ZeroCrossingCounter counter = new ZeroCrossingCounter(); + PitchDetector pitchDetector = new PitchDetector(); + LineOut lineOut = new LineOut(); + synth.add(osc); + synth.add(counter); + synth.add(lineOut); + synth.add(pitchDetector); + osc.output.connect(counter.input); + osc.output.connect(pitchDetector.input); + counter.output.connect(0, lineOut.input, 0); + + assertEquals("initial count", 0, counter.getCount()); + + int[] rates = { + 32000, 48000, 44100, 22050 + }; + for (int rate : rates) { + synth.start(rate); + lineOut.start(); + pitchDetector.start(); + + double time = synth.getCurrentTime(); + double interval = 1.0; + time += interval; + + long previousFrameCount = counter.getCount(); + synth.sleepUntil(time); + + double frequencyMeasured = pitchDetector.frequency.get(); + double confidenceMeasured = pitchDetector.confidence.get(); + double oscFreq = osc.frequency.get(); + String msg = "freq at " + rate + " Hz"; + System.out.println(msg); + assertEquals(msg, oscFreq, frequencyMeasured, oscFreq * 0.1); + assertEquals("pitch confidence", 0.9, confidenceMeasured, 0.1); + + double expectedCount = interval * oscFreq; + double framesMeasured = counter.getCount() - previousFrameCount; + msg = "count at " + rate + " Hz"; + System.out.println(msg); + assertEquals(msg, expectedCount, framesMeasured, expectedCount * 0.1); + + synth.stop(); + } + + } + +} diff --git a/tests/com/jsyn/engine/TestFifo.java b/tests/com/jsyn/engine/TestFifo.java new file mode 100644 index 0000000..e504a0b --- /dev/null +++ b/tests/com/jsyn/engine/TestFifo.java @@ -0,0 +1,219 @@ +/* + * 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.engine; + +import junit.framework.TestCase; + +import com.jsyn.io.AudioFifo; + +public class TestFifo extends TestCase { + + public void testBasic() { + Thread watchdog = startWatchdog(600); + + AudioFifo fifo = new AudioFifo(); + fifo.setReadWaitEnabled(false); + fifo.allocate(8); + assertEquals("start empty", 0, fifo.available()); + + assertEquals("read back Nan when emopty", Double.NaN, fifo.read()); + + fifo.write(1.0); + assertEquals("added one value", 1, fifo.available()); + assertEquals("read back same value", 1.0, fifo.read()); + assertEquals("back to empty", 0, fifo.available()); + + for (int i = 0; i < fifo.size(); i++) { + assertEquals("adding data", i, fifo.available()); + fifo.write(100.0 + i); + } + for (int i = 0; i < fifo.size(); i++) { + assertEquals("removing data", fifo.size() - i, fifo.available()); + assertEquals("reading back data", 100.0 + i, fifo.read()); + } + watchdog.interrupt(); + } + + /** + * Wrap around several times to test masking. + */ + public void testWrapping() { + + final int chunk = 5; + AudioFifo fifo = new AudioFifo(); + fifo.allocate(8); + double value = 1000.0; + for (int i = 0; i < (fifo.size() * chunk); i++) { + value = checkFifoChunk(fifo, value, chunk); + } + + } + + private double checkFifoChunk(AudioFifo fifo, double value, int chunk) { + for (int i = 0; i < chunk; i++) { + assertEquals("adding data", i, fifo.available()); + fifo.write(value + i); + } + for (int i = 0; i < chunk; i++) { + assertEquals("removing data", chunk - i, fifo.available()); + assertEquals("reading back data", value + i, fifo.read()); + } + return value + chunk; + } + + public void testBadSize() { + boolean caught = false; + try { + AudioFifo fifo = new AudioFifo(); + fifo.allocate(20); // not power of 2 + assertTrue("should not get here", false); + } catch (IllegalArgumentException e) { + caught = true; + } + assertTrue("should have caught size exception", caught); + } + + public void testSingleReadWait() { + final int chunk = 5; + final AudioFifo fifo = new AudioFifo(); + fifo.allocate(8); + + fifo.setWriteWaitEnabled(false); + fifo.setReadWaitEnabled(true); + final double value = 50.0; + + // Schedule a delayed write in another thread. + new Thread() { + @Override + public void run() { + try { + sleep(200); + for (int i = 0; i < chunk; i++) { + fifo.write(value + i); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + + Thread watchdog = startWatchdog(500); + for (int i = 0; i < chunk; i++) { + assertEquals("reading back data", value + i, fifo.read()); + } + watchdog.interrupt(); + } + + private Thread startWatchdog(final int msec) { + Thread watchdog = new Thread() { + @Override + public void run() { + try { + sleep(msec); + assertTrue("test must still be waiting", false); + } catch (InterruptedException e) { + } + } + }; + watchdog.start(); + return watchdog; + } + + public void testSingleWriteWait() { + final int chunk = 13; + final AudioFifo fifo = new AudioFifo(); + fifo.allocate(8); + + fifo.setWriteWaitEnabled(true); + fifo.setReadWaitEnabled(true); + final double value = 50.0; + + // Schedule a delayed read in another thread. + Thread readThread = new Thread() { + @Override + public void run() { + try { + sleep(200); + for (int i = 0; i < chunk; i++) { + // System.out.println( "testSingleWriteWait: try to read" ); + double got = fifo.read(); + assertEquals("adding data", value + i, got); + // System.out.println( "testSingleWriteWait: read " + got ); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + readThread.start(); + + Thread watchdog = startWatchdog(500); + // Try to write more than will fit so we will hang. + for (int i = 0; i < chunk; i++) { + fifo.write(value + i); + } + watchdog.interrupt(); + + try { + readThread.join(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + assertEquals("readThread should be done.", false, readThread.isAlive()); + } + + public void testBlockReadWait() { + final int chunk = 50; + final AudioFifo fifo = new AudioFifo(); + fifo.allocate(8); + + fifo.setWriteWaitEnabled(false); + fifo.setReadWaitEnabled(true); + final double value = 300.0; + double[] readBuffer = new double[chunk]; + + // Schedule delayed writes in another thread. + new Thread() { + @Override + public void run() { + int numWritten = 0; + double[] writeBuffer = new double[4]; + try { + while (numWritten < chunk) { + sleep(30); + for (int i = 0; i < writeBuffer.length; i++) { + writeBuffer[i] = value + numWritten; + numWritten += 1; + } + + fifo.write(writeBuffer); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + + Thread watchdog = startWatchdog(600); + fifo.read(readBuffer); + for (int i = 0; i < chunk; i++) { + assertEquals("reading back data", value + i, readBuffer[i]); + } + watchdog.interrupt(); + + } +} diff --git a/tests/com/jsyn/engine/TestWaveFileReadWrite.java b/tests/com/jsyn/engine/TestWaveFileReadWrite.java new file mode 100644 index 0000000..ee406de --- /dev/null +++ b/tests/com/jsyn/engine/TestWaveFileReadWrite.java @@ -0,0 +1,107 @@ +/* + * 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.engine; + +import java.io.File; +import java.io.IOException; + +import javax.sound.sampled.UnsupportedAudioFileException; + +import junit.framework.TestCase; + +import com.jsyn.data.FloatSample; +import com.jsyn.util.SampleLoader; +import com.jsyn.util.WaveFileWriter; + +public class TestWaveFileReadWrite extends TestCase { + + public void checkWriteReadWave(int numChannels, float[] data) throws IOException, + UnsupportedAudioFileException { + File temp = File.createTempFile("test_wave", ".wav"); + temp.deleteOnExit(); + System.out.println("Creating wave file " + temp); + + WaveFileWriter writer = new WaveFileWriter(temp); + writer.setFrameRate(44100); + writer.setSamplesPerFrame(numChannels); + writer.setBitsPerSample(16); + + for (int i = 0; i < data.length; i++) { + writer.write(data[i]); + } + writer.close(); + + // TODO Make sure blow up if writing after close. + // writer.write( 0.7 ); + + FloatSample sample = SampleLoader.loadFloatSample(temp); + assertEquals("stereo", numChannels, sample.getChannelsPerFrame()); + assertEquals("frame rate", 44100.0, sample.getFrameRate()); + + for (int i = 0; i < data.length; i++) { + float v = data[i]; + if (v > 1.0) + v = 1.0f; + else if (v < -1.0) + v = -1.0f; + assertEquals("sample data", v, sample.readDouble(i), 0.0001); + } + + } + + public void testRamp() throws IOException, UnsupportedAudioFileException { + float[] data = new float[200]; + for (int i = 0; i < data.length; i++) { + data[i] = i / 1000.0f; + } + + checkWriteReadWave(2, data); + } + + public void testClippedSine() throws IOException, UnsupportedAudioFileException { + float[] data = new float[200]; + for (int i = 0; i < data.length; i++) { + double phase = i * Math.PI * 2.0 / 100; + data[i] = (float) (1.3 * Math.sin(phase)); + } + + checkWriteReadWave(2, data); + } + + public void testArguments() throws IOException { + File temp = File.createTempFile("test_wave", ".wav"); + temp.deleteOnExit(); + System.out.println("Creating wave file " + temp); + + WaveFileWriter writer = new WaveFileWriter(temp); + writer.setBitsPerSample(16); + assertEquals("bitsPerSample", 16, writer.getBitsPerSample()); + writer.setBitsPerSample(24); + assertEquals("bitsPerSample", 24, writer.getBitsPerSample()); + boolean caughtIt = false; + try { + writer.setBitsPerSample(17); + assertTrue("tried setting illegal value", false); + } catch (IllegalArgumentException e) { + // e.printStackTrace(); + caughtIt = true; + } + assertTrue("17 generated exception", caughtIt); + writer.close(); + } + +} |