From a764e59e1d3a8e330f0d484fdb26b35ca3f0b2e4 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 22 Mar 2008 02:15:12 +0000 Subject: bringing pdj-0.8.3 into the main branch svn path=/trunk/externals/loaders/pdj/; revision=9621 --- src/java/com/cycling74/max/MaxObject.java | 933 ++++++++++++++++++++++++++++++ 1 file changed, 933 insertions(+) create mode 100644 src/java/com/cycling74/max/MaxObject.java (limited to 'src/java/com/cycling74/max/MaxObject.java') 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; iPD doesn't type all data yet, so every + * inlet/outlet are typed as anything; except for signals. + * @param ins list of inlet to create + * @param outs list of outlet to create + */ + protected void declareTypedIO(String ins, String outs) { + if ( _inlets_ptr != null || _outlets_ptr != null ) { + throw new IllegalStateException(name + ": inlets/outles already defined."); + } + + int[] in_type = new int[ins.length()]; + int[] out_type = new int[outs.length()]; + int i; + + for (i=0;iNot fully implemented, + * returns always DataTypes.ANYTHING with pdj + * @param idx the outlet position + * @return the DataType of the inlet + */ + public int getInletType(int idx) { + return DataTypes.ANYTHING; + } + + /** + * Returns the type of the outlet at index 'idx'. Not fully implemented, + * returns always DataTypes.ANYTHING with pdj + * @param idx the outlet position + * @return the DataType of the outlet + */ + public int getOutletType(int idx) { + return DataTypes.ANYTHING; + } + + /** + * Returns the number of inlets declared. + * @return the number of inlets used by this object + */ + public int getNumInlets() { + return _inlets_ptr.length; + } + + /** + * Returns the number of outlets declared. + * @return the number of outlets used by this object + */ + public int getNumOutlets() { + return _outlets_ptr.length; + } + + /** + * Called by PD when the current patch issue a loadbang message. + */ + protected void loadbang() { + } + + /** + * Returns the index of the inlet that has just received a message. + * @return the index of the inlet + */ + protected int getInlet() { + return _activity_inlet; + } + + /** + * Returns the index of the info outlet + */ + public int getInfoIdx() { + if ( !toCreateInfoOutlet ) { + return -1; + } + return _outlets_ptr.length-1; + } + + /** + * Sends a bang to outlet x. + * @param outlet the outlet number to use + * @return always true, since PD API returns void + */ + public final boolean outletBang(int outlet) { + doOutletBang(_outlets_ptr[outlet]); + return true; + } + + /** + * Sends a float to outlet x. + * @param outlet the outlet number to use + * @param value the float value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, float value) { + doOutletFloat(_outlets_ptr[outlet], value); + return true; + } + + /** + * Sends floats to outlet x. + * @param outlet the outlet number to use + * @param value the array of float to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, float value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a symbol to outlet x. + * @param outlet the outlet number to use + * @param value the symbol + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, String value) { + doOutletSymbol(_outlets_ptr[outlet], value); + return true; + } + + /** + * Sends symbols to outlet x. + * @param outlet the outlet number to use + * @param value the array of symbol to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, String value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a byte to outlet x. + * @param outlet the outlet number to use + * @param value the byte value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, byte value) { + return outlet(outlet, (float) value); + } + + /** + * Sends byte to outlet x. + * @param outlet the outlet number to use + * @param value the array of byte to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, byte value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a char to outlet x. + * @param outlet the outlet number to use + * @param value the char value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, char value) { + return outlet(outlet, (float) value); + } + + /** + * Sends char to outlet x. + * @param outlet the outlet number to use + * @param value the array of char to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, char value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a short to outlet x. + * @param outlet the outlet number to use + * @param value the short value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, short value) { + return outlet(outlet, (float) value); + } + + /** + * Sends shorts to outlet x. + * @param outlet the outlet number to use + * @param value the array of short to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, short value[]) { + doOutletAnything(_outlets_ptr[outlet], "list" , Atom.newAtom(value)); + return true; + } + + /** + * Sends a int to outlet x. + * @param outlet the outlet number to use + * @param value the int value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, int value) { + return outlet(outlet, (float) value); + } + + /** + * Sends ints to outlet x. + * @param outlet the outlet number to use + * @param value the array of int to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, int value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a long to outlet x. + * @param outlet the outlet number to use + * @param value the long value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, long value) { + return outlet(outlet, (float) value); + } + + /** + * Sends longs to outlet x. + * @param outlet the outlet number to use + * @param value the array of longs to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, long value[]) { + doOutletAnything(_outlets_ptr[outlet], "list", Atom.newAtom(value)); + return true; + } + + /** + * Sends a double to outlet x. + * @param outlet the outlet number to use + * @param value the double value + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, double value) { + return outlet(outlet, (float) value); + } + + /** + * Sends doubles to outlet x. + * @param outlet the outlet number to use + * @param value the array of double to send + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, double value[]) { + doOutletAnything(_outlets_ptr[outlet], "list" , Atom.newAtom(value)); + return true; + } + + /** + * Sends message with argument to outlet x. + * @param outlet the outlet number to use + * @param message the message symbol name + * @param value the arguments + * @return always true, since PD API returns void + */ + public final boolean outlet(int outlet, String message, Atom[] value) { + doOutletAnything(_outlets_ptr[outlet], message, value); + return true; + } + + /** + * Sends atom value to outlet x. If it is a int/float, it will call + * outlet(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;i=args_complete.length ) { + post("pdj: " + name + ": attribute '" + args_complete[i].toString() + + "' must have a initial value"); + return null; + } + lattr.add(args_complete[++i]); + } else { + largs.add(args_complete[i]); + } + } + + Atom args[] = new Atom[largs.size()]; + for (int i=0;i 0 ) { + try { + Object argValue[] = new Object[1]; + argValue[0] = args; + Constructor c = clz.getConstructor(argType); + obj = (MaxObject) c.newInstance(argValue); + } catch ( NoSuchMethodException e) { + popPdjPointer(); + post("pdj: object " + name + " has no constructor with Atom[] parameters"); + return null; + } + } else { + try { + Constructor c = clz.getConstructor(null); + obj = (MaxObject) c.newInstance(null); + } catch (NoSuchMethodException e) { + try { + Constructor c = clz.getConstructor(argType); + obj = (MaxObject) c.newInstance(new Object[0]); + } catch ( Exception e1 ) { + popPdjPointer(); + throw e1; + } + } + } + + // next we process attributes from the constructor arguments + Iterator i = lattr.iterator(); + while( i.hasNext() ) { + String attrName = (String) i.next(); + obj.declareAttribute(attrName); + Attribute attr = (Attribute) obj.attributes.get(attrName); + attr.set(new Atom[] { (Atom) i.next() }); + } + + obj.name = name; + obj.postInit(); + return obj; + } catch (PDJClassLoaderException e ) { + MaxSystem.post("pdj: " + e.toString()); + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + + /** + * Called after the constructor has been called. Check if the user + * has override the standard inlets/outlets. + */ + private void postInit() { + if ( _inlets_ptr == null ) { + declareInlets(new int[] { DataTypes.ANYTHING }); + } + + if ( _outlets_ptr == null ) { + declareOutlets(new int[] { DataTypes.ANYTHING }); + } + + // add the last/info outlet + if ( toCreateInfoOutlet ) { + long tmp[] = new long[_outlets_ptr.length+1]; + + System.arraycopy(_outlets_ptr, 0, tmp, 0, _outlets_ptr.length); + tmp[_outlets_ptr.length] = newOutlet(DataTypes.ANYTHING); + + _outlets_ptr = tmp; + } + } + + // native party + ///////////////////////////////////////////////////////////// + native private long newInlet(int type); + native private long newOutlet(int type); + + native private void doOutletBang(long ptr); + native private void doOutletFloat(long ptr, float value); + native private void doOutletSymbol(long ptr, String symbol); + native private void doOutletAnything(long ptr, String msg, Atom []args); + + native private String getPatchPath(); + + // ugly ugly... but MaxContext vs this; I prefer this.... + native private static void pushPdjPointer(long ptr); + native private static long popPdjPointer(); +} -- cgit v1.2.1