📄 sound_win32.cxx
字号:
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 PTrue;
}
PBoolean PSoundChannelWin32::PlaySound(const PSound & sound, PBoolean wait)
{
Abort();
PBoolean ok = PFalse;
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 = PFalse;
}
bufferMutex.Signal();
}
SetFormat(numChannels, sampleRate, bitsPerSample);
SetBuffers(bufferSize, bufferCount);
return ok;
}
PBoolean PSoundChannelWin32::PlayFile(const PFilePath & filename, PBoolean 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 PFalse;
}
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) {
osError = ::GetLastError();
SetFormat(numChannels, sampleRate, bitsPerSample);
return SetErrorValues(Miscellaneous, osError|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 (osError != MMSYSERR_NOERROR) {
SetFormat(numChannels, sampleRate, bitsPerSample);
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
}
if (dataSize == 0 && wait) {
WaitForPlayCompletion();
SetFormat(numChannels, sampleRate, bitsPerSample);
}
return PTrue;
}
PBoolean PSoundChannelWin32::HasPlayCompleted()
{
PWaitAndSignal mutex(bufferMutex);
for (PINDEX i = 0; i < buffers.GetSize(); i++) {
if ((buffers[i].header.dwFlags&WHDR_DONE) == 0)
return PFalse;
}
return PTrue;
}
PBoolean PSoundChannelWin32::WaitForPlayCompletion()
{
while (!HasPlayCompleted()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return PFalse;
}
return PTrue;
}
PBoolean PSoundChannelWin32::StartRecording()
{
PWaitAndSignal mutex(bufferMutex);
// See if has started already.
if (bufferByteOffset != P_MAX_INDEX)
return PTrue;
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 PFalse;
if ((osError = waveInAddBuffer(hWaveIn, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
return PFalse;
}
bufferByteOffset = 0;
if ((osError = waveInStart(hWaveIn)) == MMSYSERR_NOERROR) // start recording
return PTrue;
bufferByteOffset = P_MAX_INDEX;
return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
}
PBoolean PSoundChannelWin32::Read(void * data, PINDEX size)
{
lastReadCount = 0;
if (hWaveIn == NULL)
return SetErrorValues(NotOpen, EBADF, LastReadError);
if (!WaitForRecordBufferFull())
return PFalse;
PWaitAndSignal mutex(bufferMutex);
// Check to see if Abort() was called in another thread
if (bufferByteOffset == P_MAX_INDEX)
return PFalse;
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 PTrue;
}
PBoolean PSoundChannelWin32::RecordSound(PSound & sound)
{
if (!WaitForAllRecordBuffersFull())
return PFalse;
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 PTrue;
}
PBoolean PSoundChannelWin32::RecordFile(const PFilePath & filename)
{
if (!WaitForAllRecordBuffersFull())
return PFalse;
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 PTrue;
}
PBoolean PSoundChannelWin32::IsRecordBufferFull()
{
PWaitAndSignal mutex(bufferMutex);
return (buffers[bufferIndex].header.dwFlags&WHDR_DONE) != 0 &&
buffers[bufferIndex].header.dwBytesRecorded > 0;
}
PBoolean 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 PFalse;
}
return PTrue;
}
PBoolean PSoundChannelWin32::WaitForRecordBufferFull()
{
if (!StartRecording()) // Start the first read, queue all the buffers
return PFalse;
while (!IsRecordBufferFull()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return PFalse;
PWaitAndSignal mutex(bufferMutex);
if (bufferByteOffset == P_MAX_INDEX)
return PFalse;
}
return PTrue;
}
PBoolean PSoundChannelWin32::WaitForAllRecordBuffersFull()
{
if (!StartRecording()) // Start the first read, queue all the buffers
return PFalse;
while (!AreAllRecordBuffersFull()) {
if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
return PFalse;
PWaitAndSignal mutex(bufferMutex);
if (bufferByteOffset == P_MAX_INDEX)
return PFalse;
}
return PTrue;
}
PBoolean 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 PTrue;
}
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;
}
PBoolean PSoundChannelWin32::SetVolume(unsigned newVolume)
{
if (!IsOpen() || hMixer == NULL)
return SetErrorValues(NotOpen, EBADF);
MIXERCONTROLDETAILS_UNSIGNED volume;
volume.dwValue = newVolume*(volumeControl.Bounds.dwMaximum - volumeControl.Bounds.dwMinimum)/100 + volumeControl.Bounds.dwMinimum;
if (volume.dwValue > volumeControl.Bounds.dwMaximum)
volume.dwValue = volumeControl.Bounds.dwMaximum;
else if (volume.dwValue < volumeControl.Bounds.dwMinimum)
volume.dwValue = volumeControl.Bounds.dwMinimum;
MIXERCONTROLDETAILS details;
details.cbStruct = sizeof(details);
details.dwControlID = volumeControl.dwControlID;
details.cChannels = 1;
details.cMultipleItems = 0;
details.cbDetails = sizeof(volume);
details.paDetails = &volume;
MMRESULT result = mixerSetControlDetails((HMIXEROBJ)hMixer, &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
if (result != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, result|PWIN32ErrorFlag);
return true;
}
PBoolean PSoundChannelWin32::GetVolume(unsigned & oldVolume)
{
if (!IsOpen() || hMixer == NULL)
return SetErrorValues(NotOpen, EBADF);
MIXERCONTROLDETAILS_UNSIGNED volume;
MIXERCONTROLDETAILS details;
details.cbStruct = sizeof(details);
details.dwControlID = volumeControl.dwControlID;
details.cChannels = 1;
details.cMultipleItems = 0;
details.cbDetails = sizeof(volume);
details.paDetails = &volume;
MMRESULT result = mixerSetControlDetails((HMIXEROBJ)hMixer, &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
if (result != MMSYSERR_NOERROR)
return SetErrorValues(Miscellaneous, result|PWIN32ErrorFlag);
oldVolume = 100*(volume.dwValue - volumeControl.Bounds.dwMinimum)/(volumeControl.Bounds.dwMaximum - volumeControl.Bounds.dwMinimum);
return true;
}
// End of File ///////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -