blob: 4bca36d3b54d35e162de57febcf1550edf8d2007 [file] [log] [blame]
// Copyright 2016 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_input.h"
#include <algorithm>
#include <trace/event.h>
#include "src/media/audio/audio_core/audio_driver.h"
#include "src/media/audio/audio_core/process_config.h"
namespace media::audio {
constexpr zx::duration kMinFenceDistance = zx::msec(200);
constexpr zx::duration kMaxFenceDistance = kMinFenceDistance + zx::msec(20);
// static
std::shared_ptr<AudioInput> AudioInput::Create(zx::channel channel, ThreadingModel* threading_model,
DeviceRegistry* registry, LinkMatrix* link_matrix) {
return std::make_shared<AudioInput>(std::move(channel), threading_model, registry, link_matrix);
}
// static
std::shared_ptr<AudioInput> AudioInput::Create(
fidl::InterfaceRequest<fuchsia::hardware::audio::StreamConfig> stream_config,
ThreadingModel* threading_model, DeviceRegistry* registry, LinkMatrix* link_matrix) {
return std::make_shared<AudioInput>(std::move(stream_config), threading_model, registry,
link_matrix);
}
AudioInput::AudioInput(zx::channel channel, ThreadingModel* threading_model,
DeviceRegistry* registry, LinkMatrix* link_matrix)
: AudioDevice(Type::Input, threading_model, registry, link_matrix,
std::make_unique<AudioDriverV1>(this)),
initial_stream_channel_(std::move(channel)) {}
AudioInput::AudioInput(fidl::InterfaceRequest<fuchsia::hardware::audio::StreamConfig> stream_config,
ThreadingModel* threading_model, DeviceRegistry* registry,
LinkMatrix* link_matrix)
: AudioDevice(Type::Input, threading_model, registry, link_matrix,
std::make_unique<AudioDriverV2>(this)),
initial_stream_channel_(stream_config.TakeChannel()) {}
zx_status_t AudioInput::Init() {
TRACE_DURATION("audio", "AudioInput::Init");
zx_status_t res = AudioDevice::Init();
if (res != ZX_OK) {
return res;
}
res = driver()->Init(std::move(initial_stream_channel_));
if (res == ZX_OK) {
state_ = State::Initialized;
}
return res;
}
fit::result<std::shared_ptr<ReadableStream>, zx_status_t> AudioInput::InitializeDestLink(
const AudioObject& dest) {
return fit::ok(driver()->readable_ring_buffer());
}
void AudioInput::OnWakeup() {
TRACE_DURATION("audio", "AudioInput::OnWakeup");
// We were poked. Are we just starting up?
if (state_ == State::Initialized) {
if (driver()->GetDriverInfo() != ZX_OK) {
ShutdownSelf();
} else {
state_ = State::FetchingFormats;
}
return;
}
UpdateDriverGainState();
}
void AudioInput::OnDriverInfoFetched() {
TRACE_DURATION("audio", "AudioInput::OnDriverInfoFetched");
state_ = State::Idle;
uint32_t pref_fps = ProcessConfig::instance()
.device_config()
.input_device_profile(driver()->persistent_unique_id())
.rate();
uint32_t pref_chan = 1;
fuchsia::media::AudioSampleFormat pref_fmt = fuchsia::media::AudioSampleFormat::SIGNED_16;
zx_status_t res = driver()->SelectBestFormat(&pref_fps, &pref_chan, &pref_fmt);
if (res != ZX_OK) {
FX_LOGS(ERROR) << "Audio input failed to find any compatible driver formats. Req was "
<< pref_fps << " Hz " << pref_chan << " channel(s) sample format(0x" << std::hex
<< static_cast<uint32_t>(pref_fmt) << ")";
ShutdownSelf();
return;
}
auto format_result = Format::Create(fuchsia::media::AudioStreamType{
.sample_format = pref_fmt,
.channels = pref_chan,
.frames_per_second = pref_fps,
});
if (format_result.is_error()) {
FX_LOGS(ERROR) << "Driver format is invalid";
ShutdownSelf();
return;
}
auto& selected_format = format_result.value();
const auto& hw_gain = driver()->hw_gain_state();
if (hw_gain.min_gain > hw_gain.max_gain) {
FX_LOGS(ERROR) << "Audio input has invalid gain limits [" << hw_gain.min_gain << ", "
<< hw_gain.max_gain << "].";
ShutdownSelf();
return;
}
// TODO(mpuryear): Save to the hub the configured format for this input.
// Send config request; recompute distance between start|end sampling fences.
driver()->Configure(selected_format, kMaxFenceDistance);
// Tell AudioDeviceManager it can add us to the set of active audio devices.
ActivateSelf();
}
void AudioInput::OnDriverConfigComplete() {
TRACE_DURATION("audio", "AudioInput::OnDriverConfigComplete");
driver()->SetPlugDetectEnabled(true);
}
void AudioInput::OnDriverStartComplete() {
TRACE_DURATION("audio", "AudioInput::OnDriverStartComplete");
// If we were unplugged while starting, stop now.
if (!driver()->plugged()) {
driver()->Stop();
}
}
void AudioInput::OnDriverStopComplete() {
TRACE_DURATION("audio", "AudioInput::OnDriverStopComplete");
// If we were plugged while stopping, start now.
if (driver()->plugged()) {
driver()->Start();
}
}
void AudioInput::OnDriverPlugStateChange(bool plugged, zx::time plug_time) {
TRACE_DURATION("audio", "AudioInput::OnDriverPlugStateChange");
if (plugged && (driver()->state() == AudioDriver::State::Configured)) {
driver()->Start();
} else if (!plugged && (driver()->state() == AudioDriver::State::Started)) {
driver()->Stop();
}
AudioDevice::OnDriverPlugStateChange(plugged, plug_time);
}
void AudioInput::ApplyGainLimits(fuchsia::media::AudioGainInfo* in_out_info, uint32_t set_flags) {
TRACE_DURATION("audio", "AudioInput::ApplyGainLimits");
// By the time anyone is calling "ApplyGainLimits", we need to have our basic
// audio gain control capabilities established.
ZX_DEBUG_ASSERT(driver()->state() != AudioDriver::State::Uninitialized);
ZX_DEBUG_ASSERT(driver()->state() != AudioDriver::State::MissingDriverInfo);
const auto& caps = driver()->hw_gain_state();
// If someone is trying to enable mute, but our hardware does not support
// enabling mute, clear the flag.
//
// TODO(johngro): It should always be possible to mute. We should maintain a
// SW flag for implementing mute in case the hardware cannot.
if (!caps.can_mute) {
in_out_info->flags &= ~(fuchsia::media::AudioGainInfoFlag_Mute);
}
// Don't allow AGC unless HW supports it.
if (!caps.can_agc) {
in_out_info->flags &= ~(fuchsia::media::AudioGainInfoFlag_AgcEnabled);
}
// If the user is attempting to set gain, enforce the gain limits.
if (set_flags & fuchsia::media::SetAudioGainFlag_GainValid) {
// This should have been enforced in OnDriverInfoFetched.
FX_DCHECK(caps.min_gain <= caps.max_gain);
// If the hardware has not supplied a valid gain step size, or an
// ridiculously small step size, just apply a clamp based on min/max.
constexpr float kStepSizeLimit = 1e-6;
if (caps.gain_step <= kStepSizeLimit) {
in_out_info->gain_db = std::clamp(in_out_info->gain_db, caps.min_gain, caps.max_gain);
} else {
auto min_steps = static_cast<int32_t>(caps.min_gain / caps.gain_step);
auto max_steps = static_cast<int32_t>(caps.max_gain / caps.gain_step);
int32_t steps = std::clamp(static_cast<int32_t>(in_out_info->gain_db / caps.gain_step),
min_steps, max_steps);
in_out_info->gain_db = static_cast<float>(steps) * caps.gain_step;
}
}
}
void AudioInput::UpdateDriverGainState() {
TRACE_DURATION("audio", "AudioInput::UpdateDriverGainState");
auto settings = device_settings();
if ((state_ != State::Idle) || (settings == nullptr)) {
return;
}
AudioDeviceSettings::GainState state;
audio_set_gain_flags_t dirty_flags = settings->SnapshotGainState(&state);
if (!dirty_flags) {
return;
}
driver()->SetGain(state, dirty_flags);
}
} // namespace media::audio