| Index: chromeos/audio/cras_audio_handler.cc
|
| diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
|
| index 1907190f0ba0e52075ae06e254b6ce3cb27aaead..c6453118b4c393381d38ddf2489b37e0845530f4 100644
|
| --- a/chromeos/audio/cras_audio_handler.cc
|
| +++ b/chromeos/audio/cras_audio_handler.cc
|
| @@ -170,11 +170,11 @@ int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
|
| }
|
| }
|
|
|
| -uint64 CrasAudioHandler::GetActiveOutputNode() const {
|
| +uint64 CrasAudioHandler::GetPrimaryActiveOutputNode() const {
|
| return active_output_node_id_;
|
| }
|
|
|
| -uint64 CrasAudioHandler::GetActiveInputNode() const {
|
| +uint64 CrasAudioHandler::GetPrimaryActiveInputNode() const {
|
| return active_input_node_id_;
|
| }
|
|
|
| @@ -185,7 +185,7 @@ void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
|
| device_list->push_back(it->second);
|
| }
|
|
|
| -bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const {
|
| +bool CrasAudioHandler::GetPrimaryActiveOutputDevice(AudioDevice* device) const {
|
| const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
|
| if (!active_device || !device)
|
| return false;
|
| @@ -197,12 +197,59 @@ void CrasAudioHandler::SetKeyboardMicActive(bool active) {
|
| const AudioDevice* keyboard_mic = GetKeyboardMic();
|
| if (!keyboard_mic)
|
| return;
|
| - if (active) {
|
| - chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
|
| - AddActiveInputNode(keyboard_mic->id);
|
| - } else {
|
| - chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
|
| - RemoveActiveInputNode(keyboard_mic->id);
|
| + // Keyboard mic is invisible to chromeos users. It is always added or removed
|
| + // as additional active node.
|
| + DCHECK(active_input_node_id_ && active_input_node_id_ != keyboard_mic->id);
|
| + if (active)
|
| + AddActiveNode(keyboard_mic->id);
|
| + else
|
| + RemoveActiveNode(keyboard_mic->id);
|
| +}
|
| +
|
| +void CrasAudioHandler::AddActiveNode(uint64 node_id) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device) {
|
| + VLOG(1) << "AddActiveInputNode: Cannot find device id="
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| +
|
| + // If there is no primary active device, set |node_id| to primary active node.
|
| + if ((device->is_input && !active_input_node_id_) ||
|
| + (!device->is_input && !active_output_node_id_)) {
|
| + SwitchToDevice(*device);
|
| + return;
|
| + }
|
| + AddAdditionalActiveNode(node_id);
|
| +}
|
| +
|
| +void CrasAudioHandler::RemoveActiveNode(uint64 node_id) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device) {
|
| + VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| +
|
| + // We do NOT allow to remove the primary active node.
|
| + if (device->is_input && device->id == active_input_node_id_) {
|
| + VLOG(1) << "Cannot remove the primary active input node: "
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| + if (!device->is_input && device->id == active_output_node_id_) {
|
| + VLOG(1) << "Cannot remove the primary active output node: "
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| + RemoveActiveNodeInternal(node_id);
|
| +}
|
| +
|
| +void CrasAudioHandler::RemoveAllActiveNodes() {
|
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
|
| + it != audio_devices_.end();
|
| + ++it) {
|
| + RemoveActiveNodeInternal(it->second.id);
|
| }
|
| }
|
|
|
| @@ -215,29 +262,26 @@ bool CrasAudioHandler::has_alternative_output() const {
|
| }
|
|
|
| void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
|
| - volume_percent = min(max(volume_percent, 0), 100);
|
| - if (volume_percent <= kMuteThresholdPercent)
|
| - volume_percent = 0;
|
| - output_volume_ = volume_percent;
|
| -
|
| - if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
|
| - audio_pref_handler_->SetVolumeGainValue(*device, output_volume_);
|
| -
|
| - SetOutputNodeVolume(active_output_node_id_, output_volume_);
|
| - FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
|
| + // Set all active devices to the same volume.
|
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
|
| + it != audio_devices_.end();
|
| + it++) {
|
| + const AudioDevice& device = it->second;
|
| + if (!device.is_input && device.active)
|
| + SetOutputNodeVolumePercent(device.id, volume_percent);
|
| + }
|
| }
|
|
|
| // TODO: Rename the 'Percent' to something more meaningful.
|
| void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
|
| - // NOTE: We do not sanitize input gain values since the range is completely
|
| - // dependent on the device.
|
| - input_gain_ = gain_percent;
|
| -
|
| - if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
|
| - audio_pref_handler_->SetVolumeGainValue(*device, input_gain_);
|
| -
|
| - SetInputNodeGain(active_input_node_id_, input_gain_);
|
| - FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
|
| + // TODO(jennyz): Should we set all input devices' gain to the same level?
|
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
|
| + it != audio_devices_.end();
|
| + it++) {
|
| + const AudioDevice& device = it->second;
|
| + if (device.is_input && device.active)
|
| + SetInputNodeGainPercent(active_input_node_id_, gain_percent);
|
| + }
|
| }
|
|
|
| void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
|
| @@ -248,9 +292,14 @@ void CrasAudioHandler::SetOutputMute(bool mute_on) {
|
| if (!SetOutputMuteInternal(mute_on))
|
| return;
|
|
|
| - if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_)) {
|
| - DCHECK(!device->is_input);
|
| - audio_pref_handler_->SetMuteValue(*device, output_mute_on_);
|
| + // Save the mute state for all active output audio devices.
|
| + for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
|
| + it != audio_devices_.end();
|
| + it++) {
|
| + const AudioDevice& device = it->second;
|
| + if (!device.is_input && device.active) {
|
| + audio_pref_handler_->SetMuteValue(device, output_mute_on_);
|
| + }
|
| }
|
|
|
| FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
|
| @@ -285,22 +334,14 @@ void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
|
|
|
| void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
|
| int value) {
|
| - if (device_id == active_output_node_id_) {
|
| - SetOutputVolumePercent(value);
|
| - return;
|
| - } else if (device_id == active_input_node_id_) {
|
| - SetInputGainPercent(value);
|
| + const AudioDevice* device = GetDeviceFromId(device_id);
|
| + if (!device)
|
| return;
|
| - }
|
|
|
| - if (const AudioDevice* device = GetDeviceFromId(device_id)) {
|
| - if (!device->is_input) {
|
| - value = min(max(value, 0), 100);
|
| - if (value <= kMuteThresholdPercent)
|
| - value = 0;
|
| - }
|
| - audio_pref_handler_->SetVolumeGainValue(*device, value);
|
| - }
|
| + if (device->is_input)
|
| + SetInputNodeGainPercent(device_id, value);
|
| + else
|
| + SetOutputNodeVolumePercent(device_id, value);
|
| }
|
|
|
| void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
|
| @@ -470,6 +511,29 @@ void CrasAudioHandler::SetupAudioOutputState() {
|
| SetOutputNodeVolume(active_output_node_id_, output_volume_);
|
| }
|
|
|
| +// This sets up the state of an additional active node.
|
| +void CrasAudioHandler::SetupAdditionalActiveAudioNodeState(uint64 node_id) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device) {
|
| + VLOG(1) << "Can't set up audio state for unknown device id ="
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| +
|
| + DCHECK(node_id != active_output_node_id_ && node_id != active_input_node_id_);
|
| +
|
| + // Note: The mute state is a system wide state, we don't set mute per device,
|
| + // but just keep the mute state consistent for the active node in prefs.
|
| + // The output volume should be set to the same value for all active output
|
| + // devices. For input devices, we don't restore their gain value so far.
|
| + // TODO(jennyz): crbug.com/417418, track the status for the decison if
|
| + // we should persist input gain value in prefs.
|
| + if (!device->is_input) {
|
| + audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
|
| + SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
|
| + }
|
| +}
|
| +
|
| void CrasAudioHandler::InitializeAudioState() {
|
| ApplyAudioPolicy();
|
| GetNodes();
|
| @@ -506,6 +570,26 @@ void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
|
| SetOutputNodeVolume(node_id, volume);
|
| }
|
|
|
| +void CrasAudioHandler::SetOutputNodeVolumePercent(uint64 node_id,
|
| + int volume_percent) {
|
| + const AudioDevice* device = this->GetDeviceFromId(node_id);
|
| + if (!device || device->is_input)
|
| + return;
|
| +
|
| + volume_percent = min(max(volume_percent, 0), 100);
|
| + if (volume_percent <= kMuteThresholdPercent)
|
| + volume_percent = 0;
|
| + if (node_id == active_output_node_id_)
|
| + output_volume_ = volume_percent;
|
| +
|
| + audio_pref_handler_->SetVolumeGainValue(*device, volume_percent);
|
| +
|
| + if (device->active) {
|
| + SetOutputNodeVolume(node_id, volume_percent);
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
|
| + }
|
| +}
|
| +
|
| bool CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
|
| if (output_mute_locked_)
|
| return false;
|
| @@ -521,12 +605,29 @@ void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
|
| SetInputNodeGain(node_id, gain);
|
| }
|
|
|
| +void CrasAudioHandler::SetInputNodeGainPercent(uint64 node_id,
|
| + int gain_percent) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device || !device->is_input)
|
| + return;
|
| +
|
| + // NOTE: We do not sanitize input gain values since the range is completely
|
| + // dependent on the device.
|
| + if (active_input_node_id_ == node_id)
|
| + input_gain_ = gain_percent;
|
| +
|
| + audio_pref_handler_->SetVolumeGainValue(*device, gain_percent);
|
| +
|
| + if (device->active) {
|
| + SetInputNodeGain(node_id, gain_percent);
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
|
| + }
|
| +}
|
| +
|
| bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
|
| if (input_mute_locked_)
|
| return false;
|
|
|
| - VLOG(1) << "SetInputMuteInternal sets active input device id="
|
| - << "0x" << std::hex << active_input_node_id_ << " mute=" << mute_on;
|
| input_mute_on_ = mute_on;
|
| chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
|
| SetInputMute(mute_on);
|
| @@ -604,14 +705,14 @@ bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
|
| ++num_new_devices;
|
| // Look to see if the new device not in the old device list.
|
| AudioDevice device(*it);
|
| - if (FoundNewDevice(device))
|
| + if (FoundNewOrChangedDevice(device))
|
| return true;
|
| }
|
| }
|
| return num_old_devices != num_new_devices;
|
| }
|
|
|
| -bool CrasAudioHandler::FoundNewDevice(const AudioDevice& device) {
|
| +bool CrasAudioHandler::FoundNewOrChangedDevice(const AudioDevice& device) {
|
| const AudioDevice* device_found = GetDeviceFromId(device.id);
|
| if (!device_found)
|
| return true;
|
| @@ -621,51 +722,11 @@ bool CrasAudioHandler::FoundNewDevice(const AudioDevice& device) {
|
| << " new device: " << device.ToString()
|
| << " old device: " << device_found->ToString();
|
| return true;
|
| + } else if (device.active != device_found->active) {
|
| + return true;
|
| }
|
| - return false;
|
| -}
|
|
|
| -// Sanitize the audio node data. When a device is plugged in or unplugged, there
|
| -// should be only one NodesChanged signal from cras. However, we've observed
|
| -// the case that multiple NodesChanged signals being sent from cras. After the
|
| -// first NodesChanged being processed, chrome sets the active node properly.
|
| -// However, the NodesChanged received after the first one, can return stale
|
| -// nodes data in GetNodes call, the staled nodes data does not reflect the
|
| -// latest active node state. Since active audio node should only be set by
|
| -// chrome, the inconsistent data from cras could be the result of stale data
|
| -// described above and sanitized.
|
| -AudioDevice CrasAudioHandler::GetSanitizedAudioDevice(const AudioNode& node) {
|
| - AudioDevice device(node);
|
| - if (device.is_input) {
|
| - if (device.active && device.id != active_input_node_id_) {
|
| - LOG(WARNING) << "Stale audio device data, should not be active: "
|
| - << " device = " << device.ToString()
|
| - << " current active input node id = 0x" << std::hex
|
| - << active_input_node_id_;
|
| - device.active = false;
|
| - } else if (device.id == active_input_node_id_ && !device.active) {
|
| - LOG(WARNING) << "Stale audio device data, should be active:"
|
| - << " device = " << device.ToString()
|
| - << " current active input node id = 0x" << std::hex
|
| - << active_input_node_id_;
|
| - device.active = true;
|
| - }
|
| - } else {
|
| - if (device.active && device.id != active_output_node_id_) {
|
| - LOG(WARNING) << "Stale audio device data, should not be active: "
|
| - << " device = " << device.ToString()
|
| - << " current active output node id = 0x" << std::hex
|
| - << active_output_node_id_;
|
| - device.active = false;
|
| - } else if (device.id == active_output_node_id_ && !device.active) {
|
| - LOG(WARNING) << "Stale audio device data, should be active:"
|
| - << " device = " << device.ToString()
|
| - << " current active output node id = 0x" << std::hex
|
| - << active_output_node_id_;
|
| - device.active = true;
|
| - }
|
| - }
|
| - return device;
|
| + return false;
|
| }
|
|
|
| void CrasAudioHandler::UpdateDevicesAndSwitchActive(
|
| @@ -683,7 +744,7 @@ void CrasAudioHandler::UpdateDevicesAndSwitchActive(
|
| output_devices_pq_.pop();
|
|
|
| for (size_t i = 0; i < nodes.size(); ++i) {
|
| - AudioDevice device = GetSanitizedAudioDevice(nodes[i]);
|
| + AudioDevice device(nodes[i]);
|
| audio_devices_[device.id] = device;
|
|
|
| if (!has_alternative_input_ &&
|
| @@ -737,4 +798,57 @@ void CrasAudioHandler::HandleGetNodesError(const std::string& error_name,
|
| LOG_IF(ERROR, log_errors_) << "Failed to call GetNodes: "
|
| << error_name << ": " << error_msg;
|
| }
|
| +
|
| +void CrasAudioHandler::AddAdditionalActiveNode(uint64 node_id) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device) {
|
| + VLOG(1) << "AddActiveInputNode: Cannot find device id="
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| +
|
| + audio_devices_[node_id].active = true;
|
| + SetupAdditionalActiveAudioNodeState(node_id);
|
| +
|
| + if (device->is_input) {
|
| + DCHECK(node_id != active_input_node_id_);
|
| + chromeos::DBusThreadManager::Get()
|
| + ->GetCrasAudioClient()
|
| + ->AddActiveInputNode(node_id);
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
|
| + } else {
|
| + DCHECK(node_id != active_output_node_id_);
|
| + chromeos::DBusThreadManager::Get()
|
| + ->GetCrasAudioClient()
|
| + ->AddActiveOutputNode(node_id);
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
|
| + }
|
| +}
|
| +
|
| +void CrasAudioHandler::RemoveActiveNodeInternal(uint64 node_id) {
|
| + const AudioDevice* device = GetDeviceFromId(node_id);
|
| + if (!device) {
|
| + VLOG(1) << "RemoveActiveInputNode: Cannot find device id="
|
| + << "0x" << std::hex << node_id;
|
| + return;
|
| + }
|
| +
|
| + audio_devices_[node_id].active = false;
|
| + if (device->is_input) {
|
| + if (node_id == active_input_node_id_)
|
| + active_input_node_id_ = 0;
|
| + chromeos::DBusThreadManager::Get()
|
| + ->GetCrasAudioClient()
|
| + ->RemoveActiveInputNode(node_id);
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
|
| + } else {
|
| + if (node_id == active_output_node_id_)
|
| + active_output_node_id_ = 0;
|
| + chromeos::DBusThreadManager::Get()
|
| + ->GetCrasAudioClient()
|
| + ->RemoveActiveOutputNode(node_id);
|
| + }
|
| + FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
|
| +}
|
| +
|
| } // namespace chromeos
|
|
|