【问题标题】:Android - Play SoundFont with MIDI fileAndroid - 使用 MIDI 文件播放 SoundFont
【发布时间】:2019-06-11 10:09:12
【问题描述】:

我有一个 MIDI 文件,我使用以下代码在 android 中使用 MediaPlayer 播放了该 MIDI 文件:

val mMediaPlayer = MediaPlayer.create(context, R.raw.test_ring_1)

mMediaPlayer?.start()

它默认使用钢琴等一种乐器演奏,现在我想添加 soundfont (sf2/sf3) 文件来播放不同乐器和混响效果的 MIDI 音符。

请指导实现预期结果的方法。

【问题讨论】:

标签: android android-mediaplayer midi synthesizer soundfont


【解决方案1】:

有两个库将用于使用 SoundFont 播放 MIDI 文件。

Midi Driver

只是一个用于在 Android 上播放 MIDI 音符的合成器。您可以将它与 USB/Bluetooth-MIDI 库一起使用来创建您的 MIDI 应用程序。

支持 SoundFont2 文件。

Android MIDI Library

这个库提供了一个读取、操作和写入 MIDI 文件的接口。支持“回放”作为实时事件调度系统。此库不包含实际的音频播放或设备接口。

初始化 SF2-SoundBank

SF2Soundbank sf = new SF2Soundbank(getAssets().open("test.sf2"));
        synth = new SoftSynthesizer();
        synth.open();
        synth.loadAllInstruments(sf);
        synth.getChannels()[0].programChange(0);
        synth.getChannels()[1].programChange(1);
        recv = synth.getReceiver();

从 MIDI 文件播放 Midi 音符

MidiFile midiFile = new MidiFile(getAssets().open("test.mid"));

// Create a new MidiProcessor:
MidiProcessor processor = new MidiProcessor(midiFile);

// listen for all midi events:
processor.registerEventListener(new MidiEventListener() {
    @Override
    public void onStart(boolean fromBeginning) {

    }

    @Override
    public void onEvent(MidiEvent event, long ms) {

        if (event.getClass() == NoteOn.class) {

                NoteOn noteOn = ((NoteOn) event);

                try {
                    ShortMessage msg = new ShortMessage();
                    msg.setMessage(ShortMessage.NOTE_ON, channel, noteOn.getNoteValue(), noteOn.getVelocity());
                    recv.send(msg, ms);
                } catch (InvalidMidiDataException e) {
                    e.printStackTrace();
                }

            } else if (event.getClass() == NoteOff.class) {

                NoteOff noteOff = ((NoteOff) event);

                try {
                    ShortMessage msg = new ShortMessage();
                    msg.setMessage(ShortMessage.NOTE_ON, channel, noteOff.getNoteValue(), noteOff.getVelocity());
                    recv.send(msg, ms);
                } catch (InvalidMidiDataException e) {
                    e.printStackTrace();
                }

            }
    }

    @Override
    public void onStop(boolean finished) {

    }
}, MidiEvent.class);

// Start the processor:
processor.start();

用于定义 SF 通道的变量

private int channel = 0;

【讨论】:

    【解决方案2】:

    我已经测试过它可以工作

        @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            SF2Soundbank sf = new SF2Soundbank(getAssets().open("SmallTimGM6mb.sf2"));
            synth = new SoftSynthesizer();
            synth.open();
            synth.loadAllInstruments(sf);
            synth.getChannels()[0].programChange(0);
            synth.getChannels()[1].programChange(1);
            recv = synth.getReceiver();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (MidiUnavailableException e) {
            e.printStackTrace();
        }
    
    
        this.findViewById(R.id.piano).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = MotionEventCompat.getActionMasked(event);
                if (action == MotionEvent.ACTION_DOWN) {
                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_ON, 0, 60, 127);
                        recv.send(msg, -1);
                    } catch (InvalidMidiDataException e) {
                        e.printStackTrace();
                    }
                } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_OFF, 0, 60, 127);
                        recv.send(msg, -1);
                    } catch (InvalidMidiDataException e) {
                        e.printStackTrace();
                    }
                }
                return true;
            }
        });
    
        this.findViewById(R.id.woodblock).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = MotionEventCompat.getActionMasked(event);
                if (action == MotionEvent.ACTION_DOWN) {
                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_ON, 1, 60, 127);
                        recv.send(msg, -1);
                    } catch (InvalidMidiDataException e) {
                        e.printStackTrace();
                    }
                } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                    try {
                        ShortMessage msg = new ShortMessage();
                        msg.setMessage(ShortMessage.NOTE_OFF, 1, 60, 127);
                        recv.send(msg, -1);
                    } catch (InvalidMidiDataException e) {
                        e.printStackTrace();
                    }
                }
                return true;
            }
        });
    }
    

    不要忘记从下面的存储库中包含 sherlockmidi 库,示例也可以在下面的存储库中找到。

    https://github.com/agangzz/SherlockMidi

    【讨论】:

    • 我想播放一个 MIDI 文件,而不是从用户那里获取输入。有没有办法添加midi文件?
    • 不幸的是,agangzz/SherlockMidi 是 KyoSherlock/MidiDriver 的一个分支,它的许可不正确。在解决此问题之前,我不会冒险使用它:github.com/KyoSherlock/MidiDriver/issues/8
    猜你喜欢
    • 1970-01-01
    • 2016-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多