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