#include <windows.h>
#include <mmsystem.h>
#include <cstdio>
HMIDIOUT hMidiOut;
const int NOTE_DURATION = 500;
const int NOTE_VELOCITY = 127;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
if (midiOutOpen(&hMidiOut, 0, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
MessageBox(hwnd, "无法打开 MIDI 输出设备", "错误", MB_OK | MB_ICONERROR);
return -1;
}
int x = 20;
int y = 20;
int spacing = 120;
for (int i = 0; i < 21; ++i) {
char buttonText[20];
sprintf(buttonText, "音符 %d", i + 1);
CreateWindow("BUTTON", buttonText, WS_VISIBLE | WS_CHILD, x + i % 3 * spacing, y + i / 3 * 50, 100, 30, hwnd, (HMENU)(i + 1), NULL, NULL);
}
break;
}
case WM_COMMAND: {
int note = LOWORD(wParam) + 59;
midiOutShortMsg(hMidiOut, 0x90 | (note << 8) | (NOTE_VELOCITY << 16));
Sleep(NOTE_DURATION);
midiOutShortMsg(hMidiOut, 0x80 | (note << 8) | (0 << 16));
break;
}
case WM_DESTROY: {
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, 400, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}