Refactor MIDI handling and update dependencies.

Streamlined MIDI message handling by introducing `MidiMessageHandler` and removed redundant code. Added better handler support for specific message types and parameters. Also upgraded Kotlin to version 2.1.0 and adjusted build configurations.
This commit is contained in:
2024-12-21 20:42:19 +01:00
parent fbba6d1422
commit d58fb9c7b5
7 changed files with 117 additions and 167 deletions

View File

@@ -35,7 +35,6 @@ kotlin {
}
}
}
jvm()
sourceSets {
val commonMain by getting {

View File

@@ -8,6 +8,7 @@ import nl.astraeus.tba.SlicedByteArray
import nl.astraeus.vst.ADSR
import nl.astraeus.vst.AudioWorkletProcessor
import nl.astraeus.vst.currentTime
import nl.astraeus.vst.midi.MidiMessageHandler
import nl.astraeus.vst.registerProcessor
import nl.astraeus.vst.sampleRate
import org.khronos.webgl.Float32Array
@@ -65,8 +66,8 @@ enum class RecordingState {
@ExperimentalJsExport
@JsExport
class VstChipProcessor : AudioWorkletProcessor() {
var midiChannel = 0
val midiMessageBuffer = SortedTimedMidiMessageList()
val midiMessageHandler = MidiMessageHandler()
val notes = Array<PlayingNote?>(POLYPHONICS) { null }
var waveform = Waveform.SINE.ordinal
@@ -100,6 +101,82 @@ class VstChipProcessor : AudioWorkletProcessor() {
init {
this.port.onmessage = ::handleMessage
Note.updateSampleRate(sampleRate)
with(midiMessageHandler) {
addHandler(0x90) { b1, b2, b3 ->
val note = b2.toInt() and 0xff
val velocity = b3.toInt() and 0xff
if (velocity > 0) {
console.log("Note on", note, velocity)
noteOn(note, velocity)
} else {
console.log("Note off", note)
noteOff(note)
}
}
addHandler(0x80) { b1, b2, b3 ->
val note = b2.toInt() and 0xff
console.log("Note off", note)
noteOff(note)
}
addHandler(0xc9) { b1, b2, b3 ->
waveform = b2.toInt() and 0xff
}
addHandler(0xb0, 7) { b1, b2, b3 ->
volume = b3 / 127f
}
addHandler(0xb0, 0x47) { b1, b2, b3 ->
dutyCycle = b3 / 127.0
}
addHandler(0xb0, 0x40) { b1, b2, b3 ->
fmFreq = b3 / 127.0
}
addHandler(0xb0, 0x41) { b1, b2, b3 ->
fmAmp = b3 / 127.0
}
addHandler(0xb0, 0x42) { b1, b2, b3 ->
amFreq = b3 / 127.0
}
addHandler(0xb0, 0x43) { b1, b2, b3 ->
amAmp = b3 / 127.0
}
addHandler(0xb0, 0x49) { b1, b2, b3 ->
attack = b3 / 127.0
}
addHandler(0xb0, 0x4b) { b1, b2, b3 ->
decay = b3 / 127.0
}
addHandler(0xb0, 0x46) { b1, b2, b3 ->
sustain = b3 / 127.0
}
addHandler(0xb0, 0x48) { b1, b2, b3 ->
release = b3 / 127.0
}
addHandler(0xb0, 0x4e) { b1, b2, b3 ->
delay = b3 / 127.0
}
addHandler(0xb0, 0x4f) { b1, b2, b3 ->
delayDepth = b3 / 127.0
}
addHandler(0xb0, 0x50) { b1, b2, b3 ->
feedback = b3 / 127.0
}
addHandler(0xb0, 123) { b1, b2, b3 ->
for (note in notes) {
note?.noteRelease = currentTime
}
}
addHandler(0xe0) { b1, b2, b3 ->
if (b2.toInt() and 0xff > 0) {
val lsb = b2.toInt() and 0xff
val msb = b3.toInt() and 0xff
amFreq = (((msb - 0x40) + (lsb / 127.0)) / 0x40) * 10.0
}
}
}
}
private fun handleMessage(message: MessageEvent) {
@@ -122,8 +199,9 @@ class VstChipProcessor : AudioWorkletProcessor() {
data.startsWith("set_channel") -> {
val parts = data.split('\n')
if (parts.size == 2) {
midiChannel = parts[1].toInt()
println("Setting channel: $midiChannel")
midiMessageHandler.channel = parts[1].toByte()
println("Setting channel: ${midiMessageHandler.channel}")
}
}
@@ -166,8 +244,9 @@ class VstChipProcessor : AudioWorkletProcessor() {
}
private fun playBuffer() {
while (midiMessageBuffer.isNotEmpty() && (midiMessageBuffer.nextTimestamp()
?: 0.0) < currentTime
while (
midiMessageBuffer.isNotEmpty() &&
(midiMessageBuffer.nextTimestamp() ?: 0.0) < currentTime
) {
val midi = midiMessageBuffer.read()
console.log("Message", currentTime, midi)
@@ -178,163 +257,16 @@ class VstChipProcessor : AudioWorkletProcessor() {
private fun playMidi(bytes: SlicedByteArray) {
var index = 0
console.log(
"--playMidi",
bytes.size,
index,
bytes[index + 0],
bytes[index + 1],
bytes[index + 2]
)
while (index < bytes.size && bytes[index].toUByte() > 0u) {
console.log("playMidi", bytes, index, bytes[index + 0], bytes[index + 1], bytes[index + 2])
index += playMidiFromBuffer(bytes, index)
val buffer = bytes.getBlob(index, 3)
playMidiFromBuffer(buffer)
index += 3
}
}
private fun playMidiFromBuffer(bytes: SlicedByteArray, index: Int): Int {
if (bytes[index] == 0.toByte()) {
return 0
}
if (bytes.length > 0) {
var cmdByte = bytes[0].toInt() and 0xff
val channelCmd = ((cmdByte shr 4) and 0xf) != 0xf0
val channel = cmdByte and 0xf
//println("Channel cmd: $channelCmd")
val byteLength = when (cmdByte) {
0x90, 0xb0, 0xe0 -> 3
0x80, 0xc9 -> 2
else -> throw IllegalArgumentException("Unknown command: $cmdByte")
}
if (channelCmd && channel != midiChannel) {
console.log("Wrong channel", midiChannel, bytes)
return byteLength
}
cmdByte = cmdByte and 0xf0
//console.log("Received", bytes)
when (cmdByte) {
0x90 -> {
if (bytes.length >= 3) {
val note = bytes[1].toInt() and 0xff
val velocity = bytes[2].toInt() and 0xff
if (velocity > 0) {
console.log("Note on", note, velocity)
noteOn(note, velocity)
} else {
console.log("Note off", note)
noteOff(note)
}
}
}
0x80 -> {
if (bytes.length >= 2) {
val note = bytes[1].toInt() and 0xff
console.log("Note off", note)
noteOff(note)
}
}
0xc9 -> {
if (bytes.length >= 1) {
val waveform = bytes[1].toInt() and 0xff
if (waveform < 4) {
this.waveform = waveform
}
}
}
0xb0 -> {
if (bytes.length >= 3) {
val knob = bytes[1].toInt() and 0xff
val value = bytes[2].toInt() and 0xff
when (knob) {
7 -> {
volume = value / 127f
}
0x47 -> {
dutyCycle = value / 127.0
}
0x40 -> {
fmFreq = value / 127.0
}
0x41 -> {
fmAmp = value / 127.0
}
0x42 -> {
amFreq = value / 127.0
}
0x43 -> {
amAmp = value / 127.0
}
0x49 -> {
attack = value / 127.0
}
0x4b -> {
decay = value / 127.0
}
0x46 -> {
sustain = value / 127.0
}
0x48 -> {
release = value / 127.0
}
0x4e -> {
delay = value / 127.0
println("Setting delay $delay")
}
0x4f -> {
delayDepth = value / 127.0
println("Setting delayDepth $delayDepth")
}
0x50 -> {
feedback = value / 127.0
println("Setting feedback $delayDepth")
}
123 -> {
for (note in notes) {
note?.noteRelease = currentTime
}
}
}
}
}
0xe0 -> {
if (bytes.length >= 3) {
val lsb = bytes[1]
val msb = bytes[2]
amFreq = (((msb - 0x40) + (lsb / 127.0)) / 0x40) * 10.0
}
}
}
return byteLength
}
throw IllegalArgumentException("Unable to handle empty byte array")
private fun playMidiFromBuffer(bytes: SlicedByteArray) {
midiMessageHandler.handle(bytes[0], bytes[1], bytes[2])
}
private fun noteOn(note: Int, velocity: Int) {

View File

@@ -12,8 +12,6 @@ import kotlin.math.round
* Time: 11:50
*/
@ExperimentalJsExport
@JsExport
enum class Note(
val sharp: String,
val flat: String