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