blob: 526e3c223fdafadee97cd1216adb42550ca48fa6 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_FILTER_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_FILTER_H_
#include <cmath>
#include <vector>
#include "src/lib/syslog/cpp/logger.h"
#include "src/media/audio/audio_core/mixer/coefficient_table.h"
#include "src/media/audio/audio_core/mixer/constants.h"
namespace media::audio::mixer {
static constexpr uint32_t kSincFilterSideTaps = 13;
static constexpr uint32_t kSincFilterSideLength = (kSincFilterSideTaps + 1) << kPtsFractionalBits;
// This class represents a convolution-based filter, to be applied to an audio stream. Subclasses
// represent specific filters for nearest-neighbor interpolation, linear interpolation, and
// sinc-based band-pass. Note that each child class owns creating and populating its own
// filter_coefficients vector. More on these details below.
class Filter {
public:
Filter(uint32_t source_rate, uint32_t dest_rate, uint32_t side_width = kSincFilterSideLength,
uint32_t num_frac_bits = kPtsFractionalBits)
: source_rate_(source_rate),
dest_rate_(dest_rate),
side_width_(side_width),
num_frac_bits_(num_frac_bits),
frac_size_(1u << num_frac_bits),
rate_conversion_ratio_(static_cast<double>(dest_rate_) / source_rate_) {
FX_DCHECK(source_rate_ > 0);
FX_DCHECK(dest_rate_ > 0);
FX_DCHECK(side_width > 0);
FX_DCHECK(num_frac_bits_ > 0);
}
virtual float ComputeSample(uint32_t frac_offset, float* center) = 0;
// used for debugging purposes only
virtual void Display() = 0;
void DisplayTable(const CoefficientTable& filter_coefficients);
float ComputeSampleFromTable(const CoefficientTable& filter_coefficients, uint32_t frac_offset,
float* center);
uint32_t source_rate() const { return source_rate_; }
uint32_t dest_rate() const { return dest_rate_; }
uint32_t side_width() const { return side_width_; }
uint32_t num_frac_bits() const { return num_frac_bits_; }
uint32_t frac_size() const { return frac_size_; }
double rate_conversion_ratio() const { return rate_conversion_ratio_; };
private:
uint32_t source_rate_;
uint32_t dest_rate_;
uint32_t side_width_;
uint32_t num_frac_bits_;
uint32_t frac_size_;
double rate_conversion_ratio_;
};
// These child classes differ only in their filter coefficients. As mentioned above, each child
// class owns its own filter_coefficients vector, which represents one side of the filter (these
// classes expect the convolution filter to be symmetric). Also, filter coefficients cover the
// entire discrete space of fractional position values, but for any calculation we reference only a
// small subset of these values (using a stride size of one source frame: frac_size_).
//
// Nearest-neighbor "zero-order interpolation" resampler, implemented using the convolution
// filter. Width on both sides is FRAC_HALF (expressed in our fixed-point fractional scale),
// modulo the stretching effects of downsampling.
//
// Why do we say Point Interpolation's filter width is "FRAC_HALF", even as we send FRAC_HALF+1?
// Let's pretend that frac_size is 1000. Filter_width 501 includes coefficient values for locations
// from that exact position, up to positions as much as 500 away. This means:
// -Fractional source pos 1.499 requires frames between 0.999 and 1.999, thus source frame 1
// -Fractional source pos 1.500 requires frames between 1.000 and 2.000, thus source frames 1 and 2
// -Fractional source pos 1.501 requires frames between 1.001 and 2.001, thus source frame 2
// For frac src pos .5, we average the pre- and post- values so as to achieve zero phase delay
//
// TODO(37356): Make the fixed-point fractional scale typesafe.
class PointFilter : public Filter {
public:
PointFilter(uint32_t source_rate, uint32_t dest_rate, uint32_t num_frac_bits = kPtsFractionalBits)
: Filter(source_rate, dest_rate, /* side_width= */ (1u << (num_frac_bits - 1u)) + 1u,
num_frac_bits),
filter_coefficients_(side_width(), num_frac_bits) {
SetUpFilterCoefficients();
}
PointFilter() : PointFilter(48000, 48000){};
float ComputeSample(uint32_t frac_offset, float* center) override {
return ComputeSampleFromTable(filter_coefficients_, frac_offset, center);
}
void Display() override { DisplayTable(filter_coefficients_); }
float& operator[](size_t index) { return filter_coefficients_[index]; }
private:
void SetUpFilterCoefficients();
CoefficientTable filter_coefficients_;
};
// Linear interpolation, implemented using the convolution filter.
// Width on both sides is FRAC_ONE-1, modulo the stretching effects of downsampling.
//
// Why do we say Linear Interpolation's filter width is "FRAC_ONE-1", although we send FRAC_ONE?
// Let's pretend that frac_size is 1000. Filter_width 1000 includes coefficient values for locations
// from that exact position, up to positions as much as 999 away. This means:
// -Fractional source pos 1.999 requires frames between 1.000 and 2.998, thus source frames 1 and 2
// -Fractional source pos 2.000 requires frames between 1.001 and 2.999, thus source frame 2 only
// -Fractional source pos 2.001 requires frames between 1.002 and 3.000, thus source frames 2 and 3
// For frac src pos .0, we use that value precisely; no need to interpolate with any neighbor
class LinearFilter : public Filter {
public:
LinearFilter(uint32_t source_rate, uint32_t dest_rate,
uint32_t num_frac_bits = kPtsFractionalBits)
: Filter(source_rate, dest_rate, /* side_width= */ 1u << num_frac_bits, num_frac_bits),
filter_coefficients_(side_width(), num_frac_bits) {
SetUpFilterCoefficients();
}
LinearFilter() : LinearFilter(48000, 48000){};
float ComputeSample(uint32_t frac_offset, float* center) override {
return ComputeSampleFromTable(filter_coefficients_, frac_offset, center);
}
void Display() override { DisplayTable(filter_coefficients_); }
float& operator[](size_t index) { return filter_coefficients_[index]; }
private:
void SetUpFilterCoefficients();
CoefficientTable filter_coefficients_;
};
// "Fractional-delay" sinc-based resampler with integrated low-pass filter.
class SincFilter : public Filter {
public:
SincFilter(uint32_t source_rate, uint32_t dest_rate, uint32_t side_width = kSincFilterSideLength,
uint32_t num_frac_bits = kPtsFractionalBits)
: Filter(source_rate, dest_rate, side_width, num_frac_bits),
filter_coefficients_(side_width, num_frac_bits) {
SetUpFilterCoefficients();
}
SincFilter() : SincFilter(48000, 48000){};
static inline uint32_t GetFilterWidth(uint32_t source_frame_rate, uint32_t dest_frame_rate) {
return ((source_frame_rate > dest_frame_rate)
? std::ceil((static_cast<double>(kSincFilterSideLength) * source_frame_rate) /
dest_frame_rate)
: kSincFilterSideLength) -
1u;
}
float ComputeSample(uint32_t frac_offset, float* center) override {
return ComputeSampleFromTable(filter_coefficients_, frac_offset, center);
}
void Display() override { DisplayTable(filter_coefficients_); }
float& operator[](size_t index) { return filter_coefficients_[index]; }
private:
void SetUpFilterCoefficients();
CoefficientTable filter_coefficients_;
};
} // namespace media::audio::mixer
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_FILTER_H_