aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/jsyn/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/jsyn/util')
-rw-r--r--src/com/jsyn/util/AudioSampleLoader.java42
-rw-r--r--src/com/jsyn/util/AudioStreamReader.java85
-rw-r--r--src/com/jsyn/util/AutoCorrelator.java293
-rw-r--r--src/com/jsyn/util/Instrument.java38
-rw-r--r--src/com/jsyn/util/InstrumentLibrary.java32
-rw-r--r--src/com/jsyn/util/JavaSoundSampleLoader.java149
-rw-r--r--src/com/jsyn/util/JavaTools.java59
-rw-r--r--src/com/jsyn/util/MultiChannelSynthesizer.java404
-rw-r--r--src/com/jsyn/util/NumericOutput.java187
-rw-r--r--src/com/jsyn/util/PolyphonicInstrument.java155
-rw-r--r--src/com/jsyn/util/PseudoRandom.java89
-rw-r--r--src/com/jsyn/util/RecursiveSequenceGenerator.java214
-rw-r--r--src/com/jsyn/util/SampleLoader.java230
-rw-r--r--src/com/jsyn/util/SignalCorrelator.java48
-rw-r--r--src/com/jsyn/util/StreamingThread.java121
-rw-r--r--src/com/jsyn/util/TransportListener.java31
-rw-r--r--src/com/jsyn/util/TransportModel.java67
-rw-r--r--src/com/jsyn/util/VoiceAllocator.java258
-rw-r--r--src/com/jsyn/util/VoiceDescription.java68
-rw-r--r--src/com/jsyn/util/VoiceOperation.java7
-rw-r--r--src/com/jsyn/util/WaveFileWriter.java293
-rw-r--r--src/com/jsyn/util/WaveRecorder.java134
-rw-r--r--src/com/jsyn/util/soundfile/AIFFFileParser.java227
-rw-r--r--src/com/jsyn/util/soundfile/AudioFileParser.java129
-rw-r--r--src/com/jsyn/util/soundfile/ChunkHandler.java49
-rw-r--r--src/com/jsyn/util/soundfile/CustomSampleLoader.java60
-rw-r--r--src/com/jsyn/util/soundfile/IFFParser.java307
-rw-r--r--src/com/jsyn/util/soundfile/WAVEFileParser.java334
28 files changed, 0 insertions, 4110 deletions
diff --git a/src/com/jsyn/util/AudioSampleLoader.java b/src/com/jsyn/util/AudioSampleLoader.java
deleted file mode 100644
index b665933..0000000
--- a/src/com/jsyn/util/AudioSampleLoader.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import com.jsyn.data.FloatSample;
-
-public interface AudioSampleLoader {
- /**
- * Load a FloatSample from a File object.
- */
- public FloatSample loadFloatSample(File fileIn) throws IOException;
-
- /**
- * Load a FloatSample from an InputStream. This is handy when loading Resources from a JAR file.
- */
- public FloatSample loadFloatSample(InputStream inputStream) throws IOException;
-
- /**
- * Load a FloatSample from a URL.. This is handy when loading Resources from a website.
- */
- public FloatSample loadFloatSample(URL url) throws IOException;
-
-}
diff --git a/src/com/jsyn/util/AudioStreamReader.java b/src/com/jsyn/util/AudioStreamReader.java
deleted file mode 100644
index 5a725c3..0000000
--- a/src/com/jsyn/util/AudioStreamReader.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.util;
-
-import com.jsyn.Synthesizer;
-import com.jsyn.io.AudioFifo;
-import com.jsyn.io.AudioInputStream;
-import com.jsyn.ports.UnitInputPort;
-import com.jsyn.unitgen.MonoStreamWriter;
-import com.jsyn.unitgen.StereoStreamWriter;
-import com.jsyn.unitgen.UnitStreamWriter;
-
-/**
- * Reads audio signals from the background engine to a foreground application through an AudioFifo.
- * Connect to the input port returned by getInput().
- *
- * @author Phil Burk (C) 2010 Mobileer Inc
- */
-public class AudioStreamReader implements AudioInputStream {
- private UnitStreamWriter streamWriter;
- private AudioFifo fifo;
-
- public AudioStreamReader(Synthesizer synth, int samplesPerFrame) {
- if (samplesPerFrame == 1) {
- streamWriter = new MonoStreamWriter();
- } else if (samplesPerFrame == 2) {
- streamWriter = new StereoStreamWriter();
- } else {
- throw new IllegalArgumentException("Only 1 or 2 samplesPerFrame supported.");
- }
- synth.add(streamWriter);
-
- fifo = new AudioFifo();
- fifo.setWriteWaitEnabled(!synth.isRealTime());
- fifo.setReadWaitEnabled(true);
- fifo.allocate(32 * 1024);
- streamWriter.setOutputStream(fifo);
- streamWriter.start();
- }
-
- public UnitInputPort getInput() {
- return streamWriter.input;
- }
-
- /** How many values are available to read without blocking? */
- @Override
- public int available() {
- return fifo.available();
- }
-
- @Override
- public void close() {
- fifo.close();
- }
-
- @Override
- public double read() {
- return fifo.read();
- }
-
- @Override
- public int read(double[] buffer) {
- return fifo.read(buffer);
- }
-
- @Override
- public int read(double[] buffer, int start, int count) {
- return fifo.read(buffer, start, count);
- }
-
-}
diff --git a/src/com/jsyn/util/AutoCorrelator.java b/src/com/jsyn/util/AutoCorrelator.java
deleted file mode 100644
index 2996036..0000000
--- a/src/com/jsyn/util/AutoCorrelator.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright 2004 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.util;
-
-/**
- * Calculate period of a repeated waveform in an array. This algorithm is based on a normalized
- * auto-correlation function as dewscribed in: "A Smarter Way to Find Pitch" by Philip McLeod and
- * Geoff Wyvill
- *
- * @author (C) 2004 Mobileer, PROPRIETARY and CONFIDENTIAL
- */
-public class AutoCorrelator implements SignalCorrelator {
- // A higher number will reject suboctaves more.
- private static final float SUB_OCTAVE_REJECTION_FACTOR = 0.0005f;
- // We can focus our analysis on the maxima
- private static final int STATE_SEEKING_NEGATIVE = 0;
- private static final int STATE_SEEKING_POSITIVE = 1;
- private static final int STATE_SEEKING_MAXIMUM = 2;
- private static final int[] tauAdvanceByState = {
- 4, 2, 1
- };
- private int state;
-
- private float[] buffer;
- // double buffer the diffs so we can view them
- private float[] diffs;
- private float[] diffs1;
- private float[] diffs2;
- private int cursor = -1;
- private int tau;
-
- private float sumProducts;
- private float sumSquares;
- private float localMaximum;
- private int localPosition;
- private float bestMaximum;
- private int bestPosition;
- private int peakCounter;
- // This factor was found empirically to reduce a systematic offset in the pitch.
- private float pitchCorrectionFactor = 0.99988f;
-
- // Results of analysis.
- private double period;
- private double confidence;
- private int minPeriod = 2;
- private boolean bufferValid;
- private double previousSample = 0.0;
- private int maxWindowSize;
- private float noiseThreshold = 0.001f;
-
- public AutoCorrelator(int numFrames) {
- buffer = new float[numFrames];
- maxWindowSize = buffer.length / 2;
- diffs1 = new float[2 + numFrames / 2];
- diffs2 = new float[diffs1.length];
- diffs = diffs1;
- period = minPeriod;
- reset();
- }
-
- // Scan assuming we will not wrap around the buffer.
- private void rawDeltaScan(int last1, int last2, int count, int stride) {
- for (int k = 0; k < count; k += stride) {
- float d1 = buffer[last1 - k];
- float d2 = buffer[last2 - k];
- sumProducts += d1 * d2;
- sumSquares += ((d1 * d1) + (d2 * d2));
- }
- }
-
- // Do correlation when we know the splitLast will wrap around.
- private void splitDeltaScan(int last1, int splitLast, int count, int stride) {
- int c1 = splitLast;
- rawDeltaScan(last1, splitLast, c1, stride);
- rawDeltaScan(last1 - c1, buffer.length - 1, count - c1, stride);
- }
-
- private void checkDeltaScan(int last1, int last2, int count, int stride) {
- if (count > last2) {
- int c1 = last2;
- // Use recursion with reverse indexes to handle a double split.
- checkDeltaScan(last2, last1, c1, stride);
- checkDeltaScan(buffer.length - 1, last1 - c1, count - c1, stride);
- } else if (count > last1) {
- splitDeltaScan(last2, last1, count, stride);
- } else {
- rawDeltaScan(last1, last2, count, stride);
- }
- }
-
- // Perform correlation. Handle circular buffer wrap around.
- // Normalized square difference function between -1.0 and +1.0.
- private float topScan(int last1, int tau, int count, int stride) {
- final float minimumResult = 0.00000001f;
-
- int last2 = last1 - tau;
- if (last2 < 0) {
- last2 += buffer.length;
- }
- sumProducts = 0.0f;
- sumSquares = 0.0f;
- checkDeltaScan(last1, last2, count, stride);
- // Prevent divide by zero.
- if (sumSquares < minimumResult) {
- return minimumResult;
- }
- float correction = (float) Math.pow(pitchCorrectionFactor, tau);
- float result = (float) (2.0 * sumProducts / sumSquares) * correction;
-
- return result;
- }
-
- // Prepare for a new calculation.
- private void reset() {
- switchDiffs();
- int i = 0;
- for (; i < minPeriod; i++) {
- diffs[i] = 1.0f;
- }
- for (; i < diffs.length; i++) {
- diffs[i] = 0.0f;
- }
- tau = minPeriod;
- state = STATE_SEEKING_NEGATIVE;
- peakCounter = 0;
- bestMaximum = -1.0f;
- bestPosition = -1;
- }
-
- // Analyze new diff result. Incremental peak detection.
- private void nextPeakAnalysis(int index) {
- // Scale low frequency correlation down to reduce suboctave matching.
- // Note that this has a side effect of reducing confidence value for low frequency sounds.
- float value = diffs[index] * (1.0f - (index * SUB_OCTAVE_REJECTION_FACTOR));
- switch (state) {
- case STATE_SEEKING_NEGATIVE:
- if (value < -0.01f) {
- state = STATE_SEEKING_POSITIVE;
- }
- break;
- case STATE_SEEKING_POSITIVE:
- if (value > 0.2f) {
- state = STATE_SEEKING_MAXIMUM;
- localMaximum = value;
- localPosition = index;
- }
- break;
- case STATE_SEEKING_MAXIMUM:
- if (value > localMaximum) {
- localMaximum = value;
- localPosition = index;
- } else if (value < -0.1f) {
- peakCounter += 1;
- if (localMaximum > bestMaximum) {
- bestMaximum = localMaximum;
- bestPosition = localPosition;
- }
- state = STATE_SEEKING_POSITIVE;
- }
- break;
- }
- }
-
- /**
- * Generate interpolated maximum from index of absolute maximum using three point analysis.
- */
- private double findPreciseMaximum(int indexMax) {
- if (indexMax < 3) {
- return 3.0;
- }
- if (indexMax == (diffs.length - 1)) {
- return indexMax;
- }
- // Get 3 adjacent values.
- double d1 = diffs[indexMax - 1];
- double d2 = diffs[indexMax];
- double d3 = diffs[indexMax + 1];
-
- return interpolatePeak(d1, d2, d3) + indexMax;
- }
-
- // Use quadratic fit to return offset between -0.5 and +0.5 from center.
- protected static double interpolatePeak(double d1, double d2, double d3) {
- return 0.5 * (d1 - d3) / (d1 - (2.0 * d2) + d3);
- }
-
- // Calculate a little more for each sample.
- // This spreads the CPU load out more evenly.
- private boolean incrementalAnalysis() {
- boolean updated = false;
- if (bufferValid) {
- // int windowSize = maxWindowSize;
- // Interpolate between tau and maxWindowsSize based on confidence.
- // If confidence is low then use bigger window.
- int windowSize = (int) ((tau * confidence) + (maxWindowSize * (1.0 - confidence)));
-
- int stride = 1;
- // int stride = (windowSize / 32) + 1;
-
- diffs[tau] = topScan(cursor, tau, windowSize, stride);
-
- // Check to see if the signal is strong enough to analyze.
- // Look at sumPeriods on first correlation.
- if ((tau == minPeriod) && (sumProducts < noiseThreshold)) {
- // Update if we are dropping to zero confidence.
- boolean result = (confidence > 0.0);
- confidence = 0.0;
- return result;
- }
-
- nextPeakAnalysis(tau);
-
- // Reuse calculated values if we are not near a peak.
- tau += 1;
- int advance = tauAdvanceByState[state] - 1;
- while ((advance > 0) && (tau < diffs.length)) {
- diffs[tau] = diffs[tau - 1];
- tau++;
- advance--;
- }
-
- if ((peakCounter >= 4) || (tau >= maxWindowSize)) {
- if (bestMaximum > 0.0) {
- period = findPreciseMaximum(bestPosition);
- // clip into range 0.0 to 1.0, low values are really bogus
- confidence = (bestMaximum < 0.0) ? 0.0 : bestMaximum;
- } else {
- confidence = 0.0;
- }
- updated = true;
- reset();
- }
- }
- return updated;
- }
-
- @Override
- public float[] getDiffs() {
- // Return diffs that are not currently being used
- return (diffs == diffs1) ? diffs2 : diffs1;
- }
-
- private void switchDiffs() {
- diffs = (diffs == diffs1) ? diffs2 : diffs1;
- }
-
- @Override
- public boolean addSample(double value) {
- double average = (value + previousSample) * 0.5;
- previousSample = value;
-
- cursor += 1;
- if (cursor == buffer.length) {
- cursor = 0;
- bufferValid = true;
- }
- buffer[cursor] = (float) average;
-
- return incrementalAnalysis();
- }
-
- @Override
- public double getPeriod() {
- return period;
- }
-
- @Override
- public double getConfidence() {
- return confidence;
- }
-
- public float getPitchCorrectionFactor() {
- return pitchCorrectionFactor;
- }
-
- public void setPitchCorrectionFactor(float pitchCorrectionFactor) {
- this.pitchCorrectionFactor = pitchCorrectionFactor;
- }
-}
diff --git a/src/com/jsyn/util/Instrument.java b/src/com/jsyn/util/Instrument.java
deleted file mode 100644
index 8a53304..0000000
--- a/src/com/jsyn/util/Instrument.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import com.softsynth.shared.time.TimeStamp;
-
-/**
- * A note player that references one or more voices by a noteNumber. This is similar to the MIDI
- * protocol that references voices by an integer pitch or keyIndex.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public interface Instrument {
- // This will be applied to the voice when noteOn is called.
- void usePreset(int presetIndex, TimeStamp timeStamp);
-
- public void noteOn(int tag, double frequency, double amplitude, TimeStamp timeStamp);
-
- public void noteOff(int tag, TimeStamp timeStamp);
-
- public void setPort(int tag, String portName, double value, TimeStamp timeStamp);
-
- public void allNotesOff(TimeStamp timeStamp);
-}
diff --git a/src/com/jsyn/util/InstrumentLibrary.java b/src/com/jsyn/util/InstrumentLibrary.java
deleted file mode 100644
index 65113dc..0000000
--- a/src/com/jsyn/util/InstrumentLibrary.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import com.jsyn.swing.InstrumentBrowser;
-
-/**
- * A library of instruments that can be used to play notes.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- * @see InstrumentBrowser
- */
-
-public interface InstrumentLibrary {
- public String getName();
-
- public VoiceDescription[] getVoiceDescriptions();
-}
diff --git a/src/com/jsyn/util/JavaSoundSampleLoader.java b/src/com/jsyn/util/JavaSoundSampleLoader.java
deleted file mode 100644
index 56a654e..0000000
--- a/src/com/jsyn/util/JavaSoundSampleLoader.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import javax.sound.sampled.AudioFormat;
-import javax.sound.sampled.AudioInputStream;
-import javax.sound.sampled.AudioSystem;
-import javax.sound.sampled.UnsupportedAudioFileException;
-
-import com.jsyn.data.FloatSample;
-
-/**
- * Internal class for loading audio samples. Use SampleLoader instead.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-class JavaSoundSampleLoader implements AudioSampleLoader {
- /**
- * Load a FloatSample from a File object.
- */
- @Override
- public FloatSample loadFloatSample(File fileIn) throws IOException {
- try {
- return loadFloatSample(AudioSystem.getAudioInputStream(fileIn));
- } catch (UnsupportedAudioFileException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Load a FloatSample from an InputStream. This is handy when loading Resources from a JAR file.
- */
- @Override
- public FloatSample loadFloatSample(InputStream inputStream) throws IOException {
- try {
- return loadFloatSample(AudioSystem.getAudioInputStream(inputStream));
- } catch (UnsupportedAudioFileException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Load a FloatSample from a URL.. This is handy when loading Resources from a website.
- */
- @Override
- public FloatSample loadFloatSample(URL url) throws IOException {
- try {
- return loadFloatSample(AudioSystem.getAudioInputStream(url));
- } catch (UnsupportedAudioFileException e) {
- throw new IOException(e);
- }
- }
-
- private FloatSample loadFloatSample(javax.sound.sampled.AudioInputStream audioInputStream)
- throws IOException, UnsupportedAudioFileException {
- float[] floatData = null;
- FloatSample sample = null;
- int bytesPerFrame = audioInputStream.getFormat().getFrameSize();
- if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) {
- // some audio formats may have unspecified frame size
- // in that case we may read any amount of bytes
- bytesPerFrame = 1;
- }
- AudioFormat format = audioInputStream.getFormat();
- if (format.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
- floatData = loadSignedPCM(audioInputStream);
- }
- sample = new FloatSample(floatData, format.getChannels());
- sample.setFrameRate(format.getFrameRate());
- return sample;
- }
-
- private float[] loadSignedPCM(AudioInputStream audioInputStream) throws IOException,
- UnsupportedAudioFileException {
- int totalSamplesRead = 0;
- AudioFormat format = audioInputStream.getFormat();
- int numFrames = (int) audioInputStream.getFrameLength();
- int numSamples = format.getChannels() * numFrames;
- float[] data = new float[numSamples];
- final int bytesPerFrame = format.getFrameSize();
- // Set an arbitrary buffer size of 1024 frames.
- int numBytes = 1024 * bytesPerFrame;
- byte[] audioBytes = new byte[numBytes];
- int numBytesRead = 0;
- int numFramesRead = 0;
- // Try to read numBytes bytes from the file.
- while ((numBytesRead = audioInputStream.read(audioBytes)) != -1) {
- int bytesRemainder = numBytesRead % bytesPerFrame;
- if (bytesRemainder != 0) {
- // TODO Read until you get enough data.
- throw new IOException("Read partial block of sample data!");
- }
-
- if (audioInputStream.getFormat().getSampleSizeInBits() == 16) {
- if (format.isBigEndian()) {
- SampleLoader.decodeBigI16ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- } else {
- SampleLoader.decodeLittleI16ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- }
- } else if (audioInputStream.getFormat().getSampleSizeInBits() == 24) {
- if (format.isBigEndian()) {
- SampleLoader.decodeBigI24ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- } else {
- SampleLoader.decodeLittleI24ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- }
- } else if (audioInputStream.getFormat().getSampleSizeInBits() == 32) {
- if (format.isBigEndian()) {
- SampleLoader.decodeBigI32ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- } else {
- SampleLoader.decodeLittleI32ToF32(audioBytes, 0, numBytesRead, data,
- totalSamplesRead);
- }
- } else {
- throw new UnsupportedAudioFileException(
- "Only 16, 24 or 32 bit PCM samples supported.");
- }
-
- // Calculate the number of frames actually read.
- numFramesRead = numBytesRead / bytesPerFrame;
- totalSamplesRead += numFramesRead * format.getChannels();
- }
- return data;
- }
-
-}
diff --git a/src/com/jsyn/util/JavaTools.java b/src/com/jsyn/util/JavaTools.java
deleted file mode 100644
index 65e3dd1..0000000
--- a/src/com/jsyn/util/JavaTools.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.util;
-
-public class JavaTools {
-
- @SuppressWarnings("rawtypes")
- public static Class loadClass(String className, boolean verbose) {
- Class newClass = null;
- try {
- newClass = Class.forName(className);
- } catch (Throwable e) {
- if (verbose)
- System.out.println("Caught " + e);
- }
- if (newClass == null) {
- try {
- ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
- newClass = Class.forName(className, true, systemLoader);
- } catch (Throwable e) {
- if (verbose)
- System.out.println("Caught " + e);
- }
- }
- return newClass;
- }
-
- /**
- * First try Class.forName(). If this fails, try Class.forName() using
- * ClassLoader.getSystemClassLoader().
- *
- * @return Class or null
- */
- @SuppressWarnings("rawtypes")
- public static Class loadClass(String className) {
- /**
- * First try Class.forName(). If this fails, try Class.forName() using
- * ClassLoader.getSystemClassLoader().
- *
- * @return Class or null
- */
- return loadClass(className, true);
- }
-
-}
diff --git a/src/com/jsyn/util/MultiChannelSynthesizer.java b/src/com/jsyn/util/MultiChannelSynthesizer.java
deleted file mode 100644
index b84d753..0000000
--- a/src/com/jsyn/util/MultiChannelSynthesizer.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright 2016 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.util;
-
-import com.jsyn.Synthesizer;
-import com.jsyn.engine.SynthesisEngine;
-import com.jsyn.midi.MidiConstants;
-import com.jsyn.ports.UnitInputPort;
-import com.jsyn.ports.UnitOutputPort;
-import com.jsyn.unitgen.ExponentialRamp;
-import com.jsyn.unitgen.LinearRamp;
-import com.jsyn.unitgen.Multiply;
-import com.jsyn.unitgen.Pan;
-import com.jsyn.unitgen.PowerOfTwo;
-import com.jsyn.unitgen.SineOscillator;
-import com.jsyn.unitgen.TwoInDualOut;
-import com.jsyn.unitgen.UnitGenerator;
-import com.jsyn.unitgen.UnitOscillator;
-import com.jsyn.unitgen.UnitVoice;
-import com.softsynth.math.AudioMath;
-import com.softsynth.shared.time.TimeStamp;
-
-/**
- * General purpose synthesizer with "channels"
- * that could be used to implement a MIDI synthesizer.
- *
- * Each channel has:
- * <pre><code>
- * lfo -&gt; pitchToLinear -&gt; [VOICES] -&gt; volume* -&gt; panner
- * bend --/
- * </code></pre>
- *
- * Note: this class is experimental and subject to change.
- *
- * @author Phil Burk (C) 2016 Mobileer Inc
- */
-public class MultiChannelSynthesizer {
- private Synthesizer synth;
- private TwoInDualOut outputUnit;
- private ChannelContext[] channels;
- private final static int MAX_VELOCITY = 127;
- private double mMasterAmplitude = 0.25;
-
- private class ChannelGroupContext {
- private VoiceDescription voiceDescription;
- private UnitVoice[] voices;
- private VoiceAllocator allocator;
-
- ChannelGroupContext(int numVoices, VoiceDescription voiceDescription) {
- this.voiceDescription = voiceDescription;
-
- voices = new UnitVoice[numVoices];
- for (int i = 0; i < numVoices; i++) {
- UnitVoice voice = voiceDescription.createUnitVoice();
- UnitGenerator ugen = voice.getUnitGenerator();
- synth.add(ugen);
- voices[i] = voice;
-
- }
- allocator = new VoiceAllocator(voices);
- }
- }
-
- private class ChannelContext {
- private UnitOscillator lfo;
- private PowerOfTwo pitchToLinear;
- private LinearRamp timbreRamp;
- private LinearRamp pressureRamp;
- private ExponentialRamp volumeRamp;
- private Multiply volumeMultiplier;
- private Pan panner;
- private double vibratoRate = 5.0;
- private double bendRangeOctaves = 2.0 / 12.0;
- private int presetIndex;
- private ChannelGroupContext groupContext;
- VoiceOperation voiceOperation = new VoiceOperation() {
- @Override
- public void operate (UnitVoice voice) {
- voice.usePreset(presetIndex);
- connectVoice(voice);
- }
- };
-
- void setup(ChannelGroupContext groupContext) {
- this.groupContext = groupContext;
- synth.add(pitchToLinear = new PowerOfTwo());
- synth.add(lfo = new SineOscillator()); // TODO use a MorphingOscillator or switch
- // between S&H etc.
- // Use a ramp to smooth out the timbre changes.
- // This helps reduce pops from changing filter cutoff too abruptly.
- synth.add(timbreRamp = new LinearRamp());
- timbreRamp.time.set(0.02);
- synth.add(pressureRamp = new LinearRamp());
- pressureRamp.time.set(0.02);
- synth.add(volumeRamp = new ExponentialRamp());
- volumeRamp.input.set(1.0);
- volumeRamp.time.set(0.02);
- synth.add(volumeMultiplier = new Multiply());
- synth.add(panner = new Pan());
-
- pitchToLinear.input.setValueAdded(true); // so we can sum pitch bend
- lfo.output.connect(pitchToLinear.input);
- lfo.amplitude.set(0.0);
- lfo.frequency.set(vibratoRate);
-
- volumeRamp.output.connect(volumeMultiplier.inputB);
- volumeMultiplier.output.connect(panner.input);
- panner.output.connect(0, outputUnit.inputA, 0); // Use MultiPassthrough
- panner.output.connect(1, outputUnit.inputB, 0);
- }
-
- private void connectVoice(UnitVoice voice) {
- UnitGenerator ugen = voice.getUnitGenerator();
- // Hook up some channel controllers to standard ports on the voice.
- UnitInputPort freqMod = (UnitInputPort) ugen
- .getPortByName(UnitGenerator.PORT_NAME_FREQUENCY_SCALER);
- if (freqMod != null) {
- freqMod.disconnectAll();
- pitchToLinear.output.connect(freqMod);
- }
- UnitInputPort timbrePort = (UnitInputPort) ugen
- .getPortByName(UnitGenerator.PORT_NAME_TIMBRE);
- if (timbrePort != null) {
- timbrePort.disconnectAll();
- timbreRamp.output.connect(timbrePort);
- timbreRamp.input.setup(timbrePort);
- }
- UnitInputPort pressurePort = (UnitInputPort) ugen
- .getPortByName(UnitGenerator.PORT_NAME_PRESSURE);
- if (pressurePort != null) {
- pressurePort.disconnectAll();
- pressureRamp.output.connect(pressurePort);
- pressureRamp.input.setup(pressurePort);
- }
- voice.getOutput().disconnectAll();
- voice.getOutput().connect(volumeMultiplier.inputA); // mono mix all the voices
- }
-
- void programChange(int program) {
- int programWrapped = program % groupContext.voiceDescription.getPresetCount();
- String name = groupContext.voiceDescription.getPresetNames()[programWrapped];
- //System.out.println("Preset[" + program + "] = " + name);
- presetIndex = programWrapped;
- }
-
- void noteOff(int noteNumber, double amplitude) {
- groupContext.allocator.noteOff(noteNumber, synth.createTimeStamp());
- }
-
- void noteOff(int noteNumber, double amplitude, TimeStamp timeStamp) {
- groupContext.allocator.noteOff(noteNumber, timeStamp);
- }
-
- void noteOn(int noteNumber, double amplitude) {
- noteOn(noteNumber, amplitude, synth.createTimeStamp());
- }
-
- void noteOn(int noteNumber, double amplitude, TimeStamp timeStamp) {
- double frequency = AudioMath.pitchToFrequency(noteNumber);
- //System.out.println("noteOn(noteNumber) -> " + frequency + " Hz");
- groupContext.allocator.noteOn(noteNumber, frequency, amplitude, voiceOperation, timeStamp);
- }
-
- public void setPitchBend(double offset) {
- pitchToLinear.input.set(bendRangeOctaves * offset);
- }
-
- public void setBendRange(double semitones) {
- bendRangeOctaves = semitones / 12.0;
- }
-
- public void setVibratoDepth(double semitones) {
- lfo.amplitude.set(semitones);
- }
-
- public void setVolume(double volume) {
- double min = SynthesisEngine.DB96;
- double max = 1.0;
- double ratio = max / min;
- double value = min * Math.pow(ratio, volume);
- volumeRamp.input.set(value);
- }
-
- public void setPan(double pan) {
- panner.pan.set(pan);
- }
-
- /*
- * @param timbre normalized 0 to 1
- */
- public void setTimbre(double timbre) {
- double min = timbreRamp.input.getMinimum();
- double max = timbreRamp.input.getMaximum();
- double value = min + (timbre * (max - min));
- timbreRamp.input.set(value);
- }
-
- /*
- * @param pressure normalized 0 to 1
- */
- public void setPressure(double pressure) {
- double min = pressureRamp.input.getMinimum();
- double max = pressureRamp.input.getMaximum();
- double ratio = max / min;
- double value = min * Math.pow(ratio, pressure);
- pressureRamp.input.set(value);
- }
- }
-
- /**
- * Construct a synthesizer with a maximum of 16 channels like MIDI.
- */
- public MultiChannelSynthesizer() {
- this(MidiConstants.MAX_CHANNELS);
- }
-
-
- public MultiChannelSynthesizer(int maxChannels) {
- channels = new ChannelContext[maxChannels];
- for (int i = 0; i < channels.length; i++) {
- channels[i] = new ChannelContext();
- }
- }
-
- /**
- * Specify a VoiceDescription to use with multiple channels.
- *
- * @param synth
- * @param startChannel channel index is zero based
- * @param numChannels
- * @param voicesPerChannel
- * @param voiceDescription
- */
- public void setup(Synthesizer synth, int startChannel, int numChannels, int voicesPerChannel,
- VoiceDescription voiceDescription) {
- this.synth = synth;
- if (outputUnit == null) {
- synth.add(outputUnit = new TwoInDualOut());
- }
- ChannelGroupContext groupContext = new ChannelGroupContext(voicesPerChannel,
- voiceDescription);
- for (int i = 0; i < numChannels; i++) {
- channels[startChannel + i].setup(groupContext);
- }
- }
-
- public void programChange(int channel, int program) {
- ChannelContext channelContext = channels[channel];
- channelContext.programChange(program);
- }
-
-
- /**
- * Turn off a note.
- * @param channel
- * @param noteNumber
- * @param velocity between 0 and 127, will be scaled by masterAmplitude
- */
- public void noteOff(int channel, int noteNumber, int velocity) {
- double amplitude = velocity * (1.0 / MAX_VELOCITY);
- noteOff(channel, noteNumber, amplitude);
- }
-
- /**
- * Turn off a note.
- * @param channel
- * @param noteNumber
- * @param amplitude between 0 and 1.0, will be scaled by masterAmplitude
- */
- public void noteOff(int channel, int noteNumber, double amplitude) {
- ChannelContext channelContext = channels[channel];
- channelContext.noteOff(noteNumber, amplitude * mMasterAmplitude);
- }
-
- /**
- * Turn off a note.
- * @param channel
- * @param noteNumber
- * @param amplitude between 0 and 1.0, will be scaled by masterAmplitude
- */
- public void noteOff(int channel, int noteNumber, double amplitude, TimeStamp timeStamp) {
- ChannelContext channelContext = channels[channel];
- channelContext.noteOff(noteNumber, amplitude * mMasterAmplitude, timeStamp);
- }
-
- /**
- * Turn on a note.
- * @param channel
- * @param noteNumber
- * @param velocity between 0 and 127, will be scaled by masterAmplitude
- */
- public void noteOn(int channel, int noteNumber, int velocity) {
- double amplitude = velocity * (1.0 / MAX_VELOCITY);
- noteOn(channel, noteNumber, amplitude);
- }
-
- /**
- * Turn on a note.
- * @param channel
- * @param noteNumber
- * @param amplitude between 0 and 1.0, will be scaled by masterAmplitude
- */
- public void noteOn(int channel, int noteNumber, double amplitude, TimeStamp timeStamp) {
- ChannelContext channelContext = channels[channel];
- channelContext.noteOn(noteNumber, amplitude * mMasterAmplitude, timeStamp);
- }
-
- /**
- * Turn on a note.
- * @param channel
- * @param noteNumber
- * @param amplitude between 0 and 1.0, will be scaled by masterAmplitude
- */
- public void noteOn(int channel, int noteNumber, double amplitude) {
- ChannelContext channelContext = channels[channel];
- channelContext.noteOn(noteNumber, amplitude * mMasterAmplitude);
- }
-
- /**
- * Set a pitch offset that will be scaled by the range for the channel.
- *
- * @param channel
- * @param offset ranges from -1.0 to +1.0
- */
- public void setPitchBend(int channel, double offset) {
- //System.out.println("setPitchBend[" + channel + "] = " + offset);
- ChannelContext channelContext = channels[channel];
- channelContext.setPitchBend(offset);
- }
-
- public void setBendRange(int channel, double semitones) {
- ChannelContext channelContext = channels[channel];
- channelContext.setBendRange(semitones);
- }
-
- public void setPressure(int channel, double pressure) {
- ChannelContext channelContext = channels[channel];
- channelContext.setPressure(pressure);
- }
-
- public void setVibratoDepth(int channel, double semitones) {
- ChannelContext channelContext = channels[channel];
- channelContext.setVibratoDepth(semitones);
- }
-
- public void setTimbre(int channel, double timbre) {
- ChannelContext channelContext = channels[channel];
- channelContext.setTimbre(timbre);
- }
-
- /**
- * Set volume for entire channel.
- *
- * @param channel
- * @param volume normalized between 0.0 and 1.0
- */
- public void setVolume(int channel, double volume) {
- ChannelContext channelContext = channels[channel];
- channelContext.setVolume(volume);
- }
-
- /**
- * Pan from left to right.
- *
- * @param channel
- * @param pan ranges from -1.0 to +1.0
- */
- public void setPan(int channel, double pan) {
- ChannelContext channelContext = channels[channel];
- channelContext.setPan(pan);
- }
-
- /**
- * @return stereo output port
- */
- public UnitOutputPort getOutput() {
- return outputUnit.output;
- }
-
- /**
- * Set amplitude for a single voice when the velocity is 127.
- * @param masterAmplitude
- */
- public void setMasterAmplitude(double masterAmplitude) {
- mMasterAmplitude = masterAmplitude;
- }
- public double getMasterAmplitude() {
- return mMasterAmplitude;
- }
-}
diff --git a/src/com/jsyn/util/NumericOutput.java b/src/com/jsyn/util/NumericOutput.java
deleted file mode 100644
index 79afaf1..0000000
--- a/src/com/jsyn/util/NumericOutput.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 1999 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.util;
-
-/**
- * Formatted numeric output. Convert integers and floats to strings based on field widths and
- * desired decimal places.
- *
- * @author Phil Burk (C) 1999 SoftSynth.com
- */
-
-public class NumericOutput {
- static char digitToChar(int digit) {
- if (digit > 9) {
- return (char) ('A' + digit - 10);
- } else {
- return (char) ('0' + digit);
- }
- }
-
- public static String integerToString(int n, int width, boolean leadingZeros) {
- return integerToString(n, width, leadingZeros, 10);
- }
-
- public static String integerToString(int n, int width) {
- return integerToString(n, width, false, 10);
- }
-
- public static String integerToString(int n, int width, boolean leadingZeros, int radix) {
- if (width > 32)
- width = 32;
- StringBuffer buf = new StringBuffer();
- long ln = n;
- boolean ifNeg = false;
- // only do sign if decimal
- if (radix != 10) {
- // System.out.println("MASK before : ln = " + ln );
- ln = ln & 0x00000000FFFFFFFFL;
- // System.out.println("MASK after : ln = " + ln );
- } else if (ln < 0) {
- ifNeg = true;
- ln = -ln;
- }
- if (ln == 0) {
- buf.append('0');
- } else {
- // System.out.println(" ln = " + ln );
- while (ln > 0) {
- int rem = (int) (ln % radix);
- buf.append(digitToChar(rem));
- ln = ln / radix;
- }
- }
- if (leadingZeros) {
- int pl = width;
- if (ifNeg)
- pl -= 1;
- for (int i = buf.length(); i < pl; i++)
- buf.append('0');
- }
- if (ifNeg)
- buf.append('-');
- // leading spaces
- for (int i = buf.length(); i < width; i++)
- buf.append(' ');
- // reverse buffer to put characters in correct order
- buf.reverse();
-
- return buf.toString();
- }
-
- /**
- * Convert double to string.
- *
- * @param width = minimum width of formatted string
- * @param places = number of digits displayed after decimal point
- */
- public static String doubleToString(double value, int width, int places) {
- return doubleToString(value, width, places, false);
- }
-
- /**
- * Convert double to string.
- *
- * @param width = minimum width of formatted string
- * @param places = number of digits displayed after decimal point
- */
- public static String doubleToString(double value, int width, int places, boolean leadingZeros) {
- if (width > 32)
- width = 32;
- if (places > 16)
- places = 16;
-
- boolean ifNeg = false;
- if (value < 0.0) {
- ifNeg = true;
- value = -value;
- }
- // round at relevant decimal place
- value += 0.5 * Math.pow(10.0, 0 - places);
- int ival = (int) Math.floor(value);
- // get portion after decimal point as an integer
- int fval = (int) ((value - Math.floor(value)) * Math.pow(10.0, places));
- String result = "";
-
- result += integerToString(ival, 0, false, 10);
- result += ".";
- result += integerToString(fval, places, true, 10);
-
- if (leadingZeros) {
- // prepend leading zeros and {-}
- int zw = width;
- if (ifNeg)
- zw -= 1;
- while (result.length() < zw)
- result = "0" + result;
- if (ifNeg)
- result = "-" + result;
- } else {
- // prepend {-} and leading spaces
- if (ifNeg)
- result = "-" + result;
- while (result.length() < width)
- result = " " + result;
- }
- return result;
- }
-
- static void testInteger(int n) {
- System.out.println("Test " + n + ", 0x" + Integer.toHexString(n) + ", %"
- + Integer.toBinaryString(n));
- System.out.println(" +,8,t,10 = " + integerToString(n, 8, true, 10));
- System.out.println(" +,8,f,10 = " + integerToString(n, 8, false, 10));
- System.out.println(" -,8,t,10 = " + integerToString(-n, 8, true, 10));
- System.out.println(" -,8,f,10 = " + integerToString(-n, 8, false, 10));
- System.out.println(" +,8,t,16 = " + integerToString(n, 8, true, 16));
- System.out.println(" +,8,f,16 = " + integerToString(n, 8, false, 16));
- System.out.println(" -,8,t,16 = " + integerToString(-n, 8, true, 16));
- System.out.println(" -,8,f,16 = " + integerToString(-n, 8, false, 16));
- System.out.println(" +,8,t, 2 = " + integerToString(n, 8, true, 2));
- System.out.println(" +,8,f, 2 = " + integerToString(n, 8, false, 2));
- }
-
- static void testDouble(double value) {
- System.out.println("Test " + value);
- System.out.println(" +,5,1 = " + doubleToString(value, 5, 1));
- System.out.println(" -,5,1 = " + doubleToString(-value, 5, 1));
-
- System.out.println(" +,14,3 = " + doubleToString(value, 14, 3));
- System.out.println(" -,14,3 = " + doubleToString(-value, 14, 3));
-
- System.out.println(" +,6,2,true = " + doubleToString(value, 6, 2, true));
- System.out.println(" -,6,2,true = " + doubleToString(-value, 6, 2, true));
- }
-
- public static void main(String argv[]) {
- System.out.println("Test NumericOutput");
- testInteger(0);
- testInteger(1);
- testInteger(16);
- testInteger(23456);
- testInteger(0x23456);
- testInteger(0x89ABC);
- testDouble(0.0);
- testDouble(0.0678);
- testDouble(0.1234567);
- testDouble(1.234567);
- testDouble(12.34567);
- testDouble(123.4567);
- testDouble(1234.5678);
-
- }
-}
diff --git a/src/com/jsyn/util/PolyphonicInstrument.java b/src/com/jsyn/util/PolyphonicInstrument.java
deleted file mode 100644
index 08460d0..0000000
--- a/src/com/jsyn/util/PolyphonicInstrument.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import com.jsyn.ports.UnitInputPort;
-import com.jsyn.ports.UnitOutputPort;
-import com.jsyn.ports.UnitPort;
-import com.jsyn.unitgen.Circuit;
-import com.jsyn.unitgen.Multiply;
-import com.jsyn.unitgen.PassThrough;
-import com.jsyn.unitgen.UnitGenerator;
-import com.jsyn.unitgen.UnitSource;
-import com.jsyn.unitgen.UnitVoice;
-import com.softsynth.shared.time.TimeStamp;
-
-/**
- * The API for this class is likely to change. Please comment on its usefulness.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-
-public class PolyphonicInstrument extends Circuit implements UnitSource, Instrument {
- private Multiply mixer;
- private UnitVoice[] voices;
- private VoiceAllocator voiceAllocator;
- public UnitInputPort amplitude;
-
- public PolyphonicInstrument(UnitVoice[] voices) {
- this.voices = voices;
- voiceAllocator = new VoiceAllocator(voices);
- add(mixer = new Multiply());
- // Mix all the voices to one output.
- for (UnitVoice voice : voices) {
- UnitGenerator unit = voice.getUnitGenerator();
- boolean wasEnabled = unit.isEnabled();
- // This overrides the enabled property of the voice.
- add(unit);
- voice.getOutput().connect(mixer.inputA);
- // restore
- unit.setEnabled(wasEnabled);
- }
-
- addPort(amplitude = mixer.inputB, "Amplitude");
- amplitude.setup(0.0001, 0.4, 2.0);
- exportAllInputPorts();
- }
-
- /**
- * Connect a PassThrough unit to the input ports of the voices so that they can be controlled
- * together using a single port. Note that this will prevent their individual use. So the
- * "Frequency" and "Amplitude" ports are excluded. Note that this method is a bit funky and is
- * likely to change.
- */
- public void exportAllInputPorts() {
- // Iterate through the ports.
- for (UnitPort port : voices[0].getUnitGenerator().getPorts()) {
- if (port instanceof UnitInputPort) {
- UnitInputPort inputPort = (UnitInputPort) port;
- String voicePortName = inputPort.getName();
- // FIXME Need better way to identify ports that are per note.
- if (!voicePortName.equals("Frequency") && !voicePortName.equals("Amplitude")) {
- exportNamedInputPort(voicePortName);
- }
- }
- }
- }
-
- /**
- * Create a UnitInputPort for the circuit that is connected to the named port on each voice
- * through a PassThrough unit. This allows you to control all of the voices at once.
- *
- * @param portName
- * @see exportAllInputPorts
- */
- public void exportNamedInputPort(String portName) {
- UnitInputPort voicePort = null;
- PassThrough fanout = new PassThrough();
- for (UnitVoice voice : voices) {
- voicePort = (UnitInputPort) voice.getUnitGenerator().getPortByName(portName);
- fanout.output.connect(voicePort);
- }
- if (voicePort != null) {
- addPort(fanout.input, portName);
- fanout.input.setup(voicePort);
- }
- }
-
- @Override
- public UnitOutputPort getOutput() {
- return mixer.output;
- }
-
- @Override
- public void usePreset(int presetIndex) {
- usePreset(presetIndex, getSynthesisEngine().createTimeStamp());
- }
-
- // FIXME - no timestamp on UnitVoice
- @Override
- public void usePreset(int presetIndex, TimeStamp timeStamp) {
- // Apply preset to all voices.
- for (UnitVoice voice : voices) {
- voice.usePreset(presetIndex);
- }
- // Then copy values from first voice to instrument.
- for (UnitPort port : voices[0].getUnitGenerator().getPorts()) {
- if (port instanceof UnitInputPort) {
- UnitInputPort inputPort = (UnitInputPort) port;
- // FIXME Need better way to identify ports that are per note.
- UnitInputPort fanPort = (UnitInputPort) getPortByName(inputPort.getName());
- if ((fanPort != null) && (fanPort != amplitude)) {
- fanPort.set(inputPort.get());
- }
- }
- }
- }
-
- @Override
- public void noteOn(int tag, double frequency, double amplitude, TimeStamp timeStamp) {
- voiceAllocator.noteOn(tag, frequency, amplitude, timeStamp);
- }
-
- @Override
- public void noteOff(int tag, TimeStamp timeStamp) {
- voiceAllocator.noteOff(tag, timeStamp);
- }
-
- @Override
- public void setPort(int tag, String portName, double value, TimeStamp timeStamp) {
- voiceAllocator.setPort(tag, portName, value, timeStamp);
- }
-
- @Override
- public void allNotesOff(TimeStamp timeStamp) {
- voiceAllocator.allNotesOff(timeStamp);
- }
-
- public synchronized boolean isOn(int tag) {
- return voiceAllocator.isOn(tag);
- }
-}
diff --git a/src/com/jsyn/util/PseudoRandom.java b/src/com/jsyn/util/PseudoRandom.java
deleted file mode 100644
index e92b669..0000000
--- a/src/com/jsyn/util/PseudoRandom.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.
- */
-/**
- * Sep 9, 2009
- * com.jsyn.engine.units.SynthRandom.java
- */
-
-package com.jsyn.util;
-
-import java.util.Random;
-
-/**
- * Pseudo-random numbers using predictable and fast linear-congruential method.
- *
- * @author Phil Burk (C) 2009 Mobileer Inc Translated from 'C' to Java by Lisa
- * Tolentino.
- */
-public class PseudoRandom {
- // We must shift 1L or else we get a negative number!
- private static final double INT_TO_DOUBLE = (1.0 / (1L << 31));
- private long seed = 99887766;
-
- /**
- * Create an instance of SynthRandom.
- */
- public PseudoRandom() {
- this(new Random().nextInt());
- }
-
- /**
- * Create an instance of PseudoRandom.
- */
- public PseudoRandom(int seed) {
- setSeed(seed);
- }
-
- public void setSeed(int seed) {
- this.seed = (long) seed;
- }
-
- public int getSeed() {
- return (int) seed;
- }
-
- /**
- * Returns the next random double from 0.0 to 1.0
- *
- * @return value from 0.0 to 1.0
- */
- public double random() {
- int positiveInt = nextRandomInteger() & 0x7FFFFFFF;
- return positiveInt * INT_TO_DOUBLE;
- }
-
- /**
- * Returns the next random double from -1.0 to 1.0
- *
- * @return value from -1.0 to 1.0
- */
- public double nextRandomDouble() {
- return nextRandomInteger() * INT_TO_DOUBLE;
- }
-
- /** Calculate random 32 bit number using linear-congruential method. */
- public int nextRandomInteger() {
- // Use values for 64-bit sequence from MMIX by Donald Knuth.
- seed = (seed * 6364136223846793005L) + 1442695040888963407L;
- return (int) (seed >> 32); // The higher bits have a longer sequence.
- }
-
- public int choose(int range) {
- long positiveInt = nextRandomInteger() & 0x7FFFFFFF;
- long temp = positiveInt * range;
- return (int) (temp >> 31);
- }
-}
diff --git a/src/com/jsyn/util/RecursiveSequenceGenerator.java b/src/com/jsyn/util/RecursiveSequenceGenerator.java
deleted file mode 100644
index 53dbdf9..0000000
--- a/src/com/jsyn/util/RecursiveSequenceGenerator.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 1997 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.util;
-
-import java.util.Random;
-
-/**
- * Generate a sequence of integers based on a recursive mining of previous material. Notes are
- * generated by one of the following formula:
- *
- * <pre>
- * <code>
- * value[n] = value[n-delay] + offset;
- * </code>
- * </pre>
- *
- * The parameters delay and offset are randomly generated. This algorithm was first developed in
- * 1977 for a class project in FORTRAN. It was ported to Forth for HMSL in the late 80's. It was
- * then ported to Java for JSyn in 1997.
- *
- * @author Phil Burk (C) 1997,2011 Mobileer Inc
- */
-public class RecursiveSequenceGenerator {
- private int delay = 1;
- private int maxValue;
- private int maxInterval;
- private double desiredDensity = 0.5;
-
- private int offset;
- private int values[];
- private boolean enables[];
- private int cursor;
- private int countdown = -1;
- private double actualDensity;
- private int beatsPerMeasure = 8;
- private Random random;
-
- public RecursiveSequenceGenerator() {
- this(25, 7, 64);
- }
-
- public RecursiveSequenceGenerator(int maxValue, int maxInterval, int arraySize) {
- values = new int[arraySize];
- enables = new boolean[arraySize];
- this.maxValue = maxValue;
- this.maxInterval = maxInterval;
- for (int i = 0; i < values.length; i++) {
- values[i] = maxValue / 2;
- enables[i] = isNextEnabled(false);
- }
- }
-
- /** Set density of notes. 0.0 to 1.0 */
- public void setDensity(double density) {
- desiredDensity = density;
- }
-
- public double getDensity() {
- return desiredDensity;
- }
-
- /** Set maximum for generated value. */
- public void setMaxValue(int maxValue) {
- this.maxValue = maxValue;
- }
-
- public int getMaxValue() {
- return maxValue;
- }
-
- /** Set maximum for generated value. */
- public void setMaxInterval(int maxInterval) {
- this.maxInterval = maxInterval;
- }
-
- public int getMaxInterval() {
- return maxInterval;
- }
-
- /* Determine whether next in sequence should occur. */
- public boolean isNextEnabled(boolean preferance) {
- /* Calculate note density using low pass IIR filter. */
- double newDensity = (actualDensity * 0.9) + (preferance ? 0.1 : 0.0);
- /* Invert enable to push density towards desired level, with hysteresis. */
- if (preferance && (newDensity > ((desiredDensity * 0.7) + 0.3)))
- preferance = false;
- else if (!preferance && (newDensity < (desiredDensity * 0.7)))
- preferance = true;
- actualDensity = (actualDensity * 0.9) + (preferance ? 0.1 : 0.0);
- return preferance;
- }
-
- public int randomPowerOf2(int maxExp) {
- return (1 << (int) (random.nextDouble() * (maxExp + 1)));
- }
-
- /** Random number evenly distributed from -maxInterval to +maxInterval */
- public int randomEvenInterval() {
- return (int) (random.nextDouble() * ((maxInterval * 2) + 1)) - maxInterval;
- }
-
- void calcNewOffset() {
- offset = randomEvenInterval();
- }
-
- public void randomize() {
-
- delay = randomPowerOf2(4);
- calcNewOffset();
- // System.out.println("NewSeq: delay = " + delay + ", offset = " +
- // offset );
- }
-
- /** Change parameters based on random countdown. */
- public int next() {
- // If this sequence is finished, start a new one.
- if (countdown-- < 0) {
- randomize();
- countdown = randomPowerOf2(3);
- }
- return nextValue();
- }
-
- /** Change parameters using a probability based on beatIndex. */
- public int next(int beatIndex) {
- int beatMod = beatIndex % beatsPerMeasure;
- switch (beatMod) {
- case 0:
- if (Math.random() < 0.90)
- randomize();
- break;
- case 2:
- case 6:
- if (Math.random() < 0.15)
- randomize();
- break;
- case 4:
- if (Math.random() < 0.30)
- randomize();
- break;
- default:
- if (Math.random() < 0.07)
- randomize();
- break;
- }
- return nextValue();
- }
-
- /** Generate nextValue based on current delay and offset */
- public int nextValue() {
- // Generate index into circular value buffer.
- int idx = (cursor - delay);
- if (idx < 0)
- idx += values.length;
-
- // Generate new value. Calculate new offset if too high or low.
- int nextVal = 0;
- int timeout = 100;
- while (timeout > 0) {
- nextVal = values[idx] + offset;
- if ((nextVal >= 0) && (nextVal < maxValue))
- break;
- // Prevent endless loops when maxValue changes.
- if (nextVal > (maxValue + maxInterval - 1)) {
- nextVal = maxValue;
- break;
- }
- calcNewOffset();
- timeout--;
- // System.out.println("NextVal = " + nextVal + ", offset = " +
- // offset );
- }
- if (timeout <= 0) {
- System.err.println("RecursiveSequence: nextValue timed out. offset = " + offset);
- nextVal = maxValue / 2;
- offset = 0;
- }
-
- // Save new value in circular buffer.
- values[cursor] = nextVal;
-
- boolean playIt = enables[cursor] = isNextEnabled(enables[idx]);
- cursor++;
- if (cursor >= values.length)
- cursor = 0;
-
- // System.out.println("nextVal = " + nextVal );
-
- return playIt ? nextVal : -1;
- }
-
- public Random getRandom() {
- return random;
- }
-
- public void setRandom(Random random) {
- this.random = random;
- }
-
-}
diff --git a/src/com/jsyn/util/SampleLoader.java b/src/com/jsyn/util/SampleLoader.java
deleted file mode 100644
index 170b4cb..0000000
--- a/src/com/jsyn/util/SampleLoader.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.util.soundfile.CustomSampleLoader;
-
-/**
- * Load a FloatSample from various sources. The default loader uses custom code to load WAV or AIF
- * files. Supported data formats are 16, 24 and 32 bit PCM, and 32-bit float. Compressed formats
- * such as unsigned 8-bit, uLaw, A-Law and MP3 are not support. If you need to load one of those
- * files try setJavaSoundPreferred(true). Or convert it to a supported format using Audacity or Sox
- * or some other sample file tool. Here is an example of loading a sample from a file.
- *
- * <pre>
- * <code>
- * File sampleFile = new File("guitar.wav");
- * FloatSample sample = SampleLoader.loadFloatSample( sampleFile );
- * </code>
- * </pre>
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public class SampleLoader {
- private static boolean javaSoundPreferred = false;
- private static final String JS_LOADER_NAME = "com.jsyn.util.JavaSoundSampleLoader";
-
- /**
- * Try to create an implementation of AudioSampleLoader.
- *
- * @return A device supported on this platform.
- */
- private static AudioSampleLoader createLoader() {
- AudioSampleLoader loader = null;
- try {
- if (javaSoundPreferred) {
- loader = (AudioSampleLoader) JavaTools.loadClass(JS_LOADER_NAME).newInstance();
- } else {
- loader = new CustomSampleLoader();
- }
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- return loader;
- }
-
- /**
- * Load a FloatSample from a File object.
- */
- public static FloatSample loadFloatSample(File fileIn) throws IOException {
- AudioSampleLoader loader = SampleLoader.createLoader();
- return loader.loadFloatSample(fileIn);
- }
-
- /**
- * Load a FloatSample from an InputStream. This is handy when loading Resources from a JAR file.
- */
- public static FloatSample loadFloatSample(InputStream inputStream) throws IOException {
- AudioSampleLoader loader = SampleLoader.createLoader();
- return loader.loadFloatSample(inputStream);
- }
-
- /**
- * Load a FloatSample from a URL.. This is handy when loading Resources from a website.
- */
- public static FloatSample loadFloatSample(URL url) throws IOException {
- AudioSampleLoader loader = SampleLoader.createLoader();
- return loader.loadFloatSample(url);
- }
-
- public static boolean isJavaSoundPreferred() {
- return javaSoundPreferred;
- }
-
- /**
- * If set true then the audio file parser from JavaSound will be used. Note that JavaSound
- * cannot load audio files containing floating point data. But it may be able to load some
- * compressed data formats such as uLaw.
- *
- * Note: JavaSound is not supported on Android.
- *
- * @param javaSoundPreferred
- */
- public static void setJavaSoundPreferred(boolean javaSoundPreferred) {
- SampleLoader.javaSoundPreferred = javaSoundPreferred;
- }
-
- /**
- * Decode 24 bit samples from a BigEndian byte array into a float array. The samples will be
- * normalized into the range -1.0 to +1.0.
- *
- * @param audioBytes raw data from an audio file
- * @param offset first element of byte array
- * @param numBytes number of bytes to process
- * @param data array to be filled with floats
- * @param outputOffset first element of float array to be filled
- */
- public static void decodeBigI24ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
- int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int hi = ((audioBytes[byteCursor++]) & 0x00FF);
- int mid = ((audioBytes[byteCursor++]) & 0x00FF);
- int lo = ((audioBytes[byteCursor++]) & 0x00FF);
- int value = (hi << 24) | (mid << 16) | (lo << 8);
- data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
- }
- }
-
- public static void decodeBigI16ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
- int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int hi = ((audioBytes[byteCursor++]) & 0x00FF);
- int lo = ((audioBytes[byteCursor++]) & 0x00FF);
- short value = (short) ((hi << 8) | lo);
- data[floatCursor++] = value * (1.0f / 32768);
- }
- }
-
- public static void decodeBigF32ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
- int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int bits = audioBytes[byteCursor++];
- bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- data[floatCursor++] = Float.intBitsToFloat(bits);
- }
- }
-
- public static void decodeBigI32ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
- int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int value = audioBytes[byteCursor++]; // MSB
- value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
- data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
- }
- }
-
- public static void decodeLittleF32ToF32(byte[] audioBytes, int offset, int numBytes,
- float[] data, int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int bits = ((audioBytes[byteCursor++]) & 0x00FF); // LSB
- bits += ((audioBytes[byteCursor++]) & 0x00FF) << 8;
- bits += ((audioBytes[byteCursor++]) & 0x00FF) << 16;
- bits += (audioBytes[byteCursor++]) << 24;
- data[floatCursor++] = Float.intBitsToFloat(bits);
- }
- }
-
- public static void decodeLittleI32ToF32(byte[] audioBytes, int offset, int numBytes,
- float[] data, int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int value = ((audioBytes[byteCursor++]) & 0x00FF);
- value += ((audioBytes[byteCursor++]) & 0x00FF) << 8;
- value += ((audioBytes[byteCursor++]) & 0x00FF) << 16;
- value += (audioBytes[byteCursor++]) << 24;
- data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
- }
- }
-
- public static void decodeLittleI24ToF32(byte[] audioBytes, int offset, int numBytes,
- float[] data, int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int lo = ((audioBytes[byteCursor++]) & 0x00FF);
- int mid = ((audioBytes[byteCursor++]) & 0x00FF);
- int hi = ((audioBytes[byteCursor++]) & 0x00FF);
- int value = (hi << 24) | (mid << 16) | (lo << 8);
- data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
- }
- }
-
- public static void decodeLittleI16ToF32(byte[] audioBytes, int offset, int numBytes,
- float[] data, int outputOffset) {
- int lastByte = offset + numBytes;
- int byteCursor = offset;
- int floatCursor = outputOffset;
- while (byteCursor < lastByte) {
- int lo = ((audioBytes[byteCursor++]) & 0x00FF);
- int hi = ((audioBytes[byteCursor++]) & 0x00FF);
- short value = (short) ((hi << 8) | lo);
- float sample = value * (1.0f / 32768);
- data[floatCursor++] = sample;
- }
- }
-
-}
diff --git a/src/com/jsyn/util/SignalCorrelator.java b/src/com/jsyn/util/SignalCorrelator.java
deleted file mode 100644
index ebdd46b..0000000
--- a/src/com/jsyn/util/SignalCorrelator.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-/**
- * Interface used to evaluate various algorithms for pitch detection.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public interface SignalCorrelator {
- /**
- * Add a sample to be analyzed. The samples will generally be held in a circular buffer.
- *
- * @param value
- * @return true if a new period value has been generated
- */
- public boolean addSample(double value);
-
- /**
- * @return the estimated period of the waveform in samples
- */
- public double getPeriod();
-
- /**
- * Measure of how confident the analyzer is of the last result.
- *
- * @return quality of the estimate between 0.0 and 1.0
- */
- public double getConfidence();
-
- /** For internal debugging. */
- public float[] getDiffs();
-
-}
diff --git a/src/com/jsyn/util/StreamingThread.java b/src/com/jsyn/util/StreamingThread.java
deleted file mode 100644
index 682f476..0000000
--- a/src/com/jsyn/util/StreamingThread.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import java.io.IOException;
-
-import com.jsyn.io.AudioInputStream;
-import com.jsyn.io.AudioOutputStream;
-
-/**
- * Read from an AudioInputStream and write to an AudioOutputStream as a background thread.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public class StreamingThread extends Thread {
- private AudioInputStream inputStream;
- private AudioOutputStream outputStream;
- private int framesPerBuffer = 1024;
- private volatile boolean go = true;
- private TransportModel transportModel;
- private long framePosition;
- private long maxFrames;
- private int samplesPerFrame = 1;
-
- public StreamingThread(AudioInputStream inputStream, AudioOutputStream outputStream) {
- this.inputStream = inputStream;
- this.outputStream = outputStream;
- }
-
- @Override
- public void run() {
- double[] buffer = new double[framesPerBuffer * samplesPerFrame];
- try {
- transportModel.firePositionChanged(framePosition);
- transportModel.fireStateChanged(TransportModel.STATE_RUNNING);
- int framesToRead = geteFramesToRead(buffer);
- while (go && (framesToRead > 0)) {
- int samplesToRead = framesToRead * samplesPerFrame;
- while (samplesToRead > 0) {
- int samplesRead = inputStream.read(buffer, 0, samplesToRead);
- outputStream.write(buffer, 0, samplesRead);
- samplesToRead -= samplesRead;
- }
- framePosition += framesToRead;
- transportModel.firePositionChanged(framePosition);
- framesToRead = geteFramesToRead(buffer);
- }
- transportModel.fireStateChanged(TransportModel.STATE_STOPPED);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private int geteFramesToRead(double[] buffer) {
- if (maxFrames > 0) {
- long numToRead = maxFrames - framePosition;
- if (numToRead < 0) {
- return 0;
- } else if (numToRead > framesPerBuffer) {
- numToRead = framesPerBuffer;
- }
- return (int) numToRead;
- } else {
- return framesPerBuffer;
- }
- }
-
- public int getFramesPerBuffer() {
- return framesPerBuffer;
- }
-
- /**
- * Only call this before the thread has started.
- *
- * @param framesPerBuffer
- */
- public void setFramesPerBuffer(int framesPerBuffer) {
- this.framesPerBuffer = framesPerBuffer;
- }
-
- public void requestStop() {
- go = false;
- }
-
- public TransportModel getTransportModel() {
- return transportModel;
- }
-
- public void setTransportModel(TransportModel transportModel) {
- this.transportModel = transportModel;
- }
-
- /**
- * @param maxFrames
- */
- public void setMaxFrames(long maxFrames) {
- this.maxFrames = maxFrames;
- }
-
- public int getSamplesPerFrame() {
- return samplesPerFrame;
- }
-
- public void setSamplesPerFrame(int samplesPerFrame) {
- this.samplesPerFrame = samplesPerFrame;
- }
-}
diff --git a/src/com/jsyn/util/TransportListener.java b/src/com/jsyn/util/TransportListener.java
deleted file mode 100644
index 3c8b048..0000000
--- a/src/com/jsyn/util/TransportListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.util;
-
-public interface TransportListener {
- /**
- * @param transportModel
- * @param framePosition position in frames
- */
- void positionChanged(TransportModel transportModel, long framePosition);
-
- /**
- * @param transportModel
- * @param state for example TransportModel.STATE_STOPPED
- */
- void stateChanged(TransportModel transportModel, int state);
-}
diff --git a/src/com/jsyn/util/TransportModel.java b/src/com/jsyn/util/TransportModel.java
deleted file mode 100644
index bcc75be..0000000
--- a/src/com/jsyn/util/TransportModel.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.util;
-
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public class TransportModel {
- public static final int STATE_STOPPED = 0;
- public static final int STATE_PAUSED = 1;
- public static final int STATE_RUNNING = 2;
-
- private CopyOnWriteArrayList<TransportListener> listeners = new CopyOnWriteArrayList<TransportListener>();
- private int state = STATE_STOPPED;
- private long position;
-
- public void addTransportListener(TransportListener listener) {
- listeners.add(listener);
- }
-
- public void removeTransportListener(TransportListener listener) {
- listeners.remove(listener);
- }
-
- public void setState(int newState) {
- state = newState;
- fireStateChanged(newState);
- }
-
- public int getState() {
- return state;
- }
-
- public void setPosition(long newPosition) {
- position = newPosition;
- firePositionChanged(newPosition);
- }
-
- public long getPosition() {
- return position;
- }
-
- public void fireStateChanged(int newState) {
- for (TransportListener listener : listeners) {
- listener.stateChanged(this, newState);
- }
- }
-
- public void firePositionChanged(long newPosition) {
- for (TransportListener listener : listeners) {
- listener.positionChanged(this, newPosition);
- }
- }
-}
diff --git a/src/com/jsyn/util/VoiceAllocator.java b/src/com/jsyn/util/VoiceAllocator.java
deleted file mode 100644
index f20f7a5..0000000
--- a/src/com/jsyn/util/VoiceAllocator.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import com.jsyn.Synthesizer;
-import com.jsyn.unitgen.UnitVoice;
-import com.softsynth.shared.time.ScheduledCommand;
-import com.softsynth.shared.time.TimeStamp;
-
-/**
- * Allocate voices based on an integer tag. The tag could, for example, be a MIDI note number. Or a
- * tag could be an int that always increments. Use the same tag to refer to a voice for noteOn() and
- * noteOff(). If no new voices are available then a voice in use will be stolen.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public class VoiceAllocator implements Instrument {
- private int maxVoices;
- private VoiceTracker[] trackers;
- private long tick;
- private Synthesizer synthesizer;
- private static final int UNASSIGNED_PRESET = -1;
- private int mPresetIndex = UNASSIGNED_PRESET;
-
- /**
- * Create an allocator for the array of UnitVoices. The array must be full of instantiated
- * UnitVoices that are connected to some kind of mixer.
- *
- * @param voices
- */
- public VoiceAllocator(UnitVoice[] voices) {
- maxVoices = voices.length;
- trackers = new VoiceTracker[maxVoices];
- for (int i = 0; i < maxVoices; i++) {
- trackers[i] = new VoiceTracker();
- trackers[i].voice = voices[i];
- }
- }
-
- public Synthesizer getSynthesizer() {
- if (synthesizer == null) {
- synthesizer = trackers[0].voice.getUnitGenerator().getSynthesizer();
- }
- return synthesizer;
- }
-
- private class VoiceTracker {
- UnitVoice voice;
- int tag = -1;
- int presetIndex = UNASSIGNED_PRESET;
- long when;
- boolean on;
-
- public void off() {
- on = false;
- when = tick++;
- }
- }
-
- /**
- * @return number of UnitVoices passed to the allocator.
- */
- public int getVoiceCount() {
- return maxVoices;
- }
-
- private VoiceTracker findVoice(int tag) {
- for (VoiceTracker tracker : trackers) {
- if (tracker.tag == tag) {
- return tracker;
- }
- }
- return null;
- }
-
- private VoiceTracker stealVoice() {
- VoiceTracker bestOff = null;
- VoiceTracker bestOn = null;
- for (VoiceTracker tracker : trackers) {
- if (tracker.voice == null) {
- return tracker;
- }
- // If we have a bestOff voice then don't even bother with on voices.
- else if (bestOff != null) {
- // Older off voice?
- if (!tracker.on && (tracker.when < bestOff.when)) {
- bestOff = tracker;
- }
- } else if (tracker.on) {
- if (bestOn == null) {
- bestOn = tracker;
- } else if (tracker.when < bestOn.when) {
- bestOn = tracker;
- }
- } else {
- bestOff = tracker;
- }
- }
- if (bestOff != null) {
- return bestOff;
- } else {
- return bestOn;
- }
- }
-
- /**
- * Allocate a Voice associated with this tag. It will first pick a voice already assigned to
- * that tag. Next it will pick the oldest voice that is off. Next it will pick the oldest voice
- * that is on. If you are using timestamps to play the voice in the future then you should use
- * the noteOn() noteOff() and setPort() methods.
- *
- * @param tag
- * @return Voice that is most available.
- */
- protected synchronized UnitVoice allocate(int tag) {
- VoiceTracker tracker = allocateTracker(tag);
- return tracker.voice;
- }
-
- private VoiceTracker allocateTracker(int tag) {
- VoiceTracker tracker = findVoice(tag);
- if (tracker == null) {
- tracker = stealVoice();
- }
- tracker.tag = tag;
- tracker.when = tick++;
- tracker.on = true;
- return tracker;
- }
-
- protected synchronized boolean isOn(int tag) {
- VoiceTracker tracker = findVoice(tag);
- if (tracker != null) {
- return tracker.on;
- }
- return false;
- }
-
- protected synchronized UnitVoice off(int tag) {
- VoiceTracker tracker = findVoice(tag);
- if (tracker != null) {
- tracker.off();
- return tracker.voice;
- }
- return null;
- }
-
- /** Turn off all the note currently on. */
- @Override
- public void allNotesOff(TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- for (VoiceTracker tracker : trackers) {
- if (tracker.on) {
- tracker.voice.noteOff(getSynthesizer().createTimeStamp());
- tracker.off();
- }
- }
- }
- });
- }
-
- /**
- * Play a note on the voice and associate it with the given tag. if needed a new voice will be
- * allocated and an old voice may be turned off.
- */
- @Override
- public void noteOn(final int tag, final double frequency, final double amplitude,
- TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- VoiceTracker voiceTracker = allocateTracker(tag);
- if (voiceTracker.presetIndex != mPresetIndex) {
- voiceTracker.voice.usePreset(mPresetIndex);
- voiceTracker.presetIndex = mPresetIndex;
- }
- voiceTracker.voice.noteOn(frequency, amplitude, getSynthesizer().createTimeStamp());
- }
- });
- }
-
- /**
- * Play a note on the voice and associate it with the given tag. if needed a new voice will be
- * allocated and an old voice may be turned off.
- * Apply an operation to the voice.
- */
- public void noteOn(final int tag,
- final double frequency,
- final double amplitude,
- final VoiceOperation operation,
- TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- VoiceTracker voiceTracker = allocateTracker(tag);
- operation.operate(voiceTracker.voice);
- voiceTracker.voice.noteOn(frequency, amplitude, getSynthesizer().createTimeStamp());
- }
- });
- }
-
- /** Turn off the voice associated with the given tag if allocated. */
- @Override
- public void noteOff(final int tag, TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- VoiceTracker voiceTracker = findVoice(tag);
- if (voiceTracker != null) {
- voiceTracker.voice.noteOff(getSynthesizer().createTimeStamp());
- off(tag);
- }
- }
- });
- }
-
- /** Set a port on the voice associated with the given tag if allocated. */
- @Override
- public void setPort(final int tag, final String portName, final double value,
- TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- VoiceTracker voiceTracker = findVoice(tag);
- if (voiceTracker != null) {
- voiceTracker.voice.setPort(portName, value, getSynthesizer().createTimeStamp());
- }
- }
- });
- }
-
- @Override
- public void usePreset(final int presetIndex, TimeStamp timeStamp) {
- getSynthesizer().scheduleCommand(timeStamp, new ScheduledCommand() {
- @Override
- public void run() {
- mPresetIndex = presetIndex;
- }
- });
- }
-
-}
diff --git a/src/com/jsyn/util/VoiceDescription.java b/src/com/jsyn/util/VoiceDescription.java
deleted file mode 100644
index b7be044..0000000
--- a/src/com/jsyn/util/VoiceDescription.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import com.jsyn.unitgen.UnitVoice;
-
-/**
- * Describe a voice so that a user can pick it out of an InstrumentLibrary.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- * @see PolyphonicInstrument
- */
-public abstract class VoiceDescription {
- private String name;
- private String[] presetNames;
-
- public VoiceDescription(String name, String[] presetNames) {
- this.name = name;
- this.presetNames = presetNames;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getPresetCount() {
- return presetNames.length;
- }
-
- public String[] getPresetNames() {
- return presetNames;
- }
-
- public abstract String[] getTags(int presetIndex);
-
- /**
- * Instantiate one of these voices. You may want to call usePreset(n) on the voice after
- * instantiating it.
- *
- * @return a voice
- */
- public abstract UnitVoice createUnitVoice();
-
- public abstract String getVoiceClassName();
-
- @Override
- public String toString() {
- return name + "[" + getPresetCount() + "]";
- }
-}
diff --git a/src/com/jsyn/util/VoiceOperation.java b/src/com/jsyn/util/VoiceOperation.java
deleted file mode 100644
index cd3b48e..0000000
--- a/src/com/jsyn/util/VoiceOperation.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.jsyn.util;
-
-import com.jsyn.unitgen.UnitVoice;
-
-public interface VoiceOperation {
- public void operate(UnitVoice voice);
-}
diff --git a/src/com/jsyn/util/WaveFileWriter.java b/src/com/jsyn/util/WaveFileWriter.java
deleted file mode 100644
index 32e9995..0000000
--- a/src/com/jsyn/util/WaveFileWriter.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-
-import com.jsyn.io.AudioOutputStream;
-
-/**
- * Write audio data to a WAV file.
- *
- * <pre>
- * <code>
- * WaveFileWriter writer = new WaveFileWriter(file);
- * writer.setFrameRate(22050);
- * writer.setBitsPerSample(24);
- * writer.write(floatArray);
- * writer.close();
- * </code>
- * </pre>
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public class WaveFileWriter implements AudioOutputStream {
- private static final short WAVE_FORMAT_PCM = 1;
- private OutputStream outputStream;
- private long riffSizePosition = 0;
- private long dataSizePosition = 0;
- private int frameRate = 44100;
- private int samplesPerFrame = 1;
- private int bitsPerSample = 16;
- private int bytesWritten;
- private File outputFile;
- private boolean headerWritten = false;
- private final static int PCM24_MIN = -(1 << 23);
- private final static int PCM24_MAX = (1 << 23) - 1;
-
- /**
- * Create a writer that will write to the specified file.
- *
- * @param outputFile
- * @throws FileNotFoundException
- */
- public WaveFileWriter(File outputFile) throws FileNotFoundException {
- this.outputFile = outputFile;
- FileOutputStream fileOut = new FileOutputStream(outputFile);
- outputStream = new BufferedOutputStream(fileOut);
- }
-
- /**
- * @param frameRate default is 44100
- */
- public void setFrameRate(int frameRate) {
- this.frameRate = frameRate;
- }
-
- public int getFrameRate() {
- return frameRate;
- }
-
- /** For stereo, set this to 2. Default is 1. */
- public void setSamplesPerFrame(int samplesPerFrame) {
- this.samplesPerFrame = samplesPerFrame;
- }
-
- public int getSamplesPerFrame() {
- return samplesPerFrame;
- }
-
- /** Only 16 or 24 bit samples supported at the moment. Default is 16. */
- public void setBitsPerSample(int bits) {
- if ((bits != 16) && (bits != 24)) {
- throw new IllegalArgumentException("Only 16 or 24 bits per sample allowed. Not " + bits);
- }
- bitsPerSample = bits;
- }
-
- public int getBitsPerSample() {
- return bitsPerSample;
- }
-
- @Override
- public void close() throws IOException {
- outputStream.close();
- fixSizes();
- }
-
- /** Write entire buffer of audio samples to the WAV file. */
- @Override
- public void write(double[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- /** Write audio to the WAV file. */
- public void write(float[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- /** Write single audio data value to the WAV file. */
- @Override
- public void write(double value) throws IOException {
- if (!headerWritten) {
- writeHeader();
- }
-
- if (bitsPerSample == 24) {
- writePCM24(value);
- } else {
- writePCM16(value);
- }
- }
-
- private void writePCM24(double value) throws IOException {
- // Offset before casting so that we can avoid using floor().
- // Also round by adding 0.5 so that very small signals go to zero.
- double temp = (PCM24_MAX * value) + 0.5 - PCM24_MIN;
- int sample = ((int) temp) + PCM24_MIN;
- // clip to 24-bit range
- if (sample > PCM24_MAX) {
- sample = PCM24_MAX;
- } else if (sample < PCM24_MIN) {
- sample = PCM24_MIN;
- }
- // encode as little-endian
- writeByte(sample); // little end
- writeByte(sample >> 8); // middle
- writeByte(sample >> 16); // big end
- }
-
- private void writePCM16(double value) throws IOException {
- // Offset before casting so that we can avoid using floor().
- // Also round by adding 0.5 so that very small signals go to zero.
- double temp = (Short.MAX_VALUE * value) + 0.5 - Short.MIN_VALUE;
- int sample = ((int) temp) + Short.MIN_VALUE;
- if (sample > Short.MAX_VALUE) {
- sample = Short.MAX_VALUE;
- } else if (sample < Short.MIN_VALUE) {
- sample = Short.MIN_VALUE;
- }
- writeByte(sample); // little end
- writeByte(sample >> 8); // big end
- }
-
- /** Write audio to the WAV file. */
- @Override
- public void write(double[] buffer, int start, int count) throws IOException {
- for (int i = 0; i < count; i++) {
- write(buffer[start + i]);
- }
- }
-
- /** Write audio to the WAV file. */
- public void write(float[] buffer, int start, int count) throws IOException {
- for (int i = 0; i < count; i++) {
- write(buffer[start + i]);
- }
- }
-
- // Write lower 8 bits. Upper bits ignored.
- private void writeByte(int b) throws IOException {
- outputStream.write(b);
- bytesWritten += 1;
- }
-
- /**
- * Write a 32 bit integer to the stream in Little Endian format.
- */
- public void writeIntLittle(int n) throws IOException {
- writeByte(n);
- writeByte(n >> 8);
- writeByte(n >> 16);
- writeByte(n >> 24);
- }
-
- /**
- * Write a 16 bit integer to the stream in Little Endian format.
- */
- public void writeShortLittle(short n) throws IOException {
- writeByte(n);
- writeByte(n >> 8);
- }
-
- /**
- * Write a simple WAV header for PCM data.
- */
- private void writeHeader() throws IOException {
- writeRiffHeader();
- writeFormatChunk();
- writeDataChunkHeader();
- outputStream.flush();
- headerWritten = true;
- }
-
- /**
- * Write a 'RIFF' file header and a 'WAVE' ID to the WAV file.
- */
- private void writeRiffHeader() throws IOException {
- writeByte('R');
- writeByte('I');
- writeByte('F');
- writeByte('F');
- riffSizePosition = bytesWritten;
- writeIntLittle(Integer.MAX_VALUE);
- writeByte('W');
- writeByte('A');
- writeByte('V');
- writeByte('E');
- }
-
- /**
- * Write an 'fmt ' chunk to the WAV file containing the given information.
- */
- public void writeFormatChunk() throws IOException {
- int bytesPerSample = (bitsPerSample + 7) / 8;
-
- writeByte('f');
- writeByte('m');
- writeByte('t');
- writeByte(' ');
- writeIntLittle(16); // chunk size
- writeShortLittle(WAVE_FORMAT_PCM);
- writeShortLittle((short) samplesPerFrame);
- writeIntLittle(frameRate);
- // bytes/second
- writeIntLittle(frameRate * samplesPerFrame * bytesPerSample);
- // block align
- writeShortLittle((short) (samplesPerFrame * bytesPerSample));
- writeShortLittle((short) bitsPerSample);
- }
-
- /**
- * Write a 'data' chunk header to the WAV file. This should be followed by call to
- * writeShortLittle() to write the data to the chunk.
- */
- public void writeDataChunkHeader() throws IOException {
- writeByte('d');
- writeByte('a');
- writeByte('t');
- writeByte('a');
- dataSizePosition = bytesWritten;
- writeIntLittle(Integer.MAX_VALUE); // size
- }
-
- /**
- * Fix RIFF and data chunk sizes based on final size. Assume data chunk is the last chunk.
- */
- private void fixSizes() throws IOException {
- RandomAccessFile randomFile = new RandomAccessFile(outputFile, "rw");
- try {
- // adjust RIFF size
- long end = bytesWritten;
- int riffSize = (int) (end - riffSizePosition) - 4;
- randomFile.seek(riffSizePosition);
- writeRandomIntLittle(randomFile, riffSize);
- // adjust data size
- int dataSize = (int) (end - dataSizePosition) - 4;
- randomFile.seek(dataSizePosition);
- writeRandomIntLittle(randomFile, dataSize);
- } finally {
- randomFile.close();
- }
- }
-
- private void writeRandomIntLittle(RandomAccessFile randomFile, int n) throws IOException {
- byte[] buffer = new byte[4];
- buffer[0] = (byte) n;
- buffer[1] = (byte) (n >> 8);
- buffer[2] = (byte) (n >> 16);
- buffer[3] = (byte) (n >> 24);
- randomFile.write(buffer);
- }
-
-}
diff --git a/src/com/jsyn/util/WaveRecorder.java b/src/com/jsyn/util/WaveRecorder.java
deleted file mode 100644
index 059863b..0000000
--- a/src/com/jsyn/util/WaveRecorder.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2011 Phil Burk, Mobileer Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jsyn.util;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import com.jsyn.Synthesizer;
-import com.jsyn.ports.UnitInputPort;
-
-/**
- * Connect a unit generator to the input. Then start() recording. The signal will be written to a
- * WAV format file that can be read by other programs.
- *
- * @author Phil Burk (C) 2011 Mobileer Inc
- */
-public class WaveRecorder {
- private AudioStreamReader reader;
- private WaveFileWriter writer;
- private StreamingThread thread;
- private Synthesizer synth;
- private TransportModel transportModel = new TransportModel();
- private double maxRecordingTime;
-
- /**
- * Create a stereo 16-bit recorder.
- *
- * @param synth
- * @param outputFile
- * @throws FileNotFoundException
- */
- public WaveRecorder(Synthesizer synth, File outputFile) throws FileNotFoundException {
- this(synth, outputFile, 2, 16);
- }
-
- public WaveRecorder(Synthesizer synth, File outputFile, int samplesPerFrame)
- throws FileNotFoundException {
- this(synth, outputFile, samplesPerFrame, 16);
- }
-
- /**
- * @param synth
- * @param outputFile
- * @param samplesPerFrame 1 for mono, 2 for stereo
- * @param bitsPerSample 16 or 24
- * @throws FileNotFoundException
- */
- public WaveRecorder(Synthesizer synth, File outputFile, int samplesPerFrame, int bitsPerSample)
- throws FileNotFoundException {
- this.synth = synth;
- reader = new AudioStreamReader(synth, samplesPerFrame);
-
- writer = new WaveFileWriter(outputFile);
- writer.setFrameRate(synth.getFrameRate());
- writer.setSamplesPerFrame(samplesPerFrame);
- writer.setBitsPerSample(bitsPerSample);
- }
-
- public UnitInputPort getInput() {
- return reader.getInput();
- }
-
- public void start() {
- stop();
- thread = new StreamingThread(reader, writer);
- thread.setTransportModel(transportModel);
- thread.setSamplesPerFrame(writer.getSamplesPerFrame());
- updateMaxRecordingTime();
- thread.start();
- }
-
- public void stop() {
- if (thread != null) {
- thread.requestStop();
- try {
- thread.join(500);
- } catch (InterruptedException e) {
- }
- thread = null;
- }
- }
-
- /** Close and disconnect any connected inputs. */
- public void close() throws IOException {
- stop();
- if (writer != null) {
- writer.close();
- writer = null;
- }
- if (reader != null) {
- reader.close();
- for (int i = 0; i < reader.getInput().getNumParts(); i++) {
- reader.getInput().disconnectAll(i);
- }
- reader = null;
- }
- }
-
- public void addTransportListener(TransportListener listener) {
- transportModel.addTransportListener(listener);
- }
-
- public void removeTransportListener(TransportListener listener) {
- transportModel.removeTransportListener(listener);
- }
-
- public void setMaxRecordingTime(double maxRecordingTime) {
- this.maxRecordingTime = maxRecordingTime;
- updateMaxRecordingTime();
- }
-
- private void updateMaxRecordingTime() {
- StreamingThread streamingThread = thread;
- if (streamingThread != null) {
- long maxFrames = (long) (maxRecordingTime * synth.getFrameRate());
- streamingThread.setMaxFrames(maxFrames);
- }
- }
-}
diff --git a/src/com/jsyn/util/soundfile/AIFFFileParser.java b/src/com/jsyn/util/soundfile/AIFFFileParser.java
deleted file mode 100644
index 2b09d78..0000000
--- a/src/com/jsyn/util/soundfile/AIFFFileParser.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.util.soundfile;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-import com.jsyn.util.SampleLoader;
-
-public class AIFFFileParser extends AudioFileParser {
- private static final String SUPPORTED_FORMATS = "Only 16 and 24 bit PCM or 32-bit float AIF files supported.";
- static final int AIFF_ID = ('A' << 24) | ('I' << 16) | ('F' << 8) | 'F';
- static final int AIFC_ID = ('A' << 24) | ('I' << 16) | ('F' << 8) | 'C';
- static final int COMM_ID = ('C' << 24) | ('O' << 16) | ('M' << 8) | 'M';
- static final int SSND_ID = ('S' << 24) | ('S' << 16) | ('N' << 8) | 'D';
- static final int MARK_ID = ('M' << 24) | ('A' << 16) | ('R' << 8) | 'K';
- static final int INST_ID = ('I' << 24) | ('N' << 16) | ('S' << 8) | 'T';
- static final int NONE_ID = ('N' << 24) | ('O' << 16) | ('N' << 8) | 'E';
- static final int FL32_ID = ('F' << 24) | ('L' << 16) | ('3' << 8) | '2';
- static final int FL32_ID_LC = ('f' << 24) | ('l' << 16) | ('3' << 8) | '2';
-
- int sustainBeginID = -1;
- int sustainEndID = -1;
- int releaseBeginID = -1;
- int releaseEndID = -1;
- boolean typeFloat = false;
-
- @Override
- FloatSample finish() throws IOException {
- setLoops();
-
- if ((byteData == null)) {
- throw new IOException("No data found in audio sample.");
- }
- float[] floatData = new float[numFrames * samplesPerFrame];
- if (bitsPerSample == 16) {
- SampleLoader.decodeBigI16ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 24) {
- SampleLoader.decodeBigI24ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 32) {
- if (typeFloat) {
- SampleLoader.decodeBigF32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else {
- SampleLoader.decodeBigI32ToF32(byteData, 0, byteData.length, floatData, 0);
- }
- } else {
- throw new IOException(SUPPORTED_FORMATS + " size = " + bitsPerSample);
- }
-
- return makeSample(floatData);
- }
-
- double read80BitFloat() throws IOException {
- /*
- * This is not a full decoding of the 80 bit number but it should suffice for the range we
- * expect.
- */
- byte[] bytes = new byte[10];
- parser.read(bytes);
- int exp = ((bytes[0] & 0x3F) << 8) | (bytes[1] & 0xFF);
- int mant = ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 8) | (bytes[4] & 0xFF);
- // System.out.println( "exp = " + exp + ", mant = " + mant );
- return mant / (double) (1 << (22 - exp));
- }
-
- void parseCOMMChunk(IFFParser parser, int ckSize) throws IOException {
- samplesPerFrame = parser.readShortBig();
- numFrames = parser.readIntBig();
- bitsPerSample = parser.readShortBig();
- frameRate = read80BitFloat();
- if (ckSize > 18) {
- int format = parser.readIntBig();
- // Validate data format.
- if ((format == FL32_ID) || (format == FL32_ID_LC)) {
- typeFloat = true;
- } else if (format == NONE_ID) {
- typeFloat = false;
- } else {
- throw new IOException(SUPPORTED_FORMATS + " format " + IFFParser.IDToString(format));
- }
- }
-
- bytesPerSample = (bitsPerSample + 7) / 8;
- bytesPerFrame = bytesPerSample * samplesPerFrame;
- }
-
- /* parse tuning and multi-sample info */
- @SuppressWarnings("unused")
- void parseINSTChunk(IFFParser parser, int ckSize) throws IOException {
- int baseNote = parser.readByte();
- int detune = parser.readByte();
- originalPitch = baseNote + (0.01 * detune);
-
- int lowNote = parser.readByte();
- int highNote = parser.readByte();
-
- parser.skip(2); /* lo,hi velocity */
- int gain = parser.readShortBig();
-
- int playMode = parser.readShortBig(); /* sustain */
- sustainBeginID = parser.readShortBig();
- sustainEndID = parser.readShortBig();
-
- playMode = parser.readShortBig(); /* release */
- releaseBeginID = parser.readShortBig();
- releaseEndID = parser.readShortBig();
- }
-
- private void setLoops() {
- SampleMarker cuePoint = cueMap.get(sustainBeginID);
- if (cuePoint != null) {
- sustainBegin = cuePoint.position;
- }
- cuePoint = cueMap.get(sustainEndID);
- if (cuePoint != null) {
- sustainEnd = cuePoint.position;
- }
- }
-
- void parseSSNDChunk(IFFParser parser, int ckSize) throws IOException {
- long numRead;
- // System.out.println("parseSSNDChunk()");
- int offset = parser.readIntBig();
- parser.readIntBig(); /* blocksize */
- parser.skip(offset);
- dataPosition = parser.getOffset();
- int numBytes = ckSize - 8 - offset;
- if (ifLoadData) {
- byteData = new byte[numBytes];
- numRead = parser.read(byteData);
- } else {
- numRead = parser.skip(numBytes);
- }
- if (numRead != numBytes)
- throw new EOFException("AIFF data chunk too short!");
- }
-
- void parseMARKChunk(IFFParser parser, int ckSize) throws IOException {
- long startOffset = parser.getOffset();
- int numCuePoints = parser.readShortBig();
- // System.out.println( "parseCueChunk: numCuePoints = " + numCuePoints
- // );
- for (int i = 0; i < numCuePoints; i++) {
- // Some AIF files have a bogus numCuePoints so check to see if we
- // are at end.
- long numInMark = parser.getOffset() - startOffset;
- if (numInMark >= ckSize) {
- System.out.println("Reached end of MARK chunk with bogus numCuePoints = "
- + numCuePoints);
- break;
- }
-
- int uniqueID = parser.readShortBig();
- int position = parser.readIntBig();
- int len = parser.read();
- String markerName = parseString(parser, len);
- if ((len & 1) == 0) {
- parser.skip(1); /* skip pad byte */
- }
-
- SampleMarker cuePoint = findOrCreateCuePoint(uniqueID);
- cuePoint.position = position;
- cuePoint.name = markerName;
-
- if (IFFParser.debug) {
- System.out.println("AIFF Marker at " + position + ", " + markerName);
- }
- }
- }
-
- /**
- * Called by parse() method to handle FORM chunks in an AIFF specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @exception IOException If parsing fails, or IO error occurs.
- */
- @Override
- public void handleForm(IFFParser parser, int ckID, int ckSize, int type) throws IOException {
- if ((ckID == IFFParser.FORM_ID) && (type != AIFF_ID) && (type != AIFC_ID))
- throw new IOException("Bad AIFF form type = " + IFFParser.IDToString(type));
- }
-
- /**
- * Called by parse() method to handle chunks in an AIFF specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @exception IOException If parsing fails, or IO error occurs.
- */
- @Override
- public void handleChunk(IFFParser parser, int ckID, int ckSize) throws IOException {
- switch (ckID) {
- case COMM_ID:
- parseCOMMChunk(parser, ckSize);
- break;
- case SSND_ID:
- parseSSNDChunk(parser, ckSize);
- break;
- case MARK_ID:
- parseMARKChunk(parser, ckSize);
- break;
- case INST_ID:
- parseINSTChunk(parser, ckSize);
- break;
- default:
- break;
- }
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/AudioFileParser.java b/src/com/jsyn/util/soundfile/AudioFileParser.java
deleted file mode 100644
index e7bb066..0000000
--- a/src/com/jsyn/util/soundfile/AudioFileParser.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2001 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.util.soundfile;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-
-/**
- * Base class for various types of audio specific file parsers.
- *
- * @author (C) 2001 Phil Burk, SoftSynth.com
- */
-
-abstract class AudioFileParser implements ChunkHandler {
- IFFParser parser;
- protected byte[] byteData;
- boolean ifLoadData = true; /* If true, load sound data into memory. */
- long dataPosition; /*
- * Number of bytes from beginning of file where sound data resides.
- */
- protected int bitsPerSample;
- protected int bytesPerFrame; // in the file
- protected int bytesPerSample; // in the file
- protected HashMap<Integer, SampleMarker> cueMap = new HashMap<Integer, SampleMarker>();
- protected short samplesPerFrame;
- protected double frameRate;
- protected int numFrames;
- protected double originalPitch = 60.0;
- protected int sustainBegin = -1;
- protected int sustainEnd = -1;
-
- public AudioFileParser() {
- }
-
- /**
- * @return Number of bytes from beginning of stream where sound data resides.
- */
- public long getDataPosition() {
- return dataPosition;
- }
-
- /**
- * This can be read by another thread when load()ing a sample to determine how many bytes have
- * been read so far.
- */
- public synchronized long getNumBytesRead() {
- IFFParser p = parser; // prevent race
- if (p != null)
- return p.getOffset();
- else
- return 0;
- }
-
- /**
- * This can be read by another thread when load()ing a sample to determine how many bytes need
- * to be read.
- */
- public synchronized long getFileSize() {
- IFFParser p = parser; // prevent race
- if (p != null)
- return p.getFileSize();
- else
- return 0;
- }
-
- protected SampleMarker findOrCreateCuePoint(int uniqueID) {
- SampleMarker cuePoint = cueMap.get(uniqueID);
- if (cuePoint == null) {
- cuePoint = new SampleMarker();
- cueMap.put(uniqueID, cuePoint);
- }
- return cuePoint;
- }
-
- public FloatSample load(IFFParser parser) throws IOException {
- this.parser = parser;
- parser.parseAfterHead(this);
- return finish();
- }
-
- abstract FloatSample finish() throws IOException;
-
- FloatSample makeSample(float[] floatData) throws IOException {
- FloatSample floatSample = new FloatSample(floatData, samplesPerFrame);
-
- floatSample.setChannelsPerFrame(samplesPerFrame);
- floatSample.setFrameRate(frameRate);
- floatSample.setPitch(originalPitch);
-
- if (sustainBegin >= 0) {
- floatSample.setSustainBegin(sustainBegin);
- floatSample.setSustainEnd(sustainEnd);
- }
-
- for (SampleMarker marker : cueMap.values()) {
- floatSample.addMarker(marker);
- }
-
- /* Set Sustain Loop by assuming first two markers are loop points. */
- if (floatSample.getMarkerCount() >= 2) {
- floatSample.setSustainBegin(floatSample.getMarker(0).position);
- floatSample.setSustainEnd(floatSample.getMarker(1).position);
- }
- return floatSample;
- }
-
- protected String parseString(IFFParser parser, int textLength) throws IOException {
- byte[] bar = new byte[textLength];
- parser.read(bar);
- return new String(bar);
- }
-}
diff --git a/src/com/jsyn/util/soundfile/ChunkHandler.java b/src/com/jsyn/util/soundfile/ChunkHandler.java
deleted file mode 100644
index 6dfe26d..0000000
--- a/src/com/jsyn/util/soundfile/ChunkHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 1997 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.util.soundfile;
-
-import java.io.IOException;
-
-/**
- * Handle IFF Chunks as they are parsed from an IFF or RIFF file.
- *
- * @see IFFParser
- * @see AudioSampleAIFF
- * @author (C) 1997 Phil Burk, SoftSynth.com
- */
-interface ChunkHandler {
- /**
- * The parser will call this when it encounters a FORM or LIST chunk that contains other chunks.
- * This handler can either read the form's chunks, or let the parser find them and call
- * handleChunk().
- *
- * @param ID a 4 byte identifier such as FORM_ID that identifies the IFF chunk type.
- * @param numBytes number of bytes contained in the FORM, not counting the FORM type.
- * @param type a 4 byte identifier such as AIFF_ID that identifies the FORM type.
- */
- public void handleForm(IFFParser parser, int ID, int numBytes, int type) throws IOException;
-
- /**
- * The parser will call this when it encounters a chunk that is not a FORM or LIST. This handler
- * can either read the chunk's, or ignore it. The parser will skip over any unread data. Do NOT
- * read past the end of the chunk!
- *
- * @param ID a 4 byte identifier such as SSND_ID that identifies the IFF chunk type.
- * @param numBytes number of bytes contained in the chunk, not counting the ID and size field.
- */
- public void handleChunk(IFFParser parser, int ID, int numBytes) throws IOException;
-}
diff --git a/src/com/jsyn/util/soundfile/CustomSampleLoader.java b/src/com/jsyn/util/soundfile/CustomSampleLoader.java
deleted file mode 100644
index 14efde9..0000000
--- a/src/com/jsyn/util/soundfile/CustomSampleLoader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.util.soundfile;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.util.AudioSampleLoader;
-
-public class CustomSampleLoader implements AudioSampleLoader {
-
- @Override
- public FloatSample loadFloatSample(File fileIn) throws IOException {
- FileInputStream fileStream = new FileInputStream(fileIn);
- BufferedInputStream inputStream = new BufferedInputStream(fileStream);
- return loadFloatSample(inputStream);
- }
-
- @Override
- public FloatSample loadFloatSample(URL url) throws IOException {
- InputStream rawStream = url.openStream();
- BufferedInputStream inputStream = new BufferedInputStream(rawStream);
- return loadFloatSample(inputStream);
- }
-
- @Override
- public FloatSample loadFloatSample(InputStream inputStream) throws IOException {
- AudioFileParser fileParser;
- IFFParser parser = new IFFParser(inputStream);
- parser.readHead();
- if (parser.isRIFF()) {
- fileParser = new WAVEFileParser();
- } else if (parser.isIFF()) {
- fileParser = new AIFFFileParser();
- } else {
- throw new IOException("Unsupported audio file type.");
- }
- return fileParser.load(parser);
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/IFFParser.java b/src/com/jsyn/util/soundfile/IFFParser.java
deleted file mode 100644
index f429657..0000000
--- a/src/com/jsyn/util/soundfile/IFFParser.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright 1997 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.util.soundfile;
-
-import java.io.EOFException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parse Electronic Arts style IFF File. IFF is a file format that allows "chunks" of data to be
- * placed in a hierarchical file. It was designed by Jerry Morrison at Electronic Arts for the Amiga
- * computer and is now used extensively by Apple Computer and other companies. IFF is an open
- * standard.
- *
- * @see RIFFParser
- * @see AudioSampleAIFF
- * @author (C) 1997 Phil Burk, SoftSynth.com
- */
-
-class IFFParser extends FilterInputStream {
- private long numBytesRead = 0;
- private long totalSize = 0;
- private int fileId;
- static boolean debug = false;
-
- public static final int RIFF_ID = ('R' << 24) | ('I' << 16) | ('F' << 8) | 'F';
- public static final int LIST_ID = ('L' << 24) | ('I' << 16) | ('S' << 8) | 'T';
- public static final int FORM_ID = ('F' << 24) | ('O' << 16) | ('R' << 8) | 'M';
-
- IFFParser(InputStream stream) {
- super(stream);
- numBytesRead = 0;
- }
-
- /**
- * Size of file based on outermost chunk size plus 8. Can be used to report progress when
- * loading samples.
- *
- * @return Number of bytes in outer chunk plus header.
- */
- public long getFileSize() {
- return totalSize;
- }
-
- /**
- * Since IFF files use chunks with explicit size, it is important to keep track of how many
- * bytes have been read from the file. Can be used to report progress when loading samples.
- *
- * @return Number of bytes read from stream, or skipped.
- */
- public long getOffset() {
- return numBytesRead;
- }
-
- /** @return Next byte from stream. Increment offset by 1. */
- @Override
- public int read() throws IOException {
- numBytesRead++;
- return super.read();
- }
-
- /** @return Next byte array from stream. Increment offset by len. */
- @Override
- public int read(byte[] bar) throws IOException {
- return read(bar, 0, bar.length);
- }
-
- /** @return Next byte array from stream. Increment offset by len. */
- @Override
- public int read(byte[] bar, int off, int len) throws IOException {
- // Reading from a URL can return before all the bytes are available.
- // So we keep reading until we get the whole thing.
- int cursor = off;
- int numLeft = len;
- // keep reading data until we get it all
- while (numLeft > 0) {
- int numRead = super.read(bar, cursor, numLeft);
- if (numRead < 0)
- return numRead;
- cursor += numRead;
- numBytesRead += numRead;
- numLeft -= numRead;
- // System.out.println("read " + numRead + ", cursor = " + cursor +
- // ", len = " + len);
- }
- return cursor - off;
- }
-
- /** @return Skip forward in stream and add numBytes to offset. */
- @Override
- public long skip(long numBytes) throws IOException {
- numBytesRead += numBytes;
- return super.skip(numBytes);
- }
-
- /** Read 32 bit signed integer assuming Big Endian byte order. */
- public int readIntBig() throws IOException {
- int result = read() & 0xFF;
- result = (result << 8) | (read() & 0xFF);
- result = (result << 8) | (read() & 0xFF);
- int data = read();
- if (data == -1)
- throw new EOFException("readIntBig() - EOF in middle of word at offset " + numBytesRead);
- result = (result << 8) | (data & 0xFF);
- return result;
- }
-
- /** Read 32 bit signed integer assuming Little Endian byte order. */
- public int readIntLittle() throws IOException {
- int result = read() & 0xFF; // LSB
- result |= ((read() & 0xFF) << 8);
- result |= ((read() & 0xFF) << 16);
- int data = read();
- if (data == -1)
- throw new EOFException("readIntLittle() - EOF in middle of word at offset "
- + numBytesRead);
- result |= (data << 24);
- return result;
- }
-
- /** Read 16 bit signed short assuming Big Endian byte order. */
- public short readShortBig() throws IOException {
- short result = (short) ((read() << 8)); // MSB
- int data = read();
- if (data == -1)
- throw new EOFException("readShortBig() - EOF in middle of word at offset "
- + numBytesRead);
- result |= data & 0xFF;
- return result;
- }
-
- /** Read 16 bit signed short assuming Little Endian byte order. */
- public short readShortLittle() throws IOException {
- short result = (short) (read() & 0xFF); // LSB
- int data = read(); // MSB
- if (data == -1)
- throw new EOFException("readShortLittle() - EOF in middle of word at offset "
- + numBytesRead);
- result |= data << 8;
- return result;
- }
-
- public int readUShortLittle() throws IOException {
- return (readShortLittle()) & 0x0000FFFF;
- }
-
- /** Read 8 bit signed byte. */
- public byte readByte() throws IOException {
- return (byte) read();
- }
-
- /** Read 32 bit signed int assuming IFF order. */
- public int readChunkSize() throws IOException {
- if (isRIFF()) {
- return readIntLittle();
- }
- {
- return readIntBig();
- }
- }
-
- /** Convert a 4 character IFF ID to a String */
- public static String IDToString(int ID) {
- byte bar[] = new byte[4];
- bar[0] = (byte) (ID >> 24);
- bar[1] = (byte) (ID >> 16);
- bar[2] = (byte) (ID >> 8);
- bar[3] = (byte) ID;
- return new String(bar);
- }
-
- /**
- * Parse the stream after reading the first ID and pass the forms and chunks to the ChunkHandler
- */
- public void parseAfterHead(ChunkHandler handler) throws IOException {
- int numBytes = readChunkSize();
- totalSize = numBytes + 8;
- parseChunk(handler, fileId, numBytes);
- if (debug)
- System.out.println("parse() ------- end");
- }
-
- /**
- * Parse the FORM and pass the chunks to the ChunkHandler The cursor should be positioned right
- * after the type field.
- */
- void parseForm(ChunkHandler handler, int ID, int numBytes, int type) throws IOException {
- if (debug) {
- System.out.println("IFF: parseForm >>>>>>>>>>>>>>>>>> BEGIN");
- }
- while (numBytes > 8) {
- int ckid = readIntBig();
- int size = readChunkSize();
- numBytes -= 8;
- if (debug) {
- System.out.println("chunk( " + IDToString(ckid) + ", " + size + " )");
- }
- if (size < 0) {
- throw new IOException("Bad IFF chunk Size: " + IDToString(ckid) + " = 0x"
- + Integer.toHexString(ckid) + ", Size = " + size);
- }
- parseChunk(handler, ckid, size);
- if ((size & 1) == 1)
- size++; // even-up
- numBytes -= size;
- if (debug) {
- System.out.println("parseForm: numBytes left in form = " + numBytes);
- }
- }
- if (debug) {
- System.out.println("IFF: parseForm <<<<<<<<<<<<<<<<<<<< END");
- }
-
- if (numBytes > 0) {
- System.out.println("IFF Parser detected " + numBytes
- + " bytes of garbage at end of FORM.");
- skip(numBytes);
- }
- }
-
- /*
- * Parse one chunk from IFF file. After calling handler, make sure stream is positioned at end
- * of chunk.
- */
- void parseChunk(ChunkHandler handler, int ckid, int numBytes) throws IOException {
- long startOffset, endOffset;
- int numRead;
- startOffset = getOffset();
- if (isForm(ckid)) {
- int type = readIntBig();
- if (debug)
- System.out.println("parseChunk: form = " + IDToString(ckid) + ", " + numBytes
- + ", " + IDToString(type));
- handler.handleForm(this, ckid, numBytes - 4, type);
- endOffset = getOffset();
- numRead = (int) (endOffset - startOffset);
- if (numRead < numBytes)
- parseForm(handler, ckid, (numBytes - numRead), type);
- } else {
- handler.handleChunk(this, ckid, numBytes);
- }
- endOffset = getOffset();
- numRead = (int) (endOffset - startOffset);
- if (debug) {
- System.out.println("parseChunk: endOffset = " + endOffset);
- System.out.println("parseChunk: numRead = " + numRead);
- }
- if ((numBytes & 1) == 1)
- numBytes++; // even-up
- if (numRead < numBytes)
- skip(numBytes - numRead);
- }
-
- public void readHead() throws IOException {
- if (debug)
- System.out.println("parse() ------- begin");
- numBytesRead = 0;
- fileId = readIntBig();
- }
-
- public boolean isRIFF() {
- return (fileId == RIFF_ID);
- }
-
- public boolean isIFF() {
- return (fileId == FORM_ID);
- }
-
- /**
- * Does the following chunk ID correspond to a container type like FORM?
- */
- public boolean isForm(int ckid) {
- if (isRIFF()) {
- switch (ckid) {
- case LIST_ID:
- case RIFF_ID:
- return true;
- default:
- return false;
- }
- } else {
- switch (ckid) {
- case LIST_ID:
- case FORM_ID:
- return true;
- default:
- return false;
- }
- }
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/WAVEFileParser.java b/src/com/jsyn/util/soundfile/WAVEFileParser.java
deleted file mode 100644
index ec9350c..0000000
--- a/src/com/jsyn/util/soundfile/WAVEFileParser.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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.util.soundfile;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-import com.jsyn.util.SampleLoader;
-
-class WAVEFileParser extends AudioFileParser implements ChunkHandler {
- static final short WAVE_FORMAT_PCM = 1;
- static final short WAVE_FORMAT_IEEE_FLOAT = 3;
- static final short WAVE_FORMAT_EXTENSIBLE = (short) 0xFFFE;
-
- static final byte[] KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
- 3, 0, 0, 0, 0, 0, 16, 0, -128, 0, 0, -86, 0, 56, -101, 113
- };
- static final byte[] KSDATAFORMAT_SUBTYPE_PCM = {
- 1, 0, 0, 0, 0, 0, 16, 0, -128, 0, 0, -86, 0, 56, -101, 113
- };
-
- static final int WAVE_ID = ('W' << 24) | ('A' << 16) | ('V' << 8) | 'E';
- static final int FMT_ID = ('f' << 24) | ('m' << 16) | ('t' << 8) | ' ';
- static final int DATA_ID = ('d' << 24) | ('a' << 16) | ('t' << 8) | 'a';
- static final int CUE_ID = ('c' << 24) | ('u' << 16) | ('e' << 8) | ' ';
- static final int FACT_ID = ('f' << 24) | ('a' << 16) | ('c' << 8) | 't';
- static final int SMPL_ID = ('s' << 24) | ('m' << 16) | ('p' << 8) | 'l';
- static final int LTXT_ID = ('l' << 24) | ('t' << 16) | ('x' << 8) | 't';
- static final int LABL_ID = ('l' << 24) | ('a' << 16) | ('b' << 8) | 'l';
-
- int samplesPerBlock = 0;
- int blockAlign = 0;
- private int numFactSamples = 0;
- private short format;
-
- WAVEFileParser() {
- }
-
- @Override
- FloatSample finish() throws IOException {
- if ((byteData == null)) {
- throw new IOException("No data found in audio sample.");
- }
- float[] floatData = new float[numFrames * samplesPerFrame];
- if (bitsPerSample == 16) {
- SampleLoader.decodeLittleI16ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 24) {
- SampleLoader.decodeLittleI24ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 32) {
- if (format == WAVE_FORMAT_IEEE_FLOAT) {
- SampleLoader.decodeLittleF32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (format == WAVE_FORMAT_PCM) {
- SampleLoader.decodeLittleI32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else {
- throw new IOException("WAV: Unsupported format = " + format);
- }
- } else {
- throw new IOException("WAV: Unsupported bitsPerSample = " + bitsPerSample);
- }
-
- return makeSample(floatData);
- }
-
- // typedef struct {
- // long dwIdentifier;
- // long dwPosition;
- // ID fccChunk;
- // long dwChunkStart;
- // long dwBlockStart;
- // long dwSampleOffset;
- // } CuePoint;
-
- /* Parse various chunks encountered in WAV file. */
- void parseCueChunk(IFFParser parser, int ckSize) throws IOException {
- int numCuePoints = parser.readIntLittle();
- if (IFFParser.debug) {
- System.out.println("WAV: numCuePoints = " + numCuePoints);
- }
- if ((ckSize - 4) != (6 * 4 * numCuePoints))
- throw new EOFException("Cue chunk too short!");
- for (int i = 0; i < numCuePoints; i++) {
- int dwName = parser.readIntLittle(); /* dwName */
- int position = parser.readIntLittle(); // dwPosition
- parser.skip(3 * 4); // fccChunk, dwChunkStart, dwBlockStart
- int sampleOffset = parser.readIntLittle(); // dwPosition
-
- if (IFFParser.debug) {
- System.out.println("WAV: parseCueChunk: #" + i + ", dwPosition = " + position
- + ", dwName = " + dwName + ", dwSampleOffset = " + sampleOffset);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.position = position;
- }
- }
-
- void parseLablChunk(IFFParser parser, int ckSize) throws IOException {
- int dwName = parser.readIntLittle();
- int textLength = (ckSize - 4) - 1; // don't read NUL terminator
- String text = parseString(parser, textLength);
- if (IFFParser.debug) {
- System.out.println("WAV: label id = " + dwName + ", text = " + text);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.name = text;
- }
-
- void parseLtxtChunk(IFFParser parser, int ckSize) throws IOException {
- int dwName = parser.readIntLittle();
- int dwSampleLength = parser.readIntLittle();
- parser.skip(4 + (4 * 2)); // purpose through codepage
- int textLength = (ckSize - ((4 * 4) + (4 * 2))) - 1; // don't read NUL
- // terminator
- if (textLength > 0) {
- String text = parseString(parser, textLength);
- if (IFFParser.debug) {
- System.out.println("WAV: ltxt id = " + dwName + ", dwSampleLength = "
- + dwSampleLength + ", text = " + text);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.comment = text;
- }
- }
-
- void parseFmtChunk(IFFParser parser, int ckSize) throws IOException {
- format = parser.readShortLittle();
- samplesPerFrame = parser.readShortLittle();
- frameRate = parser.readIntLittle();
- parser.readIntLittle(); /* skip dwAvgBytesPerSec */
- blockAlign = parser.readShortLittle();
- bitsPerSample = parser.readShortLittle();
-
- if (IFFParser.debug) {
- System.out.println("WAV: format = 0x" + Integer.toHexString(format));
- System.out.println("WAV: bitsPerSample = " + bitsPerSample);
- System.out.println("WAV: samplesPerFrame = " + samplesPerFrame);
- }
- bytesPerFrame = blockAlign;
- bytesPerSample = bytesPerFrame / samplesPerFrame;
- samplesPerBlock = (8 * blockAlign) / bitsPerSample;
-
- if (format == WAVE_FORMAT_EXTENSIBLE) {
- int extraSize = parser.readShortLittle();
- short validBitsPerSample = parser.readShortLittle();
- int channelMask = parser.readIntLittle();
- byte[] guid = new byte[16];
- parser.read(guid);
- if (IFFParser.debug) {
- System.out.println("WAV: extraSize = " + extraSize);
- System.out.println("WAV: validBitsPerSample = " + validBitsPerSample);
- System.out.println("WAV: channelMask = " + channelMask);
- System.out.print("guid = {");
- for (int i = 0; i < guid.length; i++) {
- System.out.print(guid[i] + ", ");
- }
- System.out.println("}");
- }
- if (matchBytes(guid, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
- format = WAVE_FORMAT_IEEE_FLOAT;
- } else if (matchBytes(guid, KSDATAFORMAT_SUBTYPE_PCM)) {
- format = WAVE_FORMAT_PCM;
- }
- }
- if ((format != WAVE_FORMAT_PCM) && (format != WAVE_FORMAT_IEEE_FLOAT)) {
- throw new IOException(
- "Only WAVE_FORMAT_PCM and WAVE_FORMAT_IEEE_FLOAT supported. format = " + format);
- }
- if ((bitsPerSample != 16) && (bitsPerSample != 24) && (bitsPerSample != 32)) {
- throw new IOException(
- "Only 16 and 24 bit PCM or 32-bit float WAV files supported. width = "
- + bitsPerSample);
- }
- }
-
- private boolean matchBytes(byte[] bar1, byte[] bar2) {
- if (bar1.length != bar2.length)
- return false;
- for (int i = 0; i < bar1.length; i++) {
- if (bar1[i] != bar2[i])
- return false;
- }
- return true;
- }
-
- private int convertByteToFrame(int byteOffset) throws IOException {
- if (blockAlign == 0) {
- throw new IOException("WAV file has bytesPerBlock = zero");
- }
- if (samplesPerFrame == 0) {
- throw new IOException("WAV file has samplesPerFrame = zero");
- }
- int nFrames = (samplesPerBlock * byteOffset) / (samplesPerFrame * blockAlign);
- return nFrames;
- }
-
- private int calculateNumFrames(int numBytes) throws IOException {
- int nFrames;
- if (numFactSamples > 0) {
- // nFrames = numFactSamples / samplesPerFrame;
- nFrames = numFactSamples; // FIXME which is right
- } else {
- nFrames = convertByteToFrame(numBytes);
- }
- return nFrames;
- }
-
- // Read fraction in range of 0 to 0xFFFFFFFF and
- // convert to 0.0 to 1.0 range.
- private double readFraction(IFFParser parser) throws IOException {
- // Put L at end or we get -1.
- long maxFraction = 0x0FFFFFFFFL;
- // Get unsigned fraction. Have to fit in long.
- long fraction = (parser.readIntLittle()) & maxFraction;
- return (double) fraction / (double) maxFraction;
- }
-
- void parseSmplChunk(IFFParser parser, int ckSize) throws IOException {
- parser.readIntLittle(); // Manufacturer
- parser.readIntLittle(); // Product
- parser.readIntLittle(); // Sample Period
- int unityNote = parser.readIntLittle();
- double pitchFraction = readFraction(parser);
- originalPitch = unityNote + pitchFraction;
-
- parser.readIntLittle(); // SMPTE Format
- parser.readIntLittle(); // SMPTE Offset
- int numLoops = parser.readIntLittle();
- parser.readIntLittle(); // Sampler Data
-
- int lastCueID = Integer.MAX_VALUE;
- for (int i = 0; i < numLoops; i++) {
- int cueID = parser.readIntLittle();
- parser.readIntLittle(); // type
- int loopStartPosition = parser.readIntLittle();
- // Point to sample one after.
- int loopEndPosition = parser.readIntLittle() + 1;
- // TODO handle fractional loop sizes?
- double endFraction = readFraction(parser);
- parser.readIntLittle(); // playCount
-
- // Use lowest numbered cue.
- if (cueID < lastCueID) {
- sustainBegin = loopStartPosition;
- sustainEnd = loopEndPosition;
- }
- }
- }
-
- void parseFactChunk(IFFParser parser, int ckSize) throws IOException {
- numFactSamples = parser.readIntLittle();
- }
-
- void parseDataChunk(IFFParser parser, int ckSize) throws IOException {
- long numRead;
- dataPosition = parser.getOffset();
- if (ifLoadData) {
- byteData = new byte[ckSize];
- numRead = parser.read(byteData);
- } else {
- numRead = parser.skip(ckSize);
- }
- if (numRead != ckSize) {
- throw new EOFException("WAV data chunk too short! Read " + numRead + " instead of "
- + ckSize);
- }
- numFrames = calculateNumFrames(ckSize);
- }
-
- @Override
- public void handleForm(IFFParser parser, int ckID, int ckSize, int type) throws IOException {
- if ((ckID == IFFParser.RIFF_ID) && (type != WAVE_ID))
- throw new IOException("Bad WAV form type = " + IFFParser.IDToString(type));
- }
-
- /**
- * Called by parse() method to handle chunks in a WAV specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @return number of bytes left in chunk
- */
- @Override
- public void handleChunk(IFFParser parser, int ckID, int ckSize) throws IOException {
- switch (ckID) {
- case FMT_ID:
- parseFmtChunk(parser, ckSize);
- break;
- case DATA_ID:
- parseDataChunk(parser, ckSize);
- break;
- case CUE_ID:
- parseCueChunk(parser, ckSize);
- break;
- case FACT_ID:
- parseFactChunk(parser, ckSize);
- break;
- case SMPL_ID:
- parseSmplChunk(parser, ckSize);
- break;
- case LABL_ID:
- parseLablChunk(parser, ckSize);
- break;
- case LTXT_ID:
- parseLtxtChunk(parser, ckSize);
- break;
- default:
- break;
- }
- }
-
- /*
- * (non-Javadoc)
- * @see com.softsynth.javasonics.util.AudioSampleLoader#isLittleEndian()
- */
- boolean isLittleEndian() {
- return true;
- }
-
-}