4ce04f77f266bfe453858d42d15906a708fee7c2
[reactos.git] / reactos / base / applications / sndrec32 / audio_wavein.cpp
1 /*
2 * PROJECT: ReactOS Sound Record Application
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/sndrec32/audio_wavein.cpp
5 * PURPOSE: Audio WaveIn
6 * PROGRAMMERS: Marco Pagliaricci <ms_blue (at) hotmail (dot) it>
7 */
8
9 #include "stdafx.h"
10 #include "audio_wavein.hpp"
11
12
13
14 _AUDIO_NAMESPACE_START_
15
16
17 void
18 audio_wavein::init_( void )
19 {
20 ZeroMemory(( LPVOID ) &wave_format,
21 sizeof( WAVEFORMATEX ));
22
23 wave_format.cbSize = sizeof( WAVEFORMATEX );
24
25 wavein_handle = 0;
26 recthread_id = 0;
27 wakeup_recthread = 0;
28
29 buf_secs = _AUDIO_DEFAULT_WAVEINBUFSECS;
30
31
32 status = WAVEIN_NOTREADY;
33 }
34
35
36 void
37 audio_wavein::alloc_buffers_mem_( unsigned int buffs, float secs )
38 {
39
40
41 unsigned int
42 onebuf_size = 0, tot_size = 0;
43
44
45 //
46 // Release old memory
47 //
48
49 if ( main_buffer )
50 delete[] main_buffer;
51
52
53 if ( wave_headers )
54 delete[] wave_headers;
55
56
57
58 //
59 // Calcs size of the buffers
60 //
61
62 onebuf_size = ( unsigned int )
63 (( float )aud_info.byte_rate() * secs );
64
65
66 tot_size = onebuf_size * buffs;
67
68
69
70
71 //
72 // Allocs memory for the audio buffers
73 //
74
75 main_buffer = new BYTE [ tot_size ];
76
77
78
79 //
80 // Allocs memory for the `WAVEHDR' structures.
81 //
82
83 wave_headers = ( WAVEHDR * )
84 new BYTE [ sizeof( WAVEHDR ) * buffs ];
85
86
87
88 //
89 // Zeros memory.
90 //
91
92 ZeroMemory( main_buffer, tot_size );
93
94 ZeroMemory( wave_headers,
95 sizeof( WAVEHDR ) * buffs );
96
97
98 //
99 // Updates total size of the buffers.
100 //
101
102 mb_size = tot_size;
103
104 }
105
106
107 void
108 audio_wavein::free_buffers_mem_( void )
109 {
110
111
112 //
113 // Frees memory
114 //
115
116 if ( main_buffer )
117 delete[] main_buffer;
118
119
120 if ( wave_headers )
121 delete[] wave_headers;
122
123
124 main_buffer = 0;
125 wave_headers = 0;
126
127 }
128
129
130 void
131 audio_wavein::init_headers_( void )
132 {
133
134
135
136 //
137 // If there is no memory for memory or
138 // headers, simply return.
139 //
140
141 if (( !wave_headers ) || ( !main_buffer ))
142 return;
143
144
145 //
146 // This is the size for one buffer
147 //
148
149 DWORD buf_sz = mb_size / buffers;
150
151
152
153 //
154 // This is the base address for one buffer
155 //
156
157 BYTE * buf_addr = main_buffer;
158
159
160 //
161 // Initializes headers.
162 //
163
164 for ( unsigned int i = 0; i < buffers; ++i )
165 {
166 wave_headers[ i ].dwBufferLength = mb_size / buffers;
167 wave_headers[ i ].lpData = ( LPSTR ) buf_addr;
168
169 buf_addr += buf_sz;
170 }
171
172 }
173
174
175 void
176 audio_wavein::prep_headers_( void )
177 {
178 MMRESULT err;
179 bool error = false;
180
181
182 //
183 // If there is no memory for memory or
184 // headers, throw error.
185 //
186
187 if (( !wave_headers )
188 || ( !main_buffer ) || ( !wavein_handle ))
189 {} //TODO: throw error!
190
191
192
193 for ( unsigned int i = 0; i < buffers; ++i )
194 {
195 err = waveInPrepareHeader( wavein_handle,
196 &wave_headers[ i ], sizeof( WAVEHDR ));
197
198
199 if ( err != MMSYSERR_NOERROR )
200 error = true;
201
202 }
203
204
205 if ( error )
206 MessageBox( 0, TEXT("waveInPrepareHeader Error."), 0, 0 );
207
208
209
210 }
211
212 void
213 audio_wavein::unprep_headers_( void )
214 {
215 MMRESULT err;
216 bool error = false;
217
218
219
220 //
221 // If there is no memory for memory or
222 // headers, throw error.
223 //
224
225 if (( !wave_headers )
226 || ( !main_buffer ) || ( !wavein_handle ))
227 {} //TODO: throw error!
228
229
230
231 for ( unsigned int i = 0; i < buffers; ++i )
232 {
233 err = waveInUnprepareHeader( wavein_handle,
234 &wave_headers[ i ], sizeof( WAVEHDR ));
235
236
237 if ( err != MMSYSERR_NOERROR )
238 error = true;
239
240 }
241
242
243 if ( error )
244 MessageBox( 0, TEXT("waveInUnPrepareHeader Error."), 0, 0 );
245
246 }
247
248
249 void
250 audio_wavein::add_buffers_to_driver_( void )
251 {
252 MMRESULT err;
253 bool error = false;
254
255
256
257 //
258 // If there is no memory for memory or
259 // headers, throw error.
260 //
261
262 if (( !wave_headers )
263 || ( !main_buffer ) || ( !wavein_handle ))
264 {} //TODO: throw error!
265
266
267
268
269 for ( unsigned int i = 0; i < buffers; ++i )
270 {
271 err = waveInAddBuffer( wavein_handle,
272 &wave_headers[ i ], sizeof( WAVEHDR ));
273
274
275 if ( err != MMSYSERR_NOERROR )
276 error = true;
277
278 }
279
280
281 if ( error )
282 MessageBox( 0, TEXT("waveInAddBuffer Error."), 0, 0 );
283
284 }
285
286
287
288 void
289 audio_wavein::close( void )
290 {
291
292
293
294
295 //
296 // If wavein object is already in the status
297 // NOTREADY, nothing to do.
298 //
299
300 if ( status == WAVEIN_NOTREADY )
301 return;
302
303
304
305 //
306 // If the wavein is recording,
307 // then stop recording and close it.
308 //
309
310 if ( status == WAVEIN_RECORDING )
311 stop_recording();
312
313
314 //
315 // Updating status.
316 //
317
318 status = WAVEIN_NOTREADY;
319
320
321
322
323 //
324 // Wakeing up recording thread, so it
325 // can receive the `MM_WIM_CLOSE' message
326 // then dies.
327 //
328 if ( wakeup_recthread )
329 SetEvent( wakeup_recthread );
330
331
332
333 //
334 // Closing wavein stream
335 //
336
337 while (( waveInClose( wavein_handle ))
338 != MMSYSERR_NOERROR ) Sleep( 1 );
339
340
341
342 //
343 // Release buffers memory.
344 //
345
346 free_buffers_mem_();
347
348
349 //
350 // Re-initialize variables to the
351 // initial state.
352 //
353
354 init_();
355
356 }
357
358
359 void
360 audio_wavein::open( void )
361 {
362
363 MMRESULT err;
364 HANDLE recthread_handle = 0;
365
366
367 //
368 // Checkin the status of the object
369 //
370
371 if ( status != WAVEIN_NOTREADY )
372 {} //TODO: throw error
373
374
375
376 //
377 // Creating the EVENT object that will be signaled
378 // when the recording thread has to wake up.
379 //
380
381 wakeup_recthread =
382 CreateEvent( 0, FALSE, FALSE, 0 );
383
384
385 data_flushed_event =
386 CreateEvent( 0, FALSE, FALSE, 0 );
387
388
389
390 if (( !wakeup_recthread ) || ( !data_flushed_event ))
391 {
392
393
394 status = WAVEIN_ERR;
395
396 MessageBox( 0, TEXT("Thread Error."), 0, 0 );
397
398 //TODO: throw error
399 }
400
401
402
403 //
404 // Inialize buffers for recording audio
405 // data from the wavein audio line.
406 //
407
408 alloc_buffers_mem_( buffers, buf_secs );
409 init_headers_();
410
411
412
413
414
415
416 //
417 // Sound format that will be captured by wavein
418 //
419
420 wave_format.wFormatTag = WAVE_FORMAT_PCM;
421
422 wave_format.nChannels = aud_info.channels();
423 wave_format.nSamplesPerSec = aud_info.sample_rate();
424 wave_format.wBitsPerSample = aud_info.bits();
425 wave_format.nBlockAlign = aud_info.block_align();
426 wave_format.nAvgBytesPerSec = aud_info.byte_rate();
427
428
429
430 //
431 // Creating the recording thread
432 //
433
434 recthread_handle =
435 CreateThread( NULL,
436 0,
437 audio_wavein::recording_procedure,
438 ( PVOID ) this,
439 0,
440 &recthread_id
441 );
442
443
444
445 //
446 // Checking thread handle
447 //
448
449 if ( !recthread_handle )
450 {
451
452 //
453 // Updating status
454 //
455
456 status = WAVEIN_ERR;
457
458 MessageBox( 0, TEXT("Thread Error."), 0, 0 );
459 //TODO: throw error
460
461 }
462
463
464 //
465 // We don't need the thread handle anymore,
466 // so we can close it from now. (We'll just
467 // need the thread ID for the `waveInOpen' API)
468 //
469
470 CloseHandle( recthread_handle );
471
472
473
474 //
475 // Opening audio line wavein
476 //
477
478 err = waveInOpen( &wavein_handle,
479 0,
480 &wave_format,
481 recthread_id,
482 0,
483 CALLBACK_THREAD
484 );
485
486
487 if ( err != MMSYSERR_NOERROR )
488 {
489
490
491 //
492 // Updating status
493 //
494
495 status = WAVEIN_ERR;
496
497 if ( err == WAVERR_BADFORMAT )
498 MessageBox( 0, TEXT("waveInOpen Error"), 0, 0 );
499
500
501 //TODO: throw error
502 }
503
504
505 //
506 // Update object status
507 //
508
509 status = WAVEIN_READY;
510
511
512
513 //
514 // Now `audio_wavein' object is ready
515 // for audio recording!
516 //
517 }
518
519
520
521 void
522 audio_wavein::start_recording( void )
523 {
524
525 MMRESULT err;
526 BOOL ev;
527
528
529
530 if (( status != WAVEIN_READY )
531 && ( status != WAVEIN_STOP ))
532 {} //TODO: throw error
533
534
535
536
537 //
538 // Updating to the recording status
539 //
540
541 status = WAVEIN_RECORDING;
542
543
544
545
546 //
547 // Let's prepare header of type WAVEHDR that
548 // we will pass to the driver with our
549 // audio informations, and buffer informations.
550 //
551
552 prep_headers_();
553
554
555
556 //
557 // The waveInAddBuffer function sends an input buffer
558 // to the given waveform-audio input device.
559 // When the buffer is filled, the application is notified.
560 //
561
562 add_buffers_to_driver_();
563
564
565
566
567
568 //
569 // Signaling event for waking up
570 // the recorder thread.
571 //
572
573 ev = SetEvent( wakeup_recthread );
574
575
576 if ( !ev )
577 {
578
579
580 MessageBox( 0, TEXT("Event Error."), 0, 0 );
581
582 }
583
584
585 //
586 // Start recording
587 //
588
589
590 err = waveInStart( wavein_handle );
591
592
593 if ( err != MMSYSERR_NOERROR )
594 {
595
596 //
597 // Updating status
598 //
599
600 status = WAVEIN_ERR;
601
602 MessageBox( 0, TEXT("waveInStart Error."), 0, 0 );
603
604
605 //TODO: throw error
606
607 }
608
609 }
610
611
612
613 void
614 audio_wavein::stop_recording( void )
615 {
616
617
618 MMRESULT err;
619 DWORD wait;
620
621
622 if ( status != WAVEIN_RECORDING )
623 return;
624
625
626
627 status = WAVEIN_FLUSHING;
628
629
630 if ( data_flushed_event )
631 wait = WaitForSingleObject(
632 data_flushed_event, INFINITE
633 );
634
635
636
637 //
638 // waveInReset will make all pending buffer as done.
639 //
640
641 err = waveInReset( wavein_handle );
642
643
644 if ( err != MMSYSERR_NOERROR )
645 {
646
647 //TODO: throw error
648
649 MessageBox( 0, TEXT("waveInReset Error."), 0, 0 );
650
651
652
653 }
654
655
656
657 //
658 // Stop recording.
659 //
660
661 err = waveInStop( wavein_handle );
662
663
664 if ( err != MMSYSERR_NOERROR )
665 {
666
667 //TODO: throw error
668
669 MessageBox( 0, TEXT("waveInStop Error."), 0, 0 );
670
671
672
673 }
674
675
676 //
677 // The waveInUnprepareHeader function cleans up the
678 // preparation performed by the waveInPrepareHeader function.
679 //
680
681 unprep_headers_();
682
683
684
685
686
687
688
689
690
691
692
693 status = WAVEIN_STOP;
694
695 }
696
697
698
699 DWORD WINAPI
700 audio_wavein::recording_procedure( LPVOID arg )
701 {
702
703
704 MSG msg;
705 WAVEHDR * phdr;
706 DWORD wait;
707 audio_wavein * _this = ( audio_wavein * ) arg;
708
709
710
711
712 //
713 // Check the arg pointer
714 //
715
716 if ( _this == 0 )
717 return 0;
718
719
720
721 //
722 // The thread can go to sleep for now.
723 // It will be wake up only when there is audio data
724 // to be recorded.
725 //
726
727 if ( _this->wakeup_recthread )
728 wait = WaitForSingleObject(
729 _this->wakeup_recthread, INFINITE
730 );
731
732
733
734
735
736
737 //
738 // If status of the `audio_wavein' object
739 // is not ready or recording the thread can exit.
740 //
741
742 if (( _this->status != WAVEIN_READY ) &&
743 ( _this->status != WAVEIN_RECORDING ))
744 return 0;
745
746
747
748
749
750
751
752 //
753 // Entering main polling loop
754 //
755
756 while ( GetMessage( &msg, 0, 0, 0 ))
757 {
758
759 switch ( msg.message )
760 {
761
762 case MM_WIM_DATA:
763
764 phdr = ( WAVEHDR * ) msg.lParam;
765
766 if (( _this->status == WAVEIN_RECORDING )
767 || ( _this->status == WAVEIN_FLUSHING ))
768 {
769
770 //
771 // Flushes recorded audio data to
772 // the `audio_receiver' object.
773 //
774
775 _this->audio_rcvd.audio_receive(
776 ( unsigned char * )phdr->lpData,
777 phdr->dwBytesRecorded
778 );
779
780
781 //
782 // Updating `audio_receiver' total
783 // bytes received _AFTER_ calling
784 // `audio_receive' function.
785 //
786
787 _this->audio_rcvd.bytes_received +=
788 phdr->dwBytesRecorded;
789
790
791
792
793 //
794 // If status is not flushing data, then
795 // we can re-add the buffer for reusing it.
796 // Otherwise, if we are flushing pending data,
797 // we cannot re-add buffer because we don't need
798 // it anymore
799 //
800
801 if ( _this->status != WAVEIN_FLUSHING )
802 {
803
804 //
805 // Let the audio driver reuse the buffer
806 //
807
808 waveInAddBuffer( _this->wavein_handle,
809 phdr, sizeof( WAVEHDR ));
810
811
812 } else {
813
814 //
815 // If we are flushing pending data, we have
816 // to prepare to stop recording.
817 // Set WAVEHDR flag to 0, and fires the event
818 // `data_flushed_event', that will wake up
819 // the main thread that is sleeping into
820 // wavein_in::stop_recording() member function,
821 // waiting the last `MM_WIM_DATA' message that
822 // contain pending data.
823 //
824
825
826
827
828 phdr->dwFlags = 0;
829
830 SetEvent( _this->data_flushed_event );
831
832
833 //
834 // The recording is gooing to stop, so the
835 // recording thread can go to sleep!
836 //
837
838 wait = WaitForSingleObject(
839 _this->wakeup_recthread, INFINITE );
840
841 }
842
843
844 }//if WAVEIN_RECORDING || WAVEIN_FLUSHING
845
846 break;
847
848
849
850
851
852
853
854
855
856 case MM_WIM_CLOSE:
857
858 //
859 // The thread can exit now.
860 //
861
862 return 0;
863
864 break;
865
866
867
868 } //end switch( msg.message )
869
870 } //end while( GetMessage( ... ))
871
872 return 0;
873 }
874
875
876
877
878
879
880 _AUDIO_NAMESPACE_END_