1 /* PROJECT:         ReactOS sndrec32
2  * LICENSE:         GPL - See COPYING in the top level directory
3  * FILE:            base/applications/sndrec32/audio_wavein.cpp
4  * PURPOSE:         Sound recording
5  * PROGRAMMERS:     Marco Pagliaricci (irc: rendar)
6  */
7 
8 #include "stdafx.h"
9 #include "audio_wavein.hpp"
10 
11 _AUDIO_NAMESPACE_START_
12 
13 void
init_(void)14 audio_wavein::init_(void)
15 {
16     ZeroMemory((LPVOID)&wave_format, sizeof(WAVEFORMATEX));
17     wave_format.cbSize = sizeof(WAVEFORMATEX);
18     wavein_handle = 0;
19     recthread_id = 0;
20     wakeup_recthread = 0;
21     data_flushed_event = 0;
22     buf_secs = _AUDIO_DEFAULT_WAVEINBUFSECS;
23     status = WAVEIN_NOTREADY;
24 }
25 
26 void
alloc_buffers_mem_(unsigned int buffs,float secs)27 audio_wavein::alloc_buffers_mem_(unsigned int buffs, float secs)
28 {
29     unsigned int onebuf_size = 0, tot_size = 0;
30 
31     /* Release old memory */
32     if (main_buffer)
33         delete[] main_buffer;
34 
35     if (wave_headers)
36         delete[] wave_headers;
37 
38     /* Calcs size of the buffers */
39     onebuf_size = (unsigned int)((float)aud_info.byte_rate() * secs);
40     tot_size = onebuf_size * buffs;
41 
42     /* Allocs memory for the audio buffers */
43     main_buffer = new BYTE[tot_size];
44     /* Allocs memory for the `WAVEHDR' structures */
45     wave_headers = (WAVEHDR *)new BYTE[sizeof(WAVEHDR) * buffs];
46     /* Zeros memory */
47     ZeroMemory(main_buffer, tot_size);
48     ZeroMemory(wave_headers, sizeof(WAVEHDR) * buffs);
49     /* Updates total size of the buffers */
50     mb_size = tot_size;
51 }
52 
53 void
free_buffers_mem_(void)54 audio_wavein::free_buffers_mem_(void)
55 {
56     /* Frees memory */
57     if (main_buffer)
58         delete[] main_buffer;
59 
60 
61     if (wave_headers)
62         delete[] wave_headers;
63 
64     main_buffer = 0;
65     wave_headers = 0;
66 }
67 
68 void
init_headers_(void)69 audio_wavein::init_headers_(void)
70 {
71     /* If there is no memory for memory or headers, simply return */
72     if ((!wave_headers) || (!main_buffer))
73         return;
74 
75     /* This is the size for one buffer */
76     DWORD buf_sz = mb_size / buffers;
77     /* This is the base address for one buffer */
78     BYTE *buf_addr = main_buffer;
79     /* Initializes headers */
80     for (unsigned int i = 0; i < buffers; ++i)
81     {
82         wave_headers[i].dwBufferLength = mb_size / buffers;
83         wave_headers[i].lpData = (LPSTR)buf_addr;
84         buf_addr += buf_sz;
85     }
86 }
87 
88 void
prep_headers_(void)89 audio_wavein::prep_headers_(void)
90 {
91     MMRESULT err;
92     bool error = false;
93 
94     /* If there is no memory for memory or headers, throw error */
95     if ((!wave_headers) || (!main_buffer) || (!wavein_handle))
96     {
97         /* TODO: throw error! */
98     }
99 
100     for (unsigned int i = 0; i < buffers; ++i)
101     {
102         err = waveInPrepareHeader(wavein_handle, &wave_headers[i], sizeof(WAVEHDR));
103         if (err != MMSYSERR_NOERROR)
104             error = true;
105     }
106 
107     if (error)
108         MessageBox(0, TEXT("waveInPrepareHeader Error."), 0, 0);
109 }
110 
111 void
unprep_headers_(void)112 audio_wavein::unprep_headers_(void)
113 {
114     MMRESULT err;
115     bool error = false;
116 
117     /* If there is no memory for memory or headers, throw error */
118     if ((!wave_headers) || (!main_buffer) || (!wavein_handle))
119     {
120         /* TODO: throw error! */
121     }
122 
123     for (unsigned int i = 0; i < buffers; ++i)
124     {
125         err = waveInUnprepareHeader(wavein_handle, &wave_headers[i], sizeof(WAVEHDR));
126         if (err != MMSYSERR_NOERROR)
127             error = true;
128     }
129 
130     if (error)
131         MessageBox(0, TEXT("waveInUnPrepareHeader Error."), 0, 0);
132 }
133 
134 void
add_buffers_to_driver_(void)135 audio_wavein::add_buffers_to_driver_(void)
136 {
137     MMRESULT err;
138     bool error = false;
139 
140     /* If there is no memory for memory or headers, throw error */
141     if ((!wave_headers) || (!main_buffer) || (!wavein_handle))
142     {
143         /* TODO: throw error! */
144     }
145 
146     for (unsigned int i = 0; i < buffers; ++i)
147     {
148         err = waveInAddBuffer(wavein_handle, &wave_headers[i], sizeof(WAVEHDR));
149         if (err != MMSYSERR_NOERROR)
150             error = true;
151     }
152 
153     if (error)
154         MessageBox(0, TEXT("waveInAddBuffer Error."), 0, 0);
155 }
156 
157 void
close(void)158 audio_wavein::close(void)
159 {
160     /* If wavein object is already in the status NOTREADY, nothing to do */
161     if (status == WAVEIN_NOTREADY)
162         return;
163 
164     /* If the wavein is recording, then stop recording and close it */
165     if (status == WAVEIN_RECORDING)
166         stop_recording();
167 
168     /* Updating status */
169     status = WAVEIN_NOTREADY;
170 
171     /* Waking up recording thread, so it can receive
172        the `MM_WIM_CLOSE' message then dies */
173     if (wakeup_recthread)
174         SetEvent(wakeup_recthread);
175 
176     /* Closing wavein stream */
177     while ((waveInClose(wavein_handle)) != MMSYSERR_NOERROR)
178         Sleep(1);
179 
180     /* Release buffers memory */
181     free_buffers_mem_();
182 
183     /* Re-initialize variables to the initial state */
184     init_();
185 }
186 
187 void
open(void)188 audio_wavein::open(void)
189 {
190     MMRESULT err;
191     HANDLE recthread_handle = 0;
192 
193     /* Checkin the status of the object */
194     if (status != WAVEIN_NOTREADY)
195     {
196         /* TODO: throw error */
197     }
198 
199     /* Creating the EVENT object that will be signaled
200        when the recording thread has to wake up */
201     wakeup_recthread = CreateEvent(0, FALSE, FALSE, 0);
202 
203     data_flushed_event = CreateEvent(0, FALSE, FALSE, 0);
204 
205     if ((!wakeup_recthread) || (!data_flushed_event))
206     {
207         status = WAVEIN_ERR;
208         MessageBox(0, TEXT("Thread Error."), 0, 0);
209         /* TODO: throw error */
210     }
211 
212     /* Inialize buffers for recording audio data from the wavein audio line */
213     alloc_buffers_mem_(buffers, buf_secs);
214     init_headers_();
215 
216     /* Sound format that will be captured by wavein */
217     wave_format.wFormatTag = WAVE_FORMAT_PCM;
218     wave_format.nChannels = aud_info.channels();
219     wave_format.nSamplesPerSec = aud_info.sample_rate();
220     wave_format.wBitsPerSample = aud_info.bits();
221     wave_format.nBlockAlign = aud_info.block_align();
222     wave_format.nAvgBytesPerSec = aud_info.byte_rate();
223 
224     /* Creating the recording thread */
225     recthread_handle = CreateThread(NULL,
226                                     0,
227                                     audio_wavein::recording_procedure,
228                                     (PVOID)this,
229                                     0,
230                                     &recthread_id);
231     /* Checking thread handle */
232     if (!recthread_handle)
233     {
234         /* Updating status */
235         status = WAVEIN_ERR;
236         MessageBox(0, TEXT("Thread Error."), 0, 0);
237         /* TODO: throw error */
238     }
239 
240     /* We don't need the thread handle anymore, so we can close it from now.
241        (We'll just need the thread ID for the `waveInOpen' API) */
242     CloseHandle(recthread_handle);
243 
244     /* Opening audio line wavein */
245     err = waveInOpen(&wavein_handle,
246                      0,
247                      &wave_format,
248                      recthread_id,
249                      0,
250                      CALLBACK_THREAD);
251 
252     if (err != MMSYSERR_NOERROR)
253     {
254         /* Updating status */
255         status = WAVEIN_ERR;
256 
257         if (err == WAVERR_BADFORMAT)
258             MessageBox(0, TEXT("waveInOpen Error"), 0, 0);
259 
260         /* TODO: throw error */
261     }
262 
263     /* Update object status */
264     status = WAVEIN_READY;
265 
266     /* Now `audio_wavein' object is ready for audio recording! */
267 }
268 
269 void
start_recording(void)270 audio_wavein::start_recording(void)
271 {
272     MMRESULT err;
273     BOOL ev;
274 
275     if ((status != WAVEIN_READY) && (status != WAVEIN_STOP))
276     {
277         /* TODO: throw error */
278     }
279 
280     /* Updating to the recording status */
281     status = WAVEIN_RECORDING;
282 
283     /* Let's prepare header of type WAVEHDR that we will pass to the driver
284        with our audio informations, and buffer informations */
285     prep_headers_();
286 
287     /* The waveInAddBuffer function sends an input buffer to the given waveform-audio
288        input device. When the buffer is filled, the application is notified. */
289     add_buffers_to_driver_();
290 
291     /* Signaling event for waking up the recorder thread */
292     ev = SetEvent(wakeup_recthread);
293     if (!ev)
294         MessageBox(0, TEXT("Event Error."), 0, 0);
295 
296     /* Start recording */
297     err = waveInStart(wavein_handle);
298     if (err != MMSYSERR_NOERROR)
299     {
300         /* Updating status */
301         status = WAVEIN_ERR;
302         MessageBox(0, TEXT("waveInStart Error."), 0, 0);
303         /* TODO: throw error */
304     }
305 }
306 
307 void
stop_recording(void)308 audio_wavein::stop_recording(void)
309 {
310     MMRESULT err;
311 
312     if (status != WAVEIN_RECORDING)
313         return;
314 
315     status = WAVEIN_FLUSHING;
316 
317     /* waveInReset will make all pending buffer as done */
318     err = waveInReset(wavein_handle);
319     if ( err != MMSYSERR_NOERROR )
320     {
321         /* TODO: throw error */
322         MessageBox(0, TEXT("waveInReset Error."), 0, 0);
323     }
324 
325     if (data_flushed_event)
326         WaitForSingleObject(data_flushed_event, INFINITE);
327 
328     /* Stop recording */
329     err = waveInStop(wavein_handle);
330     if (err != MMSYSERR_NOERROR)
331     {
332         /* TODO: throw error */
333         MessageBox(0, TEXT("waveInStop Error."), 0, 0);
334     }
335 
336     /* The waveInUnprepareHeader function cleans up the preparation performed
337        by the waveInPrepareHeader function */
338     unprep_headers_();
339 
340     status = WAVEIN_STOP;
341 }
342 
343 DWORD WINAPI
recording_procedure(LPVOID arg)344 audio_wavein::recording_procedure(LPVOID arg)
345 {
346     MSG msg;
347     WAVEHDR *phdr;
348     audio_wavein *_this = (audio_wavein *)arg;
349 
350     /* Check the arg pointer */
351     if (_this == 0)
352         return 0;
353 
354     /* The thread can go to sleep for now. It will be wake up only when
355        there is audio data to be recorded */
356     if (_this->wakeup_recthread)
357         WaitForSingleObject(_this->wakeup_recthread, INFINITE);
358 
359     /* If status of the `audio_wavein' object is not ready or recording the thread can exit */
360     if ((_this->status != WAVEIN_READY) && (_this->status != WAVEIN_RECORDING))
361         return 0;
362 
363     /* Entering main polling loop */
364     while (GetMessage(&msg, 0, 0, 0))
365     {
366         switch (msg.message)
367         {
368             case MM_WIM_DATA:
369                 phdr = (WAVEHDR *)msg.lParam;
370 
371                 if ((_this->status == WAVEIN_RECORDING) ||
372                     (_this->status == WAVEIN_FLUSHING))
373                 {
374                     if (phdr->dwFlags & WHDR_DONE)
375                     {
376                         /* Flushes recorded audio data to the `audio_receiver' object */
377                         _this->audio_rcvd.audio_receive((unsigned char *)phdr->lpData,
378                                                         phdr->dwBytesRecorded);
379 
380                         /* Updating `audio_receiver' total bytes received
381                            _AFTER_ calling `audio_receive' function */
382                         _this->audio_rcvd.bytes_received += phdr->dwBytesRecorded;
383                     }
384 
385                     /* If status is not flushing data, then we can re-add the buffer
386                        for reusing it. Otherwise, if we are flushing pending data,
387                        we cannot re-add buffer because we don't need it anymore */
388                     if (_this->status != WAVEIN_FLUSHING)
389                     {
390                         /* Let the audio driver reuse the buffer */
391                         waveInAddBuffer(_this->wavein_handle, phdr, sizeof(WAVEHDR));
392                     } else {
393                         /* If we are flushing pending data, we have to prepare
394                            to stop recording. Set WAVEHDR flag to 0, and fires
395                            the event `data_flushed_event', that will wake up
396                            the main thread that is sleeping into wavein_in::stop_recording()
397                            member function, waiting the last `MM_WIM_DATA' message
398                            that contain pending data */
399 
400                         phdr->dwFlags = 0;
401                         SetEvent(_this->data_flushed_event);
402 
403                         /* The recording is going to stop, so the recording thread can go to sleep! */
404                         WaitForSingleObject(_this->wakeup_recthread, INFINITE);
405                     }
406                 } /* if WAVEIN_RECORDING || WAVEIN_FLUSHING */
407                 break;
408 
409             case MM_WIM_CLOSE:
410                 /* The thread can exit now */
411                 return 0;
412                 break;
413         } /* end switch(msg.message) */
414     } /* end while(GetMessage(...)) */
415 
416     return 0;
417 }
418 
419 _AUDIO_NAMESPACE_END_
420