You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否仅通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 05:43:10