能否仅通过Android原生SDK代码创建并发送MIDI音符数据至原生播放器?
Android原生MIDI播放实现(Kotlin)
权限配置
首先在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.MIDI" /> <!-- 若需连接外部MIDI设备,Android 13+需补充蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:maxSdkVersion="33" />
初始化MIDI合成器
通过系统MidiManager查找并打开内置合成器设备:
import android.media.midi.MidiDevice import android.media.midi.MidiDeviceInfo import android.media.midi.MidiManager import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MidiPlayerActivity : AppCompatActivity() { private var midiDevice: MidiDevice? = null private var outputPort: MidiDevice.MidiOutputPort? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val midiManager = getSystemService(MIDI_SERVICE) as MidiManager // 筛选内置合成器设备 val synthDevice = midiManager.devices.firstOrNull { it.type == MidiDeviceInfo.TYPE_SYNTHESIZER } synthDevice?.let { deviceInfo -> midiManager.openDevice(deviceInfo, object : MidiManager.OnDeviceOpenedListener { override fun onDeviceOpened(device: MidiDevice?) { device?.let { midiDevice = it // 打开第一个输出端口(多数合成器单端口即可满足需求) outputPort = it.openOutputPort(0) // 设备就绪,可开始发送MIDI消息 } } override fun onDeviceOpenFailed(errorCode: Int) { // 处理设备打开失败场景,比如弹出提示告知用户 } }, Handler(Looper.getMainLooper())) } } }
发送音符消息
使用打开的输出端口发送MIDI音符开/关指令:
// 播放指定音符,默认中央C(MIDI编号60)、力度64、时长2秒 fun playNote(note: Int = 60, velocity: Int = 64, duration: Long = 2000) { outputPort?.let { port -> // 音符开启消息:0x90代表通道1的音符开指令 val noteOn = byteArrayOf(0x90.toByte(), note.toByte(), velocity.toByte()) port.send(noteOn, 0, noteOn.size) // 延迟指定时长后发送音符关闭消息 Handler(Looper.getMainLooper()).postDelayed({ val noteOff = byteArrayOf(0x80.toByte(), note.toByte(), 0.toByte()) port.send(noteOff, 0, noteOff.size) }, duration) } }
资源释放
在Activity销毁时务必关闭端口和设备,避免资源泄漏:
override fun onDestroy() { super.onDestroy() outputPort?.close() midiDevice?.close() }
关键注意事项
- 部分设备可能没有内置合成器,此时需提示用户连接外部MIDI设备
- MIDI消息的通道编号为0-15(对应指令高4位,比如0x90是通道1,0x91是通道2)
- 如需播放复杂序列,建议使用独立线程或调度器管理消息发送时序,避免阻塞主线程
内容的提问来源于stack exchange,提问作者TLSO
相关产品推荐
相关产品推荐

