Skip to content

Commit 4efa179

Browse files
committed
a bit more CoreMidi experiment
1 parent e6cfab3 commit 4efa179

File tree

3 files changed

+35
-35
lines changed

3 files changed

+35
-35
lines changed

ktmidi/src/appleMain/kotlin/dev/atsushieno/ktmidi/CoreMidiAccess.kt

+24-32
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@ package dev.atsushieno.ktmidi
22

33
import kotlinx.cinterop.*
44
import platform.CoreFoundation.CFStringRef
5-
import platform.CoreFoundation.CFStringRefVar
65
import platform.CoreMIDI.*
7-
import platform.Foundation.CFBridgingRelease
8-
import platform.Foundation.CFBridgingRetain
96
import platform.darwin.SInt32Var
107
import platform.posix.alloca
118

129
class CoreMidiAccess : MidiAccess() {
1310
override val name = "CoreMIDI"
1411
override val inputs: Iterable<MidiPortDetails>
15-
get() = (0UL until MIDIGetNumberOfSources()).map { CoreMidiPortDetails(MIDIGetSource(it)) }
12+
get() = (0UL until MIDIGetNumberOfSources()).map { MIDIGetSource(it) }.filter { it != 0u }.map { CoreMidiPortDetails(it) }
1613

1714
override val outputs: Iterable<MidiPortDetails>
18-
get() = (0UL until MIDIGetNumberOfDestinations()).map { CoreMidiPortDetails(MIDIGetDestination(it)) }
15+
get() = (0UL until MIDIGetNumberOfDestinations()).map { MIDIGetDestination(it) }.filter { it != 0u }.map { CoreMidiPortDetails(it) }
1916

2017
override suspend fun openInput(portId: String): MidiInput = CoreMidiInput(inputs.first { it.id == portId } as CoreMidiPortDetails)
2118

@@ -26,15 +23,16 @@ private class CoreMidiPortDetails(val endpoint: MIDIEndpointRef)
2623
: MidiPortDetails {
2724

2825
@OptIn(ExperimentalForeignApi::class)
29-
override val id: String = getPropertyString(kMIDIPropertyUniqueID) ?: endpoint.toInt().toString()
26+
override val id: String
27+
get() = getPropertyInt(kMIDIPropertyUniqueID).toString()
3028

3129
@OptIn(ExperimentalForeignApi::class)
3230
private fun getPropertyString(property: CFStringRef?): String? = memScoped {
33-
viaPtr { str ->
31+
viaPtr<CFStringRef> { str ->
3432
val status = MIDIObjectGetStringProperty(endpoint, property, str)
3533
if (status == 0 || str.rawValue == NativePtr.NULL)
3634
return@memScoped null
37-
}?.getString()
35+
}?.releaseString()
3836
}
3937

4038
@OptIn(ExperimentalForeignApi::class)
@@ -47,13 +45,17 @@ private class CoreMidiPortDetails(val endpoint: MIDIEndpointRef)
4745
}
4846

4947
@OptIn(ExperimentalForeignApi::class)
50-
override val manufacturer = getPropertyString(kMIDIPropertyManufacturer)
48+
override val manufacturer
49+
get() = getPropertyString(kMIDIPropertyManufacturer)
5150
@OptIn(ExperimentalForeignApi::class)
52-
override val name = getPropertyString(kMIDIPropertyDisplayName) ?: getPropertyString(kMIDIPropertyName) ?: "(unnamed port)"
51+
override val name
52+
get() = getPropertyString(kMIDIPropertyDisplayName) ?: getPropertyString(kMIDIPropertyName) ?: "(unnamed port)"
5353
@OptIn(ExperimentalForeignApi::class)
54-
override val version = getPropertyString(kMIDIPropertyDriverVersion)
54+
override val version
55+
get() = getPropertyString(kMIDIPropertyDriverVersion)
5556
@OptIn(ExperimentalForeignApi::class)
56-
override val midiTransportProtocol = getPropertyInt(kMIDIPropertyProtocolID)
57+
override val midiTransportProtocol
58+
get() = getPropertyInt(kMIDIPropertyProtocolID)
5759
}
5860

5961
private abstract class CoreMidiPort(override val details: CoreMidiPortDetails) : MidiPort {
@@ -85,16 +87,11 @@ private class CoreMidiInput(details: CoreMidiPortDetails) : CoreMidiPort(details
8587
init {
8688
memScoped {
8789
val clientName = "KTMidiInputClient"
88-
clientName.usePinned { clientNameBuf ->
89-
val clientPtr = alloc<MIDIClientRefVar>()
90-
MIDIClientCreate(clientNameBuf.addressOf(0).reinterpret(), null, null, clientPtr.reinterpret())
91-
client = clientPtr.value
92-
90+
client = viaPtr { clientPtr: CPointer<MIDIClientRefVar> ->
91+
MIDIClientCreate(clientName.toCFStringRef(), null, null, clientPtr)
9392
val portName = "KTMidiInputPort"
94-
portName.usePinned { portNameBuf ->
95-
val portPtr = alloc<MIDIPortRefVar>()
96-
MIDIInputPortCreate(client, portNameBuf.addressOf(0).reinterpret(), null, null, portPtr.reinterpret())
97-
port = portPtr.value
93+
port = viaPtr { portPtr: CPointer<MIDIPortRefVar> ->
94+
MIDIInputPortCreate(client, portName.toCFStringRef(), null, null, portPtr)
9895
}
9996
}
10097
}
@@ -121,17 +118,12 @@ private class CoreMidiOutput(details: CoreMidiPortDetails) : CoreMidiPort(detail
121118

122119
init {
123120
memScoped {
124-
val clientName = "KTMidiInputClient"
125-
clientName.usePinned { clientNameBuf ->
126-
val clientPtr = alloc<MIDIClientRefVar>()
127-
MIDIClientCreate(clientNameBuf.addressOf(0).reinterpret(), null, null, clientPtr.reinterpret())
128-
client = clientPtr.value
129-
130-
val portName = "KTMidiInputPort"
131-
portName.usePinned { portNameBuf ->
132-
val portPtr = alloc<MIDIPortRefVar>()
133-
MIDIInputPortCreate(client, portNameBuf.addressOf(0).reinterpret(), null, null, portPtr.reinterpret())
134-
port = portPtr.value
121+
val clientName = "KTMidiOutputClient"
122+
client = viaPtr { clientPtr: CPointer<MIDIClientRefVar> ->
123+
MIDIClientCreate(clientName.toCFStringRef(), null, null, clientPtr)
124+
val portName = "KTMidiOutputPort"
125+
port = viaPtr { portPtr: CPointer<MIDIPortRefVar> ->
126+
MIDIOutputPortCreate(client, portName.toCFStringRef(), portPtr)
135127
}
136128
}
137129
}

ktmidi/src/appleMain/kotlin/dev/atsushieno/ktmidi/Utility.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import platform.Foundation.CFBridgingRetain
88
import platform.Foundation.NSString
99
import platform.darwin.NSObject
1010

11+
// a lot of ideas are taken from r4zzz4k/kmidi so far (it does not seem to work yet though)
1112
@Suppress("CAST_NEVER_SUCCEEDS")
1213
fun String.asNSString() = this as NSString
1314
@Suppress("CAST_NEVER_SUCCEEDS")
@@ -33,7 +34,6 @@ fun NSString.toCFStringRef(): CFStringRef = this.toCFTypeRef()
3334
@OptIn(ExperimentalForeignApi::class)
3435
fun String.toCFStringRef() = asNSString().toCFStringRef()
3536

36-
3737
@OptIn(ExperimentalForeignApi::class)
3838
inline fun <reified T: CVariable> viaPtrVar(block: (ptr: CPointer<T>) -> Unit): T = memScoped {
3939
val result = alloc<T>()
@@ -42,3 +42,7 @@ inline fun <reified T: CVariable> viaPtrVar(block: (ptr: CPointer<T>) -> Unit):
4242
}
4343
@OptIn(ExperimentalForeignApi::class)
4444
inline fun <T : CPointer<*>> viaPtr(block: (CPointer<CPointerVarOf<T>>) -> Unit): T? = viaPtrVar(block).value
45+
@OptIn(ExperimentalForeignApi::class)
46+
inline fun viaPtr(block: (CPointer<IntVarOf<Int>>) -> Unit): Int = viaPtrVar(block).value
47+
@OptIn(ExperimentalForeignApi::class)
48+
inline fun viaPtr(block: (CPointer<UIntVarOf<UInt>>) -> Unit): UInt = viaPtrVar(block).value

player-sample/src/commonMain/kotlin/dev/atsushieno/ktmidi/samples/playersample/PlayerSample.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,19 @@ suspend fun runMain(args: Array<String>) {
3838

3939
// If -2 is specified, then the MidiOutput will receive UMPs. Otherwise, UMPs are converted to MIDI1.
4040
val access = getMidiAccessApi(opts.api, protocol)
41-
val portDetails = access.outputs.firstOrNull { it.id == opts.port }
41+
val portDetails = access.outputs.firstOrNull {
42+
it.id == opts.port
43+
}
4244
?: access.outputs.firstOrNull { !it.name!!.contains("Through") }
4345
?: access.outputs.firstOrNull()
4446
if (portDetails == null) {
4547
println("Could not connect to output device.")
4648
exitApplication(2)
49+
return
4750
}
4851

4952
lateinit var player: MidiPlayer
50-
val midiOutput = access.openOutput(portDetails!!.id)
53+
val midiOutput = access.openOutput(portDetails.id)
5154

5255
// load music from file
5356
if (opts.musicFile != null) {
@@ -79,6 +82,7 @@ suspend fun runMain(args: Array<String>) {
7982
println("Music file was not specified.")
8083
showUsage(opts.api, protocol)
8184
exitApplication(1)
85+
return
8286
}
8387

8488
println("Using ${portDetails.name}")

0 commit comments

Comments
 (0)