src/media/audio/audio_core/mixer/test/mixer_response_tests.cc
// For the given resampler, test NxN fidelity equivalence with mono/stereo.
//
// Populate a multi-channel buffer with sinusoids at summary frequencies (one in each channel); mix
// the multi-chan buffer (at micro-SRC); compare each channel to existing mono results.
For the given resampler, test NxN fidelity equivalence with mono/stereo.
Populate a multi-channel buffer with sinusoids at summary frequencies (one in each channel); mix
the multi-chan buffer (at micro-SRC); compare each channel to existing mono results.
For the given resampler test NxN fidelity equivalence with mono / stereo . Populate a multi - channel buffer with sinusoids at summary frequencies ( one in each channel ) ; mix the multi - chan buffer ( at micro - SRC ) ; compare each channel to existing mono results .
void TestNxNEquivalence(Resampler sampler_type, double* level_db, double* sinad_db,
                        double* phase_rad) {
  if (!std::isnan(level_db[0])) {
    return;
  }
  level_db[0] = -INFINITY;
  static_assert(FrequencySet::kNumSummaryIdxs <= fuchsia::media::MAX_PCM_CHANNEL_COUNT,
                "Cannot allocate every summary frequency--rework this test.");
  auto num_chans = FrequencySet::kNumSummaryIdxs;
  auto source_rate = 48001u;
  auto dest_rate = 48000u;
  auto num_src_frames = kFreqTestBufSize + 1;
  auto mixer = SelectMixer(ASF::FLOAT, num_chans, source_rate, num_chans, dest_rate, sampler_type);
  auto num_dest_frames = kFreqTestBufSize;
  auto dest_format = Format::Create<ASF::FLOAT>(num_chans, dest_rate).take_value();
  auto source = PopulateNxNSourceBuffer(num_src_frames, num_chans, source_rate);
  auto accum = AudioBuffer(dest_format, num_dest_frames);
  auto& info = mixer->bookkeeping();
  info.step_size = (Mixer::FRAC_ONE * num_src_frames) / num_dest_frames;
  info.rate_modulo = (Mixer::FRAC_ONE * num_src_frames) - (info.step_size * num_dest_frames);
  info.denominator = num_dest_frames;
  info.src_pos_modulo = 0;
  uint32_t dest_frames, dest_offset = 0;
  uint32_t frac_src_frames = num_src_frames * Mixer::FRAC_ONE;
  int32_t frac_src_offset = static_cast<int32_t>(frac_src_frames);
  auto source_is_consumed =
      mixer->Mix(&accum.samples()[0], num_dest_frames, &dest_offset, &source.samples()[0],
                 frac_src_frames, &frac_src_offset, false);
  FX_CHECK(source_is_consumed);
  FX_CHECK(dest_offset == 0u);
  FX_CHECK(frac_src_offset == static_cast<int32_t>(frac_src_frames));
  frac_src_offset = 0;
  for (uint32_t packet = 0; packet < kResamplerTestNumPackets; ++packet) {
    dest_frames = num_dest_frames * (packet + 1) / kResamplerTestNumPackets;
    mixer->Mix(&accum.samples()[0], dest_frames, &dest_offset, &source.samples()[0],
               frac_src_frames, &frac_src_offset, false);
  }
  int32_t expected_frac_src_offset = frac_src_frames;
  if (dest_offset < dest_frames) {
    AUDIO_LOG(WARNING) << "Performing wraparound mix: dest_frames " << dest_frames
                       << ", dest_offset " << dest_offset << ", frac_src_frames " << std::hex
                       << frac_src_frames << ", frac_src_offset " << frac_src_offset;
    ASSERT_GE(frac_src_offset, 0);
    EXPECT_GE(static_cast<uint32_t>(frac_src_offset) + mixer->pos_filter_width().raw_value(),
              frac_src_frames)
        << "src_off " << std::hex << frac_src_offset << ", pos_width "
        << mixer->pos_filter_width().raw_value() << ", src_frames " << frac_src_frames;
    frac_src_offset -= frac_src_frames;
    mixer->Mix(&accum.samples()[0], dest_frames, &dest_offset, &source.samples()[0],
               frac_src_frames, &frac_src_offset, false);
    expected_frac_src_offset = 0;
  }
  EXPECT_EQ(dest_offset, dest_frames);
  EXPECT_EQ(frac_src_offset, expected_frac_src_offset);
  mixer->Reset();
  auto mono_format = Format::Create<ASF::FLOAT>(1, dest_rate).take_value();
  auto mono = AudioBuffer(mono_format, num_dest_frames);
  for (uint32_t idx = 0; idx < num_chans; ++idx) {
    uint32_t freq_idx = FrequencySet::kSummaryIdxs[idx];
    auto frequency_to_measure = FrequencySet::kReferenceFreqs[freq_idx];
    if (frequency_to_measure * 2 >= num_src_frames) {
      if (freq_idx < FrequencySet::kFirstOutBandRefFreqIdx) {
        level_db[freq_idx] = -INFINITY;
        phase_rad[freq_idx] = -INFINITY;
      }
      sinad_db[freq_idx] = -INFINITY;
      continue;
    }
    for (uint32_t i = 0; i < num_dest_frames; ++i) {
      mono.samples()[i] = accum.samples()[i * num_chans + idx];
    }
    const bool out_of_band = (frequency_to_measure * 2 >= num_dest_frames);
    auto result = out_of_band ? MeasureAudioFreqs(AudioBufferSlice(&mono), {})
                              : MeasureAudioFreqs(AudioBufferSlice(&mono), {frequency_to_measure});
    if (out_of_band) {
      sinad_db[freq_idx] = -Gain::DoubleToDb(result.total_magn_other);
    } else {
      auto magn_signal = result.magnitudes[frequency_to_measure];
      auto magn_other = result.total_magn_other;
      level_db[freq_idx] = Gain::DoubleToDb(magn_signal);
      sinad_db[freq_idx] = Gain::DoubleToDb(magn_signal / magn_other);
      phase_rad[freq_idx] = result.phases[frequency_to_measure];
    }
  }
}
void TestNxNEquivalence ( Resampler sampler_type , double * level_db , double * sinad_db , double * phase_rad ) { if ( ! std :: isnan ( level_db [ 0 ] ) ) { return ; } level_db [ 0 ] = - INFINITY ; static_assert ( FrequencySet :: kNumSummaryIdxs <= fuchsia :: media :: MAX_PCM_CHANNEL_COUNT , " " ) ; auto num_chans = FrequencySet :: kNumSummaryIdxs ; auto source_rate = 48001u ; auto dest_rate = 48000u ; auto num_src_frames = kFreqTestBufSize + 1 ; auto mixer = SelectMixer ( ASF :: FLOAT , num_chans , source_rate , num_chans , dest_rate , sampler_type ) ; auto num_dest_frames = kFreqTestBufSize ; auto dest_format = Format :: Create < ASF :: FLOAT > ( num_chans , dest_rate ) . take_value ( ) ; auto source = PopulateNxNSourceBuffer ( num_src_frames , num_chans , source_rate ) ; auto accum = AudioBuffer ( dest_format , num_dest_frames ) ; auto & info = mixer -> bookkeeping ( ) ; info . step_size = ( Mixer :: FRAC_ONE * num_src_frames ) / num_dest_frames ; info . rate_modulo = ( Mixer :: FRAC_ONE * num_src_frames ) - ( info . step_size * num_dest_frames ) ; info . denominator = num_dest_frames ; info . src_pos_modulo = 0 ; uint32_t dest_frames , dest_offset = 0 ; uint32_t frac_src_frames = num_src_frames * Mixer :: FRAC_ONE ; int32_t frac_src_offset = static_cast < int32_t > ( frac_src_frames ) ; auto source_is_consumed = mixer -> Mix ( & accum . samples ( ) [ 0 ] , num_dest_frames , & dest_offset , & source . samples ( ) [ 0 ] , frac_src_frames , & frac_src_offset , false ) ; FX_CHECK ( source_is_consumed ) ; FX_CHECK ( dest_offset == 0u ) ; FX_CHECK ( frac_src_offset == static_cast < int32_t > ( frac_src_frames ) ) ; frac_src_offset = 0 ; for ( uint32_t packet = 0 ; packet < kResamplerTestNumPackets ; ++ packet ) { dest_frames = num_dest_frames * ( packet + 1 ) / kResamplerTestNumPackets ; mixer -> Mix ( & accum . samples ( ) [ 0 ] , dest_frames , & dest_offset , & source . samples ( ) [ 0 ] , frac_src_frames , & frac_src_offset , false ) ; } int32_t expected_frac_src_offset = frac_src_frames ; if ( dest_offset < dest_frames ) { AUDIO_LOG ( WARNING ) << " " << dest_frames << " " << dest_offset << " " << std :: hex << frac_src_frames << " " << frac_src_offset ; ASSERT_GE ( frac_src_offset , 0 ) ; EXPECT_GE ( static_cast < uint32_t > ( frac_src_offset ) + mixer -> pos_filter_width ( ) . raw_value ( ) , frac_src_frames ) << " " << std :: hex << frac_src_offset << " " << mixer -> pos_filter_width ( ) . raw_value ( ) << " " << frac_src_frames ; frac_src_offset -= frac_src_frames ; mixer -> Mix ( & accum . samples ( ) [ 0 ] , dest_frames , & dest_offset , & source . samples ( ) [ 0 ] , frac_src_frames , & frac_src_offset , false ) ; expected_frac_src_offset = 0 ; } EXPECT_EQ ( dest_offset , dest_frames ) ; EXPECT_EQ ( frac_src_offset , expected_frac_src_offset ) ; mixer -> Reset ( ) ; auto mono_format = Format :: Create < ASF :: FLOAT > ( 1 , dest_rate ) . take_value ( ) ; auto mono = AudioBuffer ( mono_format , num_dest_frames ) ; for ( uint32_t idx = 0 ; idx < num_chans ; ++ idx ) { uint32_t freq_idx = FrequencySet :: kSummaryIdxs [ idx ] ; auto frequency_to_measure = FrequencySet :: kReferenceFreqs [ freq_idx ] ; if ( frequency_to_measure * 2 >= num_src_frames ) { if ( freq_idx < FrequencySet :: kFirstOutBandRefFreqIdx ) { level_db [ freq_idx ] = - INFINITY ; phase_rad [ freq_idx ] = - INFINITY ; } sinad_db [ freq_idx ] = - INFINITY ; continue ; } for ( uint32_t i = 0 ; i < num_dest_frames ; ++ i ) { mono . samples ( ) [ i ] = accum . samples ( ) [ i * num_chans + idx ] ; } const bool out_of_band = ( frequency_to_measure * 2 >= num_dest_frames ) ; auto result = out_of_band ? MeasureAudioFreqs ( AudioBufferSlice ( & mono ) , { } ) : MeasureAudioFreqs ( AudioBufferSlice ( & mono ) , { frequency_to_measure } ) ; if ( out_of_band ) { sinad_db [ freq_idx ] = - Gain :: DoubleToDb ( result . total_magn_other ) ; } else { auto magn_signal = result . magnitudes [ frequency_to_measure ] ; auto magn_other = result . total_magn_other ; level_db [ freq_idx ] = Gain :: DoubleToDb ( magn_signal ) ; sinad_db [ freq_idx ] = Gain :: DoubleToDb ( magn_signal / magn_other ) ; phase_rad [ freq_idx ] = result . phases [ frequency_to_measure ] ; } } }
For the given resampler, test NxN fidelity equivalence with mono/stereo.
For the given resampler test NxN fidelity equivalence with mono / stereo .
// This run already has NxN frequency response and SINAD results for this sampler and resampling // ratio; don't waste time and cycles rerunning it. // Set this to a valid (worst-case) value, so that (for any outcome) another test does not later // rerun this combination of sampler and resample ratio. // For this multi-channel cross-talk test, we put one of the summary frequencies in each channel. // We micro-SRC these signals, and ensure that our frequency response, SINAD and phase response // are the same as when we test these frequencies in isolation. // Mix the N-channel source[] into the N-channel accum[]. // Some resamplers need additional data in order to produce the final values, and the amount of // data can change depending on resampling ratio. However, all FFT inputs are considered periodic, // so to generate a periodic output from the resampler, we can provide extra source elements to // resamplers by simply wrapping around to source[0], etc. // We use this to keep ongoing src_pos_modulo across multiple Mix() calls. // First "prime" the resampler by sending a mix command exactly at the end of the source buffer. // This allows it to cache the frames at buffer's end. For our testing, buffers are periodic, so // these frames are exactly what would have immediately preceded the first data in the buffer. // This enables resamplers with significant side width to perform as they would in steady-state. // Resample source to accum. (Why in pieces? See kResamplerTestNumPackets in frequency_set.h) // Wrap around in the source buffer -- making the offset slightly negative. We can do // this, within the positive filter width of this sampler. // After running each frequency, clear out any remained cached filter state. // Currently, this is not strictly necessary since for each frequency test, // our initial position is the exact beginning of the buffer (and hence for // the Point and Linear resamplers, no previously-cached state is needed). // However, this IS a requirement for upcoming resamplers with larger // positive filter widths (they exposed the bug; thus addressing it now). // Copy-deinterleave each accum[] channel into mono[] and frequency-analyze. // If frequency is too high to be characterized in this buffer length, skip it. // Is this source frequency beyond the Nyquist limit for our destination frame rate? // Convert Frequency Response and Signal-to-Noise-And-Distortion (SINAD) to decibels. // This out-of-band frequency should have been entirely rejected -- capture total magnitude. // This is equivalent to Gain::DoubleToDb(1.0 / result.total_magn_other). // This frequency is in-band -- capture its level/phase and the magnitude of all else.