📄 sound_win32.cxx
字号:
}
BOOL PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::WaitForPlayCompletion()
{
while (!HasPlayCompleted()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return FALSE;
}
return TRUE;
}
BOOL PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::IsRecordBufferFull()
{
PWaitAndSignal mutex(bufferMutex);
return (buffers[bufferIndex].header.dwFlags&WHDR_DONE) != 0 &&
buffers[bufferIndex].header.dwBytesRecorded > 0;
}
BOOL PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::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 PSoundChannelWin32::Abort()
{
DWORD osError = MMSYSERR_NOERROR;
if (hWaveOut != NULL)
osError = waveOutReset(hWaveOut);
if (hWaveIn != NULL)
osError = waveInReset(hWaveIn);
{
PWaitAndSignal mutex(bufferMutex);
if (hWaveOut != NULL || hWaveIn != NULL) {
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 PSoundChannelWin32::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;
}
BOOL PSoundChannelWin32::SetVolume(unsigned newVolume)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF);
DWORD rawVolume = newVolume*65536/100;
if (rawVolume > 65535)
rawVolume = 65535;
WAVEOUTCAPS caps;
if (waveOutGetDevCaps((UINT) hWaveOut, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
// If the device does not support L/R volume only the low word matters
if ((caps.dwSupport & WAVECAPS_LRVOLUME) != 0) {
// Mantain balance
DWORD oldVolume = 0;
if (waveOutGetVolume(hWaveOut, &oldVolume) == MMSYSERR_NOERROR) {
// GetVolume() is supposed to return the value we intended to set.
// So do the proper calculations
// 1. (L + R) / 2 = rawVolume -> GetVolume() formula
// 2. L / R = oldL / oldR -> Unmodified balance
// Being:
// oldL = LOWORD(oldVolume)
// oldR = HIWORD(oldVolume)
DWORD rVol, lVol;
DWORD oldL, oldR;
// Old volume values
oldL = LOWORD(oldVolume);
oldR = HIWORD(oldVolume);
lVol = rVol = 0;
// First sort out extreme cases
if ( oldL == oldR )
rVol = lVol = rawVolume;
else if ( oldL == 0 )
rVol = rawVolume;
else if ( oldR == 0 )
lVol = rawVolume;
else {
#ifndef _WIN32_WCE
rVol = ::MulDiv( 2 * rawVolume, oldR, oldL + oldR );
lVol = ::MulDiv( rVol, oldL, oldR );
#else
rVol = 2 * rawVolume * oldR / ( oldL + oldR );
lVol = rVol * oldL / oldR;
#endif
}
rawVolume = MAKELPARAM(lVol, rVol);
}
else {
// Couldn't get current volume. Assume centered balance
rawVolume = MAKELPARAM(rawVolume, rawVolume);
}
}
}
else {
// Couldn't get device caps. Assume centered balance
// If the device does not support independant L/R volume
// the high-order word (R) is ignored
rawVolume = MAKELPARAM(rawVolume, rawVolume);
}
if (direction == Recorder) {
// Does not appear to be an input volume!!
}
else {
DWORD osError = waveOutSetVolume(hWaveOut, rawVolume);
if (osError != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
}
return TRUE;
}
BOOL PSoundChannelWin32::GetVolume(unsigned & oldVolume)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF);
DWORD rawVolume = 0;
if (direction == Recorder) {
// Does not appear to be an input volume!!
}
else {
DWORD osError = waveOutGetVolume(hWaveOut, &rawVolume);
if (osError != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
}
WAVEOUTCAPS caps;
if (waveOutGetDevCaps((UINT) hWaveOut, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
(caps.dwSupport & WAVECAPS_LRVOLUME) != 0)
rawVolume = (HIWORD(rawVolume) + LOWORD(rawVolume)) / 2;
oldVolume = rawVolume*100/65536;
return TRUE;
}
// End of File ///////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -