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() var outputs = mutableListOf() 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) } }