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 }} 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/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/PdCore/build.gradle b/PdCore/build.gradle index feb45c59..3b7183f4 100644 --- a/PdCore/build.gradle +++ b/PdCore/build.gradle @@ -1,203 +1,45 @@ -plugins { - id 'com.android.library' - id 'signing' - id 'maven-publish' -} - -group = rootProject.group -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 -} - -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'] - 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'] - } - - // 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') - } - - 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') } - } - } - - 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') +buildscript { + repositories { + google() + mavenCentral() } - - libraryVariants.all { variant -> - variant.outputs.all { output -> - outputFileName = "${archivesBaseName}.aar" - } + dependencies { + classpath 'com.android.tools.build:gradle:8.11.0' } } -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 -} - -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 = "28.2.13676358" // 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 70% rename from PdCore/AndroidManifest.xml rename to PdCore/pd-core/AndroidManifest.xml index a647310b..4390c57c 100644 --- a/PdCore/AndroidManifest.xml +++ b/PdCore/pd-core/AndroidManifest.xml @@ -1,4 +1,5 @@ + 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 85% 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 index a28bf667..32aa3d94 100644 --- a/PdCore/src/main/java/org/puredata/android/io/AudioRecordWrapper.java +++ b/PdCore/pd-core/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/pd-core/src/main/java/org/puredata/android/io/AudioWrapper.java similarity index 99% 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 index 2c259457..f5de50c9 100644 --- a/PdCore/src/main/java/org/puredata/android/io/AudioWrapper.java +++ b/PdCore/pd-core/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/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/pd-core/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java b/PdCore/pd-core/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java new file mode 100644 index 00000000..7daa6f3d --- /dev/null +++ b/PdCore/pd-core/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java @@ -0,0 +1,127 @@ +/** + * + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. + * + */ + +package org.puredata.android.midi; + +import org.puredata.core.PdBase; +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 extends MidiReceiver { + private final int port; + 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; + +/** + * 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++]); + } + + private void processByte(int b) { + if (b < 0) { + midiState = State.values()[(b >> 4) & 0x07]; + if (midiState != State.NONE) { + channel = b & 0x0f + 16 * port; + 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; + } + } + } +} diff --git a/PdCore/pd-core/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java b/PdCore/pd-core/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java new file mode 100644 index 00000000..03743c2d --- /dev/null +++ b/PdCore/pd-core/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java @@ -0,0 +1,162 @@ +/** + * + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. + * + */ + +package org.puredata.android.midi; + +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. + * + * @author Peter Brinkmann (peter.brinkmann@gmail.com) + * @author Antoine Rousseau (antoine@metalu.net) + */ +public class PdToMidiAdapter implements PdMidiReceiver { + private Map inputPorts = new HashMap(); + +/** + * 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(port, 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(ch, message); + } + + private void write(int msg, int ch, int a, int b) { + final byte[] message = {firstByte(msg, ch), (byte) a, (byte) b}; + writeMessage(ch, message); + } + + 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() { + } +} 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 97% 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 index db52ca02..18dc6c42 100644 --- a/PdCore/src/main/java/org/puredata/android/service/PdService.java +++ b/PdCore/pd-core/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; @@ -143,6 +142,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); } /** @@ -242,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) 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 81% rename from PdCore/src/main/res/values/strings.xml rename to PdCore/pd-core/src/main/res/values/strings.xml index 2aa223d6..26d9d507 100644 --- a/PdCore/src/main/res/values/strings.xml +++ b/PdCore/pd-core/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/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 85% rename from PdCore/src/main/res/xml/preferences.xml rename to PdCore/pd-core/src/main/res/xml/preferences.xml index 5a0dd393..c33c0714 100644 --- a/PdCore/src/main/res/xml/preferences.xml +++ b/PdCore/pd-core/src/main/res/xml/preferences.xml @@ -11,4 +11,6 @@ + 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/java/org/puredata/android/midi/MidiToPdAdapter.java b/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java deleted file mode 100644 index 7c9662a2..00000000 --- a/PdCore/src/main/java/org/puredata/android/midi/MidiToPdAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * - * For information on usage and redistribution, and for a DISCLAIMER OF ALL - * WARRANTIES, see the file, "LICENSE.txt," in this distribution. - * - */ - -package org.puredata.android.midi; - -import org.puredata.core.PdBase; - -import com.noisepages.nettoyeur.midi.MidiReceiver; - -/** - * Adapter class for connecting output from AndroidMidi to MIDI input for Pd. - * - * @author Peter Brinkmann (peter.brinkmann@gmail.com) - */ -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); - } - - @Override - public void onNoteOn(int channel, int key, int velocity) { - PdBase.sendNoteOn(channel, key, velocity); - } - - @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; - } - - @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 deleted file mode 100644 index 01f7f289..00000000 --- a/PdCore/src/main/java/org/puredata/android/midi/PdToMidiAdapter.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * - * For information on usage and redistribution, and for a DISCLAIMER OF ALL - * WARRANTIES, see the file, "LICENSE.txt," in this distribution. - * - */ - -package org.puredata.android.midi; - -import org.puredata.core.PdMidiReceiver; - -import com.noisepages.nettoyeur.midi.MidiReceiver; - -/** - * Adapter class for connecting MIDI output from Pd to input for AndroidMidi. - * - * @author Peter Brinkmann (peter.brinkmann@gmail.com) - */ -public class PdToMidiAdapter implements PdMidiReceiver { - - 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; - } - - @Override - public void receiveProgramChange(int channel, int value) { - receiver.onProgramChange(channel, value); - } - - @Override - public void receivePolyAftertouch(int channel, int pitch, int value) { - receiver.onPolyAftertouch(channel, pitch, value); - } - - @Override - public void receivePitchBend(int channel, int value) { - receiver.onPitchBend(channel, value); - } - - @Override - public void receiveNoteOn(int channel, int pitch, int velocity) { - receiver.onNoteOn(channel, pitch, velocity); - } - - @Override - public void receiveMidiByte(int port, int value) { - receiver.onRawByte((byte) value); - } - - @Override - public void receiveControlChange(int channel, int controller, int value) { - receiver.onControlChange(channel, controller, value); - } - - @Override - public void receiveAftertouch(int channel, int value) { - receiver.onAftertouch(channel, value); - } - - @Override - public boolean beginBlock() { - return receiver.beginBlock(); - } - - @Override - public void endBlock() { - receiver.endBlock(); - } -} 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/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/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"); 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 { diff --git a/build.gradle b/build.gradle index 159f4ba2..d86ed405 100644 --- a/build.gradle +++ b/build.gradle @@ -4,29 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.0.2' - } -} - -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/')) - } + classpath 'com.android.tools.build:gradle:8.11.0' } } @@ -34,14 +12,13 @@ allprojects { repositories { google() mavenCentral() - jcenter() // FIXME: com.noisepages.nettoyeur:midi } } ext { minSdkVersion = 28 compileSdkVersion = 34 - buildToolsVersion = '34.0.0' androidxLegacySupportVersion = '1.0.0' - ndkVersion = '25.2.9519653' // 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 } 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 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'