Index: webrtc/modules/audio_device/win/audio_device_wave_win.cc |
diff --git a/webrtc/modules/audio_device/win/audio_device_wave_win.cc b/webrtc/modules/audio_device/win/audio_device_wave_win.cc |
deleted file mode 100644 |
index 7874575e7ce166259801f1aa34bfe8f603d9ff9d..0000000000000000000000000000000000000000 |
--- a/webrtc/modules/audio_device/win/audio_device_wave_win.cc |
+++ /dev/null |
@@ -1,3723 +0,0 @@ |
-/* |
- * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
- * |
- * Use of this source code is governed by a BSD-style license |
- * that can be found in the LICENSE file in the root of the source |
- * tree. An additional intellectual property rights grant can be found |
- * in the file PATENTS. All contributing project authors may |
- * be found in the AUTHORS file in the root of the source tree. |
- */ |
- |
-#include "webrtc/base/logging.h" |
-#include "webrtc/base/timeutils.h" |
-#include "webrtc/modules/audio_device/audio_device_config.h" |
-#include "webrtc/modules/audio_device/win/audio_device_wave_win.h" |
- |
-#include "webrtc/system_wrappers/include/event_wrapper.h" |
-#include "webrtc/system_wrappers/include/trace.h" |
- |
-#include <windows.h> |
-#include <objbase.h> // CoTaskMemAlloc, CoTaskMemFree |
-#include <strsafe.h> // StringCchCopy(), StringCchCat(), StringCchPrintf() |
-#include <assert.h> |
- |
-// Avoids the need of Windows 7 SDK |
-#ifndef WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE |
-#define WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE 0x0010 |
-#endif |
- |
-// Supported in Windows Vista and Windows 7. |
-// http://msdn.microsoft.com/en-us/library/dd370819(v=VS.85).aspx |
-// Taken from Mmddk.h. |
-#define DRV_RESERVED 0x0800 |
-#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) |
-#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) |
- |
-#define POW2(A) (2 << ((A) - 1)) |
- |
-namespace webrtc { |
- |
-// ============================================================================ |
-// Construction & Destruction |
-// ============================================================================ |
- |
-// ---------------------------------------------------------------------------- |
-// AudioDeviceWindowsWave - ctor |
-// ---------------------------------------------------------------------------- |
- |
-AudioDeviceWindowsWave::AudioDeviceWindowsWave(const int32_t id) : |
- _ptrAudioBuffer(NULL), |
- _critSect(*CriticalSectionWrapper::CreateCriticalSection()), |
- _timeEvent(*EventTimerWrapper::Create()), |
- _recStartEvent(*EventWrapper::Create()), |
- _playStartEvent(*EventWrapper::Create()), |
- _hGetCaptureVolumeThread(NULL), |
- _hShutdownGetVolumeEvent(NULL), |
- _hSetCaptureVolumeThread(NULL), |
- _hShutdownSetVolumeEvent(NULL), |
- _hSetCaptureVolumeEvent(NULL), |
- _critSectCb(*CriticalSectionWrapper::CreateCriticalSection()), |
- _id(id), |
- _mixerManager(id), |
- _usingInputDeviceIndex(false), |
- _usingOutputDeviceIndex(false), |
- _inputDevice(AudioDeviceModule::kDefaultDevice), |
- _outputDevice(AudioDeviceModule::kDefaultDevice), |
- _inputDeviceIndex(0), |
- _outputDeviceIndex(0), |
- _inputDeviceIsSpecified(false), |
- _outputDeviceIsSpecified(false), |
- _initialized(false), |
- _recIsInitialized(false), |
- _playIsInitialized(false), |
- _recording(false), |
- _playing(false), |
- _startRec(false), |
- _stopRec(false), |
- _startPlay(false), |
- _stopPlay(false), |
- _AGC(false), |
- _hWaveIn(NULL), |
- _hWaveOut(NULL), |
- _recChannels(N_REC_CHANNELS), |
- _playChannels(N_PLAY_CHANNELS), |
- _recBufCount(0), |
- _recPutBackDelay(0), |
- _recDelayCount(0), |
- _playBufCount(0), |
- _prevPlayTime(0), |
- _prevRecTime(0), |
- _prevTimerCheckTime(0), |
- _timesdwBytes(0), |
- _timerFaults(0), |
- _timerRestartAttempts(0), |
- _no_of_msecleft_warnings(0), |
- _MAX_minBuffer(65), |
- _useHeader(0), |
- _dTcheckPlayBufDelay(10), |
- _playBufDelay(80), |
- _playBufDelayFixed(80), |
- _minPlayBufDelay(20), |
- _avgCPULoad(0), |
- _sndCardPlayDelay(0), |
- _sndCardRecDelay(0), |
- _plSampOld(0), |
- _rcSampOld(0), |
- _playBufType(AudioDeviceModule::kAdaptiveBufferSize), |
- _recordedBytes(0), |
- _playWarning(0), |
- _playError(0), |
- _recWarning(0), |
- _recError(0), |
- _newMicLevel(0), |
- _minMicVolume(0), |
- _maxMicVolume(0) |
-{ |
- WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); |
- |
- // Initialize value, set to 0 if it fails |
- if (!QueryPerformanceFrequency(&_perfFreq)) |
- { |
- _perfFreq.QuadPart = 0; |
- } |
- |
- _hShutdownGetVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
- _hShutdownSetVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
- _hSetCaptureVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// AudioDeviceWindowsWave - dtor |
-// ---------------------------------------------------------------------------- |
- |
-AudioDeviceWindowsWave::~AudioDeviceWindowsWave() |
-{ |
- WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); |
- |
- Terminate(); |
- |
- delete &_recStartEvent; |
- delete &_playStartEvent; |
- delete &_timeEvent; |
- delete &_critSect; |
- delete &_critSectCb; |
- |
- if (NULL != _hShutdownGetVolumeEvent) |
- { |
- CloseHandle(_hShutdownGetVolumeEvent); |
- _hShutdownGetVolumeEvent = NULL; |
- } |
- |
- if (NULL != _hShutdownSetVolumeEvent) |
- { |
- CloseHandle(_hShutdownSetVolumeEvent); |
- _hShutdownSetVolumeEvent = NULL; |
- } |
- |
- if (NULL != _hSetCaptureVolumeEvent) |
- { |
- CloseHandle(_hSetCaptureVolumeEvent); |
- _hSetCaptureVolumeEvent = NULL; |
- } |
-} |
- |
-// ============================================================================ |
-// API |
-// ============================================================================ |
- |
-// ---------------------------------------------------------------------------- |
-// AttachAudioBuffer |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- _ptrAudioBuffer = audioBuffer; |
- |
- // inform the AudioBuffer about default settings for this implementation |
- _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); |
- _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); |
- _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); |
- _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ActiveAudioLayer |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const |
-{ |
- audioLayer = AudioDeviceModule::kWindowsWaveAudio; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Init |
-// ---------------------------------------------------------------------------- |
- |
-AudioDeviceGeneric::InitStatus AudioDeviceWindowsWave::Init() { |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_initialized) { |
- return InitStatus::OK; |
- } |
- |
- const uint32_t nowTime(rtc::TimeMillis()); |
- |
- _recordedBytes = 0; |
- _prevRecByteCheckTime = nowTime; |
- _prevRecTime = nowTime; |
- _prevPlayTime = nowTime; |
- _prevTimerCheckTime = nowTime; |
- |
- _playWarning = 0; |
- _playError = 0; |
- _recWarning = 0; |
- _recError = 0; |
- |
- _mixerManager.EnumerateAll(); |
- |
- if (_ptrThread) { |
- // thread is already created and active |
- return InitStatus::OK; |
- } |
- |
- const char* threadName = "webrtc_audio_module_thread"; |
- _ptrThread.reset(new rtc::PlatformThread(ThreadFunc, this, threadName)); |
- _ptrThread->Start(); |
- _ptrThread->SetPriority(rtc::kRealtimePriority); |
- |
- const bool periodic(true); |
- if (!_timeEvent.StartTimer(periodic, TIMER_PERIOD_MS)) { |
- LOG(LS_ERROR) << "failed to start the timer event"; |
- _ptrThread->Stop(); |
- _ptrThread.reset(); |
- return InitStatus::OTHER_ERROR; |
- } |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
- "periodic timer (dT=%d) is now active", TIMER_PERIOD_MS); |
- |
- _hGetCaptureVolumeThread = |
- CreateThread(NULL, 0, GetCaptureVolumeThread, this, 0, NULL); |
- if (_hGetCaptureVolumeThread == NULL) { |
- LOG(LS_ERROR) << " failed to create the volume getter thread"; |
- return InitStatus::OTHER_ERROR; |
- } |
- |
- SetThreadPriority(_hGetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); |
- |
- _hSetCaptureVolumeThread = |
- CreateThread(NULL, 0, SetCaptureVolumeThread, this, 0, NULL); |
- if (_hSetCaptureVolumeThread == NULL) { |
- LOG(LS_ERROR) << " failed to create the volume setter thread"; |
- return InitStatus::OTHER_ERROR; |
- } |
- |
- SetThreadPriority(_hSetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); |
- |
- _initialized = true; |
- |
- return InitStatus::OK; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Terminate |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::Terminate() |
-{ |
- |
- if (!_initialized) |
- { |
- return 0; |
- } |
- |
- _critSect.Enter(); |
- |
- _mixerManager.Close(); |
- |
- if (_ptrThread) |
- { |
- rtc::PlatformThread* tmpThread = _ptrThread.release(); |
- _critSect.Leave(); |
- |
- _timeEvent.Set(); |
- |
- tmpThread->Stop(); |
- delete tmpThread; |
- } |
- else |
- { |
- _critSect.Leave(); |
- } |
- |
- _critSect.Enter(); |
- SetEvent(_hShutdownGetVolumeEvent); |
- _critSect.Leave(); |
- int32_t ret = WaitForSingleObject(_hGetCaptureVolumeThread, 2000); |
- if (ret != WAIT_OBJECT_0) |
- { |
- // the thread did not stop as it should |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
- " failed to close down volume getter thread"); |
- CloseHandle(_hGetCaptureVolumeThread); |
- _hGetCaptureVolumeThread = NULL; |
- return -1; |
- } |
- _critSect.Enter(); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
- " volume getter thread is now closed"); |
- |
- SetEvent(_hShutdownSetVolumeEvent); |
- _critSect.Leave(); |
- ret = WaitForSingleObject(_hSetCaptureVolumeThread, 2000); |
- if (ret != WAIT_OBJECT_0) |
- { |
- // the thread did not stop as it should |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
- " failed to close down volume setter thread"); |
- CloseHandle(_hSetCaptureVolumeThread); |
- _hSetCaptureVolumeThread = NULL; |
- return -1; |
- } |
- _critSect.Enter(); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
- " volume setter thread is now closed"); |
- |
- CloseHandle(_hGetCaptureVolumeThread); |
- _hGetCaptureVolumeThread = NULL; |
- |
- CloseHandle(_hSetCaptureVolumeThread); |
- _hSetCaptureVolumeThread = NULL; |
- |
- _critSect.Leave(); |
- |
- _timeEvent.StopTimer(); |
- |
- _initialized = false; |
- _outputDeviceIsSpecified = false; |
- _inputDeviceIsSpecified = false; |
- |
- return 0; |
-} |
- |
- |
-DWORD WINAPI AudioDeviceWindowsWave::GetCaptureVolumeThread(LPVOID context) |
-{ |
- return(((AudioDeviceWindowsWave*)context)->DoGetCaptureVolumeThread()); |
-} |
- |
-DWORD WINAPI AudioDeviceWindowsWave::SetCaptureVolumeThread(LPVOID context) |
-{ |
- return(((AudioDeviceWindowsWave*)context)->DoSetCaptureVolumeThread()); |
-} |
- |
-DWORD AudioDeviceWindowsWave::DoGetCaptureVolumeThread() |
-{ |
- HANDLE waitObject = _hShutdownGetVolumeEvent; |
- |
- while (1) |
- { |
- DWORD waitResult = WaitForSingleObject(waitObject, |
- GET_MIC_VOLUME_INTERVAL_MS); |
- switch (waitResult) |
- { |
- case WAIT_OBJECT_0: // _hShutdownGetVolumeEvent |
- return 0; |
- case WAIT_TIMEOUT: // timeout notification |
- break; |
- default: // unexpected error |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
- " unknown wait termination on get volume thread"); |
- return 1; |
- } |
- |
- if (AGC()) |
- { |
- uint32_t currentMicLevel = 0; |
- if (MicrophoneVolume(currentMicLevel) == 0) |
- { |
- // This doesn't set the system volume, just stores it. |
- _critSect.Enter(); |
- if (_ptrAudioBuffer) |
- { |
- _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); |
- } |
- _critSect.Leave(); |
- } |
- } |
- } |
-} |
- |
-DWORD AudioDeviceWindowsWave::DoSetCaptureVolumeThread() |
-{ |
- HANDLE waitArray[2] = {_hShutdownSetVolumeEvent, _hSetCaptureVolumeEvent}; |
- |
- while (1) |
- { |
- DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE); |
- switch (waitResult) |
- { |
- case WAIT_OBJECT_0: // _hShutdownSetVolumeEvent |
- return 0; |
- case WAIT_OBJECT_0 + 1: // _hSetCaptureVolumeEvent |
- break; |
- default: // unexpected error |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
- " unknown wait termination on set volume thread"); |
- return 1; |
- } |
- |
- _critSect.Enter(); |
- uint32_t newMicLevel = _newMicLevel; |
- _critSect.Leave(); |
- |
- if (SetMicrophoneVolume(newMicLevel) == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
- " the required modification of the microphone volume failed"); |
- } |
- } |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Initialized |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::Initialized() const |
-{ |
- return (_initialized); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// InitSpeaker |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::InitSpeaker() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_playing) |
- { |
- return -1; |
- } |
- |
- if (_mixerManager.EnumerateSpeakers() == -1) |
- { |
- // failed to locate any valid/controllable speaker |
- return -1; |
- } |
- |
- if (IsUsingOutputDeviceIndex()) |
- { |
- if (_mixerManager.OpenSpeaker(OutputDeviceIndex()) == -1) |
- { |
- return -1; |
- } |
- } |
- else |
- { |
- if (_mixerManager.OpenSpeaker(OutputDevice()) == -1) |
- { |
- return -1; |
- } |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// InitMicrophone |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::InitMicrophone() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_recording) |
- { |
- return -1; |
- } |
- |
- if (_mixerManager.EnumerateMicrophones() == -1) |
- { |
- // failed to locate any valid/controllable microphone |
- return -1; |
- } |
- |
- if (IsUsingInputDeviceIndex()) |
- { |
- if (_mixerManager.OpenMicrophone(InputDeviceIndex()) == -1) |
- { |
- return -1; |
- } |
- } |
- else |
- { |
- if (_mixerManager.OpenMicrophone(InputDevice()) == -1) |
- { |
- return -1; |
- } |
- } |
- |
- uint32_t maxVol = 0; |
- if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
- " unable to retrieve max microphone volume"); |
- } |
- _maxMicVolume = maxVol; |
- |
- uint32_t minVol = 0; |
- if (_mixerManager.MinMicrophoneVolume(minVol) == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
- " unable to retrieve min microphone volume"); |
- } |
- _minMicVolume = minVol; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerIsInitialized |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::SpeakerIsInitialized() const |
-{ |
- return (_mixerManager.SpeakerIsInitialized()); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneIsInitialized |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::MicrophoneIsInitialized() const |
-{ |
- return (_mixerManager.MicrophoneIsInitialized()); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerVolumeIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SpeakerVolumeIsAvailable(bool& available) |
-{ |
- |
- bool isAvailable(false); |
- |
- // Enumerate all avaliable speakers and make an attempt to open up the |
- // output mixer corresponding to the currently selected output device. |
- // |
- if (InitSpeaker() == -1) |
- { |
- // failed to find a valid speaker |
- available = false; |
- return 0; |
- } |
- |
- // Check if the selected speaker has a volume control |
- // |
- _mixerManager.SpeakerVolumeIsAvailable(isAvailable); |
- available = isAvailable; |
- |
- // Close the initialized output mixer |
- // |
- _mixerManager.CloseSpeaker(); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetSpeakerVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetSpeakerVolume(uint32_t volume) |
-{ |
- |
- return (_mixerManager.SetSpeakerVolume(volume)); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SpeakerVolume(uint32_t& volume) const |
-{ |
- |
- uint32_t level(0); |
- |
- if (_mixerManager.SpeakerVolume(level) == -1) |
- { |
- return -1; |
- } |
- |
- volume = level; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetWaveOutVolume |
-// |
-// The low-order word contains the left-channel volume setting, and the |
-// high-order word contains the right-channel setting. |
-// A value of 0xFFFF represents full volume, and a value of 0x0000 is silence. |
-// |
-// If a device does not support both left and right volume control, |
-// the low-order word of dwVolume specifies the volume level, |
-// and the high-order word is ignored. |
-// |
-// Most devices do not support the full 16 bits of volume-level control |
-// and will not use the least-significant bits of the requested volume setting. |
-// For example, if a device supports 4 bits of volume control, the values |
-// 0x4000, 0x4FFF, and 0x43BE will all be truncated to 0x4000. |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetWaveOutVolume(uint16_t volumeLeft, uint16_t volumeRight) |
-{ |
- |
- MMRESULT res(0); |
- WAVEOUTCAPS caps; |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_hWaveOut == NULL) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no open playout device exists => using default"); |
- } |
- |
- // To determine whether the device supports volume control on both |
- // the left and right channels, use the WAVECAPS_LRVOLUME flag. |
- // |
- res = waveOutGetDevCaps((UINT_PTR)_hWaveOut, &caps, sizeof(WAVEOUTCAPS)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- if (!(caps.dwSupport & WAVECAPS_VOLUME)) |
- { |
- // this device does not support volume control using the waveOutSetVolume API |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device does not support volume control using the Wave API"); |
- return -1; |
- } |
- if (!(caps.dwSupport & WAVECAPS_LRVOLUME)) |
- { |
- // high-order word (right channel) is ignored |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "device does not support volume control on both channels"); |
- } |
- |
- DWORD dwVolume(0x00000000); |
- dwVolume = (DWORD)(((volumeRight & 0xFFFF) << 16) | (volumeLeft & 0xFFFF)); |
- |
- res = waveOutSetVolume(_hWaveOut, dwVolume); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutSetVolume() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- return -1; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// WaveOutVolume |
-// |
-// The low-order word of this location contains the left-channel volume setting, |
-// and the high-order word contains the right-channel setting. |
-// A value of 0xFFFF (65535) represents full volume, and a value of 0x0000 |
-// is silence. |
-// |
-// If a device does not support both left and right volume control, |
-// the low-order word of the specified location contains the mono volume level. |
-// |
-// The full 16-bit setting(s) set with the waveOutSetVolume function is returned, |
-// regardless of whether the device supports the full 16 bits of volume-level |
-// control. |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::WaveOutVolume(uint16_t& volumeLeft, uint16_t& volumeRight) const |
-{ |
- |
- MMRESULT res(0); |
- WAVEOUTCAPS caps; |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_hWaveOut == NULL) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no open playout device exists => using default"); |
- } |
- |
- // To determine whether the device supports volume control on both |
- // the left and right channels, use the WAVECAPS_LRVOLUME flag. |
- // |
- res = waveOutGetDevCaps((UINT_PTR)_hWaveOut, &caps, sizeof(WAVEOUTCAPS)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- if (!(caps.dwSupport & WAVECAPS_VOLUME)) |
- { |
- // this device does not support volume control using the waveOutSetVolume API |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device does not support volume control using the Wave API"); |
- return -1; |
- } |
- if (!(caps.dwSupport & WAVECAPS_LRVOLUME)) |
- { |
- // high-order word (right channel) is ignored |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "device does not support volume control on both channels"); |
- } |
- |
- DWORD dwVolume(0x00000000); |
- |
- res = waveOutGetVolume(_hWaveOut, &dwVolume); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutGetVolume() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- return -1; |
- } |
- |
- WORD wVolumeLeft = LOWORD(dwVolume); |
- WORD wVolumeRight = HIWORD(dwVolume); |
- |
- volumeLeft = static_cast<uint16_t> (wVolumeLeft); |
- volumeRight = static_cast<uint16_t> (wVolumeRight); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MaxSpeakerVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MaxSpeakerVolume(uint32_t& maxVolume) const |
-{ |
- |
- uint32_t maxVol(0); |
- |
- if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) |
- { |
- return -1; |
- } |
- |
- maxVolume = maxVol; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MinSpeakerVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MinSpeakerVolume(uint32_t& minVolume) const |
-{ |
- |
- uint32_t minVol(0); |
- |
- if (_mixerManager.MinSpeakerVolume(minVol) == -1) |
- { |
- return -1; |
- } |
- |
- minVolume = minVol; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerVolumeStepSize |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SpeakerVolumeStepSize(uint16_t& stepSize) const |
-{ |
- |
- uint16_t delta(0); |
- |
- if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) |
- { |
- return -1; |
- } |
- |
- stepSize = delta; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerMuteIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SpeakerMuteIsAvailable(bool& available) |
-{ |
- |
- bool isAvailable(false); |
- |
- // Enumerate all avaliable speakers and make an attempt to open up the |
- // output mixer corresponding to the currently selected output device. |
- // |
- if (InitSpeaker() == -1) |
- { |
- // If we end up here it means that the selected speaker has no volume |
- // control, hence it is safe to state that there is no mute control |
- // already at this stage. |
- available = false; |
- return 0; |
- } |
- |
- // Check if the selected speaker has a mute control |
- // |
- _mixerManager.SpeakerMuteIsAvailable(isAvailable); |
- available = isAvailable; |
- |
- // Close the initialized output mixer |
- // |
- _mixerManager.CloseSpeaker(); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetSpeakerMute |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetSpeakerMute(bool enable) |
-{ |
- return (_mixerManager.SetSpeakerMute(enable)); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SpeakerMute |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SpeakerMute(bool& enabled) const |
-{ |
- |
- bool muted(0); |
- |
- if (_mixerManager.SpeakerMute(muted) == -1) |
- { |
- return -1; |
- } |
- |
- enabled = muted; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneMuteIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneMuteIsAvailable(bool& available) |
-{ |
- |
- bool isAvailable(false); |
- |
- // Enumerate all avaliable microphones and make an attempt to open up the |
- // input mixer corresponding to the currently selected input device. |
- // |
- if (InitMicrophone() == -1) |
- { |
- // If we end up here it means that the selected microphone has no volume |
- // control, hence it is safe to state that there is no boost control |
- // already at this stage. |
- available = false; |
- return 0; |
- } |
- |
- // Check if the selected microphone has a mute control |
- // |
- _mixerManager.MicrophoneMuteIsAvailable(isAvailable); |
- available = isAvailable; |
- |
- // Close the initialized input mixer |
- // |
- _mixerManager.CloseMicrophone(); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetMicrophoneMute |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetMicrophoneMute(bool enable) |
-{ |
- return (_mixerManager.SetMicrophoneMute(enable)); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneMute |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneMute(bool& enabled) const |
-{ |
- |
- bool muted(0); |
- |
- if (_mixerManager.MicrophoneMute(muted) == -1) |
- { |
- return -1; |
- } |
- |
- enabled = muted; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneBoostIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneBoostIsAvailable(bool& available) |
-{ |
- |
- bool isAvailable(false); |
- |
- // Enumerate all avaliable microphones and make an attempt to open up the |
- // input mixer corresponding to the currently selected input device. |
- // |
- if (InitMicrophone() == -1) |
- { |
- // If we end up here it means that the selected microphone has no volume |
- // control, hence it is safe to state that there is no boost control |
- // already at this stage. |
- available = false; |
- return 0; |
- } |
- |
- // Check if the selected microphone has a boost control |
- // |
- _mixerManager.MicrophoneBoostIsAvailable(isAvailable); |
- available = isAvailable; |
- |
- // Close the initialized input mixer |
- // |
- _mixerManager.CloseMicrophone(); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetMicrophoneBoost |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetMicrophoneBoost(bool enable) |
-{ |
- |
- return (_mixerManager.SetMicrophoneBoost(enable)); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneBoost |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneBoost(bool& enabled) const |
-{ |
- |
- bool onOff(0); |
- |
- if (_mixerManager.MicrophoneBoost(onOff) == -1) |
- { |
- return -1; |
- } |
- |
- enabled = onOff; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StereoRecordingIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StereoRecordingIsAvailable(bool& available) |
-{ |
- available = true; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetStereoRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetStereoRecording(bool enable) |
-{ |
- |
- if (enable) |
- _recChannels = 2; |
- else |
- _recChannels = 1; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StereoRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StereoRecording(bool& enabled) const |
-{ |
- |
- if (_recChannels == 2) |
- enabled = true; |
- else |
- enabled = false; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StereoPlayoutIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StereoPlayoutIsAvailable(bool& available) |
-{ |
- available = true; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetStereoPlayout |
-// |
-// Specifies the number of output channels. |
-// |
-// NOTE - the setting will only have an effect after InitPlayout has |
-// been called. |
-// |
-// 16-bit mono: |
-// |
-// Each sample is 2 bytes. Sample 1 is followed by samples 2, 3, 4, and so on. |
-// For each sample, the first byte is the low-order byte of channel 0 and the |
-// second byte is the high-order byte of channel 0. |
-// |
-// 16-bit stereo: |
-// |
-// Each sample is 4 bytes. Sample 1 is followed by samples 2, 3, 4, and so on. |
-// For each sample, the first byte is the low-order byte of channel 0 (left channel); |
-// the second byte is the high-order byte of channel 0; the third byte is the |
-// low-order byte of channel 1 (right channel); and the fourth byte is the |
-// high-order byte of channel 1. |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetStereoPlayout(bool enable) |
-{ |
- |
- if (enable) |
- _playChannels = 2; |
- else |
- _playChannels = 1; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StereoPlayout |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StereoPlayout(bool& enabled) const |
-{ |
- |
- if (_playChannels == 2) |
- enabled = true; |
- else |
- enabled = false; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetAGC |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetAGC(bool enable) |
-{ |
- |
- _AGC = enable; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// AGC |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::AGC() const |
-{ |
- return _AGC; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneVolumeIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneVolumeIsAvailable(bool& available) |
-{ |
- |
- bool isAvailable(false); |
- |
- // Enumerate all avaliable microphones and make an attempt to open up the |
- // input mixer corresponding to the currently selected output device. |
- // |
- if (InitMicrophone() == -1) |
- { |
- // Failed to find valid microphone |
- available = false; |
- return 0; |
- } |
- |
- // Check if the selected microphone has a volume control |
- // |
- _mixerManager.MicrophoneVolumeIsAvailable(isAvailable); |
- available = isAvailable; |
- |
- // Close the initialized input mixer |
- // |
- _mixerManager.CloseMicrophone(); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetMicrophoneVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetMicrophoneVolume(uint32_t volume) |
-{ |
- return (_mixerManager.SetMicrophoneVolume(volume)); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneVolume(uint32_t& volume) const |
-{ |
- uint32_t level(0); |
- |
- if (_mixerManager.MicrophoneVolume(level) == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to retrive current microphone level"); |
- return -1; |
- } |
- |
- volume = level; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MaxMicrophoneVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MaxMicrophoneVolume(uint32_t& maxVolume) const |
-{ |
- // _maxMicVolume can be zero in AudioMixerManager::MaxMicrophoneVolume(): |
- // (1) API GetLineControl() returns failure at querying the max Mic level. |
- // (2) API GetLineControl() returns maxVolume as zero in rare cases. |
- // Both cases show we don't have access to the mixer controls. |
- // We return -1 here to indicate that. |
- if (_maxMicVolume == 0) |
- { |
- return -1; |
- } |
- |
- maxVolume = _maxMicVolume;; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MinMicrophoneVolume |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MinMicrophoneVolume(uint32_t& minVolume) const |
-{ |
- minVolume = _minMicVolume; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MicrophoneVolumeStepSize |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MicrophoneVolumeStepSize(uint16_t& stepSize) const |
-{ |
- |
- uint16_t delta(0); |
- |
- if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) |
- { |
- return -1; |
- } |
- |
- stepSize = delta; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutDevices |
-// ---------------------------------------------------------------------------- |
- |
-int16_t AudioDeviceWindowsWave::PlayoutDevices() |
-{ |
- |
- return (waveOutGetNumDevs()); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetPlayoutDevice I (II) |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetPlayoutDevice(uint16_t index) |
-{ |
- |
- if (_playIsInitialized) |
- { |
- return -1; |
- } |
- |
- UINT nDevices = waveOutGetNumDevs(); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "number of availiable waveform-audio output devices is %u", nDevices); |
- |
- if (index < 0 || index > (nDevices-1)) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); |
- return -1; |
- } |
- |
- _usingOutputDeviceIndex = true; |
- _outputDeviceIndex = index; |
- _outputDeviceIsSpecified = true; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetPlayoutDevice II (II) |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device) |
-{ |
- if (_playIsInitialized) |
- { |
- return -1; |
- } |
- |
- if (device == AudioDeviceModule::kDefaultDevice) |
- { |
- } |
- else if (device == AudioDeviceModule::kDefaultCommunicationDevice) |
- { |
- } |
- |
- _usingOutputDeviceIndex = false; |
- _outputDevice = device; |
- _outputDeviceIsSpecified = true; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutDeviceName |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PlayoutDeviceName( |
- uint16_t index, |
- char name[kAdmMaxDeviceNameSize], |
- char guid[kAdmMaxGuidSize]) |
-{ |
- |
- uint16_t nDevices(PlayoutDevices()); |
- |
- // Special fix for the case when the user asks for the name of the default device. |
- // |
- if (index == (uint16_t)(-1)) |
- { |
- index = 0; |
- } |
- |
- if ((index > (nDevices-1)) || (name == NULL)) |
- { |
- return -1; |
- } |
- |
- memset(name, 0, kAdmMaxDeviceNameSize); |
- |
- if (guid != NULL) |
- { |
- memset(guid, 0, kAdmMaxGuidSize); |
- } |
- |
- WAVEOUTCAPSW caps; // szPname member (product name (NULL terminated) is a WCHAR |
- MMRESULT res; |
- |
- res = waveOutGetDevCapsW(index, &caps, sizeof(WAVEOUTCAPSW)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCapsW() failed (err=%d)", res); |
- return -1; |
- } |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 1", GetLastError()); |
- } |
- |
- if (guid == NULL) |
- { |
- return 0; |
- } |
- |
- // It is possible to get the unique endpoint ID string using the Wave API. |
- // However, it is only supported on Windows Vista and Windows 7. |
- |
- size_t cbEndpointId(0); |
- |
- // Get the size (including the terminating null) of the endpoint ID string of the waveOut device. |
- // Windows Vista supports the DRV_QUERYFUNCTIONINSTANCEIDSIZE and DRV_QUERYFUNCTIONINSTANCEID messages. |
- res = waveOutMessage((HWAVEOUT)IntToPtr(index), |
- DRV_QUERYFUNCTIONINSTANCEIDSIZE, |
- (DWORD_PTR)&cbEndpointId, NULL); |
- if (res != MMSYSERR_NOERROR) |
- { |
- // DRV_QUERYFUNCTIONINSTANCEIDSIZE is not supported <=> earlier version of Windows than Vista |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) failed (err=%d)", res); |
- TraceWaveOutError(res); |
- // Best we can do is to copy the friendly name and use it as guid |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 2", GetLastError()); |
- } |
- return 0; |
- } |
- |
- // waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) worked => we are on a Vista or Windows 7 device |
- |
- WCHAR *pstrEndpointId = NULL; |
- pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointId); |
- |
- // Get the endpoint ID string for this waveOut device. |
- res = waveOutMessage((HWAVEOUT)IntToPtr(index), |
- DRV_QUERYFUNCTIONINSTANCEID, |
- (DWORD_PTR)pstrEndpointId, |
- cbEndpointId); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveOutMessage(DRV_QUERYFUNCTIONINSTANCEID) failed (err=%d)", res); |
- TraceWaveOutError(res); |
- // Best we can do is to copy the friendly name and use it as guid |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 3", GetLastError()); |
- } |
- CoTaskMemFree(pstrEndpointId); |
- return 0; |
- } |
- |
- if (WideCharToMultiByte(CP_UTF8, 0, pstrEndpointId, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 4", GetLastError()); |
- } |
- CoTaskMemFree(pstrEndpointId); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingDeviceName |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::RecordingDeviceName( |
- uint16_t index, |
- char name[kAdmMaxDeviceNameSize], |
- char guid[kAdmMaxGuidSize]) |
-{ |
- |
- uint16_t nDevices(RecordingDevices()); |
- |
- // Special fix for the case when the user asks for the name of the default device. |
- // |
- if (index == (uint16_t)(-1)) |
- { |
- index = 0; |
- } |
- |
- if ((index > (nDevices-1)) || (name == NULL)) |
- { |
- return -1; |
- } |
- |
- memset(name, 0, kAdmMaxDeviceNameSize); |
- |
- if (guid != NULL) |
- { |
- memset(guid, 0, kAdmMaxGuidSize); |
- } |
- |
- WAVEINCAPSW caps; // szPname member (product name (NULL terminated) is a WCHAR |
- MMRESULT res; |
- |
- res = waveInGetDevCapsW(index, &caps, sizeof(WAVEINCAPSW)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCapsW() failed (err=%d)", res); |
- return -1; |
- } |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 1", GetLastError()); |
- } |
- |
- if (guid == NULL) |
- { |
- return 0; |
- } |
- |
- // It is possible to get the unique endpoint ID string using the Wave API. |
- // However, it is only supported on Windows Vista and Windows 7. |
- |
- size_t cbEndpointId(0); |
- |
- // Get the size (including the terminating null) of the endpoint ID string of the waveOut device. |
- // Windows Vista supports the DRV_QUERYFUNCTIONINSTANCEIDSIZE and DRV_QUERYFUNCTIONINSTANCEID messages. |
- res = waveInMessage((HWAVEIN)IntToPtr(index), |
- DRV_QUERYFUNCTIONINSTANCEIDSIZE, |
- (DWORD_PTR)&cbEndpointId, NULL); |
- if (res != MMSYSERR_NOERROR) |
- { |
- // DRV_QUERYFUNCTIONINSTANCEIDSIZE is not supported <=> earlier version of Windows than Vista |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) failed (err=%d)", res); |
- TraceWaveInError(res); |
- // Best we can do is to copy the friendly name and use it as guid |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 2", GetLastError()); |
- } |
- return 0; |
- } |
- |
- // waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) worked => we are on a Vista or Windows 7 device |
- |
- WCHAR *pstrEndpointId = NULL; |
- pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointId); |
- |
- // Get the endpoint ID string for this waveOut device. |
- res = waveInMessage((HWAVEIN)IntToPtr(index), |
- DRV_QUERYFUNCTIONINSTANCEID, |
- (DWORD_PTR)pstrEndpointId, |
- cbEndpointId); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInMessage(DRV_QUERYFUNCTIONINSTANCEID) failed (err=%d)", res); |
- TraceWaveInError(res); |
- // Best we can do is to copy the friendly name and use it as guid |
- if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 3", GetLastError()); |
- } |
- CoTaskMemFree(pstrEndpointId); |
- return 0; |
- } |
- |
- if (WideCharToMultiByte(CP_UTF8, 0, pstrEndpointId, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 4", GetLastError()); |
- } |
- CoTaskMemFree(pstrEndpointId); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingDevices |
-// ---------------------------------------------------------------------------- |
- |
-int16_t AudioDeviceWindowsWave::RecordingDevices() |
-{ |
- |
- return (waveInGetNumDevs()); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetRecordingDevice I (II) |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetRecordingDevice(uint16_t index) |
-{ |
- |
- if (_recIsInitialized) |
- { |
- return -1; |
- } |
- |
- UINT nDevices = waveInGetNumDevs(); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "number of availiable waveform-audio input devices is %u", nDevices); |
- |
- if (index < 0 || index > (nDevices-1)) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); |
- return -1; |
- } |
- |
- _usingInputDeviceIndex = true; |
- _inputDeviceIndex = index; |
- _inputDeviceIsSpecified = true; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// SetRecordingDevice II (II) |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device) |
-{ |
- if (device == AudioDeviceModule::kDefaultDevice) |
- { |
- } |
- else if (device == AudioDeviceModule::kDefaultCommunicationDevice) |
- { |
- } |
- |
- if (_recIsInitialized) |
- { |
- return -1; |
- } |
- |
- _usingInputDeviceIndex = false; |
- _inputDevice = device; |
- _inputDeviceIsSpecified = true; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PlayoutIsAvailable(bool& available) |
-{ |
- |
- available = false; |
- |
- // Try to initialize the playout side |
- int32_t res = InitPlayout(); |
- |
- // Cancel effect of initialization |
- StopPlayout(); |
- |
- if (res != -1) |
- { |
- available = true; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingIsAvailable |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::RecordingIsAvailable(bool& available) |
-{ |
- |
- available = false; |
- |
- // Try to initialize the recording side |
- int32_t res = InitRecording(); |
- |
- // Cancel effect of initialization |
- StopRecording(); |
- |
- if (res != -1) |
- { |
- available = true; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// InitPlayout |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::InitPlayout() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_playing) |
- { |
- return -1; |
- } |
- |
- if (!_outputDeviceIsSpecified) |
- { |
- return -1; |
- } |
- |
- if (_playIsInitialized) |
- { |
- return 0; |
- } |
- |
- // Initialize the speaker (devices might have been added or removed) |
- if (InitSpeaker() == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitSpeaker() failed"); |
- } |
- |
- // Enumerate all availiable output devices |
- EnumeratePlayoutDevices(); |
- |
- // Start by closing any existing wave-output devices |
- // |
- MMRESULT res(MMSYSERR_ERROR); |
- |
- if (_hWaveOut != NULL) |
- { |
- res = waveOutClose(_hWaveOut); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutClose() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- } |
- |
- // Set the output wave format |
- // |
- WAVEFORMATEX waveFormat; |
- |
- waveFormat.wFormatTag = WAVE_FORMAT_PCM; |
- waveFormat.nChannels = _playChannels; // mono <=> 1, stereo <=> 2 |
- waveFormat.nSamplesPerSec = N_PLAY_SAMPLES_PER_SEC; |
- waveFormat.wBitsPerSample = 16; |
- waveFormat.nBlockAlign = waveFormat.nChannels * (waveFormat.wBitsPerSample/8); |
- waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; |
- waveFormat.cbSize = 0; |
- |
- // Open the given waveform-audio output device for playout |
- // |
- HWAVEOUT hWaveOut(NULL); |
- |
- if (IsUsingOutputDeviceIndex()) |
- { |
- // verify settings first |
- res = waveOutOpen(NULL, _outputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- // open the given waveform-audio output device for recording |
- res = waveOutOpen(&hWaveOut, _outputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening output device corresponding to device ID %u", _outputDeviceIndex); |
- } |
- } |
- else |
- { |
- if (_outputDevice == AudioDeviceModule::kDefaultCommunicationDevice) |
- { |
- // check if it is possible to open the default communication device (supported on Windows 7) |
- res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- // if so, open the default communication device for real |
- res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); |
- } |
- else |
- { |
- // use default device since default communication device was not avaliable |
- res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to open default communication device => using default instead"); |
- } |
- } |
- else if (_outputDevice == AudioDeviceModule::kDefaultDevice) |
- { |
- // open default device since it has been requested |
- res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default output device"); |
- } |
- } |
- } |
- |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutOpen() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- return -1; |
- } |
- |
- // Log information about the aquired output device |
- // |
- WAVEOUTCAPS caps; |
- |
- res = waveOutGetDevCaps((UINT_PTR)hWaveOut, &caps, sizeof(WAVEOUTCAPS)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- |
- UINT deviceID(0); |
- res = waveOutGetID(hWaveOut, &deviceID); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetID() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "utilized device ID : %u", deviceID); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); |
- |
- // Store valid handle for the open waveform-audio output device |
- _hWaveOut = hWaveOut; |
- |
- // Store the input wave header as well |
- _waveFormatOut = waveFormat; |
- |
- // Prepare wave-out headers |
- // |
- const uint8_t bytesPerSample = 2*_playChannels; |
- |
- for (int n = 0; n < N_BUFFERS_OUT; n++) |
- { |
- // set up the output wave header |
- _waveHeaderOut[n].lpData = reinterpret_cast<LPSTR>(&_playBuffer[n]); |
- _waveHeaderOut[n].dwBufferLength = bytesPerSample*PLAY_BUF_SIZE_IN_SAMPLES; |
- _waveHeaderOut[n].dwFlags = 0; |
- _waveHeaderOut[n].dwLoops = 0; |
- |
- memset(_playBuffer[n], 0, bytesPerSample*PLAY_BUF_SIZE_IN_SAMPLES); |
- |
- // The waveOutPrepareHeader function prepares a waveform-audio data block for playback. |
- // The lpData, dwBufferLength, and dwFlags members of the WAVEHDR structure must be set |
- // before calling this function. |
- // |
- res = waveOutPrepareHeader(_hWaveOut, &_waveHeaderOut[n], sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutPrepareHeader(%d) failed (err=%d)", n, res); |
- TraceWaveOutError(res); |
- } |
- |
- // perform extra check to ensure that the header is prepared |
- if (_waveHeaderOut[n].dwFlags != WHDR_PREPARED) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutPrepareHeader(%d) failed (dwFlags != WHDR_PREPARED)", n); |
- } |
- } |
- |
- // Mark playout side as initialized |
- _playIsInitialized = true; |
- |
- _dTcheckPlayBufDelay = 10; // check playback buffer delay every 10 ms |
- _playBufCount = 0; // index of active output wave header (<=> output buffer index) |
- _playBufDelay = 80; // buffer delay/size is initialized to 80 ms and slowly decreased until er < 25 |
- _minPlayBufDelay = 25; // minimum playout buffer delay |
- _MAX_minBuffer = 65; // adaptive minimum playout buffer delay cannot be larger than this value |
- _intro = 1; // Used to make sure that adaption starts after (2000-1700)/100 seconds |
- _waitCounter = 1700; // Counter for start of adaption of playback buffer |
- _erZeroCounter = 0; // Log how many times er = 0 in consequtive calls to RecTimeProc |
- _useHeader = 0; // Counts number of "useHeader" detections. Stops at 2. |
- |
- _writtenSamples = 0; |
- _writtenSamplesOld = 0; |
- _playedSamplesOld = 0; |
- _sndCardPlayDelay = 0; |
- _sndCardRecDelay = 0; |
- |
- WEBRTC_TRACE(kTraceInfo, kTraceUtility, _id,"initial playout status: _playBufDelay=%d, _minPlayBufDelay=%d", |
- _playBufDelay, _minPlayBufDelay); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// InitRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::InitRecording() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_recording) |
- { |
- return -1; |
- } |
- |
- if (!_inputDeviceIsSpecified) |
- { |
- return -1; |
- } |
- |
- if (_recIsInitialized) |
- { |
- return 0; |
- } |
- |
- _avgCPULoad = 0; |
- _playAcc = 0; |
- |
- // Initialize the microphone (devices might have been added or removed) |
- if (InitMicrophone() == -1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitMicrophone() failed"); |
- } |
- |
- // Enumerate all availiable input devices |
- EnumerateRecordingDevices(); |
- |
- // Start by closing any existing wave-input devices |
- // |
- MMRESULT res(MMSYSERR_ERROR); |
- |
- if (_hWaveIn != NULL) |
- { |
- res = waveInClose(_hWaveIn); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInClose() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- } |
- |
- // Set the input wave format |
- // |
- WAVEFORMATEX waveFormat; |
- |
- waveFormat.wFormatTag = WAVE_FORMAT_PCM; |
- waveFormat.nChannels = _recChannels; // mono <=> 1, stereo <=> 2 |
- waveFormat.nSamplesPerSec = N_REC_SAMPLES_PER_SEC; |
- waveFormat.wBitsPerSample = 16; |
- waveFormat.nBlockAlign = waveFormat.nChannels * (waveFormat.wBitsPerSample/8); |
- waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; |
- waveFormat.cbSize = 0; |
- |
- // Open the given waveform-audio input device for recording |
- // |
- HWAVEIN hWaveIn(NULL); |
- |
- if (IsUsingInputDeviceIndex()) |
- { |
- // verify settings first |
- res = waveInOpen(NULL, _inputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- // open the given waveform-audio input device for recording |
- res = waveInOpen(&hWaveIn, _inputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening input device corresponding to device ID %u", _inputDeviceIndex); |
- } |
- } |
- else |
- { |
- if (_inputDevice == AudioDeviceModule::kDefaultCommunicationDevice) |
- { |
- // check if it is possible to open the default communication device (supported on Windows 7) |
- res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- // if so, open the default communication device for real |
- res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); |
- } |
- else |
- { |
- // use default device since default communication device was not avaliable |
- res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to open default communication device => using default instead"); |
- } |
- } |
- else if (_inputDevice == AudioDeviceModule::kDefaultDevice) |
- { |
- // open default device since it has been requested |
- res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); |
- if (MMSYSERR_NOERROR == res) |
- { |
- res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default input device"); |
- } |
- } |
- } |
- |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInOpen() failed (err=%d)", res); |
- TraceWaveInError(res); |
- return -1; |
- } |
- |
- // Log information about the aquired input device |
- // |
- WAVEINCAPS caps; |
- |
- res = waveInGetDevCaps((UINT_PTR)hWaveIn, &caps, sizeof(WAVEINCAPS)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCaps() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- UINT deviceID(0); |
- res = waveInGetID(hWaveIn, &deviceID); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetID() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "utilized device ID : %u", deviceID); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); |
- |
- // Store valid handle for the open waveform-audio input device |
- _hWaveIn = hWaveIn; |
- |
- // Store the input wave header as well |
- _waveFormatIn = waveFormat; |
- |
- // Mark recording side as initialized |
- _recIsInitialized = true; |
- |
- _recBufCount = 0; // index of active input wave header (<=> input buffer index) |
- _recDelayCount = 0; // ensures that input buffers are returned with certain delay |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StartRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StartRecording() |
-{ |
- |
- if (!_recIsInitialized) |
- { |
- return -1; |
- } |
- |
- if (_recording) |
- { |
- return 0; |
- } |
- |
- // set state to ensure that the recording starts from the audio thread |
- _startRec = true; |
- |
- // the audio thread will signal when recording has stopped |
- if (kEventTimeout == _recStartEvent.Wait(10000)) |
- { |
- _startRec = false; |
- StopRecording(); |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate recording"); |
- return -1; |
- } |
- |
- if (_recording) |
- { |
- // the recording state is set by the audio thread after recording has started |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate recording"); |
- return -1; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StopRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StopRecording() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (!_recIsInitialized) |
- { |
- return 0; |
- } |
- |
- if (_hWaveIn == NULL) |
- { |
- return -1; |
- } |
- |
- bool wasRecording = _recording; |
- _recIsInitialized = false; |
- _recording = false; |
- |
- MMRESULT res; |
- |
- // Stop waveform-adio input. If there are any buffers in the queue, the |
- // current buffer will be marked as done (the dwBytesRecorded member in |
- // the header will contain the length of data), but any empty buffers in |
- // the queue will remain there. |
- // |
- res = waveInStop(_hWaveIn); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInStop() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- // Stop input on the given waveform-audio input device and resets the current |
- // position to zero. All pending buffers are marked as done and returned to |
- // the application. |
- // |
- res = waveInReset(_hWaveIn); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInReset() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- // Clean up the preparation performed by the waveInPrepareHeader function. |
- // Only unprepare header if recording was ever started (and headers are prepared). |
- // |
- if (wasRecording) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInUnprepareHeader() will be performed"); |
- for (int n = 0; n < N_BUFFERS_IN; n++) |
- { |
- res = waveInUnprepareHeader(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInUnprepareHeader() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- } |
- } |
- |
- // Close the given waveform-audio input device. |
- // |
- res = waveInClose(_hWaveIn); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInClose() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- // Set the wave input handle to NULL |
- // |
- _hWaveIn = NULL; |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_hWaveIn is now set to NULL"); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingIsInitialized |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::RecordingIsInitialized() const |
-{ |
- return (_recIsInitialized); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Recording |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::Recording() const |
-{ |
- return (_recording); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutIsInitialized |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::PlayoutIsInitialized() const |
-{ |
- return (_playIsInitialized); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StartPlayout |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StartPlayout() |
-{ |
- |
- if (!_playIsInitialized) |
- { |
- return -1; |
- } |
- |
- if (_playing) |
- { |
- return 0; |
- } |
- |
- // set state to ensure that playout starts from the audio thread |
- _startPlay = true; |
- |
- // the audio thread will signal when recording has started |
- if (kEventTimeout == _playStartEvent.Wait(10000)) |
- { |
- _startPlay = false; |
- StopPlayout(); |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate playout"); |
- return -1; |
- } |
- |
- if (_playing) |
- { |
- // the playing state is set by the audio thread after playout has started |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate playing"); |
- return -1; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// StopPlayout |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::StopPlayout() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (!_playIsInitialized) |
- { |
- return 0; |
- } |
- |
- if (_hWaveOut == NULL) |
- { |
- return -1; |
- } |
- |
- _playIsInitialized = false; |
- _playing = false; |
- _sndCardPlayDelay = 0; |
- _sndCardRecDelay = 0; |
- |
- MMRESULT res; |
- |
- // The waveOutReset function stops playback on the given waveform-audio |
- // output device and resets the current position to zero. All pending |
- // playback buffers are marked as done (WHDR_DONE) and returned to the application. |
- // After this function returns, the application can send new playback buffers |
- // to the device by calling waveOutWrite, or close the device by calling waveOutClose. |
- // |
- res = waveOutReset(_hWaveOut); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutReset() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- |
- // The waveOutUnprepareHeader function cleans up the preparation performed |
- // by the waveOutPrepareHeader function. This function must be called after |
- // the device driver is finished with a data block. |
- // You must call this function before freeing the buffer. |
- // |
- for (int n = 0; n < N_BUFFERS_OUT; n++) |
- { |
- res = waveOutUnprepareHeader(_hWaveOut, &_waveHeaderOut[n], sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutUnprepareHeader() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- } |
- |
- // The waveOutClose function closes the given waveform-audio output device. |
- // The close operation fails if the device is still playing a waveform-audio |
- // buffer that was previously sent by calling waveOutWrite. Before calling |
- // waveOutClose, the application must wait for all buffers to finish playing |
- // or call the waveOutReset function to terminate playback. |
- // |
- res = waveOutClose(_hWaveOut); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutClose() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- |
- _hWaveOut = NULL; |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_hWaveOut is now set to NULL"); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutDelay |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PlayoutDelay(uint16_t& delayMS) const |
-{ |
- CriticalSectionScoped lock(&_critSect); |
- delayMS = (uint16_t)_sndCardPlayDelay; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingDelay |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::RecordingDelay(uint16_t& delayMS) const |
-{ |
- CriticalSectionScoped lock(&_critSect); |
- delayMS = (uint16_t)_sndCardRecDelay; |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Playing |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::Playing() const |
-{ |
- return (_playing); |
-} |
-// ---------------------------------------------------------------------------- |
-// SetPlayoutBuffer |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::SetPlayoutBuffer(const AudioDeviceModule::BufferType type, uint16_t sizeMS) |
-{ |
- CriticalSectionScoped lock(&_critSect); |
- _playBufType = type; |
- if (type == AudioDeviceModule::kFixedBufferSize) |
- { |
- _playBufDelayFixed = sizeMS; |
- } |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutBuffer |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PlayoutBuffer(AudioDeviceModule::BufferType& type, uint16_t& sizeMS) const |
-{ |
- CriticalSectionScoped lock(&_critSect); |
- type = _playBufType; |
- if (type == AudioDeviceModule::kFixedBufferSize) |
- { |
- sizeMS = _playBufDelayFixed; |
- } |
- else |
- { |
- sizeMS = _playBufDelay; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// CPULoad |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::CPULoad(uint16_t& load) const |
-{ |
- |
- load = static_cast<uint16_t>(100*_avgCPULoad); |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutWarning |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::PlayoutWarning() const |
-{ |
- return ( _playWarning > 0); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayoutError |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::PlayoutError() const |
-{ |
- return ( _playError > 0); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingWarning |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::RecordingWarning() const |
-{ |
- return ( _recWarning > 0); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecordingError |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::RecordingError() const |
-{ |
- return ( _recError > 0); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ClearPlayoutWarning |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::ClearPlayoutWarning() |
-{ |
- _playWarning = 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ClearPlayoutError |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::ClearPlayoutError() |
-{ |
- _playError = 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ClearRecordingWarning |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::ClearRecordingWarning() |
-{ |
- _recWarning = 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ClearRecordingError |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::ClearRecordingError() |
-{ |
- _recError = 0; |
-} |
- |
-// ============================================================================ |
-// Private Methods |
-// ============================================================================ |
- |
-// ---------------------------------------------------------------------------- |
-// InputSanityCheckAfterUnlockedPeriod |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::InputSanityCheckAfterUnlockedPeriod() const |
-{ |
- if (_hWaveIn == NULL) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "input state has been modified during unlocked period"); |
- return -1; |
- } |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// OutputSanityCheckAfterUnlockedPeriod |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::OutputSanityCheckAfterUnlockedPeriod() const |
-{ |
- if (_hWaveOut == NULL) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "output state has been modified during unlocked period"); |
- return -1; |
- } |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// EnumeratePlayoutDevices |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::EnumeratePlayoutDevices() |
-{ |
- |
- uint16_t nDevices(PlayoutDevices()); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#output devices: %u", nDevices); |
- |
- WAVEOUTCAPS caps; |
- MMRESULT res; |
- |
- for (UINT deviceID = 0; deviceID < nDevices; deviceID++) |
- { |
- res = waveOutGetDevCaps(deviceID, &caps, sizeof(WAVEOUTCAPS)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); |
- } |
- |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Device ID %u:", deviceID); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", caps.wMid); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u",caps.wPid); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver : %u.%u", HIBYTE(caps.vDriverVersion), LOBYTE(caps.vDriverVersion)); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwFormats : 0x%x", caps.dwFormats); |
- if (caps.dwFormats & WAVE_FORMAT_48S16) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : SUPPORTED"); |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : *NOT* SUPPORTED"); |
- } |
- if (caps.dwFormats & WAVE_FORMAT_48M16) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,mono,16bit : SUPPORTED"); |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,mono,16bit : *NOT* SUPPORTED"); |
- } |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wChannels : %u", caps.wChannels); |
- TraceSupportFlags(caps.dwSupport); |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// EnumerateRecordingDevices |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::EnumerateRecordingDevices() |
-{ |
- |
- uint16_t nDevices(RecordingDevices()); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#input devices: %u", nDevices); |
- |
- WAVEINCAPS caps; |
- MMRESULT res; |
- |
- for (UINT deviceID = 0; deviceID < nDevices; deviceID++) |
- { |
- res = waveInGetDevCaps(deviceID, &caps, sizeof(WAVEINCAPS)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCaps() failed (err=%d)", res); |
- } |
- |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Device ID %u:", deviceID); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", caps.wMid); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u",caps.wPid); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver : %u.%u", HIBYTE(caps.vDriverVersion), LOBYTE(caps.vDriverVersion)); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwFormats : 0x%x", caps.dwFormats); |
- if (caps.dwFormats & WAVE_FORMAT_48S16) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : SUPPORTED"); |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : *NOT* SUPPORTED"); |
- } |
- if (caps.dwFormats & WAVE_FORMAT_48M16) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,mono,16bit : SUPPORTED"); |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,mono,16bit : *NOT* SUPPORTED"); |
- } |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wChannels : %u", caps.wChannels); |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// TraceSupportFlags |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::TraceSupportFlags(DWORD dwSupport) const |
-{ |
- TCHAR buf[256]; |
- |
- StringCchPrintf(buf, 128, TEXT("support flags : 0x%x "), dwSupport); |
- |
- if (dwSupport & WAVECAPS_PITCH) |
- { |
- // supports pitch control |
- StringCchCat(buf, 256, TEXT("(PITCH)")); |
- } |
- if (dwSupport & WAVECAPS_PLAYBACKRATE) |
- { |
- // supports playback rate control |
- StringCchCat(buf, 256, TEXT("(PLAYBACKRATE)")); |
- } |
- if (dwSupport & WAVECAPS_VOLUME) |
- { |
- // supports volume control |
- StringCchCat(buf, 256, TEXT("(VOLUME)")); |
- } |
- if (dwSupport & WAVECAPS_LRVOLUME) |
- { |
- // supports separate left and right volume control |
- StringCchCat(buf, 256, TEXT("(LRVOLUME)")); |
- } |
- if (dwSupport & WAVECAPS_SYNC) |
- { |
- // the driver is synchronous and will block while playing a buffer |
- StringCchCat(buf, 256, TEXT("(SYNC)")); |
- } |
- if (dwSupport & WAVECAPS_SAMPLEACCURATE) |
- { |
- // returns sample-accurate position information |
- StringCchCat(buf, 256, TEXT("(SAMPLEACCURATE)")); |
- } |
- |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%S", buf); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// TraceWaveInError |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::TraceWaveInError(MMRESULT error) const |
-{ |
- TCHAR buf[MAXERRORLENGTH]; |
- TCHAR msg[MAXERRORLENGTH]; |
- |
- StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); |
- waveInGetErrorText(error, msg, MAXERRORLENGTH); |
- StringCchCat(buf, MAXERRORLENGTH, msg); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%S", buf); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// TraceWaveOutError |
-// ---------------------------------------------------------------------------- |
- |
-void AudioDeviceWindowsWave::TraceWaveOutError(MMRESULT error) const |
-{ |
- TCHAR buf[MAXERRORLENGTH]; |
- TCHAR msg[MAXERRORLENGTH]; |
- |
- StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); |
- waveOutGetErrorText(error, msg, MAXERRORLENGTH); |
- StringCchCat(buf, MAXERRORLENGTH, msg); |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%S", buf); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PrepareStartPlayout |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PrepareStartPlayout() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_hWaveOut == NULL) |
- { |
- return -1; |
- } |
- |
- // A total of 30ms of data is immediately placed in the SC buffer |
- // |
- int8_t zeroVec[4*PLAY_BUF_SIZE_IN_SAMPLES]; // max allocation |
- memset(zeroVec, 0, 4*PLAY_BUF_SIZE_IN_SAMPLES); |
- |
- { |
- Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); |
- Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); |
- Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); |
- } |
- |
- _playAcc = 0; |
- _playWarning = 0; |
- _playError = 0; |
- _dc_diff_mean = 0; |
- _dc_y_prev = 0; |
- _dc_penalty_counter = 20; |
- _dc_prevtime = 0; |
- _dc_prevplay = 0; |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PrepareStartRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::PrepareStartRecording() |
-{ |
- |
- CriticalSectionScoped lock(&_critSect); |
- |
- if (_hWaveIn == NULL) |
- { |
- return -1; |
- } |
- |
- _playAcc = 0; |
- _recordedBytes = 0; |
- _recPutBackDelay = REC_PUT_BACK_DELAY; |
- |
- MMRESULT res; |
- MMTIME mmtime; |
- mmtime.wType = TIME_SAMPLES; |
- |
- res = waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetPosition(TIME_SAMPLES) failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- _read_samples = mmtime.u.sample; |
- _read_samples_old = _read_samples; |
- _rec_samples_old = mmtime.u.sample; |
- _wrapCounter = 0; |
- |
- for (int n = 0; n < N_BUFFERS_IN; n++) |
- { |
- const uint8_t nBytesPerSample = 2*_recChannels; |
- |
- // set up the input wave header |
- _waveHeaderIn[n].lpData = reinterpret_cast<LPSTR>(&_recBuffer[n]); |
- _waveHeaderIn[n].dwBufferLength = nBytesPerSample * REC_BUF_SIZE_IN_SAMPLES; |
- _waveHeaderIn[n].dwFlags = 0; |
- _waveHeaderIn[n].dwBytesRecorded = 0; |
- _waveHeaderIn[n].dwUser = 0; |
- |
- memset(_recBuffer[n], 0, nBytesPerSample * REC_BUF_SIZE_IN_SAMPLES); |
- |
- // prepare a buffer for waveform-audio input |
- res = waveInPrepareHeader(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInPrepareHeader(%d) failed (err=%d)", n, res); |
- TraceWaveInError(res); |
- } |
- |
- // send an input buffer to the given waveform-audio input device |
- res = waveInAddBuffer(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInAddBuffer(%d) failed (err=%d)", n, res); |
- TraceWaveInError(res); |
- } |
- } |
- |
- // start input on the given waveform-audio input device |
- res = waveInStart(_hWaveIn); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInStart() failed (err=%d)", res); |
- TraceWaveInError(res); |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// GetPlayoutBufferDelay |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::GetPlayoutBufferDelay(uint32_t& writtenSamples, uint32_t& playedSamples) |
-{ |
- int i; |
- int ms_Header; |
- long playedDifference; |
- int msecInPlayoutBuffer(0); // #milliseconds of audio in the playout buffer |
- |
- const uint16_t nSamplesPerMs = (uint16_t)(N_PLAY_SAMPLES_PER_SEC/1000); // default is 48000/1000 = 48 |
- |
- MMRESULT res; |
- MMTIME mmtime; |
- |
- if (!_playing) |
- { |
- playedSamples = 0; |
- return (0); |
- } |
- |
- // Retrieve the current playback position. |
- // |
- mmtime.wType = TIME_SAMPLES; // number of waveform-audio samples |
- res = waveOutGetPosition(_hWaveOut, &mmtime, sizeof(mmtime)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetPosition() failed (err=%d)", res); |
- TraceWaveOutError(res); |
- } |
- |
- writtenSamples = _writtenSamples; // #samples written to the playout buffer |
- playedSamples = mmtime.u.sample; // current playout position in the playout buffer |
- |
- // derive remaining amount (in ms) of data in the playout buffer |
- msecInPlayoutBuffer = ((writtenSamples - playedSamples)/nSamplesPerMs); |
- |
- playedDifference = (long) (_playedSamplesOld - playedSamples); |
- |
- if (playedDifference > 64000) |
- { |
- // If the sound cards number-of-played-out-samples variable wraps around before |
- // written_sampels wraps around this needs to be adjusted. This can happen on |
- // sound cards that uses less than 32 bits to keep track of number of played out |
- // sampels. To avoid being fooled by sound cards that sometimes produces false |
- // output we compare old value minus the new value with a large value. This is |
- // neccessary because some SC:s produce an output like 153, 198, 175, 230 which |
- // would trigger the wrap-around function if we didn't compare with a large value. |
- // The value 64000 is chosen because 2^16=65536 so we allow wrap around at 16 bits. |
- |
- i = 31; |
- while((_playedSamplesOld <= (unsigned long)POW2(i)) && (i > 14)) { |
- i--; |
- } |
- |
- if((i < 31) && (i > 14)) { |
- // Avoid adjusting when there is 32-bit wrap-around since that is |
- // something neccessary. |
- // |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "msecleft() => wrap around occured: %d bits used by sound card)", (i+1)); |
- |
- _writtenSamples = _writtenSamples - POW2(i + 1); |
- writtenSamples = _writtenSamples; |
- msecInPlayoutBuffer = ((writtenSamples - playedSamples)/nSamplesPerMs); |
- } |
- } |
- else if ((_writtenSamplesOld > (unsigned long)POW2(31)) && (writtenSamples < 96000)) |
- { |
- // Wrap around as expected after having used all 32 bits. (But we still |
- // test if the wrap around happened earlier which it should not) |
- |
- i = 31; |
- while (_writtenSamplesOld <= (unsigned long)POW2(i)) { |
- i--; |
- } |
- |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, " msecleft() (wrap around occured after having used all 32 bits)"); |
- |
- _writtenSamplesOld = writtenSamples; |
- _playedSamplesOld = playedSamples; |
- msecInPlayoutBuffer = (int)((writtenSamples + POW2(i + 1) - playedSamples)/nSamplesPerMs); |
- |
- } |
- else if ((writtenSamples < 96000) && (playedSamples > (unsigned long)POW2(31))) |
- { |
- // Wrap around has, as expected, happened for written_sampels before |
- // playedSampels so we have to adjust for this until also playedSampels |
- // has had wrap around. |
- |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, " msecleft() (wrap around occured: correction of output is done)"); |
- |
- _writtenSamplesOld = writtenSamples; |
- _playedSamplesOld = playedSamples; |
- msecInPlayoutBuffer = (int)((writtenSamples + POW2(32) - playedSamples)/nSamplesPerMs); |
- } |
- |
- _writtenSamplesOld = writtenSamples; |
- _playedSamplesOld = playedSamples; |
- |
- |
- // We use the following formaula to track that playout works as it should |
- // y=playedSamples/48 - timeGetTime(); |
- // y represent the clock drift between system clock and sound card clock - should be fairly stable |
- // When the exponential mean value of diff(y) goes away from zero something is wrong |
- // The exponential formula will accept 1% clock drift but not more |
- // The driver error means that we will play to little audio and have a high negative clock drift |
- // We kick in our alternative method when the clock drift reaches 20% |
- |
- int diff,y; |
- int unsigned time =0; |
- |
- // If we have other problems that causes playout glitches |
- // we don't want to switch playout method. |
- // Check if playout buffer is extremely low, or if we haven't been able to |
- // exectue our code in more than 40 ms |
- |
- time = timeGetTime(); |
- |
- if ((msecInPlayoutBuffer < 20) || (time - _dc_prevtime > 40)) |
- { |
- _dc_penalty_counter = 100; |
- } |
- |
- if ((playedSamples != 0)) |
- { |
- y = playedSamples/48 - time; |
- if ((_dc_y_prev != 0) && (_dc_penalty_counter == 0)) |
- { |
- diff = y - _dc_y_prev; |
- _dc_diff_mean = (990*_dc_diff_mean)/1000 + 10*diff; |
- } |
- _dc_y_prev = y; |
- } |
- |
- if (_dc_penalty_counter) |
- { |
- _dc_penalty_counter--; |
- } |
- |
- if (_dc_diff_mean < -200) |
- { |
- // Always reset the filter |
- _dc_diff_mean = 0; |
- |
- // Problem is detected. Switch delay method and set min buffer to 80. |
- // Reset the filter and keep monitoring the filter output. |
- // If issue is detected a second time, increase min buffer to 100. |
- // If that does not help, we must modify this scheme further. |
- |
- _useHeader++; |
- if (_useHeader == 1) |
- { |
- _minPlayBufDelay = 80; |
- _playWarning = 1; // only warn first time |
- WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, "Modification #1: _useHeader = %d, _minPlayBufDelay = %d", _useHeader, _minPlayBufDelay); |
- } |
- else if (_useHeader == 2) |
- { |
- _minPlayBufDelay = 100; // add some more safety |
- WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, "Modification #2: _useHeader = %d, _minPlayBufDelay = %d", _useHeader, _minPlayBufDelay); |
- } |
- else |
- { |
- // This state should not be entered... (HA) |
- WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1, "further actions are required!"); |
- } |
- if (_playWarning == 1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending playout warning exists"); |
- } |
- _playWarning = 1; // triggers callback from module process thread |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "kPlayoutWarning message posted: switching to alternative playout delay method"); |
- } |
- _dc_prevtime = time; |
- _dc_prevplay = playedSamples; |
- |
- // Try a very rough method of looking at how many buffers are still playing |
- ms_Header = 0; |
- for (i = 0; i < N_BUFFERS_OUT; i++) { |
- if ((_waveHeaderOut[i].dwFlags & WHDR_INQUEUE)!=0) { |
- ms_Header += 10; |
- } |
- } |
- |
- if ((ms_Header-50) > msecInPlayoutBuffer) { |
- // Test for cases when GetPosition appears to be screwed up (currently just log....) |
- TCHAR infoStr[300]; |
- if (_no_of_msecleft_warnings%20==0) |
- { |
- StringCchPrintf(infoStr, 300, TEXT("writtenSamples=%i, playedSamples=%i, msecInPlayoutBuffer=%i, ms_Header=%i"), writtenSamples, playedSamples, msecInPlayoutBuffer, ms_Header); |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "%S", infoStr); |
- } |
- _no_of_msecleft_warnings++; |
- } |
- |
- // If this is true we have had a problem with the playout |
- if (_useHeader > 0) |
- { |
- return (ms_Header); |
- } |
- |
- |
- if (ms_Header < msecInPlayoutBuffer) |
- { |
- if (_no_of_msecleft_warnings % 100 == 0) |
- { |
- TCHAR str[300]; |
- StringCchPrintf(str, 300, TEXT("_no_of_msecleft_warnings=%i, msecInPlayoutBuffer=%i ms_Header=%i (minBuffer=%i buffersize=%i writtenSamples=%i playedSamples=%i)"), |
- _no_of_msecleft_warnings, msecInPlayoutBuffer, ms_Header, _minPlayBufDelay, _playBufDelay, writtenSamples, playedSamples); |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "%S", str); |
- } |
- _no_of_msecleft_warnings++; |
- ms_Header -= 6; // Round off as we only have 10ms resolution + Header info is usually slightly delayed compared to GetPosition |
- |
- if (ms_Header < 0) |
- ms_Header = 0; |
- |
- return (ms_Header); |
- } |
- else |
- { |
- return (msecInPlayoutBuffer); |
- } |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// GetRecordingBufferDelay |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::GetRecordingBufferDelay(uint32_t& readSamples, uint32_t& recSamples) |
-{ |
- long recDifference; |
- MMTIME mmtime; |
- MMRESULT mmr; |
- |
- const uint16_t nSamplesPerMs = (uint16_t)(N_REC_SAMPLES_PER_SEC/1000); // default is 48000/1000 = 48 |
- |
- // Retrieve the current input position of the given waveform-audio input device |
- // |
- mmtime.wType = TIME_SAMPLES; |
- mmr = waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); |
- if (MMSYSERR_NOERROR != mmr) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetPosition() failed (err=%d)", mmr); |
- TraceWaveInError(mmr); |
- } |
- |
- readSamples = _read_samples; // updated for each full fram in RecProc() |
- recSamples = mmtime.u.sample; // remaining time in input queue (recorded but not read yet) |
- |
- recDifference = (long) (_rec_samples_old - recSamples); |
- |
- if( recDifference > 64000) { |
- WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 1 (recDifference =%d)", recDifference); |
- // If the sound cards number-of-recorded-samples variable wraps around before |
- // read_sampels wraps around this needs to be adjusted. This can happen on |
- // sound cards that uses less than 32 bits to keep track of number of played out |
- // sampels. To avoid being fooled by sound cards that sometimes produces false |
- // output we compare old value minus the new value with a large value. This is |
- // neccessary because some SC:s produce an output like 153, 198, 175, 230 which |
- // would trigger the wrap-around function if we didn't compare with a large value. |
- // The value 64000 is chosen because 2^16=65536 so we allow wrap around at 16 bits. |
- // |
- int i = 31; |
- while((_rec_samples_old <= (unsigned long)POW2(i)) && (i > 14)) |
- i--; |
- |
- if((i < 31) && (i > 14)) { |
- // Avoid adjusting when there is 32-bit wrap-around since that is |
- // somethying neccessary. |
- // |
- _read_samples = _read_samples - POW2(i + 1); |
- readSamples = _read_samples; |
- _wrapCounter++; |
- } else { |
- WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1,"AEC (_rec_samples_old %d recSamples %d)",_rec_samples_old, recSamples); |
- } |
- } |
- |
- if((_wrapCounter>200)){ |
- // Do nothing, handled later |
- } |
- else if((_rec_samples_old > (unsigned long)POW2(31)) && (recSamples < 96000)) { |
- WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 2 (_rec_samples_old %d recSamples %d)",_rec_samples_old, recSamples); |
- // Wrap around as expected after having used all 32 bits. |
- _read_samples_old = readSamples; |
- _rec_samples_old = recSamples; |
- _wrapCounter++; |
- return (int)((recSamples + POW2(32) - readSamples)/nSamplesPerMs); |
- |
- |
- } else if((recSamples < 96000) && (readSamples > (unsigned long)POW2(31))) { |
- WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 3 (readSamples %d recSamples %d)",readSamples, recSamples); |
- // Wrap around has, as expected, happened for rec_sampels before |
- // readSampels so we have to adjust for this until also readSampels |
- // has had wrap around. |
- _read_samples_old = readSamples; |
- _rec_samples_old = recSamples; |
- _wrapCounter++; |
- return (int)((recSamples + POW2(32) - readSamples)/nSamplesPerMs); |
- } |
- |
- _read_samples_old = _read_samples; |
- _rec_samples_old = recSamples; |
- int res=(((int)_rec_samples_old - (int)_read_samples_old)/nSamplesPerMs); |
- |
- if((res > 2000)||(res < 0)||(_wrapCounter>200)){ |
- // Reset everything |
- WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1,"msec_read error (res %d wrapCounter %d)",res, _wrapCounter); |
- MMTIME mmtime; |
- mmtime.wType = TIME_SAMPLES; |
- |
- mmr=waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); |
- if (mmr != MMSYSERR_NOERROR) { |
- WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1, "waveInGetPosition failed (mmr=%d)", mmr); |
- } |
- _read_samples=mmtime.u.sample; |
- _read_samples_old=_read_samples; |
- _rec_samples_old=mmtime.u.sample; |
- |
- // Guess a decent value |
- res = 20; |
- } |
- |
- _wrapCounter = 0; |
- return res; |
-} |
- |
-// ============================================================================ |
-// Thread Methods |
-// ============================================================================ |
- |
-// ---------------------------------------------------------------------------- |
-// ThreadFunc |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::ThreadFunc(void* pThis) |
-{ |
- return (static_cast<AudioDeviceWindowsWave*>(pThis)->ThreadProcess()); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// ThreadProcess |
-// ---------------------------------------------------------------------------- |
- |
-bool AudioDeviceWindowsWave::ThreadProcess() |
-{ |
- uint32_t time(0); |
- uint32_t playDiff(0); |
- uint32_t recDiff(0); |
- |
- LONGLONG playTime(0); |
- LONGLONG recTime(0); |
- |
- switch (_timeEvent.Wait(1000)) |
- { |
- case kEventSignaled: |
- break; |
- case kEventError: |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "EventWrapper::Wait() failed => restarting timer"); |
- _timeEvent.StopTimer(); |
- _timeEvent.StartTimer(true, TIMER_PERIOD_MS); |
- return true; |
- case kEventTimeout: |
- return true; |
- } |
- |
- time = rtc::TimeMillis(); |
- |
- if (_startPlay) |
- { |
- if (PrepareStartPlayout() == 0) |
- { |
- _prevTimerCheckTime = time; |
- _prevPlayTime = time; |
- _startPlay = false; |
- _playing = true; |
- _playStartEvent.Set(); |
- } |
- } |
- |
- if (_startRec) |
- { |
- if (PrepareStartRecording() == 0) |
- { |
- _prevTimerCheckTime = time; |
- _prevRecTime = time; |
- _prevRecByteCheckTime = time; |
- _startRec = false; |
- _recording = true; |
- _recStartEvent.Set(); |
- } |
- } |
- |
- if (_playing) |
- { |
- playDiff = time - _prevPlayTime; |
- } |
- |
- if (_recording) |
- { |
- recDiff = time - _prevRecTime; |
- } |
- |
- if (_playing || _recording) |
- { |
- RestartTimerIfNeeded(time); |
- } |
- |
- if (_playing && |
- (playDiff > (uint32_t)(_dTcheckPlayBufDelay - 1)) || |
- (playDiff < 0)) |
- { |
- Lock(); |
- if (_playing) |
- { |
- if (PlayProc(playTime) == -1) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "PlayProc() failed"); |
- } |
- _prevPlayTime = time; |
- if (playTime != 0) |
- _playAcc += playTime; |
- } |
- UnLock(); |
- } |
- |
- if (_playing && (playDiff > 12)) |
- { |
- // It has been a long time since we were able to play out, try to |
- // compensate by calling PlayProc again. |
- // |
- Lock(); |
- if (_playing) |
- { |
- if (PlayProc(playTime)) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "PlayProc() failed"); |
- } |
- _prevPlayTime = time; |
- if (playTime != 0) |
- _playAcc += playTime; |
- } |
- UnLock(); |
- } |
- |
- if (_recording && |
- (recDiff > REC_CHECK_TIME_PERIOD_MS) || |
- (recDiff < 0)) |
- { |
- Lock(); |
- if (_recording) |
- { |
- int32_t nRecordedBytes(0); |
- uint16_t maxIter(10); |
- |
- // Deliver all availiable recorded buffers and update the CPU load measurement. |
- // We use a while loop here to compensate for the fact that the multi-media timer |
- // can sometimed enter a "bad state" after hibernation where the resolution is |
- // reduced from ~1ms to ~10-15 ms. |
- // |
- while ((nRecordedBytes = RecProc(recTime)) > 0) |
- { |
- maxIter--; |
- _recordedBytes += nRecordedBytes; |
- if (recTime && _perfFreq.QuadPart) |
- { |
- // Measure the average CPU load: |
- // This is a simplified expression where an exponential filter is used: |
- // _avgCPULoad = 0.99 * _avgCPULoad + 0.01 * newCPU, |
- // newCPU = (recTime+playAcc)/f is time in seconds |
- // newCPU / 0.01 is the fraction of a 10 ms period |
- // The two 0.01 cancels each other. |
- // NOTE - assumes 10ms audio buffers. |
- // |
- _avgCPULoad = (float)(_avgCPULoad*.99 + (recTime+_playAcc)/(double)(_perfFreq.QuadPart)); |
- _playAcc = 0; |
- } |
- if (maxIter == 0) |
- { |
- // If we get this message ofte, our compensation scheme is not sufficient. |
- WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "failed to compensate for reduced MM-timer resolution"); |
- } |
- } |
- |
- if (nRecordedBytes == -1) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "RecProc() failed"); |
- } |
- |
- _prevRecTime = time; |
- |
- // Monitor the recording process and generate error/warning callbacks if needed |
- MonitorRecording(time); |
- } |
- UnLock(); |
- } |
- |
- if (!_recording) |
- { |
- _prevRecByteCheckTime = time; |
- _avgCPULoad = 0; |
- } |
- |
- return true; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// RecProc |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::RecProc(LONGLONG& consumedTime) |
-{ |
- MMRESULT res; |
- uint32_t bufCount(0); |
- uint32_t nBytesRecorded(0); |
- |
- consumedTime = 0; |
- |
- // count modulo N_BUFFERS_IN (0,1,2,...,(N_BUFFERS_IN-1),0,1,2,..) |
- if (_recBufCount == N_BUFFERS_IN) |
- { |
- _recBufCount = 0; |
- } |
- |
- bufCount = _recBufCount; |
- |
- // take mono/stereo mode into account when deriving size of a full buffer |
- const uint16_t bytesPerSample = 2*_recChannels; |
- const uint32_t fullBufferSizeInBytes = bytesPerSample * REC_BUF_SIZE_IN_SAMPLES; |
- |
- // read number of recorded bytes for the given input-buffer |
- nBytesRecorded = _waveHeaderIn[bufCount].dwBytesRecorded; |
- |
- if (nBytesRecorded == fullBufferSizeInBytes || |
- (nBytesRecorded > 0)) |
- { |
- int32_t msecOnPlaySide; |
- int32_t msecOnRecordSide; |
- uint32_t writtenSamples; |
- uint32_t playedSamples; |
- uint32_t readSamples, recSamples; |
- bool send = true; |
- |
- uint32_t nSamplesRecorded = (nBytesRecorded/bytesPerSample); // divide by 2 or 4 depending on mono or stereo |
- |
- if (nBytesRecorded == fullBufferSizeInBytes) |
- { |
- _timesdwBytes = 0; |
- } |
- else |
- { |
- // Test if it is stuck on this buffer |
- _timesdwBytes++; |
- if (_timesdwBytes < 5) |
- { |
- // keep trying |
- return (0); |
- } |
- else |
- { |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id,"nBytesRecorded=%d => don't use", nBytesRecorded); |
- _timesdwBytes = 0; |
- send = false; |
- } |
- } |
- |
- // store the recorded buffer (no action will be taken if the #recorded samples is not a full buffer) |
- _ptrAudioBuffer->SetRecordedBuffer(_waveHeaderIn[bufCount].lpData, nSamplesRecorded); |
- |
- // update #samples read |
- _read_samples += nSamplesRecorded; |
- |
- // Check how large the playout and recording buffers are on the sound card. |
- // This info is needed by the AEC. |
- // |
- msecOnPlaySide = GetPlayoutBufferDelay(writtenSamples, playedSamples); |
- msecOnRecordSide = GetRecordingBufferDelay(readSamples, recSamples); |
- |
- // If we use the alternative playout delay method, skip the clock drift compensation |
- // since it will be an unreliable estimate and might degrade AEC performance. |
- int32_t drift = (_useHeader > 0) ? 0 : GetClockDrift(playedSamples, recSamples); |
- |
- _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, drift); |
- |
- _ptrAudioBuffer->SetTypingStatus(KeyPressed()); |
- |
- // Store the play and rec delay values for video synchronization |
- _sndCardPlayDelay = msecOnPlaySide; |
- _sndCardRecDelay = msecOnRecordSide; |
- |
- LARGE_INTEGER t1={0},t2={0}; |
- |
- if (send) |
- { |
- QueryPerformanceCounter(&t1); |
- |
- // deliver recorded samples at specified sample rate, mic level etc. to the observer using callback |
- UnLock(); |
- _ptrAudioBuffer->DeliverRecordedData(); |
- Lock(); |
- |
- QueryPerformanceCounter(&t2); |
- |
- if (InputSanityCheckAfterUnlockedPeriod() == -1) |
- { |
- // assert(false); |
- return -1; |
- } |
- } |
- |
- if (_AGC) |
- { |
- uint32_t newMicLevel = _ptrAudioBuffer->NewMicLevel(); |
- if (newMicLevel != 0) |
- { |
- // The VQE will only deliver non-zero microphone levels when a change is needed. |
- WEBRTC_TRACE(kTraceStream, kTraceUtility, _id,"AGC change of volume: => new=%u", newMicLevel); |
- |
- // We store this outside of the audio buffer to avoid |
- // having it overwritten by the getter thread. |
- _newMicLevel = newMicLevel; |
- SetEvent(_hSetCaptureVolumeEvent); |
- } |
- } |
- |
- // return utilized buffer to queue after specified delay (default is 4) |
- if (_recDelayCount > (_recPutBackDelay-1)) |
- { |
- // deley buffer counter to compensate for "put-back-delay" |
- bufCount = (bufCount + N_BUFFERS_IN - _recPutBackDelay) % N_BUFFERS_IN; |
- |
- // reset counter so we can make new detection |
- _waveHeaderIn[bufCount].dwBytesRecorded = 0; |
- |
- // return the utilized wave-header after certain delay (given by _recPutBackDelay) |
- res = waveInUnprepareHeader(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInUnprepareHeader(%d) failed (err=%d)", bufCount, res); |
- TraceWaveInError(res); |
- } |
- |
- // ensure that the utilized header can be used again |
- res = waveInPrepareHeader(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInPrepareHeader(%d) failed (err=%d)", bufCount, res); |
- TraceWaveInError(res); |
- return -1; |
- } |
- |
- // add the utilized buffer to the queue again |
- res = waveInAddBuffer(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); |
- if (res != MMSYSERR_NOERROR) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInAddBuffer(%d) failed (err=%d)", bufCount, res); |
- TraceWaveInError(res); |
- if (_recPutBackDelay < 50) |
- { |
- _recPutBackDelay++; |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "_recPutBackDelay increased to %d", _recPutBackDelay); |
- } |
- else |
- { |
- if (_recError == 1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording error exists"); |
- } |
- _recError = 1; // triggers callback from module process thread |
- WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kRecordingError message posted: _recPutBackDelay=%u", _recPutBackDelay); |
- } |
- } |
- } // if (_recDelayCount > (_recPutBackDelay-1)) |
- |
- if (_recDelayCount < (_recPutBackDelay+1)) |
- { |
- _recDelayCount++; |
- } |
- |
- // increase main buffer count since one complete buffer has now been delivered |
- _recBufCount++; |
- |
- if (send) { |
- // Calculate processing time |
- consumedTime = (int)(t2.QuadPart-t1.QuadPart); |
- // handle wraps, time should not be higher than a second |
- if ((consumedTime > _perfFreq.QuadPart) || (consumedTime < 0)) |
- consumedTime = 0; |
- } |
- |
- } // if ((nBytesRecorded == fullBufferSizeInBytes)) |
- |
- return nBytesRecorded; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// PlayProc |
-// ---------------------------------------------------------------------------- |
- |
-int AudioDeviceWindowsWave::PlayProc(LONGLONG& consumedTime) |
-{ |
- int32_t remTimeMS(0); |
- int8_t playBuffer[4*PLAY_BUF_SIZE_IN_SAMPLES]; |
- uint32_t writtenSamples(0); |
- uint32_t playedSamples(0); |
- |
- LARGE_INTEGER t1; |
- LARGE_INTEGER t2; |
- |
- consumedTime = 0; |
- _waitCounter++; |
- |
- // Get number of ms of sound that remains in the sound card buffer for playback. |
- // |
- remTimeMS = GetPlayoutBufferDelay(writtenSamples, playedSamples); |
- |
- // The threshold can be adaptive or fixed. The adaptive scheme is updated |
- // also for fixed mode but the updated threshold is not utilized. |
- // |
- const uint16_t thresholdMS = |
- (_playBufType == AudioDeviceModule::kAdaptiveBufferSize) ? _playBufDelay : _playBufDelayFixed; |
- |
- if (remTimeMS < thresholdMS + 9) |
- { |
- _dTcheckPlayBufDelay = 5; |
- |
- if (remTimeMS == 0) |
- { |
- WEBRTC_TRACE(kTraceInfo, kTraceUtility, _id, "playout buffer is empty => we must adapt..."); |
- if (_waitCounter > 30) |
- { |
- _erZeroCounter++; |
- if (_erZeroCounter == 2) |
- { |
- _playBufDelay += 15; |
- _minPlayBufDelay += 20; |
- _waitCounter = 50; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0,erZero=2): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); |
- } |
- else if (_erZeroCounter == 3) |
- { |
- _erZeroCounter = 0; |
- _playBufDelay += 30; |
- _minPlayBufDelay += 25; |
- _waitCounter = 0; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0, erZero=3): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); |
- } |
- else |
- { |
- _minPlayBufDelay += 10; |
- _playBufDelay += 15; |
- _waitCounter = 50; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0, erZero=1): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); |
- } |
- } |
- } |
- else if (remTimeMS < _minPlayBufDelay) |
- { |
- // If there is less than 25 ms of audio in the play out buffer |
- // increase the buffersize limit value. _waitCounter prevents |
- // _playBufDelay to be increased every time this function is called. |
- |
- if (_waitCounter > 30) |
- { |
- _playBufDelay += 10; |
- if (_intro == 0) |
- _waitCounter = 0; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is increased: playBufDelay=%u", _playBufDelay); |
- } |
- } |
- else if (remTimeMS < thresholdMS - 9) |
- { |
- _erZeroCounter = 0; |
- } |
- else |
- { |
- _erZeroCounter = 0; |
- _dTcheckPlayBufDelay = 10; |
- } |
- |
- QueryPerformanceCounter(&t1); // measure time: START |
- |
- // Ask for new PCM data to be played out using the AudioDeviceBuffer. |
- // Ensure that this callback is executed without taking the audio-thread lock. |
- // |
- UnLock(); |
- uint32_t nSamples = _ptrAudioBuffer->RequestPlayoutData(PLAY_BUF_SIZE_IN_SAMPLES); |
- Lock(); |
- |
- if (OutputSanityCheckAfterUnlockedPeriod() == -1) |
- { |
- // assert(false); |
- return -1; |
- } |
- |
- nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); |
- if (nSamples != PLAY_BUF_SIZE_IN_SAMPLES) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "invalid number of output samples(%d)", nSamples); |
- } |
- |
- QueryPerformanceCounter(&t2); // measure time: STOP |
- consumedTime = (int)(t2.QuadPart - t1.QuadPart); |
- |
- Write(playBuffer, PLAY_BUF_SIZE_IN_SAMPLES); |
- |
- } // if (er < thresholdMS + 9) |
- else if (thresholdMS + 9 < remTimeMS ) |
- { |
- _erZeroCounter = 0; |
- _dTcheckPlayBufDelay = 2; // check buffer more often |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Need to check playout buffer more often (dT=%u, remTimeMS=%u)", _dTcheckPlayBufDelay, remTimeMS); |
- } |
- |
- // If the buffersize has been stable for 20 seconds try to decrease the buffer size |
- if (_waitCounter > 2000) |
- { |
- _intro = 0; |
- _playBufDelay--; |
- _waitCounter = 1990; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is decreased: playBufDelay=%u", _playBufDelay); |
- } |
- |
- // Limit the minimum sound card (playback) delay to adaptive minimum delay |
- if (_playBufDelay < _minPlayBufDelay) |
- { |
- _playBufDelay = _minPlayBufDelay; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is limited to %u", _minPlayBufDelay); |
- } |
- |
- // Limit the maximum sound card (playback) delay to 150 ms |
- if (_playBufDelay > 150) |
- { |
- _playBufDelay = 150; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is limited to %d", _playBufDelay); |
- } |
- |
- // Upper limit of the minimum sound card (playback) delay to 65 ms. |
- // Deactivated during "useHeader mode" (_useHeader > 0). |
- if (_minPlayBufDelay > _MAX_minBuffer && |
- (_useHeader == 0)) |
- { |
- _minPlayBufDelay = _MAX_minBuffer; |
- WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Minimum playout threshold is limited to %d", _MAX_minBuffer); |
- } |
- |
- return (0); |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// Write |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::Write(int8_t* data, uint16_t nSamples) |
-{ |
- if (_hWaveOut == NULL) |
- { |
- return -1; |
- } |
- |
- if (_playIsInitialized) |
- { |
- MMRESULT res; |
- |
- const uint16_t bufCount(_playBufCount); |
- |
- // Place data in the memory associated with _waveHeaderOut[bufCount] |
- // |
- const int16_t nBytes = (2*_playChannels)*nSamples; |
- memcpy(&_playBuffer[bufCount][0], &data[0], nBytes); |
- |
- // Send a data block to the given waveform-audio output device. |
- // |
- // When the buffer is finished, the WHDR_DONE bit is set in the dwFlags |
- // member of the WAVEHDR structure. The buffer must be prepared with the |
- // waveOutPrepareHeader function before it is passed to waveOutWrite. |
- // Unless the device is paused by calling the waveOutPause function, |
- // playback begins when the first data block is sent to the device. |
- // |
- res = waveOutWrite(_hWaveOut, &_waveHeaderOut[bufCount], sizeof(_waveHeaderOut[bufCount])); |
- if (MMSYSERR_NOERROR != res) |
- { |
- WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutWrite(%d) failed (err=%d)", bufCount, res); |
- TraceWaveOutError(res); |
- |
- _writeErrors++; |
- if (_writeErrors > 10) |
- { |
- if (_playError == 1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending playout error exists"); |
- } |
- _playError = 1; // triggers callback from module process thread |
- WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kPlayoutError message posted: _writeErrors=%u", _writeErrors); |
- } |
- |
- return -1; |
- } |
- |
- _playBufCount = (_playBufCount+1) % N_BUFFERS_OUT; // increase buffer counter modulo size of total buffer |
- _writtenSamples += nSamples; // each sample is 2 or 4 bytes |
- _writeErrors = 0; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// GetClockDrift |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::GetClockDrift(const uint32_t plSamp, const uint32_t rcSamp) |
-{ |
- int drift = 0; |
- unsigned int plSampDiff = 0, rcSampDiff = 0; |
- |
- if (plSamp >= _plSampOld) |
- { |
- plSampDiff = plSamp - _plSampOld; |
- } |
- else |
- { |
- // Wrap |
- int i = 31; |
- while(_plSampOld <= (unsigned int)POW2(i)) |
- { |
- i--; |
- } |
- |
- // Add the amount remaining prior to wrapping |
- plSampDiff = plSamp + POW2(i + 1) - _plSampOld; |
- } |
- |
- if (rcSamp >= _rcSampOld) |
- { |
- rcSampDiff = rcSamp - _rcSampOld; |
- } |
- else |
- { // Wrap |
- int i = 31; |
- while(_rcSampOld <= (unsigned int)POW2(i)) |
- { |
- i--; |
- } |
- |
- rcSampDiff = rcSamp + POW2(i + 1) - _rcSampOld; |
- } |
- |
- drift = plSampDiff - rcSampDiff; |
- |
- _plSampOld = plSamp; |
- _rcSampOld = rcSamp; |
- |
- return drift; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MonitorRecording |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::MonitorRecording(const uint32_t time) |
-{ |
- const uint16_t bytesPerSample = 2*_recChannels; |
- const uint32_t nRecordedSamples = _recordedBytes/bytesPerSample; |
- |
- if (nRecordedSamples > 5*N_REC_SAMPLES_PER_SEC) |
- { |
- // 5 seconds of audio has been recorded... |
- if ((time - _prevRecByteCheckTime) > 5700) |
- { |
- // ...and it was more than 5.7 seconds since we last did this check <=> |
- // we have not been able to record 5 seconds of audio in 5.7 seconds, |
- // hence a problem should be reported. |
- // This problem can be related to USB overload. |
- // |
- if (_recWarning == 1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording warning exists"); |
- } |
- _recWarning = 1; // triggers callback from module process thread |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "kRecordingWarning message posted: time-_prevRecByteCheckTime=%d", time - _prevRecByteCheckTime); |
- } |
- |
- _recordedBytes = 0; // restart "check again when 5 seconds are recorded" |
- _prevRecByteCheckTime = time; // reset timer to measure time for recording of 5 seconds |
- } |
- |
- if ((time - _prevRecByteCheckTime) > 8000) |
- { |
- // It has been more than 8 seconds since we able to confirm that 5 seconds of |
- // audio was recorded, hence we have not been able to record 5 seconds in |
- // 8 seconds => the complete recording process is most likely dead. |
- // |
- if (_recError == 1) |
- { |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording error exists"); |
- } |
- _recError = 1; // triggers callback from module process thread |
- WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kRecordingError message posted: time-_prevRecByteCheckTime=%d", time - _prevRecByteCheckTime); |
- |
- _prevRecByteCheckTime = time; |
- } |
- |
- return 0; |
-} |
- |
-// ---------------------------------------------------------------------------- |
-// MonitorRecording |
-// |
-// Restart timer if needed (they seem to be messed up after a hibernate). |
-// ---------------------------------------------------------------------------- |
- |
-int32_t AudioDeviceWindowsWave::RestartTimerIfNeeded(const uint32_t time) |
-{ |
- const uint32_t diffMS = time - _prevTimerCheckTime; |
- _prevTimerCheckTime = time; |
- |
- if (diffMS > 7) |
- { |
- // one timer-issue detected... |
- _timerFaults++; |
- if (_timerFaults > 5 && _timerRestartAttempts < 2) |
- { |
- // Reinitialize timer event if event fails to execute at least every 5ms. |
- // On some machines it helps and the timer starts working as it should again; |
- // however, not all machines (we have seen issues on e.g. IBM T60). |
- // Therefore, the scheme below ensures that we do max 2 attempts to restart the timer. |
- // For the cases where restart does not do the trick, we compensate for the reduced |
- // resolution on both the recording and playout sides. |
- WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, " timer issue detected => timer is restarted"); |
- _timeEvent.StopTimer(); |
- _timeEvent.StartTimer(true, TIMER_PERIOD_MS); |
- // make sure timer gets time to start up and we don't kill/start timer serveral times over and over again |
- _timerFaults = -20; |
- _timerRestartAttempts++; |
- } |
- } |
- else |
- { |
- // restart timer-check scheme since we are OK |
- _timerFaults = 0; |
- _timerRestartAttempts = 0; |
- } |
- |
- return 0; |
-} |
- |
- |
-bool AudioDeviceWindowsWave::KeyPressed() const{ |
- |
- int key_down = 0; |
- for (int key = VK_SPACE; key < VK_NUMLOCK; key++) { |
- short res = GetAsyncKeyState(key); |
- key_down |= res & 0x1; // Get the LSB |
- } |
- return (key_down > 0); |
-} |
-} // namespace webrtc |