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