#include <windows.h>
#include <mmsystem.h>
#include <cstdio>
#include <string>
#include <vector>
#include <sstream>
// 全局变量
HMIDIOUT hMidiOut;
HWND hEdit;
const int DEFAULT_DURATION = 500; // 默认四分音符时长(毫秒)
const int NOTE_VELOCITY = 127; // 最大力度为 127
// 去除字符串中的换行符
std::string removeNewlines(const std::string& str) {
std::string result;
for (char c : str) {
if (c != '\r' && c != '\n') {
result += c;
}
}
return result;
}
// 解析简谱字符串为音符编号和时长
std::vector<std::pair<int, int>> parseSimplifiedScore(const std::string& score) {
std::vector<std::pair<int, int>> notes;
std::istringstream iss(score);
std::string token;
while (std::getline(iss, token, ',')) {
int note = 60; // 默认中央 C
int duration = DEFAULT_DURATION;
// 处理音符前的符号
int octave = 0;
int semitone = 0;
size_t i = 0;
while (i < token.length()) {
if (token[i] == '+') {
octave++;
} else if (token[i] == '-') {
octave--;
} else if (token[i] == '#') {
semitone++;
} else if (token[i] == '!') {
semitone--;
} else {
break;
}
i++;
}
// 处理基本音符
if (i < token.length()) {
int baseNote = token[i] - '0';
if (baseNote >= 0 && baseNote <= 7) {
if (baseNote == 0) {
note = -1; // 休止符
} else {
note = 60 + (baseNote - 1) + octave * 12 + semitone;
}
}
i++;
}
// 处理音符后的符号
while (i < token.length()) {
if (token[i] == '/') {
duration /= 2;
} else if (token[i] == '-') {
duration *= 2;
} else if (token[i] == '.') {
duration += duration / 2;
if (i + 1 < token.length() && token[i + 1] == '.') {
duration += duration / 4;
i++;
}
}
i++;
}
notes.emplace_back(note, duration);
}
return notes;
}
// 播放简谱
void playSimplifiedScore(const std::vector<std::pair<int, int>>& notes) {
for (const auto& notePair : notes) {
int note = notePair.first;
int duration = notePair.second;
if (note != -1) {
// 发送 MIDI 音符开启消息
midiOutShortMsg(hMidiOut, 0x90 | (note << 8) | (NOTE_VELOCITY << 16));
// 等待一段时间
Sleep(duration);
// 发送 MIDI 音符关闭消息
midiOutShortMsg(hMidiOut, 0x80 | (note << 8) | (0 << 16));
} else {
// 休止符
Sleep(duration);
}
}
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
// 打开 MIDI 输出设备
if (midiOutOpen(&hMidiOut, 0, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
MessageBox(hwnd, "无法打开 MIDI 输出设备", "错误", MB_OK | MB_ICONERROR);
return -1;
}
// 创建编辑框
hEdit = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL,
20, 20, 360, 200, hwnd, NULL, NULL, NULL);
// 创建播放按钮
CreateWindow("BUTTON", "播放", WS_VISIBLE | WS_CHILD, 20, 240, 100, 30, hwnd, (HMENU)1, NULL, NULL);
break;
}
case WM_COMMAND: {
if (LOWORD(wParam) == 1) {
// 获取编辑框中的简谱字符串
char score[1024];
GetWindowText(hEdit, score, sizeof(score));
std::string scoreStr(score);
// 去除换行符
scoreStr = removeNewlines(scoreStr);
// 解析简谱
std::vector<std::pair<int, int>> notes = parseSimplifiedScore(scoreStr);
// 播放简谱
playSimplifiedScore(notes);
}
break;
}
case WM_DESTROY: {
// 关闭 MIDI 输出设备
midiOutClose(hMidiOut);
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
// 注册窗口类
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "MidiPiano";
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow(wc.lpszClassName, "MIDI 简谱编辑器", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 320, NULL, NULL, hInstance, NULL);
// 显示窗口
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
C++简谱播放器代码QZQ
最新推荐文章于 2025-05-06 06:01:24 发布