mirror of
https://github.com/reactos/reactos.git
synced 2024-11-25 12:33:34 +08:00
475 lines
15 KiB
C++
475 lines
15 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 length 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;
|
|
|
|
if (!main_buffer)
|
|
{
|
|
/* TODO; throw error, or assert */
|
|
return;
|
|
}
|
|
|
|
/* 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 */
|
|
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 */
|
|
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;
|
|
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)
|
|
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))
|
|
{
|
|
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
|
|
{
|
|
/* 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)
|
|
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_
|