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: