mirror of
https://github.com/reactos/reactos.git
synced 2024-11-11 01:04:11 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
1003 lines
18 KiB
C++
1003 lines
18 KiB
C++
/* PROJECT: ReactOS sndrec32
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: base/applications/sndrec32/audio_waveout.cpp
|
|
* PURPOSE: Sound recording
|
|
* PROGRAMMERS: Marco Pagliaricci (irc: rendar)
|
|
*/
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "audio_waveout.hpp"
|
|
|
|
|
|
_AUDIO_NAMESPACE_START_
|
|
|
|
|
|
|
|
void
|
|
audio_waveout::init_( void )
|
|
{
|
|
|
|
ZeroMemory(( LPVOID ) &wave_format,
|
|
sizeof( WAVEFORMATEX ));
|
|
|
|
wave_format.cbSize = sizeof( WAVEFORMATEX );
|
|
|
|
waveout_handle = 0;
|
|
|
|
playthread_id = 0;
|
|
wakeup_playthread = 0;
|
|
|
|
buf_secs = _AUDIO_DEFAULT_WAVEOUTBUFSECS;
|
|
|
|
|
|
status = WAVEOUT_NOTREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
audio_waveout::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_waveout::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;
|
|
|
|
|
|
|
|
ZeroMemory( wave_headers, sizeof( WAVEHDR ) * buffers );
|
|
|
|
|
|
//
|
|
// Initializes headers.
|
|
//
|
|
|
|
for ( unsigned int i = 0; i < buffers; ++i )
|
|
{
|
|
|
|
//
|
|
// Sets the correct base address and
|
|
// lenght for the little buffer.
|
|
//
|
|
|
|
wave_headers[ i ].dwBufferLength = mb_size / buffers;
|
|
wave_headers[ i ].lpData = ( LPSTR ) buf_addr;
|
|
|
|
//
|
|
// Unsets the WHDR_DONE flag.
|
|
//
|
|
|
|
wave_headers[ i ].dwFlags &= ~WHDR_DONE;
|
|
|
|
|
|
|
|
//
|
|
// Sets the WAVEHDR user data with an
|
|
// unique little buffer ID#
|
|
//
|
|
|
|
wave_headers[ i ].dwUser = ( unsigned int ) i;
|
|
|
|
|
|
|
|
//
|
|
// Increments little buffer base address.
|
|
//
|
|
|
|
buf_addr += buf_sz;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
audio_waveout::prep_headers_( void )
|
|
{
|
|
MMRESULT err;
|
|
bool error = false;
|
|
|
|
|
|
//
|
|
// If there is no memory for memory or
|
|
// headers, throw error.
|
|
//
|
|
|
|
if (( !wave_headers )
|
|
|| ( !main_buffer ) || ( !waveout_handle ))
|
|
{} //TODO: throw error!
|
|
|
|
|
|
|
|
for ( unsigned int i = 0; i < buffers; ++i )
|
|
{
|
|
err = waveOutPrepareHeader( waveout_handle,
|
|
&wave_headers[ i ], sizeof( WAVEHDR ));
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
if ( error )
|
|
{} //TODO: throw error indicating which
|
|
//header i-th is errorneous
|
|
|
|
|
|
|
|
}
|
|
|
|
void
|
|
audio_waveout::unprep_headers_( void )
|
|
{
|
|
MMRESULT err;
|
|
bool error = false;
|
|
|
|
|
|
|
|
//
|
|
// If there is no memory for memory or
|
|
// headers, throw error.
|
|
//
|
|
|
|
if (( !wave_headers )
|
|
|| ( !main_buffer ) || ( !waveout_handle ))
|
|
{} //TODO: throw error!
|
|
|
|
|
|
|
|
for ( unsigned int i = 0; i < buffers; ++i )
|
|
{
|
|
err = waveOutUnprepareHeader( waveout_handle,
|
|
&wave_headers[ i ], sizeof( WAVEHDR ));
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
if ( error )
|
|
{} //TODO: throw error indicating which
|
|
//header i-th is errorneous
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
audio_waveout::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_waveout::open( void )
|
|
{
|
|
|
|
MMRESULT err;
|
|
HANDLE playthread_handle = 0;
|
|
|
|
|
|
//
|
|
// Checkin the status of the object
|
|
//
|
|
|
|
if ( status != WAVEOUT_NOTREADY )
|
|
{} //TODO: throw error
|
|
|
|
|
|
//
|
|
// Creating the EVENT object that will be signaled
|
|
// when the playing thread has to wake up.
|
|
//
|
|
|
|
wakeup_playthread =
|
|
CreateEvent( 0, FALSE, FALSE, 0 );
|
|
|
|
if ( !wakeup_playthread )
|
|
{
|
|
|
|
|
|
status = WAVEOUT_ERR;
|
|
|
|
//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
|
|
//
|
|
|
|
playthread_handle =
|
|
CreateThread( NULL,
|
|
0,
|
|
audio_waveout::playing_procedure,
|
|
( PVOID ) this,
|
|
0,
|
|
&playthread_id
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Checking thread handle
|
|
//
|
|
|
|
if ( !playthread_handle )
|
|
{
|
|
|
|
//
|
|
// Updating status
|
|
//
|
|
|
|
status = WAVEOUT_ERR;
|
|
//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( playthread_handle );
|
|
|
|
|
|
|
|
//
|
|
// Reset the `audio_source' to the start
|
|
// position.
|
|
//
|
|
|
|
audio_buf.set_position_start();
|
|
|
|
|
|
|
|
|
|
//
|
|
// Opens the WAVE_OUT device.
|
|
//
|
|
|
|
err = waveOutOpen(
|
|
&waveout_handle,
|
|
WAVE_MAPPER,
|
|
&wave_format,
|
|
playthread_id,
|
|
0,
|
|
CALLBACK_THREAD | WAVE_ALLOWSYNC
|
|
);
|
|
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
MessageBox(0, _T("waveOutOpen Error"), 0, 0);
|
|
//TODO: throw error
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = WAVEOUT_READY;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
audio_waveout::play( void )
|
|
{
|
|
|
|
|
|
MMRESULT err;
|
|
unsigned int i;
|
|
BOOL ev;
|
|
|
|
|
|
if ( !main_buffer )
|
|
{ return; } //TODO; throw error, or assert
|
|
|
|
|
|
|
|
|
|
//
|
|
// If the status is PAUSED, we have to
|
|
// resume the audio playing.
|
|
//
|
|
if ( status == WAVEOUT_PAUSED )
|
|
{
|
|
|
|
//
|
|
// Updates status.
|
|
//
|
|
|
|
status = WAVEOUT_PLAYING;
|
|
|
|
|
|
//
|
|
// Tells to the driver to resume
|
|
// audio playing.
|
|
//
|
|
|
|
waveOutRestart( waveout_handle );
|
|
|
|
|
|
//
|
|
// Wakeup playing thread.
|
|
//
|
|
|
|
ev = SetEvent( wakeup_playthread );
|
|
|
|
return;
|
|
|
|
} //if status == WAVEOUT_PAUSED
|
|
|
|
|
|
|
|
|
|
|
|
if ( status != WAVEOUT_READY )
|
|
return;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Prepares WAVEHDR structures.
|
|
//
|
|
|
|
prep_headers_();
|
|
|
|
|
|
|
|
//
|
|
// Sets correct status.
|
|
//
|
|
|
|
status = WAVEOUT_PLAYING;
|
|
|
|
|
|
|
|
//
|
|
// Reads the audio from the start.
|
|
//
|
|
|
|
//audio_buf.set_position_start();
|
|
|
|
|
|
|
|
|
|
//
|
|
// Reads the first N bytes from the audio
|
|
// buffer, where N = the total size of all
|
|
// little buffers.
|
|
//
|
|
|
|
audio_buf.read( main_buffer, mb_size );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Wakeup the playing thread.
|
|
//
|
|
|
|
ev = SetEvent( wakeup_playthread );
|
|
|
|
|
|
|
|
|
|
//
|
|
// Sends all the little buffers to the
|
|
// audio driver, so it can play the sound
|
|
// data.
|
|
//
|
|
|
|
for ( i = 0; i < buffers; ++ i )
|
|
{
|
|
|
|
|
|
err = waveOutWrite( waveout_handle, &wave_headers[ i ], sizeof( WAVEHDR ));
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
|
|
|
|
MessageBox(0, _T("waveOutWrite Error"), 0, 0);
|
|
|
|
//TODO: throw error
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
audio_waveout::pause( void )
|
|
{
|
|
|
|
MMRESULT err;
|
|
|
|
|
|
//
|
|
// If the waveout object is not playing audio,
|
|
// do nothing.
|
|
//
|
|
|
|
if ( status == WAVEOUT_PLAYING )
|
|
{
|
|
|
|
//
|
|
// Updating status.
|
|
//
|
|
|
|
status = WAVEOUT_PAUSED;
|
|
|
|
|
|
//
|
|
// Tells to audio driver to pause audio.
|
|
//
|
|
|
|
err = waveOutPause( waveout_handle );
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
|
|
MessageBox(0, _T("waveOutPause Error"), 0, 0);
|
|
//TODO: throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
audio_waveout::stop( void )
|
|
{
|
|
|
|
MMRESULT err;
|
|
|
|
|
|
//
|
|
// Checks the current status
|
|
//
|
|
|
|
if (( status != WAVEOUT_PLAYING )
|
|
&& ( status != WAVEOUT_FLUSHING )
|
|
&& ( status != WAVEOUT_PAUSED ))
|
|
{
|
|
//
|
|
// Do nothing.
|
|
//
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Sets a new status
|
|
//
|
|
|
|
status = WAVEOUT_STOP;
|
|
|
|
|
|
|
|
//
|
|
// Flushes pending audio datas
|
|
//
|
|
|
|
err = waveOutReset( waveout_handle );
|
|
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
|
|
MessageBox(0, _T("err WaveOutReset.\n"),_T("ERROR"), 0);
|
|
//TODO: throw error
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Sets the start position of the audio
|
|
// buffer.
|
|
//
|
|
|
|
audio_buf.set_position_start();
|
|
|
|
|
|
//
|
|
// Cleans little buffers.
|
|
//
|
|
|
|
unprep_headers_();
|
|
init_headers_();
|
|
|
|
|
|
|
|
//
|
|
// Refreshes the status.
|
|
//
|
|
|
|
status = WAVEOUT_READY;
|
|
|
|
}
|
|
|
|
void
|
|
audio_waveout::close( void )
|
|
{
|
|
|
|
MMRESULT err;
|
|
|
|
|
|
//
|
|
// If the `wave_out' object is playing audio,
|
|
// or it is in paused state, we have to call
|
|
// the `stop' member function, to flush
|
|
// pending buffers.
|
|
//
|
|
|
|
if (( status == WAVEOUT_PLAYING )
|
|
|| ( status== WAVEOUT_PAUSED ))
|
|
{
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// When we have flushed all pending buffers,
|
|
// the wave out handle can be successfully closed.
|
|
//
|
|
|
|
err = waveOutClose( waveout_handle );
|
|
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
|
|
MessageBox(0, _T("waveOutClose Error"), 0, 0);
|
|
//TODO: throw error
|
|
|
|
}
|
|
|
|
free_buffers_mem_();
|
|
|
|
}
|
|
|
|
|
|
DWORD WINAPI
|
|
audio_waveout::playing_procedure( LPVOID arg )
|
|
{
|
|
MSG msg;
|
|
WAVEHDR * phdr;
|
|
DWORD wait;
|
|
MMRESULT err;
|
|
audio_waveout * _this = ( audio_waveout * ) arg;
|
|
unsigned int read_size;
|
|
|
|
|
|
|
|
//
|
|
// 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_playthread )
|
|
wait = WaitForSingleObject(
|
|
_this->wakeup_playthread, INFINITE
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Entering main polling loop
|
|
//
|
|
|
|
while ( GetMessage( &msg, 0, 0, 0 ))
|
|
{
|
|
|
|
switch ( msg.message )
|
|
{
|
|
|
|
case MM_WOM_DONE:
|
|
|
|
phdr = ( WAVEHDR * ) msg.lParam;
|
|
|
|
|
|
//
|
|
// If the status of the `wave_out' object
|
|
// is different than playing, then the thread
|
|
// can go to sleep.
|
|
//
|
|
|
|
if (( _this->status != WAVEOUT_PLAYING ) &&
|
|
( _this->status != WAVEOUT_FLUSHING ) &&
|
|
( _this->wakeup_playthread ))
|
|
{
|
|
|
|
wait = WaitForSingleObject(
|
|
_this->wakeup_playthread,
|
|
INFINITE
|
|
);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// The playing thread doesn't have to sleep,
|
|
// so let's checking first if the little buffer
|
|
// has been sent to the audio driver (it has the
|
|
// WHDR_DONE flag). If it is, we can read new
|
|
// audio datas from the `audio_producer' object,
|
|
// refill the little buffer, and resend it to the
|
|
// driver with waveOutWrite( ) API.
|
|
//
|
|
|
|
if ( phdr->dwFlags & WHDR_DONE )
|
|
{
|
|
|
|
if ( _this->status == WAVEOUT_PLAYING )
|
|
{
|
|
|
|
//
|
|
// Here the thread is still playing a sound,
|
|
// so it can read new audio data from the
|
|
// `audio_producer' object.
|
|
//
|
|
|
|
read_size =
|
|
_this->audio_buf.read(
|
|
( BYTE * ) phdr->lpData,
|
|
phdr->dwBufferLength
|
|
);
|
|
|
|
} else
|
|
read_size = 0;
|
|
|
|
|
|
|
|
//
|
|
// If the `audio_producer' object, has produced some
|
|
// audio data, so `read_size' will be > 0.
|
|
//
|
|
|
|
if ( read_size )
|
|
{
|
|
|
|
//
|
|
// Adjusts the correct effectively read size.
|
|
//
|
|
|
|
phdr->dwBufferLength = read_size;
|
|
|
|
//
|
|
// Before sending the little buffer to the
|
|
// driver, we have to remove the `WHDR_DONE'
|
|
// flag, because the little buffer now contain
|
|
// new audio data that have to be played.
|
|
//
|
|
|
|
phdr->dwFlags &= ~WHDR_DONE;
|
|
|
|
|
|
//
|
|
// Plays the sound of the little buffer.
|
|
//
|
|
|
|
err = waveOutWrite(
|
|
_this->waveout_handle,
|
|
phdr,
|
|
sizeof( WAVEHDR )
|
|
);
|
|
|
|
|
|
//
|
|
// Checking if any error has occured.
|
|
//
|
|
|
|
if ( err != MMSYSERR_NOERROR )
|
|
{
|
|
MessageBox(0, _T("waveOutWrite Error"), 0, 0);
|
|
//TODO: throw error
|
|
}
|
|
|
|
|
|
|
|
|
|
} else { // if ( read_size )
|
|
|
|
|
|
|
|
//
|
|
// Here `read_size' is 0, so the
|
|
// `audio_producer' object, doesn't have any
|
|
// sound data to produce anymore.
|
|
// So, now we have to see the little buffer
|
|
// #ID to establishing what to do.
|
|
//
|
|
|
|
if ( phdr->dwUser == 0 )
|
|
{
|
|
|
|
|
|
|
|
//
|
|
// Here `read_size' is 0, and the buffer
|
|
// user data contain 0, so this is the
|
|
// first of N little buffers that came
|
|
// back with `WHDR_DONE' flag; this means
|
|
// that this is the last little buffer in
|
|
// which we have to read data to; so we
|
|
// can _STOP_ reading data from the
|
|
// `audio_producer' object: doing this is
|
|
// accomplished just setting the current
|
|
// status as "WAVEOUT_FLUSHING".
|
|
//
|
|
|
|
_this->status = WAVEOUT_FLUSHING;
|
|
|
|
|
|
} else if ( phdr->dwUser == ( _this->buffers - 1 )) {
|
|
|
|
|
|
|
|
//
|
|
// Here `read_size' and the buffer user
|
|
// data, that contain a buffer ID#,
|
|
// is equal to the number of the total
|
|
// buffers - 1. This means that this is the
|
|
// _LAST_ little buffer that has been played
|
|
// by the audio driver. We can STOP the
|
|
// `wave_out' object now, or restart the
|
|
// sound playing, if we have a infinite loop.
|
|
//
|
|
|
|
|
|
_this->stop();
|
|
|
|
//
|
|
// Let the thread go to sleep.
|
|
//
|
|
|
|
if ( _this->audio_buf.play_finished )
|
|
_this->audio_buf.play_finished();
|
|
|
|
|
|
if ( _this->wakeup_playthread )
|
|
wait = WaitForSingleObject(
|
|
_this->wakeup_playthread,
|
|
INFINITE
|
|
);
|
|
|
|
} //if ( phdr->dwUser == ( _this->buffers - 1 ))
|
|
|
|
} //if read_size != 0
|
|
|
|
} //( phdr->dwFlags & WHDR_DONE )
|
|
|
|
|
|
break; // end case
|
|
|
|
|
|
|
|
case MM_WOM_CLOSE:
|
|
|
|
//
|
|
// The thread can exit now.
|
|
//
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
case MM_WOM_OPEN:
|
|
|
|
//
|
|
// Do nothing.
|
|
//
|
|
|
|
break;
|
|
|
|
|
|
} //end switch( msg.message )
|
|
|
|
} //end while( GetMessage( ... ))
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
_AUDIO_NAMESPACE_END_
|