Files
vst-ui-base/src/jsMain/kotlin/nl/astraeus/vst/midi/Midi.kt
rnentjes 770607d5e6 Release version 2.1.0 and add MIDI broadcasting and handling
Updated `build.gradle.kts` to finalize version 2.1.0. Introduced `Broadcaster` and `Midi` classes for MIDI message broadcasting, synchronization, and handling. Added support for MIDI input/output devices with state management and message processing capabilities.
2025-06-06 19:55:44 +02:00

161 lines
3.3 KiB
Kotlin

package nl.astraeus.vst.midi
import kotlinx.browser.window
import org.khronos.webgl.Uint8Array
import org.khronos.webgl.get
external class MIDIInput {
val connection: String
val id: String
val manufacturer: String
val name: String
val state: String
val type: String
val version: String
var onmidimessage: (dynamic) -> Unit
var onstatechange: (dynamic) -> Unit
fun open()
fun close()
}
external class MIDIOutput {
val connection: String
val id: String
val manufacturer: String
val name: String
val state: String
val type: String
val version: String
fun send(message: dynamic, timestamp: dynamic)
fun open()
fun close()
}
object Midi {
var outputChannel: Int = -1
var inputs = mutableListOf<MIDIInput>()
var outputs = mutableListOf<MIDIOutput>()
var currentInput: MIDIInput? = null
var currentOutput: MIDIOutput? = null
fun start(
onUpdate: () -> Unit,
) {
val navigator = window.navigator.asDynamic()
navigator.requestMIDIAccess().then(
{ midiAccess ->
val inp = midiAccess.inputs
val outp = midiAccess.outputs
console.log("Midi inputs:", inputs)
console.log("Midi outputs:", outputs)
inp.forEach() { input ->
console.log("Midi input:", input)
inputs.add(input)
console.log("Name: ${(input as? MIDIInput)?.name}")
}
outp.forEach() { output ->
console.log("Midi output:", output)
outputs.add(output)
}
onUpdate()
},
{ e ->
println("Failed to get MIDI access - $e")
}
)
}
fun setInput(
id: String,
name: String = "",
onMidiInput: (data: Uint8Array) -> Unit
) {
var selected = inputs.find { it.id == id }
if (selected == null) {
var maxMatchChar = 0
inputs.forEach {
val matchChars = matchChars(it.name, name)
if (matchChars > maxMatchChar) {
selected = it
maxMatchChar = matchChars
}
}
}
setInput(selected, onMidiInput)
}
private fun matchChars(str1: String, str2: String): Int {
var result = 0
if (str1.length > str2.length) {
for (ch in str1.toCharArray()) {
if (str2.contains(ch)) {
result++
}
}
} else {
for (ch in str2.toCharArray()) {
if (str1.contains(ch)) {
result++
}
}
}
return result
}
fun setInput(
input: MIDIInput?,
onMidiInput: (data: Uint8Array) -> Unit,
) {
console.log("Setting input", input)
currentInput?.close()
currentInput = input
currentInput?.onstatechange = { message ->
console.log("State change:", message)
}
currentInput?.onmidimessage = { message ->
val data = message.data as Uint8Array
val hex = StringBuilder()
for (index in 0 until data.length) {
hex.append(data[index].toString(16))
hex.append(" ")
}
console.log("Midi message:", hex)
onMidiInput(message.data)
}
currentInput?.open()
}
fun setOutput(
output: MIDIOutput?
) {
console.log("Setting output", output)
currentOutput?.close()
currentOutput = output
currentOutput?.open()
}
fun send(
data: Uint8Array,
timestamp: dynamic = null
) {
currentOutput?.send(data, timestamp)
}
}