| // 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/coefficient_table.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| |
| namespace media_audio { |
| |
| std::unique_ptr<CoefficientTable> LinearFilterCoefficientTable::Create(Inputs inputs) { |
| CoefficientTableBuilder table(inputs.side_length, inputs.num_frac_bits); |
| |
| const int64_t kZeroCrossIdx = 1 << inputs.num_frac_bits; // frac_one |
| FX_CHECK(inputs.side_length == kZeroCrossIdx); |
| |
| const float kTransitionFactor = 1.0f / static_cast<float>(kZeroCrossIdx); |
| |
| // Just a Bartlett (triangular) window. |
| for (auto idx = 0; idx < kZeroCrossIdx; ++idx) { |
| auto factor = static_cast<float>(kZeroCrossIdx - idx) * kTransitionFactor; |
| |
| if (factor >= std::numeric_limits<float>::epsilon() || |
| factor <= -std::numeric_limits<float>::epsilon()) { |
| table[idx] = factor; |
| } else { |
| table[idx] = 0.0f; |
| } |
| } |
| |
| return table.Build(); |
| } |
| |
| std::unique_ptr<CoefficientTable> SincFilterCoefficientTable::Create(Inputs inputs) { |
| CoefficientTableBuilder table(inputs.side_length, inputs.num_frac_bits); |
| |
| const auto length = inputs.side_length; |
| const auto frac_one = 1 << inputs.num_frac_bits; |
| |
| // By capping this at 1.0, we set our low-pass filter to the lower of [source_rate, dest_rate]. |
| const double conversion_rate = M_PI * fmin(inputs.rate_conversion_ratio, 1.0); |
| |
| // Construct a sinc-based LPF, from our rate-conversion ratio and filter length. |
| const double theta_factor = conversion_rate / frac_one; |
| |
| // Concurrently, calculate a VonHann window function. These form the windowed-sinc filter. |
| const double normalize_length_factor = M_PI / static_cast<double>(length); |
| |
| table[0] = 1.0f; |
| for (auto idx = 1; idx < length; ++idx) { |
| const double theta = theta_factor * idx; |
| const double sinc_theta = sin(theta) / theta; |
| |
| // TODO(mpuryear): Pre-populate a static VonHann|Blackman|Kaiser window; don't recalc each one. |
| const double raised_cosine = cos(normalize_length_factor * idx) * 0.5 + 0.5; |
| |
| table[idx] = static_cast<float>(sinc_theta * raised_cosine); |
| } |
| |
| // Normalize our filter so that it doesn't change amplitude for DC (0 hz). |
| // While doing this, zero out any denormal float values as an optimization. |
| double amplitude_at_dc = 0.0; |
| |
| for (auto idx = frac_one; idx < length; idx += frac_one) { |
| amplitude_at_dc += table[idx]; |
| } |
| amplitude_at_dc = (2 * amplitude_at_dc) + table[0]; |
| |
| const double normalize_factor = 1.0 / amplitude_at_dc; |
| const double pre_normalized_epsilon = std::numeric_limits<float>::epsilon() * amplitude_at_dc; |
| |
| std::transform(table.physical_index_begin(), table.physical_index_end(), |
| table.physical_index_begin(), |
| [normalize_factor, pre_normalized_epsilon](float sample) -> float { |
| if (sample < pre_normalized_epsilon && sample > -pre_normalized_epsilon) { |
| return 0.0f; |
| } |
| return static_cast<float>(sample * normalize_factor); |
| }); |
| |
| return table.Build(); |
| } |
| |
| } // namespace media_audio |