PdForAndroid
libpd has moved to GitHub: https://github.com/libpd
Here are some code snippets to help get you started with playing Pd patches in your Android applications.
Unpacking patches
String patchDir = "/sdcard/mypatches";
try {
/* here we are unpacking the resource in res/raw/patch.zip to the sdcard;
the final argument specifies whether to overwrite any existing files
or not */
IoUtils.extractZipResource(getResources().openRawResource(R.raw.patch),
new File(patchDir), true);
} catch (IOException e) {
Log.e("PdTag", e.toString());
}
/* here we're telling Pd to search the path to the patch we just unpacked;
this is useful if you unpack several zipfiles with different sets of
abstractions which you want on the path */
PdBase.addToSearchPath(patchDir);
Launching Pd
/* synchronize on this lock whenever you access pdService */
private final Object lock = new Object();
/* the reference to the actual launched PdService */
PdService pdService = null;
private final ServiceConnection serviceConnection = new ServiceConnection() {
/* This gets called when our service is bound and sets up */
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized(lock) {
pdService = ((PdService.PdBinder)service).getService();
initPd(); /* see below */
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
/* this method will never be called */
}
};
/* actually bind the service, which triggers the code above;
this is the method you should call to launch Pd */
private void initPdService() {
/* a separate thread is not strictly necessary,
but it improves responsiveness */
new Thread() {
@Override
public void run() {
bindService(new Intent(MyPdDemo.this, PdService.class),
serviceConnection, BIND_AUTO_CREATE);
}
}.start();
}
/* this is how we initialize Pd */
private void initPd() {
/* here is where we bind the print statement catcher defined below */
PdBase.setReceiver(myDispatcher);
/* here we are adding the listener for various messages
from Pd sent to "GUI", i.e., anything that goes into the object
[s GUI] will send to the listener defined below */
dispatcher.addListener("GUI", myListener);
startAudio(); /* see below */
}
Starting and stopping Pd audio
/* this is where we'll save the handle of the Pd patch */
int patch = 0;
private void startAudio() {
synchronized (lock) {
if (pdService == null) return;
if (!initAudio(2, 2) && !initAudio(1, 2)) { /* see below */
if (!initAudio(0, 2)) {
Log.e("PdTag", "Unable to initialize audio interface");
finish();
return;
} else {
Log.w("PdTag", "No audio input available");
}
}
if (patch == 0) {
try {
/* assuming here that the patch zipfile contained a single
folder "patch/" that contains an _main.pd */
String path = "/sdcard/mypatches/patch";
/* open Pd patch and save its handle for future reference */
patch = PdBase.openPatch(new File(path, "_main.pd"));
} catch (IOException e) {
Log.e("PdTag", e.toString());
finish();
return;
}
try {
/* sleep for one second to give Pd a chance to load samples and such;
this is not always necessary, but not doing this may give rise to
obscure glitches when the patch contains audio files */
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
}
pdService.startAudio(new Intent(this, ScenePlayer.class),
R.drawable.notification_icon, "Pure Data", "Return to Pure Data.");
}
}
/* helper method for startAudio();
try to initialize Pd audio for the given number of input/output channels,
return true on success */
private boolean initAudio(int nIn, int nOut) {
try {
pdService.initAudio(SAMPLE_RATE, nIn, nOut, -1);
/* negative values default to PdService preferences */
} catch (IOException e) {
Log.e("PdTag", e.toString());
return false;
}
return true;
}
private void stopAudio() {
synchronized (lock) {
if (pdService == null) return;
/* consider ramping down the volume here to avoid clicks */
pdService.stopAudio();
}
}
Shutting Pd down
private void cleanup() {
synchronized(lock) {
/* make sure to release all resources */
stopAudio();
if (patch != 0) {
PdBase.closePatch(patch);
patch = 0;
}
dispatcher.release();
PdBase.release();
try {
unbindService(serviceConnection);
} catch (IllegalArgumentException e) {
// already unbound
pdService = null;
}
}
}
/* override default exit method to run cleanup() first */
@Override
public void onDestroy() {
cleanup();
super.onDestroy();
}
Receiving messages from Pd
/* We'll use this to catch print statements from Pd
when the user has a [print] object */
private final PdDispatcher myDispatcher = new PdDispatcher() {
@Override
public void print(String s) {
Log.i("Pd print", s);
}
};
/* We'll use this to listen out for messages from Pd.
Later we'll hook this up to a named receiver. */
private final PdListener myListener = new PdListener() {
@Override
public void receiveMessage(String symbol, Object... args) {
Log.i("receiveMessage symbol:", symbol);
for (Object arg: args) {
Log.i("receiveMessage atom:", arg.toString());
}
}
/* What to do when we receive a list from Pd. In this example
we're collecting the list from Pd and outputting each atom */
@Override
public void receiveList(Object... args) {
for (Object arg: args) {
Log.i("receiveList atom:", arg.toString());
}
}
/* When we receive a symbol from Pd */
@Override public void receiveSymbol(String symbol) {
Logie("receiveSymbol", symbol);
}
/* When we receive a float from Pd */
@Override public void receiveFloat(float x) {
Log.i("receiveFloat", x.toString());
}
/* When we receive a bang from Pd */
@Override public void receiveBang() {
Log.i("receiveBang", "bang!");
}
};
Sending messages into Pd
/* Here is an example of how to send a bang to Pd from Java,
for example to send to [r hello] you would invoke x.sendBang("hello") */
public void sendBang(String s) {
PdBase.sendBang(s);
}
/* Here is a more complex example of how to send a list of data to Pd.
Here we're assuming that s looks like a Pd-list,
for example s="foo bar blah".
See also PdBase.sendFloat() and PdBase.sendSymbol() */
public void send(String dest, String s) {
String[] pieces = s.split(" ");
Object[] list = new Object[pieces.length];
for (int i=0; i < pieces.length; i++) {
try {
list[i] = Float.parseFloat(pieces[i]);
} catch (NumberFormatException e) {
list[i] = pieces[i];
}
}
PdBase.sendList(dest, list);
}
MIDI over Bluetooth
pd-for-android includes support for MIDI over Bluetooth, and so it now depends on the btmidi repository. If you’re checking out a fresh copy of pd-for-android, you’ll automatically get btmidi as a submodule when checking out dependencies. If you’re updating an existing copy of pd-for-android, simply repeat the steps for initializing and updating submodules that are documented on the Layout page. Make sure to use the latest version of the ADT, or else it may balk at the nested dependencies that come with the latest additions.
The tests and demos of btmidi should give you an idea how to use MIDI over Bluetooth (building the hardware may be the hardest part). The PdTest project shows how to integrate libpd and MIDI over Bluetooth.
Miscellaneous
/* this is a useful method for making sure that your app does the correct
thing when a phone call comes in; call this once during the setup of
your app */
private void initSystemServices() {
TelephonyManager telephonyManager =
(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
synchronized (lock) {
if (pdService == null) return;
if (state == TelephonyManager.CALL_STATE_IDLE) {
if (play.isChecked() && !pdService.isRunning()) {
startAudio();
}
} else {
if (pdService.isRunning()) {
stopAudio();
}
}
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}

