|  | /* | 
|  | *  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/voice_engine/voe_hardware_impl.h" | 
|  |  | 
|  | #include <assert.h> | 
|  |  | 
|  | #include "webrtc/system_wrappers/include/trace.h" | 
|  | #include "webrtc/voice_engine/include/voe_errors.h" | 
|  | #include "webrtc/voice_engine/voice_engine_impl.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | VoEHardware* VoEHardware::GetInterface(VoiceEngine* voiceEngine) { | 
|  | #ifndef WEBRTC_VOICE_ENGINE_HARDWARE_API | 
|  | return NULL; | 
|  | #else | 
|  | if (NULL == voiceEngine) { | 
|  | return NULL; | 
|  | } | 
|  | VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); | 
|  | s->AddRef(); | 
|  | return s; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef WEBRTC_VOICE_ENGINE_HARDWARE_API | 
|  |  | 
|  | VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) : _shared(shared) { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "VoEHardwareImpl() - ctor"); | 
|  | } | 
|  |  | 
|  | VoEHardwareImpl::~VoEHardwareImpl() { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "~VoEHardwareImpl() - dtor"); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::SetAudioDeviceLayer(AudioLayers audioLayer) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetAudioDeviceLayer(audioLayer=%d)", audioLayer); | 
|  |  | 
|  | // Don't allow a change if VoE is initialized | 
|  | if (_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_ALREADY_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Map to AudioDeviceModule::AudioLayer | 
|  | AudioDeviceModule::AudioLayer wantedLayer( | 
|  | AudioDeviceModule::kPlatformDefaultAudio); | 
|  | switch (audioLayer) { | 
|  | case kAudioPlatformDefault: | 
|  | // already set above | 
|  | break; | 
|  | case kAudioWindowsCore: | 
|  | wantedLayer = AudioDeviceModule::kWindowsCoreAudio; | 
|  | break; | 
|  | case kAudioWindowsWave: | 
|  | wantedLayer = AudioDeviceModule::kWindowsWaveAudio; | 
|  | break; | 
|  | case kAudioLinuxAlsa: | 
|  | wantedLayer = AudioDeviceModule::kLinuxAlsaAudio; | 
|  | break; | 
|  | case kAudioLinuxPulse: | 
|  | wantedLayer = AudioDeviceModule::kLinuxPulseAudio; | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Save the audio device layer for Init() | 
|  | _shared->set_audio_device_layer(wantedLayer); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::GetAudioDeviceLayer(AudioLayers& audioLayer) { | 
|  | // Can always be called regardless of VoE state | 
|  |  | 
|  | AudioDeviceModule::AudioLayer activeLayer( | 
|  | AudioDeviceModule::kPlatformDefaultAudio); | 
|  |  | 
|  | if (_shared->audio_device()) { | 
|  | // Get active audio layer from ADM | 
|  | if (_shared->audio_device()->ActiveAudioLayer(&activeLayer) != 0) { | 
|  | _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError, | 
|  | "  Audio Device error"); | 
|  | return -1; | 
|  | } | 
|  | } else { | 
|  | // Return VoE's internal layer setting | 
|  | activeLayer = _shared->audio_device_layer(); | 
|  | } | 
|  |  | 
|  | // Map to AudioLayers | 
|  | switch (activeLayer) { | 
|  | case AudioDeviceModule::kPlatformDefaultAudio: | 
|  | audioLayer = kAudioPlatformDefault; | 
|  | break; | 
|  | case AudioDeviceModule::kWindowsCoreAudio: | 
|  | audioLayer = kAudioWindowsCore; | 
|  | break; | 
|  | case AudioDeviceModule::kWindowsWaveAudio: | 
|  | audioLayer = kAudioWindowsWave; | 
|  | break; | 
|  | case AudioDeviceModule::kLinuxAlsaAudio: | 
|  | audioLayer = kAudioLinuxAlsa; | 
|  | break; | 
|  | case AudioDeviceModule::kLinuxPulseAudio: | 
|  | audioLayer = kAudioLinuxPulse; | 
|  | break; | 
|  | default: | 
|  | _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError, | 
|  | "  unknown audio layer"); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | int VoEHardwareImpl::GetNumOfRecordingDevices(int& devices) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | devices = static_cast<int>(_shared->audio_device()->RecordingDevices()); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::GetNumOfPlayoutDevices(int& devices) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | devices = static_cast<int>(_shared->audio_device()->PlayoutDevices()); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::GetRecordingDeviceName(int index, | 
|  | char strNameUTF8[128], | 
|  | char strGuidUTF8[128]) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (strNameUTF8 == NULL) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "GetRecordingDeviceName() invalid argument"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note that strGuidUTF8 is allowed to be NULL | 
|  |  | 
|  | // Init len variable to length of supplied vectors | 
|  | const uint16_t strLen = 128; | 
|  |  | 
|  | // Check if length has been changed in module | 
|  | static_assert(strLen == kAdmMaxDeviceNameSize, ""); | 
|  | static_assert(strLen == kAdmMaxGuidSize, ""); | 
|  |  | 
|  | char name[strLen]; | 
|  | char guid[strLen]; | 
|  |  | 
|  | // Get names from module | 
|  | if (_shared->audio_device()->RecordingDeviceName(index, name, guid) != 0) { | 
|  | _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError, | 
|  | "GetRecordingDeviceName() failed to get device name"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Copy to vectors supplied by user | 
|  | strncpy(strNameUTF8, name, strLen); | 
|  |  | 
|  | if (strGuidUTF8 != NULL) { | 
|  | strncpy(strGuidUTF8, guid, strLen); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::GetPlayoutDeviceName(int index, | 
|  | char strNameUTF8[128], | 
|  | char strGuidUTF8[128]) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (strNameUTF8 == NULL) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "GetPlayoutDeviceName() invalid argument"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note that strGuidUTF8 is allowed to be NULL | 
|  |  | 
|  | // Init len variable to length of supplied vectors | 
|  | const uint16_t strLen = 128; | 
|  |  | 
|  | // Check if length has been changed in module | 
|  | static_assert(strLen == kAdmMaxDeviceNameSize, ""); | 
|  | static_assert(strLen == kAdmMaxGuidSize, ""); | 
|  |  | 
|  | char name[strLen]; | 
|  | char guid[strLen]; | 
|  |  | 
|  | // Get names from module | 
|  | if (_shared->audio_device()->PlayoutDeviceName(index, name, guid) != 0) { | 
|  | _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError, | 
|  | "GetPlayoutDeviceName() failed to get device name"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Copy to vectors supplied by user | 
|  | strncpy(strNameUTF8, name, strLen); | 
|  |  | 
|  | if (strGuidUTF8 != NULL) { | 
|  | strncpy(strGuidUTF8, guid, strLen); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::SetRecordingDevice(int index, | 
|  | StereoChannel recordingChannel) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetRecordingDevice(index=%d, recordingChannel=%d)", index, | 
|  | (int)recordingChannel); | 
|  | rtc::CritScope cs(_shared->crit_sec()); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool isRecording(false); | 
|  |  | 
|  | // Store state about activated recording to be able to restore it after the | 
|  | // recording device has been modified. | 
|  | if (_shared->audio_device()->Recording()) { | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetRecordingDevice() device is modified while recording" | 
|  | " is active..."); | 
|  | isRecording = true; | 
|  | if (_shared->audio_device()->StopRecording() == -1) { | 
|  | _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError, | 
|  | "SetRecordingDevice() unable to stop recording"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | // We let the module do the index sanity | 
|  |  | 
|  | // Set recording channel | 
|  | AudioDeviceModule::ChannelType recCh = AudioDeviceModule::kChannelBoth; | 
|  | switch (recordingChannel) { | 
|  | case kStereoLeft: | 
|  | recCh = AudioDeviceModule::kChannelLeft; | 
|  | break; | 
|  | case kStereoRight: | 
|  | recCh = AudioDeviceModule::kChannelRight; | 
|  | break; | 
|  | case kStereoBoth: | 
|  | // default setting kChannelBoth (<=> mono) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (_shared->audio_device()->SetRecordingChannel(recCh) != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning, | 
|  | "SetRecordingChannel() unable to set the recording channel"); | 
|  | } | 
|  |  | 
|  | // Map indices to unsigned since underlying functions need that | 
|  | uint16_t indexU = static_cast<uint16_t>(index); | 
|  |  | 
|  | int32_t res(0); | 
|  |  | 
|  | if (index == -1) { | 
|  | res = _shared->audio_device()->SetRecordingDevice( | 
|  | AudioDeviceModule::kDefaultCommunicationDevice); | 
|  | } else if (index == -2) { | 
|  | res = _shared->audio_device()->SetRecordingDevice( | 
|  | AudioDeviceModule::kDefaultDevice); | 
|  | } else { | 
|  | res = _shared->audio_device()->SetRecordingDevice(indexU); | 
|  | } | 
|  |  | 
|  | if (res != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError, | 
|  | "SetRecordingDevice() unable to set the recording device"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Init microphone, so user can do volume settings etc | 
|  | if (_shared->audio_device()->InitMicrophone() == -1) { | 
|  | _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceWarning, | 
|  | "SetRecordingDevice() cannot access microphone"); | 
|  | } | 
|  |  | 
|  | // Set number of channels | 
|  | bool available = false; | 
|  | if (_shared->audio_device()->StereoRecordingIsAvailable(&available) != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_SOUNDCARD_ERROR, kTraceWarning, | 
|  | "StereoRecordingIsAvailable() failed to query stereo recording"); | 
|  | } | 
|  |  | 
|  | if (_shared->audio_device()->SetStereoRecording(available) != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_SOUNDCARD_ERROR, kTraceWarning, | 
|  | "SetRecordingDevice() failed to set mono recording mode"); | 
|  | } | 
|  |  | 
|  | // Restore recording if it was enabled already when calling this function. | 
|  | if (isRecording) { | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetRecordingDevice() recording is now being restored..."); | 
|  | if (_shared->audio_device()->InitRecording() != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVoice, | 
|  | VoEId(_shared->instance_id(), -1), | 
|  | "SetRecordingDevice() failed to initialize recording"); | 
|  | return -1; | 
|  | } | 
|  | if (_shared->audio_device()->StartRecording() != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVoice, | 
|  | VoEId(_shared->instance_id(), -1), | 
|  | "SetRecordingDevice() failed to start recording"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::SetPlayoutDevice(int index) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetPlayoutDevice(index=%d)", index); | 
|  | rtc::CritScope cs(_shared->crit_sec()); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool isPlaying(false); | 
|  |  | 
|  | // Store state about activated playout to be able to restore it after the | 
|  | // playout device has been modified. | 
|  | if (_shared->audio_device()->Playing()) { | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetPlayoutDevice() device is modified while playout is " | 
|  | "active..."); | 
|  | isPlaying = true; | 
|  | if (_shared->audio_device()->StopPlayout() == -1) { | 
|  | _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError, | 
|  | "SetPlayoutDevice() unable to stop playout"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | // We let the module do the index sanity | 
|  |  | 
|  | // Map indices to unsigned since underlying functions need that | 
|  | uint16_t indexU = static_cast<uint16_t>(index); | 
|  |  | 
|  | int32_t res(0); | 
|  |  | 
|  | if (index == -1) { | 
|  | res = _shared->audio_device()->SetPlayoutDevice( | 
|  | AudioDeviceModule::kDefaultCommunicationDevice); | 
|  | } else if (index == -2) { | 
|  | res = _shared->audio_device()->SetPlayoutDevice( | 
|  | AudioDeviceModule::kDefaultDevice); | 
|  | } else { | 
|  | res = _shared->audio_device()->SetPlayoutDevice(indexU); | 
|  | } | 
|  |  | 
|  | if (res != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_SOUNDCARD_ERROR, kTraceError, | 
|  | "SetPlayoutDevice() unable to set the playout device"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Init speaker, so user can do volume settings etc | 
|  | if (_shared->audio_device()->InitSpeaker() == -1) { | 
|  | _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceWarning, | 
|  | "SetPlayoutDevice() cannot access speaker"); | 
|  | } | 
|  |  | 
|  | // Set number of channels | 
|  | bool available = false; | 
|  | _shared->audio_device()->StereoPlayoutIsAvailable(&available); | 
|  | if (_shared->audio_device()->SetStereoPlayout(available) != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_SOUNDCARD_ERROR, kTraceWarning, | 
|  | "SetPlayoutDevice() failed to set stereo playout mode"); | 
|  | } | 
|  |  | 
|  | // Restore playout if it was enabled already when calling this function. | 
|  | if (isPlaying) { | 
|  | WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetPlayoutDevice() playout is now being restored..."); | 
|  | if (_shared->audio_device()->InitPlayout() != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVoice, | 
|  | VoEId(_shared->instance_id(), -1), | 
|  | "SetPlayoutDevice() failed to initialize playout"); | 
|  | return -1; | 
|  | } | 
|  | if (_shared->audio_device()->StartPlayout() != 0) { | 
|  | WEBRTC_TRACE(kTraceError, kTraceVoice, | 
|  | VoEId(_shared->instance_id(), -1), | 
|  | "SetPlayoutDevice() failed to start playout"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::SetRecordingSampleRate(unsigned int samples_per_sec) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "%s", __FUNCTION__); | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->SetRecordingSampleRate(samples_per_sec); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::RecordingSampleRate(unsigned int* samples_per_sec) const { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->RecordingSampleRate(samples_per_sec); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::SetPlayoutSampleRate(unsigned int samples_per_sec) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "%s", __FUNCTION__); | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->SetPlayoutSampleRate(samples_per_sec); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::PlayoutSampleRate(unsigned int* samples_per_sec) const { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->PlayoutSampleRate(samples_per_sec); | 
|  | } | 
|  |  | 
|  | bool VoEHardwareImpl::BuiltInAECIsAvailable() const { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->BuiltInAECIsAvailable(); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::EnableBuiltInAEC(bool enable) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | return _shared->audio_device()->EnableBuiltInAEC(enable); | 
|  | } | 
|  |  | 
|  | bool VoEHardwareImpl::BuiltInAGCIsAvailable() const { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->BuiltInAGCIsAvailable(); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::EnableBuiltInAGC(bool enable) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | return _shared->audio_device()->EnableBuiltInAGC(enable); | 
|  | } | 
|  |  | 
|  | bool VoEHardwareImpl::BuiltInNSIsAvailable() const { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return false; | 
|  | } | 
|  | return _shared->audio_device()->BuiltInNSIsAvailable(); | 
|  | } | 
|  |  | 
|  | int VoEHardwareImpl::EnableBuiltInNS(bool enable) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | return _shared->audio_device()->EnableBuiltInNS(enable); | 
|  | } | 
|  |  | 
|  | #endif  // WEBRTC_VOICE_ENGINE_HARDWARE_API | 
|  |  | 
|  | }  // namespace webrtc |