DFRobot Leonardo & Xbee R3 实现MIDI播放
MIDI(Musical Instrument Digital Interface)乐器数字接口 ,是20 世纪80 年代初为解决电声乐器之间的通信问题而提出的。MIDI是编曲界最广泛的音乐标准格式,可称为“计算机能理解的乐谱”。它用音符的数字控制信号来记录音乐。一首完整的MIDI音乐只有几十KB大,而能包含数十条音乐轨道。几乎所有的现代音乐都是用MIDI加上音色库来制作合成的。MIDI传输的不是声音信号, 而是音符、控制参数等指令,它指示MIDI 设备要做什么,怎么做, 如演奏哪个音符、多大音量等。它们被统一表示成MIDI消息(MIDIMessage) 。这次的目标是使用 DFRobot 的 DFRobot Leonardo & Xbee R3板子来实现发送 MIDI消息,然后通过电脑上的软件进行播放。这个板子的核心是Atmel 的 32U4 单片机,内部支持 USB Device,因此可以用来将自身模拟为一个USBMIDI设备(你可以想象为一个自动演奏的钢琴键盘)。首先介绍一下歌曲的获得。经过研究,可以在网上找到MIDI文件,然后使用MIDI编辑软件打开之。这里我使用的是 MCH 的 MixPad。通常MIDI会有多个音轨,比如伴奏等等。这时候需要找到主要的音轨,可以通过下图指示出来的位置选择对不同的音轨静音然后预览。下面的MIDI文件中第一个(最上面)的音轨是主旋律,里面包含我们需要的数据。接下来我们需要将这个音轨提取出来。我这里使用一个MIDI转CSV的小工具。这个工具有两个软件,一个是将MIDI转为CSV 的,另外一个是相反的,将CSV中的数据生成为 MIDI。
使用非常简单,输入 MID文件名然后再给出生成的名称,然后就得到了数据:
前面提到了,MIDI文件中有多个音轨,生成的 CSV文件中,会以Start_track字符串作为标志开启数据记录。比如:下面Title_t “Track 1“ 开始的数据就是我们需要的。
简单看一下数据:第一列是数据的类型号,比如2开始的都是前面提到的Track1 数据。接下来是这个事件发生的时间。然后Note_on_c是表示一个消息,后面是它的参数,第一个参数是 Channel 号;然后是Note(可以理解为钢琴的按键编号,一般 60 对应的C4);最后是按键力度,0表示按键抬起。整理上面的数据然后我们就有一个 .h 文件:
接下来是软件的设计,代码很简单,开始之后不断检查时间标志,如果当前取得的时间大于Current指向的时间那就发送后面的 midi 消息。
#include "MIDIUSB.h"
#include "chysx.h"
uint32_t start;
uint16_t current = 0;
// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).
void noteOn(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void setup() {
Serial.begin(115200);
start = millis();
}
// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).
void controlChange(byte channel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
MidiUSB.sendMIDI(event);
}
void loop() {
if (millis() - start > music.time*3) {
Serial.println(current);
noteOn(0, music.ctrlnum, music.value);
MidiUSB.flush();
noteOn(0, music.ctrlnum, music.value);
MidiUSB.flush();
if (current == 302) {
current = 0;
start = millis();
} else {
current += 2;
}
}
}
其中的判断语句if (millis() - start >music.time*3)可以用来调整播放速度,如果想慢一点就增加乘以的数值,反之就是加速。本文最关键的部分在于如何获得歌谱,这样的方法比自己对照乐谱敲出来省事得多,另外还可以用播放软件来确认结果是否正确。
参考:
1. https://www.fourmilab.ch/webtools/midicsv/#Download
本文提到的 MIDI 文件下载
MIDICSV 工具
完整的源代码
测试的视频https://www.bilibili.com/video/BV1fF41177Rx/
哇!!!!!!!!! 大神厉害了! 值得学习! 呵呵呵呵 6666666666 MIDI播放
页:
[1]