如何解码Midi数据Android



我正在写一个Android应用程序。MIDI钢琴键盘通过一根电缆连接到Android设备上。我一直遵循官方Android Midi文档在这里https://developer.android.com/reference/android/media/midi/package-summary,但我被困解码原始Midi数据,我收到。

@RequiresApi(api = Build.VERSION_CODES.M)
class MidiFramer extends MidiReceiver {
public void onSend(byte[] data, int offset,
int count, long timestamp) throws IOException {
// parse MIDI or whatever
// How to convert data to something readable? Below doesn't make any sense.
Log.v(LOG_TAG, "onSend strData:" + data +" length:"+data.length);
StringBuffer sb = new StringBuffer();
for (int i=0; i<data.length; i++){
String hex = new String (data, StandardCharsets.UTF_8);
sb.append(hex);
}
Log.v(LOG_TAG, "onSend sb:" + sb.toString());

}
}

从接收到的原始Midi数据来看,我想知道在物理钢琴键盘上演奏的是什么音符(例如D4/c# 5)。如有任何帮助,不胜感激。

这是我的重构解决方案,使用的灵感来自多个来源,已经测试了各种设备和连接:

@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
}
}
}
}

我不确定picth band部分,但每个地方都是不同的,所以如果我得到关于它的bug报告,我会看到它,或者能够测试出来。

张贴一些额外的代码,这样你可以让它很容易地工作…

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)

希望我没有忘记任何东西…

我相信这实际上是一个很好的例子,但需要一些重构:

private fun sendMidi1Immediate(msg: ByteArray, offset: Int, count: Int) {
var off = offset
var c = count
var runningStatus = 0
while (c > 0) {
var stat = msg[off].toUnsigned()
if (stat < 0x80) {
stat = runningStatus
} else {
off++
c--
}
runningStatus = stat
val ch = stat and 0x0F
when (stat and 0xF0) {
0x80 -> syn.noteOff(ch, msg[off].toUnsigned())
0x90 -> {
if (msg[off + 1].toInt() == 0)
syn.noteOff(ch, msg[off].toUnsigned())
else
syn.noteOn(ch, msg[off].toUnsigned(), msg[off + 1].toUnsigned())
}
0xA0 -> {
// No PAf in fluidsynth?
}
0xB0 -> syn.cc(ch, msg[off].toUnsigned(), msg[off + 1].toUnsigned())
0xC0 -> syn.programChange(ch, msg[off].toUnsigned())
0xD0 -> syn.channelPressure(ch, msg[off].toUnsigned())
0xE0 -> syn.pitchBend(ch, msg[off].toUnsigned() + msg[off + 1].toUnsigned() * 0x80)
0xF0 -> {
if (stat == 0xF0) { // sysex
val idx = msg.drop(off).indexOf(0xF7.toByte())
val sysex = msg.copyOfRange(off, off + idx)
syn.sysex(sysex, null)
if (sysex[0] == 0x7E.toByte() && sysex[1] == 0x7F.toByte() && sysex[2] == 0x0D.toByte() &&
sysex[3] == 0x12.toByte() && sysex[4] == 1.toByte()) {
// we don't check the rest (Source MUID / Destination MUID / Authority Level)
// as it is obvious that this MIDI receiver is the ultimate destination.
midiProtocol = (sysex[14] and 3).toUnsigned()
}
}
}
}
when (stat and 0xF0) {
0xC0,0xD0 -> {
off++
c--
}
0xF0 -> {
off += c - 1
c = 0
}
else -> {
off += 2
c -= 2
}
}
}
}

我从https://github.com/atsushieno/fluidsynth-midi-service-j/blob/main/app/src/main/java/dev/atsushieno/fluidsynthmidideviceservicej/FluidsynthMidiReceiver.kt上偷来的,如果在阅读时仍然存在,最好在那里检查一下。我从来不会写这样的代码,但看起来有些人可以这样写,它仍然工作…

最新更新