blob: 2aebbc6104268e4516f949aee062cd7a3e54ba93 [file] [log] [blame]
// Copyright 2022 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/lib/processing/position_manager.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <algorithm>
#include <ffl/string.h>
#include "src/media/audio/lib/format2/fixed.h"
namespace media_audio {
namespace {
// Enable to emit trace events containing the position state.
// TODO(fxbug.dev/87651): Move this constant to a common location if needed during audio core
// `media::audio::Mixer` migration.
constexpr bool kTracePositionEvents = false;
} // namespace
PositionManager::PositionManager(int32_t source_channel_count, int32_t dest_channel_count,
int64_t frac_positive_length, int64_t frac_negative_length)
: source_channel_count_(source_channel_count),
dest_channel_count_(dest_channel_count),
frac_positive_length_(frac_positive_length),
frac_negative_length_(frac_negative_length) {
FX_CHECK(frac_positive_length_ > 0);
FX_CHECK(frac_negative_length_ > 0);
}
void PositionManager::CheckPositions(int64_t dest_frame_count, int64_t* dest_offset_ptr,
int64_t source_frame_count, int64_t frac_source_offset,
int64_t frac_pos_filter_length, int64_t frac_step_size,
uint64_t rate_modulo, uint64_t denominator,
uint64_t source_position_modulo) {
CheckDestPositions(dest_frame_count, *dest_offset_ptr);
CheckSourcePositions(source_frame_count, frac_source_offset, frac_pos_filter_length);
CheckRateValues(frac_step_size, rate_modulo, denominator, source_position_modulo);
}
void PositionManager::CheckDestPositions(int64_t dest_frame_count, int64_t dest_offset) {
// Location of first destination frame cannot be negative.
FX_CHECK(dest_offset >= 0) << "dest_offset (" << dest_offset << ") must be non-negative";
// Location of first destination frame to produce must be within the provided buffer.
FX_CHECK(dest_offset < dest_frame_count)
<< "dest_offset (" << dest_offset << ") must be less than dest_frame_count ("
<< dest_frame_count << ")";
}
void PositionManager::CheckSourcePositions(int64_t source_frame_count, int64_t frac_source_offset,
int64_t frac_pos_filter_length) {
FX_CHECK(source_frame_count > 0) << "Source buffer must have at least one frame";
FX_CHECK(frac_pos_filter_length > 0)
<< "Mixer lookahead frac_pos_filter_length (" << ffl::String::DecRational
<< Fixed::FromRaw(frac_pos_filter_length) << ") must be positive";
// Source offset can be negative but only within bounds of `frac_pos_filter_length`.
FX_CHECK(frac_source_offset + frac_pos_filter_length > 0)
<< "frac_source_offset (" << ffl::String::DecRational << Fixed::FromRaw(frac_source_offset)
<< ") must be greater than -pos_length (" << Fixed::FromRaw(-frac_pos_filter_length) << ")";
// Source offset cannot exceed `source_frame_count`.
FX_CHECK(((frac_source_offset - 1) >> Fixed::Format::FractionalBits) < source_frame_count)
<< "frac_source_offset: " << ffl::String::DecRational << Fixed::FromRaw(frac_source_offset)
<< ", source_frame_count: " << source_frame_count;
}
void PositionManager::CheckRateValues(int64_t frac_step_size, uint64_t rate_modulo,
uint64_t denominator, uint64_t source_position_modulo) {
FX_CHECK(frac_step_size > 0) << "step_size must be positive; cannot be zero";
FX_CHECK(denominator > 0) << "denominator cannot be zero";
FX_CHECK(rate_modulo < denominator) << "rate_modulo (" << rate_modulo
<< ") must be less than denominator (" << denominator << ")";
FX_CHECK(source_position_modulo < denominator)
<< "source_position_modulo (" << source_position_modulo << ") must be less than denominator ("
<< denominator << ")";
}
void PositionManager::Display() const {
FX_LOGS(INFO) << "Channels: source " << source_channel_count_ << ", dest " << dest_channel_count_
<< ". Filter Length: pos " << ffl::String::DecRational
<< Fixed::FromRaw(frac_positive_length_) << ", neg "
<< Fixed::FromRaw(frac_negative_length_);
FX_LOGS(INFO) << "Source: len " << source_frame_count_ << ", to " << ffl::String::DecRational
<< Fixed::FromRaw(frac_source_end_) << ". Dest: len " << dest_frame_count_;
FX_LOGS(INFO) << "Rate: frac_step_size " << ffl::String::DecRational << Fixed(frac_step_size_)
<< ", rate_mod " << rate_modulo_ << ", denom " << denominator_;
DisplayUpdate();
}
void PositionManager::DisplayUpdate() const {
FX_LOGS(INFO) << "Position: frac_source_offset " << ffl::String::DecRational
<< Fixed::FromRaw(frac_source_offset_) << ": dest_offset " << dest_offset_
<< ", pos_mod " << source_pos_modulo_;
}
void PositionManager::SetDestValues(float* dest_ptr, int64_t dest_frame_count,
int64_t* dest_offset_ptr) {
if (kTracePositionEvents) {
TRACE_DURATION("audio", __func__, "dest_frame_count", dest_frame_count, "dest_offset",
*dest_offset_ptr);
}
CheckDestPositions(dest_frame_count, *dest_offset_ptr);
dest_ptr_ = dest_ptr;
dest_frame_count_ = dest_frame_count;
dest_offset_ptr_ = dest_offset_ptr;
dest_offset_ = *dest_offset_ptr_;
}
void PositionManager::SetSourceValues(const void* source_void_ptr, int64_t source_frame_count,
Fixed* source_offset_ptr) {
if (kTracePositionEvents) {
TRACE_DURATION("audio", __func__, "source_frame_count", source_frame_count, "source_offset",
source_offset_ptr->Integral().Floor(), "source_offset.frac",
source_offset_ptr->Fraction().raw_value());
}
source_void_ptr_ = const_cast<void*>(source_void_ptr);
source_frame_count_ = source_frame_count;
source_offset_ptr_ = source_offset_ptr;
frac_source_offset_ = source_offset_ptr_->raw_value();
// `frac_source_end_` is the first subframe at which this call can no longer produce output.
frac_source_end_ =
(source_frame_count << Fixed::Format::FractionalBits) - frac_positive_length_ + 1;
}
void PositionManager::SetRateValues(int64_t frac_step_size, uint64_t rate_modulo,
uint64_t denominator, uint64_t* source_pos_mod) {
if (kTracePositionEvents) {
TRACE_DURATION("audio", __func__, "step_size",
Fixed::FromRaw(frac_step_size).Integral().Floor(), "step_size.frac",
Fixed::FromRaw(frac_step_size).Fraction().raw_value(), "rate_modulo",
rate_modulo, "denominator", denominator);
}
CheckRateValues(frac_step_size, rate_modulo, denominator, *source_pos_mod);
frac_step_size_ = frac_step_size;
rate_modulo_ = rate_modulo;
if (rate_modulo_ > 0) {
denominator_ = denominator;
source_pos_modulo_ptr_ = source_pos_mod;
source_pos_modulo_ = *source_pos_modulo_ptr_;
}
}
int64_t PositionManager::AdvanceToEnd() {
if (!CanFrameBeMixed()) {
return 0;
}
// Number of source steps available, if no rate modulo is in effect.
const int64_t est_dest_frame_count_produced =
(frac_source_end_ - frac_source_offset_ - 1) / frac_step_size_ + 1;
const int64_t dest_frame_count_space_avail = dest_frame_count_ - dest_offset_;
const int64_t avail = std::min(est_dest_frame_count_produced, dest_frame_count_space_avail);
const auto prev_source_frame_consumed =
(frac_source_offset_ + frac_positive_length_ - 1) >> Fixed::Format::FractionalBits;
// Advance source and destination by `avail` steps.
frac_source_offset_ += (avail * frac_step_size_);
dest_offset_ += avail;
if (rate_modulo_) {
// Compute the modulo after advancing, and increment `frac_source_offset_` accordingly.
const uint64_t total_mod = source_pos_modulo_ + (avail * rate_modulo_);
frac_source_offset_ += total_mod / denominator_;
source_pos_modulo_ = total_mod % denominator_;
// Maintain an offset of previous source, for the last destination frame we would produce.
int64_t prev_source_offset = frac_source_offset_ - frac_step_size_;
if (source_pos_modulo_ < rate_modulo_) {
--prev_source_offset;
}
// If the rough estimate advanced position too far, roll position back until it is correct. For
// the final destination frame we produce, `prev_source_offset` must be less than
// `frac_source_end_`.
while (prev_source_offset >= frac_source_end_) {
if (source_pos_modulo_ < rate_modulo_) {
source_pos_modulo_ += denominator_;
}
source_pos_modulo_ -= rate_modulo_;
--dest_offset_;
frac_source_offset_ = prev_source_offset;
prev_source_offset = frac_source_offset_ - frac_step_size_;
if (source_pos_modulo_ < rate_modulo_) {
--prev_source_offset;
}
}
}
const auto new_source_frame_consumed =
(frac_source_offset_ + frac_positive_length_ - 1) >> Fixed::Format::FractionalBits;
return new_source_frame_consumed - prev_source_frame_consumed;
}
void PositionManager::UpdateOffsets() {
if constexpr (kTracePositionEvents) {
TRACE_DURATION("audio", __func__, "source_offset",
Fixed::FromRaw(frac_source_offset_).Integral().Floor(), "source_offset.frac",
Fixed::FromRaw(frac_source_offset_).Fraction().raw_value(), "dest_offset",
dest_offset_, "source_pos_modulo", source_pos_modulo_);
}
*source_offset_ptr_ = Fixed::FromRaw(frac_source_offset_);
*dest_offset_ptr_ = dest_offset_;
if (rate_modulo_) {
*source_pos_modulo_ptr_ = source_pos_modulo_;
}
}
} // namespace media_audio