📄 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 + -