Added beeping support.

Signed-off-by: Xiaomao Chen <xiaomao5@live.com>
This commit is contained in:
Quantum 2013-12-09 17:09:54 -05:00
parent 06b443137c
commit 002f3bc3ea
5 changed files with 203 additions and 29 deletions

View file

@ -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

View file

@ -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

View file

@ -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"); }
@ -28,9 +43,17 @@ protected:
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
View 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

View file

@ -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
@ -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,15 +347,50 @@ 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, &param, 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)
@ -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,6 +500,10 @@ 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 };