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)
9 #include "audio_waveout.hpp"
11 _AUDIO_NAMESPACE_START_
14 audio_waveout::init_(void)
16 ZeroMemory((LPVOID
)&wave_format
, sizeof(WAVEFORMATEX
));
17 wave_format
.cbSize
= sizeof(WAVEFORMATEX
);
20 wakeup_playthread
= 0;
21 buf_secs
= _AUDIO_DEFAULT_WAVEOUTBUFSECS
;
22 status
= WAVEOUT_NOTREADY
;
26 audio_waveout::alloc_buffers_mem_(unsigned int buffs
, float secs
)
28 unsigned int onebuf_size
= 0, tot_size
= 0;
30 /* Release old memory */
35 delete[] wave_headers
;
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
];
45 ZeroMemory(main_buffer
, tot_size
);
46 ZeroMemory(wave_headers
, sizeof(WAVEHDR
) * buffs
);
47 /* Updates total size of the buffers */
52 audio_waveout::init_headers_(void)
54 /* If there is no memory for memory or headers, simply return */
55 if ((!wave_headers
) || (!main_buffer
))
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
;
63 ZeroMemory(wave_headers
, sizeof(WAVEHDR
) * buffers
);
65 /* Initializes headers */
66 for (unsigned int i
= 0; i
< buffers
; ++i
)
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
;
72 /* Unsets the WHDR_DONE flag */
73 wave_headers
[i
].dwFlags
&= ~WHDR_DONE
;
75 /* Sets the WAVEHDR user data with an unique little buffer ID# */
76 wave_headers
[i
].dwUser
= (unsigned int)i
;
78 /* Increments little buffer base address */
84 audio_waveout::prep_headers_(void)
89 /* If there is no memory for memory or headers, throw error */
90 if ((!wave_headers
) || (!main_buffer
) || (!waveout_handle
))
92 /* TODO: throw error! */
95 for (unsigned int i
= 0; i
< buffers
; ++i
)
97 err
= waveOutPrepareHeader(waveout_handle
, &wave_headers
[i
], sizeof(WAVEHDR
));
98 if (err
!= MMSYSERR_NOERROR
)
104 /* TODO: throw error indicating which header i-th is errorneous */
109 audio_waveout::unprep_headers_(void)
114 /* If there is no memory for memory or headers, throw error */
115 if ((!wave_headers
) || (!main_buffer
) || (!waveout_handle
))
117 /* TODO: throw error! */
120 for (unsigned int i
= 0; i
< buffers
; ++i
)
122 err
= waveOutUnprepareHeader(waveout_handle
, &wave_headers
[i
], sizeof(WAVEHDR
));
123 if (err
!= MMSYSERR_NOERROR
)
129 /* TODO: throw error indicating which header i-th is errorneous */
134 audio_waveout::free_buffers_mem_(void)
138 delete[] main_buffer
;
141 delete[] wave_headers
;
148 audio_waveout::open(void)
151 HANDLE playthread_handle
= 0;
153 /* Checkin the status of the object */
154 if (status
!= WAVEOUT_NOTREADY
)
156 /* TODO: throw error */
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
)
164 status
= WAVEOUT_ERR
;
165 /* TODO: throw error */
168 /* Inialize buffers for recording audio data from the wavein audio line */
169 alloc_buffers_mem_(buffers
, buf_secs
);
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();
180 /* Creating the recording thread */
181 playthread_handle
= CreateThread(NULL
,
183 audio_waveout::playing_procedure
,
187 /* Checking thread handle */
188 if (!playthread_handle
)
190 /* Updating status */
191 status
= WAVEOUT_ERR
;
192 /* TODO: throw error */
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
);
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
,
207 CALLBACK_THREAD
| WAVE_ALLOWSYNC
);
208 if (err
!= MMSYSERR_NOERROR
)
210 MessageBox(0, _T("waveOutOpen Error"), 0, 0);
211 /* TODO: throw error */
214 status
= WAVEOUT_READY
;
218 audio_waveout::play(void)
225 /* TODO; throw error, or assert */
229 /* If the status is PAUSED, we have to resume the audio playing */
230 if (status
== WAVEOUT_PAUSED
)
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
);
239 } /* if status == WAVEOUT_PAUSED */
241 if (status
!= WAVEOUT_READY
)
244 /* Prepares WAVEHDR structures */
246 /* Sets correct status */
247 status
= WAVEOUT_PLAYING
;
248 /* Reads the audio from the start */
249 //audio_buf.set_position_start();
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
);
255 /* Wakeup the playing thread */
256 SetEvent(wakeup_playthread
);
258 /* Sends all the little buffers to the audio driver, so it can play
260 for (i
= 0; i
< buffers
; ++i
)
262 err
= waveOutWrite(waveout_handle
, &wave_headers
[i
], sizeof(WAVEHDR
));
263 if (err
!= MMSYSERR_NOERROR
)
265 MessageBox(0, _T("waveOutWrite Error"), 0, 0);
266 /* TODO: throw error */
272 audio_waveout::pause(void)
276 /* If the waveout object is not playing audio, do nothing */
277 if (status
== WAVEOUT_PLAYING
)
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
)
285 MessageBox(0, _T("waveOutPause Error"), 0, 0);
286 /* TODO: throw error */
292 audio_waveout::stop(void)
296 /* Checks the current status */
297 if ((status
!= WAVEOUT_PLAYING
) &&
298 (status
!= WAVEOUT_FLUSHING
) &&
299 (status
!= WAVEOUT_PAUSED
))
305 /* Sets a new status */
306 status
= WAVEOUT_STOP
;
307 /* Flushes pending audio datas */
308 err
= waveOutReset( waveout_handle
);
309 if (err
!= MMSYSERR_NOERROR
)
311 MessageBox(0, _T("err WaveOutReset.\n"),_T("ERROR"), 0);
312 /* TODO: throw error */
315 /* Sets the start position of the audio buffer */
316 audio_buf
.set_position_start();
317 /* Cleans little buffers */
320 /* Refreshes the status */
321 status
= WAVEOUT_READY
;
325 audio_waveout::close(void)
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
))
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
)
340 MessageBox(0, _T("waveOutClose Error"), 0, 0);
341 /* TODO: throw error */
348 audio_waveout::playing_procedure(LPVOID arg
)
353 audio_waveout
*_this
= (audio_waveout
*)arg
;
354 unsigned int read_size
;
356 /* Check the arg pointer */
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
);
365 /* Entering main polling loop */
366 while (GetMessage(&msg
, 0, 0, 0))
371 phdr
= (WAVEHDR
*)msg
.lParam
;
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
))
379 WaitForSingleObject(_this
->wakeup_playthread
, INFINITE
);
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
)
389 if (_this
->status
== WAVEOUT_PLAYING
)
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
);
398 /* If the `audio_producer' object, has produced some
399 audio data, so `read_size' will be > 0 */
402 /* Adjusts the correct effectively read size */
403 phdr
->dwBufferLength
= read_size
;
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
;
410 /* Plays the sound of the little buffer */
411 err
= waveOutWrite(_this
->waveout_handle
,
415 /* Checking if any error has occured */
416 if (err
!= MMSYSERR_NOERROR
)
418 MessageBox(0, _T("waveOutWrite Error"), 0, 0);
419 /* TODO: throw error */
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)
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
;
439 else if (phdr
->dwUser
== (_this
->buffers
- 1))
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 */
449 /* Let the thread go to sleep */
450 if (_this
->audio_buf
.play_finished
)
451 _this
->audio_buf
.play_finished();
453 if (_this
->wakeup_playthread
)
454 WaitForSingleObject(_this
->wakeup_playthread
,
457 } /* if (phdr->dwUser == (_this->buffers - 1)) */
458 } /* if read_size != 0 */
459 } /* (phdr->dwFlags & WHDR_DONE) */
460 break; /* end case */
462 /* The thread can exit now */
468 } /* end switch(msg.message) */
469 } /* end while(GetMessage(...)) */
474 _AUDIO_NAMESPACE_END_