src/media/audio/audio_core/mixer/filter.cc
// SincFilter
//
// Calculate our windowed-sinc FIR filter. With it we perform band-limited frame-rate conversion.
SincFilter
Calculate our windowed-sinc FIR filter. With it we perform band-limited frame-rate conversion.
SincFilter Calculate our windowed - sinc FIR filter . With it we perform band - limited frame - rate conversion .
CoefficientTable* CreateSincFilterTable(SincFilter::Inputs inputs) {
  TRACE_DURATION("audio", "CreateSincFilterTable");
  auto out = new CoefficientTable(inputs.side_width, inputs.num_frac_bits);
  auto& table = *out;
  auto width = inputs.side_width;
  auto frac_one = 1u << inputs.num_frac_bits;
  auto conversion_rate = M_PI * fmin(inputs.rate_conversion_ratio, 1.0);
   TODO(mpuryear): Make the low-pass a BAND-pass, for DC offset removal (perhaps up to 20 hz?)
  for (auto idx = 1u; idx < width; ++idx) {
    auto double_idx = static_cast<double>(idx);
    auto idx_over_frac_one = double_idx / frac_one;
    auto theta = idx_over_frac_one * conversion_rate;
    auto sin_theta = sin(theta);
    auto sinc_theta = sin_theta / theta;
    if constexpr (kEnableVerboseDebugging) {
      FX_LOGS(TRACE) << "Sinc[" << std::hex << idx << "] -- Factors 1:" << idx_over_frac_one
                     << ", 2:" << theta << ", 3:" << sin_theta << ", 4:" << sinc_theta;
    }
     Then window the filter. Here we choose a VonHann window, but Kaiser or others can work too.
     TODO(mpuryear): Pre-populate a static VonHann|Blackman|Kaiser; don't recalc for every
     mixer.
    auto fraction_width = double_idx / width;
    auto pi_fraction_width = fraction_width * M_PI;
    auto cos_pi_frac_width = cos(pi_fraction_width);
    auto raised_cosine = cos_pi_frac_width * 0.5 + 0.5;
    if constexpr (kEnableVerboseDebugging) {
      FX_LOGS(TRACE) << "VonHann window[" << std::hex << idx
                     << "] -- Fraction of width:" << fraction_width
                     << ", PI * fraction_width:" << pi_fraction_width
                     << ", COS(PI * fraction_width):" << cos_pi_frac_width
                     << ", Raised-cosine (result):" << raised_cosine;
    }
    table[idx] = sinc_theta * raised_cosine;
  }
  table[0] = 1.0f;
   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.
  auto amplitude_at_dc = 0.0;
  for (auto idx = frac_one; idx < width; idx += frac_one) {
    amplitude_at_dc += table[idx];
  }
  amplitude_at_dc = (2 * amplitude_at_dc) + table[0];
  auto normalize_factor = 1.0 / amplitude_at_dc;
  std::transform(table.begin(), table.end(), table.begin(),
                 [normalize_factor](float sample) -> float {
                   if (normalize_factor != 1.0) {
                     sample *= normalize_factor;
                   }
                   if (sample < std::numeric_limits<float>::epsilon() &&
                       sample > -std::numeric_limits<float>::epsilon()) {
                     return 0.0f;
                   }
                   return sample;
                 });
  return out;
}
CoefficientTable * CreateSincFilterTable ( SincFilter :: Inputs inputs ) { TRACE_DURATION ( " " , " " ) ; auto out = new CoefficientTable ( inputs . side_width , inputs . num_frac_bits ) ; auto & table = * out ; auto width = inputs . side_width ; auto frac_one = 1u << inputs . num_frac_bits ; auto conversion_rate = M_PI * fmin ( inputs . rate_conversion_ratio , 1.0 ) ; for ( auto idx = 1u ; idx < width ; ++ idx ) { auto double_idx = static_cast < double > ( idx ) ; auto idx_over_frac_one = double_idx / frac_one ; auto theta = idx_over_frac_one * conversion_rate ; auto sin_theta = sin ( theta ) ; auto sinc_theta = sin_theta / theta ; if constexpr ( kEnableVerboseDebugging ) { FX_LOGS ( TRACE ) << " " << std :: hex << idx << " " << idx_over_frac_one << " " << theta << " " << sin_theta << " " << sinc_theta ; } auto fraction_width = double_idx / width ; auto pi_fraction_width = fraction_width * M_PI ; auto cos_pi_frac_width = cos ( pi_fraction_width ) ; auto raised_cosine = cos_pi_frac_width * 0.5 + 0.5 ; if constexpr ( kEnableVerboseDebugging ) { FX_LOGS ( TRACE ) << " " << std :: hex << idx << " " << fraction_width << " " << pi_fraction_width << " " << cos_pi_frac_width << " " << raised_cosine ; } table [ idx ] = sinc_theta * raised_cosine ; } table [ 0 ] = 1.0f ; auto amplitude_at_dc = 0.0 ; for ( auto idx = frac_one ; idx < width ; idx += frac_one ) { amplitude_at_dc += table [ idx ] ; } amplitude_at_dc = ( 2 * amplitude_at_dc ) + table [ 0 ] ; auto normalize_factor = 1.0 / amplitude_at_dc ; std :: transform ( table . begin ( ) , table . end ( ) , table . begin ( ) , [ normalize_factor ] ( float sample ) -> float { if ( normalize_factor != 1.0 ) { sample *= normalize_factor ; } if ( sample < std :: numeric_limits < float > :: epsilon ( ) && sample > - std :: numeric_limits < float > :: epsilon ( ) ) { return 0.0f ; } return sample ; } ) ; return out ; }
SincFilter
Calculate our windowed-sinc FIR filter.
SincFilter Calculate our windowed - sinc FIR filter .
// By capping this at 1.0, we set our low-pass filter to the lower of [src_rate, dest_rate]. // First calculate our sinc filter, based on rate-conversion ratio and filter width. // // TODO(mpuryear): Make the low-pass a BAND-pass, for DC offset removal (perhaps up to 20 hz?) // Then window the filter. Here we choose a VonHann window, but Kaiser or others can work too. // // TODO(mpuryear): Pre-populate a static VonHann|Blackman|Kaiser; don't recalc for every // mixer. // 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.