/* PROJECT: ReactOS sndrec32 * LICENSE: GPL - See COPYING in the top level directory * FILE: base/applications/sndrec32/audio_wavein.cpp * PURPOSE: Sound recording * PROGRAMMERS: Marco Pagliaricci (irc: rendar) */ #include "stdafx.h" #include "audio_wavein.hpp" _AUDIO_NAMESPACE_START_ void audio_wavein::init_( void ) { ZeroMemory(( LPVOID ) &wave_format, sizeof( WAVEFORMATEX )); wave_format.cbSize = sizeof( WAVEFORMATEX ); wavein_handle = 0; recthread_id = 0; wakeup_recthread = 0; data_flushed_event = 0; buf_secs = _AUDIO_DEFAULT_WAVEINBUFSECS; status = WAVEIN_NOTREADY; } void audio_wavein::alloc_buffers_mem_( unsigned int buffs, float secs ) { unsigned int onebuf_size = 0, tot_size = 0; // // Release old memory // if ( main_buffer ) delete[] main_buffer; if ( wave_headers ) delete[] wave_headers; // // Calcs size of the buffers // onebuf_size = ( unsigned int ) (( float )aud_info.byte_rate() * secs ); tot_size = onebuf_size * buffs; // // Allocs memory for the audio buffers // main_buffer = new BYTE [ tot_size ]; // // Allocs memory for the `WAVEHDR' structures. // wave_headers = ( WAVEHDR * ) new BYTE [ sizeof( WAVEHDR ) * buffs ]; // // Zeros memory. // ZeroMemory( main_buffer, tot_size ); ZeroMemory( wave_headers, sizeof( WAVEHDR ) * buffs ); // // Updates total size of the buffers. // mb_size = tot_size; } void audio_wavein::free_buffers_mem_( void ) { // // Frees memory // if ( main_buffer ) delete[] main_buffer; if ( wave_headers ) delete[] wave_headers; main_buffer = 0; wave_headers = 0; } void audio_wavein::init_headers_( void ) { // // If there is no memory for memory or // headers, simply return. // if (( !wave_headers ) || ( !main_buffer )) return; // // This is the size for one buffer // DWORD buf_sz = mb_size / buffers; // // This is the base address for one buffer // BYTE * buf_addr = main_buffer; // // Initializes headers. // for ( unsigned int i = 0; i < buffers; ++i ) { wave_headers[ i ].dwBufferLength = mb_size / buffers; wave_headers[ i ].lpData = ( LPSTR ) buf_addr; buf_addr += buf_sz; } } void audio_wavein::prep_headers_( void ) { MMRESULT err; bool error = false; // // If there is no memory for memory or // headers, throw error. // if (( !wave_headers ) || ( !main_buffer ) || ( !wavein_handle )) {} //TODO: throw error! for ( unsigned int i = 0; i < buffers; ++i ) { err = waveInPrepareHeader( wavein_handle, &wave_headers[ i ], sizeof( WAVEHDR )); if ( err != MMSYSERR_NOERROR ) error = true; } if ( error ) MessageBox( 0, TEXT("waveInPrepareHeader Error."), 0, 0 ); } void audio_wavein::unprep_headers_( void ) { MMRESULT err; bool error = false; // // If there is no memory for memory or // headers, throw error. // if (( !wave_headers ) || ( !main_buffer ) || ( !wavein_handle )) {} //TODO: throw error! for ( unsigned int i = 0; i < buffers; ++i ) { err = waveInUnprepareHeader( wavein_handle, &wave_headers[ i ], sizeof( WAVEHDR )); if ( err != MMSYSERR_NOERROR ) error = true; } if ( error ) MessageBox( 0, TEXT("waveInUnPrepareHeader Error."), 0, 0 ); } void audio_wavein::add_buffers_to_driver_( void ) { MMRESULT err; bool error = false; // // If there is no memory for memory or // headers, throw error. // if (( !wave_headers ) || ( !main_buffer ) || ( !wavein_handle )) {} //TODO: throw error! for ( unsigned int i = 0; i < buffers; ++i ) { err = waveInAddBuffer( wavein_handle, &wave_headers[ i ], sizeof( WAVEHDR )); if ( err != MMSYSERR_NOERROR ) error = true; } if ( error ) MessageBox( 0, TEXT("waveInAddBuffer Error."), 0, 0 ); } void audio_wavein::close( void ) { // // If wavein object is already in the status // NOTREADY, nothing to do. // if ( status == WAVEIN_NOTREADY ) return; // // If the wavein is recording, // then stop recording and close it. // if ( status == WAVEIN_RECORDING ) stop_recording(); // // Updating status. // status = WAVEIN_NOTREADY; // // Wakeing up recording thread, so it // can receive the `MM_WIM_CLOSE' message // then dies. // if ( wakeup_recthread ) SetEvent( wakeup_recthread ); // // Closing wavein stream // while (( waveInClose( wavein_handle )) != MMSYSERR_NOERROR ) Sleep( 1 ); // // Release buffers memory. // free_buffers_mem_(); // // Re-initialize variables to the // initial state. // init_(); } void audio_wavein::open( void ) { MMRESULT err; HANDLE recthread_handle = 0; // // Checkin the status of the object // if ( status != WAVEIN_NOTREADY ) {} //TODO: throw error // // Creating the EVENT object that will be signaled // when the recording thread has to wake up. // wakeup_recthread = CreateEvent( 0, FALSE, FALSE, 0 ); data_flushed_event = CreateEvent( 0, FALSE, FALSE, 0 ); if (( !wakeup_recthread ) || ( !data_flushed_event )) { status = WAVEIN_ERR; MessageBox( 0, TEXT("Thread Error."), 0, 0 ); //TODO: throw error } // // Inialize buffers for recording audio // data from the wavein audio line. // alloc_buffers_mem_( buffers, buf_secs ); init_headers_(); // // Sound format that will be captured by wavein // wave_format.wFormatTag = WAVE_FORMAT_PCM; wave_format.nChannels = aud_info.channels(); wave_format.nSamplesPerSec = aud_info.sample_rate(); wave_format.wBitsPerSample = aud_info.bits(); wave_format.nBlockAlign = aud_info.block_align(); wave_format.nAvgBytesPerSec = aud_info.byte_rate(); // // Creating the recording thread // recthread_handle = CreateThread( NULL, 0, audio_wavein::recording_procedure, ( PVOID ) this, 0, &recthread_id ); // // Checking thread handle // if ( !recthread_handle ) { // // Updating status // status = WAVEIN_ERR; MessageBox( 0, TEXT("Thread Error."), 0, 0 ); //TODO: throw error } // // We don't need the thread handle anymore, // so we can close it from now. (We'll just // need the thread ID for the `waveInOpen' API) // CloseHandle( recthread_handle ); // // Opening audio line wavein // err = waveInOpen( &wavein_handle, 0, &wave_format, recthread_id, 0, CALLBACK_THREAD ); if ( err != MMSYSERR_NOERROR ) { // // Updating status // status = WAVEIN_ERR; if ( err == WAVERR_BADFORMAT ) MessageBox( 0, TEXT("waveInOpen Error"), 0, 0 ); //TODO: throw error } // // Update object status // status = WAVEIN_READY; // // Now `audio_wavein' object is ready // for audio recording! // } void audio_wavein::start_recording( void ) { MMRESULT err; BOOL ev; if (( status != WAVEIN_READY ) && ( status != WAVEIN_STOP )) {} //TODO: throw error // // Updating to the recording status // status = WAVEIN_RECORDING; // // Let's prepare header of type WAVEHDR that // we will pass to the driver with our // audio informations, and buffer informations. // prep_headers_(); // // The waveInAddBuffer function sends an input buffer // to the given waveform-audio input device. // When the buffer is filled, the application is notified. // add_buffers_to_driver_(); // // Signaling event for waking up // the recorder thread. // ev = SetEvent( wakeup_recthread ); if ( !ev ) { MessageBox( 0, TEXT("Event Error."), 0, 0 ); } // // Start recording // err = waveInStart( wavein_handle ); if ( err != MMSYSERR_NOERROR ) { // // Updating status // status = WAVEIN_ERR; MessageBox( 0, TEXT("waveInStart Error."), 0, 0 ); //TODO: throw error } } void audio_wavein::stop_recording( void ) { MMRESULT err; DWORD wait; if ( status != WAVEIN_RECORDING ) return; status = WAVEIN_FLUSHING; // // waveInReset will make all pending buffer as done. // err = waveInReset( wavein_handle ); if ( err != MMSYSERR_NOERROR ) { //TODO: throw error MessageBox( 0, TEXT("waveInReset Error."), 0, 0 ); } if ( data_flushed_event ) wait = WaitForSingleObject( data_flushed_event, INFINITE ); // // Stop recording. // err = waveInStop( wavein_handle ); if ( err != MMSYSERR_NOERROR ) { //TODO: throw error MessageBox( 0, TEXT("waveInStop Error."), 0, 0 ); } // // The waveInUnprepareHeader function cleans up the // preparation performed by the waveInPrepareHeader function. // unprep_headers_(); status = WAVEIN_STOP; } DWORD WINAPI audio_wavein::recording_procedure( LPVOID arg ) { MSG msg; WAVEHDR * phdr; DWORD wait; audio_wavein * _this = ( audio_wavein * ) arg; // // Check the arg pointer // if ( _this == 0 ) return 0; // // The thread can go to sleep for now. // It will be wake up only when there is audio data // to be recorded. // if ( _this->wakeup_recthread ) wait = WaitForSingleObject( _this->wakeup_recthread, INFINITE ); // // If status of the `audio_wavein' object // is not ready or recording the thread can exit. // if (( _this->status != WAVEIN_READY ) && ( _this->status != WAVEIN_RECORDING )) return 0; // // Entering main polling loop // while ( GetMessage( &msg, 0, 0, 0 )) { switch ( msg.message ) { case MM_WIM_DATA: phdr = ( WAVEHDR * ) msg.lParam; if (( _this->status == WAVEIN_RECORDING ) || ( _this->status == WAVEIN_FLUSHING )) { if ( phdr->dwFlags & WHDR_DONE ) { // // Flushes recorded audio data to // the `audio_receiver' object. // _this->audio_rcvd.audio_receive( ( unsigned char * )phdr->lpData, phdr->dwBytesRecorded ); // // Updating `audio_receiver' total // bytes received _AFTER_ calling // `audio_receive' function. // _this->audio_rcvd.bytes_received += phdr->dwBytesRecorded; } // // If status is not flushing data, then // we can re-add the buffer for reusing it. // Otherwise, if we are flushing pending data, // we cannot re-add buffer because we don't need // it anymore // if ( _this->status != WAVEIN_FLUSHING ) { // // Let the audio driver reuse the buffer // waveInAddBuffer( _this->wavein_handle, phdr, sizeof( WAVEHDR )); } else { // // If we are flushing pending data, we have // to prepare to stop recording. // Set WAVEHDR flag to 0, and fires the event // `data_flushed_event', that will wake up // the main thread that is sleeping into // wavein_in::stop_recording() member function, // waiting the last `MM_WIM_DATA' message that // contain pending data. // phdr->dwFlags = 0; SetEvent( _this->data_flushed_event ); // // The recording is gooing to stop, so the // recording thread can go to sleep! // wait = WaitForSingleObject( _this->wakeup_recthread, INFINITE ); } }//if WAVEIN_RECORDING || WAVEIN_FLUSHING break; case MM_WIM_CLOSE: // // The thread can exit now. // return 0; break; } //end switch( msg.message ) } //end while( GetMessage( ... )) return 0; } _AUDIO_NAMESPACE_END_