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

如何在Android中播放MIDI文件单个声部的音符?

解决方案:过滤MIDI声部播放

MediaPlayer组件无法直接控制MIDI文件的声部播放,因为它是高层封装的播放组件,不暴露MIDI事件的精细控制接口。要实现仅播放单个声部的需求,需要解析MIDI文件并单独处理目标声部的音符事件,下面是具体的修改方案:

1. 添加MIDI解析依赖

在项目的app/build.gradle中添加第三方MIDI解析库(简化文件解析逻辑):

dependencies {
    implementation 'com.github.guichaguri:midi-player-android:2.1.0'
}

2. 修改Activity播放逻辑

替换原有的MediaPlayer播放逻辑,改为解析MIDI文件并过滤指定声部的音符:

import android.media.AudioManager
import android.media.SoundPool
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.guichaguri.musicplayer.player.MidiPlayer
import com.guichaguri.musicplayer.player.PlayerListener
import java.io.InputStream

class MidiPlayerActivity : AppCompatActivity() {
    private lateinit var soundPool: SoundPool
    private var midiPlayer: MidiPlayer? = null
    // 指定要播放的声部索引(示例为第0个声部,可根据你的MIDI文件调整)
    private val targetTrackIndex = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_midi_player)

        soundPool = SoundPool(10, AudioManager.STREAM_MUSIC, 0)

        val playBtn = findViewById<Button>(R.id.play_btn)
        val stopBtn = findViewById<Button>(R.id.stop_btn)

        playBtn.setOnClickListener {
            playTargetTrackMidi()
        }

        stopBtn.setOnClickListener {
            stopMidiPlayback()
        }
    }

    private fun playTargetTrackMidi() {
        stopMidiPlayback()

        // 从assets加载MIDI文件
        val inputStream: InputStream = assets.open("your_midi_file.mid")

        midiPlayer = MidiPlayer(this, soundPool).apply {
            // 设置仅播放指定声部
            setTrackEnabled(targetTrackIndex, true)
            // 禁用其他所有声部
            for (i in 0 until trackCount) {
                if (i != targetTrackIndex) setTrackEnabled(i, false)
            }

            setListener(object : PlayerListener {
                override fun onPlayingStart() {}
                override fun onPlayingEnd() {
                    stopMidiPlayback()
                }
                override fun onError(message: String?) {}
            })

            load(inputStream)
            play()
        }
    }

    private fun stopMidiPlayback() {
        midiPlayer?.stop()
        midiPlayer?.release()
        midiPlayer = null
    }

    override fun onDestroy() {
        super.onDestroy()
        stopMidiPlayback()
        soundPool.release()
    }
}

3. 保留原有UI布局

你的原布局代码无需修改,只要确保按钮ID与Activity中的引用一致即可。

关键说明

  • 声部索引从0开始,你可以根据MIDI文件的实际声部结构调整targetTrackIndex的值(比如你的示例图中如果第一个声部是索引0,就用0)
  • 该方案通过MidiPlayer库解析MIDI文件的各个轨道(声部),然后仅启用目标轨道的播放,其他轨道全部禁用,从而实现只播放单个声部的效果
  • 如果你不想用第三方库,也可以自己解析MIDI文件的结构(读取MIDI轨道事件,过滤出目标声部的音符事件,再用SoundPool逐个播放),但第三方库已经封装了复杂的解析和播放逻辑,更高效稳定

内容的提问来源于stack exchange,提问作者macizett

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:29:56