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