/* reverse.c - WinMain() and WndProc() for REVERSE, along with * initialization and support code. * * REVERSE is a Windows with Multimedia sample application that * illustrates how to use the low-level waveform playback services. * It also shows how to use the multimedia file I/O services to read * data from a WAVE file. * * REVERSE plays a WAVE waveform audio file backwards. * * (C) Copyright Microsoft Corp. 1991, 1992. All rights reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * */ #include #include #include "reverse.h" #define MAX_FILENAME_SIZE 128 /* Global variables. */ char szAppName[] = "Reverse"; // application name HANDLE hInstApp = NULL; // instance handle HWND hwndApp = NULL; // main window handle HWND hwndName = NULL; // filename window handle HWND hwndPlay = NULL; // "Play" button window handle HWND hwndQuit = NULL; // "Exit" button window handle HWAVEOUT hWaveOut = NULL; LPWAVEHDR lpWaveHdr = NULL; VOID cleanup(LPWAVEINST lpWaveInst); /* WinMain - Entry point for Reverse. */ int PASCAL WinMain(HANDLE hInst, HANDLE hPrev, LPSTR szCmdLine, int cmdShow) { MSG msg; WNDCLASS wc; hInstApp = hInst; /* Define and register a window class for the main window. */ if (!hPrev) { wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInst, szAppName); wc.lpszMenuName = szAppName; wc.lpszClassName = szAppName; wc.hbrBackground = GetStockObject(LTGRAY_BRUSH); wc.hInstance = hInst; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbWndExtra = 0; wc.cbClsExtra = 0; if (!RegisterClass(&wc)) return FALSE; } /* Create and show the main window. */ hwndApp = CreateWindow (szAppName, // class name szAppName, // caption WS_OVERLAPPEDWINDOW, // style bits CW_USEDEFAULT, // x position CW_USEDEFAULT, // y position WMAIN_DX, // x size WMAIN_DY, // y size (HWND)NULL, // parent window (HMENU)NULL, // use class menu (HANDLE)hInst, // instance handle (LPSTR)NULL // no params to pass on ); /* Create child windows for the "Play" and "Exit" buttons * and for an edit field to enter filenames. */ hwndPlay = CreateWindow( "BUTTON", "Play", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, PLAY_X, PLAY_Y, PLAY_DX, PLAY_DY, hwndApp, (HMENU)IDB_PLAY, hInstApp, NULL ); if( !hwndPlay ) return( FALSE ); hwndQuit = CreateWindow( "BUTTON", "Exit", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, QUIT_X, QUIT_Y, QUIT_DX, QUIT_DY, hwndApp, (HMENU)IDB_QUIT, hInstApp, NULL ); if( !hwndQuit ) return( FALSE ); hwndName = CreateWindow("EDIT","", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOHSCROLL, NAME_X, NAME_Y, NAME_DX, NAME_DY, hwndApp, (HMENU)IDE_NAME, hInstApp, NULL); if( !hwndName ) return( FALSE ); SendMessage(hwndName, EM_LIMITTEXT, MAX_FILENAME_SIZE - 1, 0); ShowWindow(hwndApp,cmdShow); /* Add about dialog to system menu. */ AppendMenu(GetSystemMenu(hwndApp, 0), MF_STRING | MF_ENABLED, IDM_ABOUT, "About Reverse..."); /* The main message processing loop. Nothing special here. */ while (GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } /* WndProc - Main window procedure function. */ LONG FAR PASCAL WndProc(HWND hWnd, unsigned msg, UINT wParam, LONG lParam) { FARPROC fpfn; LPWAVEINST lpWaveInst; switch (msg) { case WM_DESTROY: if (hWaveOut) { waveOutReset(hWaveOut); waveOutUnprepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR) ); lpWaveInst = (LPWAVEINST) lpWaveHdr->dwUser; cleanup(lpWaveInst); waveOutClose(hWaveOut); } PostQuitMessage(0); break; case WM_SYSCOMMAND: switch (LOWORD(wParam)) { case IDM_ABOUT: /* Show ABOUTBOX dialog box. */ fpfn = MakeProcInstance((FARPROC)AppAbout, hInstApp); // no op in 32 bit DialogBox(hInstApp, "ABOUTBOX", hWnd, (DLGPROC)fpfn); FreeProcInstance(fpfn); break; } break; /* Process messages sent by the child window controls. */ case WM_SETFOCUS: SetFocus(hwndName); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDE_NAME: // filename edit control return( 0L ); case IDB_PLAY: // "Play" button if (HIWORD(wParam) == BN_CLICKED) ReversePlay(); break; case IDB_QUIT: // "Exit" button if (HIWORD(wParam) == BN_CLICKED) PostQuitMessage(0); break; } return( 0L ); case MM_WOM_DONE: /* This message indicates a waveform data block has * been played and can be freed. Clean up the preparation * done previously on the header. */ waveOutUnprepareHeader( (HWAVEOUT) wParam, (LPWAVEHDR) lParam, sizeof(WAVEHDR) ); /* Get a pointer to the instance data, then unlock and free * all memory associated with the data block, including the * memory for the instance data itself. */ lpWaveInst = (LPWAVEINST) ((LPWAVEHDR)lParam)->dwUser; cleanup(lpWaveInst); /* Close the waveform output device. */ waveOutClose( (HWAVEOUT) wParam ); /* Reenable both button controls. */ EnableWindow( hwndPlay, TRUE ); EnableWindow( hwndQuit, TRUE ); SetFocus(hwndName); break; } return DefWindowProc(hWnd,msg,wParam,lParam); } /* AppAbout -- Dialog procedure for ABOUTBOX dialog box. */ BOOL FAR PASCAL AppAbout(HWND hDlg, unsigned msg, unsigned wParam, LONG lParam) { switch (msg) { case WM_COMMAND: if (LOWORD(wParam) == IDOK) EndDialog(hDlg,TRUE); break; case WM_INITDIALOG: return TRUE; } return FALSE; } /* ReversePlay - Gets a filename from the edit control, then uses * the multimedia file I/O services to read data from the requested * WAVE file. If the file is a proper WAVE file, ReversePlay() calls * the Interchange() function to reverse the order of the waveform * samples in the file. It then plays the reversed waveform data. * * Note that ReversePlay() only handles a single waveform data block. * If the requested WAVE file will not fit in a single data block, it * will not be played. The size of a single data block depends on the * amount of available system memory. * * Params: void * * Return: void */ void ReversePlay() { HANDLE hWaveHdr; LPWAVEINST lpWaveInst; HMMIO hmmio; MMCKINFO mmckinfoParent; MMCKINFO mmckinfoSubchunk; DWORD dwFmtSize; char szFileName[ MAX_FILENAME_SIZE ]; HANDLE hFormat; WAVEFORMAT *pFormat; DWORD dwDataSize; HPSTR hpch1, hpch2; WORD wBlockSize; HANDLE hWaveInst; HANDLE hData = NULL; HPSTR lpData = NULL; /* Get the filename from the edit control. */ if (!GetWindowText( hwndName, (LPSTR)szFileName, MAX_FILENAME_SIZE)) { MessageBox(hwndApp, "Failed to Get Filename", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Open the given file for reading using buffered I/O. */ if(!(hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF))) { MessageBox(hwndApp, "Failed to open file.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Locate a 'RIFF' chunk with a 'WAVE' form type * to make sure it's a WAVE file. */ mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF)) { MessageBox(hwndApp, "This is not a WAVE file.", NULL, MB_OK | MB_ICONEXCLAMATION); mmioClose(hmmio, 0); return; } /* Now, find the format chunk (form type 'fmt '). It should be * a subchunk of the 'RIFF' parent chunk. */ mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) { MessageBox(hwndApp, "WAVE file is corrupted.", NULL, MB_OK | MB_ICONEXCLAMATION); mmioClose(hmmio, 0); return; } /* Get the size of the format chunk, allocate and lock memory for it. */ dwFmtSize = mmckinfoSubchunk.cksize; hFormat = LocalAlloc(LMEM_MOVEABLE, LOWORD(dwFmtSize)); if (!hFormat) { MessageBox(hwndApp, "Out of memory.", NULL, MB_OK | MB_ICONEXCLAMATION); mmioClose(hmmio, 0); return; } pFormat = (WAVEFORMAT *) LocalLock(hFormat); if (!pFormat) { MessageBox(hwndApp, "Failed to lock memory for format chunk.", NULL, MB_OK | MB_ICONEXCLAMATION); LocalFree( hFormat ); mmioClose(hmmio, 0); return; } /* Read the format chunk. */ if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LONG) dwFmtSize) { MessageBox(hwndApp, "Failed to read format chunk.", NULL, MB_OK | MB_ICONEXCLAMATION); LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); return; } /* Make sure it's a PCM file. */ if (pFormat->wFormatTag != WAVE_FORMAT_PCM) { LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); MessageBox(hwndApp, "The file is not a PCM file.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Make sure a waveform output device supports this format. */ if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat, 0L, 0L, WAVE_FORMAT_QUERY)) { LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); MessageBox(hwndApp, "The waveform device can't play this format.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Ascend out of the format subchunk. */ mmioAscend(hmmio, &mmckinfoSubchunk, 0); /* Find the data subchunk. */ mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) { MessageBox(hwndApp, "WAVE file has no data chunk.", NULL, MB_OK | MB_ICONEXCLAMATION); LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); return; } /* Get the size of the data subchunk. */ dwDataSize = mmckinfoSubchunk.cksize; if (dwDataSize == 0L) { MessageBox(hwndApp, "The data chunk has no data.", NULL, MB_OK | MB_ICONEXCLAMATION); LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); return; } /* Open a waveform output device. */ if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat, (UINT)hwndApp, 0L, CALLBACK_WINDOW)) { MessageBox(hwndApp, "Failed to open waveform output device.", NULL, MB_OK | MB_ICONEXCLAMATION); LocalUnlock( hFormat ); LocalFree( hFormat ); mmioClose(hmmio, 0); return; } /* Save block alignment info for later use. */ wBlockSize = pFormat->nBlockAlign; /* We're done with the format header, free it. */ LocalUnlock( hFormat ); LocalFree( hFormat ); /* Allocate and lock memory for the waveform data. */ hData = GlobalAlloc(GMEM_MOVEABLE , dwDataSize ); /* GMEM_SHARE is not needed on 32 bits */ if (!hData) { MessageBox(hwndApp, "Out of memory.", NULL, MB_OK | MB_ICONEXCLAMATION); mmioClose(hmmio, 0); return; } lpData = GlobalLock(hData); if (!lpData) { MessageBox(hwndApp, "Failed to lock memory for data chunk.", NULL, MB_OK | MB_ICONEXCLAMATION); GlobalFree( hData ); mmioClose(hmmio, 0); return; } /* Read the waveform data subchunk. */ if(mmioRead(hmmio, (HPSTR) lpData, dwDataSize) != (LONG) dwDataSize) { MessageBox(hwndApp, "Failed to read data chunk.", NULL, MB_OK | MB_ICONEXCLAMATION); GlobalUnlock( hData ); GlobalFree( hData ); mmioClose(hmmio, 0); return; } /* We're done with the file, close it. */ mmioClose(hmmio, 0); /* Reverse the sound for playing. */ hpch1 = lpData; hpch2 = lpData + dwDataSize - 1; while (hpch1 < hpch2) { Interchange( hpch1, hpch2, wBlockSize ); hpch1 += wBlockSize; hpch2 -= wBlockSize; } /* Allocate a waveform data header. The WAVEHDR must be * globally allocated and locked. */ hWaveHdr = GlobalAlloc(GMEM_MOVEABLE, (DWORD) sizeof(WAVEHDR)); if (!hWaveHdr) { GlobalUnlock( hData ); GlobalFree( hData ); MessageBox(hwndApp, "Not enough memory for header.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); if (!lpWaveHdr) { GlobalUnlock( hData ); GlobalFree( hData ); GlobalFree( hWaveHdr ); MessageBox(hwndApp, "Failed to lock memory for header.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Allocate and set up instance data for waveform data block. * This information is needed by the routine that frees the * data block after it has been played. */ hWaveInst = GlobalAlloc(GMEM_MOVEABLE, (DWORD) sizeof(WAVEHDR)); if (!hWaveInst) { GlobalUnlock( hData ); GlobalFree( hData ); GlobalUnlock( hWaveHdr ); GlobalFree( hWaveHdr ); MessageBox(hwndApp, "Not enough memory for instance data.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } lpWaveInst = (LPWAVEINST) GlobalLock(hWaveInst); if (!lpWaveInst) { GlobalUnlock( hData ); GlobalFree( hData ); GlobalUnlock( hWaveHdr ); GlobalFree( hWaveHdr ); GlobalFree( hWaveInst ); MessageBox(hwndApp, "Failed to lock memory for instance data.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } lpWaveInst->hWaveInst = hWaveInst; lpWaveInst->hWaveHdr = hWaveHdr; lpWaveInst->hWaveData = hData; /* Set up WAVEHDR structure and prepare it to be written to wave device. */ lpWaveHdr->lpData = lpData; lpWaveHdr->dwBufferLength = dwDataSize; lpWaveHdr->dwFlags = 0L; lpWaveHdr->dwLoops = 0L; lpWaveHdr->dwUser = (DWORD) lpWaveInst; if(waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR))) { GlobalUnlock( hData ); GlobalFree( hData ); GlobalUnlock( hWaveHdr ); GlobalFree( hWaveHdr ); GlobalUnlock( hWaveInst ); GlobalFree( hWaveInst ); MessageBox(hwndApp, "Unable to prepare wave header.", NULL, MB_OK | MB_ICONEXCLAMATION); return; } /* Then the data block can be sent to the output device. */ { MMRESULT mmResult; mmResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); if (mmResult != 0) { waveOutUnprepareHeader( hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); GlobalUnlock( hData ); GlobalFree( hData ); MessageBox(hwndApp, "Failed to write block to device", NULL, MB_OK | MB_ICONEXCLAMATION); return; } } /* Disable input to the button controls. */ EnableWindow(hwndPlay, FALSE); EnableWindow(hwndQuit, FALSE); } /* Interchange - Interchanges two samples at the given positions. * * Params: hpchPos1 - Points to one sample. * hpchPos2 - Points to the other sample. * wLength - The length of a sample in bytes. * * Return: void */ void Interchange(HPSTR hpchPos1, HPSTR hpchPos2, unsigned uLength) { unsigned uPlace; BYTE bTemp; for (uPlace = 0; uPlace < uLength; uPlace++) { bTemp = hpchPos1[uPlace]; hpchPos1[uPlace] = hpchPos2[uPlace]; hpchPos2[uPlace] = bTemp; } } VOID cleanup(LPWAVEINST lpWaveInst) { GlobalUnlock( lpWaveInst->hWaveData ); GlobalFree( lpWaveInst->hWaveData ); GlobalUnlock( lpWaveInst->hWaveHdr ); GlobalFree( lpWaveInst->hWaveHdr ); GlobalUnlock( lpWaveInst->hWaveInst ); GlobalFree( lpWaveInst->hWaveInst ); }