1. 引入MIDI库和相关控制类
在CMakeLists.txt
中:
target_link_libraries(SunnyDays winmm)
在MIDIHelper.h
中:
#include <windows.h>
#pragma comment(lib,"winmm.lib")
定义Scale(音阶), Instrument(乐器, 仅包括部分)等枚举。我把Drum单独提了出来。
enum Scale { X1 = 36, X2 = 38, X3 = 40, X4 = 41, X5 = 43, X6 = 45, X7 = 47, L1 = 48, L2 = 50, L3 = 52, L4 = 53, L5 = 55, L6 = 57, L7 = 59, M1 = 60, M2 = 62, M3 = 64, M4 = 65, M5 = 67, M6 = 69, M7 = 71, H1 = 72, H2 = 74, H3 = 76, H4 = 77, H5 = 79, H6 = 81, H7 = 83, LOW_SPEED = 500, MIDDLE_SPEED = 400, HIGH_SPEED = 300, _ = 0XFF }; enum Drum{ BassDrum = 36, SnareDrum = 38, ClosedHiHat = 42, OpenHiHat = 46 }; enum Instrument{ AcousticGrandPiano = 0, BrightAcousticPiano = 1, ElectricGrandPiano = 2, HonkyTonkPiano = 3, ElectricPiano1 = 4, ElectricPiano2 = 5 };
一些基础方法,包括初始化/关闭设备、设置参数、播放单个音符和播放和弦等。
void initDevice(); void closeDevice(); void setInstrument(int channel, int instrument); void setVolume(int channel, int volume); void PlayNote(HMIDIOUT handle, UINT channel, UINT note, UINT velocity); void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT note4, UINT velocity); void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT velocity);
在MIDIHelper.cpp
中:
void initDevice(){ midiOutOpen(&hMidiOut, 0, 0, 0, CALLBACK_NULL); } void closeDevice(){ midiOutClose(hMidiOut); } void setInstrument(int channel,int instrument){ if (channel > 15 || instrument > 127) return; DWORD message = 0xC0 | channel | (instrument << 8); midiOutShortMsg(hMidiOut, message); } void setVolume(int channel,int volume){ if (channel > 15 || volume > 127) return; DWORD message = 0xB0 | channel | (7 << 8) | (volume << 16); midiOutShortMsg(hMidiOut, message); } //播放单个音符,note是音符,velocity是力度 void PlayNote(HMIDIOUT handle, UINT channel, UINT note, UINT velocity) { if (channel > 15 || note > 127 || velocity > 127) return; DWORD message = 0x90 | channel | (note << 8) | (velocity << 16); midiOutShortMsg(handle, message); } //四指和弦 void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT note4, UINT velocity){ if (channel > 15 || note1 > 127 || note2 > 127 || note3 > 127 || note4 > 127 || velocity > 127) return; DWORD message1 = 0x90 | channel | (note1 << 8) | (velocity << 16); DWORD message2 = 0x90 | channel | (note2 << 8) | (velocity << 16); DWORD message3 = 0x90 | channel | (note3 << 8) | (velocity << 16); DWORD message4 = 0x90 | channel | (note4 << 8) | (velocity << 16); midiOutShortMsg(handle, message1); midiOutShortMsg(handle, message2); midiOutShortMsg(handle, message3); midiOutShortMsg(handle, message4); } //三指和弦 void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT velocity) { if (channel > 15 || note1 > 127 || note2 > 127 || note3 > 127 || velocity > 127) return; DWORD message1 = 0x90 | channel | (note1 << 8) | (velocity << 16); DWORD message2 = 0x90 | channel | (note2 << 8) | (velocity << 16); DWORD message3 = 0x90 | channel | (note3 << 8) | (velocity << 16); midiOutShortMsg(handle, message1); midiOutShortMsg(handle, message2); midiOutShortMsg(handle, message3); }
2. 初始化和结束
先在头文件中定义一个全局MIDI句柄:
extern HMIDIOUT hMidiOut;
在入口处初始化MIDI设备并在结束时关闭:
HMIDIOUT hMidiOut; int main() { initDevice(); //... closeDevice(); return 0; }
初始化MIDI设备之后,为每一个乐器分配一个通道channel
(0~15,通常9分配给打击类乐器,例如鼓组),控制音量volume
,然后就可以开始演奏了。