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