-
Notifications
You must be signed in to change notification settings - Fork 91
Sample Code for Android
Yuya Matsuo edited this page Feb 1, 2020
·
3 revisions
Some code snippets to help get you started with playing Pd patches in your Android applications.
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);
/* 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 */
}
/* 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();
}
}
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();
}
/* We'll use this to catch print statements from Pd
when the user has a [print] object */
private final PdDispatcher myDispatcher = new PdUiDispatcher() {
@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 source, 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(String source, Object... args) {
for (Object arg: args) {
Log.i("receiveList atom:", arg.toString());
}
}
/* When we receive a symbol from Pd */
@Override public void receiveSymbol(String source, String symbol) {
Logie("receiveSymbol", symbol);
}
/* When we receive a float from Pd */
@Override public void receiveFloat(String source, float x) {
Log.i("receiveFloat", x.toString());
}
/* When we receive a bang from Pd */
@Override public void receiveBang(String source) {
Log.i("receiveBang", "bang!");
}
};
/* 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);
}
/* 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);
}