aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Christoph Steiner <eighthave@users.sourceforge.net>2008-03-22 02:15:12 +0000
committerHans-Christoph Steiner <eighthave@users.sourceforge.net>2008-03-22 02:15:12 +0000
commita764e59e1d3a8e330f0d484fdb26b35ca3f0b2e4 (patch)
treec4ecadccdecf2809b99c0da0545f255a6ad25bb5
bringing pdj-0.8.3 into the main branchsvn2git-root
svn path=/trunk/externals/loaders/pdj/; revision=9621
-rw-r--r--.cdtproject94
-rw-r--r--.project125
-rw-r--r--.settings/org.eclipse.cdt.core.prefs3
-rw-r--r--LICENSE22
-rw-r--r--Linux-build.properties15
-rw-r--r--Mac OS X-build.properties16
-rw-r--r--Makefile11
-rw-r--r--README174
-rw-r--r--Windows XP-build.properties14
-rw-r--r--build.properties21
-rw-r--r--build.xml250
-rw-r--r--lib/ant/cpptasks.jarbin0 -> 345204 bytes
-rw-r--r--res/help-pdj.pd63
-rw-r--r--res/pdj-test.pd42
-rw-r--r--res/pdj.properties81
-rw-r--r--res/pdj~-panner.pd10
-rw-r--r--res/pdj~-test.pd42
-rw-r--r--src/MSPBuffer.c159
-rw-r--r--src/MaxClock.c74
-rw-r--r--src/MaxObject.c131
-rw-r--r--src/MaxSystem.c178
-rw-r--r--src/init.c349
-rw-r--r--src/java/com/cycling74/io/ErrorStream.java10
-rw-r--r--src/java/com/cycling74/io/PostStream.java10
-rw-r--r--src/java/com/cycling74/max/Atom.java715
-rw-r--r--src/java/com/cycling74/max/AtomFloat.java96
-rw-r--r--src/java/com/cycling74/max/AtomString.java63
-rw-r--r--src/java/com/cycling74/max/Attribute.java463
-rw-r--r--src/java/com/cycling74/max/Callback.java213
-rw-r--r--src/java/com/cycling74/max/DataTypes.java14
-rw-r--r--src/java/com/cycling74/max/Executable.java14
-rw-r--r--src/java/com/cycling74/max/MaxClock.java113
-rw-r--r--src/java/com/cycling74/max/MaxContext.java21
-rw-r--r--src/java/com/cycling74/max/MaxObject.java933
-rw-r--r--src/java/com/cycling74/max/MaxPatcher.java17
-rw-r--r--src/java/com/cycling74/max/MaxQelem.java114
-rw-r--r--src/java/com/cycling74/max/MaxRuntimeException.java15
-rw-r--r--src/java/com/cycling74/max/MaxSystem.java186
-rw-r--r--src/java/com/cycling74/max/MessageReceiver.java11
-rw-r--r--src/java/com/cycling74/max/package.html5
-rw-r--r--src/java/com/cycling74/msp/AudioFileBuffer.java116
-rw-r--r--src/java/com/cycling74/msp/MSPBuffer.java127
-rw-r--r--src/java/com/cycling74/msp/MSPObject.java163
-rw-r--r--src/java/com/cycling74/msp/MSPPerformable.java17
-rw-r--r--src/java/com/cycling74/msp/MSPPerformer.java36
-rw-r--r--src/java/com/cycling74/msp/MSPSignal.java70
-rw-r--r--src/java/com/cycling74/msp/package.html5
-rw-r--r--src/java/com/e1/pdj/ConsoleStream.java35
-rw-r--r--src/java/com/e1/pdj/ConsoleStreamWin32.java34
-rw-r--r--src/java/com/e1/pdj/GenericCompiler.java71
-rw-r--r--src/java/com/e1/pdj/JavacCompiler.java34
-rw-r--r--src/java/com/e1/pdj/JikesCompiler.java27
-rw-r--r--src/java/com/e1/pdj/PDJClassLoader.java206
-rw-r--r--src/java/com/e1/pdj/PDJClassLoaderException.java17
-rw-r--r--src/java/com/e1/pdj/PDJError.java15
-rw-r--r--src/java/com/e1/pdj/PDJSystem.java142
-rw-r--r--src/java/com/e1/pdj/PriorityQueue.java68
-rw-r--r--src/java/com/e1/pdj/test/AtomTest.java92
-rw-r--r--src/java/com/e1/pdj/test/CallbackTest.java10
-rw-r--r--src/java/com/e1/pdj/test/MaxQelemTest.java7
-rw-r--r--src/java/help_class.java26
-rw-r--r--src/java/panner.java37
-rw-r--r--src/java/pdj_test_class.java72
-rw-r--r--src/java/pdj_test_tilde.java45
-rw-r--r--src/java/pdj_tilde.java45
-rw-r--r--src/pdj-linux.c91
-rw-r--r--src/pdj-osx.c37
-rw-r--r--src/pdj-win32.c107
-rw-r--r--src/pdj.c396
-rw-r--r--src/pdj.h140
-rw-r--r--src/pdj~.c206
-rw-r--r--src/type_handler.c151
-rw-r--r--src/type_handler.h18
-rw-r--r--www/index.html49
-rw-r--r--www/pdj-help.pngbin0 -> 23617 bytes
-rw-r--r--www/pdj-logo.pngbin0 -> 1420 bytes
-rw-r--r--www/stylesheet.css35
77 files changed, 7634 insertions, 0 deletions
diff --git a/.cdtproject b/.cdtproject
new file mode 100644
index 0000000..d299513
--- /dev/null
+++ b/.cdtproject
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse-cdt version="2.0"?>
+
+<cdtproject id="org.eclipse.cdt.make.core.make">
+<extension id="org.eclipse.cdt.core.MachO" point="org.eclipse.cdt.core.BinaryParser">
+<attribute key="c++filt" value="c++filt"/>
+</extension>
+<extension id="org.eclipse.cdt.core.domsourceindexer" point="org.eclipse.cdt.core.CIndexer"/>
+<data>
+<item id="org.eclipse.cdt.core.pathentry">
+<pathentry kind="con" path="org.eclipse.cdt.make.core.DISCOVERED_SCANNER_INFO"/>
+<pathentry kind="src" path=""/>
+<pathentry kind="out" path=""/>
+</item>
+<item id="cdt_indexer">
+<indexEnabled indexValue="true"/>
+<indexerProblemsEnabled indexProblemsValue="0"/>
+</item>
+<item id="org.eclipse.cdt.make.core.buildtargets">
+<buildTargets>
+<target name="all" path="java" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+<buildCommand>make</buildCommand>
+<buildTarget>all</buildTarget>
+<stopOnError>false</stopOnError>
+<useDefaultCommand>true</useDefaultCommand>
+</target>
+<target name="all" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+<buildCommand>make</buildCommand>
+<buildTarget>all</buildTarget>
+<stopOnError>false</stopOnError>
+<useDefaultCommand>true</useDefaultCommand>
+</target>
+<target name="test" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+<buildCommand>make</buildCommand>
+<buildTarget>test</buildTarget>
+<stopOnError>false</stopOnError>
+<useDefaultCommand>true</useDefaultCommand>
+</target>
+<target name="clean" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+<buildCommand>make</buildCommand>
+<buildTarget>clean</buildTarget>
+<stopOnError>false</stopOnError>
+<useDefaultCommand>true</useDefaultCommand>
+</target>
+</buildTargets>
+</item>
+<item id="cdt_resolver">
+<custom value="false"/>
+</item>
+<item id="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+</item>
+</data>
+</cdtproject>
diff --git a/.project b/.project
new file mode 100644
index 0000000..a6a2067
--- /dev/null
+++ b/.project
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>pdj</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.make.core.makeBuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ <dictionary>
+ <key>org.eclipse.cdt.core.errorOutputParser</key>
+ <value>org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser;</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+ <value>clean all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.incrementalBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+ <value>false</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildLocation</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.environment</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableFullBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.build.target.inc</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.build.target.clean</key>
+ <value>clean</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+ <value>clean</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.append_environment</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildCommand</key>
+ <value>make</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.stopOnError</key>
+ <value>false</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.build.target.auto</key>
+ <value>all</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.make.core.ScannerConfigBuilder</name>
+ <arguments>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.ScannerConfigDiscoveryEnabled</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.makeBuilderParserId</key>
+ <value>org.eclipse.cdt.make.core.GCCScannerInfoConsoleParser</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.esiProviderCommandEnabled</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.siProblemGenerationEnabled</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.useDefaultESIProviderCmd</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.makeBuilderParserEnabled</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.esiProviderParserId</key>
+ <value>org.eclipse.cdt.make.core.GCCSpecsConsoleParser</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.make.core.makeNature</nature>
+ <nature>org.eclipse.cdt.make.core.ScannerConfigNature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..d57d8bd
--- /dev/null
+++ b/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,3 @@
+#Mon Jul 03 15:02:11 EDT 2006
+eclipse.preferences.version=1
+indexerId=org.eclipse.cdt.core.fastIndexer
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fd043d8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2004-2007, Pascal Gauthier
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Linux-build.properties b/Linux-build.properties
new file mode 100644
index 0000000..12b5a90
--- /dev/null
+++ b/Linux-build.properties
@@ -0,0 +1,15 @@
+# property file for Linux
+
+# put the path of where the jdk (java sdk) is installed
+jdk.home=/usr/local/java
+
+# put the path of where pd is installed
+pd.home=/home/asb2m10/pd-0.39-0
+
+# common linux properties
+isLinux=true
+compiler=gcc
+linker.type=shared
+platform=linux
+jdk.libs=${jdk.home}/jre/lib/i386
+pdj.outfile=${dist.dir}/pdj \ No newline at end of file
diff --git a/Mac OS X-build.properties b/Mac OS X-build.properties
new file mode 100644
index 0000000..95e4761
--- /dev/null
+++ b/Mac OS X-build.properties
@@ -0,0 +1,16 @@
+# properties file for OSX.
+
+# put the root of your pd path installation
+pd.home=/Applications/pd.app/Contents/Resources
+
+# set this to 'pd_darwin' if you are building for PowerPC
+pdj.archBuild=d_fat
+
+# common OS X properties
+isOSX=true
+compiler=gcc
+linker.type=plugin
+platform=osx
+jdk.libs=/System/Library/Frameworks/JavaVM.framework/Libraries
+jdk.home=/Library/Java/Home
+pdj.outfile=${dist.dir}/pdj.${pdj.archBuild}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d34f915
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+# check out the build.properties / {your platform}.properties / build.xml
+# for path settings
+
+all:
+ ant package
+
+clean:
+ ant clean
+
+test:
+ ant test
diff --git a/README b/README
new file mode 100644
index 0000000..5737e2a
--- /dev/null
+++ b/README
@@ -0,0 +1,174 @@
+###########################################################################
+pdj - java plugin interface to pure-data
+###########################################################################
+
+RELEASE 0.8.3 / September 2007
+
+
+PDJ enables you to write java code to interact with pure-data objects. The
+API is totally based on Cycling74 Max/MSP 'mxj' object implementation. This
+will enable java mxj objects to run on pure-data with pdj. You can also
+create signal/tilde (~) objects.
+
+This is a work in progress and some of features are missing and some might
+not even be ported.
+
+IMPLEMENTED:
+
+ * MaxClock 100%
+ * MSPBuffer 100%
+ * Callback 100%
+ * MaxObject 100%
+ * Atom 98%
+ * MaxSystem 90%
+ * MaxQelem 90%
+ * MSPObject 85% (missing signal in/outlet detection)
+ * MSPPerformable 100%
+ * MaxPatcher 5%
+
+TODO:
+
+ * MSPObject connection status
+ * MaxBox/MaxPatcher object; inspiration from dyn~ (kudo Mr. Grill!)
+
+LIMITATION:
+
+ * Signal inlets cannot be hot inlets for receiving atom message.
+ This means that if you create a signal inlet and send an atom
+ to this inlet, pdj won't be able to process it; float or symbol.
+ This looks like a work as designed in pd.
+
+BUGS:
+
+ * OS X: cannot use AWT classes with OS X because of a weird bug
+ that locks the thread when the GUI libraries are loaded.
+ -> http://lists.apple.com/archives/java-dev/2004/Mar/msg00910.html
+ this solution has been tested but the loadLibrary still hang.
+
+WORKAROUNDS:
+
+ * on most cases you don't have to put the JVM libs dir to the
+ LD_LIBRARY_PATH. If you have a UnsatisfiedLinkError, add it
+ before you start PD.
+ * on some machines, the System.out redirection can crash PD. If
+ it is the case, you can disable it in the pdj.properties file;
+ check the property: pdj.redirect-pdio.
+
+REQUIEREMENTS:
+
+ * pure-data 0.38.x or better
+ * java JDK version 1.4.x or better; use 1.5 if you can !
+ * works on linux, windows
+
+REQUIEREMENTS FOR BUILDING:
+
+ * apache ant 1.6.x or better
+ * c compiler
+
+INSTALLATION:
+
+ * if you are using the source distribution, build it before
+ -> download java sdk from java.sun.com (unless you are using OS X)
+ -> download apache ant for ant.apache.org
+ -> edit file <your platform>-build.properties
+ -> in the root directory of pdj, run 'ant package'
+ * add the pdj external (pdj.pd_linux/pd.dll/pd.pd_dawrin) directory
+ to your pd path (very important on OS X)
+ * the other files of the original binary directory must be in
+ the same directory
+ * double check dist/pdj.properties to be sure that the JAVA
+ environment parameters are right
+ * === OS X WARNING ====
+ I am working on a universal binary of pdj. This is planned on version
+ 0.8.4. If you really need a OS X build, email me.
+
+USAGE:
+
+ * put your .java file in the /classes directory
+ * create a pdj object with the name of the java class; if
+ you have not compiled it before, pdj will do it for you
+
+CHANGELOG:
+ --- VERSION 0.8.3 ---
+ * Atom.getInt() on a float now works.
+ * corrected some classpath definition issues with windows
+ * bypass the java compilation with pdj.compiler=null
+ * remove dependencies task for cpptasks.jar (now part of distribution)
+ * FIXED: comment on property pdj.vm_args failed to initialize VM
+ * BUG: cannot link with OS X and cpptasks. Planned on 0.8.4
+
+ --- VERSION 0.8.2 ---
+ * if javac is set, using the javac compiler from the JAVA_HOME first
+ * optimizations (main thread JVM and symbol method resolution)
+ * FIXED: using pdj.classpath with directories
+ * FIXED: search path on file in the current directory
+ * FIXED: leak with open_path and casting warnings
+ * build for Intel Mac
+ * getting better at attribute support
+
+ --- VERSION 0.8.1 ---
+ * added MaxPatcher object to get patch path
+ * FIXED: to match mxj: method with args[] will always be called first
+
+ --- VERSION 0.8 ---
+ * pdj~ object support (MSPSignal/MSPObject/MSPPerformable)
+ * The JVM can now be server or in client mode (windows/linux only)
+ * JVM selection for OS X
+ * FIXED: working jikes compiler on OS X
+ * FIXED: declareIO inlets count
+ * FIXED: windows: System.out CR/LF errors on the PD console
+
+ --- VERSION 0.7.1 ---
+ * OS X support with distribution
+ * FIXED: MaxSystem.locateFile now works
+
+ --- VERSION 0.7 ---
+ * Better JVM resolution on win32 (eg: with the registry)
+ * Full implementation of MSPBuffer
+ * Added Atom methods with unit tests
+ * Windows binaries distribution
+ * FIXED: now filtering { and } with post() not to lock PD
+ * FIXED: anything gets called on a undefined bang/float/list method
+ * FIXED: defined inlet(int) without inlet(float) now works... :-\
+
+ --- VERSION 0.6.1 ---
+ * FIXED: outlet(Atom) and outlet(Atom[]) resolution error
+
+ --- VERSION 0.6 ---
+ * MaxObject constructor with arguments
+ * partial attribute support; automatic field setter/getter
+ * added method bail on MaxObject
+ * strict declaration of inlets/outlets (with info outlet)
+ * System.out and System.err linked to the pd console
+ * outlet(atom[]) checks type before calling the right func.
+
+ --- VERSION 0.5 ---
+ * method inlet resolution (bang->float->list->anything) now works
+ * windows support (premilinary support)
+ * auto-compiling class of every .java in /class (max -1; pdj +1)
+
+ --- VERSION 0.4 ---
+ * more javadoc and documentation... but could be better
+ * multiple inlet operations
+ * loadbang now works
+
+ --- VERSION 0.3 ---
+ * added a small and cheap javadoc. the original MXJ should be the
+ reference.
+ * partial MSPBuffer support
+ * Atom and MaxObject now supports Atom arrays
+
+ --- VERSION 0.2 ---
+ * added classpath of $PDJ_HOME/classes and $PDJ_HOME/lib/* by default
+ * 95 % Dynamic JVM resolution with the JAVA_HOME; via dlsym (see BUGS)
+ * MaxSystem.deferLow(...) works ! :-\
+ * added an help patch
+
+THANKS:
+
+ * Thomas Grill
+ * patrick a 11h11
+ * pd-mtl crew !
+
+(c) Pascal Gauthier 2004-2007, under BSD like license
+asb2m10@users.sourceforge.net
diff --git a/Windows XP-build.properties b/Windows XP-build.properties
new file mode 100644
index 0000000..0d777bf
--- /dev/null
+++ b/Windows XP-build.properties
@@ -0,0 +1,14 @@
+# properties file for windows
+
+# put the path of where the jdk (java sdk) is installed
+jdk.home=C:/Java/jdk1.6.0_02
+
+# put the path of where pd is installed
+pd.home=C:/Projets/pd
+# common windows properties
+isWin32=true
+compiler=msvc
+linker.type=shared
+platform=win32
+jdk.libs=${jdk.home}/jre/lib/i386
+pdj.outfile=${dist.dir}/pdj \ No newline at end of file
diff --git a/build.properties b/build.properties
new file mode 100644
index 0000000..e28db31
--- /dev/null
+++ b/build.properties
@@ -0,0 +1,21 @@
+#
+
+# PDJ packaging version
+# =========================================================================
+pdj.version=0.8.3
+
+# Various path definition
+# =========================================================================
+src.dir=src
+java.src.dir=src/java
+work.dir=work
+www.dir=www
+javadoc.dir=${www.dir}/api
+classes.dir=${work.dir}/classes
+dist.dir=dist
+javah.file=${work.dir}/native_classes.h
+pdj.jar=${dist.dir}/pdj.jar
+pdj.test-patch=pdj-test.pd
+pdj-tilde.test-patch="pdj~-test.pd"
+
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..a1414c7
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,250 @@
+<project name="pdj" default="package" basedir=".">
+
+ <property file="build.properties"/>
+ <property file="${os.name}-build.properties"/>
+
+ <!-- ============================================================== -->
+
+ <property name="jdk.include" value="${jdk.home}/include"/>
+ <property name="pd.include" value="${pd.home}/src"/>
+
+ <!-- java classes that uses a native interface -->
+ <property name="native.classes" value="
+ com.cycling74.max.MaxObject,
+ com.cycling74.max.MaxClock,
+ com.cycling74.max.MaxSystem,
+ com.cycling74.msp.MSPBuffer,
+ com.cycling74.msp.MSPObject"/>
+
+ <target name="clean" depends="-init">
+ <delete dir="${dist.dir}"/>
+ <delete dir="${work.dir}"/>
+ <delete dir="${javadoc.dir}"/>
+ <delete>
+ <fileset dir="."
+ includes="**/history.xml,**/dependencies.xml,index.html"/>
+ </delete>
+ </target>
+
+ <target name="-init">
+ <mkdir dir="${classes.dir}"/>
+ <mkdir dir="${dist.dir}"/>
+ <taskdef resource="cpptasks.tasks" classpath="lib/ant/cpptasks.jar"/>
+ <typedef resource="cpptasks.types" classpath="lib/ant/cpptasks.jar"/>
+ <available classname="junit.framework.Test" property="isJunitFound"/>
+ </target>
+
+ <target name="cl-java" depends="-init">
+ <javac destdir="${classes.dir}" srcdir="${java.src.dir}" debug="true"
+ target="1.4" source="1.4">
+ <exclude name="**/test/**" unless="isJunitFound"/>
+ <exclude name="**/*Test.java" unless="isJunitFound"/>
+ </javac>
+ <jar jarfile="${pdj.jar}" basedir="${classes.dir}">
+ <exclude name="*.class"/>
+ <exclude name="**/test/**" unless="isJunitFound"/>
+ <exclude name="**/*Test.class" unless="isJunitFound"/>
+ </jar>
+
+ <uptodate property="javah.up2date"
+ srcfile="${pdj.jar}" targetfile="${javah.file}"/>
+ </target>
+
+ <target name="javah" depends="cl-java" unless="javah.up2date">
+ <javah class="${native.classes}" classpath="${pdj.jar}"
+ outputFile="${javah.file}" force="true"/>
+ </target>
+
+ <target name="-linux-rename" if="isLinux">
+ <move file="${dist.dir}/libpdj.so" tofile="${dist.dir}/pdj.pd_linux"/>
+ </target>
+
+ <target name="-osx-rename" if="isOSX">
+ <move file="${dist.dir}/libpdj.${pdj.archBuild}.bundle" tofile="${pdj.outfile}"/>
+ </target>
+
+ <target name="cl" depends="javah">
+ <cc debug="true" objdir="${work.dir}" outtype="${linker.type}"
+ outfile="${pdj.outfile}" name="${compiler}">
+
+ <fileset dir="${src.dir}" includes="*.c">
+ <exclude name="pdj-linux.c" unless="isLinux"/>
+ <exclude name="pdj-win32.c" unless="isWin32"/>
+ <exclude name="pdj-osx.c" unless="isOSX"/>
+ </fileset>
+
+ <!-- compiler args -->
+ <defineset define="DEBUG"/>
+ <defineset define="PROFILER"/>
+ <defineset define="PD"/>
+ <defineset define="NT" if="isWin32"/>
+
+ <compilerarg value="-Wall" if="isLinux"/>
+ <compilerarg value="-fno-common" if="isOSX"/>
+ <compilerarg value="-fPIC" if="isOSX"/>
+ <compilerarg value="-arch" if="isOSX"/>
+ <compilerarg value="i386" if="isOSX"/>
+ <compilerarg value="-arch" if="isOSX"/>
+ <compilerarg value="ppc" if="isOSX"/>
+
+ <!-- includes files -->
+ <includepath location="${jdk.include}"/>
+ <includepath location="${jdk.include}/linux" if="isLinux"/>
+ <includepath location="${jdk.include}/win32" if="isWin32"/>
+ <includepath location="${pd.include}"/>
+ <includepath location="${work.dir}"/>
+
+ <!-- linker specific -->
+ <libset dir="${pd.home}/bin" libs="pd" if="isWin32"/>
+ <libset if="isWin32" libs="Advapi32"/>
+ <linkerarg value="/export:pdj_setup" if="isWin32"/>
+
+ <linkerarg value="-export-dynamic" if="isLinux"/>
+ <linkerarg value="-Xlinker" if="isLinux"/>
+
+ <!-- OS X :-\ -->
+ <linkerarg value="-arch" if="isOSX"/>
+ <linkerarg value="-arch" if="isOSX"/>
+ <linkerarg value="i386" if="isOSX"/>
+ <linkerarg value="-arch" if="isOSX"/>
+ <linkerarg value="ppc" if="isOSX"/>
+
+ <linkerarg value="-framework" if="isOSX"/>
+ <linkerarg value="JavaVM" if="isOSX"/>
+ <linkerarg value="-framework" if="isOSX"/>
+ <linkerarg value="CoreFoundation" if="isOSX"/>
+ <linkerarg value="-flat_namespace" if="isOSX"/>
+ <linkerarg value="-framework" if="isOSX"/>
+ <linkerarg value="AppKit" if="isOSX"/>
+ <linkerarg value="-undefined" if="isOSX"/>
+ <linkerarg value="suppress" if="isOSX"/>
+
+ <linkerarg value="-bundle" if="isOSX"/>
+ <linkerarg value="-Xlinker" if="isOSX"/>
+
+ </cc>
+
+ <antcall target="-linux-rename"/>
+ <antcall target="-osx-rename"/>
+ </target>
+
+ <target name="package" depends="cl">
+ <copy todir="${dist.dir}">
+ <fileset dir="res"/>
+ </copy>
+
+ <copy todir="${dist.dir}/classes">
+ <!-- include the default package (examples) source -->
+ <fileset dir="src/java" includes="*.java"/>
+ <fileset dir="${classes.dir}" includes="*.class"/>
+ </copy>
+
+ </target>
+
+ <target name="test" depends="package">
+ <exec dir="${dist.dir}" executable="${pd.home}/bin/pd">
+ <arg line="-lib pdj ${pdj.test-patch}"/>
+ </exec>
+ </target>
+
+ <target name="test-dsp" depends="package">
+ <exec dir="${dist.dir}" executable="${pd.home}/bin/pd">
+ <arg line="-lib pdj ${pdj-tilde.test-patch}"/>
+ </exec>
+ </target>
+
+ <target name="test-mxdublin" depends="package">
+ <ant antfile="../mxdublin/build.xml" target="package" dir="../mxdublin"/>
+
+ <exec dir="${dist.dir}" executable="${pd.home}/bin/pd">
+ <env key="AWT_TOOLKIT" value="MToolkit"/>
+ <arg line="-lib pdj ../../mxdublin/res/test/test-mxdublin.pd"/>
+ </exec>
+ </target>
+
+ <target name="test-mxdublin-help" depends="package">
+ <ant antfile="../mxdublin/build.xml" target="package"
+ dir="../mxdublin"/>
+ <exec dir="${dist.dir}" executable="${pd.home}/bin/pd">
+ <arg line="-lib pdj ../../mxdublin/res/examples/help-mxdublin.pd"/>
+ </exec>
+ </target>
+
+ <target name="test-helpatch" depends="package">
+ <exec dir="${dist.dir}" executable="${pd.home}/bin/pd" >
+ <arg line="-lib pdj ../res/help-pdj.pd"/>
+ </exec>
+ </target>
+
+ <target name="dist" depends="clean">
+ <tar destfile="../pdj-${pdj.version}.tar.gz" compression="gzip">
+ <tarfileset dir="." prefix="pdj-${pdj.version}">
+ <exclude name="CVS"/>
+ </tarfileset>
+ </tar>
+ </target>
+
+ <target name="javadoc" depends="cl-java">
+ <mkdir dir="${javadoc.dir}"/>
+ <javadoc source="src" destdir="${javadoc.dir}"
+ windowtitle="pdj-MXJ API">
+ <fileset dir="src/java">
+ <include name="com/cycling74/max/*.java"/>
+ <include name="com/cycling74/msp/*.java"/>
+ <exclude name="*.java"/>
+ <exclude name="**/test/**"/>
+ <exclude name="**/*Test.java"/>
+ </fileset>
+ <link href="http://java.sun.com/j2se/1.4.2/docs/api/"/>
+ <doctitle><![CDATA[<h1>The MXJ Java API for pure-data</h1>
+ <p>This API is based on MXJ for Max/MSP.</p>]]></doctitle>
+ <bottom><![CDATA[This API is based on mxj for <a
+ href="http://www.cycling74.com/products/maxmsp.html">
+ Max/MSP</a> by Cycling74. Please see original MXJ
+ implementation.]]></bottom>
+ </javadoc>
+ </target>
+
+ <target name="dist-www" depends="javadoc">
+ <tar destfile="../pdj-web.tar.gz" compression="gzip">
+ <tarfileset dir="www">
+ <exclude name="CVS"/>
+ <exclude name=".*"/>
+ </tarfileset>
+ </tar>
+ </target>
+
+ <target name="dist-win32" depends="package,javadoc">
+ <zip destfile="${dist.dir}/pdj-${pdj.version}-win32.zip">
+ <zipfileset dir="www/api" includes="**" prefix="javadoc"/>
+ <fileset dir="${dist.dir}">
+ <include name="classes/**"/>
+ <include name="*.jar"/>
+ <include name="*.pd"/>
+ <include name="*.properties"/>
+ <include name="*.dll"/>
+ </fileset>
+ <fileset dir=".">
+ <include name="README"/>
+ <include name="LICENSE"/>
+ </fileset>
+ </zip>
+ </target>
+
+ <target name="dist-osx" depends="package,javadoc">
+ <zip destfile="../pdj-${pdj.version}-${pdj.archBuild}.zip">
+ <zipfileset dir="www/api" includes="**" prefix="javadoc"/>
+ <fileset dir="${dist.dir}">
+ <include name="classes/**"/>
+ <include name="*.jar"/>
+ <include name="*.pd"/>
+ <include name="*.properties"/>
+ <include name="*.${pdj.archBuild}"/>
+ </fileset>
+ <fileset dir=".">
+ <include name="README"/>
+ <include name="LICENSE"/>
+ </fileset>
+ </zip>
+ </target>
+</project>
diff --git a/lib/ant/cpptasks.jar b/lib/ant/cpptasks.jar
new file mode 100644
index 0000000..1b6e6ad
--- /dev/null
+++ b/lib/ant/cpptasks.jar
Binary files differ
diff --git a/res/help-pdj.pd b/res/help-pdj.pd
new file mode 100644
index 0000000..c325441
--- /dev/null
+++ b/res/help-pdj.pd
@@ -0,0 +1,63 @@
+#N canvas 338 130 812 533 12;
+#X text 17 7 pdj - JAVA PLUGIN EXTERNAL;
+#X obj 18 327 pdj help_class;
+#X text 16 49 The pdj object enables you to write Java external with
+Max/MSP API mxj.;
+#X obj 18 410 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10
+-262144 -1 -1 0 256;
+#X floatatom 18 97 5 0 0 0 - - -;
+#X text 81 95 <-- the method inlet(float) will be called;
+#X text 230 204 <-- the method callme(Atom []) will be called;
+#X msg 50 241 dynamic_method;
+#X text 203 238 <-- the method dynamic_method() will be called;
+#X msg 61 276 idontexists 10;
+#X text 222 275 <-- since idontexists is not in help_class \, an error
+will be thrown;
+#X msg 37 204 callme hola ouain;
+#X text 73 162 symbols sended to the java class will be resolved by
+the method name defined in the user class.;
+#X obj 32 126 bng 30 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X text 80 132 <-- the method bang() will be called;
+#X text 16 25 ======================================================
+;
+#X obj 139 373 print help_class outlet1;
+#N canvas 297 319 715 331 attributes 0;
+#X obj 19 229 pdj help_class @attr1 10;
+#X msg 39 127 get attr1;
+#X msg 19 92 attr1 \$1;
+#X floatatom 19 66 5 0 0 0 - - -;
+#X obj 254 260 print info-outlet;
+#X text 144 126 <-- using get with attribute name will output the value
+using the info outlet (the last outlet);
+#X text 18 8 Attributes are used to access fields in a class. To do
+this \, you can use the method declareAttribute in the constructor.
+;
+#X text 109 69 <-- calling the attribute with his name will set the
+attribute(field) value;
+#X text 48 172 you can also declare a attribute within a class by using
+@ in the constructor arguments. You must also provide a initial value
+to this field.;
+#X text 17 288 attribute value will always be sended to the last outlet
+of the object that is called the info-outlet.;
+#X connect 0 2 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 2 0;
+#X restore 656 40 pd attributes;
+#X text 652 7 *** see also :;
+#X text 169 321 first argument is the classname to find in classpath.
+additionnal arguments are given to the class constructor.;
+#X text 682 504 VERSION 0.8;
+#X text 99 406 see help_class.java in classes folder to see how this
+example is handled.;
+#X text 13 454 CLASSPATH INFORMATION : by default \, (whereis pdj)/lib/*.jar
+and /classes is added to the classpath. edit pdj.properties to add
+more .jar or directories to the pdj or system classpath;
+#X connect 1 0 3 0;
+#X connect 1 1 16 0;
+#X connect 4 0 1 0;
+#X connect 7 0 1 0;
+#X connect 9 0 1 0;
+#X connect 11 0 1 0;
+#X connect 13 0 1 0;
diff --git a/res/pdj-test.pd b/res/pdj-test.pd
new file mode 100644
index 0000000..7622c67
--- /dev/null
+++ b/res/pdj-test.pd
@@ -0,0 +1,42 @@
+#N canvas 765 257 450 295 10;
+#X obj 23 187 pdj pdj_test_class;
+#X obj 39 157 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 254 47 r allo;
+#X floatatom 263 98 5 0 0 0 - - -;
+#X obj 326 93 print xc;
+#X msg 82 160 wer wer 1;
+#X obj 135 34 metro 300;
+#X obj 170 3;
+#X obj 127 9 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X floatatom 218 11 5 0 0 0 - - -;
+#X obj 30 246 print outlet_1;
+#N canvas 0 0 450 300 graph1 0;
+#X array array_tester 100 float 1;
+#A 0 0 0.585717 0.528574 0.471431 0.442859 0.485716 0.585717 0.528574
+0.257144 0.385716 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X coords 0 1 99 -1 200 140 1;
+#X restore 220 143 graph;
+#X msg 25 117 testle;
+#X msg 21 11 sizeArray 10;
+#X msg 29 50 sizeArray 100;
+#X msg 169 68 get patate;
+#X msg 161 116 set patate 1;
+#X obj 105 222 print setter;
+#X connect 0 0 10 0;
+#X connect 0 1 17 0;
+#X connect 1 0 0 0;
+#X connect 2 0 3 0;
+#X connect 2 0 4 0;
+#X connect 5 0 0 0;
+#X connect 6 0 5 0;
+#X connect 8 0 6 0;
+#X connect 9 0 6 1;
+#X connect 12 0 0 0;
+#X connect 13 0 0 0;
+#X connect 14 0 0 0;
+#X connect 15 0 0 0;
+#X connect 16 0 0 0;
diff --git a/res/pdj.properties b/res/pdj.properties
new file mode 100644
index 0000000..1f00908
--- /dev/null
+++ b/res/pdj.properties
@@ -0,0 +1,81 @@
+# pdj.properties
+#
+# this property file is not 100% java properties aware (like: \\) so
+# pay special attention
+#
+# all parameters from this properties file will be copied to the java
+# system properties
+# =========================================================================
+
+# by default the pdj.jar (where the pdj.pd_linux is located) will be added
+# to the system classpath
+#
+# add your jars or directories of jars seperated with a ':' for unix and
+# ';' for windows
+#
+pdj.system-classpath=/home/asb2m10/workspace/mxdublin/work/mxdublin.jar
+
+# by default from where the pdj.pd_linux is located, every .jar in
+# ${pdj.home}/lib will be added to the pdj classpath
+#
+# add you jars or directories of jars seperated with a ':' for unix and
+# ';' for windows
+#
+# this is the pdj classpath (dynamic) to set :
+pdj.classpath=
+
+# this will print all .jars that are loaded before compiling/loading the
+# user class
+pdj.verbose-classloader=true
+
+# the type of compiler to use with the pdj classloader. use only 'javac'
+# or 'jikes'. do not put full path to your compiler
+#
+# to disable the automatic compilation simply use
+# pdj.compiler=null
+#
+pdj.compiler=javac
+
+# pdj compiler/classloader directory. by default, ${pdj.home}/classes
+# directory is used if this property is not defined. Before compiling, pdj
+# will check if your .java needs to be compiled.
+#
+# pdj.classes-dir=[your working directory]
+
+# this will redirect the java out/err streams to the pd console. If you need
+# to log pdj errors and exceptions to stderr and stdout, set this to false.
+#
+pdj.redirect-pdio=true
+
+# the jvm to use. If it is not found and the system is on Windows,
+# the jvm installed with the registry will be used. Not used on OS X
+#
+pdj.JAVA_HOME=/usr/local/java
+
+# the JVM version to use with OS X; linked to the Java Framework on OS X
+#
+pdj.osx.JVM_JAVA_VERSION=1.5.0
+
+# NOTE: pdj.home is set automagicly from where the pdj.pd_linux or pdj.dll
+# is installed (you need to put it in your pd path in OS X)
+
+# =========================================================================
+# VM ARGUMENTS
+#
+# to monitor memory and thread usage with 1.5 (jconsole) use
+# -Dcom.sun.management.jmxremote
+# to do remote debugging at port 3999
+# -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=3999
+# to check jni calls
+# -Xcheck:jni -verbose:jni
+#
+# All arguments on the same line just like $ java -X... -X... myclass
+#
+pdj.vm_args=-Xincgc -Xms32m -Xmx256m
+
+# tells pdj what type of jvm it must use: client or server. Not used on
+# OS X. client has lower starting time and can take less memory. server
+# starts slower and takes more memory, but once the code is executed,
+# it will be faster than in client mode.
+#
+pdj.vm_type=server
diff --git a/res/pdj~-panner.pd b/res/pdj~-panner.pd
new file mode 100644
index 0000000..9c16e2c
--- /dev/null
+++ b/res/pdj~-panner.pd
@@ -0,0 +1,10 @@
+#N canvas 0 0 450 300 10;
+#X obj 54 90 pdj~ panner;
+#X obj 126 48 nbx 5 14 0 127 0 0 empty empty empty 0 -6 0 10 -262144
+-1 -1 0 256;
+#X obj 54 48 osc~ 440;
+#X obj 55 136 dac~;
+#X connect 0 0 3 0;
+#X connect 0 1 3 1;
+#X connect 1 0 0 1;
+#X connect 2 0 0 0;
diff --git a/res/pdj~-test.pd b/res/pdj~-test.pd
new file mode 100644
index 0000000..f7e1c18
--- /dev/null
+++ b/res/pdj~-test.pd
@@ -0,0 +1,42 @@
+#N canvas 699 369 461 376 10;
+#N canvas 0 0 450 300 graph1 0;
+#X array signal-debug 441 float 0;
+#X coords 0 1.02 440 -1.02 400 150 1;
+#X restore 17 -91 graph;
+#X obj 18 -264 osc~ 440;
+#X obj 17 -183 *~;
+#X obj 49 -182 *~;
+#X obj 33 -208 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 65 -208 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 17 -143 tabwrite~ signal-debug;
+#X obj 202 -185 metro 250;
+#X obj 202 -208 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 81 -182 *~;
+#X obj 97 -208 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 84 -264 phasor~ 440;
+#X obj 172 -264 osc~ 880;
+#X obj 246 -255 nbx 5 14 0.01 1 1 0 empty empty empty 0 -6 0 10 -262144
+-1 -1 0.01 256;
+#X obj 184 -229 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144
+-1 -1;
+#X obj 18 -230 pdj~ pdj_test_tilde;
+#X connect 1 0 15 0;
+#X connect 2 0 6 0;
+#X connect 3 0 6 0;
+#X connect 4 0 2 1;
+#X connect 5 0 3 1;
+#X connect 7 0 6 0;
+#X connect 8 0 7 0;
+#X connect 9 0 6 0;
+#X connect 10 0 9 1;
+#X connect 11 0 15 1;
+#X connect 12 0 15 2;
+#X connect 13 0 15 3;
+#X connect 14 0 15 3;
+#X connect 15 0 2 0;
+#X connect 15 1 3 0;
+#X connect 15 2 9 0;
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 <stdlib.h>
+#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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef MSW
+ #include <io.h>
+#else
+ #include <unistd.h>
+#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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#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;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(long 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(long value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(short 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(short value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(byte 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(byte value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(char value) {
+ return new AtomString(String.valueOf(value));
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom[] newAtom(char value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(boolean value) {
+ return new AtomFloat( value ? 1:0 );
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom[] newAtom(boolean value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(float 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(float value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(double value) {
+ return new AtomFloat((float) value);
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom[] newAtom(double value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom newAtom(String value) {
+ return new AtomString(value);
+ }
+
+ /**
+ * Creates a new atom with the specified type.
+ * @param value the value of the atom
+ * @return the new Atom
+ */
+ public static Atom[] newAtom(String value[]) {
+ Atom[] ret = new Atom[value.length];
+ for(int i=0;i<value.length;i++) {
+ ret[i] = newAtom(value[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates an array of atoms from string tokens.
+ * @param values the atoms value seperated by string
+ * @return the array of atoms created from the list
+ */
+ public static Atom[] parse(String values) {
+ return parse(values, false);
+ }
+
+ /**
+ * Creates an array of atoms from strings tokens.
+ * @param values the atoms value seperated by string
+ * @param skipfirst skip the first token
+ * @return the array of atoms created from the list
+ */
+ public static Atom[] parse(String values, boolean skipfirst) {
+ ArrayList list = new ArrayList();
+ StringTokenizer token = new StringTokenizer(values);
+
+ if ( skipfirst && token.hasMoreTokens() ) {
+ token.nextToken();
+ }
+
+ while(token.hasMoreTokens()) {
+ list.add(newAtom(token.nextToken()));
+ }
+
+ Iterator i = list.iterator();
+ Atom[] ret = new Atom[list.size()];
+ for(int idx=0; i.hasNext(); idx++) {
+ ret[idx] = (Atom) i.next();
+ }
+
+ return ret;
+ }
+
+ // Atom methods
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get string value for this Atom. If the Atom does not contains a
+ * string it will throw an UnsupportedOperationException.
+ */
+ public String getString() {
+ throw new UnsupportedOperationException("pdj: this atom is not a string");
+ }
+
+ /**
+ * Get int value for this Atom. If the Atom does not contains a
+ * int it will throw an UnsupportedOperationException.
+ */
+ public int getInt() {
+ throw new UnsupportedOperationException("pdj: this atom is not a int");
+ }
+
+ /**
+ * Get float value for this Atom. If the Atom does not contains a
+ * float it will throw an UnsupportedOperationException.
+ */
+ public float getFloat() {
+ throw new UnsupportedOperationException("pdj: this atom is not a float");
+ }
+
+ /**
+ * Returns the Object representation of the Atom. If it is an int or a
+ * float, it will return a Int/Float object.
+ * @return The java.lang.* representation of the object
+ */
+ abstract Object toObject();
+
+ /**
+ * Returns the double value of this atom.
+ * @return the double value
+ */
+ public double toDouble() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of doubles.
+ * @param values the array of atoms
+ * @return array of doubles
+ */
+ public static double[] toDouble(Atom[] values) {
+ double[] ret = new double[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toDouble();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the int value of this atom.
+ * @return the int value
+ */
+ public int toInt() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of ints.
+ * @param values the array of atoms
+ * @return array of ints
+ */
+ public static int[] toInt(Atom[] values) {
+ int[] ret = new int[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toInt();
+ }
+ return ret;
+ }
+
+
+ /**
+ * Returns the char value of this atom.
+ * @return the char value
+ */
+ public char toChar() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of chars.
+ * @param values the array of atoms
+ * @return array of chars
+ */
+ public static char[] toChar(Atom[] values) {
+ char[] ret = new char[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toChar();
+ }
+ return ret;
+ }
+
+
+ /**
+ * Returns the byte value of this atom.
+ * @return the byte value
+ */
+ public byte toByte() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of bytes.
+ * @param values the array of atoms
+ * @return array of bytes
+ */
+ public static byte[] toByte(Atom[] values) {
+ byte[] ret = new byte[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toByte();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the long value of this atom.
+ * @return the long value
+ */
+ public long toLong() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of longs.
+ * @param values the array of atoms
+ * @return array of longs
+ */
+ public static long[] toLong(Atom[] values) {
+ long[] ret = new long[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toLong();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the short value of this atom.
+ * @return the short value
+ */
+ public short toShort() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of shorts.
+ * @param values the array of atoms
+ * @return array of shorts
+ */
+ public static short[] toShort(Atom[] values) {
+ short[] ret = new short[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toShort();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the float value of this atom.
+ * @return the float value
+ */
+ public float toFloat() {
+ return 0;
+ }
+
+ /**
+ * Transform an array of Atom into an array of floats.
+ * @param values the array of atoms
+ * @return array of floats
+ */
+ public static float[] toFloat(Atom[] values) {
+ float[] ret = new float[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toFloat();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the boolean value of this atom.
+ * @return the boolean value
+ */
+ public boolean toBoolean() {
+ return true;
+ }
+
+ /**
+ * Transform an array of Atom into an array of booleans.
+ * @param values the array of atoms
+ * @return array of booleans
+ */
+ public static boolean[] toBoolean(Atom[] values) {
+ boolean[] ret = new boolean[values.length];
+
+ for(int i=0;i<values.length;i++) {
+ ret[i] = values[i].toBoolean();
+ }
+ return ret;
+ }
+
+ /**
+ * Transform an array of Atom into an array of strings.
+ * @param array the array of atoms
+ * @return array of strings
+ */
+ public static String[] toString(Atom[] array) {
+ String[] ret = new String[array.length];
+
+ for(int i=0;i<array.length;i++) {
+ ret[i] = array[i].toString();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns true if the Atom has been created with a float.
+ * @return true if the Atom has been created with a float
+ */
+ public boolean isFloat() {
+ return false;
+ }
+
+ /**
+ * Returns true if the Atom has been created with a String.
+ * @return true if the Atom has been created with a String
+ */
+ public boolean isString() {
+ return false;
+ }
+
+ /**
+ * Returns true if the Atom has been created with a int.
+ * @return true if the Atom has been created with a int
+ */
+ public boolean isInt() {
+ return false;
+ }
+
+ /**
+ * Returns the array of Atom into one string. Atom elements are
+ * seperated by spaces.
+ * @param array the array of atom
+ * @return one string representation of the atom
+ */
+ public static String toOneString(Atom[] array) {
+ StringBuffer sb = new StringBuffer();
+ boolean appendSpace = false;
+
+ for(int i=0;i<array.length;i++) {
+ if ( !appendSpace ) {
+ sb.append(' ');
+ appendSpace = true;
+ }
+ sb.append(array[i].toString());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if the instance has the same value of the object
+ * <code>object</code>. Similar to <code>"ok".equals("ok");</code>
+ */
+ 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;i<to+1;i++) {
+ if ( list[i].equals(org) )
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Remove at index <code>from</code> to index <code>to</code>.
+ * @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 <code>howmany</code> 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 <code>howmany</code> 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<list.length;i++) {
+ ret[last-i] = list[i];
+ }
+ return ret;
+ }
+
+ /**
+ * Rotates array content x number of times.
+ * @param list the list to rotate
+ * @param nbTimes the number of time that the array must be rotated
+ * @return the rotated list
+ */
+ public static Atom[] rotate(Atom[] list, int nbTimes) {
+ Atom ret[] = new Atom[list.length];
+
+ for(int i=0;i<list.length;i++) {
+ ret[(i+nbTimes) % list.length] = list[i];
+ }
+
+ return ret;
+ }
+
+ /**
+ * Don't know what this does. Max/MSP says: it does an union. If
+ * you want me to support this, send me an email and what it does.
+ * @param first
+ * @param second
+ * @return nothing yet
+ * @throws UnsupportedOperationException
+ */
+ public static Atom[] union(Atom[] first, Atom[] second) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("not implemented.");
+ }
+
+ /**
+ * Don't know what this does. Max/MSP says: it does an intersection.
+ * If you want me to support this, send me an email and what it does.
+ * @param first
+ * @param second
+ * @return nothing yet
+ * @throws UnsupportedOperationException
+ */
+ public static Atom[] intersection(Atom[] first, Atom[] second) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("not implemented.");
+ }
+
+ /**
+ * Used to return a string representation of the list with atom type.
+ * @param values the array of atoms
+ * @return the string representation of the array
+ */
+ public static String toDebugString(Atom[] values) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Atom[");
+ sb.append(values.length);
+ sb.append("]=");
+ for(int i=0;i<values.length;i++) {
+ sb.append('{');
+ sb.append(values[i].toString());
+ if ( values[i] instanceof AtomString )
+ sb.append(":S}");
+ else
+ sb.append(":F}");
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/java/com/cycling74/max/AtomFloat.java b/src/java/com/cycling74/max/AtomFloat.java
new file mode 100644
index 0000000..ef763f8
--- /dev/null
+++ b/src/java/com/cycling74/max/AtomFloat.java
@@ -0,0 +1,96 @@
+package com.cycling74.max;
+
+class AtomFloat extends Atom {
+ private static final long serialVersionUID = 6497612039215711052L;
+ private float value;
+
+ AtomFloat(float value) {
+ super(DataTypes.FLOAT);
+ this.value = value;
+ }
+
+ public int compareTo(Object obj) {
+ if ( obj instanceof AtomFloat ) {
+ AtomFloat f = (AtomFloat) obj;
+ if ( f.getFloat() <= getFloat() )
+ return 1;
+ else
+ return -1;
+ }
+
+ if ( obj instanceof AtomString ) {
+ return 1;
+ }
+
+ throw new ClassCastException();
+ }
+
+ public boolean isFloat() {
+ return true;
+ }
+
+ public boolean isInt() {
+ return true;
+ }
+
+ public Object toObject() {
+ return new Float(value);
+ }
+
+ public int toInt() {
+ return (int) value;
+ }
+
+ public double toDouble() {
+ return value;
+ }
+
+ public long toLong() {
+ return (long) value;
+ }
+
+ public float toFloat() {
+ return value;
+ }
+
+ public short toShort() {
+ return (short) value;
+ }
+
+ public byte toByte() {
+ return (byte) value;
+ }
+
+ public char toChar() {
+ return (char) value;
+ }
+
+ public String toString() {
+ return new Float(value).toString();
+ }
+
+ public boolean toBoolean() {
+ return value != 0;
+ }
+
+ public float getFloat() {
+ return value;
+ }
+
+ public int getInt() {
+ return (int) value;
+ }
+
+ public boolean equals(Object comp) {
+ if ( !(comp instanceof AtomFloat) ) {
+ return false;
+ }
+ AtomFloat test = (AtomFloat) comp;
+
+ return test.value == value;
+ }
+
+ public int hashCode() {
+ return new Float(value).hashCode();
+ }
+}
diff --git a/src/java/com/cycling74/max/AtomString.java b/src/java/com/cycling74/max/AtomString.java
new file mode 100644
index 0000000..c4135af
--- /dev/null
+++ b/src/java/com/cycling74/max/AtomString.java
@@ -0,0 +1,63 @@
+package com.cycling74.max;
+
+class AtomString extends Atom {
+ private static final long serialVersionUID = 1738861344247036680L;
+ private String value;
+
+ AtomString(String value) {
+ super(DataTypes.MESSAGE);
+ this.value = value;
+ }
+
+ public int compareTo(Object obj) {
+ if ( obj instanceof AtomString ) {
+ AtomString s = (AtomString) obj;
+ return s.getString().compareTo(getString());
+ }
+ if ( obj instanceof AtomFloat ) {
+ return -1;
+ }
+ throw new ClassCastException();
+ }
+
+ public boolean isString() {
+ return true;
+ }
+
+ public Object toObject() {
+ return value;
+ }
+
+ public byte toByte() {
+ return (byte) value.charAt(0);
+ }
+
+ public char toChar() {
+ return value.charAt(0);
+ }
+
+ public String toString() {
+ return value;
+ }
+
+ public boolean toBoolean() {
+ return !value.equals("false");
+ }
+
+ public String getString() {
+ return value;
+ }
+
+ public boolean equals(Object comp) {
+ if ( !(comp instanceof AtomString) ) {
+ return false;
+ }
+ AtomString test = (AtomString) comp;
+
+ return test.value.equals(value);
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/java/com/cycling74/max/Attribute.java b/src/java/com/cycling74/max/Attribute.java
new file mode 100644
index 0000000..2e5cccf
--- /dev/null
+++ b/src/java/com/cycling74/max/Attribute.java
@@ -0,0 +1,463 @@
+package com.cycling74.max;
+
+import java.lang.reflect.*;
+import com.e1.pdj.PDJError;
+
+class Attribute {
+ boolean readOnly = false;
+
+ // the method setter and getter if specified
+ Method setter;
+ Method getter;
+
+ // the field to update if no setter or getter is specified
+ Field field;
+
+ // attribute type
+ char type;
+
+ // the owner of the field or method
+ Object obj;
+
+ // tells if the arguments are a array
+ boolean isArray;
+
+ Attribute(Object obj, String name, String setter_name, String getter_name) {
+ char typeCheck = 0;
+
+ try {
+ this.obj = obj;
+ if ( getter == null ) {
+ field = obj.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ type = mapType(field.getType());
+ if ( type == '-' ) {
+ throw new PDJError("Field: " + name + " is a unsupported type");
+ }
+ isArray = Character.isLowerCase(type);
+ } else {
+ getter = obj.getClass().getDeclaredMethod(getter_name, null);
+ type = mapType(getter.getReturnType());
+ if ( type == '-' ) {
+ throw new PDJError("Method: " + getter_name + " returns a unsupported type");
+ }
+ isArray = Character.isLowerCase(type);
+ }
+
+ if ( setter == null ) {
+ field = obj.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ typeCheck = mapType(field.getType());
+ } else {
+ Method methods[] = obj.getClass().getMethods();
+
+ for(int i=0;i<methods.length;i++) {
+ if ( methods[i].getName().equals(setter_name) ) {
+ Class c[] = methods[i].getParameterTypes();
+ if ( c.length > 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<value.length;i++)
+ Array.setBoolean(work, i, value[i].toBoolean());
+ break;
+ case 'f' :
+ field.setFloat(obj, value[0].toFloat());
+ break;
+ case 'F' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setFloat(work, i, value[i].toFloat());
+ break;
+ case 'i' :
+ field.setInt(obj, value[0].toInt());
+ break;
+ case 'I' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setInt(work, i, value[i].toInt());
+ break;
+ case 'd' :
+ field.setDouble(obj, value[0].toDouble());
+ break;
+ case 'D' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setDouble(work, i, value[i].toDouble());
+ break;
+ case 'b' :
+ field.setByte(obj, value[0].toByte());
+ break;
+ case 'B' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setByte(work, i, value[i].toByte());
+ break;
+ case 'c' :
+ field.setChar(obj, value[0].toChar());
+ break;
+ case 'C' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setChar(work, i, value[i].toChar());
+ break;
+ case 's' :
+ field.setShort(obj, value[0].toShort());
+ break;
+ case 'S' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.setShort(work, i, value[i].toShort());
+ break;
+ case 'g' :
+ field.set(obj, value.toString());
+ break;
+ case 'G' :
+ work = field.get(obj);
+ for (i=0;i<value.length;i++)
+ Array.set(work, i, value[i].toString());
+ break;
+ case 'a' :
+ field.set(obj, value[0]);
+ break;
+ case 'A' :
+ field.set(obj, value);
+ break;
+ default:
+ throw new PDJError("Unable to pass attribute value");
+ }
+ }
+
+ private Atom[] fieldGetter() throws Exception {
+ Object array = null;
+ Atom[] ret;
+ int i;
+
+ if ( isArray ) {
+ ret = new Atom[1];
+ } else {
+ array = field.get(obj);
+ ret = new Atom[Array.getLength(array)];
+ }
+
+ switch ( type ) {
+ case 'z' :
+ ret[0] = Atom.newAtom(field.getBoolean(obj));
+ break;
+ case 'Z' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getBoolean(array, i));
+ break;
+ case 'f' :
+ ret[0] = Atom.newAtom(field.getFloat(obj));
+ break;
+ case 'F' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getFloat(array, i));
+ break;
+ case 'i' :
+ ret[0] = Atom.newAtom(field.getInt(obj));
+ break;
+ case 'I' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getInt(array, i));
+ break;
+ case 'd' :
+ ret[0] = Atom.newAtom(field.getDouble(obj));
+ break;
+ case 'D' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getDouble(array, i));
+ break;
+ case 'b' :
+ ret[0] = Atom.newAtom(field.getByte(obj));
+ break;
+ case 'B' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getByte(array, i));
+ break;
+ case 'c' :
+ ret[0] = Atom.newAtom(field.getChar(obj));
+ break;
+ case 'C' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getChar(array, i));
+ break;
+ case 's' :
+ ret[0] = Atom.newAtom(field.getShort(obj));
+ break;
+ case 'S' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(Array.getShort(array, i));
+ break;
+ case 'g' :
+ ret[0] = Atom.newAtom((String) field.get(obj));
+ break;
+ case 'G' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom((String) Array.get(array, i));
+ break;
+ case 'a' :
+ ret[0] = (Atom) field.get(obj);
+ break;
+ case 'A' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = (Atom) Array.get(array, i);
+ break;
+ default:
+ throw new PDJError("Unable to pass attribute value");
+ }
+
+ return ret;
+ }
+
+ private Atom[] methodGetter() throws Exception {
+ Object array = null;
+ Atom[] ret;
+ int i;
+
+ if ( isArray ) {
+ ret = new Atom[1];
+ } else {
+ array = field.get(obj);
+ ret = new Atom[Array.getLength(array)];
+ }
+
+ switch ( type ) {
+ case 'z' :
+ ret[0] = Atom.newAtom(((Boolean)getter.invoke(obj, null)).booleanValue());
+ break;
+ case 'Z' :
+ boolean tmpz[] = (boolean[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpz[i]);
+ break;
+ case 'f' :
+ ret[0] = Atom.newAtom(((Float)getter.invoke(obj, null)).floatValue());
+ break;
+ case 'F' :
+ float tmpf[] = (float[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpf[i]);
+ break;
+ case 'i' :
+ ret[0] = Atom.newAtom(((Integer)getter.invoke(obj, null)).intValue());
+ break;
+ case 'I' :
+ int tmpi[] = (int[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpi[i]);
+ break;
+ case 'd' :
+ ret[0] = Atom.newAtom(((Double)getter.invoke(obj, null)).doubleValue());
+ break;
+ case 'D' :
+ double tmpd[] = (double[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpd[i]);
+ break;
+ case 'b' :
+ ret[0] = Atom.newAtom(((Byte)getter.invoke(obj, null)).byteValue());
+ break;
+ case 'B' :
+ byte tmpb[] = (byte[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpb[i]);
+ break;
+ case 'c' :
+ ret[0] = Atom.newAtom(((Character)getter.invoke(obj, null)).charValue());
+ break;
+ case 'C' :
+ char tmpc[] = (char[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmpc[i]);
+ break;
+ case 's' :
+ ret[0] = Atom.newAtom(((Short)getter.invoke(obj, null)).shortValue());
+ break;
+ case 'S' :
+ short tmps[] = (short[]) getter.invoke(obj, null);
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom(tmps[i]);
+ break;
+ case 'g' :
+ ret[0] = Atom.newAtom((String) getter.invoke(obj, null));
+ break;
+ case 'G' :
+ for (i=0;i<ret.length;i++)
+ ret[i] = Atom.newAtom((String) Array.get(array, i));
+ break;
+ case 'a' :
+ ret[0] = (Atom) getter.invoke(obj, null);
+ break;
+ case 'A' :
+ ret = (Atom []) getter.invoke(obj, null);
+ break;
+ default:
+ throw new PDJError("Unable to pass attribute value");
+ }
+
+ return ret;
+ }
+
+ private Object setterCast(Atom values[]) throws Exception {
+ int i;
+
+ switch ( type ) {
+ case 'z' :
+ return new Boolean(values[0].toBoolean());
+ case 'Z' :
+ boolean tmpz[] = new boolean[values.length];
+ for (i=0;i<values.length; i++)
+ tmpz[i] = values[i].toBoolean();
+ return tmpz;
+ case 'f' :
+ return new Float(values[0].toFloat());
+ case 'F' :
+ float tmpf[] = new float[values.length];
+ for (i=0;i<values.length; i++)
+ tmpf[i] = values[i].toFloat();
+ return tmpf;
+ case 'i' :
+ return new Integer(values[0].toInt());
+ case 'I' :
+ int tmpi[] = new int[values.length];
+ for (i=0;i<values.length; i++)
+ tmpi[i] = values[i].toInt();
+ return tmpi;
+ case 'd' :
+ return new Double(values[0].toDouble());
+ case 'D' :
+ double tmpd[] = new double[values.length];
+ for (i=0;i<values.length; i++)
+ tmpd[i] = values[i].toDouble();
+ return tmpd;
+ case 'b' :
+ return new Byte(values[0].toByte());
+ case 'B' :
+ byte tmpb[] = new byte[values.length];
+ for (i=0;i<values.length; i++)
+ tmpb[i] = values[i].toByte();
+ return tmpb;
+ case 'c' :
+ return new Character(values[0].toChar());
+ case 'C' :
+ char tmpc[] = new char[values.length];
+ for (i=0;i<values.length; i++)
+ tmpc[i] = values[i].toChar();
+ return tmpc;
+ case 's' :
+ return new Short(values[0].toShort());
+ case 'S' :
+ short tmps[] = new short[values.length];
+ for (i=0;i<values.length; i++)
+ tmps[i] = values[i].toShort();
+ return tmps;
+ case 'g' :
+ return values[0].toString();
+ case 'G' :
+ String tmpg[] = new String[values.length];
+ for (i=0;i<values.length;i++)
+ tmpg[i] = values[0].toString();
+ return tmpg;
+ case 'a' :
+ return values[0];
+ case 'A' :
+ return values;
+ default:
+ throw new PDJError("Unable to pass attribute value");
+ }
+ }
+
+ Atom[] get() {
+ try {
+ if ( getter == null )
+ return fieldGetter();
+ return methodGetter();
+ } catch ( Exception e ){
+ throw new PDJError(e);
+ }
+ }
+
+ void set(Atom values[]) {
+ if ( readOnly )
+ throw new PDJError("Field is readonly");
+
+ try {
+ if ( setter == null )
+ fieldSetter(values);
+ else
+ setter.invoke(obj, new Object[] { setterCast(values) });
+ } catch (Exception e) {
+ throw new PDJError(e);
+ }
+ }
+
+}
diff --git a/src/java/com/cycling74/max/Callback.java b/src/java/com/cycling74/max/Callback.java
new file mode 100644
index 0000000..1eed30f
--- /dev/null
+++ b/src/java/com/cycling74/max/Callback.java
@@ -0,0 +1,213 @@
+package com.cycling74.max;
+
+import java.lang.reflect.*;
+
+/**
+ * Used to transform a java method into an Executable object. This
+ * simplify the job of always implementing the executable
+ * interface on all your objects since you can define it dynamically
+ * with this class.
+ * <p><blockquote><pre>
+ *
+ * class Myclass {
+ * public void doit() {
+ * do_something_fun()
+ * }
+ * }
+ *
+ * ...
+ *
+ * Myclass myclass = new Myclass();
+ * Executable e = new Callback(myclass, "doit");
+ * MaxClock clock = new MaxClock(e);
+ *
+ * </pre></blockquote></p>
+ */
+public class Callback implements Executable {
+ private Method method;
+ private String methodName;
+ private Object obj;
+ private Object args[];
+
+ /**
+ * Will call method <i>methodName</i> 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 <i>methodName</i> 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 <i>methodName</i> 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 <i>methodName</i> 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 <i>methodName</i> 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 <i>methodName</i> 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 <i>methodName</i> 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<params.length;i++) {
+ clz[i] = params[i].getClass();
+ }
+
+ return clz;
+ }
+
+ /**
+ * Execute the method with arguments specified at constructor
+ */
+ public void execute() {
+ if ( obj == null ) {
+ throw new Error("pdj: this Callback has never been initialised");
+ }
+
+ try {
+ method.invoke(obj, args);
+ } catch (IllegalArgumentException e) {
+ MaxSystem.error("pdj: IllegalArgumentException:" + e);
+ } catch (IllegalAccessException e) {
+ MaxSystem.error("pdj: IllegalAccessException:" + e);
+ } catch (InvocationTargetException e) {
+ MaxSystem.error("pdj: InvocationTargetException:" + e);
+ }
+ }
+
+ /**
+ * Returns the argument that will be used when the execute method
+ * will be invoke.
+ * @return the array of arguments
+ */
+ public Object[] getArgs() {
+ return args;
+ }
+
+ /**
+ * Returns the object used to issue the call
+ * @return the object to use with execute
+ */
+ public Object getObject() {
+ return obj;
+ }
+
+ /**
+ * Returns the Method that will be used with the execute call
+ * @return the method given by reflection
+ */
+ public Method getMethod() {
+ return method;
+ }
+
+ /**
+ * Returns the method name invoked on object with the execute call
+ * @return the method name
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * Sets int argument to method
+ * @param i int value
+ */
+ public void setArgs(int i) {
+ args = new Object[] { new Integer(i) };
+ }
+
+ /**
+ * Sets float argument to method
+ * @param f float argument
+ */
+ public void setArgs(float f) {
+ args = new Object[] { new Float(f) };
+ }
+
+ /**
+ * Sets String argument to method
+ * @param value int value
+ */
+ public void setArgs(String value) {
+ args = new Object[] { value };
+ }
+
+ /**
+ * Sets boolean argument to method
+ * @param flag boolean value
+ */
+ public void setArgs(boolean flag) {
+ args = new Object[] { flag ? Boolean.TRUE : Boolean.FALSE };
+ }
+
+ /**
+ * Set the argument for the execute call
+ * @param args the array object to pass to the method
+ */
+ public void setArgs(Object args[]) {
+ this.args = (Object[]) args.clone();
+ }
+}
diff --git a/src/java/com/cycling74/max/DataTypes.java b/src/java/com/cycling74/max/DataTypes.java
new file mode 100644
index 0000000..db0f2a2
--- /dev/null
+++ b/src/java/com/cycling74/max/DataTypes.java
@@ -0,0 +1,14 @@
+
+package com.cycling74.max;
+
+/**
+ * DataTypes values that can be used to defined an inlet or outlet.
+ */
+public class DataTypes {
+ public static int ALL = 15;
+ public static int ANYTHING = 15;
+ public static int FLOAT = 2;
+ public static int INT = 1;
+ public static int LIST = 4;
+ public static int MESSAGE = 8;
+}
diff --git a/src/java/com/cycling74/max/Executable.java b/src/java/com/cycling74/max/Executable.java
new file mode 100644
index 0000000..c658d3f
--- /dev/null
+++ b/src/java/com/cycling74/max/Executable.java
@@ -0,0 +1,14 @@
+package com.cycling74.max;
+
+/**
+ * Defines an executable object. An object that implements a
+ * Executable interface can later be used with a MaxClock or a
+ * MaxSystem.deferLow for example since it defines the
+ * execute method.
+ */
+public interface Executable {
+ /**
+ * The method to execute.
+ */
+ public void execute();
+}
diff --git a/src/java/com/cycling74/max/MaxClock.java b/src/java/com/cycling74/max/MaxClock.java
new file mode 100644
index 0000000..5c5acbb
--- /dev/null
+++ b/src/java/com/cycling74/max/MaxClock.java
@@ -0,0 +1,113 @@
+package com.cycling74.max;
+
+/**
+ * Used to delay the execution of a block of code. Java implementation
+ * of a pdclock.
+ * <p><blockquote><pre>
+ *
+ * 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)
+ * }
+ * }
+ * </pre></blockquote></p>
+ */
+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 <code>e</code>.
+ * @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.
+ * <p>Here is a basic guideline for using the MaxObject:</p>
+ * <p><blockquote><pre>
+ *
+ * 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&#60args.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);
+ * }
+ * }
+ * </pre></blockquote></p>
+ * <p>Compile this class by adding pdj.jar on the classpath and put the
+ * example.class in the <i>classes</i> directory found in your pdj home.
+ * You can also edit directly this class in the <i>classes</i> directory
+ * and pdj will try to compile it for you.</p>
+ */
+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; i<types.length; i++) {
+ _inlets_ptr[pos++] = newInlet(types[i]);
+ }
+ }
+
+ /**
+ * Declare the outlets used by this object.
+ * @see com.cycling74.max.DataTypes
+ * @param types the type of message that this inlet will use.
+ */
+ protected void declareOutlets(int[] types) {
+ if ( _outlets_ptr != null ) {
+ throw new IllegalStateException(name + ": outlets already defined");
+ }
+ _outlets_ptr = new long[types.length];
+
+ int pos = 0;
+ for(int i=0; i<types.length; i++) {
+ long ret = newOutlet(types[i]);
+ if ( ret != 0 )
+ _outlets_ptr[pos++] = ret;
+ }
+ }
+
+ /**
+ * Used to defined typed input and output for this object. Strings are
+ * used to define the type of inlet/outlet. For example, "ffi" will create
+ * a float/float/integer inlet/outlet. 'f' denotes a float, 'i' a int, 'm'
+ * a message, 'l' a list, 's' for a signal. Anything else will be considered
+ * as a outlet of type anything. <b>PD doesn't type all data yet, so every
+ * inlet/outlet are typed as anything; except for signals.</b>
+ * @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;i<in_type.length;i++) {
+ if ( ins.charAt(i) == 's' )
+ in_type[i] = MSPObject.SIGNAL;
+ else
+ in_type[i] = DataTypes.ANYTHING;
+ }
+ declareInlets(in_type);
+
+ for (i=0;i<out_type.length;i++) {
+ if ( ins.charAt(i) == 's' )
+ in_type[i] = MSPObject.SIGNAL;
+ else
+ in_type[i] = DataTypes.ANYTHING;
+ }
+ declareOutlets(out_type);
+ }
+
+ /**
+ * Quickie method for declaring both inlet and outlet that will use
+ * any type of message (DataTypes.ANYTHING)
+ * @param in the number of inlet to create
+ * @param out the number of outlet to create
+ */
+ protected void declareIO(int in, int out) {
+ if ( _inlets_ptr != null || _outlets_ptr != null ) {
+ throw new IllegalStateException(name + ": inlets/outlets already defined.");
+ }
+
+ _inlets_ptr = new long[in];
+ _outlets_ptr = new long[out];
+
+ for(int i=0; i<in; i++) {
+ _inlets_ptr[i] = newInlet(DataTypes.ANYTHING);
+ }
+
+ for(int i=0; i<out; i++) {
+ _outlets_ptr[i] = newOutlet(DataTypes.ANYTHING);
+ }
+ }
+
+ /**
+ * Creates a attribute with default setter and getter. The name used is
+ * actually a java field in the current class. Later, you can send 'a 10'
+ * and pdj will set 10 to your java field named 'a'. You can also
+ * use 'get a' that will return its value to the info outlet. The info
+ * outlet is the last outlet of your object.
+ * @param name name of the java field in this class to map.
+ */
+ protected void declareAttribute(String name) {
+ Attribute attr = new Attribute(this, name, null, null);
+ attributes.put(name, attr);
+ }
+
+ /**
+ * Creates a attribute with default getter no setter. The name used is
+ * actually a java field in the current class.
+ * @param name name of the java field in this class to map.
+ */
+ protected void declareReadOnlyAttribute(String name) {
+ Attribute attr = new Attribute(this, name, null, null);
+ attr.readOnly = true;
+ attributes.put(name, attr);
+ }
+
+
+ void declareAttribute(String name, String getter, String setter) {
+ Attribute attr = new Attribute(this, name, getter, setter);
+ attributes.put(name, attr);
+ }
+
+ void declareReadOnlyAttribute(String name, String getter) {
+ Attribute attr = new Attribute(this, name, getter, null);
+ attr.readOnly = true;
+ attributes.put(name, attr);
+ }
+
+ /**
+ * Tells the constructor to create a info outlet. The info outlet is used
+ * by attributes when the get method is used. The return value of the 'get'
+ * will be return to this outlet. The info outlet is always the last
+ * outlet of the object. By default, the info outlet is always created.
+ * @param flag false to prevent the creation of the info outlet
+ */
+ protected void createInfoOutlet(boolean flag) {
+ toCreateInfoOutlet = flag;
+ }
+
+ /**
+ * Returns the type the inlet at index 'idx'. <i>Not fully implemented,
+ * returns always DataTypes.ANYTHING with pdj</i>
+ * @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'. <i>Not fully implemented,
+ * returns always DataTypes.ANYTHING with pdj</i>
+ * @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
+ * <code>outlet(int, float)</code> otherwise <code>outlet(int, String)</code>
+ * @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,
+ * <code>outlet(outlet, Atom value)</code> 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
+ * <code>getInlet()</code> 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 <code>getInlet()</code> 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
+ * <code>getInlet()</code> 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
+ * <code>getInlet()</code> 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 <code>getInlet()</code>
+ * 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
+ /////////////////////////////////////////////////////////////
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ public void viewsource() {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b> Throws <code>UnsupportdOperationException</code></b>
+ */
+ public static Object getContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the object representing the pd patch.
+ */
+ public MaxPatcher getParentPatcher() {
+ return patch;
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ protected void save() {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ protected void setInletAssist(String[] messages) {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ protected void setInletAssist(int index, String message) {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ protected void setOutletAssist(String[] messages) {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ protected void setOutletAssist(int index, String message) {
+ }
+
+ /**
+ * <b>NOT USED IN PD.</b>
+ */
+ 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;i++) {
+ if ( args_complete[i].toString().startsWith("@") ) {
+ lattr.add(args_complete[i].toString().substring(1));
+ if ( i+1>=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<args.length;i++) {
+ args[i] = (Atom) largs.get(i);
+ }
+
+ Class argType[] = new Class[1];
+ argType[0] = Atom[].class;
+
+ pushPdjPointer(_pdobj_ptr);
+
+ // instanciate the object
+ if ( args.length > 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 <code>set</code>
+ * will trigger/execute the "job". If <code>set</code> is called while
+ * the "job" is running, the "job" won't be called again.
+ * <p>On PDJ, a Qelem is simply a Java thread that wait to be triggered.
+ * </p>
+ */
+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. <b>Does nothing on PDJ</b>
+ */
+ 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;
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static void registerCommandAccelerator(char c) {
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static void registerCommandAccelerators(char c[]) {
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static void unRegisterCommandAccelerator(char c) {
+ }
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static void unRegisterCommandAccelerators(char c[]) {
+ }
+
+ // not compatible Max methods :
+ ////////////////////////////////////////////////////////
+ /**
+ * <b>Not supported in PD</b>
+ */
+ 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;
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static public void hideCursor() {
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ static public void showCursor() {
+ }
+
+ /**
+ * <b>Not supported in PD</b>
+ */
+ 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 @@
+<html>
+<body>
+<p>Basic package for PDJ</p>
+</body>
+</html> \ 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 <i>not used in pd</i>
+ * @return the array contents
+ */
+ public static float[] peek(String name, int channel) {
+ return getArray(name, 0, -1);
+ }
+
+ /**
+ * Returns the array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param start the start index of the array
+ * @param length the size of the array to return
+ * @return the array contents
+ */
+ public static float[] peek(String name, int channel, long start, long length) {
+ return getArray(name, start, length);
+ }
+
+ /**
+ * Returns the array content value at a specific position.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param index the start index of the array
+ * @return the value stored at index <code>start</code>
+ */
+ public static float peek(String name, int channel, long index) {
+ float ret[] = getArray(name, index, 1);
+ if ( ret == null )
+ return 0;
+ return ret[0];
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param values the array to set
+ */
+ public static void poke(String name, float values[]) {
+ setArray(name, 0, values);
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param values the array to set
+ */
+ public static void poke(String name, int channel, float values[]) {
+ setArray(name, 0, values);
+ }
+
+ /**
+ * Sets array content.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param start the start index of the array
+ * @param values the array to set
+ */
+ public static void poke(String name, int channel, long start, float values[]) {
+ setArray(name, start, values);
+ }
+
+ /**
+ * Set a value in a array.
+ * @param name the array name
+ * @param channel <i>not used in pd</i>
+ * @param index the index in the array to set
+ * @param value the value to set in the array
+ */
+ public static void poke(String name, int channel, long index, float value) {
+ float content[] = new float[1];
+ content[0] = value;
+ setArray(name, index, content);
+ }
+
+ /**
+ * Sets the array size.
+ * @param name the array name
+ * @param numchannel <i>not used in pd</i>
+ * @param size the new array size;
+ */
+ public static native void setSize(String name, int numchannel, long size);
+
+ /**
+ * Returns the array size
+ * @param name the array name
+ * @return the array size or -1 if not found
+ */
+ public static native long getSize(String name);
+
+ private static native float[] getArray(String name, long from, long size);
+ private static native void setArray(String name, long from, float[]content);
+ private MSPBuffer() {}
+
+ /**
+ * Return the number of channel for this array. Useless in PD cause there
+ * is no channels in a array.
+ * @param name array name.
+ * @return always returns 1 on pd; unless the name is not defined.
+ */
+ public static int getChannel(String name) {
+ // resolv the name
+ if ( getSize(name) != -1 )
+ return 1;
+ return -1;
+ }
+}
diff --git a/src/java/com/cycling74/msp/MSPObject.java b/src/java/com/cycling74/msp/MSPObject.java
new file mode 100644
index 0000000..be2d68b
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPObject.java
@@ -0,0 +1,163 @@
+package com.cycling74.msp;
+
+import java.lang.reflect.Method;
+
+import com.cycling74.max.DataTypes;
+import com.cycling74.max.MaxObject;
+
+/**
+ * Main object to extend to use dsp with pd. You will need to
+ * use the pdj~ external if you want to process signals. Before
+ * the dsp starts processing, the method dsp will be called and it must
+ * return the performer method that will process each dsp cycles.
+ *
+ * <p>Here is a basic guideline for using the MSPObject:</p>
+ * <p><blockquote><pre>
+ * import com.cycling74.max.*;
+ * import com.cycling74.msp.*;
+ * import java.lang.reflection.Method;
+ *
+ * public class panner extends MSPObject {
+ * float left = 1, right = 1;
+ *
+ * public panner() {
+ * declareInlets( new int[] { SIGNAL, DataTypes.ANYTHING } );
+ * declareOutlets( new int[] { SIGNAL, SIGNAL } );
+ * }
+ *
+ * // From 0..127
+ * public void inlet(float val) {
+ * if ( val > 64 ) {
+ * right = 1;
+ * left = ((127-val) / 64);
+ * } else {
+ * left = 1;
+ * right = val / 64;
+ * }
+ * }
+ *
+ * public Method dsp(MSPSignal[] ins, MSPSignal[] outs) {
+ * return getPerformMethod("perform");
+ * }
+ *
+ * public void perform(MSPSignal[] ins, MSPSignal[] outs) {
+ * for (int i=0;i&lt;ins[0].n;i++) {
+ * outs[0].vec[i] = ins[0].vec[i] * left;
+ * outs[1].vec[i] = ins[0].vec[i] * right;
+ * }
+ * }
+ * }
+ * </p></blockquote></pre>
+ */
+public abstract class MSPObject extends MaxObject {
+ /**
+ * Use this value to indentify a signal intlet/outlet to
+ * the method declareInlet/declareOutlet.
+ */
+ public final static int SIGNAL = 32;
+
+ /**
+ * Initialize the dsp state. From the numbers of input/output this
+ * method must return a performing method.
+ * @param ins input signals
+ * @param outs output signals
+ * @return the method to execute at each dsp cycle
+ */
+ public abstract Method dsp(MSPSignal[] ins, MSPSignal[] outs);
+
+ /**
+ * Quicky method to be used with dsp(MSPSignal[], MSPSignal[]). It
+ * will return the method name <code>methodName</code> with signature
+ * (MSPSignal[], MSPSignal[]) in the current class.
+ * @param methodName the name of the method in the current class
+ * @return the method reflection
+ */
+ protected Method getPerformMethod(String methodName) {
+ try {
+ Method m = getClass().getDeclaredMethod(methodName, new Class[] {
+ MSP_SIGNAL_ARRAY_CLZ, MSP_SIGNAL_ARRAY_CLZ });
+ return m;
+ } catch ( NoSuchMethodException e ) {
+ error("pdj~: method: " + methodName + " not found in class in:" + getClass().toString());
+ }
+ return null;
+ }
+
+ /**
+ * Force to copy of each MSPBuffer when performer is called. Right now,
+ * on pdj the MSPBuffer is always copied.
+ * @param copyBuffer true if you need to copyBuffer
+ */
+ protected void setNoInPlace(boolean copyBuffer) {
+ //this.copyBuffer = copyBuffer;
+ }
+
+ /**
+ * This method is called when the dsp is start/stop.
+ * <b>NOT USED ON PD.</b>
+ * @param dspRunning true if the dsp is running
+ */
+ protected void dspstate(boolean dspRunning) {
+ }
+
+
+ /**
+ * Declare the inlets used by this object. Use MSPObject.SIGNAL
+ * to define a signal inlet. <b>Note:</b> any signal inlet won't
+ * be able to process any atom messages.
+ */
+ protected void declareInlets(int[] types) {
+ int i, inlets = 0;
+ for (i=0;i<types.length;i++) {
+ if ( types[i] == SIGNAL )
+ inlets++;
+ }
+ _used_inputs = new MSPSignal[inlets];
+ for(i=0;i<_used_inputs.length;i++) {
+ _used_inputs[i] = new MSPSignal();
+ }
+ super.declareInlets(types);
+ }
+
+ /**
+ * Declare the outlets used by this object. Use MSPObject.SIGNAL
+ * to define a signal outlet.
+ */
+ protected void declareOutlets(int[] types) {
+ int i, outlets = 0;
+ for (i=0;i<types.length;i++) {
+ if ( types[i] == SIGNAL )
+ outlets++;
+ }
+ _used_outputs = new MSPSignal[outlets];
+ for(i=0;i<_used_outputs.length;i++) {
+ _used_outputs[i] = new MSPSignal();
+ }
+ super.declareOutlets(types);
+ }
+
+ public static final Class MSP_SIGNAL_ARRAY_CLZ = (new MSPSignal[0]).getClass();
+
+ private MSPSignal[] _used_inputs = new MSPSignal[0];
+ private MSPSignal[] _used_outputs = new MSPSignal[0];
+
+ private Method _dspinit(float sr, int vector_size) {
+ int i;
+ for(i=0;i<_used_inputs.length;i++) {
+ _used_inputs[i].n = vector_size;
+ _used_inputs[i].vec = new float[vector_size];
+ _used_inputs[i].sr = sr;
+ }
+ for(i=0;i<_used_outputs.length;i++) {
+ _used_outputs[i].n = vector_size;
+ _used_outputs[i].vec = new float[vector_size];
+ _used_outputs[i].sr = sr;
+ }
+ dspstate(true);
+ return dsp(_used_inputs, _used_outputs);
+ }
+
+ private void _emptyPerformer(MSPSignal[] ins, MSPSignal[] outs) {
+ }
+}
+
diff --git a/src/java/com/cycling74/msp/MSPPerformable.java b/src/java/com/cycling74/msp/MSPPerformable.java
new file mode 100644
index 0000000..6f27241
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPPerformable.java
@@ -0,0 +1,17 @@
+package com.cycling74.msp;
+
+/**
+ * This interface is used to chain Java dsp objects since they don't
+ * have to be MSPObject to process signals.
+ */
+public interface MSPPerformable {
+ /**
+ * @see MSPPerformer.dspsetup(MSPSignal in[], MSPSignal out[])
+ */
+ public void dspsetup(MSPSignal in[], MSPSignal out[]);
+
+ /**
+ * @see MSPPerformer.perform(MSPSignal in[], MSPSignal out[]);
+ */
+ public void perform(MSPSignal in[], MSPSignal out[]);
+}
diff --git a/src/java/com/cycling74/msp/MSPPerformer.java b/src/java/com/cycling74/msp/MSPPerformer.java
new file mode 100644
index 0000000..2201d82
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPPerformer.java
@@ -0,0 +1,36 @@
+package com.cycling74.msp;
+
+import java.lang.reflect.Method;
+
+/**
+ * Process signals with a single method. Like MSPObject except that
+ * the performer method will always be "perform" (and you override it).
+ */
+public abstract class MSPPerformer extends MSPObject implements MSPPerformable {
+
+ /**
+ * Called by PD with the dsp message is sended. This method will then
+ * call dspsetup and map the method "perform" has the performer method.
+ * You don't have to override this method, use dspsetup if initialization
+ * needs to be done before the signals starts processing.
+ */
+ public Method dsp(MSPSignal[] in, MSPSignal[] out) {
+ dspsetup(in, out);
+ return getPerformMethod("perform");
+ }
+
+ /**
+ * Initialize the dsp state of the MSPPerformable. Override this if
+ * you have to initalize something before the signal starts
+ * processing.
+ */
+ public void dspsetup(MSPSignal[] in, MSPSignal[] out) {
+ }
+
+ /**
+ * Process signal inlets/outlets. You must override this since it is
+ * the main processing method.
+ */
+ public abstract void perform(MSPSignal[] in, MSPSignal[] out);
+
+}
diff --git a/src/java/com/cycling74/msp/MSPSignal.java b/src/java/com/cycling74/msp/MSPSignal.java
new file mode 100644
index 0000000..0fb9635
--- /dev/null
+++ b/src/java/com/cycling74/msp/MSPSignal.java
@@ -0,0 +1,70 @@
+package com.cycling74.msp;
+
+/**
+ * Signals representation; signals inlets will copy signal data to this
+ * object.
+ */
+public class MSPSignal {
+
+ /**
+ * Tells the number of time this signal is connected. This is always
+ * 1 in pure-data.
+ */
+ public short cc = 1;
+
+ /**
+ * Tells if this signal is connected. This is always true in pure-data.
+ */
+ public boolean connected = true;
+
+ /**
+ * Number of sample in vector.
+ */
+ public int n;
+
+ /**
+ * The sampling rate.
+ */
+ public double sr;
+
+ /**
+ * The current sample data.
+ */
+ public float[] vec;
+
+ MSPSignal() {
+ }
+
+ public MSPSignal(float vec[], double sr, int n, short cc) {
+ this.vec = vec;
+ this.sr = sr;
+ this.n = n;
+ this.cc = cc;
+ }
+
+ /**
+ * Returns a copy of this signal but with the same sample buffer.
+ * @return the new signal with the same buffer
+ */
+ public MSPSignal alias() {
+ return new MSPSignal(vec, sr, n, cc);
+ }
+
+ /**
+ * Returns the a duplicated signal object. Also copies the array.
+ * @return the new signal with the same value
+ */
+ public MSPSignal dup() {
+ return new MSPSignal((float [])vec.clone(), sr, n, cc);
+ }
+
+ /**
+ * Returns the a duplicated signal object, but the sample vector is
+ * re-initialized.
+ * @return the new signal with empty value (silence)
+ */
+ public MSPSignal dupclean() {
+ return new MSPSignal(new float [vec.length], sr, n, cc);
+ }
+
+}
diff --git a/src/java/com/cycling74/msp/package.html b/src/java/com/cycling74/msp/package.html
new file mode 100644
index 0000000..17f4051
--- /dev/null
+++ b/src/java/com/cycling74/msp/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<p>Package for using using array and signals objects.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/src/java/com/e1/pdj/ConsoleStream.java b/src/java/com/e1/pdj/ConsoleStream.java
new file mode 100644
index 0000000..cf396c7
--- /dev/null
+++ b/src/java/com/e1/pdj/ConsoleStream.java
@@ -0,0 +1,35 @@
+package com.e1.pdj;
+
+import java.io.*;
+
+import com.cycling74.max.MaxSystem;
+
+public class ConsoleStream extends OutputStream {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream(4096);
+
+ protected void send(String message) {
+ MaxSystem.post("pdj: " + message);
+ }
+
+ public void flush() {
+ String msg = buffer.toString();
+ if ( msg.endsWith("\n") ) {
+ msg = msg.substring(0, msg.length()-1);
+ }
+ if ( !msg.equals("") )
+ send(buffer.toString());
+ buffer.reset();
+ }
+
+ public void write(byte[] b) throws IOException {
+ buffer.write(b);
+ }
+
+ public void write(int b) throws IOException {
+ buffer.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ buffer.write(b, off, len);
+ }
+}
diff --git a/src/java/com/e1/pdj/ConsoleStreamWin32.java b/src/java/com/e1/pdj/ConsoleStreamWin32.java
new file mode 100644
index 0000000..80f767e
--- /dev/null
+++ b/src/java/com/e1/pdj/ConsoleStreamWin32.java
@@ -0,0 +1,34 @@
+package com.e1.pdj;
+
+import com.cycling74.max.MaxSystem;
+
+/**
+ * Win32 has a special stream since it can contains /r/n that will
+ * be duplicated in the console
+ */
+public class ConsoleStreamWin32 extends ConsoleStream {
+ protected void send(String message) {
+ StringBuffer ret = new StringBuffer();
+
+ for (int i=0;i<message.length();i++) {
+ char c = message.charAt(i);
+
+ if ( c != '\r' )
+ ret.append(c);
+ }
+
+ message = ret.toString();
+ if ( !message.equals("\n") )
+ MaxSystem.post("pdj: " + ret.toString());
+ }
+
+ public void flush() {
+ String msg = buffer.toString();
+ if ( msg.endsWith("\n") ) {
+ msg = msg.substring(0, msg.length()-1);
+ }
+ if ( !msg.equals("") )
+ send(buffer.toString());
+ buffer.reset();
+ }
+}
diff --git a/src/java/com/e1/pdj/GenericCompiler.java b/src/java/com/e1/pdj/GenericCompiler.java
new file mode 100644
index 0000000..31278b7
--- /dev/null
+++ b/src/java/com/e1/pdj/GenericCompiler.java
@@ -0,0 +1,71 @@
+package com.e1.pdj;
+
+import java.io.*;
+
+import com.cycling74.max.MaxSystem;
+
+abstract class GenericCompiler {
+ File javaFile;
+ String cp[];
+ String compilerName;
+
+ static String rtJar;
+
+ GenericCompiler(String name) {
+ compilerName = name + ": ";
+ }
+
+ String getConfigurationClassPath() {
+ return PDJClassLoader.getConfigurationClassPath();
+ }
+
+ File resolvJavaFile() {
+ int base = PDJClassLoader.fclasses.toString().length() + 1;
+ String className = javaFile.toString().substring(base);
+ return new File(className);
+ }
+
+ private int doexec(String arg) throws Exception {
+ Process p = Runtime.getRuntime().exec(arg, null, PDJClassLoader.fclasses);
+
+ BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ String buff;
+ boolean cont = true;
+ while(cont) {
+ cont = false;
+
+ buff = stdout.readLine();
+ if ( buff != null ) {
+ MaxSystem.post(compilerName + buff);
+ cont = true;
+ } else {
+ cont = false;
+ }
+
+ buff = stderr.readLine();
+ if ( buff != null ) {
+ MaxSystem.post(compilerName + buff);
+ cont = true;
+ } else {
+ cont = cont & false;
+ }
+ }
+
+ p.waitFor();
+
+ return p.exitValue();
+ }
+
+ int exec(String arg) throws PDJClassLoaderException {
+ MaxSystem.post("pdj: trying to compile class: " + resolvJavaFile().toString());
+
+ try {
+ return doexec(arg);
+ } catch (Exception e) {
+ throw new PDJClassLoaderException(e);
+ }
+ }
+
+ abstract void compileClass() throws PDJClassLoaderException;
+}
diff --git a/src/java/com/e1/pdj/JavacCompiler.java b/src/java/com/e1/pdj/JavacCompiler.java
new file mode 100644
index 0000000..7f2debe
--- /dev/null
+++ b/src/java/com/e1/pdj/JavacCompiler.java
@@ -0,0 +1,34 @@
+package com.e1.pdj;
+
+import com.cycling74.max.MaxSystem;
+import java.io.File;
+
+class JavacCompiler extends GenericCompiler {
+
+ public JavacCompiler() {
+ super("javac");
+ }
+
+ String javacPath() {
+ String fullPath = System.getProperty("pdj.JAVA_HOME");
+ File test = new File(new File(fullPath, "bin"), "javac");
+ if ( test.exists() ) {
+ return test.getAbsolutePath();
+ }
+ MaxSystem.post("unable to find 'bin/javac' from the JAVA_HOME, using PATH");
+ return "javac";
+ }
+
+ void compileClass() throws PDJClassLoaderException {
+ String args = javacPath() + " " + resolvJavaFile() +
+ " -classpath " + getConfigurationClassPath() +
+ " -sourcepath " + PDJClassLoader.fclasses.toString();
+
+ int rc = exec(args);
+
+ if ( rc != 0 ) {
+ throw new PDJClassLoaderException("pdj: compiler returned: "+ rc + ",args: " +args);
+ }
+ MaxSystem.post("pdj: compile successful");
+ }
+}
diff --git a/src/java/com/e1/pdj/JikesCompiler.java b/src/java/com/e1/pdj/JikesCompiler.java
new file mode 100644
index 0000000..f9337b2
--- /dev/null
+++ b/src/java/com/e1/pdj/JikesCompiler.java
@@ -0,0 +1,27 @@
+package com.e1.pdj;
+
+import java.io.*;
+import com.cycling74.max.MaxSystem;
+
+public class JikesCompiler extends GenericCompiler {
+
+ JikesCompiler() {
+ super("jikes");
+ }
+
+ void compileClass() throws PDJClassLoaderException {
+ if ( GenericCompiler.rtJar == null )
+ throw new PDJClassLoaderException("pdj: JAVA_HOME not found");
+
+ String args = "jikes " + resolvJavaFile() +
+ " -classpath " + GenericCompiler.rtJar + getConfigurationClassPath() +
+ " -sourcepath " + PDJClassLoader.fclasses.toString();
+
+ int rc = exec(args);
+
+ if ( rc != 0 ) {
+ throw new PDJClassLoaderException("pdj: compiler returned: "+ rc + ",args: " +args);
+ }
+ MaxSystem.post("pdj: compile successful");
+ }
+}
diff --git a/src/java/com/e1/pdj/PDJClassLoader.java b/src/java/com/e1/pdj/PDJClassLoader.java
new file mode 100644
index 0000000..ee54e71
--- /dev/null
+++ b/src/java/com/e1/pdj/PDJClassLoader.java
@@ -0,0 +1,206 @@
+package com.e1.pdj;
+
+import java.net.URLClassLoader;
+import java.net.*;
+import java.util.*;
+import java.io.*;
+
+import com.cycling74.max.MaxSystem;
+
+public class PDJClassLoader extends URLClassLoader {
+ /** The folder where the class (and java files) are */
+ static File fclasses;
+
+ /** if this is set to true, all classloader operation will be logged */
+ static boolean verboseCL = false;
+
+ static private PDJClassLoader instance;
+
+ static {
+ String prop = System.getProperty("pdj.classes-dir");
+
+ if ( prop == null ) {
+ fclasses = new File(System.getProperty("pdj.home") + "/classes");
+ } else {
+ fclasses = new File(prop);
+ }
+ instance = new PDJClassLoader();
+
+ verboseCL = PDJSystem.isSystemPropertyTrue("pdj.verbose-classloader");
+ }
+
+ static public PDJClassLoader getInstance() {
+ return instance;
+ }
+
+ public PDJClassLoader() {
+ super(resolvClasspath());
+ }
+
+ private static void findJars(File f, Collection v) throws MalformedURLException {
+ File files[] = f.listFiles();
+
+ for(int i = 0;i<files.length;i++) {
+ String filename = files[i].toString();
+ if ( filename.endsWith(".jar") || filename.endsWith(".zip") ) {
+ v.add(new URL("jar:" + files[i].toURL().toString() + "!/"));
+ }
+ }
+ }
+
+ private static URL[] resolvClasspath() {
+ String cp = System.getProperty("pdj.classpath");
+ ArrayList list = new ArrayList();
+ String pathSeparator = "" + File.pathSeparator;
+
+ try {
+ if ( fclasses.isDirectory() )
+ list.add(fclasses.toURL());
+
+ File lib = new File(System.getProperty("pdj.home") + "/lib");
+ if ( lib.isDirectory() )
+ findJars(lib, list);
+
+ StringTokenizer st = new StringTokenizer(cp, pathSeparator);
+ while( st.hasMoreTokens() ) {
+ String token = st.nextToken(pathSeparator);
+ File f = new File(token);
+ if ( f.isFile() && (f.toString().endsWith(".jar") || f.toString().endsWith(".zip")) )
+ list.add(new URL("jar:" + f.toURL().toString() + "!/"));
+ if ( f.isDirectory() ) {
+ findJars(f, list);
+ list.add(f.toURL());
+ }
+ }
+ } catch ( MalformedURLException mue ) {
+ throw new Error("pdj: unable to add to classpath: " + mue);
+ }
+
+ URL ret[] = (URL[]) list.toArray(new URL[list.size()]);
+
+ if ( verboseCL ) {
+ MaxSystem.post("pdj: verbose classloader: system classpath: " + System.getProperty("java.class.path"));
+ MaxSystem.post("pdj: verbose classloader: dynamic classpath:");
+ for(int i=0;i<ret.length;i++) {
+ MaxSystem.post("\t" + ret[i].toString());
+ }
+ }
+
+ return ret;
+ }
+
+ public static String[] getCurrentClassPath() {
+ URL url[] = instance.getURLs();
+ String ret[] = new String[url.length];
+
+ for(int i=0;i<url.length;i++) {
+ ret[i] = url[i].toString();
+ }
+ return ret;
+ }
+
+ public static String getReadableClassPath() {
+ PDJClassLoader cloader = getInstance();
+ StringBuffer sb = new StringBuffer();
+
+ URL url[] = cloader.getURLs();
+ for(int i=0;i<url.length;i++) {
+ sb.append("\t" + url[i].toString() + "\n");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns a readable classpath for javac
+ * @return the classpath for javac / jikes
+ */
+ public static String getConfigurationClassPath() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(System.getProperty("java.class.path") + File.pathSeparatorChar);
+
+ URL url[] = getInstance().getURLs();
+ for(int i=0;i<url.length;i++) {
+ String path = url[i].toString();
+
+ if ( path.startsWith("file:") ) {
+ sb.append(path.substring(6) + File.pathSeparatorChar);
+ continue;
+ }
+
+ if ( path.startsWith("jar:") ) {
+ sb.append(path.substring(10, path.lastIndexOf("!")) + File.pathSeparatorChar);
+ continue;
+ }
+
+ sb.append(path + File.pathSeparatorChar);
+ }
+
+ return sb.toString();
+ }
+
+ public static void resetClassloader() {
+ instance = new PDJClassLoader();
+ System.gc();
+ }
+
+ /**
+ * Dynamic resolv will try to load .java file from date with .class
+ * @param name classname
+ * @return class definition
+ * @throws ClassNotFoundException
+ */
+ public static Class dynamicResolv(String name) throws PDJClassLoaderException {
+ if ( System.getProperty("pdj.compiler").equals("null") ) {
+ resetClassloader();
+ try {
+ return instance.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ throw new PDJClassLoaderException(e);
+ }
+ }
+
+ String pathName = name.replace('.', File.pathSeparatorChar);
+
+ File classFile = new File(fclasses, pathName + ".class");
+ File javaFile = new File(fclasses, pathName + ".java");
+
+ if ( javaFile.exists() ) {
+ if ( classFile.exists() ) {
+ if ( javaFile.lastModified() > classFile.lastModified() ) {
+ compileClass(javaFile);
+ } else {
+ if ( verboseCL )
+ MaxSystem.post("class: " + name + " is already compiled and younger than the source .java file");
+ }
+ } else {
+ compileClass(javaFile);
+ }
+ }
+
+ resetClassloader();
+
+ try {
+ return instance.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ throw new PDJClassLoaderException(e);
+ }
+ }
+
+ protected static void compileClass(File javaFile) throws PDJClassLoaderException {
+ String str_compiler = System.getProperty("pdj.compiler");
+ GenericCompiler compiler = null;
+
+ if ( str_compiler.equals("jikes") ) {
+ compiler = new JikesCompiler();
+ } else {
+ if ( !str_compiler.equals("javac") ) {
+ System.err.println("pdj: unknown compiler:" + str_compiler + ", using javac.");
+ }
+ compiler = new JavacCompiler();
+ }
+ compiler.javaFile = javaFile;
+ compiler.compileClass();
+ }
+}
diff --git a/src/java/com/e1/pdj/PDJClassLoaderException.java b/src/java/com/e1/pdj/PDJClassLoaderException.java
new file mode 100644
index 0000000..547853c
--- /dev/null
+++ b/src/java/com/e1/pdj/PDJClassLoaderException.java
@@ -0,0 +1,17 @@
+package com.e1.pdj;
+
+public class PDJClassLoaderException extends Exception {
+ private static final long serialVersionUID = 8714491904913363787L;
+
+ public PDJClassLoaderException(String msg) {
+ super(msg);
+ }
+
+ public PDJClassLoaderException(String msg, Exception root) {
+ super(msg, root);
+ }
+
+ public PDJClassLoaderException(Exception root) {
+ super(root);
+ }
+}
diff --git a/src/java/com/e1/pdj/PDJError.java b/src/java/com/e1/pdj/PDJError.java
new file mode 100644
index 0000000..7d89f1a
--- /dev/null
+++ b/src/java/com/e1/pdj/PDJError.java
@@ -0,0 +1,15 @@
+package com.e1.pdj;
+
+
+public class PDJError extends Error {
+ private static final long serialVersionUID = 1264000707984047887L;
+
+ public PDJError(String msg) {
+ super(msg);
+ }
+ public PDJError(Throwable t) {
+ initCause(t);
+ }
+ public PDJError() {
+ }
+}
diff --git a/src/java/com/e1/pdj/PDJSystem.java b/src/java/com/e1/pdj/PDJSystem.java
new file mode 100644
index 0000000..3d30a53
--- /dev/null
+++ b/src/java/com/e1/pdj/PDJSystem.java
@@ -0,0 +1,142 @@
+package com.e1.pdj;
+
+import com.cycling74.max.MaxSystem;
+
+import java.awt.Component;
+import java.awt.Frame;
+import java.awt.Toolkit;
+import java.io.*;
+
+/**
+ * Startup class for pdj.
+ */
+public class PDJSystem {
+ private static int loaded = 0;
+
+ public static PrintStream err;
+
+ public static PrintStream out;
+
+ /**
+ * Called by the pdj external when the JVM is initializing.
+ */
+ public static void _init_system() {
+ if ( loaded == 1 )
+ return;
+ linknative();
+ initIO();
+ }
+
+ static void resolvRtJar() {
+ char ps = File.separatorChar;
+ String systemCpJar = System.getProperty("pdj.JAVA_HOME");
+ if ( systemCpJar == null ) {
+ systemCpJar = System.getProperty("JAVA_HOME");
+ if ( systemCpJar == null ) {
+ systemCpJar = System.getenv("JAVA_HOME");
+ }
+ }
+
+ System.setProperty("pdj.JAVA_HOME", systemCpJar);
+ GenericCompiler.rtJar = systemCpJar + ps + "jre" + ps + "lib" + ps + "rt.jar" + File.pathSeparator;
+ }
+
+ /**
+ * Link the Java native classes
+ */
+ static void linknative() {
+ String pdjHome = System.getProperty("pdj.home");
+
+ // this is a hack to be sure that statics of MaxSystem are loaded
+ // before everything
+ Class cls = MaxSystem.class;
+
+ String osname = System.getProperty("os.name");
+
+ if ( osname.indexOf("Linux") != -1 ) {
+ // maps PD object as a JVM native library
+ Runtime.getRuntime().load(pdjHome + "/pdj.pd_linux");
+ loaded = 1;
+ resolvRtJar();
+ return;
+ }
+
+ if ( osname.indexOf("Windows") != -1 ) {
+ // maps PD object as a JVM native library
+ Runtime.getRuntime().load(pdjHome + "/pdj.dll");
+ loaded = 1;
+ resolvRtJar();
+ return;
+ }
+
+ if ( osname.indexOf("OS X") != -1 ) {
+ // maps PD object as a JVM native library
+ try {
+ Runtime.getRuntime().load(pdjHome + "/pdj.pd_darwin");
+ } catch (UnsatisfiedLinkError e ) {
+ Runtime.getRuntime().load(pdjHome + "/pdj.pd_imac");
+ }
+ loaded = 1;
+
+ // this will initialize the AWT component in another thread
+ new Thread(new Runnable() {
+ public void run() {
+ Class clz = Component.class;
+ }
+ }).start();
+
+ GenericCompiler.rtJar = "/System/Library/Frameworks/JavaVM.framework/Classes/classes.jar:";
+
+ return;
+ }
+
+ System.err.println("pdj: operating system type not found, the native link has not been made");
+ }
+
+ static boolean redirectIO() {
+ String prop = System.getProperty("pdj.redirect-pdio");
+
+ if ( prop == null )
+ return true;
+
+ if ( prop.charAt(0) == '0' )
+ return false;
+
+ if ( prop.equals("false") )
+ return false;
+
+ return true;
+ }
+
+ static void initIO() {
+ if ( redirectIO() ) {
+ if ( System.getProperty("os.name").indexOf("Windows") == -1 ) {
+ out = new PrintStream(new ConsoleStream(), true);
+ err = new PrintStream(new ConsoleStream(), true);
+ } else {
+ out = new PrintStream(new ConsoleStreamWin32(), true);
+ err = new PrintStream(new ConsoleStreamWin32(), true);
+ }
+ System.setOut(out);
+ System.setErr(err);
+ } else {
+ out = System.out;
+ err = System.err;
+ }
+ }
+
+ public static boolean isSystemPropertyTrue(String name) {
+ String value = System.getProperty(name);
+
+ if ( value == null )
+ return false;
+
+ if ( value.toLowerCase().equals("true") )
+ return true;
+
+ if ( value.equals("1") )
+ return true;
+
+ return false;
+ }
+}
diff --git a/src/java/com/e1/pdj/PriorityQueue.java b/src/java/com/e1/pdj/PriorityQueue.java
new file mode 100644
index 0000000..59a69a4
--- /dev/null
+++ b/src/java/com/e1/pdj/PriorityQueue.java
@@ -0,0 +1,68 @@
+package com.e1.pdj;
+
+import java.util.*;
+import com.cycling74.max.Executable;
+
+public class PriorityQueue implements Runnable {
+ List list = new ArrayList();
+ private Thread thread;
+ boolean tostop = false;
+
+ public PriorityQueue(int priority) {
+ thread = new Thread(this);
+
+ switch( priority ) {
+ case Thread.MIN_PRIORITY :
+ thread.setName("PriorityQueue:low");
+ break;
+ case Thread.NORM_PRIORITY :
+ thread.setName("PriorityQueue:norm");
+ break;
+ case Thread.MAX_PRIORITY :
+ thread.setName("PriorityQueue:max");
+ break;
+ }
+ thread.setPriority(priority);
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ public void shutdown() {
+ synchronized(this) {
+ tostop = true;
+ notify();
+ }
+ }
+
+ public void run() {
+ Executable exec;
+
+ while(true) {
+ try {
+ synchronized(this) {
+ if ( list.size() == 0 )
+ wait();
+
+ if ( tostop )
+ break;
+ exec = (Executable) list.remove(0);
+ }
+ try {
+ exec.execute();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ public void defer(Executable e) {
+ synchronized(this) {
+ list.add(e);
+ notify();
+ }
+ }
+
+}
diff --git a/src/java/com/e1/pdj/test/AtomTest.java b/src/java/com/e1/pdj/test/AtomTest.java
new file mode 100644
index 0000000..c900ed8
--- /dev/null
+++ b/src/java/com/e1/pdj/test/AtomTest.java
@@ -0,0 +1,92 @@
+package com.e1.pdj.test;
+
+import junit.framework.TestCase;
+import com.cycling74.max.Atom;
+
+public class AtomTest extends TestCase {
+
+ public void testIsIn() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ assertEquals(Atom.isIn(Atom.newAtom("ok"), list), 1);
+ assertEquals(Atom.isIn(Atom.newAtom(5), list), 2);
+ assertEquals(Atom.isIn(Atom.newAtom(0), list), -1);
+ assertEquals(Atom.isIn(Atom.newAtom(1), list, 1 ,2), -1);
+ assertEquals(Atom.isIn(Atom.newAtom("ok"), list, 1 ,2), 1);
+ }
+
+ public void testRemoveSome() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ Atom[] test = Atom.removeSome(list, 1, 2);
+ assertEquals(1, test.length);
+ assertEquals(Atom.newAtom(1), test[0]);
+ }
+
+ public void testReverse() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ Atom[] test = Atom.reverse(list);
+ assertEquals(Atom.newAtom(5), test[0]);
+ assertEquals(Atom.newAtom("ok"), test[1]);
+ assertEquals(Atom.newAtom(1),test[2]);
+ }
+
+ public void testRotate() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ Atom[] test = Atom.rotate(list, 2);
+ assertEquals(Atom.newAtom("ok"), test[0]);
+ assertEquals(Atom.newAtom(5), test[1]);
+ assertEquals(Atom.newAtom(1), test[2]);
+
+ test = Atom.rotate(list, 5);
+ assertEquals(Atom.newAtom("ok"), test[0]);
+ assertEquals(Atom.newAtom(5), test[1]);
+ assertEquals(Atom.newAtom(1), test[2]);
+
+ test = Atom.rotate(list, 1);
+ assertEquals(Atom.newAtom(5), test[0]);
+ assertEquals(Atom.newAtom(1), test[1]);
+ assertEquals(Atom.newAtom("ok"), test[2]);
+ }
+
+ public void testRemoveFirst() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ Atom[] test = Atom.removeFirst(list, 2);
+ assertEquals(1, test.length);
+ assertEquals(Atom.newAtom(5), test[0]);
+
+ test = Atom.removeFirst(list);
+ assertEquals(2, test.length);
+ assertEquals(Atom.newAtom("ok"), test[0]);
+ assertEquals(Atom.newAtom(5), test[1]);
+ }
+
+ public void testRemoveLast() {
+ Atom[] list = new Atom[] { Atom.newAtom(1), Atom.newAtom("ok"), Atom.newAtom(5) };
+
+ Atom[] test = Atom.removeLast(list, 2);
+ assertEquals(1, test.length);
+ assertEquals(Atom.newAtom(1), test[0]);
+
+ test = Atom.removeLast(list);
+ assertEquals(2, test.length);
+ assertEquals(Atom.newAtom(1), test[0]);
+ assertEquals(Atom.newAtom("ok"), test[1]);
+ }
+
+ public void testUnion() {
+ assertTrue("union not implemented", false);
+ }
+
+ public void testIntersection() {
+ assertTrue("intersection not implementated", false);
+ }
+
+ public void testInt() {
+ Atom test = Atom.newAtom(0x90);
+ assertEquals(0x90, test.getInt());
+ }
+}
diff --git a/src/java/com/e1/pdj/test/CallbackTest.java b/src/java/com/e1/pdj/test/CallbackTest.java
new file mode 100644
index 0000000..4105479
--- /dev/null
+++ b/src/java/com/e1/pdj/test/CallbackTest.java
@@ -0,0 +1,10 @@
+package com.e1.pdj.test;
+
+import junit.framework.TestCase;
+
+public class CallbackTest extends TestCase {
+
+ public void testSomething() {
+
+ }
+}
diff --git a/src/java/com/e1/pdj/test/MaxQelemTest.java b/src/java/com/e1/pdj/test/MaxQelemTest.java
new file mode 100644
index 0000000..a55fef9
--- /dev/null
+++ b/src/java/com/e1/pdj/test/MaxQelemTest.java
@@ -0,0 +1,7 @@
+package com.e1.pdj.test;
+
+import junit.framework.TestCase;
+
+public class MaxQelemTest extends TestCase {
+
+}
diff --git a/src/java/help_class.java b/src/java/help_class.java
new file mode 100644
index 0000000..3410c70
--- /dev/null
+++ b/src/java/help_class.java
@@ -0,0 +1,26 @@
+import com.cycling74.max.*;
+
+public class help_class extends MaxObject {
+ int attr1;
+
+ public help_class() {
+ declareOutlets(new int[] { DataTypes.FLOAT, DataTypes.ALL });
+ }
+
+ public void inlet(float x) {
+ outlet(0, x);
+ }
+
+ public void bang() {
+ outlet(1, "BANG! received");
+ }
+
+ public void callme(Atom args[]) {
+ outlet(1, "callme has called with arg1:" + args[0].toString());
+ }
+
+ public void dynamic_method() {
+ outlet(1, "dynamic_method has been called");
+ }
+
+}
diff --git a/src/java/panner.java b/src/java/panner.java
new file mode 100644
index 0000000..34fa77b
--- /dev/null
+++ b/src/java/panner.java
@@ -0,0 +1,37 @@
+import java.lang.reflect.Method;
+
+import com.cycling74.max.*;
+import com.cycling74.msp.*;
+
+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;
+ }
+ }
+}
diff --git a/src/java/pdj_test_class.java b/src/java/pdj_test_class.java
new file mode 100644
index 0000000..ff6f756
--- /dev/null
+++ b/src/java/pdj_test_class.java
@@ -0,0 +1,72 @@
+import com.cycling74.max.*;
+import com.cycling74.msp.MSPBuffer;
+
+public class pdj_test_class extends MaxObject implements Executable {
+ MaxClock clock;
+ float patate;
+
+ public pdj_test_class() {
+ clock = new MaxClock(this);
+
+ declareAttribute("patate");
+ }
+
+ public pdj_test_class(Atom args[]) {
+ for (int i=0;i<args.length;i++) {
+ post("arg[" + i +"]:" + args[i].toString());
+ }
+ clock = new MaxClock(this);
+ }
+
+ protected void bang() {
+ Atom[] atom = new Atom[1];
+
+ atom[0] = Atom.newAtom(10);
+ MaxSystem.sendMessageToBoundObject("allo", "float", atom);
+ outlet(0, 20);
+ outlet(0, new float[] { 0.5f, 0.1f, 1, 200 });
+ clock.delay(600);
+
+ }
+
+ void testle() {
+ post("array size: " + MSPBuffer.getSize("array_tester"));
+ float f[] = MSPBuffer.peek("array_tester");
+
+ for(int i=0;i<f.length;i++) {
+ f[i] = patate;
+ }
+ MSPBuffer.poke("array_tester", f);
+
+ MSPBuffer.poke("array_tester", 1, 11, 1f);
+ MSPBuffer.poke("array_tester", 1, 2, new float[] { 0.2f, -0.2f} );
+ f = MSPBuffer.peek("array_tester", 1, 2, 2);
+ post("array_tester[2:3]=" + f[0] + "," + f[1]);
+ MSPBuffer.poke("array_tester", 1, 9, -1f);
+ post("array_tester[9]=" + MSPBuffer.peek("array_tester", 1, 9));
+ post("path of this patch:" + MaxSystem.locateFile("pdj-test.pd"));
+ post("path of this patch(from patcher):" + getParentPatcher().getPath());
+ }
+
+ protected void inlet(float f) {
+ post("le float " + f);
+ }
+
+ void wer(Atom[] atom) {
+ post("atom len "+ atom.length);
+ post("calle " + atom[0].getString());
+ post("calle 2 " + atom[1].toString());
+ }
+
+ void sizeArray(Atom[] atom) {
+ MSPBuffer.setSize("array_tester", 0, atom[0].toInt());
+ }
+
+ protected void loadbang() {
+ post("hey!!!! this is loadbang");
+ }
+
+ public void execute() {
+ System.out.println("allo");
+ }
+}
diff --git a/src/java/pdj_test_tilde.java b/src/java/pdj_test_tilde.java
new file mode 100644
index 0000000..ba8de69
--- /dev/null
+++ b/src/java/pdj_test_tilde.java
@@ -0,0 +1,45 @@
+import java.lang.reflect.Method;
+
+import com.cycling74.max.*;
+import com.cycling74.msp.*;
+
+
+public class pdj_test_tilde extends MSPObject {
+ float f = 1;
+ boolean debug = false;
+
+ public pdj_test_tilde() {
+ int[] inlets = new int[] { MSPObject.SIGNAL, MSPObject.SIGNAL, MSPObject.SIGNAL, DataTypes.FLOAT };
+ int[] outlets = new int[] { MSPObject.SIGNAL , MSPObject.SIGNAL, MSPObject.SIGNAL };
+
+ declareInlets(inlets);
+ declareOutlets(outlets);
+ }
+
+ public void inlet(float f) {
+ this.f = f;
+ System.out.println(""+f);
+ }
+
+ public void bang() {
+ if ( debug == false )
+ debug = true;
+ else
+ debug = false;
+ }
+
+ public Method dsp(MSPSignal[] ins, MSPSignal[] outs) {
+ return getPerformMethod("doit");
+ }
+
+ public void doit(MSPSignal[] ins, MSPSignal[] outs) {
+ int i;
+ if ( debug )
+ System.out.println("a:" + ins[0].vec[0] + " b:" + ins[1].vec[0] + " c:" + ins[2].vec[0]);
+ for(i=0; i<ins[0].n;i++) {
+ outs[0].vec[i] = ins[0].vec[i] * f;
+ outs[1].vec[i] = ins[1].vec[i] * f;
+ outs[2].vec[i] = ins[2].vec[i] * f;
+ }
+ }
+}
diff --git a/src/java/pdj_tilde.java b/src/java/pdj_tilde.java
new file mode 100644
index 0000000..7b9703b
--- /dev/null
+++ b/src/java/pdj_tilde.java
@@ -0,0 +1,45 @@
+import java.lang.reflect.Method;
+
+import com.cycling74.max.*;
+import com.cycling74.msp.*;
+
+
+public class pdj_tilde extends MSPObject {
+ float f = 1;
+ boolean debug = false;
+
+ public pdj_tilde() {
+ int[] inlets = new int[] { MSPObject.SIGNAL, MSPObject.SIGNAL, MSPObject.SIGNAL, DataTypes.FLOAT };
+ int[] outlets = new int[] { MSPObject.SIGNAL , MSPObject.SIGNAL, MSPObject.SIGNAL };
+
+ declareInlets(inlets);
+ declareOutlets(outlets);
+ }
+
+ public void inlet(float f) {
+ this.f = f;
+ System.out.println(""+f);
+ }
+
+ public void bang() {
+ if ( debug == false )
+ debug = true;
+ else
+ debug = false;
+ }
+
+ public Method dsp(MSPSignal[] ins, MSPSignal[] outs) {
+ return getPerformMethod("doit");
+ }
+
+ public void doit(MSPSignal[] ins, MSPSignal[] outs) {
+ int i;
+ if ( debug )
+ System.out.println("a:" + ins[0].vec[0] + " b:" + ins[1].vec[0] + " c:" + ins[2].vec[0]);
+ for(i=0; i<ins[0].n;i++) {
+ outs[0].vec[i] = ins[0].vec[i] * f;
+ outs[1].vec[i] = ins[1].vec[i] * f;
+ outs[2].vec[i] = ins[2].vec[i] * f;
+ }
+ }
+}
diff --git a/src/pdj-linux.c b/src/pdj-linux.c
new file mode 100644
index 0000000..63bb015
--- /dev/null
+++ b/src/pdj-linux.c
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include "pdj.h"
+
+int getuglylibpath(char *path) {
+ char buffer[BUFFER_SIZE];
+ FILE *f;
+
+ sprintf(buffer, "/proc/%d/maps", getpid());
+ f = fopen(buffer, "r");
+ if ( f == NULL ) {
+ perror("pdj: unable to map :");
+ strcpy(path, ".");
+ return 1;
+ }
+
+ while(!feof(f)) {
+ fgets(buffer, BUFFER_SIZE-1, f);
+ if ( strstr(buffer, "pdj.pd_linux") != NULL ) {
+ buffer[strlen(buffer) - 14] = 0;
+ strcpy(path, buffer+49);
+ fclose(f);
+ return 0;
+ }
+ }
+
+ /* not found, check in the current dir :( */
+ post("pdj: humm... pdj path library not found, setting current path");
+ strcpy(path, ".");
+ fclose(f);
+ return 1;
+}
+
+
+JNI_CreateJavaVM_func *linkjvm(char *vm_type) {
+ JNI_CreateJavaVM_func *func;
+ char work[BUFFER_SIZE];
+ char *javahome = pdj_getProperty("pdj.JAVA_HOME");
+ void *libVM;
+
+ if ( javahome == NULL ) {
+ javahome = getenv("JAVA_HOME");
+ } else {
+ sprintf(work, "%s/jre/lib/i386/%s/libjvm.so", javahome, vm_type);
+ libVM = dlopen(work, RTLD_LAZY);
+
+ if ( libVM == NULL ) {
+ post("pdj: unable to use the JVM specified at pdj.JAVA_HOME");
+ javahome = getenv("JAVA_HOME");
+ }
+ }
+
+ if ( javahome == NULL ) {
+ post("pdj: using JVM from the LD_LIBRARY_PATH");
+ libVM = dlopen("libjava.so", RTLD_LAZY);
+ } else {
+ post("pdj: using JVM %s", javahome);
+ /* using LD_LIBRARY_PATH + putenv doesn't work, load std jvm libs
+ * with absolute path. order is important.
+ */
+ sprintf(work, "%s/jre/lib/i386/%s/libjvm.so", javahome, vm_type);
+ dlopen(work, RTLD_LAZY);
+
+ sprintf(work, "%s/jre/lib/i386/libverify.so", javahome);
+ dlopen(work, RTLD_LAZY);
+
+ sprintf(work, "%s/jre/lib/i386/libjava.so", javahome);
+ dlopen(work, RTLD_LAZY);
+
+ sprintf(work, "%s/jre/lib/i386/libmlib_image.so", javahome);
+ dlopen(work, RTLD_LAZY);
+
+ /* ELF should support dynamic LD_LIBRARY_PATH :( :( :( */
+ sprintf(work, "%s/jre/lib/i386/libjava.so", javahome);
+ libVM = dlopen(work, RTLD_LAZY);
+ }
+
+ if ( libVM == 0 ) {
+ error("pdj: %s", dlerror());
+ return NULL;
+ }
+
+ func = dlsym(libVM, "JNI_CreateJavaVM");
+ if ( func == 0 ) {
+ error("pdj: %s", dlerror());
+ return NULL;
+ }
+ return func;
+}
diff --git a/src/pdj-osx.c b/src/pdj-osx.c
new file mode 100644
index 0000000..bd2be18
--- /dev/null
+++ b/src/pdj-osx.c
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "pdj.h"
+
+int getuglylibpath(char *path) {
+ char fullpath[MAXPDSTRING], *pfullpath;
+ FILE *fd;
+
+ fd = (FILE *) open_via_path("", "pdj.properties", "", fullpath, &pfullpath, MAXPDSTRING, 0);
+ if ( fd != NULL ) {
+ close(fd);
+ if ( fullpath[0] != 0 ) {
+ if ( pfullpath == ((char *) &fullpath) ) {
+ getcwd(path, MAXPDSTRING);
+ } else {
+ strcpy(path, fullpath);
+ }
+ return 0;
+ }
+ }
+
+ error("unable to find pdj directory, please add it in your pure-data path settings");
+ return 1;
+}
+
+JNI_CreateJavaVM_func *linkjvm(char *vmtype) {
+ char *jvmVersion = pdj_getProperty("pdj.osx.JAVA_JVM_VERSION");
+
+ if ( jvmVersion != NULL ) {
+ setenv("JAVA_JVM_VERSION", jvmVersion, 1);
+ }
+
+ return (JNI_CreateJavaVM_func *) &JNI_CreateJavaVM;
+} \ No newline at end of file
diff --git a/src/pdj-win32.c b/src/pdj-win32.c
new file mode 100644
index 0000000..c93e53a
--- /dev/null
+++ b/src/pdj-win32.c
@@ -0,0 +1,107 @@
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include "pdj.h"
+
+int getuglylibpath(char *path) {
+ HMODULE hmodule = GetModuleHandle("pdj.dll");
+ char dllPath[BUFFER_SIZE];
+ int rc;
+
+ if ( hmodule == NULL ) {
+ post("pdj: can't get windows dll handle");
+ strcpy(path, ".");
+ return 1;
+ }
+
+ rc = GetModuleFileName(hmodule, dllPath, 1023);
+ if ( rc == 0 || rc == 1023 ) {
+ post("pdj: can't get windows dll path");
+ strcpy(path, ".");
+ return 1;
+ }
+
+ dllPath[strlen(dllPath)-8] = 0;
+ strcpy(path, dllPath);
+ return 0;
+}
+
+
+static char *getJavaHomeFromReg(char *javaHome) {
+ char keyName[BUFFER_SIZE] = "Software\\JavaSoft\\Java Development Kit";
+ char *dest;
+ int size = BUFFER_SIZE;
+ HKEY hKey;
+ long rc;
+
+ rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &hKey);
+ if ( rc != ERROR_SUCCESS )
+ return NULL;
+
+ strcat(keyName, "\\");
+ dest = strlen(keyName) + keyName;
+ rc = RegQueryValueEx(hKey, "CurrentVersion", NULL, NULL,
+ (LPBYTE) dest, &size);
+
+ RegCloseKey(hKey);
+
+ if ( rc != ERROR_SUCCESS )
+ return NULL;
+
+ rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &hKey);
+ if ( rc != ERROR_SUCCESS )
+ return NULL;
+
+ size = BUFFER_SIZE;
+ rc = RegQueryValueEx(hKey, "JavaHome", NULL, NULL,
+ (LPBYTE) javaHome, &size);
+
+ RegCloseKey(hKey);
+
+ if ( rc != ERROR_SUCCESS )
+ return NULL;
+
+ return javaHome;
+}
+
+
+JNI_CreateJavaVM_func *linkjvm(char *vm_type) {
+ HINSTANCE jvmDll = (HINSTANCE)NULL;
+ JNI_CreateJavaVM_func *func;
+ char work[BUFFER_SIZE];
+ char *javahome = pdj_getProperty("pdj.JAVA_HOME");
+ void *libVM;
+
+ // default config or no config ? try
+ if ( javahome == NULL || javahome[0] == '/' ) {
+ javahome = NULL;
+ javahome = getenv("JAVA_HOME");
+ if ( javahome == NULL ) {
+ javahome = getJavaHomeFromReg(work);
+ if ( javahome == NULL ) {
+ post("pdj: unable to find any java VM. Please set JAVA_HOME environment variable");
+ return NULL;
+ }
+ }
+ }
+
+ strcpy(work, javahome);
+ strcat(work, "\\jre\\bin\\");
+ strcat(work, vm_type);
+ strcat(work, "\\jvm.dll");
+ jvmDll = LoadLibrary(work);
+
+ if ( jvmDll == NULL ) {
+ post("post: unable to find jvm.dll in %s", work);
+ return NULL;
+ }
+ func = GetProcAddress(jvmDll, "JNI_CreateJavaVM");
+ if ( func == NULL ) {
+ post("pdj: unable to find symbol: JNI_CreateJavaVM in jvm.dll ??");
+ return NULL;
+ }
+
+ post("pdj: using JVM %s", javahome);
+
+ return func;
+}
diff --git a/src/pdj.c b/src/pdj.c
new file mode 100644
index 0000000..d76c4f9
--- /dev/null
+++ b/src/pdj.c
@@ -0,0 +1,396 @@
+#include <stdlib.h>
+#include "pdj.h"
+#include "type_handler.h"
+
+JavaVM *jni_jvm = NULL;
+t_class *inlet_proxy;
+t_class *pdj_class;
+
+#define PDTHREAD_STACKSIZE 1000000
+static JNIEnv *pdthread_jnienv = NULL;
+static char *pdthread_stackaddr;
+
+#define PROF_MIN 999
+
+double prof_max = 0, prof_min = PROF_MIN, prof_tot = 0, prof_nb = 0, prof_tmp;
+
+JNIEnv *pdjAttachVM() {
+ const int N_REFS = 16;
+ JNIEnv *env;
+ char stack_pos; /* the position of this variable is relative to the
+ position in the stack of the main thread */
+
+ PROF(prof_tmp = sys_getrealtime(););
+
+ /* this avoids getting the JNIEnv when we are in the main thread. so
+ * if the current stack is more far than 1 meg of distance between the
+ * main thread stack when pdj was initialized, it is considered a
+ * "external thread"
+ */
+ if ( abs(pdthread_stackaddr - (&stack_pos)) > PDTHREAD_STACKSIZE ) {
+ (*jni_jvm)->AttachCurrentThread(jni_jvm, (void **)&env, NULL);
+ ASSERT(env);
+ } else {
+ env = pdthread_jnienv;
+ }
+
+ if ( (*env)->PushLocalFrame(env, N_REFS) < 0 ) {
+ SHOWEXC;
+ bug("pdj: java: out of memory!?!");
+ }
+
+ return env;
+}
+
+
+void pdjDetachVM(JNIEnv *env) {
+#ifdef DEBUG
+ if ( (*env)->ExceptionOccurred(env) ) {
+ error("pdj: unhandled exception in JNI interface:");
+ (*env)->ExceptionDescribe(env);
+ }
+#endif
+ (*env)->PopLocalFrame(env, NULL);
+
+ PROF(prof_tmp = sys_getrealtime() - prof_tmp;);
+ PROF(prof_max = prof_tmp > prof_max ? prof_tmp : prof_max;);
+ PROF(prof_min = prof_tmp < prof_min ? prof_tmp : prof_min;);
+ PROF(prof_nb++;);
+ PROF(prof_tot += prof_tmp;);
+}
+
+
+static void pdj_mapmethods(JNIEnv *env, t_pdj *pdj) {
+ jclass base = pdjCaching.cls_MaxObject;
+ jmethodID idBase, id;
+ JASSERT(base);
+
+ id = (*env)->GetMethodID(env, pdj->cls, "bang", "()V");
+ idBase = (*env)->GetMethodID(env, base, "bang", "()V");
+ (*env)->ExceptionClear(env);
+ if ( id != idBase ) {
+ pdj->MIDbang = id;
+ } else {
+ pdj->MIDbang = NULL;
+ }
+
+ id = (*env)->GetMethodID(env, pdj->cls, "inlet", "(F)V");
+ idBase = (*env)->GetMethodID(env, base, "inlet", "(F)V");
+ (*env)->ExceptionClear(env);
+ if ( id != idBase ) {
+ pdj->MIDfloat = id;
+ } else {
+ pdj->MIDfloat = NULL;
+ id = (*env)->GetMethodID(env, pdj->cls, "inlet", "(I)V");
+ idBase = (*env)->GetMethodID(env, base, "inlet", "(I)V");
+ (*env)->ExceptionClear(env);
+ if ( id != idBase ) {
+ pdj->MIDint = id;
+ } else {
+ pdj->MIDint = NULL;
+ }
+ }
+
+ id = (*env)->GetMethodID(env, pdj->cls, "list", "([com/cycling74/max/Atom;)V");
+ idBase = (*env)->GetMethodID(env, base, "list", "([com/cycling74/max/Atom;)V");
+ (*env)->ExceptionClear(env);
+ if ( id != idBase ) {
+ pdj->MIDlist = id;
+ } else {
+ pdj->MIDlist = NULL;
+ }
+
+ idBase = (*env)->GetMethodID(env, pdj->cls, "anything", "(Ljava/lang/String;[Lcom/cycling74/max/Atom;)V");
+ pdj->MIDanything = idBase;
+}
+
+
+static jobject init_pdj_class(JNIEnv *env, char *name, long cobj, int argc, t_atom *argv) {
+ char *sig = "(Ljava/lang/String;J[Lcom/cycling74/max/Atom;)Lcom/cycling74/max/MaxObject;";
+ jstring objectName;
+ jmethodID id;
+ jobject newMaxObject;
+ jobjectArray args;
+ jlong jval = cobj;
+
+ id = (*env)->GetStaticMethodID(env, pdjCaching.cls_MaxObject, "registerObject", sig);
+ JASSERT(id);
+
+ objectName = (*env)->NewStringUTF(env, name);
+ JASSERT(objectName);
+
+ args = atoms2jatoms(env, argc, argv);
+ JASSERT(args);
+
+ newMaxObject = (*env)->CallStaticObjectMethod(env, pdjCaching.cls_MaxObject, id, objectName, jval, args);
+ if ( (*env)->ExceptionOccurred(env) ) {
+ (*env)->ExceptionDescribe(env);
+ }
+ if ( newMaxObject != NULL ) {
+ jobject global = (*env)->NewGlobalRef(env, newMaxObject);
+ (*env)->DeleteLocalRef(env, newMaxObject);
+ return global;
+ }
+ return NULL;
+}
+
+
+void *pdj_new(t_symbol *s, int argc, t_atom *argv) {
+ JNIEnv *env = NULL;
+ t_pdj *x;
+
+ if ( argc < 1 ) {
+ post("pdj: no class specified");
+ return NULL;
+ }
+
+ if ( argv[0].a_type != A_SYMBOL ) {
+ post("pdj: first argument must be a class name");
+ return NULL;
+ }
+
+ if ( jni_jvm == NULL ) {
+ env = init_jvm();
+ if ( env == NULL )
+ return NULL;
+ } else {
+ (*jni_jvm)->AttachCurrentThread(jni_jvm, (void **)&env, NULL);
+ ASSERT(env);
+ }
+
+ pdthread_jnienv = env;
+
+ if ( s == gensym("pdj~") ) {
+ x = (t_pdj *)pd_new(pdj_tilde_class);
+ } else {
+ x = (t_pdj *)pd_new(pdj_class);
+ }
+ ASSERT(x);
+ x->jobject_name = argv[0].a_w.w_symbol->s_name;
+ x->nb_inlet = 0;
+ x->patch_path = canvas_getcurrentdir()->s_name;
+
+ x->cache = NULL;
+ x->obj = init_pdj_class(env, x->jobject_name, (long) x, argc, argv);
+
+ if ( x->obj != NULL ) {
+ jclass cls = (*env)->GetObjectClass(env, x->obj);
+ JASSERT(cls);
+
+ x->cls = (*env)->NewGlobalRef(env, cls);
+ pdj_mapmethods(env, x);
+ return x;
+ }
+
+ return NULL;
+}
+
+
+static void pdj_profiler(t_pdj *pdj) {
+ if ( prof_nb != 0 )
+ post("pdj-profiler: %f min %f max %f avg", prof_min, prof_max, prof_tot / prof_nb);
+ prof_min = PROF_MIN;
+ prof_max = 0;
+ prof_tot = 0;
+ prof_nb = 0;
+}
+
+
+void pdj_free(t_pdj *pdj) {
+ JNIEnv *env = pdjAttachVM();
+ jmethodID id = (*env)->GetMethodID(env, pdj->cls, "notifyDeleted", "()V");
+ JASSERT(id);
+
+ (*env)->CallVoidMethod(env, pdj->obj, id);
+ SHOWEXC;
+
+ PROF(pdj_profiler(pdj););
+
+ while (pdj->cache != NULL) {
+ t_pdjcached_sym *next = pdj->cache->next;
+ free(pdj->cache);
+ pdj->cache = next;
+ }
+
+ (*env)->DeleteGlobalRef(env, pdj->cls);
+ (*env)->DeleteGlobalRef(env, pdj->obj);
+
+ pdjDetachVM(env);
+}
+
+
+static void pdj_addcache_sym(t_pdj *pdj, t_symbol *s, jmethodID id, int arged) {
+ t_pdjcached_sym *n = malloc(sizeof(t_pdjcached_sym));
+
+ n->sym = s;
+ n->mid = id;
+ n->arged = arged;
+ n->next = pdj->cache;
+
+ pdj->cache = n;
+}
+
+
+static void pdj_process_inlet(int idx, t_pdj *pdj, t_symbol *s, int argc, t_atom atoms[]){
+ JNIEnv *env = pdjAttachVM();
+ jobjectArray args = NULL;
+ t_pdjcached_sym *cache;
+ jmethodID id;
+ jboolean rc;
+ jstring s_name;
+
+ (*env)->SetIntField(env, pdj->obj, pdjCaching.FIDMaxObject_activity_inlet, idx);
+
+ if ( s == &s_bang ) {
+ if ( pdj->MIDbang != NULL ) {
+ (*env)->CallVoidMethod(env, pdj->obj, pdj->MIDbang);
+ SHOWEXC;
+ pdjDetachVM(env);
+ return;
+ }
+ } else if ( s == &s_float ) {
+ if ( pdj->MIDfloat != NULL ) {
+ (*env)->CallVoidMethod(env, pdj->obj, pdj->MIDfloat, atom_getfloatarg(0, argc, atoms));
+ SHOWEXC;
+ pdjDetachVM(env);
+ return;
+ }
+ if ( pdj->MIDint != NULL ) {
+ (*env)->CallVoidMethod(env, pdj->obj, pdj->MIDint, atom_getintarg(0, argc, atoms));
+ SHOWEXC;
+ pdjDetachVM(env);
+ return;
+ }
+ } else if ( s == &s_list ) {
+ if ( pdj->MIDlist != NULL ) {
+ args = atoms2jatoms(env, argc, atoms);
+ JASSERT(args);
+ (*env)->CallVoidMethod(env, pdj->obj, pdj->MIDlist, args);
+ SHOWEXC;
+ pdjDetachVM(env);
+ return;
+ }
+ }
+
+ /* is the symbol in cache ? */
+ cache = pdj->cache;
+ while( cache != NULL ) {
+ if ( cache->sym == s ) {
+ if ( ! cache->arged ) {
+ (*env)->CallVoidMethod(env, pdj->obj, cache->mid, NULL);
+ } else {
+ args = atoms2jatoms(env, argc, atoms);
+ JASSERT(args);
+ (*env)->CallVoidMethod(env, pdj->obj, cache->mid, args);
+ }
+ SHOWEXC;
+ pdjDetachVM(env);
+ return;
+ }
+ cache = cache->next;
+ }
+
+ /* the last tries will require a Atom[] arguments */
+ args = atoms2jatoms(env, argc, atoms);
+ JASSERT(args);
+
+ /* try with [name](Atom []) */
+ id = (*env)->GetMethodID(env, pdj->cls, s->s_name, "([Lcom/cycling74/max/Atom;)V");
+ (*env)->ExceptionClear(env);
+ if ( id != NULL ) {
+ (*env)->CallVoidMethod(env, pdj->obj, id, args);
+ SHOWEXC;
+ pdjDetachVM(env);
+ pdj_addcache_sym(pdj, s, id, 1);
+ return;
+ }
+
+ /* try again with [name]() */
+ id = (*env)->GetMethodID(env, pdj->cls, s->s_name, "()V");
+ (*env)->ExceptionClear(env);
+ if ( id != NULL ) {
+ (*env)->CallVoidMethod(env, pdj->obj, id, NULL);
+ SHOWEXC;
+ pdjDetachVM(env);
+ pdj_addcache_sym(pdj, s, id, 0);
+ return;
+ }
+
+ s_name = (*env)->NewStringUTF(env, s->s_name);
+ JASSERT(s_name);
+
+ /* try with the setter */
+ rc = (*env)->CallBooleanMethod(env, pdj->obj, pdjCaching.MIDMaxObject_trySetter, s_name, args);
+ if ( (*env)->ExceptionCheck(env) == 1 ) {
+ /* we got an exception, the class do have a setter, but it trowed an
+ * exception: log it and don't try with anything()
+ */
+ (*env)->ExceptionDescribe(env);
+ rc = 1;
+ }
+
+ if ( rc == 0 ) {
+ /* nothing... call the anything method anything... */
+ (*env)->CallVoidMethod(env, pdj->obj, pdj->MIDanything, s_name, args);
+ SHOWEXC;
+ }
+
+ // TODO: setters should be cached too
+ pdjDetachVM(env);
+}
+
+
+static void pdj_anything(t_pdj *pdj, t_symbol *s, int argc, t_atom atoms[]){
+ pdj_process_inlet(0, pdj, s, argc, atoms);
+}
+
+
+static void inlet_proxy_anything(t_inlet_proxy *proxy, t_symbol *s, int argc, t_atom atoms[]){
+ pdj_process_inlet(proxy->idx, proxy->peer, s, argc, atoms);
+}
+
+
+static void pdj_loadbang(t_pdj *pdj) {
+ JNIEnv *env = pdjAttachVM();
+ jmethodID id = (*env)->GetMethodID(env, pdj->cls, "loadbang", "()V");
+ JASSERT(id);
+
+ (*env)->CallVoidMethod(env, pdj->obj, id, NULL);
+ SHOWEXC;
+
+ pdjDetachVM(env);
+}
+
+
+void pdj_setup(void) {
+ char stack_pos;
+
+ pdj_class = class_new(gensym("pdj"),
+ (t_newmethod)pdj_new, (t_method)pdj_free,
+ sizeof(t_pdj), CLASS_DEFAULT|CLASS_NOINLET, A_GIMME, 0);
+
+ class_addmethod(pdj_class, (t_method)pdj_loadbang, gensym("loadbang"),
+ A_CANT, A_NULL);
+ class_addanything(pdj_class, (t_method)pdj_anything);
+
+ /* pdj~ things */
+ pdj_tilde_class = class_new(gensym("pdj~"),
+ (t_newmethod)pdj_tilde_new, (t_method)pdj_tilde_free,
+ sizeof(t_pdj_tilde), CLASS_DEFAULT|CLASS_NOINLET, A_GIMME, 0);
+
+ class_addmethod(pdj_tilde_class, (t_method)pdj_loadbang, gensym("loadbang"),
+ A_CANT, A_NULL);
+ class_addanything(pdj_tilde_class, (t_method)pdj_anything);
+ class_addmethod(pdj_tilde_class, (t_method)pdj_tilde_dsp, gensym("dsp"), 0);
+ CLASS_MAINSIGNALIN(pdj_tilde_class, t_pdj_tilde, _dummy_f);
+
+ /* inlet_proxy: we create a dummy class to catch all messages from cold
+ * inlets */
+ inlet_proxy = class_new(gensym("pdj_inlet_proxy"),
+ NULL,NULL, sizeof(t_inlet_proxy),
+ CLASS_PD|CLASS_NOINLET, A_NULL);
+ class_addanything(inlet_proxy, (t_method)inlet_proxy_anything);
+
+ /* main thread stack address */
+ pdthread_stackaddr = (void *) &(stack_pos);
+}
diff --git a/src/pdj.h b/src/pdj.h
new file mode 100644
index 0000000..d318ded
--- /dev/null
+++ b/src/pdj.h
@@ -0,0 +1,140 @@
+#include <jni.h>
+#include <m_pd.h>
+
+#ifdef DEBUG
+ #define ASSERT(v) { if ( v == NULL ) {bug("ouch, assertion failed %s:%d\n", __FILE__, __LINE__);}}
+ #define JASSERT(v) { if ( v == NULL ){(*env)->ExceptionDescribe(env);bug("ouch, assertion failed %s:%d\n", __FILE__, __LINE__);}}
+ #undef DEBUG
+ #define DEBUG(X) {X};
+#else
+ #define ASSERT(v)
+ #define DEBUG(X)
+ #define JASSERT(v)
+#endif
+
+#define SHOWEXC { if ((*env)->ExceptionOccurred(env)) (*env)->ExceptionDescribe(env); }
+
+#ifdef PROFILER
+ #define PROF(v) { v }
+#else
+ #define PROF(v)
+#endif
+
+#ifdef MSW
+ #define DIR_SEP "\\"
+ #define PATH_SEP ";"
+#else
+ #define DIR_SEP "/"
+ #define PATH_SEP ":"
+#endif
+
+// the JVM takes 50M; I don't care taking 4K...
+#define BUFFER_SIZE 4096
+
+// MAXIMUM atom[x] size for type casting from jatoms to atoms
+#define MAX_ATOMS_STACK 32
+
+typedef struct PdjCaching {
+ jclass cls_Atom;
+ jclass cls_AtomString;
+ jclass cls_AtomFloat;
+ jclass cls_MaxClock;
+ jclass cls_MaxObject;
+ jclass cls_MSPObject;
+ jclass cls_MSPSignal;
+ jmethodID MIDAtom_newAtom_String;
+ jmethodID MIDAtom_newAtom_Float;
+ jmethodID MIDMaxObject_trySetter;
+ jmethodID MIDMSPObject_dspinit;
+ jmethodID MIDMSPObject_emptyPerformer;
+ jfieldID FIDAtom_type;
+ jfieldID FIDMaxObject_pdobj_ptr;
+ jfieldID FIDMaxObject_activity_inlet;
+ jfieldID FIDMaxClock_clock_ptr;
+ jfieldID FIDAtomFloat_value;
+ jfieldID FIDAtomString_value;
+ jfieldID FIDMSPObject_used_inputs;
+ jfieldID FIDMSPObject_used_outputs;
+ jfieldID FIDMSPSignal_vec;
+} PdjCaching;
+extern PdjCaching pdjCaching;
+
+typedef struct _pdjcached_sym {
+ t_symbol *sym;
+ jmethodID mid;
+ int arged;
+ struct _pdjcached_sym *next;
+} t_pdjcached_sym;
+
+extern t_class *pdj_class;
+typedef struct _pdj {
+ t_object x_obj;
+ char *jobject_name;
+ int nb_inlet;
+ char *patch_path;
+
+ /* already resolved symbol to method id */
+ t_pdjcached_sym *cache;
+
+ /* java object instance and class definition */
+ jobject obj;
+ jclass cls;
+
+ /* object method binder */
+ jmethodID MIDbang;
+ jmethodID MIDfloat;
+ jmethodID MIDint;
+ jmethodID MIDlist;
+ jmethodID MIDanything;
+} t_pdj;
+
+extern t_class *pdj_tilde_class;
+typedef struct _pdj_tilde {
+ t_pdj pdj;
+ t_sample _dummy_f;
+
+ /* performer method */
+ jmethodID performer;
+
+ /* pointer to private field _used_inputs/outputs */
+ jobject _used_inputs;
+ jobject _used_outputs;
+
+ /* C array to the java float vector */
+ jobject *ins;
+ jobject *outs;
+
+ int ins_count;
+ int outs_count;
+
+ /* number of arguments sended to the performer */
+ int argc;
+} t_pdj_tilde;
+
+extern t_class *inlet_proxy;
+typedef struct _inlet_proxy {
+ t_object x_obj;
+ t_pdj *peer;
+ int idx;
+} t_inlet_proxy;
+
+
+typedef int JNICALL JNI_CreateJavaVM_func(JavaVM**, JNIEnv**, JavaVMInitArgs*);
+int getuglylibpath(char *path);
+JNI_CreateJavaVM_func *linkjvm(char *vm_type);
+char *pdj_getProperty(char *name);
+
+JNIEnv *init_jvm();
+
+void *pdj_new(t_symbol *s, int argc, t_atom *argv);
+void pdj_free(t_pdj *obj);
+
+void pdj_tilde_dsp(t_pdj_tilde *obj, t_signal **sp);
+void *pdj_tilde_new(t_symbol *s, int argc, t_atom *argv);
+void pdj_tilde_free(t_pdj_tilde *pdjt);
+
+JNIEnv *pdjAttachVM();
+void pdjDetachVM(JNIEnv *env);
+
+extern int REDIRECT_PD_IO;
+extern JavaVM *jni_jvm;
diff --git a/src/pdj~.c b/src/pdj~.c
new file mode 100644
index 0000000..d2f2a36
--- /dev/null
+++ b/src/pdj~.c
@@ -0,0 +1,206 @@
+#include <stdlib.h>
+#include <string.h>
+#include "pdj.h"
+
+t_class *pdj_tilde_class;
+
+
+t_int *pdj_tilde_perform(t_int *w) {
+ JNIEnv *env = pdjAttachVM();
+ t_pdj_tilde *pdjt = (t_pdj_tilde *) (w[1]);
+ int sz = (int) (w[2]);
+ int i, work;
+
+ /* copy buffer in */
+ for (i=0;i<pdjt->ins_count;i++) {
+ t_sample *in = (t_sample *)(w[i+3]);
+ (*env)->SetFloatArrayRegion(env, pdjt->ins[i], 0, sz, in);
+ }
+
+ /* call the performer */
+ (*env)->CallVoidMethod(env, pdjt->pdj.obj, pdjt->performer,
+ pdjt->_used_inputs, pdjt->_used_outputs);
+
+ /* if an exception occured, stop the dsp processing for this object */
+ if ( (*env)->ExceptionOccurred(env) ) {
+ int tmp;
+
+ work = i + 3;
+
+ /* insert silence */
+ for (tmp=0;tmp<pdjt->outs_count;tmp++) {
+ t_sample *out = (t_sample *)(w[work+tmp]);
+ memset(out, 0, sizeof(float) * sz);
+ }
+
+ error("pdj~: exception occured in dsp processing of: %s", pdjt->pdj.jobject_name);
+ (*env)->ExceptionDescribe(env);
+
+ /* cancels current dsp processing */
+ pdjt->performer = pdjCaching.MIDMSPObject_emptyPerformer;
+ }
+
+ /* copy buffer out */
+ work = i + 3;
+ for (i=0;i<pdjt->outs_count;i++) {
+ t_sample *out = (t_sample *)(w[work+i]);
+ (*env)->GetFloatArrayRegion(env, pdjt->outs[i], 0, sz, out);
+ }
+
+ pdjDetachVM(env);
+
+ return (w+pdjt->argc+1);
+}
+
+
+void pdj_tilde_dsp(t_pdj_tilde *pdjt, t_signal **sp) {
+ JNIEnv *env = pdjAttachVM();
+ jobject perfReflection = (*env)->CallObjectMethod(env, pdjt->pdj.obj,
+ pdjCaching.MIDMSPObject_dspinit, (jfloat) sp[0]->s_sr, sp[0]->s_n);
+ int i;
+
+ SHOWEXC;
+ if ( perfReflection == NULL ) {
+ post("pdj~: can't bind MSPObject to dsp chain, dsp(MSPSignal[], MSPSignal[]) returned null");
+ return;
+ }
+
+ pdjt->performer = (*env)->FromReflectedMethod(env, perfReflection);
+ JASSERT(pdjt->performer);
+
+ for(i=0;i<pdjt->ins_count;i++) {
+ jobject tmp, obj = (*env)->GetObjectArrayElement(env, pdjt->_used_inputs, i);
+ JASSERT(obj);
+ if ( pdjt->ins[i] != NULL )
+ (*env)->DeleteGlobalRef(env, pdjt->ins[i]);
+ tmp = (*env)->GetObjectField(env, obj, pdjCaching.FIDMSPSignal_vec);
+ pdjt->ins[i] = (*env)->NewGlobalRef(env, tmp);
+ (*env)->DeleteLocalRef(env, tmp);
+ JASSERT(pdjt->ins[i]);
+ }
+
+ for(i=0;i<pdjt->outs_count;i++) {
+ jobject tmp, obj = (*env)->GetObjectArrayElement(env, pdjt->_used_outputs, i);
+ JASSERT(obj);
+ if ( pdjt->outs[i] != NULL )
+ (*env)->DeleteGlobalRef(env, pdjt->outs[i]);
+ tmp = (*env)->GetObjectField(env, obj, pdjCaching.FIDMSPSignal_vec);
+ pdjt->outs[i] = (*env)->NewGlobalRef(env, tmp);
+ (*env)->DeleteLocalRef(env, tmp);
+ JASSERT(pdjt->outs[i]);
+ }
+ pdjDetachVM(env);
+
+ switch(pdjt->argc) {
+ case 2:
+ // no inlets/outlets == do nothing
+ break;
+ case 3:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec);
+ break;
+ case 4:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec);
+ break;
+ case 5:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec);
+ break;
+ case 6:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec);
+ break;
+ case 7:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,
+ sp[4]->s_vec);
+ break;
+ case 8:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,
+ sp[4]->s_vec, sp[5]->s_vec);
+ break;
+ case 9:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,
+ sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec);
+ break;
+ case 10:
+ dsp_add(pdj_tilde_perform, pdjt->argc, pdjt, sp[0]->s_n,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,
+ sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec);
+ break;
+ default:
+ error("pdj~: too much ~ inlets/outlets, go see the source luke");
+ break;
+ }
+}
+
+
+void *pdj_tilde_new(t_symbol *s, int argc, t_atom *argv) {
+ t_pdj_tilde *pdjt = pdj_new(s, argc, argv);
+ JNIEnv *env;
+ jobject tmp;
+
+ if ( pdjt == NULL )
+ return NULL;
+
+ env = pdjAttachVM();
+
+ /* check if we are really a MSPObject */
+ if ( !(*env)->IsInstanceOf(env, pdjt->pdj.obj, pdjCaching.cls_MSPObject) ) {
+ error("pdj~: class must be a superclass of MSPObject");
+ pdj_free((t_pdj*)pdjt);
+ return NULL;
+ }
+
+ /* initialize inlets/outlets */
+ tmp = (*env)->GetObjectField(env, pdjt->pdj.obj, pdjCaching.FIDMSPObject_used_inputs);
+ JASSERT(tmp);
+ pdjt->_used_inputs = (*env)->NewGlobalRef(env, tmp);
+ (*env)->DeleteLocalRef(env, tmp);
+
+ tmp = (*env)->GetObjectField(env, pdjt->pdj.obj, pdjCaching.FIDMSPObject_used_outputs);
+ JASSERT(tmp);
+ pdjt->_used_outputs = (*env)->NewGlobalRef(env, tmp);
+ (*env)->DeleteLocalRef(env, tmp);
+
+ pdjt->ins_count = (*env)->GetArrayLength(env, pdjt->_used_inputs);
+ pdjt->outs_count = (*env)->GetArrayLength(env, pdjt->_used_outputs);
+
+ pdjDetachVM(env);
+
+ pdjt->ins = malloc(sizeof(jobject) * pdjt->ins_count);
+ memset(pdjt->ins, 0, sizeof(jobject) * pdjt->ins_count);
+ pdjt->outs = malloc(sizeof(jobject) * pdjt->outs_count);
+ memset(pdjt->outs, 0, sizeof(jobject) * pdjt->outs_count);
+
+ // pdj object pointer + signal size + nb inlets + nb outlets
+ pdjt->argc = 2 + pdjt->ins_count + pdjt->outs_count;
+
+ return pdjt;
+}
+
+
+void pdj_tilde_free(t_pdj_tilde *pdjt) {
+ JNIEnv *env = pdjAttachVM();
+ int i;
+
+ (*env)->DeleteGlobalRef(env, pdjt->_used_inputs);
+ (*env)->DeleteGlobalRef(env, pdjt->_used_outputs);
+ for(i=0;i<pdjt->ins_count;i++) {
+ if ( pdjt->ins[i] != NULL )
+ (*env)->DeleteGlobalRef(env, pdjt->ins[i]);
+ }
+ for(i=0;i<pdjt->outs_count;i++) {
+ if ( pdjt->outs[i] != NULL )
+ (*env)->DeleteGlobalRef(env, pdjt->outs[i]);
+ }
+ pdjDetachVM(env);
+
+ free(pdjt->ins);
+ free(pdjt->outs);
+
+ pdj_free((t_pdj*)pdjt);
+}
diff --git a/src/type_handler.c b/src/type_handler.c
new file mode 100644
index 0000000..7089a31
--- /dev/null
+++ b/src/type_handler.c
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include "pdj.h"
+#include "type_handler.h"
+
+int jatom2atom(JNIEnv *env, jobject jatom, t_atom *atom) {
+ int type = (*env)->GetIntField(env, jatom, pdjCaching.FIDAtom_type);
+ const jbyte *symbolValue;
+ jstring value;
+
+ switch(type) {
+ case DataTypes_INT:
+ case DataTypes_FLOAT:
+ atom->a_type = A_FLOAT;
+ atom->a_w.w_float = (*env)->GetFloatField(env, jatom, pdjCaching.FIDAtomFloat_value);
+ return 0;
+
+ case DataTypes_MESSAGE :
+ atom->a_type = A_SYMBOL;
+ value = (*env)->GetObjectField(env, jatom, pdjCaching.FIDAtomString_value);
+ symbolValue = (*env)->GetStringUTFChars(env, value, NULL);
+ atom->a_w.w_symbol = gensym((char *)symbolValue);
+ (*env)->ReleaseStringUTFChars(env, value, symbolValue);
+ return 0;
+
+ default:
+ error("pdj: java unhandled datatype: %d ", type);
+ atom->a_type = A_CANT;
+ return 1;
+ }
+}
+
+
+int jatoms2atoms(JNIEnv *env, jobjectArray jatoms, int *nb_atoms, t_atom *atoms) {
+ jobject obj;
+ int i, rc = 0;
+
+ *nb_atoms = (*env)->GetArrayLength(env, jatoms);
+
+ if ( *nb_atoms >= MAX_ATOMS_STACK ) {
+ error("pdj: array of atoms truncated, original size: %d, new size: %d",
+ *nb_atoms, MAX_ATOMS_STACK);
+ *nb_atoms = MAX_ATOMS_STACK;
+ }
+
+ for(i=0;i<*nb_atoms;i++) {
+ obj = (*env)->GetObjectArrayElement(env, jatoms, i);
+ rc |= jatom2atom(env, obj, atoms+i);
+ }
+
+ return rc;
+}
+
+
+jobject atom2jatom(JNIEnv *env, t_atom *atom) {
+ jstring arg;
+ jobject ret;
+
+ ASSERT(atom);
+ switch(atom->a_type) {
+ case A_NULL:
+ ret = (*env)->CallStaticObjectMethod(env, pdjCaching.cls_Atom,
+ pdjCaching.MIDAtom_newAtom_String, NULL);
+ if ( ret == NULL ) {
+ SHOWEXC;
+ return NULL;
+ }
+ return ret;
+
+ case A_SYMBOL:
+ arg = (*env)->NewStringUTF(env, atom->a_w.w_symbol->s_name);
+ JASSERT(arg);
+ ret = (*env)->CallStaticObjectMethod(env, pdjCaching.cls_Atom,
+ pdjCaching.MIDAtom_newAtom_String, arg);
+ if ( ret == NULL ) {
+ SHOWEXC;
+ return NULL;
+ }
+ return ret;
+
+ case A_FLOAT:
+ ret = (*env)->CallStaticObjectMethod(env, pdjCaching.cls_Atom,
+ pdjCaching.MIDAtom_newAtom_Float, atom->a_w.w_float);
+ if ( ret == NULL ) {
+ SHOWEXC;
+ return NULL;
+ }
+ return ret;
+
+ default:
+ error("pdj: don't know how to handle atom! type=%d", atom->a_type);
+ return NULL;
+ }
+}
+
+
+jobjectArray atoms2jatoms(JNIEnv *env, int argc, t_atom *argv) {
+ jobjectArray ret;
+ int i;
+
+ ret = (*env)->NewObjectArray(env, argc, pdjCaching.cls_Atom, NULL);
+ JASSERT(ret);
+
+ for(i=0;i<argc;i++) {
+ jobject elem = atom2jatom(env, &(argv[i]));
+
+ if ( elem == NULL ) {
+ (*env)->DeleteLocalRef(env, ret);
+ return NULL;
+ }
+
+ (*env)->SetObjectArrayElement(env, ret, i, elem);
+ if ( (*env)->ExceptionOccurred(env) ) {
+ (*env)->ExceptionDescribe(env);
+ (*env)->DeleteLocalRef(env, ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+
+t_symbol *jstring2symbol(JNIEnv *env, jstring strvalue) {
+ const jbyte *tmp = (*env)->GetStringUTFChars(env, strvalue, NULL);
+ t_symbol *value;
+
+ JASSERT(tmp);
+ // TODO : do we have a valid symbol name ???
+ value = gensym((char *) tmp);
+ (*env)->ReleaseStringUTFChars(env, strvalue, tmp);
+
+ return value;
+}
+
+
+/**
+ * Returns the first elements with symbol name
+ */
+t_pd *findPDObject(t_symbol *name) {
+ void *bindlist_class = gensym("bindlist")->s_thing;
+
+ if ( !name->s_thing )
+ return NULL;
+ if ( *name->s_thing != bindlist_class )
+ return name->s_thing;
+
+ // TODO: work with bindlist to find first object
+ error("pdj: duplicate named objects not supported: %s", name->s_name);
+
+ return NULL;
+}
diff --git a/src/type_handler.h b/src/type_handler.h
new file mode 100644
index 0000000..621f618
--- /dev/null
+++ b/src/type_handler.h
@@ -0,0 +1,18 @@
+#include <jni.h>
+#include "m_pd.h"
+
+#define DataTypes_ALL 15
+#define DataTypes_ANYTHING 15
+#define DataTypes_FLOAT 2
+#define DataTypes_INT 1
+#define DataTypes_LIST 4
+#define DataTypes_MESSAGE 8
+
+int jatom2atom(JNIEnv *env, jobject jatom, t_atom *atom);
+int jatoms2atoms(JNIEnv *env, jobjectArray jatoms, int *nb_atoms, t_atom *atoms);
+
+jobject atom2jatom(JNIEnv *env, t_atom *atom);
+jobjectArray atoms2jatoms(JNIEnv *env, int argc, t_atom *argv);
+
+t_symbol *jstring2symbol(JNIEnv *env, jstring strvalue);
+t_pd *findPDObject(t_symbol *name);
diff --git a/www/index.html b/www/index.html
new file mode 100644
index 0000000..219bed2
--- /dev/null
+++ b/www/index.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>java plug-in for pure-data</title>
+ </head>
+ <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
+<body>
+<h1><img src="pdj-logo.png" alt="pdj-logo" align="middle"> java
+external plugin for pure-data</h1>
+<p>PDJ enables you to write java code to interact with pure-data
+objects. The
+API is totally based on Cycling74 Max/MSP 'mxj' object implementation.
+This will enable java mxj objects to run on pure-data with pdj.</p>
+<p>This is a work in progress and some features are missing and some
+might not even be ported. I am trying to do my best to release soon and
+often! If you find any bugs feel free to contact me.</p>
+<h2>Is it compatible with mxj on Max/MSP ?</h2>
+<p>On the I/O level it is. Anything that deals on receiving/sending atoms
+will try to match extact mxj behavior. Off course, mxj is acting like a
+reference implementation. The API complience is based on Max/MSP 4.5.5 with
+MSP signal support.
+</p>
+<h2>How does it looks ?</h2>
+<p>This overview :
+<pre>import com.cycling74.max.*;<br><br>public class help_class extends MaxObject {<br><br> public help_class() {<br> declareOutlets(new int[] { DataTypes.FLOAT, DataTypes.ALL });<br> }<br> <br> public void inlet(float x) {<br> outlet(0, x);<br> }<br> <br> public void bang() {<br> outlet(1, "BANG! received");<br> }<br> <br> public void callme(Atom args[]) {<br> outlet(1, "callme has called with arg1:" + args[0].toString());<br> }<br><br> public void dynamic_method() {<br> outlet(1, "dynamic_method has been called");<br> }<br><br>}<br></pre>
+Looks like this: <br>
+<br>
+<img alt="pdj-help example" src="pdj-help.png">
+</p><p>Please see the <a href="api/index.html">javadoc</a> for more information.</p>
+<h2>How to write pdj externals:</h2>
+<p>Edit your .java in the /classes directory (in the pdj home) with your
+favorite editor. Type the name of the .java file (without the extension)
+into the pdj object argument. If pdj finds out that the .java is younger
+than the .class, pdj will compile it for you.</p>
+<h2>Requirements:</h2>
+<li><a href="http://www-crca.ucsd.edu/%7Emsp/software.html">pure-data</a>
+0.38.x or better</li>
+<li><a href="http://java.sun.com">java jdk</a> 1.4.x or better; <i><b>use java 1.5 !!!</b></i></li>
+<li><a href="http://ant.apache.org">apache ant</a> 1.5.x or better for
+building</li>
+<li><b>works on linux, os x (tested on 10.4) and windows</b></li>
+<h2>Download:</h2>
+<p>Download latest release here: <a href="pdj-0.8.3.tar.gz">pdj-0.8.3.tar.gz</a>
+/ <a href="pdj-0.8.3-win32.zip">pdj-0.8.3-win32.zip</a>
+(September 9 2006)
+</p>
+<small>Contact: asb2m10 .at. users.sourceforge.net</small>
+</body>
+</html>
diff --git a/www/pdj-help.png b/www/pdj-help.png
new file mode 100644
index 0000000..d384b2b
--- /dev/null
+++ b/www/pdj-help.png
Binary files differ
diff --git a/www/pdj-logo.png b/www/pdj-logo.png
new file mode 100644
index 0000000..5930dfa
--- /dev/null
+++ b/www/pdj-logo.png
Binary files differ
diff --git a/www/stylesheet.css b/www/stylesheet.css
new file mode 100644
index 0000000..6ddda95
--- /dev/null
+++ b/www/stylesheet.css
@@ -0,0 +1,35 @@
+body {
+ margin:5px;
+ padding:5px;
+ background-color:white;
+ font-size : 11px;
+ font-family : verdana, arial, helvetica, sans-serif;
+ width: 785px;
+ color:black;
+ }
+h1 {
+ font-size : 28px;
+ padding:5px;
+ border:1px dashed black;
+}
+
+h2 {
+ font-size : 16px;
+ padding:2px;
+}
+
+h3 {
+ font-size : 16px;
+ padding:2px;
+}
+
+a {
+ color:#09C;
+ font-size:11px;
+ text-decoration:none;
+ font-weight:600;
+ font-family:verdana, arial, helvetica, sans-serif;
+ }
+a:link {color:#09c;}
+a:visited {color:#07a;}
+a:hover {background-color:white;}