#include #include // add winmm.lib to Project -Settings -Link #include "resource.h" #define WHITE 0xFCFCFC #define TIMER_RESOLUTION 1 #define WM_USER2 WM_USER+1 #define STOPPED 0 #define PLAYING 1 #define PAUSED 2 //int z, InsertLoc; // to show what data is removed when making note 72 into note 73 //double dTemp, dInterval = 0.94387431268169; // interval int x, y, PTR, divisor, zoom, middle, PointsPerScreen, MillisecondsPerScreen, playing, lastPTR, xLoc, yLoc; unsigned int uTimerID = 0; DWORD fileSize, dwBytesRead, subchunksize, nextchunk, nSamplesPerSec, Milliseconds, Seconds, Minutes; WORD nChannels, wBitsPerSample; double pointsPerScreen, pointsPerMillisecond, millisecondsPerScreen, totalMilliseconds; char Filename[MAX_PATH]; char FullFilename[MAX_PATH]; char **FilePart; char CmdLine; char szAppName[] = "PlayWave"; BYTE *WaveBuf; BYTE *Buf; BOOL first = TRUE, nosendtoflg = FALSE, oddeven = 1; char Zoom[10] = "Zoom = "; char time[] = "00:00:000"; char temp[10]; char Help[] = "\ SpaceBar to Play or Pause\n\ PgDown for the next screen's waveform\n\ PgUp for the previous screen's waveform\n\ Home or End\n\ Esc to Stop Playing or to Exit if not Playing\n\ \n\ If the WAVE file is stereo, instead of lines,\n\ red and green dots will show for the two channels.\n\ \n\ Doug Cox\n\ 13 May 2010\n\ http://jdmcox.com\n\ jdmcox@jdmcox.com"; HWND hwnd; HANDLE hFile; HINSTANCE hInst; HMENU hMenu; RECT rect; HBRUSH hBrush; HPEN hPen, hRedPen, hGreenPen; HDC hdc; PAINTSTRUCT ps; OPENFILENAME ofn; HWAVEOUT hWaveOut; WAVEHDR WaveOutHdr; WAVEFORMATEX WaveFormat; WIN32_FIND_DATA fd; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; WNDCLASS wndclass; hInst = hInstance; hBrush = CreateSolidBrush(WHITE); hPen = CreatePen(PS_SOLID, 0, 0); hRedPen = CreatePen(PS_SOLID, 0, 0x0000F0); hGreenPen = CreatePen(PS_SOLID, 0, 0x00F000); wndclass.style = CS_HREDRAW|CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = hBrush; // (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = "MENU"; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) return 0; if (szCmdLine[0] == 0) nosendtoflg = TRUE; else { y = 0; CmdLine = '\x0'; if (szCmdLine[0] == '"') { CmdLine = '"'; y = 1; } for (x = 0; szCmdLine[y] != CmdLine ; x++, y++) Filename[x] = szCmdLine[y]; Filename[x] = 0; GetFullPathName(Filename, MAX_PATH, FullFilename, FilePart); } hwnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, SW_SHOWMAXIMIZED); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void GetTime(void) { _asm fld totalMilliseconds _asm fistp Milliseconds Minutes = Milliseconds / 60000; Seconds = (Milliseconds % 60000) / 1000; time[0] = (char)(Minutes / 10) + '0'; time[1] = (char)(Minutes % 10) + '0'; time[3] = (char)(Seconds / 10) + '0'; time[4] = (char)(Seconds % 10) + '0'; time[6] = (char)((Milliseconds % 1000) / 100) + '0'; time[7] = (char)((Milliseconds % 100) / 10) + '0'; time[8] = (char)(Milliseconds % 10) + '0'; } void StopPlaying(void) { waveOutUnprepareHeader(hWaveOut, &WaveOutHdr, sizeof(WAVEHDR)); waveOutReset(hWaveOut); waveOutClose(hWaveOut); if (uTimerID) { timeKillEvent(uTimerID); uTimerID = 0; timeEndPeriod(TIMER_RESOLUTION); } playing = STOPPED; } LRESULT CALLBACK GotoProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) { static char temp[3]; static HWND hwndEdit1, hwndEdit2; switch (message) { case WM_INITDIALOG: hwndEdit1 = GetDlgItem(hwndDlg, IDC_EDIT1); hwndEdit2 = GetDlgItem(hwndDlg, IDC_EDIT2); SetFocus(hwndEdit1); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (GetWindowText(hwndEdit1, temp, 3)) { if (temp[1]) Minutes = ((temp[0] - '0') * 10) + (temp[1] - '0'); else Minutes = temp[0] - '0'; } else Minutes = 0; if (GetWindowText(hwndEdit2, temp, 3)) { if (temp[1]) Seconds = ((temp[0] - '0') * 10) + (temp[1] - '0'); else Seconds = temp[0] - '0'; } else Seconds = 0; EndDialog (hwndDlg, TRUE); return TRUE; case IDCANCEL: EndDialog (hwndDlg, FALSE); return FALSE; } } return 0; } void CALLBACK TimerFunc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { PostMessage(hwnd, WM_USER2, 0, 0); } void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT message, DWORD dwInstance, DWORD wParam, DWORD lParam) { if (message == WOM_DONE) PostMessage(hwnd, WM_USER, 0, 0); } void OpenWaveFile(void) { hFile = CreateFile(FullFilename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { if (fileSize = GetFileSize(hFile, NULL)) { PTR = 0; divisor = zoom = 64; totalMilliseconds = 0.0; Zoom[7] = (zoom / 100) + '0'; Zoom[8] = ((zoom % 100) / 10) + '0'; Zoom[9] = (zoom % 10) + '0'; time[0] = '0'; time[1] = '0'; time[3] = '0'; time[4] = '0'; time[6] = '0'; time[7] = '0'; time[8] = '0'; if (WaveBuf) VirtualFree(WaveBuf, 0, MEM_RELEASE); WaveBuf = VirtualAlloc(NULL, fileSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); ReadFile(hFile, WaveBuf, fileSize, &dwBytesRead, NULL); CloseHandle(hFile); if (*(DWORD*)&WaveBuf[8] == 0x45564157) {// "WAVE" if (*(WORD*)&WaveBuf[20] == 1) {// PCM type subchunksize = *(DWORD*)&WaveBuf[16]; nextchunk = subchunksize + 20; if (*(DWORD*)&WaveBuf[nextchunk] == 0x74636166)// "fact" nextchunk += 12; if (*(DWORD*)&WaveBuf[nextchunk] != 0x61746164) // "data" nextchunk += (*(DWORD*)&WaveBuf[nextchunk+4]) + 8; if (*(DWORD*)&WaveBuf[nextchunk] == 0x61746164) {// "data" Buf = &WaveBuf[nextchunk+8]; WaveFormat.wFormatTag = WAVE_FORMAT_PCM; WaveFormat.nChannels = *(WORD*)&WaveBuf[22]; WaveFormat.nSamplesPerSec = *(DWORD*)&WaveBuf[24]; WaveFormat.nAvgBytesPerSec = *(DWORD*)&WaveBuf[28]; WaveFormat.nBlockAlign = *(WORD*)&WaveBuf[32]; WaveFormat.wBitsPerSample = *(WORD*)&WaveBuf[34]; WaveFormat.cbSize = 0; nChannels = WaveFormat.nChannels; nSamplesPerSec = WaveFormat.nSamplesPerSec; pointsPerMillisecond = (double)nSamplesPerSec / 1000.0; millisecondsPerScreen = pointsPerScreen / pointsPerMillisecond; wBitsPerSample = WaveFormat.wBitsPerSample; waveOutOpen(&hWaveOut, WAVE_MAPPER, &WaveFormat, (DWORD)&waveOutProc, 0, CALLBACK_FUNCTION); WaveOutHdr.lpData = (LPSTR)&WaveBuf[nextchunk+8]; WaveOutHdr.dwBufferLength = *(DWORD*)&WaveBuf[nextchunk+4]; lastPTR = WaveOutHdr.dwBufferLength - rect.right; WaveOutHdr.dwBytesRecorded = 0; WaveOutHdr.dwUser = 0; WaveOutHdr.dwFlags = 0; WaveOutHdr.dwLoops = 0; waveOutPrepareHeader(hWaveOut, &WaveOutHdr, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &WaveOutHdr, sizeof(WAVEHDR)); timeBeginPeriod(TIMER_RESOLUTION); uTimerID = timeSetEvent(100, TIMER_RESOLUTION, TimerFunc, 0, TIME_PERIODIC);// every 100 milliseconds playing = PLAYING; InvalidateRect(hwnd, &rect, FALSE); SetWindowText(hwnd, Filename); } else { VirtualFree(WaveBuf, 0, MEM_RELEASE); WaveBuf = NULL; MessageBox(hwnd, "", ERROR, MB_OK); } } else { VirtualFree(WaveBuf, 0, MEM_RELEASE); WaveBuf = NULL; MessageBox(hwnd, "Can't play that kind of WAVE file.", ERROR, MB_OK); } } else { VirtualFree(WaveBuf, 0, MEM_RELEASE); WaveBuf = NULL; MessageBox(hwnd, "That's not a WAVE file", ERROR, MB_OK); } } } } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_CREATE: ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwnd; ofn.hInstance = hInst; ofn.lpstrFilter = " WAVE files\0""*.wav;*.wave\0\0"; ofn.lpstrFile = FullFilename; ofn.lpstrFileTitle = Filename; ofn.Flags = OFN_HIDEREADONLY|OFN_NOCHANGEDIR|OFN_ENABLESIZING; ofn.lpstrTitle = NULL; ofn.lpstrDefExt = "wav"; ofn.nMaxFile = MAX_PATH; ofn.lpstrCustomFilter = 0; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 0; ofn.nMaxFileTitle = MAX_PATH; ofn.lpstrInitialDir = NULL; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; hMenu = GetMenu(hwnd); WaveBuf = NULL; playing = STOPPED; return 0; case WM_SIZE: rect.left = rect.top = 0; rect.right = LOWORD(lParam); rect.bottom = HIWORD(lParam); middle = rect.bottom / 2; PointsPerScreen = rect.right / 2; _asm fild PointsPerScreen _asm fstp pointsPerScreen return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_FILES_OPEN: if (GetOpenFileName(&ofn)) OpenWaveFile(); break; case ID_FILES_EXIT: DestroyWindow(hwnd); break; case ID_PLAY: if (playing != PLAYING) { if (PTR < (*(int*)&WaveBuf[nextchunk+4] - rect.right)) { if (playing == STOPPED) { waveOutOpen(&hWaveOut, WAVE_MAPPER, &WaveFormat, (DWORD)&waveOutProc, 0, CALLBACK_FUNCTION); WaveOutHdr.lpData = (LPSTR)&Buf[PTR]; WaveOutHdr.dwBufferLength = *(DWORD*)&WaveBuf[nextchunk+4]; WaveOutHdr.dwBufferLength -= PTR; WaveOutHdr.dwBytesRecorded = 0; WaveOutHdr.dwUser = 0; WaveOutHdr.dwFlags = 0; WaveOutHdr.dwLoops = 0; waveOutPrepareHeader(hWaveOut, &WaveOutHdr, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &WaveOutHdr, sizeof(WAVEHDR)); } else if (playing == PAUSED) { waveOutRestart(hWaveOut); } timeBeginPeriod(TIMER_RESOLUTION); uTimerID = timeSetEvent(100, TIMER_RESOLUTION, TimerFunc, 0, TIME_PERIODIC); playing = PLAYING; InvalidateRect(hwnd, &rect, FALSE); } } break; case ID_PAUSE: if (playing == PLAYING) { waveOutPause(hWaveOut); if (uTimerID) { timeKillEvent(uTimerID); uTimerID = 0; timeEndPeriod(TIMER_RESOLUTION); } playing = PAUSED; InvalidateRect(hwnd, &rect, FALSE); } break; case ID_STOP: if (playing != STOPPED) { StopPlaying(); SendMessage(hwnd, WM_KEYDOWN, VK_HOME, 0); } break; case ID_GOTO: if (Filename[0]) { if (DialogBox(hInst, "GOTO", hwnd, GotoProc)) { x = nSamplesPerSec * 2 * ((Minutes * 60) + Seconds); if (x < (*(int*)&WaveBuf[nextchunk+4] - rect.right)) { PTR = x; StopPlaying(); totalMilliseconds = (double)(((Minutes * 60) + Seconds) * 1000); GetTime(); InvalidateRect(hwnd, &rect, FALSE); } } } break; case ID_HELP: MessageBox(hwnd, Help, szAppName, MB_OK); break; case PLUS: divisor >>= 1; InvalidateRect(hwnd, &rect, FALSE); break; case MINUS: divisor <<= 1; InvalidateRect(hwnd, &rect, FALSE); break; } return 0; case WM_KEYDOWN: switch (wParam) { case VK_SPACE: if (playing == PLAYING) SendMessage(hwnd, WM_COMMAND, ID_PAUSE, 0); else if (Filename[0]) SendMessage(hwnd, WM_COMMAND, ID_PLAY, 0); InvalidateRect(hwnd, &rect, FALSE); break; case VK_NEXT: if (playing != PLAYING) { if (PTR < (*(int*)&WaveBuf[nextchunk+4] - rect.right)) { PTR += rect.right; totalMilliseconds += millisecondsPerScreen; GetTime(); if (playing == PAUSED) StopPlaying(); InvalidateRect(hwnd, &rect, FALSE); } } break; case VK_PRIOR: if (playing != PLAYING) { if (PTR < rect.right) { PTR = rect.right; totalMilliseconds = millisecondsPerScreen; InvalidateRect(hwnd, &rect, FALSE); } if (PTR >= rect.right) { PTR -= rect.right; totalMilliseconds -= millisecondsPerScreen; GetTime(); if (playing == PAUSED) StopPlaying(); InvalidateRect(hwnd, &rect, FALSE); } } break; case VK_HOME: if (playing != PLAYING) { PTR = 0; totalMilliseconds = 0.0; time[0] = '0'; time[1] = '0'; time[3] = '0'; time[4] = '0'; time[6] = '0'; time[7] = '0'; time[8] = '0'; playing = STOPPED; InvalidateRect(hwnd, &rect, FALSE); } break; case VK_END: if (playing != PLAYING) { PTR = *(int*)&WaveBuf[nextchunk+4] - rect.right; totalMilliseconds = PTR / (pointsPerMillisecond * 2); GetTime(); InvalidateRect(hwnd, &rect, FALSE); } break; case VK_ESCAPE: if (playing == PLAYING) SendMessage(hwnd, WM_COMMAND, ID_STOP, 0); else DestroyWindow(hwnd); break; case 187:// '+' if (zoom < 512) { divisor >>= 1; zoom <<= 1; Zoom[7] = (zoom / 100) + '0'; Zoom[8] = ((zoom % 100) / 10) + '0'; Zoom[9] = (zoom % 10) + '0'; InvalidateRect(hwnd, &rect, FALSE); } break; case 189:// '-' if (zoom >= 2) { divisor <<= 1; zoom >>= 1; Zoom[7] = (zoom / 100) + '0'; Zoom[8] = ((zoom % 100) / 10) + '0'; Zoom[9] = (zoom % 10) + '0'; InvalidateRect(hwnd, &rect, FALSE); } break; } return 0; // case WM_MOUSEMOVE: // xLoc = LOWORD(lParam); // yLoc = HIWORD(lParam); // InvalidateRect(hwnd, &rect, FALSE); // return 0; case WM_USER2:// "00:00:000" time[6]++; if (time[6] == ':') { time[6] = '0'; time[4]++; if (time[4] == ':') { time[4] = '0'; time[3]++; if (time[3] == '6') { time[3] = '0'; time[1]++; if (time[1] == ':') { time[1] = '0'; time[0]++; } } } } PTR += (nSamplesPerSec / 5) & 0xFFFFFFFE; // to make it even if ((PTR < lastPTR) && (PTR < (*(int*)&WaveBuf[nextchunk+4] - rect.right))) { totalMilliseconds += 100; InvalidateRect(hwnd, &rect, FALSE); } else PTR -= (nSamplesPerSec / 5); return 0; case WM_PAINT: // InsertLoc = 0; // dTemp = 0; hdc = BeginPaint(hwnd, &ps); FillRect(hdc, &rect, hBrush); MoveToEx(hdc, 0, middle, NULL); LineTo(hdc, rect.right, middle); // _itoa(xLoc+PTR, temp, 10); // TextOut(hdc, 400, 0, temp, strlen(temp)); // _itoa(middle - yLoc, temp, 10); // TextOut(hdc, 460, 0, temp, strlen(temp)); if (WaveBuf) { SetBkMode(hdc, TRANSPARENT); TextOut(hdc, 0, 0, Zoom, 10); TextOut(hdc, 0, middle, time, 9); SetBkMode(hdc, OPAQUE); if (WaveFormat.nChannels == 1) { MoveToEx(hdc, 0, middle, NULL); for (x = 0; x < rect.right; x += 2) { LineTo(hdc, x, (middle - *(SHORT*)&Buf[x+PTR]/divisor)); } } else { // z = 0; for (x = 0; x < rect.right; x += 2) { // dTemp += dInterval; // _asm fld dTemp // from dTemp // _asm fistp InsertLoc // to InsertLoc // if (InsertLoc > z) { // z++; if (oddeven) SelectObject(hdc, hRedPen); else SelectObject(hdc, hGreenPen); // } // else // SelectObject(hdc, hPen); oddeven ^= 1; y = middle - *(SHORT*)&Buf[x+PTR]/divisor; Ellipse(hdc, x-2, y-2, x+2, y+2); } } } EndPaint(hwnd, &ps); if (first) { first = FALSE; if (nosendtoflg) GetOpenFileName (&ofn); OpenWaveFile(); } return 0; case WM_USER: StopPlaying(); return 0; case WM_DESTROY: if (WaveBuf != NULL) VirtualFree(WaveBuf, 0, MEM_RELEASE); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }