📄 sound.cxx
字号:
BOOL ok = TRUE;
PWaitAndSignal mutex(bufferMutex);
if (!buffers.SetSize(count))
ok = FALSE;
else {
for (PINDEX i = 0; i < count; i++) {
if (buffers.GetAt(i) == NULL)
buffers.SetAt(i, new PWaveBuffer(size));
if (!buffers[i].SetSize(size))
ok = FALSE;
}
}
bufferByteOffset = P_MAX_INDEX;
bufferIndex = 0;
return ok;
}
BOOL PSoundChannel::GetBuffers(PINDEX & size, PINDEX & count)
{
PWaitAndSignal mutex(bufferMutex);
count = buffers.GetSize();
if (count == 0)
size = 0;
else
size = buffers[0].GetSize();
return TRUE;
}
BOOL PSoundChannel::Write(const void * data, PINDEX size)
{
lastWriteCount = 0;
if (hWaveOut == NULL)
return SetErrorValues(NotOpen, EBADF, LastWriteError);
const BYTE * ptr = (const BYTE *)data;
bufferMutex.Wait();
DWORD osError = MMSYSERR_NOERROR;
while (size > 0) {
PWaveBuffer & buffer = buffers[bufferIndex];
while ((buffer.header.dwFlags&WHDR_DONE) == 0) {
bufferMutex.Signal();
// No free buffers, so wait for one
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return SetErrorValues(Miscellaneous, ::GetLastError()|PWIN32ErrorFlag, LastWriteError);
bufferMutex.Wait();
}
// Can't write more than a buffer full
PINDEX count = size;
if ((osError = buffer.Prepare(hWaveOut, count)) != MMSYSERR_NOERROR)
break;
memcpy(buffer.GetPointer(), ptr, count);
if ((osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
break;
bufferIndex = (bufferIndex+1)%buffers.GetSize();
lastWriteCount += count;
size -= count;
ptr += count;
}
bufferMutex.Signal();
if (size != 0)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
return TRUE;
}
BOOL PSoundChannel::PlaySound(const PSound & sound, BOOL wait)
{
Abort();
BOOL ok = FALSE;
PINDEX bufferSize;
PINDEX bufferCount;
GetBuffers(bufferSize, bufferCount);
unsigned numChannels = waveFormat->nChannels;
unsigned sampleRate = waveFormat->nSamplesPerSec;
unsigned bitsPerSample = waveFormat->wBitsPerSample;
if (sound.GetEncoding() == 0)
ok = SetFormat(sound.GetChannels(), sound.GetSampleRate(), sound.GetSampleSize());
else {
waveFormat.SetFormat(sound.GetFormatInfoData(), sound.GetFormatInfoSize());
ok = OpenDevice(os_handle);
}
if (ok) {
bufferMutex.Wait();
// To avoid lots of copying of sound data, we fake the PSound buffer into
// the internal buffers and play directly from the PSound object.
buffers.SetSize(1);
PWaveBuffer & buffer = buffers[0];
buffer = sound;
DWORD osError;
PINDEX count = sound.GetSize();
if ((osError = buffer.Prepare(hWaveOut, count)) == MMSYSERR_NOERROR &&
(osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) == MMSYSERR_NOERROR) {
if (wait)
ok = WaitForPlayCompletion();
}
else {
SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
ok = FALSE;
}
bufferMutex.Signal();
}
SetFormat(numChannels, sampleRate, bitsPerSample);
SetBuffers(bufferSize, bufferCount);
return ok;
}
BOOL PSoundChannel::PlayFile(const PFilePath & filename, BOOL wait)
{
Abort();
PMultiMediaFile mmio;
PWaveFormat fileFormat;
DWORD dataSize;
if (!mmio.OpenWaveFile(filename, fileFormat, dataSize))
return SetErrorValues(NotOpen, mmio.GetLastError()|PWIN32ErrorFlag, LastWriteError);
// Save old format and set to one loaded from file.
unsigned numChannels = waveFormat->nChannels;
unsigned sampleRate = waveFormat->nSamplesPerSec;
unsigned bitsPerSample = waveFormat->wBitsPerSample;
waveFormat = fileFormat;
if (!OpenDevice(os_handle)) {
SetFormat(numChannels, sampleRate, bitsPerSample);
return FALSE;
}
bufferMutex.Wait();
DWORD osError = MMSYSERR_NOERROR;
while (dataSize > 0) {
PWaveBuffer & buffer = buffers[bufferIndex];
while ((buffer.header.dwFlags&WHDR_DONE) == 0) {
bufferMutex.Signal();
// No free buffers, so wait for one
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0) {
SetFormat(numChannels, sampleRate, bitsPerSample);
return SetErrorValues(Miscellaneous, ::GetLastError()|PWIN32ErrorFlag, LastWriteError);
}
bufferMutex.Wait();
}
// Can't write more than a buffer full
PINDEX count = dataSize;
if ((osError = buffer.Prepare(hWaveOut, count)) != MMSYSERR_NOERROR)
break;
// Read the waveform data subchunk
if (!mmio.Read(buffer.GetPointer(), count)) {
osError = mmio.GetLastError();
break;
}
if ((osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
break;
bufferIndex = (bufferIndex+1)%buffers.GetSize();
dataSize -= count;
}
bufferMutex.Signal();
if (dataSize == 0 && wait)
WaitForPlayCompletion();
SetFormat(numChannels, sampleRate, bitsPerSample);
if (osError != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
return TRUE;
}
BOOL PSoundChannel::HasPlayCompleted()
{
PWaitAndSignal mutex(bufferMutex);
for (PINDEX i = 0; i < buffers.GetSize(); i++) {
if ((buffers[i].header.dwFlags&WHDR_DONE) == 0)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannel::WaitForPlayCompletion()
{
while (!HasPlayCompleted()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannel::StartRecording()
{
PWaitAndSignal mutex(bufferMutex);
// See if has started already.
if (bufferByteOffset != P_MAX_INDEX)
return TRUE;
DWORD osError;
// Start the first read, queue all the buffers
for (PINDEX i = 0; i < buffers.GetSize(); i++) {
PWaveBuffer & buffer = buffers[i];
if ((osError = buffer.Prepare(hWaveIn)) != MMSYSERR_NOERROR)
return FALSE;
if ((osError = waveInAddBuffer(hWaveIn, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
return FALSE;
}
bufferByteOffset = 0;
if ((osError = waveInStart(hWaveIn)) == MMSYSERR_NOERROR) // start recording
return TRUE;
bufferByteOffset = P_MAX_INDEX;
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
}
BOOL PSoundChannel::Read(void * data, PINDEX size)
{
lastReadCount = 0;
if (hWaveIn == NULL)
return SetErrorValues(NotOpen, EBADF, LastReadError);
if (!WaitForRecordBufferFull())
return FALSE;
PWaitAndSignal mutex(bufferMutex);
// Check to see if Abort() was called in another thread
if (bufferByteOffset == P_MAX_INDEX)
return FALSE;
PWaveBuffer & buffer = buffers[bufferIndex];
lastReadCount = buffer.header.dwBytesRecorded - bufferByteOffset;
if (lastReadCount > size)
lastReadCount = size;
memcpy(data, &buffer[bufferByteOffset], lastReadCount);
bufferByteOffset += lastReadCount;
if (bufferByteOffset >= (PINDEX)buffer.header.dwBytesRecorded) {
DWORD osError;
if ((osError = buffer.Prepare(hWaveIn)) != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
if ((osError = waveInAddBuffer(hWaveIn, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
bufferIndex = (bufferIndex+1)%buffers.GetSize();
bufferByteOffset = 0;
}
return TRUE;
}
BOOL PSoundChannel::RecordSound(PSound & sound)
{
if (!WaitForAllRecordBuffersFull())
return FALSE;
sound.SetFormat(waveFormat->nChannels,
waveFormat->nSamplesPerSec,
waveFormat->wBitsPerSample);
PWaitAndSignal mutex(bufferMutex);
if (buffers.GetSize() == 1 &&
(PINDEX)buffers[0].header.dwBytesRecorded == buffers[0].GetSize())
sound = buffers[0];
else {
PINDEX totalSize = 0;
PINDEX i;
for (i = 0; i < buffers.GetSize(); i++)
totalSize += buffers[i].header.dwBytesRecorded;
if (!sound.SetSize(totalSize))
return SetErrorValues(NoMemory, ENOMEM, LastReadError);
BYTE * ptr = sound.GetPointer();
for (i = 0; i < buffers.GetSize(); i++) {
PINDEX sz = buffers[i].header.dwBytesRecorded;
memcpy(ptr, buffers[i], sz);
ptr += sz;
}
}
return TRUE;
}
BOOL PSoundChannel::RecordFile(const PFilePath & filename)
{
if (!WaitForAllRecordBuffersFull())
return FALSE;
PWaitAndSignal mutex(bufferMutex);
PINDEX dataSize = 0;
PINDEX i;
for (i = 0; i < buffers.GetSize(); i++)
dataSize += buffers[i].header.dwBytesRecorded;
PMultiMediaFile mmio;
if (!mmio.CreateWaveFile(filename, waveFormat, dataSize))
return SetErrorValues(Miscellaneous, mmio.GetLastError()|PWIN32ErrorFlag, LastReadError);
for (i = 0; i < buffers.GetSize(); i++) {
if (!mmio.Write(buffers[i], buffers[i].header.dwBytesRecorded))
return SetErrorValues(Miscellaneous, mmio.GetLastError()|PWIN32ErrorFlag, LastReadError);
}
return TRUE;
}
BOOL PSoundChannel::IsRecordBufferFull()
{
PWaitAndSignal mutex(bufferMutex);
return (buffers[bufferIndex].header.dwFlags&WHDR_DONE) != 0 &&
buffers[bufferIndex].header.dwBytesRecorded > 0;
}
BOOL PSoundChannel::AreAllRecordBuffersFull()
{
PWaitAndSignal mutex(bufferMutex);
for (PINDEX i = 0; i < buffers.GetSize(); i++) {
if ((buffers[i].header.dwFlags&WHDR_DONE) == 0 ||
buffers[i].header.dwBytesRecorded == 0)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannel::WaitForRecordBufferFull()
{
if (!StartRecording()) // Start the first read, queue all the buffers
return FALSE;
while (!IsRecordBufferFull()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return FALSE;
PWaitAndSignal mutex(bufferMutex);
if (bufferByteOffset == P_MAX_INDEX)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannel::WaitForAllRecordBuffersFull()
{
if (!StartRecording()) // Start the first read, queue all the buffers
return FALSE;
while (!AreAllRecordBuffersFull()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return FALSE;
PWaitAndSignal mutex(bufferMutex);
if (bufferByteOffset == P_MAX_INDEX)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannel::Abort()
{
DWORD osError = MMSYSERR_NOERROR;
if (hWaveOut != NULL)
osError = waveOutReset(hWaveOut);
if (hWaveIn != NULL)
osError = waveInReset(hWaveIn);
PWaitAndSignal mutex(bufferMutex);
for (PINDEX i = 0; i < buffers.GetSize(); i++) {
while (buffers[i].Release() == WAVERR_STILLPLAYING) {
if (hWaveOut != NULL)
waveOutReset(hWaveOut);
if (hWaveIn != NULL)
waveInReset(hWaveIn);
}
}
bufferByteOffset = P_MAX_INDEX;
bufferIndex = 0;
// Signal any threads waiting on this event, they should then check
// the bufferByteOffset variable for an abort.
SetEvent(hEventDone);
if (osError != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
return TRUE;
}
PString PSoundChannel::GetErrorText(ErrorGroup group) const
{
PString str;
if ((lastErrorNumber[group]&PWIN32ErrorFlag) == 0)
return PChannel::GetErrorText(group);
DWORD osError = lastErrorNumber[group]&~PWIN32ErrorFlag;
if (direction == Recorder) {
if (waveInGetErrorText(osError, str.GetPointer(256), 256) != MMSYSERR_NOERROR)
return PChannel::GetErrorText(group);
}
else {
if (waveOutGetErrorText(osError, str.GetPointer(256), 256) != MMSYSERR_NOERROR)
return PChannel::GetErrorText(group);
}
return str;
}
// End of File ///////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -