This is my refactored solution using inspiration from multiple sources already tested with various devices and connections:
@WorkerThread
private fun onSend(data: ByteArray, startOffset: Int, count: Int) {
var offset = startOffset
fun param(index: Int) = data[offset + index].unsignedInt
while (offset < count) {
val command = data[offset] and STATUS_COMMAND_MASK
val channel: Int = data[offset] and STATUS_CHANNEL_MASK
when (command) {
STATUS_NOTE_ON -> {
val velocity = param(2)
if (velocity == 0) noteOff(channel, note = param(1))
else noteOn(channel, note = param(1), velocity = param(2))
offset += 3
}
STATUS_NOTE_OFF -> {
noteOff(channel, note = param(1), velocity = param(2))
offset += 3
}
STATUS_CONTROL_CHANGE -> {
cc(channel, command = param(1), value = param(2))
offset += 3
}
STATUS_PITCH_BEND -> {
pitchBend(channel, bend = (param(2) shl 7) + data[offset + 1])
offset += 3
}
STATUS_PROGRAM_CHANGE -> {
programChange(channel, program = param(1))
offset += 2
}
STATUS_CHANNEL_PRESSURE -> {
channelPressure(channel, program = param(1))
offset += 2
}
STATUS_START -> {
start()
offset += 1
}
STATUS_CONTINUE -> {
midiContinue()
offset += 1
}
STATUS_STOP -> {
stop()
offset += 1
}
STATUS_CLOCK -> {
timing()
offset += 1
}
else -> {
logWarn {
"Unhandled MIDI data.size:${data.size} offset:$offset," +
" count:$count, command:$command}"
}
offset += 1
}
}
}
}
That picth band part I am not sure about, but everywhere it is done differently, so will be seen if I ever get bug report on that, or will able to test it out.
Posting some additional code so you can get it working easyly..
object MidiConstants {
internal const val TAG = "MidiTools"
const val STATUS_COMMAND_MASK = 0xF0
const val STATUS_CHANNEL_MASK = 0x0F
// Channel voice messages.
const val STATUS_NOTE_OFF = 0x80
const val STATUS_NOTE_ON = 0x90
const val STATUS_POLYPHONIC_AFTERTOUCH = 0xA0
const val STATUS_CONTROL_CHANGE = 0xB0
const val STATUS_PROGRAM_CHANGE = 0xC0
const val STATUS_CHANNEL_PRESSURE = 0xD0
const val STATUS_PITCH_BEND = 0xE0
// System Common Messages.
const val STATUS_SYSEX_START = 0xF0
const val STATUS_MIDI_TIME_CODE = 0xF1
const val STATUS_SONG_POSITION = 0xF2
const val STATUS_SONG_SELECT = 0xF3
const val STATUS_TUNE_REQUEST = 0xF6
const val STATUS_SYSEX_END = 0xF7
const val STATUS_RESET = 0xFF //followed by 0xFF and 0x00
// System Real-Time Messages, single byte
const val STATUS_CLOCK = 0xF8 //248
const val STATUS_START = 0xFA //250
const val STATUS_CONTINUE = 0xFB //251
const val STATUS_STOP = 0xFC //252
const val STATUS_ACTIVE_SENSING = 0xFE //254
}
infix fun Byte.and(that: Int): Int = this.toInt().and(that)
Hope I didnt forget anythin...