| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/audio/cras/audio_manager_chromeos.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <utility> |
| |
| #include "ash/components/audio/audio_device.h" |
| #include "ash/components/audio/cras_audio_handler.h" |
| #include "base/bind.h" |
| #include "base/check_op.h" |
| #include "base/command_line.h" |
| #include "base/cxx17_backports.h" |
| #include "base/environment.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/nix/xdg_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/system/sys_info.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "media/audio/audio_device_description.h" |
| #include "media/audio/audio_features.h" |
| #include "media/audio/cras/cras_input.h" |
| #include "media/audio/cras/cras_unified.h" |
| #include "media/base/channel_layout.h" |
| #include "media/base/limits.h" |
| #include "media/base/localized_strings.h" |
| |
| namespace media { |
| namespace { |
| |
| using ::ash::AudioDevice; |
| using ::ash::AudioDeviceList; |
| using ::ash::CrasAudioHandler; |
| |
| // Default sample rate for input and output streams. |
| const int kDefaultSampleRate = 48000; |
| |
| // Default input buffer size. |
| const int kDefaultInputBufferSize = 1024; |
| |
| const char kInternalInputVirtualDevice[] = "Built-in mic"; |
| const char kInternalOutputVirtualDevice[] = "Built-in speaker"; |
| const char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out"; |
| |
| // Used for the Media.CrosBeamformingDeviceState histogram, currently not used |
| // since beamforming is disabled. |
| enum CrosBeamformingDeviceState { |
| BEAMFORMING_DEFAULT_ENABLED = 0, |
| BEAMFORMING_USER_ENABLED, |
| BEAMFORMING_DEFAULT_DISABLED, |
| BEAMFORMING_USER_DISABLED, |
| BEAMFORMING_STATE_MAX = BEAMFORMING_USER_DISABLED |
| }; |
| |
| bool HasKeyboardMic(const AudioDeviceList& devices) { |
| for (const auto& device : devices) { |
| if (device.is_input && |
| device.type == chromeos::AudioDeviceType::kKeyboardMic) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| const AudioDevice* GetDeviceFromId(const AudioDeviceList& devices, |
| uint64_t id) { |
| for (const auto& device : devices) { |
| if (device.id == id) { |
| return &device; |
| } |
| } |
| return nullptr; |
| } |
| |
| // Process |device_list| that two shares the same dev_index by creating a |
| // virtual device name for them. |
| void ProcessVirtualDeviceName(AudioDeviceNames* device_names, |
| const AudioDeviceList& device_list) { |
| DCHECK_EQ(2U, device_list.size()); |
| if (device_list[0].type == chromeos::AudioDeviceType::kLineout || |
| device_list[1].type == chromeos::AudioDeviceType::kLineout) { |
| device_names->emplace_back(kHeadphoneLineOutVirtualDevice, |
| base::NumberToString(device_list[0].id)); |
| } else if (device_list[0].type == |
| chromeos::AudioDeviceType::kInternalSpeaker || |
| device_list[1].type == |
| chromeos::AudioDeviceType::kInternalSpeaker) { |
| device_names->emplace_back(kInternalOutputVirtualDevice, |
| base::NumberToString(device_list[0].id)); |
| } else { |
| DCHECK(device_list[0].IsInternalMic() || device_list[1].IsInternalMic()); |
| device_names->emplace_back(kInternalInputVirtualDevice, |
| base::NumberToString(device_list[0].id)); |
| } |
| } |
| |
| // Collects flags values for whether, and in what way, the AEC, NS or AGC |
| // effects should be enforced in spite of them not being flagged as supported by |
| // the board. |
| void RetrieveSystemEffectFeatures(bool& enforce_system_aec, |
| bool& enforce_system_ns, |
| bool& enforce_system_agc, |
| bool& tuned_system_aec_allowed) { |
| const bool enforce_system_aec_ns_agc_feature = |
| base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNsAgc); |
| const bool enforce_system_aec_ns_feature = |
| base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNs); |
| const bool enforce_system_aec_agc_feature = |
| base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecAgc); |
| const bool enforce_system_aec_feature = |
| base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAec); |
| |
| enforce_system_aec = |
| enforce_system_aec_feature || enforce_system_aec_ns_agc_feature || |
| enforce_system_aec_ns_feature || enforce_system_aec_agc_feature; |
| enforce_system_ns = |
| enforce_system_aec_ns_agc_feature || enforce_system_aec_ns_feature; |
| enforce_system_agc = |
| enforce_system_aec_ns_agc_feature || enforce_system_aec_agc_feature; |
| |
| tuned_system_aec_allowed = |
| base::FeatureList::IsEnabled(features::kCrOSSystemAEC); |
| } |
| |
| // Checks if a system AEC with a specific group ID is flagged to be deactivated |
| // by the field trial. |
| bool IsSystemAecDeactivated(int aec_group_id) { |
| return base::GetFieldTrialParamByFeatureAsBool( |
| features::kCrOSSystemAECDeactivatedGroups, std::to_string(aec_group_id), |
| false); |
| } |
| |
| } // namespace |
| |
| bool AudioManagerChromeOS::HasAudioOutputDevices() { |
| return true; |
| } |
| |
| bool AudioManagerChromeOS::HasAudioInputDevices() { |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| for (size_t i = 0; i < devices.size(); ++i) { |
| if (devices[i].is_input && devices[i].is_for_simple_usage()) |
| return true; |
| } |
| return false; |
| } |
| |
| AudioManagerChromeOS::AudioManagerChromeOS( |
| std::unique_ptr<AudioThread> audio_thread, |
| AudioLogFactory* audio_log_factory) |
| : AudioManagerCrasBase(std::move(audio_thread), audio_log_factory), |
| on_shutdown_(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| weak_ptr_factory_(this) { |
| weak_this_ = weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| AudioManagerChromeOS::~AudioManagerChromeOS() = default; |
| |
| void AudioManagerChromeOS::GetAudioDeviceNamesImpl( |
| bool is_input, |
| AudioDeviceNames* device_names) { |
| DCHECK(device_names->empty()); |
| |
| device_names->push_back(AudioDeviceName::CreateDefault()); |
| |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| |
| // |dev_idx_map| is a map of dev_index and their audio devices. |
| std::map<int, AudioDeviceList> dev_idx_map; |
| for (const auto& device : devices) { |
| if (device.is_input != is_input || !device.is_for_simple_usage()) |
| continue; |
| |
| dev_idx_map[dev_index_of(device.id)].push_back(device); |
| } |
| |
| for (const auto& item : dev_idx_map) { |
| if (1 == item.second.size()) { |
| const AudioDevice& device = item.second.front(); |
| device_names->emplace_back(device.display_name, |
| base::NumberToString(device.id)); |
| } else { |
| // Create virtual device name for audio nodes that share the same device |
| // index. |
| ProcessVirtualDeviceName(device_names, item.second); |
| } |
| } |
| } |
| |
| void AudioManagerChromeOS::GetAudioInputDeviceNames( |
| AudioDeviceNames* device_names) { |
| GetAudioDeviceNamesImpl(true, device_names); |
| } |
| |
| void AudioManagerChromeOS::GetAudioOutputDeviceNames( |
| AudioDeviceNames* device_names) { |
| GetAudioDeviceNamesImpl(false, device_names); |
| } |
| |
| AudioParameters AudioManagerChromeOS::GetInputStreamParameters( |
| const std::string& device_id) { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| |
| // Check if the device has keyboard. |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| const bool has_keyboard = HasKeyboardMic(devices); |
| |
| // Retrieve buffer size. |
| int user_buffer_size = GetUserBufferSize(); |
| user_buffer_size = |
| user_buffer_size != 0 ? user_buffer_size : kDefaultInputBufferSize; |
| |
| // Retrieve the board support in terms of APM effects and properties. |
| const SystemAudioProcessingInfo system_apm_info = |
| GetSystemApmEffectsSupportedPerBoard(); |
| |
| // TODO(hshi): Fine-tune audio parameters based on |device_id|. The optimal |
| // parameters for the loopback stream may differ from the default. |
| return GetStreamParametersForSystem(user_buffer_size, has_keyboard, |
| system_apm_info); |
| } |
| |
| std::string AudioManagerChromeOS::GetAssociatedOutputDeviceID( |
| const std::string& input_device_id) { |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| |
| if (input_device_id == AudioDeviceDescription::kDefaultDeviceId) { |
| // Note: the default input should not be associated to any output, as this |
| // may lead to accidental uses of a pinned stream. |
| return ""; |
| } |
| |
| const std::string device_name = |
| GetHardwareDeviceFromDeviceId(devices, true, input_device_id); |
| |
| if (device_name.empty()) |
| return ""; |
| |
| // Now search for an output device with the same device name. |
| auto output_device_it = std::find_if( |
| devices.begin(), devices.end(), [device_name](const AudioDevice& device) { |
| return !device.is_input && device.device_name == device_name; |
| }); |
| return output_device_it == devices.end() |
| ? "" |
| : base::NumberToString(output_device_it->id); |
| } |
| |
| std::string AudioManagerChromeOS::GetDefaultInputDeviceID() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| return base::NumberToString(GetPrimaryActiveInputNode()); |
| } |
| |
| std::string AudioManagerChromeOS::GetDefaultOutputDeviceID() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| return base::NumberToString(GetPrimaryActiveOutputNode()); |
| } |
| |
| std::string AudioManagerChromeOS::GetGroupIDOutput( |
| const std::string& output_device_id) { |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| |
| return GetHardwareDeviceFromDeviceId(devices, false, output_device_id); |
| } |
| |
| std::string AudioManagerChromeOS::GetGroupIDInput( |
| const std::string& input_device_id) { |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| |
| return GetHardwareDeviceFromDeviceId(devices, true, input_device_id); |
| } |
| |
| bool AudioManagerChromeOS::Shutdown() { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| on_shutdown_.Signal(); |
| return AudioManager::Shutdown(); |
| } |
| |
| int AudioManagerChromeOS::GetDefaultOutputBufferSizePerBoard() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| int32_t buffer_size = 512; |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| if (main_task_runner_->BelongsToCurrentThread()) { |
| // Unittest may use the same thread for audio thread. |
| GetDefaultOutputBufferSizeOnMainThread(&buffer_size, &event); |
| } else { |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &AudioManagerChromeOS::GetDefaultOutputBufferSizeOnMainThread, |
| weak_this_, base::Unretained(&buffer_size), |
| base::Unretained(&event))); |
| } |
| WaitEventOrShutdown(&event); |
| return static_cast<int>(buffer_size); |
| } |
| |
| AudioManagerChromeOS::SystemAudioProcessingInfo |
| AudioManagerChromeOS::GetSystemApmEffectsSupportedPerBoard() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| SystemAudioProcessingInfo system_apm_info; |
| if (main_task_runner_->BelongsToCurrentThread()) { |
| // Unittest may use the same thread for audio thread. |
| GetSystemApmEffectsSupportedOnMainThread(&system_apm_info, &event); |
| } else { |
| // Using base::Unretained is safe here because we wait for callback be |
| // executed in main thread before local variables are destructed. |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &AudioManagerChromeOS::GetSystemApmEffectsSupportedOnMainThread, |
| weak_this_, base::Unretained(&system_apm_info), |
| base::Unretained(&event))); |
| } |
| WaitEventOrShutdown(&event); |
| return system_apm_info; |
| } |
| |
| AudioParameters AudioManagerChromeOS::GetPreferredOutputStreamParameters( |
| const std::string& output_device_id, |
| const AudioParameters& input_params) { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| |
| ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| int sample_rate = kDefaultSampleRate; |
| int buffer_size = GetUserBufferSize(); |
| if (input_params.IsValid()) { |
| channel_layout = input_params.channel_layout(); |
| sample_rate = input_params.sample_rate(); |
| if (!buffer_size) // Not user-provided. |
| buffer_size = |
| std::min(static_cast<int>(limits::kMaxAudioBufferSize), |
| std::max(static_cast<int>(limits::kMinAudioBufferSize), |
| input_params.frames_per_buffer())); |
| return AudioParameters( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate, |
| buffer_size, |
| AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, |
| limits::kMaxAudioBufferSize)); |
| } |
| |
| // Get max supported channels from |output_device_id| or the primary active |
| // one if |output_device_id| is the default device. |
| uint64_t preferred_device_id; |
| if (AudioDeviceDescription::IsDefaultDevice(output_device_id)) { |
| preferred_device_id = GetPrimaryActiveOutputNode(); |
| } else { |
| if (!base::StringToUint64(output_device_id, &preferred_device_id)) |
| preferred_device_id = 0; // 0 represents invalid |output_device_id|. |
| } |
| |
| if (preferred_device_id) { |
| AudioDeviceList devices; |
| GetAudioDevices(&devices); |
| const AudioDevice* device = GetDeviceFromId(devices, preferred_device_id); |
| if (device && device->is_input == false) { |
| channel_layout = |
| GuessChannelLayout(static_cast<int>(device->max_supported_channels)); |
| // Fall-back to old fashion: always fixed to STEREO layout. |
| if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { |
| channel_layout = CHANNEL_LAYOUT_STEREO; |
| } |
| } |
| } |
| |
| if (!buffer_size) // Not user-provided. |
| buffer_size = GetDefaultOutputBufferSizePerBoard(); |
| |
| return AudioParameters( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate, |
| buffer_size, |
| AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, |
| limits::kMaxAudioBufferSize)); |
| } |
| |
| bool AudioManagerChromeOS::IsDefault(const std::string& device_id, |
| bool is_input) { |
| AudioDeviceNames device_names; |
| GetAudioDeviceNamesImpl(is_input, &device_names); |
| DCHECK(!device_names.empty()); |
| const AudioDeviceName& device_name = device_names.front(); |
| return device_name.unique_id == device_id; |
| } |
| |
| std::string AudioManagerChromeOS::GetHardwareDeviceFromDeviceId( |
| const AudioDeviceList& devices, |
| bool is_input, |
| const std::string& device_id) { |
| uint64_t u64_device_id = 0; |
| if (AudioDeviceDescription::IsDefaultDevice(device_id)) { |
| u64_device_id = |
| is_input ? GetPrimaryActiveInputNode() : GetPrimaryActiveOutputNode(); |
| } else { |
| if (!base::StringToUint64(device_id, &u64_device_id)) |
| return ""; |
| } |
| |
| const AudioDevice* device = GetDeviceFromId(devices, u64_device_id); |
| |
| return device ? device->device_name : ""; |
| } |
| |
| void AudioManagerChromeOS::GetAudioDevices(AudioDeviceList* devices) { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| if (main_task_runner_->BelongsToCurrentThread()) { |
| GetAudioDevicesOnMainThread(devices, &event); |
| } else { |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&AudioManagerChromeOS::GetAudioDevicesOnMainThread, |
| weak_this_, base::Unretained(devices), |
| base::Unretained(&event))); |
| } |
| WaitEventOrShutdown(&event); |
| } |
| |
| void AudioManagerChromeOS::GetAudioDevicesOnMainThread( |
| AudioDeviceList* devices, |
| base::WaitableEvent* event) { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| // CrasAudioHandler is shut down before AudioManagerChromeOS. |
| if (CrasAudioHandler::Get()) |
| CrasAudioHandler::Get()->GetAudioDevices(devices); |
| event->Signal(); |
| } |
| |
| uint64_t AudioManagerChromeOS::GetPrimaryActiveInputNode() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| uint64_t device_id = 0; |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| if (main_task_runner_->BelongsToCurrentThread()) { |
| GetPrimaryActiveInputNodeOnMainThread(&device_id, &event); |
| } else { |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &AudioManagerChromeOS::GetPrimaryActiveInputNodeOnMainThread, |
| weak_this_, &device_id, &event)); |
| } |
| WaitEventOrShutdown(&event); |
| return device_id; |
| } |
| |
| uint64_t AudioManagerChromeOS::GetPrimaryActiveOutputNode() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| uint64_t device_id = 0; |
| if (main_task_runner_->BelongsToCurrentThread()) { |
| // Unittest may use the same thread for audio thread. |
| GetPrimaryActiveOutputNodeOnMainThread(&device_id, &event); |
| } else { |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &AudioManagerChromeOS::GetPrimaryActiveOutputNodeOnMainThread, |
| weak_this_, base::Unretained(&device_id), |
| base::Unretained(&event))); |
| } |
| WaitEventOrShutdown(&event); |
| return device_id; |
| } |
| |
| void AudioManagerChromeOS::GetPrimaryActiveInputNodeOnMainThread( |
| uint64_t* active_input_node_id, |
| base::WaitableEvent* event) { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| if (CrasAudioHandler::Get()) { |
| *active_input_node_id = |
| CrasAudioHandler::Get()->GetPrimaryActiveInputNode(); |
| } |
| event->Signal(); |
| } |
| |
| void AudioManagerChromeOS::GetPrimaryActiveOutputNodeOnMainThread( |
| uint64_t* active_output_node_id, |
| base::WaitableEvent* event) { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| if (CrasAudioHandler::Get()) { |
| *active_output_node_id = |
| CrasAudioHandler::Get()->GetPrimaryActiveOutputNode(); |
| } |
| event->Signal(); |
| } |
| |
| void AudioManagerChromeOS::GetDefaultOutputBufferSizeOnMainThread( |
| int32_t* buffer_size, |
| base::WaitableEvent* event) { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| if (CrasAudioHandler::Get()) |
| CrasAudioHandler::Get()->GetDefaultOutputBufferSize(buffer_size); |
| event->Signal(); |
| } |
| |
| void AudioManagerChromeOS::GetSystemApmEffectsSupportedOnMainThread( |
| SystemAudioProcessingInfo* system_apm_info, |
| base::WaitableEvent* event) { |
| DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| if (CrasAudioHandler::Get()) { |
| system_apm_info->aec_supported = |
| CrasAudioHandler::Get()->system_aec_supported(); |
| system_apm_info->aec_group_id = |
| CrasAudioHandler::Get()->system_aec_group_id(); |
| system_apm_info->ns_supported = |
| CrasAudioHandler::Get()->system_ns_supported(); |
| system_apm_info->agc_supported = |
| CrasAudioHandler::Get()->system_agc_supported(); |
| } |
| event->Signal(); |
| } |
| |
| void AudioManagerChromeOS::WaitEventOrShutdown(base::WaitableEvent* event) { |
| base::WaitableEvent* waitables[] = {event, &on_shutdown_}; |
| base::WaitableEvent::WaitMany(waitables, base::size(waitables)); |
| } |
| |
| enum CRAS_CLIENT_TYPE AudioManagerChromeOS::GetClientType() { |
| return CRAS_CLIENT_TYPE_CHROME; |
| } |
| |
| AudioParameters AudioManagerChromeOS::GetStreamParametersForSystem( |
| int user_buffer_size, |
| bool has_keyboard, |
| const AudioManagerChromeOS::SystemAudioProcessingInfo& system_apm_info) { |
| AudioParameters params( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, |
| kDefaultSampleRate, user_buffer_size, |
| AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, |
| limits::kMaxAudioBufferSize)); |
| if (has_keyboard) |
| params.set_effects(AudioParameters::KEYBOARD_MIC); |
| |
| bool enforce_system_aec; |
| bool enforce_system_ns; |
| bool enforce_system_agc; |
| bool tuned_system_aec_allowed; |
| RetrieveSystemEffectFeatures(enforce_system_aec, enforce_system_ns, |
| enforce_system_agc, tuned_system_aec_allowed); |
| |
| // Activation of the system AEC. Allow experimentation with system AEC with |
| // all devices, but enable it by default on devices that actually support it. |
| params.set_effects(params.effects() | |
| AudioParameters::EXPERIMENTAL_ECHO_CANCELLER); |
| |
| // Rephrase the field aec_supported to properly reflect its meaning in this |
| // context (since it currently signals whether an CrAS APM with tuned settings |
| // is available). |
| const bool tuned_system_apm_available = system_apm_info.aec_supported; |
| |
| // Don't use the system AEC if it is deactivated for this group ID. Also never |
| // activate NS nor AGC for this board if the AEC is not activated, since this |
| // will cause issues for the Browser AEC. |
| bool use_system_aec = |
| (tuned_system_apm_available && tuned_system_aec_allowed) || |
| enforce_system_aec; |
| |
| if (!use_system_aec || IsSystemAecDeactivated(system_apm_info.aec_group_id)) { |
| return params; |
| } |
| |
| // Activation of the system AEC. |
| params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER); |
| |
| // Don't use system NS or AGC if the AEC has board-specific tunings. |
| if (tuned_system_apm_available) { |
| return params; |
| } |
| |
| // Activation of the system NS. |
| if (system_apm_info.ns_supported || enforce_system_ns) { |
| params.set_effects(params.effects() | AudioParameters::NOISE_SUPPRESSION); |
| } |
| |
| // Activation of the system AGC. |
| if (system_apm_info.agc_supported || enforce_system_agc) { |
| params.set_effects(params.effects() | |
| AudioParameters::AUTOMATIC_GAIN_CONTROL); |
| } |
| |
| return params; |
| } |
| |
| } // namespace media |