mirror of
https://github.com/quantum5/MusicKeyboard.git
synced 2025-04-24 13:11:58 -04:00
Added beeping support.
This commit is contained in:
parent
9859d8ef48
commit
2b5ff9a51d
|
@ -21,7 +21,7 @@ initdir:
|
|||
MusicKeyboard.exe: $(FILES)
|
||||
$(LINK) $(CFLAGS) $(LDFLAGS) $(FILES) -o $@ -lgdi32 -lwinmm -lcomctl32 -lcomdlg32
|
||||
|
||||
include/MainWindow.hpp: include/Window.hpp include/PianoControl.hpp include/midifile.h
|
||||
include/MainWindow.hpp: include/Window.hpp include/PianoControl.hpp include/midifile.h include/mkntapi.h
|
||||
include/PianoControl.hpp: include/Window.hpp
|
||||
include/midifile.h: include/midiinfo.h
|
||||
|
||||
|
@ -43,4 +43,3 @@ build/%.o: src/%.c
|
|||
clean:
|
||||
rm -f build/*.o
|
||||
rm -f MusicKeyboard.exe
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -38,7 +38,7 @@ initdir:
|
|||
@if not exist build md dist
|
||||
@if not exist $(DISTDIR) md $(DISTDIR)
|
||||
|
||||
$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp $(INCDIR)\PianoControl.hpp $(INCDIR)\midifile.h
|
||||
$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp $(INCDIR)\PianoControl.hpp $(INCDIR)\midifile.h $(INCDIR)\mkntapi.h
|
||||
$(INCDIR)\midifile.h: $(INCDIR)\midiinfo.h
|
||||
|
||||
$(SRCDIR)\MainWindow.cpp: $(INCDIR)\MainWindow.hpp
|
||||
|
|
|
@ -5,11 +5,26 @@
|
|||
#include <Window.hpp>
|
||||
#include <PianoControl.hpp>
|
||||
#include <midifile.h>
|
||||
#include <mkntapi.h>
|
||||
|
||||
#include <mmsystem.h>
|
||||
#include <commctrl.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
// Some compatibility defines for poor compilers
|
||||
#ifndef MAPVK_VK_TO_VSC
|
||||
#define MAPVK_VK_TO_VSC 0
|
||||
#endif
|
||||
#ifndef MAPVK_VSC_TO_VK
|
||||
#define MAPVK_VSC_TO_VK 1
|
||||
#endif
|
||||
#ifndef MAPVK_VK_TO_CHAR
|
||||
#define MAPVK_VK_TO_CHAR 2
|
||||
#endif
|
||||
#ifndef GET_KEYSTATE_WPARAM
|
||||
#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
|
||||
#endif
|
||||
|
||||
class MainWindow : public Window {
|
||||
public:
|
||||
virtual LPCTSTR ClassName() { return TEXT("MusicKeyboardMain"); }
|
||||
|
@ -27,10 +42,18 @@ protected:
|
|||
void OnReOpenMIDI();
|
||||
|
||||
virtual HICON GetIcon();
|
||||
|
||||
|
||||
bool useBeep;
|
||||
HANDLE hBeep;
|
||||
unsigned lastFrequency;
|
||||
UNICODE_STRING usBeepDevice;
|
||||
T_RtlInitUnicodeString F_RtlInitUnicodeString;
|
||||
T_NtCreateFile F_NtCreateFile;
|
||||
|
||||
HWND m_volumeLabel, m_volumeBar;
|
||||
HWND m_forceLabel, m_forceBar;
|
||||
HWND m_instruLabel, m_instruSelect;
|
||||
HWND m_beepCheck;
|
||||
HWND m_saveCheck, m_saveLabel, m_saveFile, m_saveBrowse, m_reopen;
|
||||
int m_instrument, m_volume, m_force;
|
||||
HMIDIOUT m_midi;
|
||||
|
|
84
include/mkntapi.h
Normal file
84
include/mkntapi.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/* MusicKeyboard's Windows NT Native API Wrapper header.
|
||||
*
|
||||
* This header emulates <winternl.h> for the poor compilers without this
|
||||
* header, and allows for the GetProcAddress of some functions in ntdll.dll,
|
||||
* which allows for on demand beep start and stop.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef id3F62391D_3432_4F1C_A9E03EA4B121A1A5
|
||||
#define id3F62391D_3432_4F1C_A9E03EA4B121A1A5
|
||||
|
||||
typedef LONG NTSTATUS;
|
||||
|
||||
#ifndef _WINIOCTL_
|
||||
typedef struct _BEEP_PARAM {
|
||||
ULONG Frequency;
|
||||
ULONG Duration;
|
||||
} BEEP_PARAM, *PBEEP_PARAM, *LPBEEP_PARAM;
|
||||
|
||||
#define CTL_CODE( DeviceType, Function, Method, Access ) (\
|
||||
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
|
||||
)
|
||||
#define FILE_DEVICE_BEEP 0x00000001
|
||||
#define METHOD_BUFFERED 0
|
||||
#define FILE_ANY_ACCESS 0
|
||||
#define IOCTL_BEEP_SET CTL_CODE(FILE_DEVICE_BEEP,0,METHOD_BUFFERED,FILE_ANY_ACCESS)
|
||||
#endif /* _WINIOCTL_ */
|
||||
|
||||
#ifndef _WINTERNL_
|
||||
typedef struct _UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
typedef const UNICODE_STRING *PCUNICODE_STRING;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES {
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor;
|
||||
PVOID SecurityQualityOfService;
|
||||
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
union {
|
||||
NTSTATUS Status;
|
||||
PVOID Pointer;
|
||||
} DUMMYUNIONNAME;
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
||||
#endif /* _WINTERNL_ */
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
#ifndef FILE_OPEN_IF
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#endif
|
||||
|
||||
#ifndef InitializeObjectAttributes
|
||||
#define InitializeObjectAttributes( p, n, a, r, s ) { \
|
||||
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
|
||||
(p)->RootDirectory = r; \
|
||||
(p)->Attributes = a; \
|
||||
(p)->ObjectName = n; \
|
||||
(p)->SecurityDescriptor = s; \
|
||||
(p)->SecurityQualityOfService = NULL; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NTAPI
|
||||
# define NTAPI WINAPI
|
||||
#endif
|
||||
|
||||
typedef VOID (NTAPI *T_RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
|
||||
typedef NTSTATUS (NTAPI *T_NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes,
|
||||
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions,
|
||||
PVOID EaBuffer OPTIONAL, ULONG EaLength);
|
||||
#endif
|
|
@ -13,6 +13,7 @@
|
|||
#define KEYBOARD_VOLUME 0xAA01
|
||||
#define KEYBOARD_FORCE 0xAA02
|
||||
#define KEYBOARD_INSTRUMENT 0xAA03
|
||||
#define KEYBOARD_USE_BEEP 0xAAFF
|
||||
#define KEYBOARD_SAVE 0xAB00
|
||||
#define KEYBOARD_SAVE_FILE 0xAB01
|
||||
#define KEYBOARD_BROWSE 0xAB02
|
||||
|
@ -36,7 +37,7 @@
|
|||
DWORD rgbWindowBackground;
|
||||
|
||||
static char keymap[256];
|
||||
static LPWSTR keychars =
|
||||
static LPWSTR keychars =
|
||||
L"~`\0" // F#3
|
||||
L"\x21c6Q\0" // G3
|
||||
L"W\0" // G#3
|
||||
|
@ -63,6 +64,18 @@ static LPWSTR keychars =
|
|||
L"\x2190\0" // F5
|
||||
;
|
||||
|
||||
static WORD frequency[] = {
|
||||
8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21, 22, 23,
|
||||
24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65,
|
||||
69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165,
|
||||
175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392,
|
||||
415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932,
|
||||
988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865,
|
||||
1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729,
|
||||
3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459,
|
||||
7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544
|
||||
};
|
||||
|
||||
int dnslen(LPWSTR dns)
|
||||
{
|
||||
LPWSTR i = dns;
|
||||
|
@ -110,6 +123,9 @@ LRESULT MainWindow::OnCreate()
|
|||
CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL, 0, 0, 0, 0,
|
||||
m_hwnd, (HMENU) KEYBOARD_INSTRUMENT, GetInstance(), NULL);
|
||||
|
||||
m_beepCheck = CreateWindow(WC_BUTTON, L"&Beep?", WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
|
||||
0, 0, 0, 0, m_hwnd, (HMENU) KEYBOARD_USE_BEEP, GetInstance(), NULL);
|
||||
|
||||
m_saveCheck = CreateWindow(WC_BUTTON, L"&Save?", WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
|
||||
0, 0, 0, 0, m_hwnd, (HMENU) KEYBOARD_SAVE, GetInstance(), NULL);
|
||||
m_saveLabel = CreateWindow(WC_STATIC, L"File:",
|
||||
|
@ -118,14 +134,12 @@ LRESULT MainWindow::OnCreate()
|
|||
m_saveFile = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, NULL,
|
||||
WS_CHILD | WS_VISIBLE | WS_DISABLED, 0, 0, 0, 0,
|
||||
m_hwnd, (HMENU) KEYBOARD_SAVE_FILE, GetInstance(), NULL);
|
||||
m_saveBrowse = CreateWindow(WC_BUTTON, L"&Browse...",
|
||||
m_saveBrowse = CreateWindow(WC_BUTTON, L"B&rowse...",
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
||||
0, 0, 0, 0, m_hwnd, (HMENU) KEYBOARD_BROWSE, GetInstance(), NULL);
|
||||
m_reopen = CreateWindow(WC_BUTTON, L"&Reopen",
|
||||
m_reopen = CreateWindow(WC_BUTTON, L"R&eopen",
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
|
||||
0, 0, 0, 0, m_hwnd, (HMENU) KEYBOARD_REOPEN, GetInstance(), NULL);
|
||||
if (!m_saveLabel)
|
||||
MessageBox(m_hwnd, NULL, NULL, NULL);
|
||||
|
||||
SendMessage(m_volumeBar, TBM_SETRANGEMIN, FALSE, 0x0000);
|
||||
SendMessage(m_volumeBar, TBM_SETRANGEMAX, FALSE, 0xFFFF);
|
||||
|
@ -158,6 +172,7 @@ LRESULT MainWindow::OnCreate()
|
|||
SETFONT(m_forceBar);
|
||||
SETFONT(m_instruLabel);
|
||||
SETFONT(m_instruSelect);
|
||||
SETFONT(m_beepCheck);
|
||||
SETFONT(m_saveCheck);
|
||||
SETFONT(m_saveLabel);
|
||||
SETFONT(m_saveFile);
|
||||
|
@ -165,12 +180,24 @@ LRESULT MainWindow::OnCreate()
|
|||
SETFONT(m_reopen);
|
||||
#undef SETFONT
|
||||
|
||||
if (midiOutOpen(&m_midi, 0, NULL, NULL, CALLBACK_NULL) != MMSYSERR_NOERROR)
|
||||
if (midiOutOpen(&m_midi, 0, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR)
|
||||
MessageBox(m_hwnd, L"Failed to open MIDI device!", L"Fatal Error", MB_ICONERROR);
|
||||
|
||||
|
||||
this->piano = PianoControl::Create(NULL, m_hwnd, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0);
|
||||
this->piano->SetBackground(GetSysColorBrush(COLOR_3DFACE));
|
||||
|
||||
// Beep Initialization
|
||||
F_RtlInitUnicodeString = (T_RtlInitUnicodeString) GetProcAddress(GetModuleHandleA("ntdll"), "RtlInitUnicodeString");
|
||||
F_NtCreateFile = (T_NtCreateFile) GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateFile");
|
||||
|
||||
if (!F_RtlInitUnicodeString || !F_NtCreateFile) {
|
||||
EnableWindow(m_beepCheck, FALSE);
|
||||
} else {
|
||||
F_RtlInitUnicodeString(&usBeepDevice, L"\\Device\\Beep");
|
||||
hBeep = NULL;
|
||||
useBeep = false;
|
||||
}
|
||||
|
||||
{
|
||||
keymap[VK_OEM_3] = 54; // `~ key
|
||||
keymap['Q'] = 55;
|
||||
|
@ -234,6 +261,7 @@ LRESULT MainWindow::OnDestroy()
|
|||
DestroyWindow(m_forceBar);
|
||||
DestroyWindow(m_instruLabel);
|
||||
DestroyWindow(m_instruSelect);
|
||||
DestroyWindow(m_beepCheck);
|
||||
DestroyWindow(m_saveCheck);
|
||||
DestroyWindow(m_saveLabel);
|
||||
DestroyWindow(m_saveFile);
|
||||
|
@ -319,16 +347,51 @@ bool MainWindow::Play(WPARAM wParam, LPARAM lParam, bool down)
|
|||
|
||||
void MainWindow::PlayNote(int note, bool down)
|
||||
{
|
||||
if (down) {
|
||||
int num = note % 24;
|
||||
while (num < 0x7F) {
|
||||
if (num != note)
|
||||
MIDI_MESSAGE(m_midi, 0x90, num, 0);
|
||||
num += 24;
|
||||
if (useBeep) {
|
||||
BEEP_PARAM param = {frequency[note], (ULONG) -1};
|
||||
|
||||
if (down) {
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
NTSTATUS status;
|
||||
|
||||
if (hBeep)
|
||||
CloseHandle(hBeep);
|
||||
lastFrequency = param.Frequency;
|
||||
|
||||
if (param.Frequency < 0x25 || param.Frequency > 0x7FFF)
|
||||
return; // Out of range
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes, &usBeepDevice, 0, NULL, NULL);
|
||||
status = F_NtCreateFile(&hBeep, FILE_READ_DATA | FILE_WRITE_DATA,
|
||||
&ObjectAttributes, &IoStatusBlock, NULL, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
FILE_OPEN_IF, 0, NULL, 0);
|
||||
if (NT_SUCCESS(status)) {
|
||||
DeviceIoControl(hBeep, IOCTL_BEEP_SET, ¶m, sizeof param, NULL, 0, NULL, NULL);
|
||||
} else {
|
||||
MessageBox(m_hwnd, TEXT("Fail to open the beep device."),
|
||||
TEXT("Fatal Error"), MB_ICONERROR);
|
||||
}
|
||||
} else {
|
||||
// Kill the current beep ONLY if no new beep has hijacked the handle
|
||||
if (lastFrequency == param.Frequency && hBeep) {
|
||||
CloseHandle(hBeep);
|
||||
hBeep = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (down) {
|
||||
int num = note % 24;
|
||||
while (num < 0x7F) {
|
||||
if (num != note)
|
||||
MIDI_MESSAGE(m_midi, 0x90, num, 0);
|
||||
num += 24;
|
||||
}
|
||||
}
|
||||
MIDI_MESSAGE(m_midi, down ? 0x90 : 0x80, note, m_force);
|
||||
}
|
||||
MIDI_MESSAGE(m_midi, down ? 0x90 : 0x80, note, m_force);
|
||||
|
||||
|
||||
if (m_midifile && saving) {
|
||||
if (deltaTime == (DWORD) -1)
|
||||
deltaTime = GetTickCount();
|
||||
|
@ -344,13 +407,13 @@ void MainWindow::PaintContent(PAINTSTRUCT *pps)
|
|||
HPEN hOldPen = SelectPen(pps->hdc, GetStockPen(DC_PEN));
|
||||
HBRUSH hOldBrush = SelectBrush(pps->hdc, GetSysColorBrush(COLOR_3DFACE));
|
||||
RECT client;
|
||||
|
||||
|
||||
GetClientRect(m_hwnd, &client);
|
||||
SetBkColor(pps->hdc, GetSysColor(COLOR_3DFACE));
|
||||
SetDCPenColor(pps->hdc, GetSysColor(COLOR_3DHILIGHT));
|
||||
|
||||
|
||||
RoundRect(pps->hdc, 12, client.bottom - 52, client.right - 12, client.bottom - 12, 5, 5);
|
||||
|
||||
|
||||
SelectPen(pps->hdc, hOldPen);
|
||||
SelectBrush(pps->hdc, hOldBrush);
|
||||
}
|
||||
|
@ -364,7 +427,7 @@ void MainWindow::OnReOpenMIDI()
|
|||
return;
|
||||
|
||||
WideCharToMultiByte(CP_ACP, 0, path, -1, cpath, MAX_PATH * 2, NULL, NULL);
|
||||
|
||||
|
||||
if (m_midifile)
|
||||
midiFileClose(m_midifile);
|
||||
deltaTime = (DWORD) -1;
|
||||
|
@ -409,6 +472,7 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
REPOS(m_saveFile, BOTTOM(62, client.bottom - 17, client.right - 249, 25));
|
||||
REPOS(m_saveBrowse, BOTTOMRIGHT(client.right - 102, client.bottom - 17, 80, 25));
|
||||
REPOS(m_reopen, BOTTOMRIGHT(client.right - 17, client.bottom - 17, 80, 25));
|
||||
REPOS(m_beepCheck, BOTTOMRIGHT(client.right - 17, client.bottom - 42, 60, 20));
|
||||
EndDeferWindowPos(hdwp);
|
||||
#undef REPOS
|
||||
return 0;
|
||||
|
@ -436,10 +500,14 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
saving = checked == TRUE;
|
||||
break;
|
||||
}
|
||||
case KEYBOARD_USE_BEEP:
|
||||
useBeep = !IsDlgButtonChecked(m_hwnd, KEYBOARD_USE_BEEP);
|
||||
Button_SetCheck(m_beepCheck, useBeep);
|
||||
break;
|
||||
case KEYBOARD_BROWSE: {
|
||||
OPENFILENAME ofn = { sizeof(OPENFILENAME), 0 };
|
||||
WCHAR path[MAX_PATH] = { 0 };
|
||||
|
||||
|
||||
ofn.hwndOwner = m_hwnd;
|
||||
ofn.lpstrFilter = L"Standard MIDI Files (*.mid)\0*.mid\0All Files (*.*)\0*.*\0";
|
||||
ofn.lpstrFile = path;
|
||||
|
@ -450,9 +518,9 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
|
||||
if (!GetSaveFileName(&ofn))
|
||||
break;
|
||||
|
||||
|
||||
SetDlgItemText(m_hwnd, KEYBOARD_SAVE_FILE, path);
|
||||
|
||||
|
||||
OnReOpenMIDI();
|
||||
break;
|
||||
}
|
||||
|
@ -502,7 +570,7 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
isQWERTY = lstrcmpi(buf, L"00000409") == 0;
|
||||
if (!isQWERTY && !hklQWERTY)
|
||||
hklQWERTY = LoadKeyboardLayout(L"00000409", KLF_NOTELLSHELL);
|
||||
|
||||
|
||||
int size = dnslen(keychars) + 1, i;
|
||||
LPWSTR s;
|
||||
if (m_keychars)
|
||||
|
@ -520,11 +588,11 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
goto giveup;
|
||||
m_keychars[i] = ch;
|
||||
continue;
|
||||
|
||||
|
||||
giveup:
|
||||
m_keychars[i] = keychars[i];
|
||||
}
|
||||
|
||||
|
||||
for (s = m_keychars, i = 0; i < 24 && lstrlen(s); s += lstrlen(s) + 1, ++i)
|
||||
piano->SetKeyText(i, s);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue