Reverse
>> Thursday, October 29, 2009
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.h:
/* reverse.h - Header file for REVERSE sample application.
*/
/*
* Constants
*/
// Child window identifiers
#define IDE_NAME 200
#define IDB_PLAY 201
#define IDB_QUIT 202
// Window Position and size definitions
#define WMAIN_DX 207
#define WMAIN_DY 120
#define NAME_DX 180
#define NAME_DY 30
#define NAME_X 10
#define NAME_Y 10
#define PLAY_DX 85
#define PLAY_DY 35
#define PLAY_X 10
#define PLAY_Y 50
#define QUIT_DX 85
#define QUIT_DY 35
#define QUIT_X 105
#define QUIT_Y 50
#define IDM_ABOUT 0x101
/*
* Data Types
*/
typedef struct waveInst
{
HANDLE hWaveInst;
HANDLE hWaveHdr;
HANDLE hWaveData;
} WAVEINST;
typedef WAVEINST FAR *LPWAVEINST;
/*
* Function prototypes
*/
BOOL FAR PASCAL AppAbout(HWND, unsigned, UINT, LONG);
LONG FAR PASCAL WndProc(HWND, unsigned, UINT, LONG);
void Interchange(HPSTR, HPSTR, unsigned);
void ReversePlay(void);
reverse.cpp:
#include <windows.h>
#include <mmsystem.h>
#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);
}
0 comments:
Post a Comment