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