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/MSPBuffer.c | 159 ++++
src/MaxClock.c | 74 ++
src/MaxObject.c | 131 +++
src/MaxSystem.c | 178 ++++
src/init.c | 349 ++++++++
src/java/com/cycling74/io/ErrorStream.java | 10 +
src/java/com/cycling74/io/PostStream.java | 10 +
src/java/com/cycling74/max/Atom.java | 715 ++++++++++++++++
src/java/com/cycling74/max/AtomFloat.java | 96 +++
src/java/com/cycling74/max/AtomString.java | 63 ++
src/java/com/cycling74/max/Attribute.java | 463 ++++++++++
src/java/com/cycling74/max/Callback.java | 213 +++++
src/java/com/cycling74/max/DataTypes.java | 14 +
src/java/com/cycling74/max/Executable.java | 14 +
src/java/com/cycling74/max/MaxClock.java | 113 +++
src/java/com/cycling74/max/MaxContext.java | 21 +
src/java/com/cycling74/max/MaxObject.java | 933 +++++++++++++++++++++
src/java/com/cycling74/max/MaxPatcher.java | 17 +
src/java/com/cycling74/max/MaxQelem.java | 114 +++
.../com/cycling74/max/MaxRuntimeException.java | 15 +
src/java/com/cycling74/max/MaxSystem.java | 186 ++++
src/java/com/cycling74/max/MessageReceiver.java | 11 +
src/java/com/cycling74/max/package.html | 5 +
src/java/com/cycling74/msp/AudioFileBuffer.java | 116 +++
src/java/com/cycling74/msp/MSPBuffer.java | 127 +++
src/java/com/cycling74/msp/MSPObject.java | 163 ++++
src/java/com/cycling74/msp/MSPPerformable.java | 17 +
src/java/com/cycling74/msp/MSPPerformer.java | 36 +
src/java/com/cycling74/msp/MSPSignal.java | 70 ++
src/java/com/cycling74/msp/package.html | 5 +
src/java/com/e1/pdj/ConsoleStream.java | 35 +
src/java/com/e1/pdj/ConsoleStreamWin32.java | 34 +
src/java/com/e1/pdj/GenericCompiler.java | 71 ++
src/java/com/e1/pdj/JavacCompiler.java | 34 +
src/java/com/e1/pdj/JikesCompiler.java | 27 +
src/java/com/e1/pdj/PDJClassLoader.java | 206 +++++
src/java/com/e1/pdj/PDJClassLoaderException.java | 17 +
src/java/com/e1/pdj/PDJError.java | 15 +
src/java/com/e1/pdj/PDJSystem.java | 142 ++++
src/java/com/e1/pdj/PriorityQueue.java | 68 ++
src/java/com/e1/pdj/test/AtomTest.java | 92 ++
src/java/com/e1/pdj/test/CallbackTest.java | 10 +
src/java/com/e1/pdj/test/MaxQelemTest.java | 7 +
src/java/help_class.java | 26 +
src/java/panner.java | 37 +
src/java/pdj_test_class.java | 72 ++
src/java/pdj_test_tilde.java | 45 +
src/java/pdj_tilde.java | 45 +
src/pdj-linux.c | 91 ++
src/pdj-osx.c | 37 +
src/pdj-win32.c | 107 +++
src/pdj.c | 396 +++++++++
src/pdj.h | 140 ++++
src/pdj~.c | 206 +++++
src/type_handler.c | 151 ++++
src/type_handler.h | 18 +
56 files changed, 6567 insertions(+)
create mode 100644 src/MSPBuffer.c
create mode 100644 src/MaxClock.c
create mode 100644 src/MaxObject.c
create mode 100644 src/MaxSystem.c
create mode 100644 src/init.c
create mode 100644 src/java/com/cycling74/io/ErrorStream.java
create mode 100644 src/java/com/cycling74/io/PostStream.java
create mode 100644 src/java/com/cycling74/max/Atom.java
create mode 100644 src/java/com/cycling74/max/AtomFloat.java
create mode 100644 src/java/com/cycling74/max/AtomString.java
create mode 100644 src/java/com/cycling74/max/Attribute.java
create mode 100644 src/java/com/cycling74/max/Callback.java
create mode 100644 src/java/com/cycling74/max/DataTypes.java
create mode 100644 src/java/com/cycling74/max/Executable.java
create mode 100644 src/java/com/cycling74/max/MaxClock.java
create mode 100644 src/java/com/cycling74/max/MaxContext.java
create mode 100644 src/java/com/cycling74/max/MaxObject.java
create mode 100644 src/java/com/cycling74/max/MaxPatcher.java
create mode 100644 src/java/com/cycling74/max/MaxQelem.java
create mode 100644 src/java/com/cycling74/max/MaxRuntimeException.java
create mode 100644 src/java/com/cycling74/max/MaxSystem.java
create mode 100644 src/java/com/cycling74/max/MessageReceiver.java
create mode 100644 src/java/com/cycling74/max/package.html
create mode 100644 src/java/com/cycling74/msp/AudioFileBuffer.java
create mode 100644 src/java/com/cycling74/msp/MSPBuffer.java
create mode 100644 src/java/com/cycling74/msp/MSPObject.java
create mode 100644 src/java/com/cycling74/msp/MSPPerformable.java
create mode 100644 src/java/com/cycling74/msp/MSPPerformer.java
create mode 100644 src/java/com/cycling74/msp/MSPSignal.java
create mode 100644 src/java/com/cycling74/msp/package.html
create mode 100644 src/java/com/e1/pdj/ConsoleStream.java
create mode 100644 src/java/com/e1/pdj/ConsoleStreamWin32.java
create mode 100644 src/java/com/e1/pdj/GenericCompiler.java
create mode 100644 src/java/com/e1/pdj/JavacCompiler.java
create mode 100644 src/java/com/e1/pdj/JikesCompiler.java
create mode 100644 src/java/com/e1/pdj/PDJClassLoader.java
create mode 100644 src/java/com/e1/pdj/PDJClassLoaderException.java
create mode 100644 src/java/com/e1/pdj/PDJError.java
create mode 100644 src/java/com/e1/pdj/PDJSystem.java
create mode 100644 src/java/com/e1/pdj/PriorityQueue.java
create mode 100644 src/java/com/e1/pdj/test/AtomTest.java
create mode 100644 src/java/com/e1/pdj/test/CallbackTest.java
create mode 100644 src/java/com/e1/pdj/test/MaxQelemTest.java
create mode 100644 src/java/help_class.java
create mode 100644 src/java/panner.java
create mode 100644 src/java/pdj_test_class.java
create mode 100644 src/java/pdj_test_tilde.java
create mode 100644 src/java/pdj_tilde.java
create mode 100644 src/pdj-linux.c
create mode 100644 src/pdj-osx.c
create mode 100644 src/pdj-win32.c
create mode 100644 src/pdj.c
create mode 100644 src/pdj.h
create mode 100644 src/pdj~.c
create mode 100644 src/type_handler.c
create mode 100644 src/type_handler.h
(limited to 'src')
diff --git a/src/MSPBuffer.c b/src/MSPBuffer.c
new file mode 100644
index 0000000..084fdf3
--- /dev/null
+++ b/src/MSPBuffer.c
@@ -0,0 +1,159 @@
+#include "native_classes.h"
+#include "type_handler.h"
+#include "pdj.h"
+
+
+static t_garray *getArray(JNIEnv *env, jstring name) {
+ t_symbol *s = jstring2symbol(env, name);
+ t_garray *ret = (t_garray *)pd_findbyclass(s, garray_class);
+
+ if ( ret == NULL ) {
+ post("pdj: array %s not found", s->s_name);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+JNIEXPORT jlong JNICALL Java_com_cycling74_msp_MSPBuffer_getSize
+ (JNIEnv *env, jclass cls, jstring name) {
+ t_garray *array = getArray(env, name);
+
+ if ( array == NULL )
+ return -1;
+
+ return garray_npoints(array);
+}
+
+
+JNIEXPORT jfloatArray JNICALL Java_com_cycling74_msp_MSPBuffer_peek
+ (JNIEnv *env , jclass cls, jstring name) {
+ t_garray *array = getArray(env, name);
+ t_sample *vec;
+ jfloatArray ret;
+ int size;
+
+ if ( array == NULL )
+ return NULL;
+
+ if ( ! garray_getfloatarray(array, &size, &vec) )
+ return NULL;
+
+ ret = (*env)->NewFloatArray(env, size);
+
+ if ( ret == NULL ) {
+ SHOWEXC;
+ return NULL;
+ }
+
+ (*env)->SetFloatArrayRegion(env, ret, 0, size, (jfloat *) vec);
+
+ return ret;
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_msp_MSPBuffer_poke
+ (JNIEnv *env, jclass cls, jstring name, jfloatArray values) {
+ t_garray *array = getArray(env, name);
+ t_sample *vec;
+ jsize jarray_length;
+ int size;
+
+ if ( array == NULL )
+ return;
+
+ if ( ! garray_getfloatarray(array, &size, &vec) )
+ return;
+
+ jarray_length = (*env)->GetArrayLength(env, values);
+
+ if ( jarray_length < size ) {
+ error("pdj: warning: array too short");
+ } else {
+ size = jarray_length;
+ }
+
+ (*env)->GetFloatArrayRegion(env, values, 0, size, vec);
+ garray_redraw(array);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_msp_MSPBuffer_setSize
+ (JNIEnv *env, jclass cls, jstring name, jint channel, jlong size) {
+ t_garray *array = getArray(env, name);
+
+ if ( array == NULL )
+ return;
+
+ garray_resize(array, size);
+}
+
+
+JNIEXPORT jfloatArray JNICALL Java_com_cycling74_msp_MSPBuffer_getArray
+ (JNIEnv *env, jclass cls, jstring name, jlong from, jlong arraySize) {
+ t_garray *array = getArray(env, name);
+ t_sample *vec;
+ jfloatArray ret;
+ int size, ifrom = (int) from, iarraySize = (int) arraySize;
+
+ if ( array == NULL )
+ return NULL;
+
+ if ( ! garray_getfloatarray(array, &size, &vec) )
+ return NULL;
+
+ if ( iarraySize != -1 ) {
+ if ( size < ifrom ) {
+ error("pdj: array is shorter than the starting point");
+ return NULL;
+ }
+
+ if ( size < ifrom + iarraySize ) {
+ error("pdj: array is not big enough to fill the array");
+ return NULL;
+ }
+ size = iarraySize;
+ }
+
+ ret = (*env)->NewFloatArray(env, size);
+ if ( ret == NULL ) {
+ SHOWEXC;
+ return NULL;
+ }
+
+ vec += ifrom;
+ (*env)->SetFloatArrayRegion(env, ret, 0, size, (jfloat *) vec);
+
+ return ret;
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_msp_MSPBuffer_setArray
+ (JNIEnv *env, jclass cls, jstring name, jlong from, jfloatArray values) {
+ t_garray *array = getArray(env, name);
+ t_sample *vec;
+ jsize jarray_length;
+ int size, ifrom = from;
+
+ if ( array == NULL )
+ return;
+
+ if ( ! garray_getfloatarray(array, &size, &vec) )
+ return;
+
+ if ( size < from ) {
+ error("pdj: array too short from starting point");
+ return;
+ }
+ jarray_length = (*env)->GetArrayLength(env, values);
+
+ if ( jarray_length + ifrom > size ) {
+ error("pdj: warning: array too short from java array size");
+ return;
+ }
+
+ vec += from;
+ (*env)->GetFloatArrayRegion(env, values, 0, jarray_length, vec);
+ garray_redraw(array);
+}
diff --git a/src/MaxClock.c b/src/MaxClock.c
new file mode 100644
index 0000000..e7e5886
--- /dev/null
+++ b/src/MaxClock.c
@@ -0,0 +1,74 @@
+#include
+#include "native_classes.h"
+#include "type_handler.h"
+#include "pdj.h"
+
+typedef struct _clockCtnr {
+ jobject instance;
+ jmethodID tick;
+ t_clock *pd_clock;
+} t_clockCtnr;
+
+
+static t_clockCtnr *getClock(JNIEnv *env, jobject obj) {
+ return (t_clockCtnr *) (*env)->GetLongField(env, obj, pdjCaching.FIDMaxClock_clock_ptr);
+}
+
+
+void clock_callback(t_clockCtnr *clk) {
+ JNIEnv *env = pdjAttachVM();
+ JASSERT(clk->instance);
+
+ (*env)->CallVoidMethod(env, clk->instance , clk->tick, NULL);
+ pdjDetachVM(env);
+}
+
+
+JNIEXPORT jdouble JNICALL Java_com_cycling74_max_MaxClock_getTime
+ (JNIEnv *env, jclass cls) {
+ return sys_getrealtime() * 1000;
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxClock_create_1clock
+ (JNIEnv *env, jobject obj) {
+ jclass cls = (*env)->GetObjectClass(env, obj);
+ t_clockCtnr *clk;
+
+ clk = malloc(sizeof(t_clockCtnr));
+ ASSERT(clk);
+
+ clk->pd_clock = clock_new(clk, (t_method) clock_callback);
+ (*env)->SetLongField(env, obj, pdjCaching.FIDMaxClock_clock_ptr, (long) clk);
+
+ clk->instance = (*env)->NewGlobalRef(env, obj);
+ JASSERT(clk->instance);
+ clk->tick = (*env)->GetMethodID(env, cls, "tick", "()V");
+ JASSERT(clk->tick);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxClock_delay
+ (JNIEnv *env, jobject obj, jdouble value) {
+ t_clockCtnr *clk = getClock(env, obj);
+ clock_delay(clk->pd_clock, value);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxClock_unset
+ (JNIEnv *env, jobject obj) {
+ t_clockCtnr *clk = getClock(env, obj);
+ clock_unset(clk->pd_clock);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxClock_release
+ (JNIEnv *env, jobject obj) {
+ t_clockCtnr *clk = getClock(env, obj);
+ clock_unset(clk->pd_clock);
+ clock_free(clk->pd_clock);
+ free(clk);
+
+ (*env)->SetObjectField(env, obj, pdjCaching.FIDMaxClock_clock_ptr, 0);
+ (*env)->DeleteGlobalRef(env, obj);
+}
diff --git a/src/MaxObject.c b/src/MaxObject.c
new file mode 100644
index 0000000..3c77f49
--- /dev/null
+++ b/src/MaxObject.c
@@ -0,0 +1,131 @@
+#include "native_classes.h"
+#include "type_handler.h"
+#include "pdj.h"
+
+
+static t_pdj *getMaxObject(JNIEnv *env, jobject obj) {
+ t_pdj *ret = (t_pdj *) (*env)->GetLongField(env, obj,
+ pdjCaching.FIDMaxObject_pdobj_ptr);
+
+ if ( ret == NULL )
+ error("pdj: using a native method without pd context");
+
+ return ret;
+}
+
+
+JNIEXPORT jlong JNICALL Java_com_cycling74_max_MaxObject_newInlet
+ (JNIEnv *env, jobject obj, jint type) {
+ t_pdj *pdj = getMaxObject(env, obj);
+ t_inlet_proxy *proxy;
+
+ if ( pdj == NULL )
+ return 0;
+ if ( type == com_cycling74_msp_MSPObject_SIGNAL ) {
+ inlet_new(&pdj->x_obj, &pdj->x_obj.ob_pd, &s_signal, 0);
+ return 0;
+ }
+
+ proxy = (t_inlet_proxy *) pd_new(inlet_proxy);
+ pdj->nb_inlet++;
+ proxy->idx = pdj->nb_inlet;
+ proxy->peer = pdj;
+
+ return (jlong) inlet_new(&pdj->x_obj, &proxy->x_obj.ob_pd, 0, 0);
+}
+
+
+JNIEXPORT jlong JNICALL Java_com_cycling74_max_MaxObject_newOutlet
+ (JNIEnv *env, jobject obj, jint type) {
+ t_pdj *pdj = getMaxObject(env, obj);
+
+ if ( pdj == NULL )
+ return 0;
+
+ if ( type == com_cycling74_msp_MSPObject_SIGNAL ) {
+ outlet_new(&pdj->x_obj, &s_signal);
+ return 0;
+ }
+
+ return (jlong) outlet_new(&pdj->x_obj, NULL);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxObject_doOutletBang
+ (JNIEnv *env, jobject obj, jlong outlet) {
+ t_outlet *x = (t_outlet *) ((unsigned int) outlet);
+ outlet_bang(x);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxObject_doOutletFloat
+ (JNIEnv *env, jobject obj, jlong outlet , jfloat value) {
+ t_outlet *x = (t_outlet *) ((unsigned int) outlet);
+ outlet_float(x, value);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxObject_doOutletSymbol
+ (JNIEnv *env, jobject obj, jlong outlet, jstring value) {
+ t_outlet *x = (t_outlet *) ((unsigned int) outlet);
+ outlet_symbol(x, jstring2symbol(env, value));
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxObject_doOutletAnything
+ (JNIEnv *env, jobject obj, jlong outlet, jstring str, jobjectArray value) {
+ t_outlet *x = (t_outlet *) ((unsigned int) outlet);
+ t_atom args[MAX_ATOMS_STACK];
+ int argc;
+
+ jatoms2atoms(env, value, &argc, args);
+ if ( str == NULL ) {
+ if ( args[0].a_type == A_FLOAT ) {
+ outlet_anything(x, &s_list, argc, args);
+ } else {
+ t_symbol *sym = atom_getsymbol(&(args[0]));
+ outlet_anything(x, sym, argc-1, args+1);
+ }
+ } else {
+ outlet_anything(x, jstring2symbol(env, str), argc, args);
+ }
+}
+
+JNIEXPORT jstring JNICALL Java_com_cycling74_max_MaxObject_getPatchPath
+ (JNIEnv *env, jobject obj) {
+ t_pdj *pdj = getMaxObject(env, obj);
+
+ if ( pdj == NULL )
+ return NULL;
+
+ return (*env)->NewStringUTF(env, pdj->patch_path);
+
+}
+
+// UGLY UGLY UGLY, but this is used not force the user from using
+// a constructor. MaxObject CAN be used outside the pdj object
+// but all the natives calls will break. Theses methods are always called
+// within a synchronized() block. This is ugly, but less ugly than
+// putting the information in a ThreadLocals? ... or using a MaxContext ?
+// ===========================================================================
+static t_pdj *pdjConstructor = NULL;
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxObject_pushPdjPointer
+ (JNIEnv *env, jclass cls, jlong ptr) {
+ pdjConstructor = (t_pdj *) ptr;
+}
+
+
+JNIEXPORT jlong JNICALL Java_com_cycling74_max_MaxObject_popPdjPointer
+ (JNIEnv *env, jclass cls) {
+ t_pdj *tmp;
+
+ // the object has been used instanciated without pdj. Return a null
+ // pointer.
+ if ( pdjConstructor == NULL ) {
+ return 0;
+ }
+ tmp = pdjConstructor;
+ pdjConstructor = NULL;
+
+ return tmp;
+}
diff --git a/src/MaxSystem.c b/src/MaxSystem.c
new file mode 100644
index 0000000..c506fa2
--- /dev/null
+++ b/src/MaxSystem.c
@@ -0,0 +1,178 @@
+#include "native_classes.h"
+#include "type_handler.h"
+#include "pdj.h"
+#include
+#include
+#include
+
+#ifdef MSW
+ #include
+#else
+ #include
+#endif
+
+/* from m_imp.h... todo: ask Miller ??? */
+EXTERN void outlet_setstacklim(void);
+
+
+/**
+ * Using a { or } in a post or error will lock PD, we substitute
+ * them with a ( and ).
+ */
+static char* removePdAcc(char *str, jboolean isCopy) {
+ char *work;
+
+ if ( isCopy == JNI_TRUE ) {
+ work = str;
+ while(*work != 0) {
+ switch(*work) {
+ case '{' :
+ *work = '(';
+ break;
+ case '}' :
+ *work = ')';
+ break;
+ }
+ work++;
+ }
+ return str;
+ }
+
+ work = malloc(strlen(str)+1);
+ while(*str != 0) {
+ switch(*str) {
+ case '{' :
+ *work = '(';
+ break;
+ case '}' :
+ *work = ')';
+ break;
+ default:
+ *work = *str;
+ }
+ work++;
+ str++;
+ }
+
+ *work = 0;
+ return work;
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxSystem_error
+ (JNIEnv *env, jclass cls, jstring message) {
+ jboolean isCopy;
+
+ char *msg = (*env)->GetStringUTFChars(env, message, &isCopy);
+ msg = removePdAcc(msg, isCopy);
+
+ if ( REDIRECT_PD_IO ) {
+ error(msg);
+ } else {
+ fprintf(stderr, msg);
+ fprintf(stderr, "\n");
+ }
+
+ if ( isCopy == JNI_FALSE )
+ free(msg);
+
+ (*env)->ReleaseStringUTFChars(env, message, msg);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxSystem_post
+ (JNIEnv *env, jclass cls, jstring message) {
+ jboolean isCopy;
+
+ char *msg = (*env)->GetStringUTFChars(env, message, &isCopy);
+ msg = removePdAcc(msg, isCopy);
+
+ if ( REDIRECT_PD_IO ) {
+ post(msg);
+ } else {
+ fprintf(stdout, msg);
+ fprintf(stdout, "\n");
+ }
+
+ if ( isCopy == JNI_FALSE )
+ free(msg);
+
+ (*env)->ReleaseStringUTFChars(env, message, msg);
+}
+
+
+JNIEXPORT void JNICALL Java_com_cycling74_max_MaxSystem_ouch
+ (JNIEnv *env, jclass cls, jstring message) {
+ jboolean isCopy;
+
+ char *msg = (*env)->GetStringUTFChars(env, message, &isCopy);
+ fprintf(stderr, msg);
+ fprintf(stderr, "\n");
+ msg = removePdAcc(msg, isCopy);
+ bug(msg);
+
+ if ( isCopy == JNI_FALSE )
+ free(msg);
+
+ (*env)->ReleaseStringUTFChars(env, message, msg);
+}
+
+
+JNIEXPORT jboolean JNICALL Java_com_cycling74_max_MaxSystem_sendMessageToBoundObject
+ (JNIEnv *env, jclass cls, jstring jname, jstring jmsg, jobjectArray jatoms) {
+ t_symbol *name = jstring2symbol(env, jname);
+ t_symbol *msg = jstring2symbol(env, jmsg);
+ t_pd *dest = findPDObject(name);
+ t_atom argv[MAX_ATOMS_STACK];
+ int argc;
+
+ if ( dest == NULL ) {
+ post("pdj: unable to get object %s for sendMessageToBoundObject", name->s_name);
+ return 0;
+ }
+
+ /* reset the stack pointer for pd events */
+ outlet_setstacklim();
+
+ if ( msg == &s_bang ) {
+ pd_bang(dest);
+ return 1;
+ }
+
+ if ( jatoms == NULL ) {
+ pd_symbol(dest, msg);
+ return 1;
+ }
+
+ jatoms2atoms(env, jatoms, &argc, argv);
+
+ if ( msg == &s_float ) {
+ pd_float(dest, atom_getfloatarg(0, argc, argv));
+ return 1;
+ }
+
+ pd_list(dest, msg, argc, argv);
+ return 1;
+}
+
+
+JNIEXPORT jstring JNICALL Java_com_cycling74_max_MaxSystem_locateFile
+ (JNIEnv *env, jclass cls, jstring filename) {
+ const jbyte *file = (*env)->GetStringUTFChars(env, filename, NULL);
+ char fullpath[MAXPDSTRING], *pfullpath;
+ FILE *fd;
+
+ fd = open_via_path("", file, "", fullpath, &pfullpath, MAXPDSTRING, 0);
+ (*env)->ReleaseStringUTFChars(env, filename, file);
+ if ( fd != NULL ) {
+ close(fd);
+ if ( fullpath[0] != 0 ) {
+ if ( pfullpath == &fullpath ) {
+ getcwd(fullpath, MAXPDSTRING);
+ }
+ return (*env)->NewStringUTF(env, fullpath);
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/init.c b/src/init.c
new file mode 100644
index 0000000..4666367
--- /dev/null
+++ b/src/init.c
@@ -0,0 +1,349 @@
+/**
+ * This code is very ugly and needs rewrite. PERIOD.
+ */
+
+#include
+#include
+#include
+#include "pdj.h"
+
+#define MAX_PROPERTIES 40
+char *properties[MAX_PROPERTIES+1][2];
+
+char *pdj_getProperty(char *name) {
+ int i;
+
+ for(i=0; properties[i][0] != NULL; i++) {
+ if ( !strcmp(properties[i][0], name) ) {
+ return properties[i][1];
+ }
+ }
+ return NULL;
+}
+
+
+static void load_properties() {
+ char propPath[BUFFER_SIZE];
+ int propIdx = 0;
+ char *alloc;
+ FILE *f;
+
+ getuglylibpath(propPath);
+
+ properties[0][0] = "pdj.home";
+ alloc = malloc(strlen(propPath)+1);
+ strcpy(alloc, propPath);
+ properties[0][1] = alloc;
+
+ strcat(propPath, DIR_SEP "pdj.properties");
+ f = fopen(propPath, "r");
+
+ if ( f == NULL ) {
+ post("pdj: warning: property file not found at %s", propPath);
+ return;
+ }
+
+ while(!feof(f)) {
+ char buffer[BUFFER_SIZE];
+ char *work, *key, *value;
+
+ fgets(buffer, BUFFER_SIZE-1, f);
+
+ work = strchr(buffer, '\n');
+ if ( work != 0 ) {
+ *work = 0;
+
+ if ( work == buffer )
+ continue;
+ }
+
+ /* cuts comments */
+ work = strchr(buffer, '#');
+ if ( work != NULL ) {
+ *work = 0;
+
+ if ( work == buffer )
+ continue;
+ }
+
+ key = strtok(buffer, "=");
+ if ( key == NULL ) {
+ continue;
+ }
+
+ value = strtok(NULL, "");
+ if ( value == NULL ) {
+ value = "";
+ }
+
+ if ( propIdx == MAX_PROPERTIES ) {
+ error("pdj: maximum defined properties");
+ break;
+ }
+ propIdx++;
+
+ alloc = malloc(strlen(key)+1);
+ strcpy(alloc, key);
+ properties[propIdx][0] = alloc;
+
+ alloc = malloc(strlen(value)+1);
+ strcpy(alloc, value);
+ properties[propIdx][1] = alloc;
+ }
+
+ properties[propIdx+1][0] = NULL;
+ properties[propIdx+1][1] = NULL;
+ fclose(f);
+}
+
+
+static void copyToJavaSystemProperties(JNIEnv *env) {
+ jclass system = (*env)->FindClass(env, "java/lang/System");
+ jmethodID id = (*env)->GetStaticMethodID(env, system, "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ int i;
+
+ for(i=0; properties[i][0] != NULL; i++) {
+ jobject key, value;
+
+ key = (*env)->NewStringUTF(env, properties[i][0]);
+ JASSERT(key);
+ value = (*env)->NewStringUTF(env, properties[i][1]);
+ JASSERT(value);
+
+ (*env)->CallStaticObjectMethod(env, system, id, key, value);
+ }
+}
+
+
+PdjCaching pdjCaching;
+static int initIDCaching(JNIEnv *env) {
+ pdjCaching.cls_Atom = (*env)->FindClass(env, "com/cycling74/max/Atom");
+ if ( pdjCaching.cls_Atom == NULL ) {
+ // if the Atom class is not found... it means that pdj.jar is not on
+ // classpath and the installation is broken.
+ error("pdj: com.cycling74.max.Atom is not found on classpath ! pdj.jar must in the same directory of the external!");
+ return 1;
+ }
+ pdjCaching.cls_Atom = (*env)->NewGlobalRef(env, pdjCaching.cls_Atom);
+
+ pdjCaching.cls_MaxClock = (*env)->FindClass(env, "com/cycling74/max/MaxClock");
+ JASSERT(pdjCaching.cls_MaxClock);
+ pdjCaching.cls_MaxClock = (*env)->NewGlobalRef(env, pdjCaching.cls_MaxClock);
+
+ pdjCaching.cls_MaxObject = (*env)->FindClass(env, "com/cycling74/max/MaxObject");
+ JASSERT(pdjCaching.cls_MaxObject);
+ pdjCaching.cls_MaxObject = (*env)->NewGlobalRef(env, pdjCaching.cls_MaxObject);
+
+ pdjCaching.cls_MSPObject = (*env)->FindClass(env, "com/cycling74/msp/MSPObject");
+ JASSERT(pdjCaching.cls_MSPObject);
+ pdjCaching.cls_MSPObject = (*env)->NewGlobalRef(env, pdjCaching.cls_MSPObject);
+
+ pdjCaching.cls_MSPSignal = (*env)->FindClass(env, "com/cycling74/msp/MSPSignal");
+ JASSERT(pdjCaching.cls_MSPSignal);
+ pdjCaching.cls_MSPSignal = (*env)->NewGlobalRef(env, pdjCaching.cls_MSPSignal);
+
+ pdjCaching.MIDAtom_newAtom_Float =
+ (*env)->GetStaticMethodID(env, pdjCaching.cls_Atom, "newAtom", "(F)Lcom/cycling74/max/Atom;");
+ JASSERT(pdjCaching.MIDAtom_newAtom_Float);
+
+ pdjCaching.MIDAtom_newAtom_String =
+ (*env)->GetStaticMethodID(env, pdjCaching.cls_Atom, "newAtom", "(Ljava/lang/String;)Lcom/cycling74/max/Atom;");
+ JASSERT(pdjCaching.MIDAtom_newAtom_String);
+
+ pdjCaching.FIDAtom_type =
+ (*env)->GetFieldID(env, pdjCaching.cls_Atom, "type", "I");
+ JASSERT(pdjCaching.FIDAtom_type);
+
+ pdjCaching.cls_AtomFloat = (*env)->FindClass(env, "com/cycling74/max/AtomFloat");
+ JASSERT(pdjCaching.cls_AtomFloat);
+
+ pdjCaching.FIDAtomFloat_value =
+ (*env)->GetFieldID(env, pdjCaching.cls_AtomFloat, "value", "F");
+ JASSERT(pdjCaching.FIDAtomFloat_value);
+
+ pdjCaching.cls_AtomString = (*env)->FindClass(env, "com/cycling74/max/AtomString");
+ JASSERT(pdjCaching.cls_AtomString);
+
+ pdjCaching.FIDAtomString_value =
+ (*env)->GetFieldID(env, pdjCaching.cls_AtomString, "value", "Ljava/lang/String;");
+ JASSERT(pdjCaching.FIDAtomString_value);
+
+ pdjCaching.FIDMaxClock_clock_ptr =
+ (*env)->GetFieldID(env, pdjCaching.cls_MaxClock, "_clock_ptr", "J");
+ JASSERT(pdjCaching.FIDMaxClock_clock_ptr);
+
+ pdjCaching.FIDMaxObject_pdobj_ptr =
+ (*env)->GetFieldID(env, pdjCaching.cls_MaxObject, "_pdobj_ptr", "J");
+ JASSERT(pdjCaching.FIDMaxObject_pdobj_ptr);
+
+ pdjCaching.FIDMaxObject_activity_inlet =
+ (*env)->GetFieldID(env, pdjCaching.cls_MaxObject, "_activity_inlet", "I");
+ JASSERT(pdjCaching.FIDMaxObject_activity_inlet);
+
+ pdjCaching.MIDMaxObject_trySetter =
+ (*env)->GetMethodID(env, pdjCaching.cls_MaxObject, "_trySetter", "(Ljava/lang/String;[Lcom/cycling74/max/Atom;)Z");
+ JASSERT(pdjCaching.MIDMaxObject_trySetter);
+
+ pdjCaching.FIDMSPObject_used_inputs =
+ (*env)->GetFieldID(env, pdjCaching.cls_MSPObject, "_used_inputs", "[Lcom/cycling74/msp/MSPSignal;");
+ JASSERT(pdjCaching.FIDMSPObject_used_inputs);
+
+ pdjCaching.FIDMSPObject_used_outputs =
+ (*env)->GetFieldID(env, pdjCaching.cls_MSPObject, "_used_outputs", "[Lcom/cycling74/msp/MSPSignal;");
+ JASSERT(pdjCaching.FIDMSPObject_used_outputs);
+
+ pdjCaching.MIDMSPObject_dspinit =
+ (*env)->GetMethodID(env, pdjCaching.cls_MSPObject, "_dspinit", "(FI)Ljava/lang/reflect/Method;");
+ JASSERT(pdjCaching.MIDMSPObject_dspinit);
+
+ pdjCaching.MIDMSPObject_emptyPerformer =
+ (*env)->GetMethodID(env, pdjCaching.cls_MSPObject, "_emptyPerformer", "([Lcom/cycling74/msp/MSPSignal;[Lcom/cycling74/msp/MSPSignal;)V");
+ JASSERT(pdjCaching.MIDMSPObject_emptyPerformer);
+
+ pdjCaching.FIDMSPSignal_vec =
+ (*env)->GetFieldID(env, pdjCaching.cls_MSPSignal, "vec", "[F");
+ JASSERT(pdjCaching.FIDMSPSignal_vec);
+
+ return 0;
+}
+
+
+static int linkClasses(JNIEnv *env) {
+ jclass pdjSystem = (*env)->FindClass(env, "com/e1/pdj/PDJSystem");
+ jmethodID id;
+ if ( pdjSystem == NULL ) {
+ SHOWEXC;
+ return 1;
+ }
+
+ id = (*env)->GetStaticMethodID(env, pdjSystem, "_init_system", "()V");
+ if ( id == NULL ) {
+ SHOWEXC;
+ return 1;
+ }
+
+ (*env)->CallStaticVoidMethod(env, pdjSystem, id);
+ if ( (*env)->ExceptionOccurred(env) ) {
+ (*env)->ExceptionDescribe(env);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void buildVMOptions(jint *nb, JavaVMOption *options) {
+ static char cp[BUFFER_SIZE], pdj_cp[BUFFER_SIZE];
+ char installPath[BUFFER_SIZE];
+ char *prop;
+ char *token, *work;
+ int i;
+ *nb = 0;
+
+ getuglylibpath(installPath);
+
+ // first; we set the system classpath
+ strcpy(cp, "-Djava.class.path=");
+ strcat(cp, installPath);
+ strcat(cp, DIR_SEP "pdj.jar" PATH_SEP);
+ prop = pdj_getProperty("pdj.system-classpath");
+ if ( prop != NULL )
+ strcat(cp, prop);
+ options[0].optionString = cp;
+
+ prop = pdj_getProperty("pdj.vm_args");
+ if ( prop == NULL ) {
+ *nb = 1;
+ return;
+ }
+
+ work = malloc(strlen(prop)+1);
+ strcpy(work, prop);
+ token = strtok(work, " ");
+
+ for(i=*nb; i<32; i++) {
+ *nb += 1;
+
+ if ( token == NULL ) {
+ free(work);
+ return;
+ }
+
+ options[*nb].optionString = malloc(strlen(token)+1);
+ strcpy(options[*nb].optionString, token);
+ token = strtok(NULL, " ");
+ }
+
+ bug("pdj: maximum vm_args properties defined. Go see the source Luke.");
+}
+
+
+int REDIRECT_PD_IO;
+static void redirectIoInit(void) {
+ char *ret;
+
+ ret = pdj_getProperty("pdj.redirect-pdio");
+ if ( ret == NULL ) {
+ REDIRECT_PD_IO = 1;
+ return;
+ }
+
+ if ( ret[0] == '0' ) {
+ REDIRECT_PD_IO = 0;
+ return;
+ }
+
+ if ( strcmp(ret, "false") == 0 ) {
+ REDIRECT_PD_IO = 0;
+ return;
+ }
+
+ REDIRECT_PD_IO = 1;
+}
+
+
+JNIEnv *init_jvm(void) {
+ JNI_CreateJavaVM_func *func;
+ JavaVMOption opt[32];
+ JavaVMInitArgs vm_args;
+ JNIEnv *jni_env;
+ char *vm_type;
+ int rc;
+
+ load_properties();
+
+ buildVMOptions(&(vm_args.nOptions), opt);
+ vm_args.options = opt;
+ vm_args.version = JNI_VERSION_1_4;
+ vm_args.ignoreUnrecognized = JNI_FALSE;
+
+ vm_type = pdj_getProperty("pdj.vm_type");
+ if ( vm_type == NULL ) {
+ error("pdj: unknown vm_type, using client");
+ vm_type = "client";
+ }
+
+ func = linkjvm(vm_type);
+ if ( func == NULL )
+ return NULL;
+
+ rc = func(&jni_jvm, &jni_env, &vm_args);
+ if ( rc != 0 ) {
+ error("pdj: unable to create JVM: JNI_CreateJavaVM = %d", rc);
+ return NULL;
+ }
+
+ copyToJavaSystemProperties(jni_env);
+ if ( initIDCaching(jni_env) != 0) {
+ return NULL;
+ }
+
+ if ( linkClasses(jni_env) != 0 ) {
+ return NULL;
+ }
+
+ redirectIoInit();
+
+ return jni_env;
+}
+
diff --git a/src/java/com/cycling74/io/ErrorStream.java b/src/java/com/cycling74/io/ErrorStream.java
new file mode 100644
index 0000000..612bf3f
--- /dev/null
+++ b/src/java/com/cycling74/io/ErrorStream.java
@@ -0,0 +1,10 @@
+package com.cycling74.io;
+
+import com.e1.pdj.*;
+import java.io.*;
+
+public class ErrorStream extends PrintStream {
+ public ErrorStream() {
+ super(PDJSystem.err, true);
+ }
+}
diff --git a/src/java/com/cycling74/io/PostStream.java b/src/java/com/cycling74/io/PostStream.java
new file mode 100644
index 0000000..5d7a58e
--- /dev/null
+++ b/src/java/com/cycling74/io/PostStream.java
@@ -0,0 +1,10 @@
+package com.cycling74.io;
+
+import com.e1.pdj.*;
+import java.io.*;
+
+public class PostStream extends PrintStream {
+ public PostStream() {
+ super(PDJSystem.out, true);
+ }
+}
diff --git a/src/java/com/cycling74/max/Atom.java b/src/java/com/cycling74/max/Atom.java
new file mode 100644
index 0000000..c1949df
--- /dev/null
+++ b/src/java/com/cycling74/max/Atom.java
@@ -0,0 +1,715 @@
+package com.cycling74.max;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * PD element that is used in message or arguments. It can contains a
+ * float, a int (always map to a float in pd) or a string.
+ */
+public abstract class Atom implements Comparable, Serializable {
+
+ /**
+ * Empty array to use with the API when theres is no arguments.
+ */
+ public static final Atom[] emptyArray = new Atom[] {};
+
+ int type;
+
+ Atom(int type) {
+ this.type = type;
+ }
+
+ // Atom factories
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(int value) {
+ return new AtomFloat(value);
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom[] newAtom(int value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;iobject. Similar to "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;ifrom to index to
.
+ * @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 1 )
+ throw new PDJError("Method: " + setter_name + " has too much parameters to be a setter");
+ typeCheck = mapType(c[0]);
+ setter = methods[i];
+ break;
+ }
+ }
+ if ( typeCheck == 0 ) {
+ throw new NoSuchMethodException(setter_name);
+ }
+ }
+ } catch ( Exception e ) {
+ throw new PDJError(e);
+ }
+
+ if ( typeCheck != type ) {
+ throw new PDJError("Object type for setter and getter is not the same");
+ }
+
+ }
+
+ private char mapType(Class clz) {
+ if ( clz == Integer.TYPE )
+ return 'i';
+ if ( clz == int[].class )
+ return 'I';
+ if ( clz == Float.TYPE )
+ return 'f';
+ if ( clz == float[].class )
+ return 'F';
+ if ( clz == Double.TYPE )
+ return 'd';
+ if ( clz == double[].class )
+ return 'D';
+ if ( clz == Boolean.TYPE )
+ return 'z';
+ if ( clz == Boolean[].class )
+ return 'Z';
+ if ( clz == Byte.TYPE )
+ return 'b';
+ if ( clz == byte[].class )
+ return 'B';
+ if ( clz == Character.TYPE )
+ return 'c';
+ if ( clz == char[].class )
+ return 'C';
+ if ( clz == Short.TYPE )
+ return 's';
+ if ( clz == short[].class )
+ return 'S';
+ if ( clz == String.class )
+ return 'g';
+ if ( clz == String[].class )
+ return 'G';
+ if ( clz == Atom.class )
+ return 'a';
+ if ( clz == Atom[].class )
+ return 'A';
+ return '-';
+ }
+
+ private void fieldSetter(Atom[] value) throws Exception {
+ Object work;
+ int i;
+
+ switch ( type ) {
+ case 'z' :
+ field.setBoolean(obj, value[0].toBoolean());
+ break;
+ case 'Z' :
+ work = field.get(obj);
+ for (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 Callback implements Executable {
+ private Method method;
+ private String methodName;
+ private Object obj;
+ private Object args[];
+
+ /**
+ * Will call method methodName with no argument by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ */
+ public Callback(Object obj, String methodName) {
+ this(obj, methodName, null, null);
+ }
+
+ /**
+ * Will call method methodName with a int by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param i the int value
+ */
+ public Callback(Object obj, String methodName, int i) {
+ this(obj, methodName, new Object[] { new Integer(i) }, new Class[] { Integer.TYPE });
+ }
+
+ /**
+ * Will call method methodName with a float by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param f the float value
+ */
+ public Callback(Object obj, String methodName, float f) {
+ this(obj, methodName, new Object[] { new Float(f) }, new Class[] { Float.TYPE });
+ }
+
+ /**
+ * Will call method methodName with a Stringt by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param str the string value
+ */
+ public Callback(Object obj, String methodName, String str) {
+ this(obj, methodName, new Object[] { str });
+ }
+
+ /**
+ * Will call method methodName with a boolean by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param flag the boolean value
+ */
+ public Callback(Object obj, String methodName, boolean flag) {
+ this(obj, methodName, new Object[] { flag ? Boolean.TRUE : Boolean.FALSE });
+ }
+
+ /**
+ * Will call method methodName with multiple arguments by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param params argument to pass to the method
+ */
+ public Callback(Object obj, String methodName, Object params[]) {
+ this(obj, methodName, params, buildClasses(params));
+ }
+
+ /**
+ * Will call method methodName with multiple arguments (typed) by using execute()
+ * @param obj the object with the method
+ * @param methodName the name of the method
+ * @param params argument to pass to the method
+ * @param params_types the type of arguments
+ */
+ public Callback(Object obj, String methodName, Object params[], Class params_types[]) {
+ try {
+ if ( params == null ) {
+ method = obj.getClass().getDeclaredMethod(methodName, null);
+ } else {
+ method = obj.getClass().getDeclaredMethod(methodName, params_types);
+ }
+ this.obj = obj;
+ this.methodName = methodName;
+ } catch ( NoSuchMethodException e ) {
+ MaxSystem.post("pdj: unable to find method: " + methodName + ", "+ e);
+ }
+ }
+
+ private static Class[] buildClasses(Object params[]) {
+ Class clz[] = new Class[params.length];
+
+ for(int i=0;i
+ *
+ * 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)
+ * }
+ * }
+ *
+ */
+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 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; 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();
+}
diff --git a/src/java/com/cycling74/max/MaxPatcher.java b/src/java/com/cycling74/max/MaxPatcher.java
new file mode 100644
index 0000000..b2fb778
--- /dev/null
+++ b/src/java/com/cycling74/max/MaxPatcher.java
@@ -0,0 +1,17 @@
+package com.cycling74.max;
+
+/**
+ * Java object that encapsulate the pd patch. Minimal support on pdj; only
+ * getPath() is supported.
+ */
+public class MaxPatcher {
+ String patchPath;
+
+ /**
+ * Returns the absolute path of the associated patch.
+ * @return the absolute patch path
+ */
+ public String getPath() {
+ return patchPath;
+ }
+}
diff --git a/src/java/com/cycling74/max/MaxQelem.java b/src/java/com/cycling74/max/MaxQelem.java
new file mode 100644
index 0000000..100f671
--- /dev/null
+++ b/src/java/com/cycling74/max/MaxQelem.java
@@ -0,0 +1,114 @@
+package com.cycling74.max;
+
+/**
+ * Background job utility class. This is used to execute code in the
+ * background that might take time to execute. Calling set
+ * 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 index start
+ */
+ 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:
+ *
+ * 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;
+ * }
+ * }
+ * }
+ *
+ */
+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 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;i
+
+Package for using using array and signals objects.
+
+