blob: 03dab9e3a8040070ab2d73826eeb097da0ae869d [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/media/audio/audio_core/audio_tuner_impl.h"
#include <filesystem>
#include "src/media/audio/audio_core/audio_device.h"
#include "src/media/audio/audio_core/audio_device_manager.h"
#include "src/media/audio/audio_core/stream_usage.h"
#include "src/media/audio/lib/effects_loader/effects_loader.h"
namespace media::audio {
fidl::InterfaceRequestHandler<fuchsia::media::tuning::AudioTuner>
AudioTunerImpl::GetFidlRequestHandler() {
return bindings_.GetHandler(this);
}
void AudioTunerImpl::GetAvailableAudioEffects(GetAvailableAudioEffectsCallback callback) {
std::vector<fuchsia::media::tuning::AudioEffectType> available_effects;
for (auto& file : std::filesystem::directory_iterator("/pkg/lib")) {
if (file.is_directory()) {
continue;
}
auto lib_name = file.path().filename();
std::unique_ptr<EffectsLoader> loader;
zx_status_t status = EffectsLoader::CreateWithModule(lib_name.c_str(), &loader);
if (status != ZX_OK) {
continue;
}
for (uint32_t id = 0; id < loader->GetNumEffects(); ++id) {
fuchsia_audio_effects_description desc;
zx_status_t status = loader->GetEffectInfo(id, &desc);
if (status != ZX_OK) {
continue;
}
fuchsia::media::tuning::AudioEffectType effect;
effect.set_module_name(lib_name.string());
effect.set_effect_name(desc.name);
available_effects.push_back(std::move(effect));
}
}
callback(std::move(available_effects));
}
void AudioTunerImpl::GetAudioDeviceProfile(std::string device_id,
GetAudioDeviceProfileCallback callback) {
auto tuned_specification_it = tuned_device_specifications_.find(device_id);
auto device = tuned_specification_it != tuned_device_specifications_.end()
? tuned_specification_it->second
: GetDefaultDeviceSpecification(device_id);
callback(ToAudioDeviceTuningProfile(device.pipeline_config, device.volume_curve));
}
void AudioTunerImpl::GetDefaultAudioDeviceProfile(std::string device_id,
GetDefaultAudioDeviceProfileCallback callback) {
auto default_device = GetDefaultDeviceSpecification(device_id);
callback(ToAudioDeviceTuningProfile(default_device.pipeline_config, default_device.volume_curve));
}
void AudioTunerImpl::SetAudioDeviceProfile(std::string device_id,
fuchsia::media::tuning::AudioDeviceTuningProfile profile,
SetAudioDeviceProfileCallback callback) {
auto config = PipelineConfig(ToPipelineConfigMixGroup(std::move(profile.pipeline())));
auto volume_curve = ToVolumeCurve(profile.volume_curve());
auto promise = context_.device_manager().UpdatePipelineConfig(device_id, config, volume_curve);
context_.threading_model().FidlDomain().executor()->schedule_task(
promise.then([this, device_id, config, volume_curve,
callback = std::move(callback)](fit::result<void, zx_status_t>& result) {
if (result.is_ok()) {
auto tuned_device_it = tuned_device_specifications_.find(device_id);
if (tuned_device_it != tuned_device_specifications_.end()) {
tuned_device_it->second =
OutputDeviceSpecification{.pipeline_config = config, .volume_curve = volume_curve};
} else {
tuned_device_specifications_.emplace(
device_id,
OutputDeviceSpecification{.pipeline_config = config, .volume_curve = volume_curve});
}
callback(ZX_OK);
} else {
callback(result.take_error());
}
}));
}
void AudioTunerImpl::DeleteAudioDeviceProfile(std::string device_id,
DeleteAudioDeviceProfileCallback callback) {
if (tuned_device_specifications_.find(device_id) == tuned_device_specifications_.end()) {
callback(ZX_OK);
return;
}
auto default_device = GetDefaultDeviceSpecification(device_id);
auto promise = context_.device_manager().UpdatePipelineConfig(
device_id, default_device.pipeline_config, default_device.volume_curve);
context_.threading_model().FidlDomain().executor()->schedule_task(promise.then(
[this, device_id, callback = std::move(callback)](fit::result<void, zx_status_t>& result) {
FX_CHECK(result.is_ok());
tuned_device_specifications_.erase(device_id);
callback(ZX_OK);
}));
}
void AudioTunerImpl::SetAudioEffectConfig(std::string device_id,
fuchsia::media::tuning::AudioEffectConfig effect,
SetAudioEffectConfigCallback callback) {
if (!effect.has_instance_name() || !effect.has_configuration()) {
callback(ZX_ERR_BAD_STATE);
return;
}
auto promise = context_.device_manager().UpdateDeviceEffect(device_id, effect.instance_name(),
effect.configuration());
context_.threading_model().FidlDomain().executor()->schedule_task(
promise.then([this, device_id, effect = std::move(effect), callback = std::move(callback)](
fit::result<void, fuchsia::media::audio::UpdateEffectError>& result) {
if (result.is_ok()) {
UpdateTunedDeviceSpecification(device_id, effect) ? callback(ZX_OK)
: callback(ZX_ERR_NOT_FOUND);
} else if (result.error() == fuchsia::media::audio::UpdateEffectError::INVALID_CONFIG) {
callback(ZX_ERR_BAD_STATE);
} else {
callback(ZX_ERR_NOT_FOUND);
}
}));
}
AudioTunerImpl::OutputDeviceSpecification AudioTunerImpl::GetDefaultDeviceSpecification(
const std::string& device_id) {
auto unique_id = AudioDevice::UniqueIdFromString(device_id).take_value();
auto device_profile = context_.process_config().device_config().output_device_profile(unique_id);
return OutputDeviceSpecification{.pipeline_config = device_profile.pipeline_config(),
.volume_curve = device_profile.volume_curve()};
}
bool AudioTunerImpl::UpdateTunedDeviceSpecification(
const std::string& device_id, const fuchsia::media::tuning::AudioEffectConfig& effect) {
auto tuned_device_it = tuned_device_specifications_.find(device_id);
if (tuned_device_it == tuned_device_specifications_.end()) {
tuned_device_it =
tuned_device_specifications_.emplace(device_id, GetDefaultDeviceSpecification(device_id))
.first;
}
PipelineConfig::MixGroup& root = tuned_device_it->second.pipeline_config.mutable_root();
return UpdateTunedEffectConfig(root, effect.instance_name(), effect.configuration());
}
bool AudioTunerImpl::UpdateTunedEffectConfig(PipelineConfig::MixGroup& root,
const std::string& instance_name,
const std::string& config) {
for (PipelineConfig::Effect& effect : root.effects) {
if (instance_name == effect.instance_name) {
effect.effect_config = config;
return true;
}
}
for (PipelineConfig::MixGroup& mix_group : root.inputs) {
if (UpdateTunedEffectConfig(mix_group, instance_name, config)) {
return true;
}
}
return false;
}
} // namespace media::audio