aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/jsyn/data
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/jsyn/data')
-rw-r--r--src/main/java/com/jsyn/data/AudioSample.java108
-rw-r--r--src/main/java/com/jsyn/data/DoubleTable.java109
-rw-r--r--src/main/java/com/jsyn/data/FloatSample.java164
-rw-r--r--src/main/java/com/jsyn/data/Function.java35
-rw-r--r--src/main/java/com/jsyn/data/HammingWindow.java41
-rw-r--r--src/main/java/com/jsyn/data/HannWindow.java36
-rw-r--r--src/main/java/com/jsyn/data/SampleMarker.java30
-rw-r--r--src/main/java/com/jsyn/data/SegmentedEnvelope.java125
-rw-r--r--src/main/java/com/jsyn/data/SequentialData.java96
-rw-r--r--src/main/java/com/jsyn/data/SequentialDataCommon.java136
-rw-r--r--src/main/java/com/jsyn/data/ShortSample.java123
-rw-r--r--src/main/java/com/jsyn/data/SpectralWindow.java21
-rw-r--r--src/main/java/com/jsyn/data/SpectralWindowFactory.java55
-rw-r--r--src/main/java/com/jsyn/data/Spectrum.java97
14 files changed, 1176 insertions, 0 deletions
diff --git a/src/main/java/com/jsyn/data/AudioSample.java b/src/main/java/com/jsyn/data/AudioSample.java
new file mode 100644
index 0000000..dcbbae5
--- /dev/null
+++ b/src/main/java/com/jsyn/data/AudioSample.java
@@ -0,0 +1,108 @@
+/*
+ * 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.data;
+
+import java.util.ArrayList;
+
+/**
+ * Base class for FloatSample and ShortSample.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public abstract class AudioSample extends SequentialDataCommon {
+ protected int numFrames;
+ protected int channelsPerFrame = 1;
+ private double frameRate = 44100.0;
+ private double pitch;
+ private ArrayList<SampleMarker> markers;
+
+ public abstract void allocate(int numFrames, int channelsPerFrame);
+
+ @Override
+ public double getRateScaler(int index, double synthesisRate) {
+ return 1.0;
+ }
+
+ public double getFrameRate() {
+ return frameRate;
+ }
+
+ public void setFrameRate(double f) {
+ this.frameRate = f;
+ }
+
+ @Override
+ public int getNumFrames() {
+ return numFrames;
+ }
+
+ @Override
+ public int getChannelsPerFrame() {
+ return channelsPerFrame;
+ }
+
+ public void setChannelsPerFrame(int channelsPerFrame) {
+ this.channelsPerFrame = channelsPerFrame;
+ }
+
+ /**
+ * Set the recorded pitch as a fractional MIDI semitone value where 60 is Middle C.
+ *
+ * @param pitch
+ */
+ public void setPitch(double pitch) {
+ this.pitch = pitch;
+ }
+
+ public double getPitch() {
+ return pitch;
+ }
+
+ public int getMarkerCount() {
+ if (markers == null)
+ return 0;
+ else
+ return markers.size();
+ }
+
+ public SampleMarker getMarker(int index) {
+ if (markers == null)
+ return null;
+ else
+ return markers.get(index);
+ }
+
+ /**
+ * Add a marker that will be stored sorted by position. This is normally used internally by the
+ * SampleLoader.
+ *
+ * @param marker
+ */
+ public void addMarker(SampleMarker marker) {
+ if (markers == null)
+ markers = new ArrayList<SampleMarker>();
+ int idx = markers.size();
+ for (int k = 0; k < markers.size(); k++) {
+ SampleMarker cue = markers.get(k);
+ if (cue.position > marker.position) {
+ idx = k;
+ break;
+ }
+ }
+ markers.add(idx, marker);
+ }
+}
diff --git a/src/main/java/com/jsyn/data/DoubleTable.java b/src/main/java/com/jsyn/data/DoubleTable.java
new file mode 100644
index 0000000..ca64c94
--- /dev/null
+++ b/src/main/java/com/jsyn/data/DoubleTable.java
@@ -0,0 +1,109 @@
+/*
+ * 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.data;
+
+import com.jsyn.exceptions.ChannelMismatchException;
+
+/**
+ * Evaluate a Function by interpolating between the values in a table. This can be used for
+ * wavetable lookup or waveshaping.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public class DoubleTable implements Function {
+ private double[] table;
+
+ public DoubleTable(int numFrames) {
+ allocate(numFrames);
+ }
+
+ public DoubleTable(double[] data) {
+ allocate(data.length);
+ write(data);
+ }
+
+ public DoubleTable(ShortSample shortSample) {
+ if (shortSample.getChannelsPerFrame() != 1) {
+ throw new ChannelMismatchException("DoubleTable can only be built from mono samples.");
+ }
+ short[] buffer = new short[256];
+ int framesLeft = shortSample.getNumFrames();
+ allocate(framesLeft);
+ int cursor = 0;
+ while (framesLeft > 0) {
+ int numTransfer = framesLeft;
+ if (numTransfer > buffer.length) {
+ numTransfer = buffer.length;
+ }
+ shortSample.read(cursor, buffer, 0, numTransfer);
+ write(cursor, buffer, 0, numTransfer);
+ cursor += numTransfer;
+ framesLeft -= numTransfer;
+ }
+ }
+
+ public void allocate(int numFrames) {
+ table = new double[numFrames];
+ }
+
+ public int length() {
+ return table.length;
+ }
+
+ public void write(double[] data) {
+ write(0, data, 0, data.length);
+ }
+
+ public void write(int startFrame, short[] data, int startIndex, int numFrames) {
+ for (int i = 0; i < numFrames; i++) {
+ table[startFrame + i] = data[startIndex + i] * (1.0 / 32768.0);
+ }
+ }
+
+ public void write(int startFrame, double[] data, int startIndex, int numFrames) {
+ for (int i = 0; i < numFrames; i++) {
+ table[startFrame + i] = data[startIndex + i];
+ }
+ }
+
+ /**
+ * Treat the double array as a lookup table with a domain of -1.0 to 1.0. If the input is out of
+ * range then the output will clip to the end values.
+ *
+ * @param input
+ * @return interpolated value from table
+ */
+ @Override
+ public double evaluate(double input) {
+ double interp;
+ if (input < -1.0) {
+ interp = table[0];
+ } else if (input < 1.0) {
+ double fractionalIndex = (table.length - 1) * (input - (-1.0)) / 2.0;
+ // We don't need floor() because fractionalIndex >= 0.0
+ int index = (int) fractionalIndex;
+ double fraction = fractionalIndex - index;
+
+ double s1 = table[index];
+ double s2 = table[index + 1];
+ interp = ((s2 - s1) * fraction) + s1;
+ } else {
+ interp = table[table.length - 1];
+ }
+ return interp;
+ }
+}
diff --git a/src/main/java/com/jsyn/data/FloatSample.java b/src/main/java/com/jsyn/data/FloatSample.java
new file mode 100644
index 0000000..2d8c973
--- /dev/null
+++ b/src/main/java/com/jsyn/data/FloatSample.java
@@ -0,0 +1,164 @@
+/*
+ * 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.data;
+
+import com.jsyn.unitgen.FixedRateMonoReader;
+import com.jsyn.unitgen.FixedRateStereoReader;
+import com.jsyn.unitgen.VariableRateMonoReader;
+import com.jsyn.unitgen.VariableRateStereoReader;
+import com.jsyn.util.SampleLoader;
+
+/**
+ * Store multi-channel floating point audio data in an interleaved buffer. The values are stored as
+ * 32-bit floats. You can play samples using one of the readers, for example VariableRateMonoReader.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ * @see SampleLoader
+ * @see FixedRateMonoReader
+ * @see FixedRateStereoReader
+ * @see VariableRateMonoReader
+ * @see VariableRateStereoReader
+ */
+public class FloatSample extends AudioSample implements Function {
+ private float[] buffer;
+
+ public FloatSample() {
+ }
+
+ /** Constructor for mono samples. */
+ public FloatSample(int numFrames) {
+ this(numFrames, 1);
+ }
+
+ /** Constructor for mono samples with data. */
+ public FloatSample(float[] data) {
+ this(data.length, 1);
+ write(data);
+ }
+
+ /** Constructor for multi-channel samples with data. */
+ public FloatSample(float[] data, int channelsPerFrame) {
+ this(data.length / channelsPerFrame, channelsPerFrame);
+ write(data);
+ }
+
+ /**
+ * Create a silent sample with enough memory to hold the audio data. The number of sample
+ * numbers in the array will be numFrames*channelsPerFrame.
+ *
+ * @param numFrames number of sample groups. A stereo frame contains 2 samples.
+ * @param channelsPerFrame 1 for mono, 2 for stereo
+ */
+ public FloatSample(int numFrames, int channelsPerFrame) {
+ allocate(numFrames, channelsPerFrame);
+ }
+
+ /**
+ * Allocate memory to hold the audio data. The number of sample numbers in the array will be
+ * numFrames*channelsPerFrame.
+ *
+ * @param numFrames number of sample groups. A stereo frame contains 2 samples.
+ * @param channelsPerFrame 1 for mono, 2 for stereo
+ */
+ @Override
+ public void allocate(int numFrames, int channelsPerFrame) {
+ buffer = new float[numFrames * channelsPerFrame];
+ this.numFrames = numFrames;
+ this.channelsPerFrame = channelsPerFrame;
+ }
+
+ /**
+ * Note that in a stereo sample, a frame has two values.
+ *
+ * @param startFrame index of frame in the sample
+ * @param data data to be written
+ * @param startIndex index of first value in array
+ * @param numFrames
+ */
+ public void write(int startFrame, float[] data, int startIndex, int numFrames) {
+ int numSamplesToWrite = numFrames * channelsPerFrame;
+ int firstSampleIndexToWrite = startFrame * channelsPerFrame;
+ System.arraycopy(data, startIndex, buffer, firstSampleIndexToWrite, numSamplesToWrite);
+ }
+
+ /**
+ * Note that in a stereo sample, a frame has two values.
+ *
+ * @param startFrame index of frame in the sample
+ * @param data array to receive the data from the sample
+ * @param startIndex index of first location in array to start filling
+ * @param numFrames
+ */
+ public void read(int startFrame, float[] data, int startIndex, int numFrames) {
+ int numSamplesToRead = numFrames * channelsPerFrame;
+ int firstSampleIndexToRead = startFrame * channelsPerFrame;
+ System.arraycopy(buffer, firstSampleIndexToRead, data, startIndex, numSamplesToRead);
+ }
+
+ /**
+ * Write the entire array to the sample. The sample data must have already been allocated with
+ * enough room to contain the data.
+ *
+ * @param data
+ */
+ public void write(float[] data) {
+ write(0, data, 0, data.length / getChannelsPerFrame());
+ }
+
+ public void read(float[] data) {
+ read(0, data, 0, data.length / getChannelsPerFrame());
+ }
+
+ @Override
+ public double readDouble(int index) {
+ return buffer[index];
+ }
+
+ @Override
+ public void writeDouble(int index, double value) {
+ buffer[index] = (float) value;
+ }
+
+ /**
+ * Interpolate between two adjacent samples.
+ * Note that this will only work for mono, single channel samples.
+ *
+ * @param fractionalIndex must be >=0 and < (size-1)
+ */
+ public double interpolate(double fractionalIndex) {
+ int index = (int) fractionalIndex;
+ float phase = (float) (fractionalIndex - index);
+ float source = buffer[index];
+ float target = buffer[index + 1];
+ return ((target - source) * phase) + source;
+ }
+
+ /**
+ * Note that this will only work for mono, single channel samples.
+ */
+ @Override
+ public double evaluate(double input) {
+ // Input ranges from -1 to +1
+ // Map it to range of sample with guard point.
+ double normalizedInput = (input + 1.0) * 0.5;
+ // Clip so it does not go out of range of the sample.
+ if (normalizedInput < 0.0) normalizedInput = 0.0;
+ else if (normalizedInput > 1.0) normalizedInput = 1.0;
+ double fractionalIndex = (getNumFrames() - 1.01) * normalizedInput;
+ return interpolate(fractionalIndex);
+ }
+}
diff --git a/src/main/java/com/jsyn/data/Function.java b/src/main/java/com/jsyn/data/Function.java
new file mode 100644
index 0000000..c0e6566
--- /dev/null
+++ b/src/main/java/com/jsyn/data/Function.java
@@ -0,0 +1,35 @@
+/*
+ * 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.data;
+
+import com.jsyn.unitgen.FunctionEvaluator;
+import com.jsyn.unitgen.FunctionOscillator;
+
+/**
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ * @see FunctionEvaluator
+ * @see FunctionOscillator
+ */
+public interface Function {
+ /**
+ * Convert an input value to an output value.
+ *
+ * @param input
+ * @return generated value
+ */
+ public double evaluate(double input);
+}
diff --git a/src/main/java/com/jsyn/data/HammingWindow.java b/src/main/java/com/jsyn/data/HammingWindow.java
new file mode 100644
index 0000000..d8e1238
--- /dev/null
+++ b/src/main/java/com/jsyn/data/HammingWindow.java
@@ -0,0 +1,41 @@
+/*
+ * 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.data;
+
+public class HammingWindow implements SpectralWindow {
+ private double[] data;
+
+ /** Construct a generalized Hamming Window */
+ public HammingWindow(int length, double alpha, double beta) {
+ data = new double[length];
+ double scaler = 2.0 * Math.PI / (length - 1);
+ for (int i = 0; i < length; i++) {
+ data[i] = alpha - (beta * (Math.cos(i * scaler)));
+ }
+ }
+
+ /** Traditional Hamming Window with alpha = 25/46 and beta = 21/46 */
+ public HammingWindow(int length) {
+ this(length, 25.0 / 46.0, 21.0 / 46.0);
+ }
+
+ @Override
+ public double get(int index) {
+ return data[index];
+ }
+
+}
diff --git a/src/main/java/com/jsyn/data/HannWindow.java b/src/main/java/com/jsyn/data/HannWindow.java
new file mode 100644
index 0000000..878d07c
--- /dev/null
+++ b/src/main/java/com/jsyn/data/HannWindow.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 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.data;
+
+import com.jsyn.unitgen.SpectralFFT;
+import com.jsyn.unitgen.SpectralIFFT;
+
+/**
+ * A HammingWindow with alpha and beta = 0.5.
+ *
+ * @author Phil Burk (C) 2013 Mobileer Inc
+ * @see SpectralWindow
+ * @see SpectralFFT
+ * @see SpectralIFFT
+ */
+public class HannWindow extends HammingWindow {
+
+ public HannWindow(int length) {
+ super(length, 0.5, 0.5);
+ }
+
+}
diff --git a/src/main/java/com/jsyn/data/SampleMarker.java b/src/main/java/com/jsyn/data/SampleMarker.java
new file mode 100644
index 0000000..d3db1d4
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SampleMarker.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 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.data;
+
+/**
+ * A marker for an audio sample.
+ *
+ * @author (C) 2012 Phil Burk, Mobileer Inc
+ */
+
+public class SampleMarker {
+ /** Sample frame index. */
+ public int position;
+ public String name;
+ public String comment;
+}
diff --git a/src/main/java/com/jsyn/data/SegmentedEnvelope.java b/src/main/java/com/jsyn/data/SegmentedEnvelope.java
new file mode 100644
index 0000000..efdfd89
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SegmentedEnvelope.java
@@ -0,0 +1,125 @@
+/*
+ * 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.data;
+
+import com.jsyn.unitgen.EnvelopeDAHDSR;
+import com.jsyn.unitgen.VariableRateMonoReader;
+
+/**
+ * Store an envelope as a series of line segments. Each line is described as a duration and a target
+ * value. The envelope can be played using a {@link VariableRateMonoReader}. Here is an example that
+ * generates an envelope that looks like a traditional ADSR envelope.
+ *
+ * <pre>
+ * <code>
+ * // Create an amplitude envelope and fill it with data.
+ * double[] ampData = {
+ * 0.02, 0.9, // duration,value pair 0, "attack"
+ * 0.10, 0.5, // pair 1, "decay"
+ * 0.50, 0.0 // pair 2, "release"
+ * };
+ * SegmentedEnvelope ampEnvelope = new SegmentedEnvelope( ampData );
+ *
+ * // Hang at end of decay segment to provide a "sustain" segment.
+ * ampEnvelope.setSustainBegin( 1 );
+ * ampEnvelope.setSustainEnd( 1 );
+ *
+ * // Play the envelope using queueOn so that it uses the sustain and release information.
+ * synth.add( ampEnv = new VariableRateMonoReader() );
+ * ampEnv.dataQueue.queueOn( ampEnvelope );
+ * </code>
+ * </pre>
+ *
+ * As an alternative you could use an {@link EnvelopeDAHDSR}.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ * @see VariableRateMonoReader
+ * @see EnvelopeDAHDSR
+ */
+public class SegmentedEnvelope extends SequentialDataCommon {
+ private double[] buffer;
+
+ public SegmentedEnvelope(int maxFrames) {
+ allocate(maxFrames);
+ }
+
+ public SegmentedEnvelope(double[] pairs) {
+ this(pairs.length / 2);
+ write(pairs);
+ }
+
+ public void allocate(int maxFrames) {
+ buffer = new double[maxFrames * 2];
+ this.maxFrames = maxFrames;
+ this.numFrames = 0;
+ }
+
+ /**
+ * Write frames of envelope data. A frame consists of a duration and a value.
+ *
+ * @param startFrame Index of frame in envelope to write to.
+ * @param data Pairs of duration and value.
+ * @param startIndex Index of frame in data[] to read from.
+ * @param numToWrite Number of frames (pairs) to write.
+ */
+ public void write(int startFrame, double[] data, int startIndex, int numToWrite) {
+ System.arraycopy(data, startIndex * 2, buffer, startFrame * 2, numToWrite * 2);
+ if ((startFrame + numToWrite) > numFrames) {
+ numFrames = startFrame + numToWrite;
+ }
+ }
+
+ public void read(int startFrame, double[] data, int startIndex, int numToRead) {
+ System.arraycopy(buffer, startFrame * 2, data, startIndex * 2, numToRead * 2);
+ }
+
+ public void write(double[] data) {
+ write(0, data, 0, data.length / 2);
+ }
+
+ public void read(double[] data) {
+ read(0, data, 0, data.length / 2);
+ }
+
+ /** Read the value of an envelope, not the duration. */
+ @Override
+ public double readDouble(int index) {
+ return buffer[(index * 2) + 1];
+ }
+
+ @Override
+ public void writeDouble(int index, double value) {
+ buffer[(index * 2) + 1] = value;
+ if ((index + 1) > numFrames) {
+ numFrames = index + 1;
+ }
+ }
+
+ @Override
+ public double getRateScaler(int index, double synthesisPeriod) {
+ double duration = buffer[index * 2];
+ if (duration < synthesisPeriod) {
+ duration = synthesisPeriod;
+ }
+ return 1.0 / duration;
+ }
+
+ @Override
+ public int getChannelsPerFrame() {
+ return 1;
+ }
+}
diff --git a/src/main/java/com/jsyn/data/SequentialData.java b/src/main/java/com/jsyn/data/SequentialData.java
new file mode 100644
index 0000000..f567493
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SequentialData.java
@@ -0,0 +1,96 @@
+/*
+ * 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.data;
+
+import com.jsyn.unitgen.FixedRateMonoReader;
+import com.jsyn.unitgen.FixedRateMonoWriter;
+import com.jsyn.unitgen.FixedRateStereoReader;
+import com.jsyn.unitgen.FixedRateStereoWriter;
+import com.jsyn.unitgen.VariableRateMonoReader;
+import com.jsyn.unitgen.VariableRateStereoReader;
+
+/**
+ * Interface for objects that can be read and/or written by index. The index is not stored
+ * internally so they can be shared by multiple readers.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ * @see FixedRateMonoReader
+ * @see FixedRateStereoReader
+ * @see FixedRateMonoWriter
+ * @see FixedRateStereoWriter
+ * @see VariableRateMonoReader
+ * @see VariableRateStereoReader
+ */
+public interface SequentialData {
+ /**
+ * Write a value at the given index.
+ *
+ * @param index sample index is ((frameIndex * channelsPerFrame) + channelIndex)
+ * @param value the value to be written
+ */
+ void writeDouble(int index, double value);
+
+ /**
+ * Read a value from the sample independently from the internal storage format.
+ *
+ * @param index sample index is ((frameIndex * channelsPerFrame) + channelIndex)
+ */
+ double readDouble(int index);
+
+ /***
+ * @return Beginning of sustain loop or -1 if no loop.
+ */
+ public int getSustainBegin();
+
+ /**
+ * SustainEnd value is the frame index of the frame just past the end of the loop. The number of
+ * frames included in the loop is (SustainEnd - SustainBegin).
+ *
+ * @return End of sustain loop or -1 if no loop.
+ */
+ public int getSustainEnd();
+
+ /***
+ * @return Beginning of release loop or -1 if no loop.
+ */
+ public int getReleaseBegin();
+
+ /***
+ * @return End of release loop or -1 if no loop.
+ */
+ public int getReleaseEnd();
+
+ /**
+ * Get rate to play the data. In an envelope this correspond to the inverse of the frame
+ * duration and would vary frame to frame. For an audio sample it is 1.0.
+ *
+ * @param index
+ * @param synthesisRate
+ * @return rate to scale the playback speed.
+ */
+ double getRateScaler(int index, double synthesisRate);
+
+ /**
+ * @return For a stereo sample, return 2.
+ */
+ int getChannelsPerFrame();
+
+ /**
+ * @return The number of valid frames that can be read.
+ */
+ int getNumFrames();
+}
diff --git a/src/main/java/com/jsyn/data/SequentialDataCommon.java b/src/main/java/com/jsyn/data/SequentialDataCommon.java
new file mode 100644
index 0000000..5cc51df
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SequentialDataCommon.java
@@ -0,0 +1,136 @@
+/*
+ * 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.data;
+
+/**
+ * Abstract base class for envelopes and samples that adds sustain and release loops.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ */
+public abstract class SequentialDataCommon implements SequentialData {
+ protected int numFrames;
+ protected int maxFrames;
+ private int sustainBegin = -1;
+ private int sustainEnd = -1;
+ private int releaseBegin = -1;
+ private int releaseEnd = -1;
+
+ @Override
+ public abstract void writeDouble(int index, double value);
+
+ @Override
+ public abstract double readDouble(int index);
+
+ @Override
+ public abstract double getRateScaler(int index, double synthesisRate);
+
+ @Override
+ public abstract int getChannelsPerFrame();
+
+ /**
+ * @return Maximum number of frames of data.
+ */
+ public int getMaxFrames() {
+ return maxFrames;
+ }
+
+ /**
+ * Set number of frames of data. Input will be clipped to maxFrames. This is useful when
+ * changing the contents of a sample or envelope.
+ */
+ public void setNumFrames(int numFrames) {
+ if (numFrames > maxFrames)
+ numFrames = maxFrames;
+ this.numFrames = numFrames;
+ }
+
+ @Override
+ public int getNumFrames() {
+ return numFrames;
+ }
+
+ // JavaDocs will be copied from SequentialData
+
+ @Override
+ public int getSustainBegin() {
+ return this.sustainBegin;
+ }
+
+ @Override
+ public int getSustainEnd() {
+ return this.sustainEnd;
+ }
+
+ @Override
+ public int getReleaseBegin() {
+ return this.releaseBegin;
+ }
+
+ @Override
+ public int getReleaseEnd() {
+ return this.releaseEnd;
+ }
+
+ /**
+ * Set beginning of a sustain loop. When UnitDataQueuePort.queueOn() is called,
+ * if the loop is set then the attack portion will be queued followed by this sustain
+ * region using queueLoop().
+ * The number of frames in the loop will be (SustainEnd - SustainBegin).
+ * <p>
+ * For a steady sustain level, like in an ADSR envelope, set SustainBegin and
+ * SustainEnd to the same frame.
+ * <p>
+ * For a sustain that is modulated, include two or more frames in the loop.
+ *
+ * @param sustainBegin
+ */
+ public void setSustainBegin(int sustainBegin) {
+ this.sustainBegin = sustainBegin;
+ }
+
+ /**
+ * SustainEnd value is the frame index of the frame just past the end of the loop.
+ * The number of frames included in the loop is (SustainEnd - SustainBegin).
+ *
+ * @param sustainEnd
+ */
+ public void setSustainEnd(int sustainEnd) {
+ this.sustainEnd = sustainEnd;
+ }
+
+ /**
+ * The release loop behaves like the sustain loop but it is triggered
+ * by UnitDataQueuePort.queueOff().
+ *
+ * @param releaseBegin
+ */
+ public void setReleaseBegin(int releaseBegin) {
+ this.releaseBegin = releaseBegin;
+ }
+
+ /**
+ * ReleaseEnd value is the frame index of the frame just past the end of the loop.
+ * The number of frames included in the loop is (ReleaseEnd - ReleaseBegin).
+ *
+ * @param releaseEnd
+ */
+
+ public void setReleaseEnd(int releaseEnd) {
+ this.releaseEnd = releaseEnd;
+ }
+
+}
diff --git a/src/main/java/com/jsyn/data/ShortSample.java b/src/main/java/com/jsyn/data/ShortSample.java
new file mode 100644
index 0000000..4a4110e
--- /dev/null
+++ b/src/main/java/com/jsyn/data/ShortSample.java
@@ -0,0 +1,123 @@
+/*
+ * 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.data;
+
+import com.jsyn.engine.SynthesisEngine;
+import com.jsyn.unitgen.FixedRateMonoReader;
+import com.jsyn.unitgen.FixedRateStereoReader;
+import com.jsyn.unitgen.VariableRateMonoReader;
+import com.jsyn.unitgen.VariableRateStereoReader;
+import com.jsyn.util.SampleLoader;
+
+/**
+ * Store multi-channel short audio data in an interleaved buffer.
+ *
+ * @author Phil Burk (C) 2010 Mobileer Inc
+ * @see SampleLoader
+ * @see FixedRateMonoReader
+ * @see FixedRateStereoReader
+ * @see VariableRateMonoReader
+ * @see VariableRateStereoReader
+ */
+public class ShortSample extends AudioSample {
+ private short[] buffer;
+
+ public ShortSample() {
+ }
+
+ public ShortSample(int numFrames, int channelsPerFrame) {
+ allocate(numFrames, channelsPerFrame);
+ }
+
+ /** Constructor for mono samples with data. */
+ public ShortSample(short[] data) {
+ this(data.length, 1);
+ write(data);
+ }
+
+ /** Constructor for multi-channel samples with data. */
+ public ShortSample(short[] data, int channelsPerFrame) {
+ this(data.length / channelsPerFrame, channelsPerFrame);
+ write(data);
+ }
+
+ @Override
+ public void allocate(int numFrames, int channelsPerFrame) {
+ buffer = new short[numFrames * channelsPerFrame];
+ this.numFrames = numFrames;
+ this.channelsPerFrame = channelsPerFrame;
+ }
+
+ /**
+ * Note that in a stereo sample, a frame has two values.
+ *
+ * @param startFrame index of frame in the sample
+ * @param data data to be written
+ * @param startIndex index of first value in array
+ * @param numFrames
+ */
+ public void write(int startFrame, short[] data, int startIndex, int numFrames) {
+ int numSamplesToWrite = numFrames * channelsPerFrame;
+ int firstSampleIndexToWrite = startFrame * channelsPerFrame;
+ System.arraycopy(data, startIndex, buffer, firstSampleIndexToWrite, numSamplesToWrite);
+ }
+
+ /**
+ * Note that in a stereo sample, a frame has two values.
+ *
+ * @param startFrame index of frame in the sample
+ * @param data array to receive the data from the sample
+ * @param startIndex index of first location in array to start filling
+ * @param numFrames
+ */
+ public void read(int startFrame, short[] data, int startIndex, int numFrames) {
+ int numSamplesToRead = numFrames * channelsPerFrame;
+ int firstSampleIndexToRead = startFrame * channelsPerFrame;
+ System.arraycopy(buffer, firstSampleIndexToRead, data, startIndex, numSamplesToRead);
+ }
+
+ public void write(short[] data) {
+ write(0, data, 0, data.length);
+ }
+
+ public void read(short[] data) {
+ read(0, data, 0, data.length);
+ }
+
+ public short readShort(int index) {
+ return buffer[index];
+ }
+
+ public void writeShort(int index, short value) {
+ buffer[index] = value;
+ }
+
+ /** Read a sample converted to a double in the range -1.0 to almost 1.0. */
+ @Override
+ public double readDouble(int index) {
+ return SynthesisEngine.convertShortToDouble(buffer[index]);
+ }
+
+ /**
+ * Write a double that will be clipped to the range -1.0 to almost 1.0 and converted to a short.
+ */
+ @Override
+ public void writeDouble(int index, double value) {
+ buffer[index] = SynthesisEngine.convertDoubleToShort(value);
+ }
+
+}
diff --git a/src/main/java/com/jsyn/data/SpectralWindow.java b/src/main/java/com/jsyn/data/SpectralWindow.java
new file mode 100644
index 0000000..0fcfac4
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SpectralWindow.java
@@ -0,0 +1,21 @@
+/*
+ * 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.data;
+
+public interface SpectralWindow {
+ public double get(int index);
+}
diff --git a/src/main/java/com/jsyn/data/SpectralWindowFactory.java b/src/main/java/com/jsyn/data/SpectralWindowFactory.java
new file mode 100644
index 0000000..01cced6
--- /dev/null
+++ b/src/main/java/com/jsyn/data/SpectralWindowFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 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.data;
+
+import com.jsyn.unitgen.SpectralFFT;
+import com.jsyn.unitgen.SpectralFilter;
+import com.jsyn.unitgen.SpectralIFFT;
+
+/**
+ * Create shared windows as needed for use with FFTs and IFFTs.
+ *
+ * @author Phil Burk (C) 2013 Mobileer Inc
+ * @see SpectralWindow
+ * @see SpectralFFT
+ * @see SpectralIFFT
+ * @see SpectralFilter
+ */
+public class SpectralWindowFactory {
+ private static final int NUM_WINDOWS = 16;
+ private static final int MIN_SIZE_LOG_2 = 2;
+ private static HammingWindow[] hammingWindows = new HammingWindow[NUM_WINDOWS];
+ private static HannWindow[] hannWindows = new HannWindow[NUM_WINDOWS];
+
+ /** @return a shared standard HammingWindow */
+ public static HammingWindow getHammingWindow(int sizeLog2) {
+ int index = sizeLog2 - MIN_SIZE_LOG_2;
+ if (hammingWindows[index] == null) {
+ hammingWindows[index] = new HammingWindow(1 << sizeLog2);
+ }
+ return hammingWindows[index];
+ }
+
+ /** @return a shared HannWindow */
+ public static HannWindow getHannWindow(int sizeLog2) {
+ int index = sizeLog2 - MIN_SIZE_LOG_2;
+ if (hannWindows[index] == null) {
+ hannWindows[index] = new HannWindow(1 << sizeLog2);
+ }
+ return hannWindows[index];
+ }
+}
diff --git a/src/main/java/com/jsyn/data/Spectrum.java b/src/main/java/com/jsyn/data/Spectrum.java
new file mode 100644
index 0000000..66e4ee4
--- /dev/null
+++ b/src/main/java/com/jsyn/data/Spectrum.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 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.data;
+
+import com.jsyn.unitgen.SpectralFFT;
+import com.jsyn.unitgen.SpectralIFFT;
+import com.jsyn.unitgen.SpectralProcessor;
+
+/**
+ * Complex spectrum with real and imaginary parts. The frequency associated with each bin of the
+ * spectrum is:
+ *
+ * <pre>
+ * frequency = binIndex * sampleRate / size
+ * </pre>
+ *
+ * Note that the upper half of the spectrum is above the Nyquist frequency. Those frequencies are
+ * mirrored around the Nyquist frequency. Note that this spectral API is experimental and may change
+ * at any time.
+ *
+ * @author Phil Burk (C) 2013 Mobileer Inc
+ * @version 016
+ * @see SpectralFFT
+ * @see SpectralIFFT
+ * @see SpectralProcessor
+ */
+public class Spectrum {
+ private double[] real;
+ private double[] imaginary;
+ public static final int DEFAULT_SIZE_LOG_2 = 9;
+ public static final int DEFAULT_SIZE = 1 << DEFAULT_SIZE_LOG_2;
+
+ public Spectrum() {
+ this(DEFAULT_SIZE);
+ }
+
+ public Spectrum(int size) {
+ setSize(size);
+ }
+
+ public double[] getReal() {
+ return real;
+ }
+
+ public double[] getImaginary() {
+ return imaginary;
+ }
+
+ /**
+ * If you change the size of the spectrum then the real and imaginary arrays will be
+ * reallocated.
+ *
+ * @param size
+ */
+ public void setSize(int size) {
+ if ((real == null) || (real.length != size)) {
+ real = new double[size];
+ imaginary = new double[size];
+ }
+ }
+
+ public int size() {
+ return real.length;
+ }
+
+ /**
+ * Copy this spectrum to another spectrum of the same length.
+ *
+ * @param destination
+ */
+ public void copyTo(Spectrum destination) {
+ assert (size() == destination.size());
+ System.arraycopy(real, 0, destination.real, 0, real.length);
+ System.arraycopy(imaginary, 0, destination.imaginary, 0, imaginary.length);
+ }
+
+ public void clear() {
+ for (int i = 0; i < real.length; i++) {
+ real[i] = 0.0;
+ imaginary[i] = 0.0;
+ }
+ }
+}