mirror of
https://github.com/reactos/reactos.git
synced 2024-11-10 08:43:28 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
887 lines
14 KiB
C++
887 lines
14 KiB
C++
/* 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_
|