home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / audio / lowpass / lowpass.c next >
C/C++ Source or Header  |  1997-10-05  |  14KB  |  395 lines

  1. /***********************************************************************
  2.  * lowpass.c - WinMain() and dialog procedures for LOWPASS, along with
  3.  * initialization and support code.
  4.  *
  5.  * LOWPASS is a sample application illustrating how to use the multimedia
  6.  * file I/O services to read and write RIFF files.
  7.  *
  8.  * LOWPASS runs a simple low-pass filter over an 8-bit-per-sample
  9.  * mono WAVE file.  Note that this program does not copy unknown chunks
  10.  * to the output file.
  11.  ************************************************************************
  12.  *
  13.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  14.  *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  15.  *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
  16.  *  A PARTICULAR PURPOSE.
  17.  *
  18.  *  Copyright (C) 1993 - 1997 Microsoft Corporation. All Rights Reserved.
  19.  *********************************************************************** */
  20.  
  21. #include <windows.h>
  22. #include <windowsx.h>
  23. #include <mmsystem.h>
  24. #include "lowpass.h"
  25. #include "strings.h"
  26.  
  27.  
  28. /* Globals
  29.  */
  30. char     gszAppName[] = "LowPass";  // for title bar, etc.
  31. HINSTANCE   ghInst;           // app's instance handle
  32. LPSTR    lpstrLoadStrBuf   = NULL;
  33.  
  34.  
  35. /* DoLowPass - Gets the name of the input and output WAVE files from
  36.  *  the dialog box; reads waveform data from the input file, performs
  37.  *  a simple low-pass filter by averaging adjacent samples, and writes
  38.  *  the filtered waveform data to the output WAVE file.
  39.  *
  40.  * Params:  hWnd - Window handle for our dialog box.
  41.  *
  42.  * Returns:
  43.  */
  44. void DoLowPass(
  45. HWND hWnd)
  46. {
  47.    char         achInFile[200];  // name of input file
  48.    char         achOutFile[200]; // name of output file
  49.    HMMIO        hmmioIn = NULL;  // handle to open input WAVE file
  50.    HMMIO        hmmioOut = NULL; // handle to open output WAVE file
  51.    MMCKINFO       ckInRIFF;   // chunk info. for input RIFF chunk
  52.    MMCKINFO       ckOutRIFF;  // chunk info. for output RIFF chunk
  53.    MMCKINFO       ckIn;    // info. for a chunk in input file
  54.    MMCKINFO       ckOut;      // info. for a chunk in output file
  55.    PCMWAVEFORMAT  pcmWaveFormat; // contents of 'fmt' chunks
  56.    MMIOINFO       mmioinfoIn; // current status of <hmmioIn>
  57.    MMIOINFO       mmioinfoOut;   // current status of <hmmioOut>
  58.    long         lSamples;  // number of samples to filter
  59.    BYTE         abSamples[3]; // this, last, and before-last sample
  60.  
  61.    /* Read filenames from dialog box fields.
  62.     */
  63.    achInFile[0] = 0;
  64.    GetDlgItemText(hWnd, ID_INPUTFILEEDIT, achInFile, sizeof(achInFile));
  65.    achOutFile[0] = 0;
  66.    GetDlgItemText(hWnd, ID_OUTPUTFILEEDIT, achOutFile, sizeof(achOutFile));
  67.  
  68.    /* Open the input file for reading using buffered I/O.
  69.     */
  70.    hmmioIn = mmioOpen(achInFile, NULL, MMIO_ALLOCBUF | MMIO_READ);
  71.    if (hmmioIn == NULL)
  72.       goto LOWPASS_ERROR_CANNOT_READ;  // cannot open WAVE file
  73.  
  74.    /* Open the output file for writing using buffered I/O. Note that
  75.     * if the file exists, the MMIO_CREATE flag causes it to be truncated
  76.     * to zero length.  This means of course that if the SAME FILE is given
  77.     * for both input and output, the input file will be truncated right here
  78.     * and the program will then (surprise!) be unable to read it!
  79.     */
  80.    hmmioOut = mmioOpen(achOutFile, NULL,
  81.                   MMIO_ALLOCBUF | MMIO_WRITE | MMIO_CREATE);
  82.    if (hmmioOut == NULL)
  83.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot open WAVE file
  84.  
  85.    /* Descend the input file into the 'RIFF' chunk.
  86.     */
  87.    if (mmioDescend(hmmioIn, &ckInRIFF, NULL, 0) != 0)
  88.       goto LOWPASS_ERROR_CANNOT_READ;  // end-of-file, probably
  89.  
  90.    /* Make sure the input file is a WAVE file.
  91.     */
  92.    if ((ckInRIFF.ckid != FOURCC_RIFF) ||
  93.       (ckInRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
  94.       goto LOWPASS_ERROR_BAD_FORMAT;
  95.  
  96.    /* Search the input file for for the 'fmt ' chunk.
  97.     */
  98.    ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
  99.    if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  100.       goto LOWPASS_ERROR_BAD_FORMAT;   // no 'fmt ' chunk
  101.  
  102.    /* Expect the 'fmt' chunk to be at least as large as <pcmWaveFormat>;
  103.     * if there are extra parameters at the end, we'll ignore them
  104.     */
  105.    if (ckIn.cksize < (long) sizeof(pcmWaveFormat))
  106.       goto LOWPASS_ERROR_BAD_FORMAT;   // 'fmt ' chunk too small
  107.  
  108.    /* Read the 'fmt ' chunk into <pcmWaveFormat>.
  109.     */
  110.    if (mmioRead(hmmioIn, (HPSTR) &pcmWaveFormat,
  111.       (long) sizeof(pcmWaveFormat)) != (long) sizeof(pcmWaveFormat))
  112.       goto LOWPASS_ERROR_CANNOT_READ;  // truncated file, probably
  113.  
  114.    /* Ascend the input file out of the 'fmt ' chunk.
  115.     */
  116.    if (mmioAscend(hmmioIn, &ckIn, 0) != 0)
  117.       goto LOWPASS_ERROR_CANNOT_READ;  // truncated file, probably
  118.  
  119.    /* Make sure the input file is an 8-bit mono PCM WAVE file.
  120.     */
  121.    if ((pcmWaveFormat.wf.wFormatTag != WAVE_FORMAT_PCM) ||
  122.       (pcmWaveFormat.wf.nChannels != 1) ||
  123.       (pcmWaveFormat.wBitsPerSample != 8))
  124.       goto LOWPASS_ERROR_BAD_FORMAT;   // bad input file format
  125.  
  126.    /* Search the input file for for the 'data' chunk.
  127.     */
  128.    ckIn.ckid = mmioFOURCC('d', 'a', 't', 'a');
  129.    if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  130.       goto LOWPASS_ERROR_BAD_FORMAT;   // no 'data' chunk
  131.  
  132.    /* Create the output file RIFF chunk of form type 'WAVE'.
  133.     */
  134.    ckOutRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  135.    if (mmioCreateChunk(hmmioOut, &ckOutRIFF, MMIO_CREATERIFF) != 0)
  136.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  137.  
  138.    /* We are now descended into the 'RIFF' chunk we just created.
  139.     * Now create the 'fmt ' chunk. Since we know the size of this chunk,
  140.     * specify it in the MMCKINFO structure so MMIO doesn't have to seek
  141.     * back and set the chunk size after ascending from the chunk.
  142.     */
  143.    ckOut.ckid = mmioFOURCC('f', 'm', 't', ' ');
  144.    ckOut.cksize = sizeof(pcmWaveFormat);  // we know the size of this ck.
  145.    if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  146.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  147.  
  148.    /* Write the PCMWAVEFORMAT structure to the 'fmt ' chunk.
  149.     */
  150.    if (mmioWrite(hmmioOut, (HPSTR) &pcmWaveFormat, sizeof(pcmWaveFormat))
  151.       != sizeof(pcmWaveFormat))
  152.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  153.  
  154.    /* Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
  155.     */
  156.    if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  157.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  158.  
  159.    /* Create the 'data' chunk that holds the waveform samples.
  160.     */
  161.    ckOut.ckid = mmioFOURCC('d', 'a', 't', 'a');
  162.    if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  163.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  164.  
  165.    /* Read samples from the 'data' chunk of the input file, and write
  166.     * samples to the 'data' chunk of the output file.  Each sample in
  167.     * the output file equals the average of the corresponding sample
  168.     * in the input file and the previous two samples from the input file.
  169.     * Access the I/O buffers of <hmmioIn> and <hmmioOut> directly,
  170.     * since this is faster than calling mmioRead() and mmioWrite()
  171.     * for each sample.
  172.     */
  173.    abSamples[0] = abSamples[1] = abSamples[2] = 128;
  174.  
  175.    /* Begin direct access of the I/O buffers.
  176.     */
  177.    if (mmioGetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  178.       goto LOWPASS_ERROR_UNKNOWN;
  179.    if (mmioGetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  180.       goto LOWPASS_ERROR_UNKNOWN;
  181.  
  182.    /* For each input sample, compute and write the output sample.
  183.     */
  184.    for (lSamples = ckIn.cksize; lSamples > 0; lSamples--)
  185.    {
  186.       /* If we are at the end of the input file I/O buffer, fill it.
  187.        * Test to see that we don't hit end of file while (lSamples > 0).
  188.        */
  189.       if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  190.       {
  191.          if (mmioAdvance(hmmioIn, &mmioinfoIn, MMIO_READ) != 0)
  192.             goto LOWPASS_ERROR_CANNOT_READ;
  193.  
  194.          if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  195.             goto LOWPASS_ERROR_CANNOT_READ;
  196.       }
  197.  
  198.       /* If we are the end of the output file I/O buffer, flush it.
  199.        */
  200.       if (mmioinfoOut.pchNext == mmioinfoOut.pchEndWrite)
  201.       {
  202.          mmioinfoOut.dwFlags |= MMIO_DIRTY;
  203.          if (mmioAdvance(hmmioOut, &mmioinfoOut, MMIO_WRITE) != 0)
  204.             goto LOWPASS_ERROR_CANNOT_WRITE;
  205.       }
  206.  
  207.       /* Keep track of the last 3 samples so we can average.
  208.        */
  209.       abSamples[2] = abSamples[1];  // next-to-last sample
  210.       abSamples[1] = abSamples[0];  // last sample
  211.       abSamples[0] = *(mmioinfoIn.pchNext)++; // current sample
  212.  
  213.       /* The output file sample is the average of the last
  214.        * 3 input file samples.
  215.        */
  216.       *(mmioinfoOut.pchNext)++ = (BYTE) (((int) abSamples[0]
  217.          + (int) abSamples[1] + (int) abSamples[2]) / 3);
  218.    }
  219.  
  220.    /* We are through processing samples, end direct access of
  221.     * the I/O buffers. Set the MMIO_DIRTY flag for the output buffer
  222.     * to flush it.
  223.     */
  224.    if (mmioSetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  225.       goto LOWPASS_ERROR_UNKNOWN;
  226.    mmioinfoOut.dwFlags |= MMIO_DIRTY;
  227.    if (mmioSetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  228.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot flush, probably
  229.  
  230.    /* Ascend the output file out of the 'data' chunk -- this will cause
  231.     * the chunk size of the 'data' chunk to be written.
  232.     */
  233.    if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  234.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  235.  
  236.    /* Ascend the output file out of the 'RIFF' chunk -- this will cause
  237.     * the chunk size of the 'RIFF' chunk to be written.
  238.     */
  239.    if (mmioAscend(hmmioOut, &ckOutRIFF, 0) != 0)
  240.       goto LOWPASS_ERROR_CANNOT_WRITE; // cannot write file, probably
  241.  
  242.    /* We are done -- files are closed below.
  243.     */
  244.    goto EXIT_FUNCTION;
  245.  
  246. LOWPASS_ERROR_BAD_FORMAT:
  247.  
  248.    LoadString(ghInst, IDS_MUSTBEPCMWAVE, lpstrLoadStrBuf,
  249.       LOADSTRBUFSIZE);
  250.    MessageBox(hWnd, lpstrLoadStrBuf,
  251.          gszAppName, MB_OK | MB_ICONEXCLAMATION);
  252.    goto EXIT_FUNCTION;
  253.  
  254. LOWPASS_ERROR_CANNOT_READ:
  255.  
  256.    LoadString(ghInst, IDS_CANTREADINPUTWAV, lpstrLoadStrBuf,
  257.       LOADSTRBUFSIZE);
  258.    MessageBox(hWnd, lpstrLoadStrBuf,
  259.          gszAppName, MB_OK | MB_ICONEXCLAMATION);
  260.    goto EXIT_FUNCTION;
  261.  
  262. LOWPASS_ERROR_CANNOT_WRITE:
  263.  
  264.    LoadString(ghInst, IDS_CANTWRITEOUTPUT, lpstrLoadStrBuf,
  265.       LOADSTRBUFSIZE);
  266.    MessageBox(hWnd, lpstrLoadStrBuf,
  267.          gszAppName, MB_OK | MB_ICONEXCLAMATION);
  268.    goto EXIT_FUNCTION;
  269.  
  270. LOWPASS_ERROR_UNKNOWN:
  271.  
  272.    LoadString(ghInst, IDS_UNKOWNERROR, lpstrLoadStrBuf,
  273.       LOADSTRBUFSIZE);
  274.    MessageBox(hWnd, lpstrLoadStrBuf,
  275.          gszAppName, MB_OK | MB_ICONEXCLAMATION);
  276.    goto EXIT_FUNCTION;
  277.  
  278. EXIT_FUNCTION:
  279.  
  280.    /* Close the files (unless they weren't opened successfully).
  281.     */
  282.    if (hmmioIn != NULL)
  283.       mmioClose(hmmioIn, 0);
  284.    if (hmmioOut != NULL)
  285.       mmioClose(hmmioOut, 0);
  286. }
  287.  
  288.  
  289. /* WinMain - Entry point for LowPass.
  290.  */
  291. int PASCAL WinMain(
  292. HINSTANCE hInst,
  293. HINSTANCE hPrev,
  294. LPSTR lpszCmdLine,
  295. int iCmdShow)
  296. {
  297.    // Keep string memory out of WIN16 DS.
  298.    lpstrLoadStrBuf = GlobalAllocPtr(GMEM_MOVEABLE, LOADSTRBUFSIZE);
  299.    if(lpstrLoadStrBuf == NULL)
  300.    {
  301.       // don't use LoadString here; it fails when memory is low
  302.       MessageBox(NULL, "No memory left.",
  303.             NULL, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  304.       return FALSE;
  305.    }
  306.    /* Save instance handle for dialog boxes.
  307.     */
  308.    ghInst = hInst;
  309.  
  310.    /* Display our dialog box.
  311.     */
  312.    DialogBox(ghInst, "LOWPASSBOX", NULL, LowPassDlgProc);
  313.    if(lpstrLoadStrBuf)
  314.       GlobalFreePtr(lpstrLoadStrBuf);
  315.    return TRUE;
  316. }
  317.  
  318.  
  319. /* AboutDlgProc - Dialog procedure function for ABOUTBOX dialog box.
  320.  */
  321. BOOL FAR PASCAL AboutDlgProc(
  322. HWND hWnd,
  323. UINT wMsg,
  324. WPARAM wParam,
  325. LPARAM lParam)
  326. {
  327.    switch (wMsg)
  328.    {
  329.    case WM_INITDIALOG:
  330.       return TRUE;
  331.  
  332.    case WM_COMMAND:
  333.       if (wParam == IDOK || wParam == IDCANCEL)
  334.          EndDialog(hWnd, TRUE);
  335.       break;
  336.    }
  337.    return FALSE;
  338. }
  339.  
  340.  
  341. /* LowPassDlgProc - Dialog procedure function for LOWPASSBOX dialog box.
  342.  */
  343. BOOL WINAPI LowPassDlgProc(
  344. HWND hWnd,
  345. UINT wMsg,
  346. WPARAM wParam,
  347. LPARAM lParam)
  348. {
  349.    HMENU hmenuSystem;   // system menu
  350.    HCURSOR  ghcurSave;  // previous cursor
  351.  
  352.    switch (wMsg)
  353.    {
  354.    case WM_INITDIALOG:
  355.       /* Append "About" menu item to system menu.
  356.        */
  357.       hmenuSystem = GetSystemMenu(hWnd, FALSE);
  358.       AppendMenu(hmenuSystem, MF_SEPARATOR, 0, NULL);
  359.       LoadString(ghInst, IDS_ABOUTMENUCAPTION, lpstrLoadStrBuf,
  360.          LOADSTRBUFSIZE);
  361.       AppendMenu(hmenuSystem, MF_STRING, IDM_ABOUT,
  362.          lpstrLoadStrBuf);
  363.       return TRUE;
  364.  
  365.    case WM_SYSCOMMAND:
  366.       switch (wParam)   // WIN16 and 32 have same message packing
  367.       {
  368.          case IDM_ABOUT:
  369.             /* Display "About" dialog box.
  370.              */
  371.             DialogBox(ghInst, "ABOUTBOX", hWnd, AboutDlgProc);
  372.             break;
  373.       }
  374.       break;
  375.  
  376.    case WM_COMMAND:
  377.       switch (wParam)
  378.       {
  379.          case IDOK:  // "Begin"
  380.             /* Set "busy" cursor, filter input file, restore cursor.
  381.              */
  382.             ghcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));
  383.             DoLowPass(hWnd);
  384.             SetCursor(ghcurSave);
  385.             break;
  386.  
  387.          case IDCANCEL: // "Done"
  388.             EndDialog(hWnd, TRUE);
  389.             break;
  390.       }
  391.       break;
  392.    }
  393.    return FALSE;
  394. }
  395.