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_
475