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