From 238e131d408b59aac4f81daa672d95cd734e36c6 Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Thu, 7 Dec 2023 16:35:51 +0900 Subject: [PATCH] switch to rtmidi-javacpp from rtmidi-jna. JavaCPP is a maintained native-interop technology that we can resort to in 2023. Due to weird build breakage on Maven package solution, rtmidi-javacpp is submoduled and referenced as a local project module. (I guess it is equivalent to https://github.com/atsushieno/aap-core/issues/174) --- .gitmodules | 3 + build.gradle | 2 + external/rtmidi-javacpp | 1 + gradle/libs.versions.toml | 12 ++-- input-sample/build.gradle | 6 +- ktmidi-jvm-desktop/build.gradle | 17 ++--- .../dev/atsushieno/ktmidi/RtMidiAccess.kt | 62 ++++++++----------- .../dev/atsushieno/ktmidi/RtMidiAccessTest.kt | 2 +- publish-root.gradle | 4 +- settings.gradle | 4 ++ 10 files changed, 52 insertions(+), 61 deletions(-) create mode 160000 external/rtmidi-javacpp diff --git a/.gitmodules b/.gitmodules index 68a1a6d1b..4fa70a669 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external/rtmidi"] path = external/rtmidi url = https://github.com/thestk/rtmidi.git +[submodule "external/rtmidi-javacpp"] + path = external/rtmidi-javacpp + url = https://github.com/atsushieno/rtmidi-javacpp.git diff --git a/build.gradle b/build.gradle index 53d4e80dc..579b73d0a 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,8 @@ plugins { id "org.jetbrains.kotlin.jvm" version "1.8.22" apply false id "com.android.library" version "7.4.2" apply false id "org.jetbrains.dokka" version "1.8.20" apply false + id 'org.bytedeco.gradle-javacpp-build' version "1.5.9" apply false + id 'org.bytedeco.gradle-javacpp-platform' version '1.5.9' apply false } allprojects { diff --git a/external/rtmidi-javacpp b/external/rtmidi-javacpp new file mode 160000 index 000000000..2477210ed --- /dev/null +++ b/external/rtmidi-javacpp @@ -0,0 +1 @@ +Subproject commit 2477210eda45c5490d28d588563a912c5899dc5f diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d3346897b..99eb8a0a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,29 +1,29 @@ [versions] alsakt = "0.3.4" core-ktx = "1.10.1" -jna = "5.12.1" -jnaerator-runtime = "0.12" junit = "4.13.2" +junit-jupiter-api = "5.9.0" kotlin-test-junit = "1.9.0" kotlinx-coroutines-core = "1.7.0" kotlinx-datetime = "0.4.0" ktor-io = "2.3.2" metalava-gradle = "0.2.3" -rtmidi-jna = "0.1.3" +rtmidi-javacpp = "0.1.2" [libraries] alsakt = { module = "dev.atsushieno:alsakt", version.ref = "alsakt" } core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" } -jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } -jnaerator-runtime = { module = "com.nativelibs4java:jnaerator-runtime", version.ref = "jnaerator-runtime" } junit = { module = "junit:junit", version.ref = "junit" } +junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter-api" } +junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter-api" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin-test-junit" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-core" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor-io" } metalava-gradle = { module = "me.tylerbwong.gradle:metalava-gradle", version.ref = "metalava-gradle" } -rtmidi-jna = { module = "dev.atsushieno:rtmidi-jna", version.ref = "rtmidi-jna" } +rtmidi-javacpp = { module = "dev.atsushieno:rtmidi-javacpp", version.ref = "rtmidi-javacpp" } +rtmidi-javacpp-platform = { module = "dev.atsushieno:rtmidi-javacpp-platform", version.ref = "rtmidi-javacpp" } [plugins] diff --git a/input-sample/build.gradle b/input-sample/build.gradle index ad28c3444..d21ce6971 100644 --- a/input-sample/build.gradle +++ b/input-sample/build.gradle @@ -4,11 +4,11 @@ plugins { } dependencies { - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" + implementation libs.kotlinx.coroutines.core implementation project(":ktmidi") implementation project(":ktmidi-jvm-desktop") - testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.0" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.0" + testImplementation libs.junit.jupiter.api + testRuntimeOnly libs.junit.jupiter.engine } mainClassName = "dev.atsushieno.ktmidi.samples.inputsample.InputSampleKt" diff --git a/ktmidi-jvm-desktop/build.gradle b/ktmidi-jvm-desktop/build.gradle index 826b30a68..360bb496c 100644 --- a/ktmidi-jvm-desktop/build.gradle +++ b/ktmidi-jvm-desktop/build.gradle @@ -2,14 +2,15 @@ plugins { id 'org.jetbrains.kotlin.jvm' id 'maven-publish' id 'signing' + id "org.bytedeco.gradle-javacpp-platform" version "1.5.9" } dependencies { implementation project(":ktmidi") implementation libs.alsakt - implementation libs.rtmidi.jna - implementation libs.jna - implementation libs.jnaerator.runtime + //implementation libs.rtmidi.javacpp + //implementation libs.rtmidi.javacpp.platform + implementation project(":rtmidi-javacpp") implementation libs.kotlinx.coroutines.core implementation libs.kotlinx.datetime @@ -80,16 +81,6 @@ afterEvaluate { } repositories { - /* - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/atsushieno/ktmidi") - credentials { - username = project.findProperty("gpr.user") ?: System.getenv("USERNAME") - password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN") - } - } - */ maven { name = "OSSRH" url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") diff --git a/ktmidi-jvm-desktop/src/main/kotlin/dev/atsushieno/ktmidi/RtMidiAccess.kt b/ktmidi-jvm-desktop/src/main/kotlin/dev/atsushieno/ktmidi/RtMidiAccess.kt index 50f3dabb2..277fc0e80 100644 --- a/ktmidi-jvm-desktop/src/main/kotlin/dev/atsushieno/ktmidi/RtMidiAccess.kt +++ b/ktmidi-jvm-desktop/src/main/kotlin/dev/atsushieno/ktmidi/RtMidiAccess.kt @@ -1,19 +1,18 @@ package dev.atsushieno.ktmidi +import dev.atsushieno.rtmidi_javacpp.RtMidiCCallback +import org.bytedeco.javacpp.BytePointer import java.nio.ByteBuffer import java.nio.IntBuffer -import com.ochafik.lang.jnaerator.runtime.NativeSize -import com.sun.jna.Pointer -import dev.atsushieno.rtmidijna.RtMidiWrapper -import dev.atsushieno.rtmidijna.RtmidiLibrary -import dev.atsushieno.rtmidijna.RtmidiLibrary.RtMidiCCallback -import kotlinx.coroutines.yield +import org.bytedeco.javacpp.IntPointer +import org.bytedeco.javacpp.Pointer + +import dev.atsushieno.rtmidi_javacpp.global.RtMidi as library class RtMidiAccess() : MidiAccess() { companion object { - private val library = RtmidiLibrary.INSTANCE - internal fun getPortName(rtmidi: RtMidiWrapper, index: Int) : String { + internal fun getPortName(rtmidi: Pointer, index: Int) : String { val lenArr = intArrayOf(0) val len = IntBuffer.wrap(lenArr) if (library.rtmidi_get_port_name(rtmidi, index, null, len) < 0) @@ -58,8 +57,8 @@ class RtMidiAccess() : MidiAccess() { override val canCreateVirtualPort: Boolean get() = when(library.rtmidi_out_get_current_api(library.rtmidi_out_create_default())) { - RtmidiLibrary.RtMidiApi.RTMIDI_API_LINUX_ALSA, - RtmidiLibrary.RtMidiApi.RTMIDI_API_MACOSX_CORE -> true + library.RTMIDI_API_LINUX_ALSA, + library.RTMIDI_API_MACOSX_CORE -> true else -> false } @@ -81,21 +80,23 @@ class RtMidiAccess() : MidiAccess() { abstract override fun close() } - internal class RtMidiInputHandler(private val rtmidi: RtMidiWrapper) { + internal class RtMidiInputHandler(rtmidi: Pointer/*RtMidiInPtr*/) { private var listener: OnMidiReceivedEventListener? = null fun setMessageReceivedListener(listener: OnMidiReceivedEventListener) { this.listener = listener } - private fun onRtMidiMessage(timestamp: Double, message: Pointer, messageSize: NativeSize) { - listener?.onEventReceived(message.getByteArray(0, messageSize.toInt()), 0, messageSize.toInt(), (timestamp * 1_000_000_000).toLong()) + private fun onRtMidiMessage(timestamp: Double, message: Pointer, messageSize: Long) { + val array = ByteArray(messageSize.toInt()) + message.asByteBuffer().get(array) + listener?.onEventReceived(array, 0, messageSize.toInt(), (timestamp * 1_000_000_000).toLong()) } - class RtMidiAccessInputCallback(val owner: RtMidiInputHandler) : RtMidiCCallback { + class RtMidiAccessInputCallback(val owner: RtMidiInputHandler) : RtMidiCCallback() { - override fun apply(timeStamp: Double, message: Pointer?, messageSize: NativeSize?, userData: Pointer?) { - owner.onRtMidiMessage(timeStamp, message!!, messageSize!!) + override fun call(timeStamp: Double, message: BytePointer?, messageSize: Long, userData: Pointer?) { + owner.onRtMidiMessage(timeStamp, message!!, messageSize) } } @@ -104,12 +105,12 @@ class RtMidiAccess() : MidiAccess() { init { library.rtmidi_in_set_callback(rtmidi, callback, - Pointer.NULL) + IntPointer()) library.rtmidi_in_ignore_types( rtmidi, - 0, - 1, - 1, + false, + true, + true, ) } } @@ -160,30 +161,19 @@ class RtMidiAccess() : MidiAccess() { // Virtual ports internal class RtMidiVirtualPortDetails(context: PortCreatorContext) : MidiPortDetails { - override val id: String - override val manufacturer: String - override val name: String - override val version: String - - init { - id = context.portName - name = context.portName - manufacturer = context.manufacturer - version = context.version - } + override val id: String = context.portName + override val manufacturer: String = context.manufacturer + override val name: String = context.portName + override val version: String = context.version } internal abstract class RtMidiVirtualPort(context: PortCreatorContext) : MidiPort { - private val detailsImpl: MidiPortDetails + private val detailsImpl: MidiPortDetails = RtMidiVirtualPortDetails(context) override val details: MidiPortDetails get() = detailsImpl override var midiProtocol: Int = 0 - - init { - detailsImpl = RtMidiVirtualPortDetails(context) - } } internal class RtMidiVirtualInput(context: PortCreatorContext) : MidiInput, RtMidiVirtualPort(context) { diff --git a/ktmidi-jvm-desktop/src/test/java/dev/atsushieno/ktmidi/RtMidiAccessTest.kt b/ktmidi-jvm-desktop/src/test/java/dev/atsushieno/ktmidi/RtMidiAccessTest.kt index b6477a2c8..cc8ae2abf 100644 --- a/ktmidi-jvm-desktop/src/test/java/dev/atsushieno/ktmidi/RtMidiAccessTest.kt +++ b/ktmidi-jvm-desktop/src/test/java/dev/atsushieno/ktmidi/RtMidiAccessTest.kt @@ -13,7 +13,7 @@ class RtMidiAccessTest { println("Test rtMidiAccessInfo() is skipped on GitHub Actions as ALSA is unavailable.") return } - // it does not work on M1 mac and arm64 Linux either + // it does not work on M1 mac and arm64 Linux either (lack of deps) if (System.getProperty("os.arch") == "aarch64") { println("Test rtMidiAccessInfo() is skipped as rtmidi-jna aarch64 builds are not available.") return diff --git a/publish-root.gradle b/publish-root.gradle index 2e9c6d857..be183f2e7 100644 --- a/publish-root.gradle +++ b/publish-root.gradle @@ -2,7 +2,7 @@ // Create variables with empty default values ext["ossrhUsername"] = '' ext["ossrhPassword"] = '' -ext["sonatypeStagingProfileId"] = '' +ext["ossrhStagingRepositoryId"] = '' ext["signing.keyId"] = '' ext["signing.password"] = '' ext["signing.secretKeyRingFile"] = '' @@ -17,7 +17,7 @@ if (secretPropsFile.exists()) { // Use system environment variables ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') ?: ext["ossrhUsername"] ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') ?: ext["ossrhPassword"] -ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') ?: ext["sonatypeStagingProfileId"] +ext["ossrhStagingRepositoryId"] = System.getenv('OSSRH_STAGING_REPOSITORY_ID') ?: ext["ossrhStagingRepositoryId"] ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') ?: ext["signing.keyId"] ext["signing.password"] = System.getenv('SIGNING_PASSWORD') ?: ext["signing.password"] ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') ?: ext["signing.secretKeyRingFile"] diff --git a/settings.gradle b/settings.gradle index 8df58a6eb..54ab2e8b3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,3 +22,7 @@ include(":input-sample") include(":player-sample-lib") include(":player-sample") include(":player-sample-native") + +include(":rtmidi-javacpp") +project(":rtmidi-javacpp").projectDir = new File("external/rtmidi-javacpp/rtmidi-javacpp") +gradle.rootProject { ext.javacppVersion = '1.5.8' }