windows-nt/Source/XPSP1/NT/multimedia/media/samples/reverse/reverse.c
2020-09-26 16:20:57 +08:00

597 lines
18 KiB
C

/* 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 <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 );
}