blob: 71865a35381776fefa06bcfd22ea07c61b6850d1 [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.
#pragma once
#include <memory>
#include <media/cpp/fidl.h>
#include "garnet/bin/media/audio_server/constants.h"
#include "garnet/bin/media/audio_server/gain.h"
namespace media {
namespace audio {
class Mixer;
using MixerPtr = std::unique_ptr<Mixer>;
class Mixer {
public:
static constexpr uint32_t FRAC_ONE = 1u << kPtsFractionalBits;
static constexpr uint32_t FRAC_MASK = FRAC_ONE - 1u;
virtual ~Mixer();
//
// Resampler enum
//
// This enum lists Fuchsia's available resamplers. Callers of Mixer::Select
// optionally use this enum to specify which resampler they require. Default
// allows an existing algorithm to select a resampler based on the ratio of
// incoming and outgoing sample rates.
enum class Resampler {
Default = 0,
SampleAndHold,
LinearInterpolation,
};
//
// Select
//
// Select an appropriate mixer instance, based on an optionally-specified
// resampler type, or else by the properties of source/destination formats.
//
// When calling Mixer::Select, resampler_type is optional. If caller specifies
// a particular resampler, Mixer::Select will either instantiate exactly what
// was requested, or return nullptr -- even if otherwise it could successfully
// instantiate a different one. Setting this param to non-Default says "I know
// exactly what I need: I want you to fail rather than give me anything else."
//
// If resampler_type is absent or indicates Default, the resampler type is
// determined by algorithm (as has been the case before this CL).
// For optimum system performance across changing conditions, callers should
// take care when directly specifying a resampler type, if they do so at all.
// The default should be allowed whenever possible.
static MixerPtr Select(const AudioMediaTypeDetails& src_format,
const AudioMediaTypeDetails& dst_format,
Resampler resampler_type = Resampler::Default);
//
// Mix
//
// Perform a mixing operation from the source buffer into the destination
// buffer.
//
// @param dst
// The pointer to the destination buffer into which frames will be mixed.
//
// @param dst_frames
// The total number of frames of audio which comprise the destination buffer.
//
// @param dst_offset
// The pointer to the offset (in destination frames) at which we should start
// to mix destination frames. When Mix has finished, dst_offset will be
// updated to indicate the offset into the destination buffer of the next
// frame to be mixed.
//
// @param src
// The pointer the the source buffer containing the frames to be mixed into
// the destination buffer.
//
// @param frac_src_frames
// The total number of fractional renderer frames contained by the source
// buffer.
//
// @param frac_src_offset
// A pointer to the offset (expressed in fractional renderer frames) at which
// the first frame to be mixed with the destination buffer should be sampled.
// When Mix has finished, frac_src_offset will be updated to indicate the
// offset of the sampling position of the next frame to be mixed with the
// output buffer.
//
// @param frac_step_size
// How much to increment the fractional sampling position for each output
// frame produced.
//
// @param amplitude_scale
// The scale factor for the amplitude to be applied when mixing. Currently,
// this is expressed as a 4.28 fixed point integer. See the AudioLink class
// for details.
//
// @param accumulate
// When true, the mixer will accumulate into the destination buffer (read,
// sum, clip, write-back). When false, the mixer will simply replace the
// destination buffer with its output.
//
// @param modulo
// If frac_step_size cannot perfectly express the mix's resampling ratio,
// this parameter (along with subsequent denominator) expresses any leftover
// precision. When present, modulo and denominator express a fractional value
// of frac_step_size unit that should be advanced, for each destination frame.
//
// @param denominator
// If frac_step_size cannot perfectly express the mix's resampling ratio, this
// parameter (along with precedent modulo) expresses any leftover precision.
// When present, modulo and denominator express a fractional value of
// frac_step_size unit that should be advanced, for each destination frame.
//
// @return True if the mixer is finished with this source data and will not
// need it in the future. False if the mixer has not consumed the entire
// source buffer and will need more of it in the future.
//
// TODO(mpuryear): Change frac_src_frames parameter to be (integer)
// src_frames, as number of src_frames was never intended to be fractional.
virtual bool Mix(int32_t* dst, uint32_t dst_frames, uint32_t* dst_offset,
const void* src, uint32_t frac_src_frames,
int32_t* frac_src_offset, uint32_t frac_step_size,
Gain::AScale amplitude_scale, bool accumulate,
uint32_t modulo = 0, uint32_t denominator = 1) = 0;
// When calling Mix(), we communicate the resampling rate with three
// parameters. We augment frac_step_size with modulo and denominator
// arguments that capture the remaining rate component that cannot be
// expressed by a 19.13 fixed-point step_size. Note: frac_step_size and
// frac_input_offset use the same format -- they have the same limitations
// in what they can and cannot communicate. This begs two questions:
//
// Q1: For perfect position accuracy, don't we also need an in/out param
// to specify initial/final subframe modulo, for fractional source offset?
// A1: Yes, for optimum position accuracy (within quantization limits), we
// SHOULD incorporate running subframe position_modulo in this way.
//
// For now, we are defering this work, tracking it with MTWN-128.
//
// Q2: Why did we solve this issue for rate but not for initial position?
// A2: We solved this issue for *rate* because its effect accumulates over
// time, causing clearly measurable distortion that becomes crippling with
// larger jobs. For *position*, there is no accumulated magnification over
// time -- in analyzing the distortion that this should cause, mix job
// size would affect the distortion frequency but not amplitude. We expect
// the effects to be below audible thresholds. Until the effects are
// measurable and attributable to this jitter, we will defer this work.
//
// Reset
//
// Reset the internal state of the mixer. Will be called every time there is
// a discontinuity in the source stream. Mixer implementations should reset
// anything related to their internal filter state.
virtual void Reset() {}
//
// Filter widths
//
// The positive and negative widths of the filter for this mixer, expressed in
// fractional input renderer units. These widths convey which input frames
// will be referenced by the filter, when producing output for a specific
// instant in time. Positive filter width refers to how far forward
// (positively) the filter looks, from the PTS in question; negative filter
// width refers to how far backward (negatively) the filter looks, from that
// same PTS. Specifically...
//
// Let:
// P = pos_filter_width()
// N = neg_filter_width()
// S = An arbitrary point in time at which the input stream will be sampled.
// X = The PTS of an input frame.
//
// If (X >= (S - N)) && (X <= (S + P))
// Then input frame X is within the filter and contributes to mix operation.
//
// Conversely, input frame X contributes to the output samples S where
// (S >= X - P) and (S <= X + N)
//
inline uint32_t pos_filter_width() const { return pos_filter_width_; }
inline uint32_t neg_filter_width() const { return neg_filter_width_; }
protected:
Mixer(uint32_t pos_filter_width, uint32_t neg_filter_width);
private:
uint32_t pos_filter_width_;
uint32_t neg_filter_width_;
};
} // namespace audio
} // namespace media