From 62a87399080244ca5f4b3711a037142c55030ff1 Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Sun, 29 Jun 2025 15:12:04 +0200
Subject: [PATCH 01/11] Give up com.noisepages.nettoyeur:midi
Reimplement Midi adapters using Android Midi API,
reusing Peter's (from/to)WireConverters coming from:
https://github.com/nettoyeurny/btmidi/blob/master/AndroidMidi/src/com/noisepages/nettoyeur/midi/
---
PdCore/build.gradle | 2 -
.../android/midi/MidiToPdAdapter.java | 119 +++++++++++-------
.../android/midi/PdToMidiAdapter.java | 82 +++++++-----
3 files changed, 121 insertions(+), 82 deletions(-)
diff --git a/PdCore/build.gradle b/PdCore/build.gradle
index feb45c59..c1ccca55 100644
--- a/PdCore/build.gradle
+++ b/PdCore/build.gradle
@@ -9,8 +9,6 @@ archivesBaseName = 'pd-core'
version = rootProject.version
dependencies {
- api 'com.noisepages.nettoyeur:midi:1.0.0-rc1'
- implementation 'com.noisepages.nettoyeur:midi:1.0.0-rc1'
implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
}
diff --git a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java b/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
index 7c9662a2..ad6a0ea6 100644
--- a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
+++ b/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
@@ -8,61 +8,88 @@
package org.puredata.android.midi;
import org.puredata.core.PdBase;
-
-import com.noisepages.nettoyeur.midi.MidiReceiver;
+import android.media.midi.MidiReceiver;
/**
* Adapter class for connecting output from AndroidMidi to MIDI input for Pd.
*
* @author Peter Brinkmann (peter.brinkmann@gmail.com)
+ * @author Antoine Rousseau (antoine@metalu.net)
*/
-public class MidiToPdAdapter implements MidiReceiver {
-
- @Override
- public void onRawByte(byte value) {
- PdBase.sendMidiByte(0, value);
- }
-
- @Override
- public void onProgramChange(int channel, int program) {
- PdBase.sendProgramChange(channel, program);
- }
-
- @Override
- public void onPolyAftertouch(int channel, int key, int velocity) {
- PdBase.sendPolyAftertouch(channel, key, velocity);
- }
-
- @Override
- public void onPitchBend(int channel, int value) {
- PdBase.sendPitchBend(channel, value);
+public class MidiToPdAdapter extends MidiReceiver {
+ private static enum State {
+ NOTE_OFF, NOTE_ON, POLY_TOUCH, CONTROL_CHANGE, PROGRAM_CHANGE, AFTERTOUCH, PITCH_BEND, NONE
}
+ private State midiState = State.NONE;
+ private int channel;
+ private int firstByte;
@Override
- public void onNoteOn(int channel, int key, int velocity) {
- PdBase.sendNoteOn(channel, key, velocity);
+ public void onSend(byte[] msg, int offset, int count, long timestamp) {
+ while(count-- != 0) processByte(msg[offset++]);
}
- @Override
- public void onNoteOff(int channel, int key, int velocity) {
- PdBase.sendNoteOn(channel, key, 0);
- }
-
- @Override
- public void onControlChange(int channel, int controller, int value) {
- PdBase.sendControlChange(channel, controller, value);
- }
-
- @Override
- public void onAftertouch(int channel, int velocity) {
- PdBase.sendAftertouch(channel, velocity);
- }
-
- @Override
- public boolean beginBlock() {
- return false;
+ private void processByte(int b) {
+ if (b < 0) {
+ midiState = State.values()[(b >> 4) & 0x07];
+ if (midiState != State.NONE) {
+ channel = b & 0x0f;
+ firstByte = -1;
+ } else {
+ PdBase.sendMidiByte(0, b);
+ }
+ } else {
+ switch (midiState) {
+ case NOTE_OFF:
+ if (firstByte < 0) {
+ firstByte = b;
+ } else {
+ PdBase.sendNoteOn(channel, firstByte, 0);
+ firstByte = -1;
+ }
+ break;
+ case NOTE_ON:
+ if (firstByte < 0) {
+ firstByte = b;
+ } else {
+ PdBase.sendNoteOn(channel, firstByte, b);
+ firstByte = -1;
+ }
+ break;
+ case POLY_TOUCH:
+ if (firstByte < 0) {
+ firstByte = b;
+ } else {
+ PdBase.sendPolyAftertouch(channel, firstByte, b);
+ firstByte = -1;
+ }
+ break;
+ case CONTROL_CHANGE:
+ if (firstByte < 0) {
+ firstByte = b;
+ } else {
+ PdBase.sendControlChange(channel, firstByte, b);
+ firstByte = -1;
+ }
+ break;
+ case PROGRAM_CHANGE:
+ PdBase.sendProgramChange(channel, b);
+ break;
+ case AFTERTOUCH:
+ PdBase.sendAftertouch(channel, b);
+ break;
+ case PITCH_BEND:
+ if (firstByte < 0) {
+ firstByte = b;
+ } else {
+ PdBase.sendPitchBend(channel, ((b << 7) | firstByte) - 8192);
+ firstByte = -1;
+ }
+ break;
+ default /* State.NONE */:
+ PdBase.sendMidiByte(0, b);
+ break;
+ }
+ }
}
-
- @Override
- public void endBlock() {}
-}
\ No newline at end of file
+}
diff --git a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java b/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
index 01f7f289..120a463c 100644
--- a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
+++ b/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
@@ -8,70 +8,84 @@
package org.puredata.android.midi;
import org.puredata.core.PdMidiReceiver;
-
-import com.noisepages.nettoyeur.midi.MidiReceiver;
+import android.media.midi.MidiInputPort;
/**
* Adapter class for connecting MIDI output from Pd to input for AndroidMidi.
*
* @author Peter Brinkmann (peter.brinkmann@gmail.com)
+ * @author Antoine Rousseau (antoine@metalu.net)
*/
public class PdToMidiAdapter implements PdMidiReceiver {
+ private final MidiInputPort inputPort;
- private final MidiReceiver receiver;
-
- /**
- * Constructor. Note that instances of this class still need to be installed with
- * PdBase.setMidiReceiver.
- *
- * @param receiver to forward MIDI messages to
- */
- public PdToMidiAdapter(MidiReceiver receiver) {
- this.receiver = receiver;
+ public PdToMidiAdapter(MidiInputPort inputPort) {
+ this.inputPort = inputPort;
}
-
+
@Override
- public void receiveProgramChange(int channel, int value) {
- receiver.onProgramChange(channel, value);
+ public void receiveNoteOn(int channel, int pitch, int velocity) {
+ write(0x90, channel, pitch, velocity);
}
-
+
@Override
public void receivePolyAftertouch(int channel, int pitch, int value) {
- receiver.onPolyAftertouch(channel, pitch, value);
+ write(0xa0, channel, pitch, value);
}
-
+
@Override
- public void receivePitchBend(int channel, int value) {
- receiver.onPitchBend(channel, value);
+ public void receiveControlChange(int channel, int controller, int value) {
+ write(0xb0, channel, controller, value);
}
-
+
@Override
- public void receiveNoteOn(int channel, int pitch, int velocity) {
- receiver.onNoteOn(channel, pitch, velocity);
+ public void receiveProgramChange(int channel, int program) {
+ write(0xc0, channel, program);
}
-
+
@Override
- public void receiveMidiByte(int port, int value) {
- receiver.onRawByte((byte) value);
+ public void receiveAftertouch(int channel, int value) {
+ write(0xd0, channel, value);
}
-
+
@Override
- public void receiveControlChange(int channel, int controller, int value) {
- receiver.onControlChange(channel, controller, value);
+ public void receivePitchBend(int channel, int value) {
+ value += 8192;
+ write(0xe0, channel, (value & 0x7f), (value >> 7));
}
-
+
@Override
- public void receiveAftertouch(int channel, int value) {
- receiver.onAftertouch(channel, value);
+ public void receiveMidiByte(int port, int value) {
+ final byte[] message = {(byte) value};
+ writeMessage(message);
+ }
+
+ private static byte firstByte(int msg, int ch) {
+ return (byte) (msg | (ch & 0x0f));
+ }
+
+ private void write(int msg, int ch, int a) {
+ final byte[] message = {firstByte(msg, ch), (byte) a};
+ writeMessage(message);
+ }
+
+ private void write(int msg, int ch, int a, int b) {
+ final byte[] message = {firstByte(msg, ch), (byte) a, (byte) b};
+ writeMessage(message);
+ }
+
+ private void writeMessage(byte[] message) {
+ try {
+ inputPort.send(message, 0, message.length);
+ } catch(Exception e) {}
}
@Override
public boolean beginBlock() {
- return receiver.beginBlock();
+ return false;
}
@Override
public void endBlock() {
- receiver.endBlock();
}
}
From f824aecd20d3b9ca2339994e132756f25d84f802 Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Tue, 1 Jul 2025 16:58:37 +0200
Subject: [PATCH 02/11] Midi adapters: handle multiple devices, and
javadoc-ument code
---
.../android/midi/MidiToPdAdapter.java | 34 +++++++-
.../android/midi/PdToMidiAdapter.java | 87 +++++++++++++++++--
2 files changed, 112 insertions(+), 9 deletions(-)
diff --git a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java b/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
index ad6a0ea6..7daa6f3d 100644
--- a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
+++ b/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
@@ -17,6 +17,7 @@
* @author Antoine Rousseau (antoine@metalu.net)
*/
public class MidiToPdAdapter extends MidiReceiver {
+ private final int port;
private static enum State {
NOTE_OFF, NOTE_ON, POLY_TOUCH, CONTROL_CHANGE, PROGRAM_CHANGE, AFTERTOUCH, PITCH_BEND, NONE
}
@@ -24,6 +25,37 @@ private static enum State {
private int channel;
private int firstByte;
+/**
+ * Create an adapter for a specific port, to connect to a MidiOutputPort
+ * @param port starting at 0; Midi messages sent to Pd will have the channel increased by (16 * port).
+ *
+ * Example code:
+ *
{@code
+MidiManager midiManager = (MidiManager) getSystemService(MIDI_SERVICE);
+final MidiDeviceInfo[] infos = midiManager.getDevices();
+if (infos.length == 0) return;
+final MidiDeviceInfo info = infos[0]; // Select the first available device
+midiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+ @Override
+ public void onDeviceOpened(MidiDevice device) {
+ if (device == null) return;
+ for (MidiDeviceInfo.PortInfo portInfo : device.getInfo().getPorts()) {
+ if (portInfo.getType() == MidiDeviceInfo.PortInfo.TYPE_OUTPUT) {
+ MidiOutputPort outputPort = device.openOutputPort(portInfo.getPortNumber());
+ if (outputPort != null) {
+ outputPort.connect(new MidiToPdAdapter(0)); // Map the device to Pd Midi port 0 (channel 0-15)
+ break; // Only connect to the first available output port
+ }
+ }
+ }
+ }
+}
+ * }
+ */
+ public MidiToPdAdapter(int port) {
+ this.port = port;
+ }
+
@Override
public void onSend(byte[] msg, int offset, int count, long timestamp) {
while(count-- != 0) processByte(msg[offset++]);
@@ -33,7 +65,7 @@ private void processByte(int b) {
if (b < 0) {
midiState = State.values()[(b >> 4) & 0x07];
if (midiState != State.NONE) {
- channel = b & 0x0f;
+ channel = b & 0x0f + 16 * port;
firstByte = -1;
} else {
PdBase.sendMidiByte(0, b);
diff --git a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java b/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
index 120a463c..03743c2d 100644
--- a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
+++ b/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
@@ -9,6 +9,9 @@
import org.puredata.core.PdMidiReceiver;
import android.media.midi.MidiInputPort;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Map.Entry;
/**
* Adapter class for connecting MIDI output from Pd to input for AndroidMidi.
@@ -17,47 +20,112 @@
* @author Antoine Rousseau (antoine@metalu.net)
*/
public class PdToMidiAdapter implements PdMidiReceiver {
- private final MidiInputPort inputPort;
+ private Map inputPorts = new HashMap();
- public PdToMidiAdapter(MidiInputPort inputPort) {
- this.inputPort = inputPort;
+/**
+ * Send Midi messages from Pd to a Midi device
+ * @param inputPort input port of the Midi ouput device, as returned by MidiDevice.openInputPort()
+ * @param pdPort starting at 0; Midi messages received from Pd whose channel is between
+ * (16 * pdPort) and (16 * pdPort + 15) will be sent to the device with the channel reduced by (16 * pdPort)
+ *
+ * Example code:
+ * {@code
+MidiManager midiManager = (MidiManager) getSystemService(MIDI_SERVICE);
+final MidiDeviceInfo[] infos = midiManager.getDevices();
+if (infos.length == 0) return;
+final MidiDeviceInfo info = infos[0]; // Select the first available device
+midiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+ @Override
+ public void onDeviceOpened(MidiDevice device) {
+ if (device == null) return;
+ for (MidiDeviceInfo.PortInfo portInfo : device.getInfo().getPorts()) {
+ if (portInfo.getType() == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
+ MidiInputPort inputPort = device.openInputPort(portInfo.getPortNumber());
+ if (inputPort != null) {
+ pdToMidiAdapter.open(inputPort, 0); // Map the device to Pd Midi port 0 (channel 0-15)
+ break; // Only connect to the first available input port
+ }
+ }
+ }
+ }
+}
+ * }
+ */
+ public void open(MidiInputPort inputPort, int pdPort) {
+ close(inputPort);
+ close(pdPort);
+ inputPorts.put(pdPort, inputPort);
+ }
+
+/**
+ * Close the connection to a device port
+ * @param inputPort input port of the Midi ouput device, that needs to be closed
+ */
+ public void close(MidiInputPort inputPort) {
+ if(! inputPorts.containsValue(inputPort)) return;
+ for (Entry entry : inputPorts.entrySet()) {
+ if (entry.getValue().equals(inputPort)) {
+ close(entry.getKey());
+ }
+ }
+ }
+
+/**
+ * Close the connection from a Pd Midi port
+ * @param pdPort starting at 0; Midi messages coming from Pd for this port will be ignored, and the associated MidiInputPort will be closed
+ */
+ public void close(int pdPort) {
+ MidiInputPort inputPort = inputPorts.get(pdPort);
+ if(inputPort != null) {
+ try {
+ inputPort.close();
+ } catch(Exception e) {}
+ inputPorts.remove(pdPort);
+ }
}
+/** @hidden to javadoc*/
@Override
public void receiveNoteOn(int channel, int pitch, int velocity) {
write(0x90, channel, pitch, velocity);
}
+/** @hidden to javadoc*/
@Override
public void receivePolyAftertouch(int channel, int pitch, int value) {
write(0xa0, channel, pitch, value);
}
+/** @hidden to javadoc*/
@Override
public void receiveControlChange(int channel, int controller, int value) {
write(0xb0, channel, controller, value);
}
+/** @hidden to javadoc*/
@Override
public void receiveProgramChange(int channel, int program) {
write(0xc0, channel, program);
}
+/** @hidden to javadoc*/
@Override
public void receiveAftertouch(int channel, int value) {
write(0xd0, channel, value);
}
+/** @hidden to javadoc*/
@Override
public void receivePitchBend(int channel, int value) {
value += 8192;
write(0xe0, channel, (value & 0x7f), (value >> 7));
}
+/** @hidden to javadoc*/
@Override
public void receiveMidiByte(int port, int value) {
final byte[] message = {(byte) value};
- writeMessage(message);
+ writeMessage(port, message);
}
private static byte firstByte(int msg, int ch) {
@@ -66,25 +134,28 @@ private static byte firstByte(int msg, int ch) {
private void write(int msg, int ch, int a) {
final byte[] message = {firstByte(msg, ch), (byte) a};
- writeMessage(message);
+ writeMessage(ch, message);
}
private void write(int msg, int ch, int a, int b) {
final byte[] message = {firstByte(msg, ch), (byte) a, (byte) b};
- writeMessage(message);
+ writeMessage(ch, message);
}
- private void writeMessage(byte[] message) {
- try {
+ private void writeMessage(int channel, byte[] message) {
+ MidiInputPort inputPort = inputPorts.get(channel / 16);
+ if(inputPort != null) try {
inputPort.send(message, 0, message.length);
} catch(Exception e) {}
}
+/** @hidden to javadoc*/
@Override
public boolean beginBlock() {
return false;
}
+/** @hidden to javadoc*/
@Override
public void endBlock() {
}
From 660f15750c7cb34dda481eab28a2737887438dbe Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Thu, 3 Jul 2025 17:27:06 +0200
Subject: [PATCH 03/11] PdCore: add a 'Verbose [on/off]' preference
---
.../src/main/java/org/puredata/android/service/PdService.java | 2 ++
PdCore/src/main/res/values/strings.xml | 3 +++
PdCore/src/main/res/xml/preferences.xml | 2 ++
3 files changed, 7 insertions(+)
diff --git a/PdCore/src/main/java/org/puredata/android/service/PdService.java b/PdCore/src/main/java/org/puredata/android/service/PdService.java
index db52ca02..adf2e3a0 100644
--- a/PdCore/src/main/java/org/puredata/android/service/PdService.java
+++ b/PdCore/src/main/java/org/puredata/android/service/PdService.java
@@ -143,6 +143,8 @@ public synchronized void initAudio(int srate, int nic, int noc, float millis) th
inputChannels = nic;
outputChannels = noc;
bufferSizeMillis = millis;
+ boolean verbose = prefs.getBoolean(res.getString(R.string.pref_key_verbose), false);
+ PdBase.setVerbose(verbose);
}
/**
diff --git a/PdCore/src/main/res/values/strings.xml b/PdCore/src/main/res/values/strings.xml
index 2aa223d6..26d9d507 100644
--- a/PdCore/src/main/res/values/strings.xml
+++ b/PdCore/src/main/res/values/strings.xml
@@ -11,4 +11,7 @@
OUTPUT_CHANNELS
Output channels
Number of output channels
+ PD_VERBOSE
+ Verbose
+ Increase Pd loglevel
diff --git a/PdCore/src/main/res/xml/preferences.xml b/PdCore/src/main/res/xml/preferences.xml
index 5a0dd393..c33c0714 100644
--- a/PdCore/src/main/res/xml/preferences.xml
+++ b/PdCore/src/main/res/xml/preferences.xml
@@ -11,4 +11,6 @@
+
From 0eb78b244a933be6a3340ea6314d1562e959de4c Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Sat, 12 Jul 2025 19:22:33 +0200
Subject: [PATCH 04/11] upgrade android tools
AGP 8.11.0
gradle 8.13
buildToolsVersion 35.0.0
ndk 27.2.12479018
---
build.gradle | 6 +++---
gradle/wrapper/gradle-wrapper.properties | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/build.gradle b/build.gradle
index 159f4ba2..dc27539b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.0.2'
+ classpath 'com.android.tools.build:gradle:8.11.0'
}
}
@@ -41,7 +41,7 @@ allprojects {
ext {
minSdkVersion = 28
compileSdkVersion = 34
- buildToolsVersion = '34.0.0'
+ buildToolsVersion = '35.0.0'
androidxLegacySupportVersion = '1.0.0'
- ndkVersion = '25.2.9519653' // https://developer.android.com/ndk/downloads#lts-downloads
+ ndkVersion = "27.2.12479018" // https://developer.android.com/ndk/downloads#lts-downloads
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 42defcc9..c6f00302 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 301dce875888c8798f42f6d7637ffa2c483d817b Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Tue, 1 Jul 2025 17:41:43 +0200
Subject: [PATCH 05/11] fix javadoc and lint errors
---
PdCore/AndroidManifest.xml | 1 +
.../puredata/android/io/AudioRecordWrapper.java | 16 ++++++++++------
.../org/puredata/android/io/AudioWrapper.java | 2 +-
.../org/puredata/android/service/PdService.java | 3 +--
4 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/PdCore/AndroidManifest.xml b/PdCore/AndroidManifest.xml
index a647310b..4390c57c 100644
--- a/PdCore/AndroidManifest.xml
+++ b/PdCore/AndroidManifest.xml
@@ -1,4 +1,5 @@
+
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java b/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
index a28bf667..32aa3d94 100644
--- a/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
+++ b/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
@@ -28,7 +28,7 @@
public class AudioRecordWrapper {
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
- private final AudioRecord rec;
+ private AudioRecord rec = null;
private final int bufSizeShorts;
private final BlockingQueue queue = new SynchronousQueue();
private Thread inputThread = null;
@@ -43,14 +43,17 @@ public AudioRecordWrapper(int sampleRate, int inChannels, int bufferSizePerChann
throw new IOException("bad AudioRecord parameters; sr: " + sampleRate + ", ch: " + inChannels + ", bufSize: " + bufferSizePerChannel);
}
while (recSizeBytes < minRecSizeBytes) recSizeBytes += bufSizeBytes;
- rec = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, ENCODING, recSizeBytes);
- if (rec != null && rec.getState() != AudioRecord.STATE_INITIALIZED) {
- rec.release();
- throw new IOException("unable to initialize AudioRecord instance for sr: " + sampleRate + ", ch: " + inChannels + ", bufSize: " + bufferSizePerChannel);
- }
+ try{
+ rec = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, ENCODING, recSizeBytes);
+ if (rec != null && rec.getState() != AudioRecord.STATE_INITIALIZED) {
+ rec.release();
+ throw new IOException("unable to initialize AudioRecord instance for sr: " + sampleRate + ", ch: " + inChannels + ", bufSize: " + bufferSizePerChannel);
+ }
+ } catch(SecurityException e) {}
}
public synchronized void start() {
+ if (rec == null) return;
inputThread = new Thread() {
@Override
public void run() {
@@ -91,6 +94,7 @@ public synchronized void stop() {
}
public synchronized void release() {
+ if (rec == null) return;
stop();
rec.release();
queue.clear();
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java b/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java
index 2c259457..f5de50c9 100644
--- a/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java
+++ b/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java
@@ -9,7 +9,7 @@
import java.io.IOException;
-import org.puredata.android.service.R;
+import org.puredata.android.service.*;
import org.puredata.android.utils.Properties;
import android.annotation.TargetApi;
diff --git a/PdCore/src/main/java/org/puredata/android/service/PdService.java b/PdCore/src/main/java/org/puredata/android/service/PdService.java
index adf2e3a0..18dc6c42 100644
--- a/PdCore/src/main/java/org/puredata/android/service/PdService.java
+++ b/PdCore/src/main/java/org/puredata/android/service/PdService.java
@@ -25,7 +25,6 @@
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
-import androidx.core.app.NotificationCompat;
import android.util.Log;
import java.io.File;
@@ -244,7 +243,7 @@ private Notification makeNotification(Intent intent, int icon, String title, Str
}
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE);
- return new NotificationCompat.Builder(PdService.this, TAG)
+ return new Notification.Builder(PdService.this, TAG)
.setSmallIcon(icon)
.setContentTitle(title)
.setTicker(title)
From 2e2d6f76d3e5759e0f933a152e60e743b158eabe Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Wed, 2 Jul 2025 13:39:31 +0200
Subject: [PATCH 06/11] modernize and simplify ndk build task
---
PdCore/build.gradle | 49 +++++++++------------------------------------
1 file changed, 10 insertions(+), 39 deletions(-)
diff --git a/PdCore/build.gradle b/PdCore/build.gradle
index c1ccca55..0aef954a 100644
--- a/PdCore/build.gradle
+++ b/PdCore/build.gradle
@@ -29,8 +29,6 @@ android {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/jni/libpd/java']
- jniLibs.srcDir 'src/main/libs' //set .so files location to libs
- jni.srcDirs = [] //disable automatic ndk-build call
res.srcDirs = ['src/main/res']
assets.srcDirs = ['assets']
}
@@ -45,34 +43,19 @@ android {
release.setRoot('build-types/release')
}
- tasks.create(name: 'buildNative', type: Exec, description: 'Compile JNI source via NDK') {
- commandLine ndkBuildExecutablePath,
- '-C', file('src/main/jni').absolutePath,
- '-j', Runtime.runtime.availableProcessors(),
- 'all',
- 'NDK_DEBUG=1'
- }
-
- // After ndk-build, copy libexpr.so to libexpr_tilde.so and libfexpr_tilde.so
- buildNative.doLast {
- def src = 'libexpr.so'
- file('src/main/libs').eachDir() { dir ->
- println "Cloning $src in $dir"
- copy { from(dir) into(dir) include(src) rename(src, 'libexpr_tilde.so') }
- copy { from(dir) into(dir) include(src) rename(src, 'libfexpr_tilde.so') }
+ defaultConfig {
+ externalNativeBuild {
+ ndkBuild {
+ arguments "NDK_DEBUG=1", "-j" + Runtime.runtime.availableProcessors()
+ cFlags "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
+ }
}
}
- tasks.create(name: 'cleanNative', type: Exec, description: 'Clean JNI object files') {
- commandLine ndkBuildExecutablePath, '-C', file('src/main/jni').absolutePath, 'clean'
- }
-
- clean.configure {
- dependsOn tasks.named('cleanNative')
- }
-
- tasks.withType(JavaCompile).configureEach {
- dependsOn tasks.named('buildNative')
+ externalNativeBuild {
+ ndkBuild {
+ path "src/main/jni/Android.mk"
+ }
}
libraryVariants.all { variant ->
@@ -84,18 +67,6 @@ android {
import org.apache.tools.ant.taskdefs.condition.Os
-// TODO: Move to convention plugin?
-def getNdkBuildExecutablePath() {
- // android.ndkDirectory should return project.android.ndkVersion ndkDirectory
- def ndkDir = android.ndkDirectory.absolutePath
- def ndkBuildName = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build'
- def ndkBuildFullPath = new File(ndkDir, ndkBuildName).getAbsolutePath()
- if (!new File(ndkBuildFullPath).canExecute()) {
- throw new GradleScriptException("ndk-build executable not found: $ndkBuildFullPath")
- }
- return ndkBuildFullPath
-}
-
task sourcesJar(type: Jar) {
archiveClassifier.set('sources')
from android.sourceSets.main.java.srcDirs
From cb8eb552a5ca33271203d9b6ffeb232a299160d5 Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Mon, 14 Jul 2025 11:57:13 +0200
Subject: [PATCH 07/11] split PdCore to seperate project (composite build)
this allows to make apps independent from PdCore sources,
so they can declare pd-core dependency like a maven one.
---
.gitmodules | 2 +-
PdCore/build.gradle | 189 +++---------------
PdCore/gradle.properties | 1 +
PdCore/{ => pd-core}/AndroidManifest.xml | 0
PdCore/{ => pd-core}/LICENSE.txt | 0
PdCore/pd-core/build.gradle | 187 +++++++++++++++++
.../puredata/android/io/AudioFormatUtil.java | 0
.../puredata/android/io/AudioParameters.java | 0
.../android/io/AudioRecordWrapper.java | 0
.../org/puredata/android/io/AudioWrapper.java | 0
.../java/org/puredata/android/io/PdAudio.java | 0
.../android/midi/MidiToPdAdapter.java | 0
.../android/midi/PdToMidiAdapter.java | 0
.../android/service/PdPreferences.java | 0
.../puredata/android/service/PdService.java | 0
.../android/utils/PdUiDispatcher.java | 0
.../puredata/android/utils/Properties.java | 0
PdCore/{ => pd-core}/src/main/jni/Android.mk | 0
.../{ => pd-core}/src/main/jni/Application.mk | 0
PdCore/pd-core/src/main/jni/libpd | 1 +
.../src/main/res/drawable/icon.png | Bin
.../src/main/res/raw/extra_abs.zip | Bin
.../src/main/res/raw/silence.wav | Bin
.../src/main/res/values/audio.xml | 0
.../src/main/res/values/strings.xml | 0
.../src/main/res/values/styles.xml | 0
.../src/main/res/xml/preferences.xml | 0
PdCore/settings.gradle | 1 +
PdCore/src/main/jni/libpd | 1 -
build.gradle | 25 +--
settings.gradle | 5 +-
31 files changed, 227 insertions(+), 185 deletions(-)
create mode 100644 PdCore/gradle.properties
rename PdCore/{ => pd-core}/AndroidManifest.xml (100%)
rename PdCore/{ => pd-core}/LICENSE.txt (100%)
create mode 100644 PdCore/pd-core/build.gradle
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/io/AudioFormatUtil.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/io/AudioParameters.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/io/AudioRecordWrapper.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/io/AudioWrapper.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/io/PdAudio.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/service/PdPreferences.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/service/PdService.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/utils/PdUiDispatcher.java (100%)
rename PdCore/{ => pd-core}/src/main/java/org/puredata/android/utils/Properties.java (100%)
rename PdCore/{ => pd-core}/src/main/jni/Android.mk (100%)
rename PdCore/{ => pd-core}/src/main/jni/Application.mk (100%)
create mode 160000 PdCore/pd-core/src/main/jni/libpd
rename PdCore/{ => pd-core}/src/main/res/drawable/icon.png (100%)
rename PdCore/{ => pd-core}/src/main/res/raw/extra_abs.zip (100%)
rename PdCore/{ => pd-core}/src/main/res/raw/silence.wav (100%)
rename PdCore/{ => pd-core}/src/main/res/values/audio.xml (100%)
rename PdCore/{ => pd-core}/src/main/res/values/strings.xml (100%)
rename PdCore/{ => pd-core}/src/main/res/values/styles.xml (100%)
rename PdCore/{ => pd-core}/src/main/res/xml/preferences.xml (100%)
create mode 100644 PdCore/settings.gradle
delete mode 160000 PdCore/src/main/jni/libpd
diff --git a/.gitmodules b/.gitmodules
index 241be1c1..f04915a4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "PdCore/src/main/jni/libpd"]
- path = PdCore/src/main/jni/libpd
+ path = PdCore/pd-core/src/main/jni/libpd
url = https://github.com/libpd/libpd.git
diff --git a/PdCore/build.gradle b/PdCore/build.gradle
index 0aef954a..2efef44d 100644
--- a/PdCore/build.gradle
+++ b/PdCore/build.gradle
@@ -1,172 +1,45 @@
-plugins {
- id 'com.android.library'
- id 'signing'
- id 'maven-publish'
-}
-
-group = rootProject.group
-archivesBaseName = 'pd-core'
-version = rootProject.version
-
-dependencies {
- implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
-}
-
-android {
- compileSdkVersion rootProject.compileSdkVersion
- buildToolsVersion rootProject.buildToolsVersion
- ndkVersion rootProject.ndkVersion
- namespace = 'org.puredata.android.service'
-
- defaultConfig {
- minSdkVersion rootProject.minSdkVersion
- targetSdkVersion 33
- versionCode 1
- versionName version
- }
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src/main/java', 'src/main/jni/libpd/java']
- res.srcDirs = ['src/main/res']
- assets.srcDirs = ['assets']
- }
-
- // Move the build types to build-types/
- // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
- // This moves them out of them default location under src//... which would
- // conflict with src/ being used by the main source set.
- // Adding new build types or product flavors should be accompanied
- // by a similar customization.
- debug.setRoot('build-types/debug')
- release.setRoot('build-types/release')
- }
-
- defaultConfig {
- externalNativeBuild {
- ndkBuild {
- arguments "NDK_DEBUG=1", "-j" + Runtime.runtime.availableProcessors()
- cFlags "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
- }
- }
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
}
-
- externalNativeBuild {
- ndkBuild {
- path "src/main/jni/Android.mk"
- }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.11.0'
}
-
- libraryVariants.all { variant ->
- variant.outputs.all { output ->
- outputFileName = "${archivesBaseName}.aar"
- }
- }
-}
-
-import org.apache.tools.ant.taskdefs.condition.Os
-
-task sourcesJar(type: Jar) {
- archiveClassifier.set('sources')
- from android.sourceSets.main.java.srcDirs
-}
-
-task javadoc(type: Javadoc) {
- source = android.sourceSets.main.java.srcDirs
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
- failOnError = false // TODO: fix javadoc issues
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
- archiveClassifier.set('javadoc')
- from javadoc.destinationDir
}
-artifacts {
- archives javadocJar
- archives sourcesJar
+plugins {
+ // must be applied to root project
+ id 'io.github.gradle-nexus.publish-plugin' version '1.0.0'
}
-def siteUrl = 'https://github.com/libpd/pd-for-android'
-
-publishing {
- publications {
- maven(MavenPublication) {
- groupId group
- artifactId archivesBaseName
- version version
- // TODO: include aar artifact from components?
- artifact "${buildDir}/outputs/aar/${archivesBaseName}.aar"
- // ossrh requires javadoc and sources
- artifact sourcesJar
- artifact javadocJar
-
- pom {
- name = "${project.group}:${project.archivesBaseName}"
- description = 'Pure Data for Android'
- url = siteUrl
- licenses {
- license {
- name = 'BSD New'
- url = 'https://raw.githubusercontent.com/libpd/pd-for-android/master/PdCore/LICENSE.txt'
- }
- }
- developers {
- developer {
- id = 'joebowbeer'
- name = 'Joe Bowbeer'
- }
- // TODO: Add all other devs here...
- }
- scm {
- connection = 'scm:git:https://github.com/libpd/pd-for-android'
- developerConnection = 'scm:git:ssh://github.com/libpd/pd-for-android.git'
- url = siteUrl
- }
- }
- }
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
}
}
-// configure publishing to a local directory for testing (not necessary)
-// ./gradlew publishMavenPublicationToLocalRepository
-// tree ./PdCore/build/repos
-publishing {
+// These are specific to PdCore, but nexusPublishing needs them here:
+// https://github.com/gradle-nexus/publish-plugin/issues/84
+group = 'io.github.libpd.android'
+version = '1.3.0'
+
+// Create a Sonatype user token for these environment variables:
+// export ORG_GRADLE_PROJECT_sonatypeUsername=""
+// export ORG_GRADLE_PROJECT_sonatypePassword=""
+nexusPublishing {
repositories {
- maven {
- name = 'local'
- def releasesRepoUrl = "$buildDir/repos/releases"
- def snapshotsRepoUrl = "$buildDir/repos/snapshots"
- url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
- }
+ sonatype {
+ nexusUrl.set(uri('https://s01.oss.sonatype.org/service/local/'))
+ snapshotRepositoryUrl.set(uri('https://s01.oss.sonatype.org/content/repositories/snapshots/'))
+ }
}
}
-// ossrh requires signed releases, but not snapshots.
-// This configures signing if a key is found.
-// The following environment variables provide a signing key and passphrase:
-// export ORG_GRADLE_PROJECT_signingKey=`cat private.pgp`
-// export ORG_GRADLE_PROJECT_signingPassword=""
-// After making the above available, you can try signing using
-// ./gradlew signMavenPublication
-def hasSigningKey = project.hasProperty('signingKeyId') || project.hasProperty('signingKey')
-if (hasSigningKey) {
- sign(project)
-}
-void sign(Project project) {
- project.signing {
- required { project.gradle.taskGraph.hasTask('required') }
- def signingKeyId = project.findProperty('signingKeyId')
- def signingKey = project.findProperty('signingKey')
- def signingPassword = project.findProperty('signingPassword')
- if (signingKeyId) {
- // use in-memory ascii-armored OpenPGP subkey
- useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
- } else if (signingKey) {
- // use in-memory ascii-armored key
- useInMemoryPgpKeys(signingKey, signingPassword)
- }
- sign publishing.publications.maven
- }
+ext {
+ minSdkVersion = 28
+ compileSdkVersion = 34
+ androidxLegacySupportVersion = '1.0.0'
+ ndkVersion = "27.2.12479018" // https://developer.android.com/ndk/downloads#lts-downloads
}
diff --git a/PdCore/gradle.properties b/PdCore/gradle.properties
new file mode 100644
index 00000000..2d8d1e4d
--- /dev/null
+++ b/PdCore/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
\ No newline at end of file
diff --git a/PdCore/AndroidManifest.xml b/PdCore/pd-core/AndroidManifest.xml
similarity index 100%
rename from PdCore/AndroidManifest.xml
rename to PdCore/pd-core/AndroidManifest.xml
diff --git a/PdCore/LICENSE.txt b/PdCore/pd-core/LICENSE.txt
similarity index 100%
rename from PdCore/LICENSE.txt
rename to PdCore/pd-core/LICENSE.txt
diff --git a/PdCore/pd-core/build.gradle b/PdCore/pd-core/build.gradle
new file mode 100644
index 00000000..c33f6a89
--- /dev/null
+++ b/PdCore/pd-core/build.gradle
@@ -0,0 +1,187 @@
+plugins {
+ id 'com.android.library'
+ id 'signing'
+ id 'maven-publish'
+}
+
+archivesBaseName = 'pd-core'
+group = rootProject.group
+version = rootProject.version
+
+dependencies {
+ implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
+}
+
+android {
+ compileSdkVersion rootProject.compileSdkVersion
+ ndkVersion rootProject.ndkVersion
+
+ namespace = 'org.puredata.android.service'
+
+ defaultConfig {
+ minSdkVersion rootProject.minSdkVersion
+ targetSdkVersion rootProject.compileSdkVersion
+ versionCode 1
+ versionName version
+ }
+
+ buildFeatures {
+ prefabPublishing true
+ }
+
+ prefab {
+ pd {
+ headers "src/main/jni/libpd/pure-data/src"
+ }
+ }
+
+ packagingOptions {
+ //exclude("**/*.so")
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/main/java', 'src/main/jni/libpd/java']
+ res.srcDirs = ['src/main/res']
+ assets.srcDirs = ['assets']
+ }
+
+ // Move the build types to build-types/
+ // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
+ // This moves them out of them default location under src//... which would
+ // conflict with src/ being used by the main source set.
+ // Adding new build types or product flavors should be accompanied
+ // by a similar customization.
+ debug.setRoot('build-types/debug')
+ release.setRoot('build-types/release')
+ }
+
+ defaultConfig {
+ externalNativeBuild {
+ ndkBuild {
+ arguments "NDK_DEBUG=1", "-j" + Runtime.runtime.availableProcessors()
+ cFlags "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
+ }
+ }
+ }
+
+ externalNativeBuild {
+ ndkBuild {
+ path "src/main/jni/Android.mk"
+ }
+ }
+
+ android.libraryVariants.all { variant ->
+ variant.outputs.all { output ->
+ output.outputFileName = "${archivesBaseName}.aar"
+ }
+ }
+}
+
+import org.apache.tools.ant.taskdefs.condition.Os
+
+task sourcesJar(type: Jar) {
+ archiveClassifier.set('sources')
+ from android.sourceSets.main.java.srcDirs
+}
+
+task javadoc(type: Javadoc) {
+ source = android.sourceSets.main.java.srcDirs
+ classpath += files("${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
+ failOnError = false // TODO: fix javadoc issues
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ archiveClassifier.set('javadoc')
+ from javadoc.destinationDir
+}
+
+artifacts {
+ archives javadocJar
+ archives sourcesJar
+}
+
+def siteUrl = 'https://github.com/libpd/pd-for-android'
+
+publishing {
+ publications {
+ maven(MavenPublication) {
+ groupId group
+ artifactId archivesBaseName
+ version version
+ // TODO: include aar artifact from components?
+ artifact "${buildDir}/outputs/aar/${archivesBaseName}.aar"
+ // ossrh requires javadoc and sources
+ artifact sourcesJar
+ artifact javadocJar
+
+ pom {
+ name = "${project.group}:${project.archivesBaseName}"
+ description = 'Pure Data for Android'
+ url = siteUrl
+ licenses {
+ license {
+ name = 'BSD New'
+ url = 'https://raw.githubusercontent.com/libpd/pd-for-android/master/PdCore/LICENSE.txt'
+ }
+ }
+ developers {
+ developer {
+ id = 'joebowbeer'
+ name = 'Joe Bowbeer'
+ }
+ // TODO: Add all other devs here...
+ }
+ scm {
+ connection = 'scm:git:https://github.com/libpd/pd-for-android'
+ developerConnection = 'scm:git:ssh://github.com/libpd/pd-for-android.git'
+ url = siteUrl
+ }
+ }
+ }
+ }
+}
+
+// configure publishing to a local directory for testing (not necessary)
+// ./gradlew publishMavenPublicationToLocalRepository
+// tree ./PdCore/build/repos
+publishing {
+ repositories {
+ maven {
+ name = 'local'
+ def releasesRepoUrl = "$buildDir/repos/releases"
+ def snapshotsRepoUrl = "$buildDir/repos/snapshots"
+ url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
+ }
+ }
+}
+
+// ossrh requires signed releases, but not snapshots.
+// This configures signing if a key is found.
+// The following environment variables provide a signing key and passphrase:
+// export ORG_GRADLE_PROJECT_signingKey=`cat private.pgp`
+// export ORG_GRADLE_PROJECT_signingPassword=""
+// After making the above available, you can try signing using
+// ./gradlew signMavenPublication
+def hasSigningKey = project.hasProperty('signingKeyId') || project.hasProperty('signingKey')
+if (hasSigningKey) {
+ sign(project)
+}
+void sign(Project project) {
+ project.signing {
+ required { project.gradle.taskGraph.hasTask('required') }
+ def signingKeyId = project.findProperty('signingKeyId')
+ def signingKey = project.findProperty('signingKey')
+ def signingPassword = project.findProperty('signingPassword')
+ if (signingKeyId) {
+ // use in-memory ascii-armored OpenPGP subkey
+ useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
+ } else if (signingKey) {
+ // use in-memory ascii-armored key
+ useInMemoryPgpKeys(signingKey, signingPassword)
+ }
+ sign publishing.publications.maven
+ }
+}
+
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioFormatUtil.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/AudioFormatUtil.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/io/AudioFormatUtil.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/io/AudioFormatUtil.java
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioParameters.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/AudioParameters.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/io/AudioParameters.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/io/AudioParameters.java
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/io/AudioRecordWrapper.java
diff --git a/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/AudioWrapper.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/io/AudioWrapper.java
diff --git a/PdCore/src/main/java/org/puredata/android/io/PdAudio.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/io/PdAudio.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java
diff --git a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java b/PdCore/pd-core/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java
diff --git a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java b/PdCore/pd-core/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java
diff --git a/PdCore/src/main/java/org/puredata/android/service/PdPreferences.java b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/service/PdPreferences.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java
diff --git a/PdCore/src/main/java/org/puredata/android/service/PdService.java b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/service/PdService.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java
diff --git a/PdCore/src/main/java/org/puredata/android/utils/PdUiDispatcher.java b/PdCore/pd-core/src/main/java/org/puredata/android/utils/PdUiDispatcher.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/utils/PdUiDispatcher.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/utils/PdUiDispatcher.java
diff --git a/PdCore/src/main/java/org/puredata/android/utils/Properties.java b/PdCore/pd-core/src/main/java/org/puredata/android/utils/Properties.java
similarity index 100%
rename from PdCore/src/main/java/org/puredata/android/utils/Properties.java
rename to PdCore/pd-core/src/main/java/org/puredata/android/utils/Properties.java
diff --git a/PdCore/src/main/jni/Android.mk b/PdCore/pd-core/src/main/jni/Android.mk
similarity index 100%
rename from PdCore/src/main/jni/Android.mk
rename to PdCore/pd-core/src/main/jni/Android.mk
diff --git a/PdCore/src/main/jni/Application.mk b/PdCore/pd-core/src/main/jni/Application.mk
similarity index 100%
rename from PdCore/src/main/jni/Application.mk
rename to PdCore/pd-core/src/main/jni/Application.mk
diff --git a/PdCore/pd-core/src/main/jni/libpd b/PdCore/pd-core/src/main/jni/libpd
new file mode 160000
index 00000000..d7d1e1ef
--- /dev/null
+++ b/PdCore/pd-core/src/main/jni/libpd
@@ -0,0 +1 @@
+Subproject commit d7d1e1ef6259583065d7bd0b5b37112fa29d2eb6
diff --git a/PdCore/src/main/res/drawable/icon.png b/PdCore/pd-core/src/main/res/drawable/icon.png
similarity index 100%
rename from PdCore/src/main/res/drawable/icon.png
rename to PdCore/pd-core/src/main/res/drawable/icon.png
diff --git a/PdCore/src/main/res/raw/extra_abs.zip b/PdCore/pd-core/src/main/res/raw/extra_abs.zip
similarity index 100%
rename from PdCore/src/main/res/raw/extra_abs.zip
rename to PdCore/pd-core/src/main/res/raw/extra_abs.zip
diff --git a/PdCore/src/main/res/raw/silence.wav b/PdCore/pd-core/src/main/res/raw/silence.wav
similarity index 100%
rename from PdCore/src/main/res/raw/silence.wav
rename to PdCore/pd-core/src/main/res/raw/silence.wav
diff --git a/PdCore/src/main/res/values/audio.xml b/PdCore/pd-core/src/main/res/values/audio.xml
similarity index 100%
rename from PdCore/src/main/res/values/audio.xml
rename to PdCore/pd-core/src/main/res/values/audio.xml
diff --git a/PdCore/src/main/res/values/strings.xml b/PdCore/pd-core/src/main/res/values/strings.xml
similarity index 100%
rename from PdCore/src/main/res/values/strings.xml
rename to PdCore/pd-core/src/main/res/values/strings.xml
diff --git a/PdCore/src/main/res/values/styles.xml b/PdCore/pd-core/src/main/res/values/styles.xml
similarity index 100%
rename from PdCore/src/main/res/values/styles.xml
rename to PdCore/pd-core/src/main/res/values/styles.xml
diff --git a/PdCore/src/main/res/xml/preferences.xml b/PdCore/pd-core/src/main/res/xml/preferences.xml
similarity index 100%
rename from PdCore/src/main/res/xml/preferences.xml
rename to PdCore/pd-core/src/main/res/xml/preferences.xml
diff --git a/PdCore/settings.gradle b/PdCore/settings.gradle
new file mode 100644
index 00000000..377573a0
--- /dev/null
+++ b/PdCore/settings.gradle
@@ -0,0 +1 @@
+include ':pd-core'
diff --git a/PdCore/src/main/jni/libpd b/PdCore/src/main/jni/libpd
deleted file mode 160000
index fb174959..00000000
--- a/PdCore/src/main/jni/libpd
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit fb174959373d18d8bd135f6a3abf86ded82be534
diff --git a/build.gradle b/build.gradle
index dc27539b..9a0a5fca 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,40 +8,17 @@ buildscript {
}
}
-plugins {
- // must be applied to root project
- id 'io.github.gradle-nexus.publish-plugin' version '1.0.0'
-}
-
-// These are specific to PdCode, but nexusPublishing needs them here:
-// https://github.com/gradle-nexus/publish-plugin/issues/84
-group = 'io.github.libpd.android'
-version = '1.2.1-SNAPSHOT'
-
-// Create a Sonatype user token for these environment variables:
-// export ORG_GRADLE_PROJECT_sonatypeUsername=""
-// export ORG_GRADLE_PROJECT_sonatypePassword=""
-nexusPublishing {
- repositories {
- sonatype {
- nexusUrl.set(uri('https://s01.oss.sonatype.org/service/local/'))
- snapshotRepositoryUrl.set(uri('https://s01.oss.sonatype.org/content/repositories/snapshots/'))
- }
- }
-}
-
allprojects {
repositories {
google()
mavenCentral()
- jcenter() // FIXME: com.noisepages.nettoyeur:midi
}
}
ext {
minSdkVersion = 28
compileSdkVersion = 34
- buildToolsVersion = '35.0.0'
androidxLegacySupportVersion = '1.0.0'
ndkVersion = "27.2.12479018" // https://developer.android.com/ndk/downloads#lts-downloads
+ pdCoreVersion = '1.3.0' // Must match version in PdCore/build.gradle
}
diff --git a/settings.gradle b/settings.gradle
index 7b433b2c..3d576f2b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,8 @@
+rootProject.name = 'pd-for-android'
+
+includeBuild 'PdCore'
+
include ':CircleOfFifths'
-include ':PdCore'
include ':PdTest'
include ':ScenePlayer'
include ':Voice-O-Rama'
From c891749c9d1663ec35d67283bb63552a406fdf01 Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Mon, 14 Jul 2025 12:04:26 +0200
Subject: [PATCH 08/11] update apps build config, to use pd-core prefab, and
refer to 'composite' style dependency
---
CircleOfFifths/build.gradle | 7 ++--
PdTest/build.gradle | 63 +++++++++++++++-------------------
PdTest/jni/Android.mk | 13 ++-----
PdTest/jni/Application.mk | 4 +--
ScenePlayer/build.gradle | 54 +++++++++++++----------------
ScenePlayer/jni/Android.mk | 13 ++-----
ScenePlayer/jni/Application.mk | 4 +--
Voice-O-Rama/build.gradle | 3 +-
8 files changed, 66 insertions(+), 95 deletions(-)
diff --git a/CircleOfFifths/build.gradle b/CircleOfFifths/build.gradle
index ddc476dd..b76e37f9 100644
--- a/CircleOfFifths/build.gradle
+++ b/CircleOfFifths/build.gradle
@@ -1,13 +1,12 @@
apply plugin: 'com.android.application'
dependencies {
- implementation project(':PdCore')
+ implementation 'io.github.libpd.android:pd-core:' + rootProject.pdCoreVersion
}
android {
- compileSdkVersion rootProject.compileSdkVersion
- buildToolsVersion rootProject.buildToolsVersion
- ndkVersion rootProject.ndkVersion
+ compileSdkVersion = rootProject.compileSdkVersion
+ ndkVersion = rootProject.ndkVersion
namespace = 'org.puredata.android.fifths'
defaultConfig {
diff --git a/PdTest/build.gradle b/PdTest/build.gradle
index eac38f5d..3b3cd637 100644
--- a/PdTest/build.gradle
+++ b/PdTest/build.gradle
@@ -2,14 +2,8 @@ apply plugin: 'com.android.application'
import org.apache.tools.ant.taskdefs.condition.Os
-dependencies {
- implementation project(':PdCore')
- implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
-}
-
android {
compileSdkVersion rootProject.compileSdkVersion
- buildToolsVersion rootProject.buildToolsVersion
ndkVersion rootProject.ndkVersion
namespace = 'org.puredata.android.test'
@@ -25,6 +19,14 @@ android {
// }
}
+ packagingOptions {
+ jniLibs {
+ // since externals are loaded at native code level, they need to be copied to the actual
+ // filesystem, instead of staying in the apk; so we must set useLegacyPackaging:
+ useLegacyPackaging true
+ }
+ }
+
buildTypes {
release {
minifyEnabled true
@@ -32,12 +34,16 @@ android {
}
}
+ buildFeatures {
+ // Extract native libs from prefab, to be able to access the headers and to link to the libs
+ prefab true
+ viewBinding true
+ }
+
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
- jniLibs.srcDir 'libs' //set .so files location to libs
- jni.srcDirs = [] //disable automatic ndk-build call
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
@@ -55,36 +61,23 @@ android {
release.setRoot('build-types/release')
}
- tasks.create(name: 'buildNative', type: Exec, description: 'Compile JNI source via NDK') {
- commandLine ndkBuildExecutablePath,
- 'V=1',
- '-C', file('jni').absolutePath,
- '-j', Runtime.runtime.availableProcessors(),
- 'all',
- 'NDK_DEBUG=1'
- }
-
- tasks.create(name: 'cleanNative', type: Exec, description: 'Clean JNI object files') {
- commandLine ndkBuildExecutablePath, 'V=1', '-C', file('jni').absolutePath, 'clean'
- }
-
- clean.configure {
- dependsOn tasks.named('cleanNative')
+ defaultConfig {
+ externalNativeBuild {
+ ndkBuild {
+ arguments "NDK_DEBUG=1", "-j" + Runtime.runtime.availableProcessors()
+ }
+ }
}
- tasks.withType(JavaCompile).configureEach {
- dependsOn tasks.named('buildNative')
+ externalNativeBuild {
+ ndkBuild {
+ path "jni/Android.mk"
+ }
}
}
-// TODO: Move to convention plugin?
-def getNdkBuildExecutablePath() {
- // android.ndkDirectory should return project.android.ndkVersion ndkDirectory
- def ndkDir = android.ndkDirectory.absolutePath
- def ndkBuildName = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build'
- def ndkBuildFullPath = new File(ndkDir, ndkBuildName).getAbsolutePath()
- if (!new File(ndkBuildFullPath).canExecute()) {
- throw new GradleScriptException("ndk-build executable not found: $ndkBuildFullPath")
- }
- return ndkBuildFullPath
+dependencies {
+ implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
+ implementation 'io.github.libpd.android:pd-core:' + rootProject.pdCoreVersion
}
+
diff --git a/PdTest/jni/Android.mk b/PdTest/jni/Android.mk
index 8edb3d03..c4a1bff7 100644
--- a/PdTest/jni/Android.mk
+++ b/PdTest/jni/Android.mk
@@ -2,16 +2,6 @@ LOCAL_PATH := $(call my-dir)
#---------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := pd
-LOCAL_EXPORT_C_INCLUDES := ../../PdCore/src/main/jni/libpd/pure-data/src
-LOCAL_SRC_FILES := ../../PdCore/src/main/libs/$(TARGET_ARCH_ABI)/libpd.so
-ifneq ($(MAKECMDGOALS),clean)
- include $(PREBUILT_SHARED_LIBRARY)
-endif
-
-#---------------------------------------------------------------
-
include $(CLEAR_VARS)
LOCAL_MODULE := helloworld
LOCAL_CFLAGS := -DPD
@@ -20,3 +10,6 @@ LOCAL_SHARED_LIBRARIES = pd
include $(BUILD_SHARED_LIBRARY)
#---------------------------------------------------------------
+
+$(call import-module,prefab/pd-core)
+
diff --git a/PdTest/jni/Application.mk b/PdTest/jni/Application.mk
index 8732a49b..1dfcd802 100644
--- a/PdTest/jni/Application.mk
+++ b/PdTest/jni/Application.mk
@@ -1,4 +1,4 @@
-APP_PLATFORM := android-17
APP_OPTIM := release
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
-APP_ALLOW_MISSING_DEPS=true
\ No newline at end of file
+APP_ALLOW_MISSING_DEPS=true
+
diff --git a/ScenePlayer/build.gradle b/ScenePlayer/build.gradle
index e40a7727..05e41f36 100644
--- a/ScenePlayer/build.gradle
+++ b/ScenePlayer/build.gradle
@@ -3,18 +3,17 @@ apply plugin: 'com.android.application'
import org.apache.tools.ant.taskdefs.condition.Os
dependencies {
- implementation project(':PdCore')
+ implementation 'io.github.libpd.android:pd-core:' + rootProject.pdCoreVersion
}
android {
compileSdkVersion rootProject.compileSdkVersion
- buildToolsVersion rootProject.buildToolsVersion
ndkVersion rootProject.ndkVersion
namespace = 'org.puredata.android.scenes'
defaultConfig {
minSdkVersion rootProject.minSdkVersion
- targetSdkVersion 28
+ targetSdkVersion 33
versionCode 11
versionName "0.9.2"
}
@@ -23,8 +22,6 @@ android {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
- jniLibs.srcDir 'libs' //set .so files location to libs
- jni.srcDirs = [] //disable automatic ndk-build call
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
@@ -42,39 +39,36 @@ android {
release.setRoot('build-types/release')
}
- tasks.create(name: 'buildNative', type: Exec, description: 'Compile JNI source via NDK') {
- commandLine ndkBuildExecutablePath,
- '-C', file('jni').absolutePath,
- '-j', Runtime.runtime.availableProcessors(),
- 'all',
- 'NDK_DEBUG=1'
+ lintOptions {
+ ignore 'ExpiredTargetSdkVersion'
}
- tasks.create(name: 'cleanNative', type: Exec, description: 'Clean JNI object files') {
- commandLine ndkBuildExecutablePath, '-C', file('jni').absolutePath, 'clean'
+ packagingOptions {
+ jniLibs {
+ // since externals are loaded at native code level, they need to be copied to the actual
+ // filesystem, instead of staying in the apk; so we must set useLegacyPackaging:
+ useLegacyPackaging true
+ }
}
- clean.configure {
- dependsOn tasks.named('cleanNative')
+ buildFeatures {
+ // Extract native libs from prefab, to be able to access the headers and to link to the libs
+ prefab = true
+ viewBinding = true
}
- tasks.withType(JavaCompile).configureEach {
- dependsOn tasks.named('buildNative')
+ defaultConfig {
+ externalNativeBuild {
+ ndkBuild {
+ arguments "NDK_DEBUG=1", "-j" + Runtime.runtime.availableProcessors()
+ }
+ }
}
- lintOptions {
- ignore 'ExpiredTargetSdkVersion'
+ externalNativeBuild {
+ ndkBuild {
+ path "jni/Android.mk"
+ }
}
}
-// TODO: Move to convention plugin?
-def getNdkBuildExecutablePath() {
- // android.ndkDirectory should return project.android.ndkVersion ndkDirectory
- def ndkDir = android.ndkDirectory.absolutePath
- def ndkBuildName = Os.isFamily(Os.FAMILY_WINDOWS) ? 'ndk-build.cmd' : 'ndk-build'
- def ndkBuildFullPath = new File(ndkDir, ndkBuildName).getAbsolutePath()
- if (!new File(ndkBuildFullPath).canExecute()) {
- throw new GradleScriptException("ndk-build executable not found: $ndkBuildFullPath")
- }
- return ndkBuildFullPath
-}
diff --git a/ScenePlayer/jni/Android.mk b/ScenePlayer/jni/Android.mk
index 4ef3dec3..1833a659 100644
--- a/ScenePlayer/jni/Android.mk
+++ b/ScenePlayer/jni/Android.mk
@@ -2,16 +2,6 @@ LOCAL_PATH := $(call my-dir)
#---------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := pd
-LOCAL_EXPORT_C_INCLUDES := ../../PdCore/src/main/jni/libpd/pure-data/src
-LOCAL_SRC_FILES := ../../PdCore/src/main/libs/$(TARGET_ARCH_ABI)/libpd.so
-ifneq ($(MAKECMDGOALS),clean)
- include $(PREBUILT_SHARED_LIBRARY)
-endif
-
-#---------------------------------------------------------------
-
include $(CLEAR_VARS)
LOCAL_MODULE := rj_accum
LOCAL_CFLAGS := -DPD
@@ -56,3 +46,6 @@ LOCAL_SHARED_LIBRARIES = pd
include $(BUILD_SHARED_LIBRARY)
#---------------------------------------------------------------
+
+$(call import-module,prefab/pd-core)
+
diff --git a/ScenePlayer/jni/Application.mk b/ScenePlayer/jni/Application.mk
index 8732a49b..1dfcd802 100644
--- a/ScenePlayer/jni/Application.mk
+++ b/ScenePlayer/jni/Application.mk
@@ -1,4 +1,4 @@
-APP_PLATFORM := android-17
APP_OPTIM := release
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
-APP_ALLOW_MISSING_DEPS=true
\ No newline at end of file
+APP_ALLOW_MISSING_DEPS=true
+
diff --git a/Voice-O-Rama/build.gradle b/Voice-O-Rama/build.gradle
index e1b50fe7..ead052eb 100644
--- a/Voice-O-Rama/build.gradle
+++ b/Voice-O-Rama/build.gradle
@@ -1,12 +1,11 @@
apply plugin: 'com.android.application'
dependencies {
- implementation project(':PdCore')
+ implementation 'io.github.libpd.android:pd-core:' + rootProject.pdCoreVersion
}
android {
compileSdkVersion rootProject.compileSdkVersion
- buildToolsVersion rootProject.buildToolsVersion
namespace = 'at.or.at.voiceorama'
defaultConfig {
From 93b0a063fe034ca0638e64d63c8b8e7f90e24a3e Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Mon, 14 Jul 2025 12:05:17 +0200
Subject: [PATCH 09/11] update CI config for latest changes
---
.github/workflows/android.yml | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 92e2e986..936e8e8b 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -26,15 +26,19 @@ jobs:
git submodule sync --recursive
git submodule update --init --recursive
- run: ./gradlew androidDependencies
- - run: ./gradlew clean assembleRelease
+ - run: ./gradlew clean
+ - run: ./gradlew PdCore:pd-core:build
+ env:
+ JVM_OPTS: -Xmx3200m
+ - run: ./gradlew assembleRelease
env:
JVM_OPTS: -Xmx3200m
- uses: actions/upload-artifact@v4
with:
name: pd-core-aar
- path: PdCore/build/outputs/aar
+ path: PdCore/pd-core/build/outputs/aar
- if: github.event_name == 'push'
- run: ./gradlew publishToSonatype
+ run: ./gradlew :PdCore:pd-core:publishToSonatype
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
From 20a9e5dba448fe23a62b2800b3c1cd270184ad0c Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Mon, 14 Jul 2025 14:24:18 +0200
Subject: [PATCH 10/11] clear last lint errors
---
.../android/scenes/SceneDataBase.java | 12 +++++++---
.../puredata/android/scenes/ScenePlayer.java | 22 ++++++++++---------
2 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/ScenePlayer/src/org/puredata/android/scenes/SceneDataBase.java b/ScenePlayer/src/org/puredata/android/scenes/SceneDataBase.java
index eeaa967e..84be2d84 100644
--- a/ScenePlayer/src/org/puredata/android/scenes/SceneDataBase.java
+++ b/ScenePlayer/src/org/puredata/android/scenes/SceneDataBase.java
@@ -181,7 +181,9 @@ public static String getString(Cursor cursor, Column column) {
}
public static String getString(Cursor cursor, String column) {
- return cursor.getString(cursor.getColumnIndex(column));
+ int column_index = cursor.getColumnIndex(column);
+ if(column_index < 0) column_index = 0;
+ return cursor.getString(column_index);
}
public static long getLong(Cursor cursor, Column column) {
@@ -189,7 +191,9 @@ public static long getLong(Cursor cursor, Column column) {
}
public static long getLong(Cursor cursor, String column) {
- return cursor.getLong(cursor.getColumnIndex(column));
+ int column_index = cursor.getColumnIndex(column);
+ if(column_index < 0) column_index = 0;
+ return cursor.getLong(column_index);
}
public static double getDouble(Cursor cursor, Column column) {
@@ -197,7 +201,9 @@ public static double getDouble(Cursor cursor, Column column) {
}
public static double getDouble(Cursor cursor, String column) {
- return cursor.getDouble(cursor.getColumnIndex(column));
+ int column_index = cursor.getColumnIndex(column);
+ if(column_index < 0) column_index = 0;
+ return cursor.getDouble(column_index);
}
private static class SceneDataBaseHelper extends SQLiteOpenHelper {
diff --git a/ScenePlayer/src/org/puredata/android/scenes/ScenePlayer.java b/ScenePlayer/src/org/puredata/android/scenes/ScenePlayer.java
index d9751b11..643e58fd 100644
--- a/ScenePlayer/src/org/puredata/android/scenes/ScenePlayer.java
+++ b/ScenePlayer/src/org/puredata/android/scenes/ScenePlayer.java
@@ -398,16 +398,18 @@ private void stopRecording() {
if (recFile == null) return;
PdBase.sendMessage(TRANSPORT, "record", 0);
long duration = System.currentTimeMillis() - recStart;
- double longitude = 0.0;
- double latitude = 0.0;
- LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- if (locationManager != null) { // Paranoid? Maybe...
- Location location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
- if (location != null) {
- longitude = location.getLongitude();
- latitude = location.getLatitude();
- }
- }
+ double longitude = 0.0;
+ double latitude = 0.0;
+ try {
+ LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ if (locationManager != null) { // Paranoid? Maybe...
+ Location location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+ if (location != null) {
+ longitude = location.getLongitude();
+ latitude = location.getLatitude();
+ }
+ }
+ } catch(SecurityException e) {} // do nothing
db.addRecording(recFile, recStart, duration, longitude, latitude, sceneId);
recFile = null;
post("Finished recording");
From 4c74215f1042a9166afaf527346123064a71ab95 Mon Sep 17 00:00:00 2001
From: Antoine Rousseau <_antoine_@metalu.net>
Date: Thu, 25 Sep 2025 22:36:02 +0200
Subject: [PATCH 11/11] upgrade ndkVersion from 27.2.12479018 to 28.2.13676358
in order to align to 16KB pagesize, as required by google
---
PdCore/build.gradle | 2 +-
build.gradle | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/PdCore/build.gradle b/PdCore/build.gradle
index 2efef44d..3b7183f4 100644
--- a/PdCore/build.gradle
+++ b/PdCore/build.gradle
@@ -41,5 +41,5 @@ ext {
minSdkVersion = 28
compileSdkVersion = 34
androidxLegacySupportVersion = '1.0.0'
- ndkVersion = "27.2.12479018" // https://developer.android.com/ndk/downloads#lts-downloads
+ ndkVersion = "28.2.13676358" // https://developer.android.com/ndk/downloads#lts-downloads
}
diff --git a/build.gradle b/build.gradle
index 9a0a5fca..d86ed405 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,6 +19,6 @@ ext {
minSdkVersion = 28
compileSdkVersion = 34
androidxLegacySupportVersion = '1.0.0'
- ndkVersion = "27.2.12479018" // https://developer.android.com/ndk/downloads#lts-downloads
+ ndkVersion = "28.2.13676358" // https://developer.android.com/ndk/downloads#lts-downloads
pdCoreVersion = '1.3.0' // Must match version in PdCore/build.gradle
}