diff --git a/src/media/audio/audio_core/mixer/linear_sampler.cc b/src/media/audio/audio_core/mixer/linear_sampler.cc index 0b1da19c9ae24090d96d76d85f59e88d050aee6d..bee995bf9e8d7f679791d2f699acd021e3c940c8 100644 --- a/src/media/audio/audio_core/mixer/linear_sampler.cc +++ b/src/media/audio/audio_core/mixer/linear_sampler.cc @@ -198,7 +198,7 @@ inline bool LinearSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( } // Now we are fully in the current buffer and need not rely on our cache. - while ((dest_off < dest_frames) && (src_off < src_end)) { + while ((dest_off < dest_frames) && (src_off <= src_end)) { uint32_t S = (src_off >> kPtsFractionalBits) * SrcChanCount; float* out = dest + (dest_off * DestChanCount); if constexpr (ScaleType == ScalerType::RAMPING) { @@ -226,49 +226,35 @@ inline bool LinearSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( } else { // We are muted. Don't mix, but figure out how many samples we WOULD have // produced and update the src_off and dest_off values appropriately. - if ((dest_off < dest_frames) && (src_off < src_end)) { - uint32_t src_avail = (((src_end - src_off) + step_size - 1) / step_size); + if ((dest_off < dest_frames) && (src_off <= src_end)) { + uint32_t src_avail = ((src_end - src_off) / step_size) + 1; uint32_t dest_avail = (dest_frames - dest_off); uint32_t avail = std::min(src_avail, dest_avail); dest_off += avail; - src_off += avail * step_size; + src_off += (avail * step_size); if constexpr (HasModulo) { - src_pos_modulo += (rate_modulo * avail); - src_off += (src_pos_modulo / denominator); - src_pos_modulo %= denominator; - } - } - } - - // If we have room for at least one more sample, and our sampling position - // hits the input buffer's final frame exactly ... - if ((dest_off < dest_frames) && (src_off == src_end)) { - // ... and if we are not muted, of course ... - if constexpr (ScaleType != ScalerType::MUTED) { - // ... then we can _point-sample_ one final frame into our output buffer. - // We need not _interpolate_ since fractional position is exactly zero. - uint32_t S = (src_off >> kPtsFractionalBits) * SrcChanCount; - float* out = dest + (dest_off * DestChanCount); - if constexpr (ScaleType == ScalerType::RAMPING) { - amplitude_scale = info->scale_arr[dest_off - dest_off_start]; - } - - for (size_t D = 0; D < DestChanCount; ++D) { - float sample = SR::Read(src + S + (D / SR::DestPerSrc)); - out[D] = DM::Mix(out[D], sample, amplitude_scale); - } - } + uint64_t total_mod = src_pos_modulo + (avail * rate_modulo); + src_off += (total_mod / denominator); + src_pos_modulo = total_mod % denominator; + + int32_t prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + while (prev_src_off > src_end) { + if (src_pos_modulo < rate_modulo) { + src_pos_modulo += denominator; + } - dest_off += 1; - src_off += step_size; + --dest_off; + src_off = prev_src_off; + src_pos_modulo -= rate_modulo; - if constexpr (HasModulo) { - src_pos_modulo += rate_modulo; - if (src_pos_modulo >= denominator) { - ++src_off; - src_pos_modulo -= denominator; + prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + } } } } @@ -511,49 +497,35 @@ inline bool NxNLinearSamplerImpl<SrcSampleType>::Mix( } else { // We are muted. Don't mix, but figure out how many samples we WOULD have // produced and update the src_off and dest_off values appropriately. - if ((dest_off < dest_frames) && (src_off < src_end)) { - uint32_t src_avail = (((src_end - src_off) + step_size - 1) / step_size); + if ((dest_off < dest_frames) && (src_off <= src_end)) { + uint32_t src_avail = ((src_end - src_off) / step_size) + 1; uint32_t dest_avail = (dest_frames - dest_off); uint32_t avail = std::min(src_avail, dest_avail); dest_off += avail; - src_off += avail * step_size; + src_off += (avail * step_size); if constexpr (HasModulo) { - src_pos_modulo += (rate_modulo * avail); - src_off += (src_pos_modulo / denominator); - src_pos_modulo %= denominator; - } - } - } - - // If we have room for at least one more sample, and our sampling position - // hits the input buffer's final frame exactly ... - if ((dest_off < dest_frames) && (src_off == src_end)) { - // ... and if we are not muted, of course ... - if constexpr (ScaleType != ScalerType::MUTED) { - // ... then we can _point-sample_ one final frame into our output buffer. - // We need not _interpolate_ since fractional position is exactly zero. - uint32_t S = (src_off >> kPtsFractionalBits) * chan_count; - float* out = dest + (dest_off * chan_count); - if constexpr (ScaleType == ScalerType::RAMPING) { - amplitude_scale = info->scale_arr[dest_off - dest_off_start]; - } - - for (size_t D = 0; D < chan_count; ++D) { - float sample = SampleNormalizer<SrcSampleType>::Read(src + S + D); - out[D] = DM::Mix(out[D], sample, amplitude_scale); - } - } + uint64_t total_mod = src_pos_modulo + (avail * rate_modulo); + src_off += (total_mod / denominator); + src_pos_modulo = total_mod % denominator; + + int32_t prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + while (prev_src_off > src_end) { + if (src_pos_modulo < rate_modulo) { + src_pos_modulo += denominator; + } - dest_off += 1; - src_off += step_size; + --dest_off; + src_off = prev_src_off; + src_pos_modulo -= rate_modulo; - if constexpr (HasModulo) { - src_pos_modulo += rate_modulo; - if (src_pos_modulo >= denominator) { - ++src_off; - src_pos_modulo -= denominator; + prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + } } } } diff --git a/src/media/audio/audio_core/mixer/point_sampler.cc b/src/media/audio/audio_core/mixer/point_sampler.cc index 81b813988b2e48d83500cc46625d9aa82cbeb6c0..07adb6b8a84bcd26ec68a3fc525642cfc41df9e0 100644 --- a/src/media/audio/audio_core/mixer/point_sampler.cc +++ b/src/media/audio/audio_core/mixer/point_sampler.cc @@ -114,10 +114,12 @@ inline bool PointSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( // That is: its "positive filter width" is zero. FXL_DCHECK(src_off >= 0) << std::hex << "src_off: 0x" << src_off; - // Source offset must also be within neg_filter_width of our last sample. - // Neg_filter_width is just shy of FRAC_ONE; src_off can't exceed the buf. - FXL_DCHECK(src_off < static_cast<int32_t>(frac_src_frames)) - << std::hex << "src_off: 0x" << src_off; + // src_off cannot exceed our last sampleable subframe. We define this as + // "Source end": the last subframe for which this Mix call can produce output. + // Otherwise, all these src samples are in the past and irrelevant here. + int32_t src_end = frac_src_frames - 1; + FXL_DCHECK(src_off <= src_end) + << std::hex << "src_off: 0x" << src_off << ", src_end: 0x" << src_end; // If we are not attenuated to the point of being muted, go ahead and perform // the mix. Otherwise, just update the source and dest offsets. @@ -127,8 +129,7 @@ inline bool PointSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( amplitude_scale = info->gain.GetGainScale(); } - while ((dest_off < dest_frames) && - (src_off < static_cast<int32_t>(frac_src_frames))) { + while ((dest_off < dest_frames) && (src_off <= src_end)) { if constexpr (ScaleType == ScalerType::RAMPING) { amplitude_scale = info->scale_arr[dest_off - dest_off_start]; } @@ -152,20 +153,36 @@ inline bool PointSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( } } } else { - if (dest_off < dest_frames) { - // Calc how much we would've produced; update src_off & dest_off. - uint32_t src_avail = - ((frac_src_frames - src_off) + step_size - 1) / step_size; - uint32_t dest_avail = (dest_frames - dest_off); - uint32_t avail = std::min(src_avail, dest_avail); - - src_off += avail * step_size; + // We are muted. Don't mix, but figure out how many samples we WOULD have + // produced and update the src_off and dest_off values appropriately. + if ((dest_off < dest_frames) && (src_off <= src_end)) { + uint32_t dest_avail = dest_frames - dest_off; + uint32_t src_avail = ((src_end - src_off) / step_size) + 1; + uint32_t avail = std::min(dest_avail, src_avail); dest_off += avail; + src_off += (avail * step_size); if constexpr (HasModulo) { - src_pos_modulo += (rate_modulo * avail); - src_off += (src_pos_modulo / denominator); - src_pos_modulo %= denominator; + uint64_t total_mod = src_pos_modulo + (avail * rate_modulo); + src_off += (total_mod / denominator); + src_pos_modulo = total_mod % denominator; + + int32_t prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + while (prev_src_off > src_end) { + if (src_pos_modulo < rate_modulo) { + src_pos_modulo += denominator; + } + + --dest_off; + src_off = prev_src_off; + src_pos_modulo -= rate_modulo; + + prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + } } } } @@ -178,7 +195,7 @@ inline bool PointSamplerImpl<DestChanCount, SrcSampleType, SrcChanCount>::Mix( } // If we passed the last valid source subframe, then we exhausted this source. - return (src_off >= static_cast<int32_t>(frac_src_frames)); + return (src_off > src_end); } template <size_t DestChanCount, typename SrcSampleType, size_t SrcChanCount> @@ -303,10 +320,10 @@ inline bool NxNPointSamplerImpl<SrcSampleType>::Mix( // PointSampler has no memory: input frames only affect present/future output. // That is: its "positive filter width" is zero. FXL_DCHECK(src_off >= 0); - // Source offset must also be within neg_filter_width of our last sample. - // Neg_filter_width is less than FRAC_ONE, so src_off can't exceed the buf. - FXL_DCHECK(src_off < static_cast<int32_t>(frac_src_frames)); + int32_t src_end = frac_src_frames - 1; + FXL_DCHECK(src_off <= src_end) + << std::hex << "src_off: 0x" << src_off << ", src_end: 0x" << src_end; // If we are not attenuated to the point of being muted, go ahead and perform // the mix. Otherwise, just update the source and dest offsets. if constexpr (ScaleType != ScalerType::MUTED) { @@ -315,8 +332,7 @@ inline bool NxNPointSamplerImpl<SrcSampleType>::Mix( amplitude_scale = info->gain.GetGainScale(); } - while ((dest_off < dest_frames) && - (src_off < static_cast<int32_t>(frac_src_frames))) { + while ((dest_off < dest_frames) && (src_off <= src_end)) { if constexpr (ScaleType == ScalerType::RAMPING) { amplitude_scale = info->scale_arr[dest_off - dest_off_start]; } @@ -330,7 +346,7 @@ inline bool NxNPointSamplerImpl<SrcSampleType>::Mix( out[dest_iter] = DM::Mix(out[dest_iter], sample, amplitude_scale); } - dest_off += 1; + ++dest_off; src_off += step_size; if constexpr (HasModulo) { @@ -342,20 +358,36 @@ inline bool NxNPointSamplerImpl<SrcSampleType>::Mix( } } } else { - if (dest_off < dest_frames) { - // Calc how many samples we would've produced; update src_off & dest_off. - uint32_t src_avail = - ((frac_src_frames - src_off) + step_size - 1) / step_size; - uint32_t dest_avail = (dest_frames - dest_off); - uint32_t avail = std::min(src_avail, dest_avail); - - src_off += avail * step_size; + // We are muted. Don't mix, but figure out how many samples we WOULD have + // produced and update the src_off and dest_off values appropriately. + if ((dest_off < dest_frames) && (src_off <= src_end)) { + uint32_t dest_avail = dest_frames - dest_off; + uint32_t src_avail = ((src_end - src_off) / step_size) + 1; + uint32_t avail = std::min(dest_avail, src_avail); dest_off += avail; + src_off += (avail * step_size); if constexpr (HasModulo) { - src_pos_modulo += (rate_modulo * avail); - src_off += (src_pos_modulo / denominator); - src_pos_modulo %= denominator; + uint64_t total_mod = src_pos_modulo + (avail * rate_modulo); + src_off += (total_mod / denominator); + src_pos_modulo = total_mod % denominator; + + int32_t prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + while (prev_src_off > src_end) { + if (src_pos_modulo < rate_modulo) { + src_pos_modulo += denominator; + } + + --dest_off; + src_off = prev_src_off; + src_pos_modulo -= rate_modulo; + + prev_src_off = (src_pos_modulo < rate_modulo) + ? (src_off - step_size - 1) + : (src_off - step_size); + } } } } @@ -368,7 +400,7 @@ inline bool NxNPointSamplerImpl<SrcSampleType>::Mix( } // If we passed the last valid source subframe, then we exhausted this source. - return (src_off >= static_cast<int32_t>(frac_src_frames)); + return (src_off > src_end); } template <typename SrcSampleType> diff --git a/src/media/audio/audio_core/mixer/test/audio_performance.cc b/src/media/audio/audio_core/mixer/test/audio_performance.cc index da2cc7ce2d04cb42e1130854938f386a4d79c886..a597a53269dc1a2092effbdb482b8884e1634f38 100644 --- a/src/media/audio/audio_core/mixer/test/audio_performance.cc +++ b/src/media/audio/audio_core/mixer/test/audio_performance.cc @@ -144,19 +144,19 @@ void AudioPerformance::ProfileMixer(uint32_t num_input_chans, if (std::is_same<SampleType, uint8_t>::value) { sample_format = fuchsia::media::AudioSampleFormat::UNSIGNED_8; amplitude = std::numeric_limits<int8_t>::max(); - format = "un8"; + format = "Un8"; } else if (std::is_same<SampleType, int16_t>::value) { sample_format = fuchsia::media::AudioSampleFormat::SIGNED_16; amplitude = std::numeric_limits<int16_t>::max(); - format = "i16"; + format = "I16"; } else if (std::is_same<SampleType, int32_t>::value) { sample_format = fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32; amplitude = std::numeric_limits<int32_t>::max() & ~0x0FF; - format = "i24"; + format = "I24"; } else if (std::is_same<SampleType, float>::value) { sample_format = fuchsia::media::AudioSampleFormat::FLOAT; amplitude = 1.0; - format = "f32"; + format = "F32"; } else { ASSERT_TRUE(false) << "Unknown mix sample format for testing"; return; @@ -165,6 +165,9 @@ void AudioPerformance::ProfileMixer(uint32_t num_input_chans, uint32_t dest_rate = 48000; auto mixer = SelectMixer(sample_format, num_input_chans, source_rate, num_output_chans, dest_rate, sampler_type); + if (mixer == nullptr) { + return; + } uint32_t source_buffer_size = kFreqTestBufSize * dest_rate / source_rate; uint32_t source_frames = source_buffer_size + 1; @@ -259,10 +262,22 @@ void AudioPerformance::ProfileMixer(uint32_t num_input_chans, } auto mean = static_cast<double>(total_elapsed) / kNumMixerProfilerRuns; - printf("%c-%s.%u%u%c%c%u:", - (sampler_type == Resampler::SampleAndHold ? 'P' : 'L'), format.c_str(), - num_input_chans, num_output_chans, gain_char, (accumulate ? '+' : '-'), - source_rate); + + char sampler_ch; + switch (sampler_type) { + case Resampler::SampleAndHold: + sampler_ch = 'P'; + break; + case Resampler::LinearInterpolation: + sampler_ch = 'L'; + break; + case Resampler::Default: + FXL_LOG(ERROR) << "Test should specify the Resampler exactly"; + return; + } + + printf("%c-%s.%u%u%c%c%u:", sampler_ch, format.c_str(), num_input_chans, + num_output_chans, gain_char, (accumulate ? '+' : '-'), source_rate); printf("\t%9.3lf\t%9.3lf\t%9.3lf\t%9.3lf\n", mean / 1000.0, first / 1000.0, best / 1000.0, worst / 1000.0); @@ -277,7 +292,7 @@ void AudioPerformance::DisplayOutputConfigLegend() { kFreqTestBufSize); printf( "\n For output configuration FFF-Rn, where:\n" - "\t FFF: Format of source data - Un8, I16, I24, F32,\n" + "\t FFF: Format of output data - Un8, I16, I24, F32,\n" "\t R: Range of source data - [S]ilence, [O]ut-of-range, [N]ormal,\n" "\t n: Number of output channels (one-digit number)\n\n"); } diff --git a/src/media/audio/audio_core/mixer/test/audio_performance.h b/src/media/audio/audio_core/mixer/test/audio_performance.h index 2bfce66466339d644bd7f989b46d564c7a8a5a29..dc32bab32a4a34c6d32c538d4410984cf040fc93 100644 --- a/src/media/audio/audio_core/mixer/test/audio_performance.h +++ b/src/media/audio/audio_core/mixer/test/audio_performance.h @@ -18,10 +18,10 @@ class AudioPerformance { // After first run ("cold"), timings measured are tightly clustered (+/-1-2%); // we can get a high-confidence profile assessment with fewer runs. // - // These values were chosen to keep Mixer and OutputProducer profile times - // under 180 seconds each, on both a standard VIM2 and a standard NUC. - static constexpr uint32_t kNumMixerProfilerRuns = 140; - static constexpr uint32_t kNumOutputProfilerRuns = 1200; + // We set these values to keep profile times for Mixer and OutputProducer both + // below 180 seconds, for Release builds on standard VIM2 and NUC. + static constexpr uint32_t kNumMixerProfilerRuns = 100; + static constexpr uint32_t kNumOutputProfilerRuns = 1295; // class is static only - prevent attempts to instantiate it AudioPerformance() = delete; diff --git a/src/media/audio/audio_core/mixer/test/main.cc b/src/media/audio/audio_core/mixer/test/main.cc index 97a0f9e431cf9a2a97a8fb0e554a571a69ca49ae..3051e784ad221263a1150a684109304ef1a70786 100644 --- a/src/media/audio/audio_core/mixer/test/main.cc +++ b/src/media/audio/audio_core/mixer/test/main.cc @@ -16,8 +16,8 @@ int main(int argc, char** argv) { // This flag is used when updating AudioResult kPrev arrays. // --profile Profile the performance of Mix() across numerous configurations. bool show_full_frequency_set = command_line.HasOption("full"); - bool do_performance_profiling = command_line.HasOption("profile"); bool dump_threshold_values = command_line.HasOption("dump"); + bool do_performance_profiling = command_line.HasOption("profile"); media::audio::test::FrequencySet::UseFullFrequencySet = (show_full_frequency_set || dump_threshold_values); diff --git a/src/media/audio/audio_core/mixer/test/mixer_gain_tests.cc b/src/media/audio/audio_core/mixer/test/mixer_gain_tests.cc index 1aa9fca90e30aae7df63d81255998721ee954ff0..50b491730f4188321ec4f7e6a7f20d9e402a9a8a 100644 --- a/src/media/audio/audio_core/mixer/test/mixer_gain_tests.cc +++ b/src/media/audio/audio_core/mixer/test/mixer_gain_tests.cc @@ -792,45 +792,35 @@ TEST(MixGain, Accumulator) { // Our mixer contains an optimization in which it skips mixing operations if it // detects that gain is below a certain threshold (regardless of "accumulate"). -TEST(MixGain, Accumulator_Clear) { - int16_t source[] = {-32768, 32767}; - float accum[] = {-32768, 32767}; - float expect[] = {-32768, 32767}; +void TestAccumulatorClear(Resampler sampler_type) { + int16_t source[] = {-32768, 32767, -16384, 16383}; + float accum[] = {-32768, 32767, -16384, 16383}; + float expect[] = {-32768, 32767, -16384, 16383}; - // We will test both SampleAndHold and LinearInterpolation interpolators. auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, - 48000, 1, 48000, Resampler::SampleAndHold); - // Use the gain guaranteed to silence all signals: Gain::kMinScale. - DoMix(mixer.get(), source, accum, true, fbl::count_of(accum), - Gain::kMinGainDb); - EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); - - // Try with the other sampler. - mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, 48000, 1, - 48000, Resampler::LinearInterpolation); + 48000, 1, 48000, sampler_type); + // Use a gain guaranteed to silence any signal -- Gain::kMinGainDb. DoMix(mixer.get(), source, accum, true, fbl::count_of(accum), Gain::kMinGainDb); EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); - // - // When accumulate = false, this is overridden: it behaves identically. - // + // When accumulate = false but gain is sufficiently low, overwriting previous + // contents is skipped. This should lead to the same results as above. mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, 48000, 1, - 48000, Resampler::SampleAndHold); + 48000, sampler_type); DoMix(mixer.get(), source, accum, false, fbl::count_of(accum), Gain::kMinGainDb); EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); +} - // Ensure that both samplers behave identically in this regard. - mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, 48000, 1, - 48000, Resampler::LinearInterpolation); - DoMix(mixer.get(), source, accum, false, fbl::count_of(accum), - Gain::kMinGainDb); - EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); +// Validate the SampleAndHold interpolator for this behavior. +TEST(MixGain, Accumulator_Clear_Point) { + TestAccumulatorClear(Resampler::SampleAndHold); } -// TODO(mpuryear): When we have a master gain stage that can take advantage of -// the headroom inherent in a multi-stream accumulator, implement a test that -// assesses our headroom for a post-SUM gain stage. +// Validate the same assertions, with LinearInterpolation interpolator. +TEST(MixGain, Accumulator_Clear_Linear) { + TestAccumulatorClear(Resampler::LinearInterpolation); +} } // namespace media::audio::test diff --git a/src/media/audio/audio_core/mixer/test/mixer_resampling_tests.cc b/src/media/audio/audio_core/mixer/test/mixer_resampling_tests.cc index df2ae4ea8074ea9f9da80707123726b6902d9884..7b43105349cf3a446768aeb6d8303acd244f4859 100644 --- a/src/media/audio/audio_core/mixer/test/mixer_resampling_tests.cc +++ b/src/media/audio/audio_core/mixer/test/mixer_resampling_tests.cc @@ -76,64 +76,14 @@ TEST(Bookkeeping, Reset) { // Likewise "demand" is determined by dest_frames and dest_offset into // dest_frames. -// Verify that PointSampler mixes from/to correct buffer locations. Also ensure -// that it doesn't touch other buffer sections, regardless of 'accumulate'. +// Verify that the samplers mix to/from correct buffer locations. Also ensure +// that they don't touch other buffer sections, regardless of 'accumulate'. // This first test uses integer lengths/offsets, and a step_size of ONE. -TEST(Resampling, Position_Basic_Point) { - auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, - 24000, 1, 24000, Resampler::SampleAndHold); - - // - // Check: source supply exceeds destination demand. - // Source (offset 2 of 5) can supply 3. Destination (offset 1 of 3) wants 2. - int32_t frac_src_offset = 2 << kPtsFractionalBits; - uint32_t dest_offset = 1; - int16_t source[] = {1, 0x17, 0x7B, 0x4D2, 0x3039}; - - // Mix will accumulate src[2,3] into accum[1,2] - float accum[] = {-0x00002000, -0x00017000, -0x000EA000, -0x00929000, - -0x05BA0000}; - float expect[] = {-0x00002000, 0x00064000, 0x003E8000, -0x00929000, - -0x05BA0000}; - NormalizeInt28ToPipelineBitwidth(accum, fbl::count_of(accum)); - NormalizeInt28ToPipelineBitwidth(expect, fbl::count_of(expect)); - - Bookkeeping info; - bool mix_result = - mixer->Mix(accum, 3, &dest_offset, source, 5 << kPtsFractionalBits, - &frac_src_offset, true, &info); - - EXPECT_FALSE(mix_result); // False: Mix did not complete all of src_frames - EXPECT_EQ(3u, dest_offset); - EXPECT_EQ(4 << kPtsFractionalBits, frac_src_offset); - EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); - - // - // Check: destination demand exceeds source supply. - // Source (offset 3 of 4) has 1. Destination (offset 1 of 4) wants 3. - frac_src_offset = 3 << kPtsFractionalBits; - dest_offset = 1; - // Mix will move source[3] into accum[1] (accum==false) - expect[1] = 0x004D2000; - NormalizeInt28ToPipelineBitwidth(&expect[1], 1); - - mix_result = - mixer->Mix(accum, 4, &dest_offset, source, 4 << kPtsFractionalBits, - &frac_src_offset, false, &info); - - EXPECT_TRUE(mix_result); // True: Mix completed all of src_frames - EXPECT_EQ(2u, dest_offset); - EXPECT_EQ(4 << kPtsFractionalBits, frac_src_offset); - EXPECT_TRUE(CompareBuffers(accum, expect, fbl::count_of(accum))); -} +void TestBasicPosition(Resampler samplerType) { + bool mix_result; -// Verify that LinearSampler mixes from and to correct buffer locations. -// Ensure it doesn't touch other buffer sections, regardless of 'accumulate' -// flag. Check scenarios when supply > demand, and vice versa, and ==. -// This first test uses integer lengths/offsets, and a step_size of ONE. -TEST(Resampling, Position_Basic_Linear) { auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::SIGNED_16, 1, - 48000, 1, 48000, Resampler::LinearInterpolation); + 48000, 1, 48000, samplerType); // // Check: source supply equals destination demand. @@ -141,6 +91,7 @@ TEST(Resampling, Position_Basic_Linear) { int32_t frac_src_offset = 2 << kPtsFractionalBits; uint32_t dest_offset = 1; int16_t source[] = {1, 0xC, 0x7B, 0x4D2, 0x3039}; + // Mix will add source[2,3,4] to accum[1,2,3] float accum[] = {-0x00002000, -0x00017000, -0x000EA000, -0x00929000, -0x05BA0000}; @@ -150,7 +101,7 @@ TEST(Resampling, Position_Basic_Linear) { NormalizeInt28ToPipelineBitwidth(expect, fbl::count_of(expect)); Bookkeeping info; - bool mix_result = + mix_result = mixer->Mix(accum, 4, &dest_offset, source, 5 << kPtsFractionalBits, &frac_src_offset, true, &info); @@ -201,6 +152,16 @@ TEST(Resampling, Position_Basic_Linear) { EXPECT_TRUE(CompareBuffers(accum2, expect3, fbl::count_of(accum2))); } +// Validate basic (frame-level) position for SampleAndHold resampler. +TEST(Resampling, Position_Basic_Point) { + TestBasicPosition(Resampler::SampleAndHold); +} + +// Validate basic (frame-level) position for Linear resampler. +TEST(Resampling, Position_Basic_Linear) { + TestBasicPosition(Resampler::LinearInterpolation); +} + // For PointSampler, test sample placement when given fractional position. // Ensure it doesn't touch other buffer sections, regardless of 'accumulate' // flag. Check when supply > demand and vice versa (we already know = works). @@ -364,9 +325,8 @@ TEST(Resampling, Rate_Modulo_Linear) { TestRateModulo(Resampler::LinearInterpolation); } -// For the provided sampler, validate src_pos_modulo for default, 0, non-zero. -// For these three input conditions, verify rollover and non-rollover cases. -void TestPositionModulo(Resampler sampler_type) { +// For provided sampler, validate src_pos_modulo for zero/non-zero no rollover. +void TestPositionModuloNoRollover(Resampler sampler_type, bool mute = false) { auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::FLOAT, 1, 44100, 1, 44100, sampler_type); @@ -375,8 +335,8 @@ void TestPositionModulo(Resampler sampler_type) { int32_t frac_src_offset; float source[4] = {0.0f}; - // For these three "almost-but-not-rollover" cases, we generate 3 output - // samples, leaving source and dest at pos 3 and src_pos_modulo at 9999/10000. + // For "almost-but-not-rollover" cases, we generate 3 output samples, leaving + // source and dest at pos 3 and src_pos_modulo at 9999/10000. // // Case: Zero src_pos_modulo, almost-but-not-rollover. dest_offset = 0; @@ -389,6 +349,9 @@ void TestPositionModulo(Resampler sampler_type) { info.rate_modulo = 3333; info.denominator = 10000; info.src_pos_modulo = 0; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, @@ -405,6 +368,9 @@ void TestPositionModulo(Resampler sampler_type) { info.rate_modulo = 3332; info.denominator = 10000; info.src_pos_modulo = 3; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, @@ -412,6 +378,17 @@ void TestPositionModulo(Resampler sampler_type) { EXPECT_EQ(fbl::count_of(accum), dest_offset); EXPECT_TRUE(3 * Mixer::FRAC_ONE == frac_src_offset); EXPECT_EQ(9999u, info.src_pos_modulo); +} + +// For provided sampler, validate src_pos_modulo for zero/non-zero w/rollover. +void TestPositionModuloRollover(Resampler sampler_type, bool mute = false) { + auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::FLOAT, 1, 44100, + 1, 44100, sampler_type); + + float accum[3]; + uint32_t dest_offset; + int32_t frac_src_offset; + float source[4] = {0.0f}; // For these three "just-barely-rollover" cases, we generate 2 output // samples, leaving source and dest pos at 3 but src_pos_modulo at 0/10000. @@ -420,10 +397,14 @@ void TestPositionModulo(Resampler sampler_type) { dest_offset = 1; frac_src_offset = Mixer::FRAC_ONE - 1; + Bookkeeping info; info.step_size = Mixer::FRAC_ONE; info.rate_modulo = 5000; info.denominator = 10000; info.src_pos_modulo = 0; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, @@ -440,6 +421,9 @@ void TestPositionModulo(Resampler sampler_type) { info.rate_modulo = 3332; info.denominator = 10000; info.src_pos_modulo = 3336; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, @@ -449,16 +433,120 @@ void TestPositionModulo(Resampler sampler_type) { EXPECT_EQ(0u, info.src_pos_modulo); } +// For provided sampler, validate src_pos_modulo for early rollover. +void TestPositionModuloEarlyRolloverPoint(bool mute = false) { + auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::FLOAT, 1, 44100, + 1, 44100, Resampler::SampleAndHold); + float accum[3]; + uint32_t dest_offset; + int32_t frac_src_offset; + float source[3] = {0.0f}; + + // Non-zero src_pos_modulo, early-rollover case. + dest_offset = 0; + frac_src_offset = Mixer::FRAC_ONE - 1; + + Bookkeeping info; + info.step_size = Mixer::FRAC_ONE; + info.rate_modulo = 1; + info.denominator = 2; + info.src_pos_modulo = 0; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } + + mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, + fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, + false, &info); + EXPECT_EQ(2u, dest_offset); + EXPECT_TRUE(3 * Mixer::FRAC_ONE == frac_src_offset); + EXPECT_EQ(0u, info.src_pos_modulo); +} + +// For provided sampler, validate src_pos_modulo for early rollover. +void TestPositionModuloEarlyRolloverLinear(bool mute = false) { + auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::FLOAT, 1, 44100, + 1, 44100, Resampler::LinearInterpolation); + float accum[3]; + uint32_t dest_offset; + int32_t frac_src_offset; + float source[3] = {0.0f}; + + // Non-zero src_pos_modulo, early-rollover case. + dest_offset = 0; + frac_src_offset = 1; + + Bookkeeping info; + info.step_size = Mixer::FRAC_ONE - 1; + info.rate_modulo = 2; + info.denominator = 3; + info.src_pos_modulo = 2; + if (mute) { + info.gain.SetSourceGain(Gain::kMinGainDb); + } + + mixer->Mix(accum, fbl::count_of(accum), &dest_offset, source, + fbl::count_of(source) << kPtsFractionalBits, &frac_src_offset, + false, &info); + EXPECT_EQ(2u, dest_offset); + EXPECT_TRUE((2 * Mixer::FRAC_ONE) + 1 == frac_src_offset); + EXPECT_EQ(0u, info.src_pos_modulo); +} + // Verify PointSampler correctly incorporates src_pos_modulo (along with // rate_modulo and denominator) into position and interpolation results. TEST(Resampling, Position_Modulo_Point) { - TestPositionModulo(Resampler::SampleAndHold); + TestPositionModuloNoRollover(Resampler::SampleAndHold); +} + +TEST(Resampling, Position_Modulo_Point_Rollover) { + TestPositionModuloRollover(Resampler::SampleAndHold); +} + +TEST(Resampling, Position_Modulo_Point_Early_Rollover) { + TestPositionModuloEarlyRolloverPoint(); } // Verify LinearSampler correctly incorporates src_pos_modulo (along with // rate_modulo and denominator) into position and interpolation results. TEST(Resampling, Position_Modulo_Linear) { - TestPositionModulo(Resampler::LinearInterpolation); + TestPositionModuloNoRollover(Resampler::LinearInterpolation); +} + +TEST(Resampling, Position_Modulo_Linear_Rollover) { + TestPositionModuloRollover(Resampler::LinearInterpolation); +} + +TEST(Resampling, Position_Modulo_Linear_Early_Rollover) { + TestPositionModuloEarlyRolloverLinear(); +} + +// Verify PointSampler correctly incorporates src_pos_modulo (along with +// rate_modulo and denominator) into position and interpolation results. +TEST(Resampling, Position_Modulo_Point_Mute) { + TestPositionModuloNoRollover(Resampler::SampleAndHold, true); +} + +TEST(Resampling, Position_Modulo_Point_Mute_Rollover) { + TestPositionModuloRollover(Resampler::SampleAndHold, true); +} + +TEST(Resampling, Position_Modulo_Point_Mute_Early_Rollover) { + TestPositionModuloEarlyRolloverPoint(true); +} + +// Verify LinearSampler correctly incorporates src_pos_modulo (along with +// rate_modulo and denominator) into position and interpolation results. +TEST(Resampling, Position_Modulo_Linear_Mute) { + TestPositionModuloNoRollover(Resampler::LinearInterpolation, true); +} + +TEST(Resampling, Position_Modulo_Linear_Mute_Rollover) { + TestPositionModuloRollover(Resampler::LinearInterpolation, true); +} + +TEST(Resampling, Position_Modulo_Linear_Mute_Early_Rollover) { + TestPositionModuloEarlyRolloverLinear(true); } // Test LinearSampler interpolation accuracy, given fractional position. @@ -466,8 +554,8 @@ TEST(Resampling, Position_Modulo_Linear) { // // With these six precise spot checks, we verify interpolation accuracy to the // fullest extent possible with 32-bit float and 13-bit subframe timestamps. -void TestInterpolation(uint32_t source_frames_per_second, - uint32_t dest_frames_per_second) { +void TestLinearInterpolation(uint32_t source_frames_per_second, + uint32_t dest_frames_per_second) { auto mixer = SelectMixer(fuchsia::media::AudioSampleFormat::FLOAT, 1, source_frames_per_second, 1, dest_frames_per_second, Resampler::LinearInterpolation); @@ -593,41 +681,45 @@ void TestInterpolation(uint32_t source_frames_per_second, } // This test varies the fractional starting offsets, still with rate ratio ONE. -TEST(Resampling, Interpolation_Values) { TestInterpolation(48000, 48000); } +TEST(Resampling, Linear_Interp_Values) { + TestLinearInterpolation(48000, 48000); +} // Various checks similar to above, while varying rate ratio. Interp results // should not change: they depend only on frac_src_pos, not the rate ratio. // dest_offset and frac_src_offset should continue to advance accurately. // // Ratios related to the very-common 147:160 conversion. -TEST(Resampling, Interpolation_Rate_441_48) { - TestInterpolation(88200, 48000); - TestInterpolation(44100, 48000); +TEST(Resampling, Linear_Interp_Rate_441_48) { + TestLinearInterpolation(88200, 48000); + TestLinearInterpolation(44100, 48000); } // Ratios related to the very-common 160:147 conversion. -TEST(Resampling, Interpolation_Rate_48_441) { - TestInterpolation(48000, 44100); - TestInterpolation(48000, 88200); +TEST(Resampling, Linear_Interp_Rate_48_441) { + TestLinearInterpolation(48000, 44100); + TestLinearInterpolation(48000, 88200); } // Power-of-3 rate ratio 1:3 is guaranteed to have fractional rate error, since // 1/3 cannot be perfectly represented by a single binary value. -TEST(Resampling, Interpolation_Rate_16_48) { TestInterpolation(16000, 48000); } +TEST(Resampling, Linear_Interp_Rate_16_48) { + TestLinearInterpolation(16000, 48000); +} // Rate change by the smallest-possible increment will be used as micro-SRC, to // synchronize multiple physically-distinct output devices. This rate ratio also // has the maximum fractional error when converting to the standard 48000 rate. -TEST(Resampling, Interpolation_Rate_MicroSRC) { - TestInterpolation(47999, 48000); +TEST(Resampling, Linear_Interp_Rate_MicroSRC) { + TestLinearInterpolation(47999, 48000); } // This rate ratio, when translated into a step_size based on 4096 subframes, // equates to 3568.999909, generating a maximal fractional value [0.999909]. // Because the callers of Mix() [standard_output_base and audio_renderer_impl] // truncate, a maximal fractional value represents maximal fractional error. -TEST(Resampling, Interpolation_Rate_Max_Error) { - TestInterpolation(38426, 44100); +TEST(Resampling, Linear_Interp_Rate_Max_Error) { + TestLinearInterpolation(38426, 44100); } // Verify PointSampler filter widths. diff --git a/src/media/audio/audio_core/mixer/test/mixer_response_tests.cc b/src/media/audio/audio_core/mixer/test/mixer_response_tests.cc index 0fb3682233bc3255a174d0faf414fa0373fe95cc..5f699e53d1f9a45eab25ac48096ef5acd1908f67 100644 --- a/src/media/audio/audio_core/mixer/test/mixer_response_tests.cc +++ b/src/media/audio/audio_core/mixer/test/mixer_response_tests.cc @@ -345,6 +345,14 @@ void MeasureFreqRespSinad(Mixer* mixer, uint32_t src_buf_size, double* level_db, // Calculate Frequency Response and Signal-to-Noise-And-Distortion (SINAD). level_db[freq_idx] = Gain::DoubleToDb(magn_signal); sinad_db[freq_idx] = Gain::DoubleToDb(magn_signal / magn_other); + + // 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). + mixer->Reset(); } } @@ -643,7 +651,7 @@ TEST(Sinad, Point_MicroSRC) { AudioResult::kPrevSinadPointMicro.data()); } -// Measure Freq Response for Point sampler, no rate conversion. +// Measure Freq Response for Linear sampler, no rate conversion. TEST(FrequencyResponse, Linear_Unity) { TestUnitySampleRatio(Resampler::LinearInterpolation, AudioResult::FreqRespLinearUnity.data(), @@ -653,7 +661,7 @@ TEST(FrequencyResponse, Linear_Unity) { AudioResult::kPrevFreqRespLinearUnity.data()); } -// Measure SINAD for Point sampler, no rate conversion. +// Measure SINAD for Linear sampler, no rate conversion. TEST(Sinad, Linear_Unity) { TestUnitySampleRatio(Resampler::LinearInterpolation, AudioResult::FreqRespLinearUnity.data(), diff --git a/src/media/audio/audio_core/mixer/test/mixer_tests_recap.cc b/src/media/audio/audio_core/mixer/test/mixer_tests_recap.cc index 5f983450e948dbbb4c97b4aaba7903db62f01c65..0d17baf4aa26ec4af57794c012beeaaa8f9b3e99 100644 --- a/src/media/audio/audio_core/mixer/test/mixer_tests_recap.cc +++ b/src/media/audio/audio_core/mixer/test/mixer_tests_recap.cc @@ -15,8 +15,8 @@ namespace media::audio::test { TEST(Recap, FreqResp) { printf("\n Frequency Response"); printf("\n (in dB, with prior results)"); - uint32_t num_freqs; + printf("\n\n Point resampler\n "); printf(" No SRC "); @@ -47,6 +47,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (FrequencySet::UseFullFrequencySet) { if (AudioResult::kPrevFreqRespPointDown0[freq] != -std::numeric_limits<double>::infinity()) { @@ -56,6 +57,7 @@ TEST(Recap, FreqResp) { printf(" "); } } + if (AudioResult::kPrevFreqRespPointDown1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespPointDown1[freq], @@ -72,6 +74,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespPointUp1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespPointUp1[freq], @@ -79,6 +82,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespPointUp2[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespPointUp2[freq], @@ -86,6 +90,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespPointUp3[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespPointUp3[freq], @@ -93,6 +98,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespPointMicro[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespPointMicro[freq], @@ -130,6 +136,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespLinearDown0[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespLinearDown0[freq], @@ -137,6 +144,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespLinearDown1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespLinearDown1[freq], @@ -153,6 +161,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespLinearUp1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespLinearUp1[freq], @@ -169,6 +178,7 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespLinearUp3[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespLinearUp3[freq], @@ -176,12 +186,11 @@ TEST(Recap, FreqResp) { } else { printf(" "); } + if (AudioResult::kPrevFreqRespLinearMicro[freq] != -std::numeric_limits<double>::infinity()) { printf(" %9.6lf (%9.6lf)", AudioResult::FreqRespLinearMicro[freq], AudioResult::kPrevFreqRespLinearMicro[freq]); - } else { - printf(" "); } } } @@ -192,8 +201,8 @@ TEST(Recap, FreqResp) { TEST(Recap, SINAD) { printf("\n Signal-to-Noise-and-Distortion (SINAD)"); printf("\n (in dB, with prior results)"); - uint32_t num_freqs; + printf("\n\n Point resampler\n "); printf(" No SRC "); @@ -224,6 +233,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (FrequencySet::UseFullFrequencySet) { if (AudioResult::kPrevSinadPointDown0[freq] != -std::numeric_limits<double>::infinity()) { @@ -233,6 +243,7 @@ TEST(Recap, SINAD) { printf(" "); } } + if (AudioResult::kPrevSinadPointDown1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadPointDown1[freq], @@ -249,6 +260,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (AudioResult::kPrevSinadPointUp1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadPointUp1[freq], @@ -256,6 +268,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (AudioResult::kPrevSinadPointUp2[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadPointUp2[freq], @@ -307,6 +320,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (AudioResult::kPrevSinadLinearDown0[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadLinearDown0[freq], @@ -314,6 +328,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (AudioResult::kPrevSinadLinearDown1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadLinearDown1[freq], @@ -330,6 +345,7 @@ TEST(Recap, SINAD) { } else { printf(" "); } + if (AudioResult::kPrevSinadLinearUp1[freq] != -std::numeric_limits<double>::infinity()) { printf(" %6.2lf (%6.2lf)", AudioResult::SinadLinearUp1[freq], @@ -376,38 +392,42 @@ TEST(Recap, NoiseFloor) { printf("\n (in dB, with prior results)"); printf("\n\n Sources"); - printf( - "\n\t 8-bit 16-bit 24-bit Float"); - printf( - "\n\t %5.2lf (%5.2lf) %5.2lf (%5.2lf) %6.2lf (%6.2lf) %6.2lf " - "(%6.2lf)", - AudioResult::FloorSource8, AudioResult::kPrevFloorSource8, - AudioResult::FloorSource16, AudioResult::kPrevFloorSource16, - AudioResult::FloorSource24, AudioResult::kPrevFloorSource24, - AudioResult::FloorSourceFloat, AudioResult::kPrevFloorSourceFloat); + printf("\n\t 8-bit "); + printf(" 16-bit "); + printf(" 24-bit "); + printf(" Float"); + printf("\n\t %6.2lf (%6.2lf) %6.2lf (%6.2lf) ", + AudioResult::FloorSource8, AudioResult::kPrevFloorSource8, + AudioResult::FloorSource16, AudioResult::kPrevFloorSource16); + printf(" %6.2lf (%6.2lf) %6.2lf (%6.2lf)", AudioResult::FloorSource24, + AudioResult::kPrevFloorSource24, AudioResult::FloorSourceFloat, + AudioResult::kPrevFloorSourceFloat); printf("\n\n Mix Floor"); - printf("\n\t 8-bit 16-bit 24-bit "); - printf(" Float Stereo->Mono"); - printf( - "\n\t %5.2lf (%5.2lf) %5.2lf (%5.2lf) %6.2lf (%6.2lf) %6.2lf " - "(%6.2lf) %6.2lf (%6.2lf)", - AudioResult::FloorMix8, AudioResult::kPrevFloorMix8, - AudioResult::FloorMix16, AudioResult::kPrevFloorMix16, - AudioResult::FloorMix24, AudioResult::kPrevFloorMix24, - AudioResult::FloorMixFloat, AudioResult::kPrevFloorMixFloat, - AudioResult::FloorStereoMono, AudioResult::kPrevFloorStereoMono); + printf("\n\t 8-bit "); + printf(" 16-bit "); + printf(" 24-bit "); + printf(" Float "); + printf(" Stereo->Mono"); + printf("\n\t %6.2lf (%6.2lf) %6.2lf (%6.2lf) %6.2lf (%6.2lf) ", + AudioResult::FloorMix8, AudioResult::kPrevFloorMix8, + AudioResult::FloorMix16, AudioResult::kPrevFloorMix16, + AudioResult::FloorMix24, AudioResult::kPrevFloorMix24); + printf(" %6.2lf (%6.2lf) %6.2lf (%6.2lf)", AudioResult::FloorMixFloat, + AudioResult::kPrevFloorMixFloat, AudioResult::FloorStereoMono, + AudioResult::kPrevFloorStereoMono); printf("\n\n Outputs"); - printf( - "\n\t 8-bit 16-bit 24-bit Float"); - printf( - "\n\t %5.2lf (%5.2lf) %5.2lf (%5.2lf) %6.2lf (%6.2lf) %6.2lf " - "(%6.2lf)", - AudioResult::FloorOutput8, AudioResult::kPrevFloorOutput8, - AudioResult::FloorOutput16, AudioResult::kPrevFloorOutput16, - AudioResult::FloorOutput24, AudioResult::kPrevFloorOutput24, - AudioResult::FloorOutputFloat, AudioResult::kPrevFloorOutputFloat); + printf("\n\t 8-bit "); + printf(" 16-bit "); + printf(" 24-bit "); + printf(" Float"); + printf("\n\t %6.2lf (%6.2lf) %6.2lf (%6.2lf) ", + AudioResult::FloorOutput8, AudioResult::kPrevFloorOutput8, + AudioResult::FloorOutput16, AudioResult::kPrevFloorOutput16); + printf(" %6.2lf (%6.2lf) %6.2lf (%6.2lf)", AudioResult::FloorOutput24, + AudioResult::kPrevFloorOutput24, AudioResult::FloorOutputFloat, + AudioResult::kPrevFloorOutputFloat); printf("\n\n"); } @@ -420,17 +440,17 @@ TEST(Recap, DynamicRange) { printf("\n (in dB, with prior results)"); printf("\n\n Input Gain Mixed Result Usable Range\n"); - printf("\n %9.6lf %10.6lf ( > %9.6lf) %6.2lf (%5.2lf)", + printf("\n %9.6lf %10.6lf ( > %9.6lf) %6.2lf (%6.2lf)", AudioResult::kMaxGainDbNonUnity, AudioResult::LevelEpsilonDown, AudioResult::kPrevLevelEpsilonDown, AudioResult::SinadEpsilonDown, AudioResult::kPrevSinadEpsilonDown); - printf("\n -30.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%5.2lf)", + printf("\n -30.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%6.2lf)", AudioResult::Level30Down, AudioResult::kPrevDynRangeTolerance, AudioResult::Sinad30Down, AudioResult::kPrevSinad30Down); - printf("\n -60.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%5.2lf)", + printf("\n -60.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%6.2lf)", AudioResult::Level60Down, AudioResult::kPrevDynRangeTolerance, AudioResult::Sinad60Down, AudioResult::kPrevSinad60Down); - printf("\n -90.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%5.2lf)", + printf("\n -90.0000 %8.4lf (+/- %6.4lf ) %6.2lf (%6.2lf)", AudioResult::Level90Down, AudioResult::kPrevDynRangeTolerance, AudioResult::Sinad90Down, AudioResult::kPrevSinad90Down); printf("\n\n");