push_sinc_resampler_unittest.cc (16392B)
1 /* 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "common_audio/resampler/push_sinc_resampler.h" 12 13 #include <algorithm> 14 #include <cmath> 15 #include <cstdint> 16 #include <cstdio> 17 #include <cstring> 18 #include <memory> 19 #include <tuple> 20 21 #include "common_audio/include/audio_util.h" 22 #include "common_audio/resampler/sinc_resampler.h" 23 #include "common_audio/resampler/sinusoidal_linear_chirp_source.h" 24 #include "rtc_base/time_utils.h" 25 #include "test/gtest.h" 26 27 namespace webrtc { 28 namespace { 29 30 // Almost all conversions have an RMS error of around -14 dbFS. 31 constexpr double kResamplingRMSError = -14.42; 32 33 // Used to convert errors to dbFS. 34 template <typename T> 35 T DBFS(T x) { 36 return 20 * std::log10(x); 37 } 38 39 } // namespace 40 41 class PushSincResamplerTest : public ::testing::TestWithParam< 42 ::testing::tuple<int, int, double, double>> { 43 public: 44 PushSincResamplerTest() 45 : input_rate_(::testing::get<0>(GetParam())), 46 output_rate_(::testing::get<1>(GetParam())), 47 rms_error_(::testing::get<2>(GetParam())), 48 low_freq_error_(::testing::get<3>(GetParam())) {} 49 50 ~PushSincResamplerTest() override {} 51 52 protected: 53 void ResampleBenchmarkTest(bool int_format); 54 void ResampleTest(bool int_format); 55 56 int input_rate_; 57 int output_rate_; 58 double rms_error_; 59 double low_freq_error_; 60 }; 61 62 class ZeroSource : public SincResamplerCallback { 63 public: 64 void Run(size_t frames, float* destination) override { 65 std::memset(destination, 0, sizeof(float) * frames); 66 } 67 }; 68 69 void PushSincResamplerTest::ResampleBenchmarkTest(bool int_format) { 70 const size_t input_samples = static_cast<size_t>(input_rate_ / 100); 71 const size_t output_samples = static_cast<size_t>(output_rate_ / 100); 72 const int kResampleIterations = 500000; 73 74 // Source for data to be resampled. 75 ZeroSource resampler_source; 76 77 std::unique_ptr<float[]> resampled_destination(new float[output_samples]); 78 std::unique_ptr<float[]> source(new float[input_samples]); 79 std::unique_ptr<int16_t[]> source_int(new int16_t[input_samples]); 80 std::unique_ptr<int16_t[]> destination_int(new int16_t[output_samples]); 81 82 resampler_source.Run(input_samples, source.get()); 83 for (size_t i = 0; i < input_samples; ++i) { 84 source_int[i] = static_cast<int16_t>(floor(32767 * source[i] + 0.5)); 85 } 86 87 printf("Benchmarking %d iterations of %d Hz -> %d Hz:\n", kResampleIterations, 88 input_rate_, output_rate_); 89 const double io_ratio = input_rate_ / static_cast<double>(output_rate_); 90 SincResampler sinc_resampler(io_ratio, SincResampler::kDefaultRequestSize, 91 &resampler_source); 92 int64_t start = TimeNanos(); 93 for (int i = 0; i < kResampleIterations; ++i) { 94 sinc_resampler.Resample(output_samples, resampled_destination.get()); 95 } 96 double total_time_sinc_us = (TimeNanos() - start) / kNumNanosecsPerMicrosec; 97 printf("SincResampler took %.2f us per frame.\n", 98 total_time_sinc_us / kResampleIterations); 99 100 PushSincResampler resampler(input_samples, output_samples); 101 start = TimeNanos(); 102 if (int_format) { 103 for (int i = 0; i < kResampleIterations; ++i) { 104 EXPECT_EQ(output_samples, 105 resampler.Resample(source_int.get(), input_samples, 106 destination_int.get(), output_samples)); 107 } 108 } else { 109 for (int i = 0; i < kResampleIterations; ++i) { 110 EXPECT_EQ(output_samples, resampler.Resample(source.get(), input_samples, 111 resampled_destination.get(), 112 output_samples)); 113 } 114 } 115 double total_time_us = (TimeNanos() - start) / kNumNanosecsPerMicrosec; 116 printf( 117 "PushSincResampler took %.2f us per frame; which is a %.1f%% overhead " 118 "on SincResampler.\n\n", 119 total_time_us / kResampleIterations, 120 (total_time_us - total_time_sinc_us) / total_time_sinc_us * 100); 121 } 122 123 // Disabled because it takes too long to run routinely. Use for performance 124 // benchmarking when needed. 125 TEST_P(PushSincResamplerTest, DISABLED_BenchmarkInt) { 126 ResampleBenchmarkTest(true); 127 } 128 129 TEST_P(PushSincResamplerTest, DISABLED_BenchmarkFloat) { 130 ResampleBenchmarkTest(false); 131 } 132 133 // Tests resampling using a given input and output sample rate. 134 void PushSincResamplerTest::ResampleTest(bool int_format) { 135 // Make comparisons using one second of data. 136 static const double kTestDurationSecs = 1; 137 // 10 ms blocks. 138 const size_t kNumBlocks = static_cast<size_t>(kTestDurationSecs * 100); 139 const size_t input_block_size = static_cast<size_t>(input_rate_ / 100); 140 const size_t output_block_size = static_cast<size_t>(output_rate_ / 100); 141 const size_t input_samples = 142 static_cast<size_t>(kTestDurationSecs * input_rate_); 143 const size_t output_samples = 144 static_cast<size_t>(kTestDurationSecs * output_rate_); 145 146 // Nyquist frequency for the input sampling rate. 147 const double input_nyquist_freq = 0.5 * input_rate_; 148 149 // Source for data to be resampled. 150 SinusoidalLinearChirpSource resampler_source(input_rate_, input_samples, 151 input_nyquist_freq, 0); 152 153 PushSincResampler resampler(input_block_size, output_block_size); 154 155 // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to 156 // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes. 157 std::unique_ptr<float[]> resampled_destination(new float[output_samples]); 158 std::unique_ptr<float[]> pure_destination(new float[output_samples]); 159 std::unique_ptr<float[]> source(new float[input_samples]); 160 std::unique_ptr<int16_t[]> source_int(new int16_t[input_block_size]); 161 std::unique_ptr<int16_t[]> destination_int(new int16_t[output_block_size]); 162 163 // The sinc resampler has an implicit delay of approximately half the kernel 164 // size at the input sample rate. By moving to a push model, this delay 165 // becomes explicit and is managed by zero-stuffing in PushSincResampler. We 166 // deal with it in the test by delaying the "pure" source to match. It must be 167 // checked before the first call to Resample(), because ChunkSize() will 168 // change afterwards. 169 const size_t output_delay_samples = 170 output_block_size - resampler.get_resampler_for_testing()->ChunkSize(); 171 172 // Generate resampled signal. 173 // With the PushSincResampler, we produce the signal block-by-10ms-block 174 // rather than in a single pass, to exercise how it will be used in WebRTC. 175 resampler_source.Run(input_samples, source.get()); 176 if (int_format) { 177 for (size_t i = 0; i < kNumBlocks; ++i) { 178 FloatToS16(&source[i * input_block_size], input_block_size, 179 source_int.get()); 180 EXPECT_EQ(output_block_size, 181 resampler.Resample(source_int.get(), input_block_size, 182 destination_int.get(), output_block_size)); 183 S16ToFloat(destination_int.get(), output_block_size, 184 &resampled_destination[i * output_block_size]); 185 } 186 } else { 187 for (size_t i = 0; i < kNumBlocks; ++i) { 188 EXPECT_EQ( 189 output_block_size, 190 resampler.Resample(&source[i * input_block_size], input_block_size, 191 &resampled_destination[i * output_block_size], 192 output_block_size)); 193 } 194 } 195 196 // Generate pure signal. 197 SinusoidalLinearChirpSource pure_source( 198 output_rate_, output_samples, input_nyquist_freq, output_delay_samples); 199 pure_source.Run(output_samples, pure_destination.get()); 200 201 // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which 202 // we refer to as low and high. 203 static const double kLowFrequencyNyquistRange = 0.7; 204 static const double kHighFrequencyNyquistRange = 0.9; 205 206 // Calculate Root-Mean-Square-Error and maximum error for the resampling. 207 double sum_of_squares = 0; 208 double low_freq_max_error = 0; 209 double high_freq_max_error = 0; 210 int minimum_rate = std::min(input_rate_, output_rate_); 211 double low_frequency_range = kLowFrequencyNyquistRange * 0.5 * minimum_rate; 212 double high_frequency_range = kHighFrequencyNyquistRange * 0.5 * minimum_rate; 213 214 for (size_t i = 0; i < output_samples; ++i) { 215 double error = fabs(resampled_destination[i] - pure_destination[i]); 216 217 if (pure_source.Frequency(i) < low_frequency_range) { 218 if (error > low_freq_max_error) 219 low_freq_max_error = error; 220 } else if (pure_source.Frequency(i) < high_frequency_range) { 221 if (error > high_freq_max_error) 222 high_freq_max_error = error; 223 } 224 // TODO(dalecurtis): Sanity check frequencies > kHighFrequencyNyquistRange. 225 226 sum_of_squares += error * error; 227 } 228 229 double rms_error = sqrt(sum_of_squares / output_samples); 230 231 rms_error = DBFS(rms_error); 232 // In order to keep the thresholds in this test identical to SincResamplerTest 233 // we must account for the quantization error introduced by truncating from 234 // float to int. This happens twice (once at input and once at output) and we 235 // allow for the maximum possible error (1 / 32767) for each step. 236 // 237 // The quantization error is insignificant in the RMS calculation so does not 238 // need to be accounted for there. 239 low_freq_max_error = DBFS(low_freq_max_error - 2.0 / 32767); 240 high_freq_max_error = DBFS(high_freq_max_error - 2.0 / 32767); 241 242 EXPECT_LE(rms_error, rms_error_); 243 EXPECT_LE(low_freq_max_error, low_freq_error_); 244 245 // All conversions currently have a high frequency error around -6 dbFS. 246 static const double kHighFrequencyMaxError = -6.01; 247 EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError); 248 } 249 250 TEST_P(PushSincResamplerTest, ResampleInt) { 251 ResampleTest(true); 252 } 253 254 TEST_P(PushSincResamplerTest, ResampleFloat) { 255 ResampleTest(false); 256 } 257 258 // Thresholds chosen arbitrarily based on what each resampling reported during 259 // testing. All thresholds are in dbFS, http://en.wikipedia.org/wiki/DBFS. 260 INSTANTIATE_TEST_SUITE_P( 261 PushSincResamplerTest, 262 PushSincResamplerTest, 263 ::testing::Values( 264 // First run through the rates tested in SincResamplerTest. The 265 // thresholds are identical. 266 // 267 // We don't directly test rates which fail to provide an integer number 268 // of samples in a 10 ms block (22050 and 11025 Hz), they are replaced 269 // by nearby rates in order to simplify testing. 270 // 271 // The PushSincResampler is in practice sample rate agnostic and derives 272 // resampling ratios from the block size, which for WebRTC purposes are 273 // blocks of floor(sample_rate/100) samples. So the 22050 Hz case is 274 // treated identically to the 22000 Hz case. Direct tests of 22050 Hz 275 // have to account for the simulated clock drift induced by the 276 // resampler inferring an incorrect sample rate ratio, without testing 277 // anything new within the resampler itself. 278 279 // To 22kHz 280 std::make_tuple(8000, 22000, kResamplingRMSError, -62.73), 281 std::make_tuple(11000, 22000, kResamplingRMSError, -74.17), 282 std::make_tuple(16000, 22000, kResamplingRMSError, -62.54), 283 std::make_tuple(22000, 22000, kResamplingRMSError, -73.53), 284 std::make_tuple(32000, 22000, kResamplingRMSError, -46.45), 285 std::make_tuple(44100, 22000, kResamplingRMSError, -28.34), 286 std::make_tuple(48000, 22000, -15.01, -25.56), 287 std::make_tuple(96000, 22000, -18.49, -13.30), 288 std::make_tuple(192000, 22000, -20.50, -9.20), 289 290 // To 44.1kHz 291 ::testing::make_tuple(8000, 44100, kResamplingRMSError, -62.73), 292 ::testing::make_tuple(11000, 44100, kResamplingRMSError, -63.57), 293 ::testing::make_tuple(16000, 44100, kResamplingRMSError, -62.54), 294 ::testing::make_tuple(22000, 44100, kResamplingRMSError, -62.73), 295 ::testing::make_tuple(32000, 44100, kResamplingRMSError, -63.32), 296 ::testing::make_tuple(44100, 44100, kResamplingRMSError, -73.53), 297 ::testing::make_tuple(48000, 44100, -15.01, -64.04), 298 ::testing::make_tuple(96000, 44100, -18.49, -25.51), 299 ::testing::make_tuple(192000, 44100, -20.50, -13.31), 300 301 // To 48kHz 302 ::testing::make_tuple(8000, 48000, kResamplingRMSError, -63.43), 303 ::testing::make_tuple(11000, 48000, kResamplingRMSError, -63.96), 304 ::testing::make_tuple(16000, 48000, kResamplingRMSError, -63.96), 305 ::testing::make_tuple(22000, 48000, kResamplingRMSError, -63.80), 306 ::testing::make_tuple(32000, 48000, kResamplingRMSError, -64.04), 307 ::testing::make_tuple(44100, 48000, kResamplingRMSError, -62.63), 308 ::testing::make_tuple(48000, 48000, kResamplingRMSError, -73.52), 309 ::testing::make_tuple(96000, 48000, -18.40, -28.44), 310 ::testing::make_tuple(192000, 48000, -20.43, -14.11), 311 312 // To 96kHz 313 ::testing::make_tuple(8000, 96000, kResamplingRMSError, -63.19), 314 ::testing::make_tuple(11000, 96000, kResamplingRMSError, -63.89), 315 ::testing::make_tuple(16000, 96000, kResamplingRMSError, -63.39), 316 ::testing::make_tuple(22000, 96000, kResamplingRMSError, -63.39), 317 ::testing::make_tuple(32000, 96000, kResamplingRMSError, -63.95), 318 ::testing::make_tuple(44100, 96000, kResamplingRMSError, -62.63), 319 ::testing::make_tuple(48000, 96000, kResamplingRMSError, -73.52), 320 ::testing::make_tuple(96000, 96000, kResamplingRMSError, -73.52), 321 ::testing::make_tuple(192000, 96000, kResamplingRMSError, -28.41), 322 323 // To 192kHz 324 ::testing::make_tuple(8000, 192000, kResamplingRMSError, -63.10), 325 ::testing::make_tuple(11000, 192000, kResamplingRMSError, -63.17), 326 ::testing::make_tuple(16000, 192000, kResamplingRMSError, -63.14), 327 ::testing::make_tuple(22000, 192000, kResamplingRMSError, -63.14), 328 ::testing::make_tuple(32000, 192000, kResamplingRMSError, -63.38), 329 ::testing::make_tuple(44100, 192000, kResamplingRMSError, -62.63), 330 ::testing::make_tuple(48000, 192000, kResamplingRMSError, -73.44), 331 ::testing::make_tuple(96000, 192000, kResamplingRMSError, -73.52), 332 ::testing::make_tuple(192000, 192000, kResamplingRMSError, -73.52), 333 334 // Next run through some additional cases interesting for WebRTC. 335 // We skip some extreme downsampled cases (192 -> {8, 16}, 96 -> 8) 336 // because they violate `kHighFrequencyMaxError`, which is not 337 // unexpected. It's very unlikely that we'll see these conversions in 338 // practice anyway. 339 340 // To 8 kHz 341 ::testing::make_tuple(8000, 8000, kResamplingRMSError, -75.50), 342 ::testing::make_tuple(16000, 8000, -18.56, -28.79), 343 ::testing::make_tuple(32000, 8000, -20.36, -14.13), 344 ::testing::make_tuple(44100, 8000, -21.00, -11.39), 345 ::testing::make_tuple(48000, 8000, -20.96, -11.04), 346 347 // To 16 kHz 348 ::testing::make_tuple(8000, 16000, kResamplingRMSError, -70.30), 349 ::testing::make_tuple(11000, 16000, kResamplingRMSError, -72.31), 350 ::testing::make_tuple(16000, 16000, kResamplingRMSError, -75.51), 351 ::testing::make_tuple(22000, 16000, kResamplingRMSError, -52.08), 352 ::testing::make_tuple(32000, 16000, -18.48, -28.59), 353 ::testing::make_tuple(44100, 16000, -19.30, -19.67), 354 ::testing::make_tuple(48000, 16000, -19.81, -18.11), 355 ::testing::make_tuple(96000, 16000, -20.95, -10.9596), 356 357 // To 32 kHz 358 ::testing::make_tuple(8000, 32000, kResamplingRMSError, -70.30), 359 ::testing::make_tuple(11000, 32000, kResamplingRMSError, -71.34), 360 ::testing::make_tuple(16000, 32000, kResamplingRMSError, -75.51), 361 ::testing::make_tuple(22000, 32000, kResamplingRMSError, -72.05), 362 ::testing::make_tuple(32000, 32000, kResamplingRMSError, -75.51), 363 ::testing::make_tuple(44100, 32000, -16.44, -51.0349), 364 ::testing::make_tuple(48000, 32000, -16.90, -43.9967), 365 ::testing::make_tuple(96000, 32000, -19.61, -18.04), 366 ::testing::make_tuple(192000, 32000, -21.02, -10.94))); 367 368 } // namespace webrtc