597 lines
18 KiB
C
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 );
|
|
}
|