Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

365 lines
12 KiB

  1. /* lowpass.c - WinMain() and dialog procedures for LOWPASS, along with
  2. * initialization and support code.
  3. *
  4. * LOWPASS is a sample application illustrating how to use the multimedia
  5. * file I/O services to read and write RIFF files.
  6. *
  7. * LOWPASS runs a simple low-pass filter over an 8-bit-per-sample
  8. * mono WAVE file. Note that this program does not copy unknown chunks
  9. * to the output file.
  10. *
  11. *
  12. * (C) Copyright Microsoft Corp. 1991. All rights reserved.
  13. *
  14. * You have a royalty-free right to use, modify, reproduce and
  15. * distribute the Sample Files (and/or any modified version) in
  16. * any way you find useful, provided that you agree that
  17. * Microsoft has no warranty obligations or liability for any
  18. * Sample Application Files which are modified.
  19. */
  20. #include <windows.h>
  21. #include <mmsystem.h>
  22. #include "lowpass.h"
  23. /* Globals
  24. */
  25. char gszAppName[] = "LowPass"; // for title bar, etc.
  26. HANDLE ghInst; // app's instance handle
  27. /* DoLowPass - Gets the name of the input and output WAVE files from
  28. * the dialog box; reads waveform data from the input file, performs
  29. * a simple low-pass filter by averaging adjacent samples, and writes
  30. * the filtered waveform data to the output WAVE file.
  31. *
  32. * Params: hWnd - Window handle for our dialog box.
  33. *
  34. * Returns:
  35. */
  36. void DoLowPass(HWND hWnd)
  37. {
  38. char achInFile[200]; // name of input file
  39. char achOutFile[200]; // name of output file
  40. HMMIO hmmioIn = NULL; // handle to open input WAVE file
  41. HMMIO hmmioOut = NULL; // handle to open output WAVE file
  42. MMCKINFO ckInRIFF; // chunk info. for input RIFF chunk
  43. MMCKINFO ckOutRIFF; // chunk info. for output RIFF chunk
  44. MMCKINFO ckIn; // info. for a chunk in input file
  45. MMCKINFO ckOut; // info. for a chunk in output file
  46. PCMWAVEFORMAT pcmWaveFormat; // contents of 'fmt' chunks
  47. MMIOINFO mmioinfoIn; // current status of <hmmioIn>
  48. MMIOINFO mmioinfoOut; // current status of <hmmioOut>
  49. long lSamples; // number of samples to filter
  50. BYTE abSamples[3]; // this, last, and before-last sample
  51. /* Read filenames from dialog box fields.
  52. */
  53. achInFile[0] == 0;
  54. GetDlgItemText(hWnd, ID_INPUTFILEEDIT, achInFile, sizeof(achInFile));
  55. achOutFile[0] == 0;
  56. GetDlgItemText(hWnd, ID_OUTPUTFILEEDIT, achOutFile, sizeof(achOutFile));
  57. /* Open the input file for reading using buffered I/O.
  58. */
  59. hmmioIn = mmioOpen(achInFile, NULL, MMIO_ALLOCBUF | MMIO_READ);
  60. if (hmmioIn == NULL)
  61. goto ERROR_CANNOT_READ; // cannot open WAVE file
  62. /* Open the output file for writing using buffered I/O. Note that
  63. * if the file exists, the MMIO_CREATE flag causes it to be truncated
  64. * to zero length.
  65. */
  66. hmmioOut = mmioOpen(achOutFile, NULL,
  67. MMIO_ALLOCBUF | MMIO_WRITE | MMIO_CREATE);
  68. if (hmmioOut == NULL)
  69. goto ERROR_CANNOT_WRITE; // cannot open WAVE file
  70. /* Descend the input file into the 'RIFF' chunk.
  71. */
  72. if (mmioDescend(hmmioIn, &ckInRIFF, NULL, 0) != 0)
  73. goto ERROR_CANNOT_READ; // end-of-file, probably
  74. /* Make sure the input file is a WAVE file.
  75. */
  76. if ((ckInRIFF.ckid != FOURCC_RIFF) ||
  77. (ckInRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
  78. goto ERROR_FORMAT_BAD;
  79. /* Search the input file for for the 'fmt ' chunk.
  80. */
  81. ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
  82. if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  83. goto ERROR_FORMAT_BAD; // no 'fmt ' chunk
  84. /* Expect the 'fmt' chunk to be at least as large as <pcmWaveFormat>;
  85. * if there are extra parameters at the end, we'll ignore them
  86. */
  87. if (ckIn.cksize < (long) sizeof(pcmWaveFormat))
  88. goto ERROR_FORMAT_BAD; // 'fmt ' chunk too small
  89. /* Read the 'fmt ' chunk into <pcmWaveFormat>.
  90. */
  91. if (mmioRead(hmmioIn, (HPSTR) &pcmWaveFormat,
  92. (long) sizeof(pcmWaveFormat)) != (long) sizeof(pcmWaveFormat))
  93. goto ERROR_CANNOT_READ; // truncated file, probably
  94. /* Ascend the input file out of the 'fmt ' chunk.
  95. */
  96. if (mmioAscend(hmmioIn, &ckIn, 0) != 0)
  97. goto ERROR_CANNOT_READ; // truncated file, probably
  98. /* Make sure the input file is an 8-bit mono PCM WAVE file.
  99. */
  100. if ((pcmWaveFormat.wf.wFormatTag != WAVE_FORMAT_PCM) ||
  101. (pcmWaveFormat.wf.nChannels != 1) ||
  102. (pcmWaveFormat.wBitsPerSample != 8))
  103. goto ERROR_FORMAT_BAD; // bad input file format
  104. /* Search the input file for for the 'data' chunk.
  105. */
  106. ckIn.ckid = mmioFOURCC('d', 'a', 't', 'a');
  107. if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  108. goto ERROR_FORMAT_BAD; // no 'data' chunk
  109. /* Create the output file RIFF chunk of form type 'WAVE'.
  110. */
  111. ckOutRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  112. if (mmioCreateChunk(hmmioOut, &ckOutRIFF, MMIO_CREATERIFF) != 0)
  113. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  114. /* We are now descended into the 'RIFF' chunk we just created.
  115. * Now create the 'fmt ' chunk. Since we know the size of this chunk,
  116. * specify it in the MMCKINFO structure so MMIO doesn't have to seek
  117. * back and set the chunk size after ascending from the chunk.
  118. */
  119. ckOut.ckid = mmioFOURCC('f', 'm', 't', ' ');
  120. ckOut.cksize = sizeof(pcmWaveFormat); // we know the size of this ck.
  121. if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  122. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  123. /* Write the PCMWAVEFORMAT structure to the 'fmt ' chunk.
  124. */
  125. if (mmioWrite(hmmioOut, (HPSTR) &pcmWaveFormat, sizeof(pcmWaveFormat))
  126. != sizeof(pcmWaveFormat))
  127. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  128. /* Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
  129. */
  130. if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  131. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  132. /* Create the 'data' chunk that holds the waveform samples.
  133. */
  134. ckOut.ckid = mmioFOURCC('d', 'a', 't', 'a');
  135. if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  136. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  137. /* Read samples from the 'data' chunk of the input file, and write
  138. * samples to the 'data' chunk of the output file. Each sample in
  139. * the output file equals the average of the corresponding sample
  140. * in the input file and the previous two samples from the input file.
  141. * Access the I/O buffers of <hmmioIn> and <hmmioOut> directly,
  142. * since this is faster than calling mmioRead() and mmioWrite()
  143. * for each sample.
  144. */
  145. abSamples[0] = abSamples[1] = abSamples[2] = 128;
  146. /* Begin direct access of the I/O buffers.
  147. */
  148. if (mmioGetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  149. goto ERROR_UNKNOWN;
  150. if (mmioGetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  151. goto ERROR_UNKNOWN;
  152. /* For each input sample, compute and write the output sample.
  153. */
  154. for (lSamples = ckIn.cksize; lSamples > 0; lSamples--)
  155. {
  156. /* If we are at the end of the input file I/O buffer, fill it.
  157. * Test to see that we don't hit end of file while (lSamples > 0).
  158. */
  159. if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  160. {
  161. if (mmioAdvance(hmmioIn, &mmioinfoIn, MMIO_READ) != 0)
  162. goto ERROR_CANNOT_READ;
  163. if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  164. goto ERROR_CANNOT_READ;
  165. }
  166. /* If we are the end of the output file I/O buffer, flush it.
  167. */
  168. if (mmioinfoOut.pchNext == mmioinfoOut.pchEndWrite)
  169. {
  170. mmioinfoOut.dwFlags |= MMIO_DIRTY;
  171. if (mmioAdvance(hmmioOut, &mmioinfoOut, MMIO_WRITE) != 0)
  172. goto ERROR_CANNOT_WRITE;
  173. }
  174. /* Keep track of the last 3 samples so we can average.
  175. */
  176. abSamples[2] = abSamples[1]; // next-to-last sample
  177. abSamples[1] = abSamples[0]; // last sample
  178. abSamples[0] = *(mmioinfoIn.pchNext)++; // current sample
  179. /* The output file sample is the average of the last
  180. * 3 input file samples.
  181. */
  182. *(mmioinfoOut.pchNext)++ = (BYTE) (((int) abSamples[0]
  183. + (int) abSamples[1] + (int) abSamples[2]) / 3);
  184. }
  185. /* We are through processing samples, end direct access of
  186. * the I/O buffers. Set the MMIO_DIRTY flag for the output buffer
  187. * to flush it.
  188. */
  189. if (mmioSetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  190. goto ERROR_UNKNOWN;
  191. mmioinfoOut.dwFlags |= MMIO_DIRTY;
  192. if (mmioSetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  193. goto ERROR_CANNOT_WRITE; // cannot flush, probably
  194. /* Ascend the output file out of the 'data' chunk -- this will cause
  195. * the chunk size of the 'data' chunk to be written.
  196. */
  197. if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  198. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  199. /* Ascend the output file out of the 'RIFF' chunk -- this will cause
  200. * the chunk size of the 'RIFF' chunk to be written.
  201. */
  202. if (mmioAscend(hmmioOut, &ckOutRIFF, 0) != 0)
  203. goto ERROR_CANNOT_WRITE; // cannot write file, probably
  204. /* We are done -- files are closed below.
  205. */
  206. goto EXIT_FUNCTION;
  207. ERROR_FORMAT_BAD:
  208. MessageBox(NULL, "Input file must be an 8-bit mono PCM WAVE file",
  209. gszAppName, MB_ICONEXCLAMATION | MB_OK);
  210. goto EXIT_FUNCTION;
  211. ERROR_CANNOT_READ:
  212. MessageBox(NULL, "Cannot read input WAVE file",
  213. gszAppName, MB_ICONEXCLAMATION | MB_OK);
  214. goto EXIT_FUNCTION;
  215. ERROR_CANNOT_WRITE:
  216. MessageBox(NULL, "Cannot write output WAVE file",
  217. gszAppName, MB_ICONEXCLAMATION | MB_OK);
  218. goto EXIT_FUNCTION;
  219. ERROR_UNKNOWN:
  220. MessageBox(NULL, "Unknown error",
  221. gszAppName, MB_ICONEXCLAMATION | MB_OK);
  222. goto EXIT_FUNCTION;
  223. EXIT_FUNCTION:
  224. /* Close the files (unless they weren't opened successfully).
  225. */
  226. if (hmmioIn != NULL)
  227. mmioClose(hmmioIn, 0);
  228. if (hmmioOut != NULL)
  229. mmioClose(hmmioOut, 0);
  230. }
  231. /* WinMain - Entry point for LowPass.
  232. */
  233. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmdLine, int iCmdShow)
  234. {
  235. FARPROC fpfn;
  236. /* Save instance handle for dialog boxes.
  237. */
  238. ghInst = hInst;
  239. /* Display our dialog box.
  240. */
  241. fpfn = MakeProcInstance((FARPROC) LowPassDlgProc, ghInst);
  242. DialogBox(ghInst, "LOWPASSBOX", NULL, (DLGPROC)fpfn);
  243. FreeProcInstance(fpfn);
  244. return TRUE;
  245. }
  246. /* AboutDlgProc - Dialog procedure function for ABOUTBOX dialog box.
  247. */
  248. BOOL FAR PASCAL
  249. AboutDlgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  250. {
  251. switch (wMsg)
  252. {
  253. case WM_INITDIALOG:
  254. return TRUE;
  255. case WM_COMMAND:
  256. if (LOWORD(wParam) == IDOK)
  257. EndDialog(hWnd, TRUE);
  258. break;
  259. }
  260. return FALSE;
  261. }
  262. /* LowPassDlgProc - Dialog procedure function for LOWPASSBOX dialog box.
  263. */
  264. BOOL FAR PASCAL
  265. LowPassDlgProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  266. {
  267. FARPROC fpfn;
  268. HMENU hmenuSystem; // system menu
  269. HCURSOR ghcurSave; // previous cursor
  270. switch (wMsg)
  271. {
  272. case WM_INITDIALOG:
  273. /* Append "About" menu item to system menu.
  274. */
  275. hmenuSystem = GetSystemMenu(hWnd, FALSE);
  276. AppendMenu(hmenuSystem, MF_SEPARATOR, 0, NULL);
  277. AppendMenu(hmenuSystem, MF_STRING, IDM_ABOUT,
  278. "&About LowPass...");
  279. return TRUE;
  280. case WM_SYSCOMMAND:
  281. switch (wParam)
  282. {
  283. case IDM_ABOUT:
  284. /* Display "About" dialog box.
  285. */
  286. fpfn = MakeProcInstance((FARPROC) AboutDlgProc, ghInst);
  287. DialogBox(ghInst, "ABOUTBOX", hWnd, (DLGPROC)fpfn);
  288. FreeProcInstance(fpfn);
  289. break;
  290. }
  291. break;
  292. case WM_COMMAND:
  293. switch (LOWORD(wParam))
  294. {
  295. case IDOK: // "Begin"
  296. /* Set "busy" cursor, filter input file, restore cursor.
  297. */
  298. ghcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));
  299. DoLowPass(hWnd);
  300. SetCursor(ghcurSave);
  301. break;
  302. case IDCANCEL: // "Done"
  303. EndDialog(hWnd, TRUE);
  304. break;
  305. }
  306. break;
  307. }
  308. return FALSE;
  309. }