From a764e59e1d3a8e330f0d484fdb26b35ca3f0b2e4 Mon Sep 17 00:00:00 2001
From: Hans-Christoph Steiner "ok".equals("ok");
+ */
+ public abstract boolean equals(Object object);
+
+ /**
+ * Returns the hashCode representation for this Atom. If it is an float,
+ * the float into bit value is return and if it is an String, the hashcode
+ * value of the string is returned.
+ */
+ public abstract int hashCode();
+
+ // Array utility classes
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the index of the first atom that is found in the list.
+ * @param org the atom to find
+ * @param list the list of atom to search
+ * @return the index of the atom found. -1 if not found.
+ */
+ public static int isIn(Atom org, Atom[] list) {
+ return isIn(org, list, 0, list.length-1);
+ }
+
+ /**
+ * Returns the index of the first atom that is found in the list.
+ * @param org the atom to find
+ * @param list the list of atom to search
+ * @param from the start index to check
+ * @param to the last index to check
+ * @return the index of the atom found. -1 if not found.
+ */
+ public static int isIn(Atom org, Atom[] list, int from, int to) {
+ for(int i=from;ito
.
+ * @param list the list to strip
+ * @param from the start index
+ * @param to the last index
+ * @return the stripped array
+ */
+ public static Atom[] removeSome(Atom[] list, int from, int to) {
+ if ( from == 0 && from == list.length - 1 )
+ return new Atom[] {};
+
+ Atom[] ret = new Atom[list.length - (to-from+1)];
+ System.arraycopy(list, 0, ret, 0, from);
+ System.arraycopy(list, to + 1, ret, from, list.length - to - 1);
+
+ return ret;
+ }
+
+ /**
+ * Removes one atom in the list.
+ * @param list the list to strip
+ * @param i the index of the atom to remove
+ * @return the stripped array
+ */
+ public static Atom[] removeOne(Atom[] list, int i) {
+ return removeSome(list, i, i);
+ }
+
+ /**
+ * Removes the first atom in the list.
+ * @param list the list to strip
+ * @return the stripped array
+ */
+ public static Atom[] removeFirst(Atom[] list) {
+ return removeFirst(list, 1);
+ }
+
+ /**
+ * Removes the first howmany
atoms in the list.
+ * @param list the list to strip
+ * @param howmany how many element to remove
+ * @return the stripped array
+ */
+ public static Atom[] removeFirst(Atom[] list, int howmany) {
+ return removeSome(list, 0, howmany-1);
+ }
+
+ /**
+ * Remove the last atom in the list.
+ * @param list the list to strip
+ * @return the stripped array
+ */
+ public static Atom[] removeLast(Atom[] list) {
+ return removeLast(list, 1);
+ }
+
+ /**
+ * Removes the last howmany
atoms in the list.
+ * @param list the list to strip
+ * @param howmany how many element to remove
+ * @return the stripped array
+ */
+ public static Atom[] removeLast(Atom[] list, int howmany) {
+ return removeSome(list, list.length-howmany, list.length-1);
+ }
+
+ /**
+ * Reverses the element content; the first element is the last and so on.
+ * @param list the list to reverse
+ * @return the reversed list
+ */
+ public static Atom[] reverse(Atom[] list) {
+ Atom[] ret = new Atom[list.length];
+ int last = list.length - 1;
+ for(int i=0;i
+ *
+ * class Myclass {
+ * public void doit() {
+ * do_something_fun()
+ * }
+ * }
+ *
+ * ...
+ *
+ * Myclass myclass = new Myclass();
+ * Executable e = new Callback(myclass, "doit");
+ * MaxClock clock = new MaxClock(e);
+ *
+ *
+ */ +public class MaxClock { + private Executable exec; + private long _clock_ptr; + + /** + * Creates a pdclock without an executable. + */ + public MaxClock() { + create_clock(); + } + + /** + * Creates a pdclock with an executable+ * + * import com.cycling74.max.*; + * + * class clocktest extends MaxObject implements Executable { + * MaxClock clock; + * float value; + * + * public clocktest() { + * clock = new MaxClock(this); + * } + * + * public void inlet(float f) { + * value = f; + * // ask to call execute after 250ms + * clock.delay(250); + * } + * + * // this is called after 250ms + * public void execute() { + * outlet(0, value) + * } + * } + *
e
.
+ * @param e the executable to execute when the clock will be triggerd.
+ */
+ public MaxClock(Executable e) {
+ create_clock();
+ exec = e;
+ }
+
+ /**
+ * Creates a pdclock with a specific method on a object.
+ * @param o the object that holds the method
+ * @param methodName the name of the method to execute when the clock
+ * will be triggerd.
+ */
+ public MaxClock(Object o, String methodName) {
+ create_clock();
+ exec = new Callback(o, methodName);
+ }
+
+ /**
+ * Returns the Executable for this clock.
+ * @return the Executable for this clock
+ */
+ public Executable getExecutable() {
+ return exec;
+ }
+
+ /**
+ * Set the Executable for this clock.
+ * @param e the Executable to call for this clock
+ */
+ public void setExecutable(Executable e) {
+ exec = e;
+ }
+
+ /**
+ * The method to override if no Executable is provided.
+ */
+ public void tick() {
+ exec.execute();
+ }
+
+ protected void finalize() throws Throwable {
+ release();
+ super.finalize();
+ }
+
+ /**
+ * Returns pure-data time in milliseconds.
+ * @return pure-data time in milliseconds
+ */
+ public static native double getTime();
+
+ /**
+ * Time to wait until next tick.
+ * @param time in miliseconds
+ */
+ public native void delay(double time);
+
+ /**
+ * Release the clock from pure-data. The clock becomes unless afterwards.
+ */
+ public native void release();
+
+ /**
+ * Cancels the last delay call.
+ */
+ public native void unset();
+
+ private native void create_clock();
+}
diff --git a/src/java/com/cycling74/max/MaxContext.java b/src/java/com/cycling74/max/MaxContext.java
new file mode 100644
index 0000000..e5fae12
--- /dev/null
+++ b/src/java/com/cycling74/max/MaxContext.java
@@ -0,0 +1,21 @@
+
+package com.cycling74.max;
+
+import java.util.Set;
+
+/**
+ * MaxContext holder. Does not do anything usefull on pdj, yet.
+ */
+public class MaxContext {
+ public Set getAllObject() {
+ return null;
+ }
+
+ MaxObject getMaxObject(String name) {
+ return null;
+ }
+
+ String getMxjVersion() {
+ return MaxSystem.MXJ_VERSION;
+ }
+}
diff --git a/src/java/com/cycling74/max/MaxObject.java b/src/java/com/cycling74/max/MaxObject.java
new file mode 100644
index 0000000..3d07d49
--- /dev/null
+++ b/src/java/com/cycling74/max/MaxObject.java
@@ -0,0 +1,933 @@
+package com.cycling74.max;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+
+import com.e1.pdj.*;
+import java.util.*;
+import com.cycling74.msp.MSPObject;
+
+/**
+ * Main object to extend to use with pd. The name of this class will
+ * reflect the name of the pdj object when it is instanciated.
+ * Here is a basic guideline for using the MaxObject:
+ *+ *+ * + * import com.cycling74.max.*; + * + * public class example extends MaxObject { + * + * // called when arguments are used in object creation + * public example(Atom args[]) { + * for(int i=0;i<args.length;i++) { + * post("args:" + args[i].toString()); + * } + * } + * + * // this method will be called when a float is sended to the object + * public inlet(float f) { + * post("hello float:" + f); + * outlet(0, f); + * } + * + * // this method will be called when symbol callme is sended to the object + * public callme(Atom args[]) { + * post("hello object:" + args[0]); + * outlet(0, args[0]); + * } + * + * // this method will be called when bang is sended to the object + * public bang() { + * post("hello bang"); + * outletBang(0); + * } + * } + *
Compile this class by adding pdj.jar on the classpath and put the + * example.class in the classes directory found in your pdj home. + * You can also edit directly this class in the classes directory + * and pdj will try to compile it for you.
+ */ +public class MaxObject { + /** + * Native C pdj object pointer. + */ + private long _pdobj_ptr; + + /** + * Native C inlet pointers. + */ + private long _outlets_ptr[]; + + /** + * Native C outlets pointers. + */ + private long _inlets_ptr[]; + + /** + * The last inlet that received a message. + */ + private int _activity_inlet; + private String name; + private HashMap attributes = new HashMap(); + private boolean toCreateInfoOutlet = true; + private MaxPatcher patch = new MaxPatcher(); + + // useless statics.... + /** + * Use this to declare that your object has no inlets. + */ + public static final int[] NO_INLETS = {}; + + /** + * Use this to declare that your object has no outlets. + */ + public static final int[] NO_OUTLETS = {}; + + /** + * Defined in the original MXJ API; don't know what it is used for... + */ + public static final String[] EMPTY_STRING_ARRAY = {}; + + /** + * Default constructor for MaxObject. You can add an constructor that + * supports Atom[] to read object creation arguments. It is not defined + * in this API since it does not to force a 'super()' call in all the + * extended class. + */ + protected MaxObject() { + synchronized (MaxObject.class) { + _pdobj_ptr = popPdjPointer(); + } + name = this.getClass().getName(); + patch.patchPath = getPatchPath(); + } + + /** + * Will show an error message in the pure-data console. + * @param message the string to show + */ + public static void error(String message) { + MaxSystem.error(message); + } + + /** + * Will show an info message in the pure-data console. + * @param message the string to show + */ + public static void post(String message) { + MaxSystem.post(message); + } + + /** + * Will show an error message in the pure-data console and crash + * pure-data. Do not use if you have no friend left. + * @param message the message to show when you make pd crash + */ + public static void ouch(String message) { + MaxSystem.ouch(message); + } + + /** + * Show the exeception in the pure-data console. + * @param t the exception it self + */ + public static void showException(Throwable t) { + t.printStackTrace(PDJSystem.err); + } + + /** + * Show the exception in the pure-data console with a message. + * @param message the message that comes with the exception + * @param t the exception it self + */ + public static void showException(String message, Throwable t) { + PDJSystem.err.println(message); + t.printStackTrace(PDJSystem.err); + } + + /** + * Returns the object name. + * @return the pdj object name + */ + public String getName() { + return name; + } + + /** + * Sets the object name. + * @param name pdj object name + */ + public void setName(String name) { + this.name = name; + } + + /** + * This method is called when the object is deleted by the user. + */ + public void notifyDeleted() { + } + + /** + * Bail will throw an exception upon object instanciation. This is usefull + * if you have a missing requirement and you want to cancel object creation. + * @param errormsg the error message to show + */ + protected static void bail(String errormsg) { + throw new PDJError(errormsg); + } + + /** + * Declare the inlets used by this object. + * @see com.cycling74.max.DataTypes + * @param types the type of message that this inlet will use. + */ + protected void declareInlets(int[] types) { + if ( _inlets_ptr != null ) { + throw new IllegalStateException(name + ": inlets already defined"); + } + _inlets_ptr = new long[types.length]; + + int pos = 0; + for(int i=0; ioutlet(int, float)
otherwise outlet(int, String)
+ * @param outlet the outlet number to use
+ * @param value the atom value to send
+ * @return always true, since PD API returns void
+ */
+ public final boolean outlet(int outlet, Atom value) {
+ if ( value.isFloat() || value.isInt() ) {
+ doOutletFloat(_outlets_ptr[outlet], value.toFloat());
+ } else {
+ doOutletSymbol(_outlets_ptr[outlet], value.toString());
+ }
+ return true;
+ }
+
+ /**
+ * Sends atoms to outlet x. If the array contains only one item,
+ * outlet(outlet, Atom value)
will be called. If the
+ * first element of the array is a float/int, list will be
+ * appended to the message. Otherwise, the first atom will be the
+ * message and the rest of it the arguments.
+ * @param outlet the outlet number to use
+ * @param value the arguments
+ * @return true or false if atom[] is empty
+ */
+ public final boolean outlet(int outlet, Atom[] value) {
+ if ( value.length == 0 )
+ return false;
+
+ if ( value.length == 1 ) {
+ outlet(outlet, value[0]);
+ } else {
+ doOutletAnything(_outlets_ptr[outlet], null, value);
+ }
+ return true;
+ }
+
+ // user methods
+ /////////////////////////////////////////////////////////////
+ /**
+ * Called by PD when pdj receives a bang from an inlet. Use
+ * getInlet()
to know which inlet has received the message.
+ */
+ protected void bang() {
+ }
+
+ /**
+ * This will be called if pd sends a float and the float method is not
+ * overridden. Use getInlet()
to know which inlet has
+ * received the message.
+ * @param i int value
+ */
+ protected void inlet(int i) {
+ }
+
+ /**
+ * Called by PD when pdj receives a float from an inlet. Use
+ * getInlet()
to know which inlet has received the message.
+ * @param f float value received from the inlet
+ */
+ protected void inlet(float f) {
+ }
+
+ /**
+ * Called by PD when pdj receives a list of atoms. Use
+ * getInlet()
to know which inlet has received the message.
+ * @param args the list
+ */
+ protected void list(Atom args[]) {
+ }
+
+ /**
+ * Called by PD when pdj receives an un-overriden method. If you need
+ * to catch all messages, override this method. Use getInlet()
+ * to know which inlet has received the message.
+ * @param symbol first atom symbol representation
+ * @param args the arguments of the message
+ */
+ protected void anything(String symbol, Atom[] args) {
+ post("pdj: object '" + name + "' doesn't understand " + symbol);
+ }
+
+ // compatibility methods
+ /////////////////////////////////////////////////////////////
+ public final boolean outletBangHigh(int outlet) {
+ return outletBang(outlet);
+ }
+
+ public final boolean outletHigh(int outlet, int value) {
+ return outlet(outlet, value);
+ }
+
+ public final boolean outletHigh(int outlet, float value) {
+ return outlet(outlet, value);
+ }
+
+ public final boolean outletHigh(int outlet, double value) {
+ return outlet(outlet, value);
+ }
+
+ public final boolean outletHigh(int outlet, String value) {
+ return outlet(outlet, value);
+ }
+
+ public final boolean outletHigh(int outlet, String msg, Atom[] args) {
+ return outlet(outlet, msg, args);
+ }
+
+ public final boolean outletHigh(int outlet, Atom[] value) {
+ return outlet(outlet, value);
+ }
+
+ /**
+ * Returns the output stream of PDJ.
+ * @return the PrintStream output stream of PDJ
+ */
+ public static com.cycling74.io.PostStream getPostStream() {
+ return new com.cycling74.io.PostStream();
+ }
+
+ /**
+ * Returns the error stream of PDJ.
+ * @return the PrintStream error stream of PDJ
+ */
+ public static com.cycling74.io.ErrorStream getErrorStream() {
+ return new com.cycling74.io.ErrorStream();
+ }
+
+ // unimplementable methods
+ /////////////////////////////////////////////////////////////
+ /**
+ * NOT USED IN PD.
+ */
+ public void viewsource() {
+ }
+
+ /**
+ * NOT USED IN PD. Throws UnsupportdOperationException
+ */
+ public static Object getContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the object representing the pd patch.
+ */
+ public MaxPatcher getParentPatcher() {
+ return patch;
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void save() {
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void setInletAssist(String[] messages) {
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void setInletAssist(int index, String message) {
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void setOutletAssist(String[] messages) {
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void setOutletAssist(int index, String message) {
+ }
+
+ /**
+ * NOT USED IN PD.
+ */
+ protected void embedMessage(String msg, Atom[] args) {
+ }
+
+ /**
+ * Useless, but in the original API.
+ */
+ public void gc() {
+ System.gc();
+ }
+
+ /**
+ * Refresh/reinitialize the PDJ classloader.
+ */
+ public void zap() {
+ PDJClassLoader.resetClassloader();
+ }
+
+ // not from original class
+ /////////////////////////////////////////////////////////////
+
+ /**
+ * Tries to get the attribute name[0] and send its value to
+ * the last outlet.
+ * @param name the name of the attribute
+ */
+ void get(Atom[] name) throws IllegalAccessException, InvocationTargetException {
+ String attrName = name[0].toString();
+ if ( attributes.containsKey(attrName) ) {
+ Attribute attr = (Attribute) attributes.get(attrName);
+ outlet(_outlets_ptr.length-1, attr.get());
+ } else {
+ error(this.name + ": attribute not defined '" + attrName + "'");
+ }
+ }
+
+ /**
+ * Called by pdj to check if it is possible to set value x.
+ * @param the name of the setter
+ * @param arg [0] the value to set
+ * @return true if the setter has been set
+ */
+ private boolean _trySetter(String name, Atom[] arg) {
+ if ( !attributes.containsKey(name) )
+ return false;
+ Attribute attr = (Attribute) attributes.get(name);
+ attr.set(arg);
+ return true;
+ }
+
+ /**
+ * Tries to instanciate a MaxObject.
+ * @param name fq java name
+ * @param _pdobj_ptr C pointer to pd object
+ * @param args objects arguments
+ */
+ static synchronized MaxObject registerObject(String name, long _pdobj_ptr, Atom[] args_complete) {
+ try {
+ Class clz = PDJClassLoader.dynamicResolv(name);
+ MaxObject obj = null;
+
+ // map arguments and attributes
+ List largs = new ArrayList();
+ List lattr = new ArrayList();
+
+ for (int i=1;iset
+ * will trigger/execute the "job". If set
is called while
+ * the "job" is running, the "job" won't be called again.
+ * On PDJ, a Qelem is simply a Java thread that wait to be triggered. + *
+ */ +public class MaxQelem { + Thread job; + Executable exec; + boolean incall; + static int nbQelem = 0; + boolean stopThread = true; + + /** + * Constructs a Qelem that is bound the overriden class with method + * name qfn. + */ + public MaxQelem() { + exec = new Callback(this, "qfn"); + do_init(); + } + + /** + * Constructs a Qelem that is bound to an executable. + * @param exec the executable to run + */ + public MaxQelem(Executable exec) { + this.exec = exec; + do_init(); + } + + /** + * Constructs a Qelem that is bound to a class with method name. + * @param src the object that contains the method + * @param method the method to execute + */ + public MaxQelem(Object src, String method) { + exec = new Callback(src, method); + do_init(); + } + + private void do_init() { + job = new Thread(new Dispatcher(), "MaxQelem#" + (++nbQelem)); + job.setPriority(Thread.MIN_PRIORITY); + } + + /** + * Puts thread in front execution. Does nothing on PDJ + */ + public void front() { + } + + /** + * The callback method, otherwise it calls the 'executable' method + * provided in constructor. + */ + public void qfn() { + } + + /** + * Ask qfn to execute. If it is already set, the qfn won't be called + * twice. + */ + public synchronized void set() { + if ( !incall ) { + job.notify(); + } + } + + /** + * Cancels execution of qelem. Use with caution since it will throw + * an InterruptedException on PDJ. + */ + public synchronized void unset() { + job.interrupt(); + } + + + /** + * Releases current Qelem and cancel the running thread. + */ + public void release() { + unset(); + stopThread = true; + } + + /** + * Returns the executable object. + * @return the executble object that is bound to this Qelem + */ + public Executable getExecutable() { + return exec; + } + + class Dispatcher implements Runnable { + public void run() { + while( !stopThread ) { + try { + incall = false; + job.wait(); + incall = true; + exec.execute(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } +} diff --git a/src/java/com/cycling74/max/MaxRuntimeException.java b/src/java/com/cycling74/max/MaxRuntimeException.java new file mode 100644 index 0000000..9909a5c --- /dev/null +++ b/src/java/com/cycling74/max/MaxRuntimeException.java @@ -0,0 +1,15 @@ +package com.cycling74.max; + +/** + * API based runtime exception with Max/PDJ. + */ +public class MaxRuntimeException extends RuntimeException { + + public MaxRuntimeException(String msg) { + super(msg); + } + + public MaxRuntimeException() { + } + +} diff --git a/src/java/com/cycling74/max/MaxSystem.java b/src/java/com/cycling74/max/MaxSystem.java new file mode 100644 index 0000000..3a29540 --- /dev/null +++ b/src/java/com/cycling74/max/MaxSystem.java @@ -0,0 +1,186 @@ +package com.cycling74.max; + +import com.e1.pdj.*; + +/** + * MXJ System utilities. + */ +public class MaxSystem { + static private PriorityQueue low = new PriorityQueue(Thread.MIN_PRIORITY); + + /** + * Shows a message to the pd console + * @param message the string to show + */ + static native public void post(String message); + + /** + * Shows a error message to the pd console + * @param message the string to show + */ + static native public void error(String message); + + /** + * Shows a message in the pd console and kill PD afterwards. + * @param message the string to show + */ + static native public void ouch(String message); + + /** + * Sends a message to a bound object (IEM object or a receiver) + * @param name the destination of the message + * @param msg the symbol message ("bang", "float", "list") + * @param args the array of Atoms + * @return true if successfull + */ + static native public boolean sendMessageToBoundObject(String name, String msg, Atom[] args); + + /** + * Tries to locate file in pure-data search path. + * @param filename of the file to search in path + * @return the full path of this file + */ + static native public String locateFile(String filename); + + /** + * Will schedule the executable to a low priority thread + * @param fn the executable + */ + static synchronized public void deferLow(Executable fn) { + low.defer(fn); + } + + /** + * Will schedule the executable to a medium priority thread + * @param fn the executable + */ + static synchronized public void deferMedium(Executable fn) { + low.defer(fn); + } + + /** + * Returns the user classpath. + * @return Array of strings of each entries in the user classpath + */ + static public String[] getClassPath() { + return PDJClassLoader.getCurrentClassPath(); + } + + // implemented but not fully supported... + static public void defer(Executable fn) { + fn.execute(); + } + static public void deferFront(Executable fn) { + fn.execute(); + } + + static public String[] getSystemClassPath() { + return null; + } + + static boolean inMainThread() { + return false; + } + + static boolean inMaxThread() { + return false; + } + + static boolean inTimerThread() { + return false; + } + + static boolean isOsMacOsX() { + String osname = System.getProperty("os.name"); + if ( osname.indexOf("OS X") != -1 ) { + return true; + } + return false; + } + + static boolean isOsWindows() { + String osname = System.getProperty("os.name"); + if ( osname.indexOf("Windows") != -1 ) { + return true; + } + return false; + } + + /** + * Not supported in PD + */ + static void registerCommandAccelerator(char c) { + } + + /** + * Not supported in PD + */ + static void registerCommandAccelerators(char c[]) { + } + + /** + * Not supported in PD + */ + static void unRegisterCommandAccelerator(char c) { + } + /** + * Not supported in PD + */ + static void unRegisterCommandAccelerators(char c[]) { + } + + // not compatible Max methods : + //////////////////////////////////////////////////////// + /** + * Not supported in PD + */ + static public boolean isStandAlone() { + return false; + } + + static public short getMaxVersion() { + return 0; + } + + static public int[] getMaxVersionInts() { + int ret[] = new int[3]; + + ret[0] = 0; + ret[1] = 99; + ret[2] = 0; + + return ret; + } + + /** + * Not supported in PD + */ + static public void hideCursor() { + } + + /** + * Not supported in PD + */ + static public void showCursor() { + } + + /** + * Not supported in PD + */ + static public void nextWindowIsModal() { + } + + // constants + public static String MXJ_VERSION = "pdj 0.8.3"; + + public static final int PATH_STYLE_COLON = 2; + public static final int PATH_STYLE_MAX = 0; + public static final int PATH_STYLE_NATIVE = 1; + public static final int PATH_STYLE_NATIVE_WIN = 4; + public static final int PATH_STYLE_SLASH = 3; + public static final int PATH_TYPE_ABSOLUTE = 1; + public static final int PATH_TYPE_BOOT = 3; + public static final int PATH_TYPE_C74 = 4; + public static final int PATH_TYPE_IGNORE = 0; + public static final int PATH_TYPE_RELATIVE = 2; +} diff --git a/src/java/com/cycling74/max/MessageReceiver.java b/src/java/com/cycling74/max/MessageReceiver.java new file mode 100644 index 0000000..3310de0 --- /dev/null +++ b/src/java/com/cycling74/max/MessageReceiver.java @@ -0,0 +1,11 @@ +package com.cycling74.max; + +/** + * This is a utility callback class that can be used to notify background job. + */ +public interface MessageReceiver { + /** + * Called when a event has occured. + */ + public void messageReceived(Object src, int messageId, Object data); +} diff --git a/src/java/com/cycling74/max/package.html b/src/java/com/cycling74/max/package.html new file mode 100644 index 0000000..dcd23c3 --- /dev/null +++ b/src/java/com/cycling74/max/package.html @@ -0,0 +1,5 @@ + + +Basic package for PDJ
+ + \ No newline at end of file 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 not used in pd + * @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 not used in pd + * @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 not used in pd + * @param index the start index of the array + * @return the value stored at indexstart
+ */
+ 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 not used in pd
+ * @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 not used in pd
+ * @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 not used in pd
+ * @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 not used in pd
+ * @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.
+ *
+ * Here is a basic guideline for using the MSPObject:
+ *+ */ +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+ * 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<ins[0].n;i++) { + * outs[0].vec[i] = ins[0].vec[i] * left; + * outs[1].vec[i] = ins[0].vec[i] * right; + * } + * } + * } + *
methodName
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.
+ * NOT USED ON PD.
+ * @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. Note: any signal inlet won't
+ * be able to process any atom messages.
+ */
+ protected void declareInlets(int[] types) {
+ int i, inlets = 0;
+ for (i=0;iPackage for using using array and signals objects.
+ +