* 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;
if (main_buffer)
delete[] main_buffer;
if (wave_headers)
delete[] wave_headers;
onebuf_size = (unsigned int)((float)aud_info.byte_rate() * secs);
tot_size = onebuf_size * buffs;
main_buffer = new BYTE[tot_size];
wave_headers = (WAVEHDR *) new BYTE[sizeof(WAVEHDR) * buffs];
ZeroMemory(main_buffer, tot_size);
ZeroMemory(wave_headers, sizeof(WAVEHDR) * buffs);
mb_size = tot_size;
}
void
audio_waveout::init_headers_(void)
{
if ((!wave_headers) || (!main_buffer))
return;
DWORD buf_sz = mb_size / buffers;
BYTE *buf_addr = main_buffer;
ZeroMemory(wave_headers, sizeof(WAVEHDR) * buffers);
for (unsigned int i = 0; i < buffers; ++i)
{
wave_headers[i].dwBufferLength = mb_size / buffers;
wave_headers[i].lpData = (LPSTR) buf_addr;
wave_headers[i].dwFlags &= ~WHDR_DONE;
wave_headers[i].dwUser = (unsigned int)i;
buf_addr += buf_sz;
}
}
void
audio_waveout::prep_headers_(void)
{
MMRESULT err;
bool error = false;
if ((!wave_headers) || (!main_buffer) || (!waveout_handle))
{
}
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)
{
}
}
void
audio_waveout::unprep_headers_(void)
{
MMRESULT err;
bool error = false;
if ((!wave_headers) || (!main_buffer) || (!waveout_handle))
{
}
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)
{
}
}
void
audio_waveout::free_buffers_mem_(void)
{
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;
if (status != WAVEOUT_NOTREADY)
{
}
the playing thread has to wake up */
wakeup_playthread = CreateEvent(0, FALSE, FALSE, 0);
if (!wakeup_playthread)
{
status = WAVEOUT_ERR;
}
alloc_buffers_mem_(buffers, buf_secs);
init_headers_();
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();
playthread_handle = CreateThread(NULL,
0,
audio_waveout::playing_procedure,
(PVOID)this,
0,
&playthread_id);
if (!playthread_handle)
{
status = WAVEOUT_ERR;
}
(We'll just need the thread ID for the `waveInOpen' API) */
CloseHandle(playthread_handle);
audio_buf.set_position_start();
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);
}
status = WAVEOUT_READY;
}
void
audio_waveout::play(void)
{
MMRESULT err;
unsigned int i;
if (!main_buffer)
{
return;
}
if (status == WAVEOUT_PAUSED)
{
status = WAVEOUT_PLAYING;
waveOutRestart(waveout_handle);
SetEvent(wakeup_playthread);
return;
}
if (status != WAVEOUT_READY)
return;
prep_headers_();
status = WAVEOUT_PLAYING;
size of all little buffers */
audio_buf.read(main_buffer, mb_size);
SetEvent(wakeup_playthread);
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);
}
}
}
void
audio_waveout::pause(void)
{
MMRESULT err;
if (status == WAVEOUT_PLAYING)
{
status = WAVEOUT_PAUSED;
err = waveOutPause(waveout_handle);
if (err != MMSYSERR_NOERROR)
{
MessageBox(0, _T("waveOutPause Error"), 0, 0);
}
}
}
void
audio_waveout::stop(void)
{
MMRESULT err;
if ((status != WAVEOUT_PLAYING) &&
(status != WAVEOUT_FLUSHING) &&
(status != WAVEOUT_PAUSED))
{
return;
}
status = WAVEOUT_STOP;
err = waveOutReset( waveout_handle );
if (err != MMSYSERR_NOERROR)
{
MessageBox(0, _T("err WaveOutReset.\n"),_T("ERROR"), 0);
}
audio_buf.set_position_start();
unprep_headers_();
init_headers_();
status = WAVEOUT_READY;
}
void
audio_waveout::close(void)
{
MMRESULT err;
we have to call the `stop' member function, to flush pending buffers */
if ((status == WAVEOUT_PLAYING) || (status== WAVEOUT_PAUSED))
{
stop();
}
err = waveOutClose(waveout_handle);
if (err != MMSYSERR_NOERROR)
{
MessageBox(0, _T("waveOutClose Error"), 0, 0);
}
free_buffers_mem_();
}
DWORD WINAPI
audio_waveout::playing_procedure(LPVOID arg)
{
MSG msg;
WAVEHDR *phdr;
MMRESULT err;
audio_waveout *_this = (audio_waveout *)arg;
unsigned int read_size;
if (_this == 0)
return 0;
there is audio data to be recorded */
if (_this->wakeup_playthread)
WaitForSingleObject(_this->wakeup_playthread, INFINITE);
while (GetMessage(&msg, 0, 0, 0))
{
switch (msg.message)
{
case MM_WOM_DONE:
phdr = (WAVEHDR *)msg.lParam;
than playing, then the thread can go to sleep */
if ((_this->status != WAVEOUT_PLAYING) &&
(_this->status != WAVEOUT_FLUSHING) &&
(_this->wakeup_playthread))
{
WaitForSingleObject(_this->wakeup_playthread, INFINITE);
}
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)
{
read new audio data from the `audio_producer' object */
read_size = _this->audio_buf.read((BYTE *)phdr->lpData,
phdr->dwBufferLength);
} else
read_size = 0;
audio data, so `read_size' will be > 0 */
if (read_size)
{
phdr->dwBufferLength = read_size;
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;
err = waveOutWrite(_this->waveout_handle,
phdr,
sizeof(WAVEHDR));
if (err != MMSYSERR_NOERROR)
{
MessageBox(0, _T("waveOutWrite Error"), 0, 0);
}
}
else
{
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)
{
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))
{
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();
if (_this->audio_buf.play_finished)
_this->audio_buf.play_finished();
if (_this->wakeup_playthread)
WaitForSingleObject(_this->wakeup_playthread,
INFINITE);
}
}
}
break;
case MM_WOM_CLOSE:
return 0;
break;
case MM_WOM_OPEN:
break;
}
}
return 0;
}
_AUDIO_NAMESPACE_END_