[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / base / applications / sndrec32 / audio_waveout.cpp
1 /* PROJECT: ReactOS sndrec32
2 * LICENSE: GPL - See COPYING in the top level directory
3 * FILE: base/applications/sndrec32/audio_waveout.cpp
4 * PURPOSE: Sound recording
5 * PROGRAMMERS: Marco Pagliaricci (irc: rendar)
6 */
7
8 #include "stdafx.h"
9 #include "audio_waveout.hpp"
10
11 _AUDIO_NAMESPACE_START_
12
13 void
14 audio_waveout::init_(void)
15 {
16 ZeroMemory((LPVOID)&wave_format, sizeof(WAVEFORMATEX));
17 wave_format.cbSize = sizeof(WAVEFORMATEX);
18 waveout_handle = 0;
19 playthread_id = 0;
20 wakeup_playthread = 0;
21 buf_secs = _AUDIO_DEFAULT_WAVEOUTBUFSECS;
22 status = WAVEOUT_NOTREADY;
23 }
24
25 void
26 audio_waveout::alloc_buffers_mem_(unsigned int buffs, float secs)
27 {
28 unsigned int onebuf_size = 0, tot_size = 0;
29
30 /* Release old memory */
31 if (main_buffer)
32 delete[] main_buffer;
33
34 if (wave_headers)
35 delete[] wave_headers;
36
37 /* Calcs size of the buffers */
38 onebuf_size = (unsigned int)((float)aud_info.byte_rate() * secs);
39 tot_size = onebuf_size * buffs;
40 /* Allocs memory for the audio buffers */
41 main_buffer = new BYTE[tot_size];
42 /* Allocs memory for the `WAVEHDR' structures */
43 wave_headers = (WAVEHDR *) new BYTE[sizeof(WAVEHDR) * buffs];
44 /* Zeros memory */
45 ZeroMemory(main_buffer, tot_size);
46 ZeroMemory(wave_headers, sizeof(WAVEHDR) * buffs);
47 /* Updates total size of the buffers */
48 mb_size = tot_size;
49 }
50
51 void
52 audio_waveout::init_headers_(void)
53 {
54 /* If there is no memory for memory or headers, simply return */
55 if ((!wave_headers) || (!main_buffer))
56 return;
57
58 /* This is the size for one buffer */
59 DWORD buf_sz = mb_size / buffers;
60 /* This is the base address for one buffer */
61 BYTE *buf_addr = main_buffer;
62
63 ZeroMemory(wave_headers, sizeof(WAVEHDR) * buffers);
64
65 /* Initializes headers */
66 for (unsigned int i = 0; i < buffers; ++i)
67 {
68 /* Sets the correct base address and length for the little buffer */
69 wave_headers[i].dwBufferLength = mb_size / buffers;
70 wave_headers[i].lpData = (LPSTR) buf_addr;
71
72 /* Unsets the WHDR_DONE flag */
73 wave_headers[i].dwFlags &= ~WHDR_DONE;
74
75 /* Sets the WAVEHDR user data with an unique little buffer ID# */
76 wave_headers[i].dwUser = (unsigned int)i;
77
78 /* Increments little buffer base address */
79 buf_addr += buf_sz;
80 }
81 }
82
83 void
84 audio_waveout::prep_headers_(void)
85 {
86 MMRESULT err;
87 bool error = false;
88
89 /* If there is no memory for memory or headers, throw error */
90 if ((!wave_headers) || (!main_buffer) || (!waveout_handle))
91 {
92 /* TODO: throw error! */
93 }
94
95 for (unsigned int i = 0; i < buffers; ++i)
96 {
97 err = waveOutPrepareHeader(waveout_handle, &wave_headers[i], sizeof(WAVEHDR));
98 if (err != MMSYSERR_NOERROR)
99 error = true;
100 }
101
102 if (error)
103 {
104 /* TODO: throw error indicating which header i-th is errorneous */
105 }
106 }
107
108 void
109 audio_waveout::unprep_headers_(void)
110 {
111 MMRESULT err;
112 bool error = false;
113
114 /* If there is no memory for memory or headers, throw error */
115 if ((!wave_headers) || (!main_buffer) || (!waveout_handle))
116 {
117 /* TODO: throw error! */
118 }
119
120 for (unsigned int i = 0; i < buffers; ++i)
121 {
122 err = waveOutUnprepareHeader(waveout_handle, &wave_headers[i], sizeof(WAVEHDR));
123 if (err != MMSYSERR_NOERROR)
124 error = true;
125 }
126
127 if (error)
128 {
129 /* TODO: throw error indicating which header i-th is errorneous */
130 }
131 }
132
133 void
134 audio_waveout::free_buffers_mem_(void)
135 {
136 /* Frees memory */
137 if (main_buffer)
138 delete[] main_buffer;
139
140 if (wave_headers)
141 delete[] wave_headers;
142
143 main_buffer = 0;
144 wave_headers = 0;
145 }
146
147 void
148 audio_waveout::open(void)
149 {
150 MMRESULT err;
151 HANDLE playthread_handle = 0;
152
153 /* Checkin the status of the object */
154 if (status != WAVEOUT_NOTREADY)
155 {
156 /* TODO: throw error */
157 }
158
159 /* Creating the EVENT object that will be signaled when
160 the playing thread has to wake up */
161 wakeup_playthread = CreateEvent(0, FALSE, FALSE, 0);
162 if (!wakeup_playthread)
163 {
164 status = WAVEOUT_ERR;
165 /* TODO: throw error */
166 }
167
168 /* Inialize buffers for recording audio data from the wavein audio line */
169 alloc_buffers_mem_(buffers, buf_secs);
170 init_headers_();
171
172 /* Sound format that will be captured by wavein */
173 wave_format.wFormatTag = WAVE_FORMAT_PCM;
174 wave_format.nChannels = aud_info.channels();
175 wave_format.nSamplesPerSec = aud_info.sample_rate();
176 wave_format.wBitsPerSample = aud_info.bits();
177 wave_format.nBlockAlign = aud_info.block_align();
178 wave_format.nAvgBytesPerSec = aud_info.byte_rate();
179
180 /* Creating the recording thread */
181 playthread_handle = CreateThread(NULL,
182 0,
183 audio_waveout::playing_procedure,
184 (PVOID)this,
185 0,
186 &playthread_id);
187 /* Checking thread handle */
188 if (!playthread_handle)
189 {
190 /* Updating status */
191 status = WAVEOUT_ERR;
192 /* TODO: throw error */
193 }
194
195 /* We don't need the thread handle anymore, so we can close it from now.
196 (We'll just need the thread ID for the `waveInOpen' API) */
197 CloseHandle(playthread_handle);
198
199 /* Reset the `audio_source' to the start position */
200 audio_buf.set_position_start();
201 /* Opens the WAVE_OUT device */
202 err = waveOutOpen(&waveout_handle,
203 WAVE_MAPPER,
204 &wave_format,
205 playthread_id,
206 0,
207 CALLBACK_THREAD | WAVE_ALLOWSYNC);
208 if (err != MMSYSERR_NOERROR)
209 {
210 MessageBox(0, _T("waveOutOpen Error"), 0, 0);
211 /* TODO: throw error */
212 }
213
214 status = WAVEOUT_READY;
215 }
216
217 void
218 audio_waveout::play(void)
219 {
220 MMRESULT err;
221 unsigned int i;
222
223 if (!main_buffer)
224 {
225 /* TODO; throw error, or assert */
226 return;
227 }
228
229 /* If the status is PAUSED, we have to resume the audio playing */
230 if (status == WAVEOUT_PAUSED)
231 {
232 /* Updates status */
233 status = WAVEOUT_PLAYING;
234 /* Tells to the driver to resume audio playing */
235 waveOutRestart(waveout_handle);
236 /* Wakeup playing thread */
237 SetEvent(wakeup_playthread);
238 return;
239 } /* if status == WAVEOUT_PAUSED */
240
241 if (status != WAVEOUT_READY)
242 return;
243
244 /* Prepares WAVEHDR structures */
245 prep_headers_();
246 /* Sets correct status */
247 status = WAVEOUT_PLAYING;
248 /* Reads the audio from the start */
249 //audio_buf.set_position_start();
250
251 /* Reads the first N bytes from the audio buffer, where N = the total
252 size of all little buffers */
253 audio_buf.read(main_buffer, mb_size);
254
255 /* Wakeup the playing thread */
256 SetEvent(wakeup_playthread);
257
258 /* Sends all the little buffers to the audio driver, so it can play
259 the sound data */
260 for (i = 0; i < buffers; ++i)
261 {
262 err = waveOutWrite(waveout_handle, &wave_headers[i], sizeof(WAVEHDR));
263 if (err != MMSYSERR_NOERROR)
264 {
265 MessageBox(0, _T("waveOutWrite Error"), 0, 0);
266 /* TODO: throw error */
267 }
268 }
269 }
270
271 void
272 audio_waveout::pause(void)
273 {
274 MMRESULT err;
275
276 /* If the waveout object is not playing audio, do nothing */
277 if (status == WAVEOUT_PLAYING)
278 {
279 /* Updating status */
280 status = WAVEOUT_PAUSED;
281 /* Tells to audio driver to pause audio */
282 err = waveOutPause(waveout_handle);
283 if (err != MMSYSERR_NOERROR)
284 {
285 MessageBox(0, _T("waveOutPause Error"), 0, 0);
286 /* TODO: throw error */
287 }
288 }
289 }
290
291 void
292 audio_waveout::stop(void)
293 {
294 MMRESULT err;
295
296 /* Checks the current status */
297 if ((status != WAVEOUT_PLAYING) &&
298 (status != WAVEOUT_FLUSHING) &&
299 (status != WAVEOUT_PAUSED))
300 {
301 /* Do nothing */
302 return;
303 }
304
305 /* Sets a new status */
306 status = WAVEOUT_STOP;
307 /* Flushes pending audio datas */
308 err = waveOutReset( waveout_handle );
309 if (err != MMSYSERR_NOERROR)
310 {
311 MessageBox(0, _T("err WaveOutReset.\n"),_T("ERROR"), 0);
312 /* TODO: throw error */
313 }
314
315 /* Sets the start position of the audio buffer */
316 audio_buf.set_position_start();
317 /* Cleans little buffers */
318 unprep_headers_();
319 init_headers_();
320 /* Refreshes the status */
321 status = WAVEOUT_READY;
322 }
323
324 void
325 audio_waveout::close(void)
326 {
327 MMRESULT err;
328
329 /* If the `wave_out' object is playing audio, or it is in paused state,
330 we have to call the `stop' member function, to flush pending buffers */
331 if ((status == WAVEOUT_PLAYING) || (status== WAVEOUT_PAUSED))
332 {
333 stop();
334 }
335
336 /* When we have flushed all pending buffers, the wave out handle can be successfully closed */
337 err = waveOutClose(waveout_handle);
338 if (err != MMSYSERR_NOERROR)
339 {
340 MessageBox(0, _T("waveOutClose Error"), 0, 0);
341 /* TODO: throw error */
342 }
343
344 free_buffers_mem_();
345 }
346
347 DWORD WINAPI
348 audio_waveout::playing_procedure(LPVOID arg)
349 {
350 MSG msg;
351 WAVEHDR *phdr;
352 MMRESULT err;
353 audio_waveout *_this = (audio_waveout *)arg;
354 unsigned int read_size;
355
356 /* Check the arg pointer */
357 if (_this == 0)
358 return 0;
359
360 /* The thread can go to sleep for now. It will be wake up only when
361 there is audio data to be recorded */
362 if (_this->wakeup_playthread)
363 WaitForSingleObject(_this->wakeup_playthread, INFINITE);
364
365 /* Entering main polling loop */
366 while (GetMessage(&msg, 0, 0, 0))
367 {
368 switch (msg.message)
369 {
370 case MM_WOM_DONE:
371 phdr = (WAVEHDR *)msg.lParam;
372
373 /* If the status of the `wave_out' object is different
374 than playing, then the thread can go to sleep */
375 if ((_this->status != WAVEOUT_PLAYING) &&
376 (_this->status != WAVEOUT_FLUSHING) &&
377 (_this->wakeup_playthread))
378 {
379 WaitForSingleObject(_this->wakeup_playthread, INFINITE);
380 }
381
382 /* The playing thread doesn't have to sleep, so let's checking
383 first if the little buffer has been sent to the audio driver
384 (it has the WHDR_DONE flag). If it is, we can read new audio
385 datas from the `audio_producer' object, refill the little buffer,
386 and resend it to the driver with waveOutWrite( ) API */
387 if (phdr->dwFlags & WHDR_DONE)
388 {
389 if (_this->status == WAVEOUT_PLAYING)
390 {
391 /* Here the thread is still playing a sound, so it can
392 read new audio data from the `audio_producer' object */
393 read_size = _this->audio_buf.read((BYTE *)phdr->lpData,
394 phdr->dwBufferLength);
395 } else
396 read_size = 0;
397
398 /* If the `audio_producer' object, has produced some
399 audio data, so `read_size' will be > 0 */
400 if (read_size)
401 {
402 /* Adjusts the correct effectively read size */
403 phdr->dwBufferLength = read_size;
404
405 /* Before sending the little buffer to the driver,
406 we have to remove the `WHDR_DONE' flag, because
407 the little buffer now contain new audio data that have to be played */
408 phdr->dwFlags &= ~WHDR_DONE;
409
410 /* Plays the sound of the little buffer */
411 err = waveOutWrite(_this->waveout_handle,
412 phdr,
413 sizeof(WAVEHDR));
414
415 /* Checking if any error has occured */
416 if (err != MMSYSERR_NOERROR)
417 {
418 MessageBox(0, _T("waveOutWrite Error"), 0, 0);
419 /* TODO: throw error */
420 }
421 }
422 else
423 {
424 /* Here `read_size' is 0, so the `audio_producer' object,
425 doesn't have any sound data to produce anymore. So,
426 now we have to see the little buffer #ID to establishing what to do */
427 if (phdr->dwUser == 0)
428 {
429 /* Here `read_size' is 0, and the buffer user data
430 contain 0, so this is the first of N little
431 buffers that came back with `WHDR_DONE' flag;
432 this means that this is the last little buffer
433 in which we have to read data to; so we can
434 _STOP_ reading data from the `audio_producer'
435 object: doing this is accomplished just setting
436 the current status as "WAVEOUT_FLUSHING" */
437 _this->status = WAVEOUT_FLUSHING;
438 }
439 else if (phdr->dwUser == (_this->buffers - 1))
440 {
441 /* Here `read_size' and the buffer user data, that
442 contain a buffer ID#, is equal to the number of
443 the total buffers - 1. This means that this is
444 the _LAST_ little buffer that has been played by
445 the audio driver. We can STOP the `wave_out'
446 object now, or restart the sound playing, if we have a infinite loop */
447 _this->stop();
448
449 /* Let the thread go to sleep */
450 if (_this->audio_buf.play_finished)
451 _this->audio_buf.play_finished();
452
453 if (_this->wakeup_playthread)
454 WaitForSingleObject(_this->wakeup_playthread,
455 INFINITE);
456
457 } /* if (phdr->dwUser == (_this->buffers - 1)) */
458 } /* if read_size != 0 */
459 } /* (phdr->dwFlags & WHDR_DONE) */
460 break; /* end case */
461 case MM_WOM_CLOSE:
462 /* The thread can exit now */
463 return 0;
464 break;
465 case MM_WOM_OPEN:
466 /* Do nothing */
467 break;
468 } /* end switch(msg.message) */
469 } /* end while(GetMessage(...)) */
470
471 return 0;
472 }
473
474 _AUDIO_NAMESPACE_END_