aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/cycling74/msp
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/cycling74/msp')
-rw-r--r--src/java/com/cycling74/msp/AudioFileBuffer.java116
-rw-r--r--src/java/com/cycling74/msp/MSPBuffer.java127
-rw-r--r--src/java/com/cycling74/msp/MSPObject.java163
-rw-r--r--src/java/com/cycling74/msp/MSPPerformable.java17
-rw-r--r--src/java/com/cycling74/msp/MSPPerformer.java36
-rw-r--r--src/java/com/cycling74/msp/MSPSignal.java70
-rw-r--r--src/java/com/cycling74/msp/package.html5
7 files changed, 534 insertions, 0 deletions
diff --git a/src/java/com/cycling74/msp/AudioFileBuffer.java b/src/java/com/cycling74/msp/AudioFileBuffer.java
new file mode 100644
index 0000000..b2e76f3
--- /dev/null
+++ b/src/java/com/cycling74/msp/AudioFileBuffer.java
@@ -0,0 +1,116 @@
+package com.cycling74.msp;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+import com.cycling74.max.MessageReceiver;
+
+/**
+ * Work in progress, target: 0.8.4
+ */
+class AudioFileBuffer {
+
+ /** file buffer value */
+ public float buf[][];
+
+ /** Value of the message that will be sended to MessageReceiver when the file will be loaded */
+ public static final int FINISHED_READING = 1;
+
+ private MessageReceiver callback = null;
+ private String file;
+ private AudioFormat audioFormat;
+ private AudioFormat targetAudioFormat;
+
+ // getters values
+ private float sampleRate;
+ private int sampleBitFormat;
+ private boolean sampleBigEndian;
+ private int sampleChannels;
+ private long sampleFrames;
+
+ public AudioFileBuffer(String filename) throws FileNotFoundException, IOException, UnsupportedAudioFileException {
+ open(filename);
+ }
+
+ public AudioFileBuffer(String filename, MessageReceiver callback) throws FileNotFoundException, IOException, UnsupportedAudioFileException {
+ this.callback = callback;
+ open(filename);
+ }
+
+ public void open(String filename) throws FileNotFoundException, IOException, UnsupportedAudioFileException {
+ // file format
+ sampleRate = 0;
+ sampleBitFormat = 0;
+ sampleChannels = 0;
+ sampleFrames = 0;
+
+ AudioInputStream sourceAudioInputStream = AudioSystem.getAudioInputStream( new FileInputStream(file) );
+ AudioFormat sourceAudioFormat = sourceAudioInputStream.getFormat();
+ targetAudioFormat = new AudioFormat(
+ AudioFormat.Encoding.PCM_UNSIGNED, // Encoding
+ getSystemSampleRate(), // Sample rate
+ 16, // bit
+ sourceAudioFormat.getChannels(), // channel
+ sourceAudioFormat.getFrameSize(), // frame size
+ sourceAudioFormat.getFrameRate(), // frame rate
+ false ); // big Endian
+ AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(audioFormat, sourceAudioInputStream );
+
+ FileLoader thread = new FileLoader(audioInputStream);
+ new Thread(thread).start();
+ }
+
+ class FileLoader implements Runnable {
+ AudioInputStream ais;
+
+ FileLoader(AudioInputStream input) {
+ ais = input;
+ }
+
+ public void run() {
+ try {
+ buf = new float[audioFormat.getChannels()][];
+
+ for (int i=0;ais.available() != 0;i++) {
+ //doc.content[i] = audioInputStream.read() / 65535;
+ }
+
+ if ( callback != null )
+ callback.messageReceived(this, FINISHED_READING, null);
+ } catch ( IOException e ) {
+ }
+ }
+ }
+
+ public float getSampleRate() {
+ return sampleRate;
+ }
+
+ public int getSampleSizeInBits() {
+ return sampleBitFormat;
+ }
+
+ public boolean isBigEndian() {
+ return sampleBigEndian;
+ }
+
+ public long getFrameLength() {
+ return sampleFrames;
+ }
+
+ public int getChannels() {
+ return sampleChannels;
+ }
+
+ public float getLengthMs() {
+ return buf[0].length / (sampleRate / 1000);
+ }
+
+ private native int getSystemSampleRate();
+} \ No newline at end of file
diff --git a/src/java/com/cycling74/msp/MSPBuffer.java b/src/java/com/cycling74/msp/MSPBuffer.java
new file mode 100644
index 0000000..70b730a
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPBuffer.java
@@ -0,0 +1,127 @@
+package com.cycling74.msp;
+
+/**
+ * Used to get or set pd array content. Please note that the channel parameter
+ * is added in the API to match Max/MSP MSPBuffer signature.
+ */
+public class MSPBuffer {
+ /**
+ * Returns the array content.
+ * @param name the array name
+ * @return the array contents
+ */
+ public static float[] peek(String name) {
+ return getArray(name, 0, -1);
+ }
+
+ /**
+ * Returns the array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @return the array contents
+ */
+ public static float[] peek(String name, int channel) {
+ return getArray(name, 0, -1);
+ }
+
+ /**
+ * Returns the array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param start the start index of the array
+ * @param length the size of the array to return
+ * @return the array contents
+ */
+ public static float[] peek(String name, int channel, long start, long length) {
+ return getArray(name, start, length);
+ }
+
+ /**
+ * Returns the array content value at a specific position.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param index the start index of the array
+ * @return the value stored at index <code>start</code>
+ */
+ public static float peek(String name, int channel, long index) {
+ float ret[] = getArray(name, index, 1);
+ if ( ret == null )
+ return 0;
+ return ret[0];
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param values the array to set
+ */
+ public static void poke(String name, float values[]) {
+ setArray(name, 0, values);
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param values the array to set
+ */
+ public static void poke(String name, int channel, float values[]) {
+ setArray(name, 0, values);
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param start the start index of the array
+ * @param values the array to set
+ */
+ public static void poke(String name, int channel, long start, float values[]) {
+ setArray(name, start, values);
+ }
+
+ /**
+ * Set a value in a array.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param index the index in the array to set
+ * @param value the value to set in the array
+ */
+ public static void poke(String name, int channel, long index, float value) {
+ float content[] = new float[1];
+ content[0] = value;
+ setArray(name, index, content);
+ }
+
+ /**
+ * Sets the array size.
+ * @param name the array name
+ * @param numchannel <i>not used in pd</i>
+ * @param size the new array size;
+ */
+ public static native void setSize(String name, int numchannel, long size);
+
+ /**
+ * Returns the array size
+ * @param name the array name
+ * @return the array size or -1 if not found
+ */
+ public static native long getSize(String name);
+
+ private static native float[] getArray(String name, long from, long size);
+ private static native void setArray(String name, long from, float[]content);
+ private MSPBuffer() {}
+
+ /**
+ * Return the number of channel for this array. Useless in PD cause there
+ * is no channels in a array.
+ * @param name array name.
+ * @return always returns 1 on pd; unless the name is not defined.
+ */
+ public static int getChannel(String name) {
+ // resolv the name
+ if ( getSize(name) != -1 )
+ return 1;
+ return -1;
+ }
+}
diff --git a/src/java/com/cycling74/msp/MSPObject.java b/src/java/com/cycling74/msp/MSPObject.java
new file mode 100644
index 0000000..be2d68b
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPObject.java
@@ -0,0 +1,163 @@
+package com.cycling74.msp;
+
+import java.lang.reflect.Method;
+
+import com.cycling74.max.DataTypes;
+import com.cycling74.max.MaxObject;
+
+/**
+ * Main object to extend to use dsp with pd. You will need to
+ * use the pdj~ external if you want to process signals. Before
+ * the dsp starts processing, the method dsp will be called and it must
+ * return the performer method that will process each dsp cycles.
+ *
+ * <p>Here is a basic guideline for using the MSPObject:</p>
+ * <p><blockquote><pre>
+ * import com.cycling74.max.*;
+ * import com.cycling74.msp.*;
+ * import java.lang.reflection.Method;
+ *
+ * public class panner extends MSPObject {
+ * float left = 1, right = 1;
+ *
+ * public panner() {
+ * declareInlets( new int[] { SIGNAL, DataTypes.ANYTHING } );
+ * declareOutlets( new int[] { SIGNAL, SIGNAL } );
+ * }
+ *
+ * // From 0..127
+ * public void inlet(float val) {
+ * if ( val > 64 ) {
+ * right = 1;
+ * left = ((127-val) / 64);
+ * } else {
+ * left = 1;
+ * right = val / 64;
+ * }
+ * }
+ *
+ * public Method dsp(MSPSignal[] ins, MSPSignal[] outs) {
+ * return getPerformMethod("perform");
+ * }
+ *
+ * public void perform(MSPSignal[] ins, MSPSignal[] outs) {
+ * for (int i=0;i&lt;ins[0].n;i++) {
+ * outs[0].vec[i] = ins[0].vec[i] * left;
+ * outs[1].vec[i] = ins[0].vec[i] * right;
+ * }
+ * }
+ * }
+ * </p></blockquote></pre>
+ */
+public abstract class MSPObject extends MaxObject {
+ /**
+ * Use this value to indentify a signal intlet/outlet to
+ * the method declareInlet/declareOutlet.
+ */
+ public final static int SIGNAL = 32;
+
+ /**
+ * Initialize the dsp state. From the numbers of input/output this
+ * method must return a performing method.
+ * @param ins input signals
+ * @param outs output signals
+ * @return the method to execute at each dsp cycle
+ */
+ public abstract Method dsp(MSPSignal[] ins, MSPSignal[] outs);
+
+ /**
+ * Quicky method to be used with dsp(MSPSignal[], MSPSignal[]). It
+ * will return the method name <code>methodName</code> with signature
+ * (MSPSignal[], MSPSignal[]) in the current class.
+ * @param methodName the name of the method in the current class
+ * @return the method reflection
+ */
+ protected Method getPerformMethod(String methodName) {
+ try {
+ Method m = getClass().getDeclaredMethod(methodName, new Class[] {
+ MSP_SIGNAL_ARRAY_CLZ, MSP_SIGNAL_ARRAY_CLZ });
+ return m;
+ } catch ( NoSuchMethodException e ) {
+ error("pdj~: method: " + methodName + " not found in class in:" + getClass().toString());
+ }
+ return null;
+ }
+
+ /**
+ * Force to copy of each MSPBuffer when performer is called. Right now,
+ * on pdj the MSPBuffer is always copied.
+ * @param copyBuffer true if you need to copyBuffer
+ */
+ protected void setNoInPlace(boolean copyBuffer) {
+ //this.copyBuffer = copyBuffer;
+ }
+
+ /**
+ * This method is called when the dsp is start/stop.
+ * <b>NOT USED ON PD.</b>
+ * @param dspRunning true if the dsp is running
+ */
+ protected void dspstate(boolean dspRunning) {
+ }
+
+
+ /**
+ * Declare the inlets used by this object. Use MSPObject.SIGNAL
+ * to define a signal inlet. <b>Note:</b> any signal inlet won't
+ * be able to process any atom messages.
+ */
+ protected void declareInlets(int[] types) {
+ int i, inlets = 0;
+ for (i=0;i<types.length;i++) {
+ if ( types[i] == SIGNAL )
+ inlets++;
+ }
+ _used_inputs = new MSPSignal[inlets];
+ for(i=0;i<_used_inputs.length;i++) {
+ _used_inputs[i] = new MSPSignal();
+ }
+ super.declareInlets(types);
+ }
+
+ /**
+ * Declare the outlets used by this object. Use MSPObject.SIGNAL
+ * to define a signal outlet.
+ */
+ protected void declareOutlets(int[] types) {
+ int i, outlets = 0;
+ for (i=0;i<types.length;i++) {
+ if ( types[i] == SIGNAL )
+ outlets++;
+ }
+ _used_outputs = new MSPSignal[outlets];
+ for(i=0;i<_used_outputs.length;i++) {
+ _used_outputs[i] = new MSPSignal();
+ }
+ super.declareOutlets(types);
+ }
+
+ public static final Class MSP_SIGNAL_ARRAY_CLZ = (new MSPSignal[0]).getClass();
+
+ private MSPSignal[] _used_inputs = new MSPSignal[0];
+ private MSPSignal[] _used_outputs = new MSPSignal[0];
+
+ private Method _dspinit(float sr, int vector_size) {
+ int i;
+ for(i=0;i<_used_inputs.length;i++) {
+ _used_inputs[i].n = vector_size;
+ _used_inputs[i].vec = new float[vector_size];
+ _used_inputs[i].sr = sr;
+ }
+ for(i=0;i<_used_outputs.length;i++) {
+ _used_outputs[i].n = vector_size;
+ _used_outputs[i].vec = new float[vector_size];
+ _used_outputs[i].sr = sr;
+ }
+ dspstate(true);
+ return dsp(_used_inputs, _used_outputs);
+ }
+
+ private void _emptyPerformer(MSPSignal[] ins, MSPSignal[] outs) {
+ }
+}
+
diff --git a/src/java/com/cycling74/msp/MSPPerformable.java b/src/java/com/cycling74/msp/MSPPerformable.java
new file mode 100644
index 0000000..6f27241
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPPerformable.java
@@ -0,0 +1,17 @@
+package com.cycling74.msp;
+
+/**
+ * This interface is used to chain Java dsp objects since they don't
+ * have to be MSPObject to process signals.
+ */
+public interface MSPPerformable {
+ /**
+ * @see MSPPerformer.dspsetup(MSPSignal in[], MSPSignal out[])
+ */
+ public void dspsetup(MSPSignal in[], MSPSignal out[]);
+
+ /**
+ * @see MSPPerformer.perform(MSPSignal in[], MSPSignal out[]);
+ */
+ public void perform(MSPSignal in[], MSPSignal out[]);
+}
diff --git a/src/java/com/cycling74/msp/MSPPerformer.java b/src/java/com/cycling74/msp/MSPPerformer.java
new file mode 100644
index 0000000..2201d82
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPPerformer.java
@@ -0,0 +1,36 @@
+package com.cycling74.msp;
+
+import java.lang.reflect.Method;
+
+/**
+ * Process signals with a single method. Like MSPObject except that
+ * the performer method will always be "perform" (and you override it).
+ */
+public abstract class MSPPerformer extends MSPObject implements MSPPerformable {
+
+ /**
+ * Called by PD with the dsp message is sended. This method will then
+ * call dspsetup and map the method "perform" has the performer method.
+ * You don't have to override this method, use dspsetup if initialization
+ * needs to be done before the signals starts processing.
+ */
+ public Method dsp(MSPSignal[] in, MSPSignal[] out) {
+ dspsetup(in, out);
+ return getPerformMethod("perform");
+ }
+
+ /**
+ * Initialize the dsp state of the MSPPerformable. Override this if
+ * you have to initalize something before the signal starts
+ * processing.
+ */
+ public void dspsetup(MSPSignal[] in, MSPSignal[] out) {
+ }
+
+ /**
+ * Process signal inlets/outlets. You must override this since it is
+ * the main processing method.
+ */
+ public abstract void perform(MSPSignal[] in, MSPSignal[] out);
+
+}
diff --git a/src/java/com/cycling74/msp/MSPSignal.java b/src/java/com/cycling74/msp/MSPSignal.java
new file mode 100644
index 0000000..0fb9635
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPSignal.java
@@ -0,0 +1,70 @@
+package com.cycling74.msp;
+
+/**
+ * Signals representation; signals inlets will copy signal data to this
+ * object.
+ */
+public class MSPSignal {
+
+ /**
+ * Tells the number of time this signal is connected. This is always
+ * 1 in pure-data.
+ */
+ public short cc = 1;
+
+ /**
+ * Tells if this signal is connected. This is always true in pure-data.
+ */
+ public boolean connected = true;
+
+ /**
+ * Number of sample in vector.
+ */
+ public int n;
+
+ /**
+ * The sampling rate.
+ */
+ public double sr;
+
+ /**
+ * The current sample data.
+ */
+ public float[] vec;
+
+ MSPSignal() {
+ }
+
+ public MSPSignal(float vec[], double sr, int n, short cc) {
+ this.vec = vec;
+ this.sr = sr;
+ this.n = n;
+ this.cc = cc;
+ }
+
+ /**
+ * Returns a copy of this signal but with the same sample buffer.
+ * @return the new signal with the same buffer
+ */
+ public MSPSignal alias() {
+ return new MSPSignal(vec, sr, n, cc);
+ }
+
+ /**
+ * Returns the a duplicated signal object. Also copies the array.
+ * @return the new signal with the same value
+ */
+ public MSPSignal dup() {
+ return new MSPSignal((float [])vec.clone(), sr, n, cc);
+ }
+
+ /**
+ * Returns the a duplicated signal object, but the sample vector is
+ * re-initialized.
+ * @return the new signal with empty value (silence)
+ */
+ public MSPSignal dupclean() {
+ return new MSPSignal(new float [vec.length], sr, n, cc);
+ }
+
+}
diff --git a/src/java/com/cycling74/msp/package.html b/src/java/com/cycling74/msp/package.html
new file mode 100644
index 0000000..17f4051
--- /dev/null
+++ b/src/java/com/cycling74/msp/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<p>Package for using using array and signals objects.</p>
+</body>
+</html> \ No newline at end of file