sinc_resampler_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <memory>
  5. #include "base/bind.h"
  6. #include "base/callback_helpers.h"
  7. #include "base/numerics/math_constants.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/time/time.h"
  10. #include "build/build_config.h"
  11. #include "media/base/sinc_resampler.h"
  12. #include "testing/gmock/include/gmock/gmock.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. using testing::_;
  15. namespace media {
  16. static const double kSampleRateRatio = 192000.0 / 44100.0;
  17. // Helper class to ensure ChunkedResample() functions properly.
  18. class MockSource {
  19. public:
  20. MOCK_METHOD2(ProvideInput, void(int frames, float* destination));
  21. };
  22. ACTION(ClearBuffer) {
  23. memset(arg1, 0, arg0 * sizeof(float));
  24. }
  25. ACTION(FillBuffer) {
  26. // Value chosen arbitrarily such that SincResampler resamples it to something
  27. // easily representable on all platforms; e.g., using kSampleRateRatio this
  28. // becomes 1.81219.
  29. memset(arg1, 64, arg0 * sizeof(float));
  30. }
  31. // Test requesting multiples of ChunkSize() frames results in the proper number
  32. // of callbacks.
  33. TEST(SincResamplerTest, ChunkedResample) {
  34. MockSource mock_source;
  35. // Choose a high ratio of input to output samples which will result in quick
  36. // exhaustion of SincResampler's internal buffers.
  37. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  38. base::BindRepeating(&MockSource::ProvideInput,
  39. base::Unretained(&mock_source)));
  40. static const int kChunks = 2;
  41. int max_chunk_size = resampler.ChunkSize() * kChunks;
  42. std::unique_ptr<float[]> resampled_destination(new float[max_chunk_size]);
  43. // Verify requesting ChunkSize() frames causes a single callback.
  44. EXPECT_CALL(mock_source, ProvideInput(_, _))
  45. .Times(1).WillOnce(ClearBuffer());
  46. resampler.Resample(resampler.ChunkSize(), resampled_destination.get());
  47. // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks.
  48. testing::Mock::VerifyAndClear(&mock_source);
  49. EXPECT_CALL(mock_source, ProvideInput(_, _))
  50. .Times(kChunks).WillRepeatedly(ClearBuffer());
  51. resampler.Resample(max_chunk_size, resampled_destination.get());
  52. }
  53. // Verify priming the resampler avoids changes to ChunkSize() between calls.
  54. TEST(SincResamplerTest, PrimedResample) {
  55. MockSource mock_source;
  56. // Choose a high ratio of input to output samples which will result in quick
  57. // exhaustion of SincResampler's internal buffers.
  58. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  59. base::BindRepeating(&MockSource::ProvideInput,
  60. base::Unretained(&mock_source)));
  61. // Verify the priming adjusts the chunk size within reasonable limits.
  62. const int first_chunk_size = resampler.ChunkSize();
  63. resampler.PrimeWithSilence();
  64. const int max_chunk_size = resampler.ChunkSize();
  65. EXPECT_NE(first_chunk_size, max_chunk_size);
  66. EXPECT_LE(
  67. max_chunk_size,
  68. static_cast<int>(first_chunk_size + std::ceil(SincResampler::kKernelSize /
  69. (2 * kSampleRateRatio))));
  70. // Verify Flush() resets to an unprimed state.
  71. resampler.Flush();
  72. EXPECT_EQ(first_chunk_size, resampler.ChunkSize());
  73. resampler.PrimeWithSilence();
  74. EXPECT_EQ(max_chunk_size, resampler.ChunkSize());
  75. const int kChunks = 2;
  76. const int kMaxFrames = max_chunk_size * kChunks;
  77. std::unique_ptr<float[]> resampled_destination(new float[kMaxFrames]);
  78. // Verify requesting ChunkSize() frames causes a single callback.
  79. EXPECT_CALL(mock_source, ProvideInput(_, _))
  80. .Times(1).WillOnce(ClearBuffer());
  81. resampler.Resample(max_chunk_size, resampled_destination.get());
  82. EXPECT_EQ(max_chunk_size, resampler.ChunkSize());
  83. // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks.
  84. testing::Mock::VerifyAndClear(&mock_source);
  85. EXPECT_CALL(mock_source, ProvideInput(_, _))
  86. .Times(kChunks).WillRepeatedly(ClearBuffer());
  87. resampler.Resample(kMaxFrames, resampled_destination.get());
  88. EXPECT_EQ(max_chunk_size, resampler.ChunkSize());
  89. }
  90. // Test flush resets the internal state properly.
  91. TEST(SincResamplerTest, Flush) {
  92. MockSource mock_source;
  93. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  94. base::BindRepeating(&MockSource::ProvideInput,
  95. base::Unretained(&mock_source)));
  96. std::unique_ptr<float[]> resampled_destination(
  97. new float[resampler.ChunkSize()]);
  98. // Fill the resampler with junk data.
  99. EXPECT_CALL(mock_source, ProvideInput(_, _))
  100. .Times(1).WillOnce(FillBuffer());
  101. resampler.Resample(resampler.ChunkSize() / 2, resampled_destination.get());
  102. ASSERT_NE(resampled_destination[0], 0);
  103. // Flush and request more data, which should all be zeros now.
  104. resampler.Flush();
  105. testing::Mock::VerifyAndClear(&mock_source);
  106. EXPECT_CALL(mock_source, ProvideInput(_, _))
  107. .Times(1).WillOnce(ClearBuffer());
  108. resampler.Resample(resampler.ChunkSize() / 2, resampled_destination.get());
  109. for (int i = 0; i < resampler.ChunkSize() / 2; ++i)
  110. ASSERT_FLOAT_EQ(resampled_destination[i], 0);
  111. }
  112. TEST(SincResamplerTest, DISABLED_SetRatioBench) {
  113. MockSource mock_source;
  114. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  115. base::BindRepeating(&MockSource::ProvideInput,
  116. base::Unretained(&mock_source)));
  117. base::TimeTicks start = base::TimeTicks::Now();
  118. for (int i = 1; i < 10000; ++i)
  119. resampler.SetRatio(1.0 / i);
  120. double total_time_c_ms = (base::TimeTicks::Now() - start).InMillisecondsF();
  121. printf("SetRatio() took %.2fms.\n", total_time_c_ms);
  122. }
  123. // Ensure various optimized Convolve() methods return the same value. Only run
  124. // this test if other optimized methods exist, otherwise the default Convolve()
  125. // will be tested by the parameterized SincResampler tests below.
  126. static const double kKernelInterpolationFactor = 0.5;
  127. TEST(SincResamplerTest, Convolve) {
  128. // Initialize a dummy resampler.
  129. MockSource mock_source;
  130. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  131. base::BindRepeating(&MockSource::ProvideInput,
  132. base::Unretained(&mock_source)));
  133. // The optimized Convolve methods are slightly more precise than Convolve_C(),
  134. // so comparison must be done using an epsilon.
  135. static const double kEpsilon = 0.00000005;
  136. // Use a kernel from SincResampler as input and kernel data, this has the
  137. // benefit of already being properly sized and aligned for Convolve_SSE().
  138. double result = resampler.Convolve_C(
  139. resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
  140. resampler.kernel_storage_.get(), kKernelInterpolationFactor);
  141. double result2 = resampler.convolve_proc_(
  142. resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
  143. resampler.kernel_storage_.get(), kKernelInterpolationFactor);
  144. EXPECT_NEAR(result2, result, kEpsilon);
  145. // Test Convolve() w/ unaligned input pointer.
  146. result = resampler.Convolve_C(
  147. resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
  148. resampler.kernel_storage_.get(), kKernelInterpolationFactor);
  149. result2 = resampler.convolve_proc_(
  150. resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
  151. resampler.kernel_storage_.get(), kKernelInterpolationFactor);
  152. EXPECT_NEAR(result2, result, kEpsilon);
  153. }
  154. // Fake audio source for testing the resampler. Generates a sinusoidal linear
  155. // chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
  156. // resampler for the specific sample rate conversion being used.
  157. class SinusoidalLinearChirpSource {
  158. public:
  159. SinusoidalLinearChirpSource(int sample_rate,
  160. int samples,
  161. double max_frequency)
  162. : sample_rate_(sample_rate),
  163. total_samples_(samples),
  164. max_frequency_(max_frequency),
  165. current_index_(0) {
  166. // Chirp rate.
  167. double duration = static_cast<double>(total_samples_) / sample_rate_;
  168. k_ = (max_frequency_ - kMinFrequency) / duration;
  169. }
  170. SinusoidalLinearChirpSource(const SinusoidalLinearChirpSource&) = delete;
  171. SinusoidalLinearChirpSource& operator=(const SinusoidalLinearChirpSource&) =
  172. delete;
  173. virtual ~SinusoidalLinearChirpSource() = default;
  174. void ProvideInput(int frames, float* destination) {
  175. for (int i = 0; i < frames; ++i, ++current_index_) {
  176. // Filter out frequencies higher than Nyquist.
  177. if (Frequency(current_index_) > 0.5 * sample_rate_) {
  178. destination[i] = 0;
  179. } else {
  180. // Calculate time in seconds.
  181. double t = static_cast<double>(current_index_) / sample_rate_;
  182. // Sinusoidal linear chirp.
  183. destination[i] =
  184. sin(2 * base::kPiDouble * (kMinFrequency * t + (k_ / 2) * t * t));
  185. }
  186. }
  187. }
  188. double Frequency(int position) {
  189. return kMinFrequency + position * (max_frequency_ - kMinFrequency)
  190. / total_samples_;
  191. }
  192. private:
  193. static constexpr int kMinFrequency = 5;
  194. double sample_rate_;
  195. int total_samples_;
  196. double max_frequency_;
  197. double k_;
  198. int current_index_;
  199. };
  200. typedef std::tuple<int, int, double, double> SincResamplerTestData;
  201. class SincResamplerTest
  202. : public testing::TestWithParam<SincResamplerTestData> {
  203. public:
  204. SincResamplerTest()
  205. : input_rate_(std::get<0>(GetParam())),
  206. output_rate_(std::get<1>(GetParam())),
  207. rms_error_(std::get<2>(GetParam())),
  208. low_freq_error_(std::get<3>(GetParam())) {}
  209. virtual ~SincResamplerTest() = default;
  210. protected:
  211. int input_rate_;
  212. int output_rate_;
  213. double rms_error_;
  214. double low_freq_error_;
  215. };
  216. // Tests resampling using a given input and output sample rate.
  217. TEST_P(SincResamplerTest, Resample) {
  218. // Make comparisons using one second of data.
  219. static const double kTestDurationSecs = 1;
  220. int input_samples = kTestDurationSecs * input_rate_;
  221. int output_samples = kTestDurationSecs * output_rate_;
  222. // Nyquist frequency for the input sampling rate.
  223. double input_nyquist_freq = 0.5 * input_rate_;
  224. // Source for data to be resampled.
  225. SinusoidalLinearChirpSource resampler_source(
  226. input_rate_, input_samples, input_nyquist_freq);
  227. const double io_ratio = input_rate_ / static_cast<double>(output_rate_);
  228. SincResampler resampler(
  229. io_ratio, SincResampler::kDefaultRequestSize,
  230. base::BindRepeating(&SinusoidalLinearChirpSource::ProvideInput,
  231. base::Unretained(&resampler_source)));
  232. // Force an update to the sample rate ratio to ensure dyanmic sample rate
  233. // changes are working correctly.
  234. std::unique_ptr<float[]> kernel(new float[SincResampler::kKernelStorageSize]);
  235. memcpy(kernel.get(), resampler.get_kernel_for_testing(),
  236. SincResampler::kKernelStorageSize);
  237. resampler.SetRatio(base::kPiDouble);
  238. ASSERT_NE(0, memcmp(kernel.get(), resampler.get_kernel_for_testing(),
  239. SincResampler::kKernelStorageSize));
  240. resampler.SetRatio(io_ratio);
  241. ASSERT_EQ(0, memcmp(kernel.get(), resampler.get_kernel_for_testing(),
  242. SincResampler::kKernelStorageSize));
  243. // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
  244. // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
  245. std::unique_ptr<float[]> resampled_destination(new float[output_samples]);
  246. std::unique_ptr<float[]> pure_destination(new float[output_samples]);
  247. // Generate resampled signal.
  248. resampler.Resample(output_samples, resampled_destination.get());
  249. // Generate pure signal.
  250. SinusoidalLinearChirpSource pure_source(
  251. output_rate_, output_samples, input_nyquist_freq);
  252. pure_source.ProvideInput(output_samples, pure_destination.get());
  253. // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
  254. // we refer to as low and high.
  255. static const double kLowFrequencyNyquistRange = 0.7;
  256. static const double kHighFrequencyNyquistRange = 0.9;
  257. // Calculate Root-Mean-Square-Error and maximum error for the resampling.
  258. double sum_of_squares = 0;
  259. double low_freq_max_error = 0;
  260. double high_freq_max_error = 0;
  261. int minimum_rate = std::min(input_rate_, output_rate_);
  262. double low_frequency_range = kLowFrequencyNyquistRange * 0.5 * minimum_rate;
  263. double high_frequency_range = kHighFrequencyNyquistRange * 0.5 * minimum_rate;
  264. for (int i = 0; i < output_samples; ++i) {
  265. double error = fabs(resampled_destination[i] - pure_destination[i]);
  266. if (pure_source.Frequency(i) < low_frequency_range) {
  267. if (error > low_freq_max_error)
  268. low_freq_max_error = error;
  269. } else if (pure_source.Frequency(i) < high_frequency_range) {
  270. if (error > high_freq_max_error)
  271. high_freq_max_error = error;
  272. }
  273. // TODO(dalecurtis): Sanity check frequencies > kHighFrequencyNyquistRange.
  274. sum_of_squares += error * error;
  275. }
  276. double rms_error = sqrt(sum_of_squares / output_samples);
  277. // Convert each error to dbFS.
  278. #define DBFS(x) 20 * log10(x)
  279. rms_error = DBFS(rms_error);
  280. low_freq_max_error = DBFS(low_freq_max_error);
  281. high_freq_max_error = DBFS(high_freq_max_error);
  282. EXPECT_LE(rms_error, rms_error_);
  283. EXPECT_LE(low_freq_max_error, low_freq_error_);
  284. // All conversions currently have a high frequency error around -6 dbFS.
  285. static const double kHighFrequencyMaxError = -6.02;
  286. EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError);
  287. }
  288. // Almost all conversions have an RMS error of around -14 dbFS.
  289. static const double kResamplingRMSError = -14.58;
  290. // Thresholds chosen arbitrarily based on what each resampling reported during
  291. // testing. All thresholds are in dbFS, http://en.wikipedia.org/wiki/DBFS.
  292. INSTANTIATE_TEST_SUITE_P(
  293. SincResamplerTest,
  294. SincResamplerTest,
  295. testing::Values(
  296. // To 44.1kHz
  297. std::make_tuple(8000, 44100, kResamplingRMSError, -62.73),
  298. std::make_tuple(11025, 44100, kResamplingRMSError, -72.19),
  299. std::make_tuple(16000, 44100, kResamplingRMSError, -62.54),
  300. std::make_tuple(22050, 44100, kResamplingRMSError, -73.53),
  301. std::make_tuple(32000, 44100, kResamplingRMSError, -63.32),
  302. std::make_tuple(44100, 44100, kResamplingRMSError, -73.53),
  303. std::make_tuple(48000, 44100, -15.01, -64.04),
  304. std::make_tuple(96000, 44100, -18.49, -25.51),
  305. std::make_tuple(192000, 44100, -20.50, -13.31),
  306. // To 48kHz
  307. std::make_tuple(8000, 48000, kResamplingRMSError, -63.43),
  308. std::make_tuple(11025, 48000, kResamplingRMSError, -62.61),
  309. std::make_tuple(16000, 48000, kResamplingRMSError, -63.96),
  310. std::make_tuple(22050, 48000, kResamplingRMSError, -62.42),
  311. std::make_tuple(32000, 48000, kResamplingRMSError, -64.04),
  312. std::make_tuple(44100, 48000, kResamplingRMSError, -62.63),
  313. std::make_tuple(48000, 48000, kResamplingRMSError, -73.52),
  314. std::make_tuple(96000, 48000, -18.40, -28.44),
  315. std::make_tuple(192000, 48000, -20.43, -14.11),
  316. // To 96kHz
  317. std::make_tuple(8000, 96000, kResamplingRMSError, -63.19),
  318. std::make_tuple(11025, 96000, kResamplingRMSError, -62.61),
  319. std::make_tuple(16000, 96000, kResamplingRMSError, -63.39),
  320. std::make_tuple(22050, 96000, kResamplingRMSError, -62.42),
  321. std::make_tuple(32000, 96000, kResamplingRMSError, -63.95),
  322. std::make_tuple(44100, 96000, kResamplingRMSError, -62.63),
  323. std::make_tuple(48000, 96000, kResamplingRMSError, -73.52),
  324. std::make_tuple(96000, 96000, kResamplingRMSError, -73.52),
  325. std::make_tuple(192000, 96000, kResamplingRMSError, -28.41),
  326. // To 192kHz
  327. std::make_tuple(8000, 192000, kResamplingRMSError, -63.10),
  328. std::make_tuple(11025, 192000, kResamplingRMSError, -62.61),
  329. std::make_tuple(16000, 192000, kResamplingRMSError, -63.14),
  330. std::make_tuple(22050, 192000, kResamplingRMSError, -62.42),
  331. std::make_tuple(32000, 192000, kResamplingRMSError, -63.38),
  332. std::make_tuple(44100, 192000, kResamplingRMSError, -62.63),
  333. std::make_tuple(48000, 192000, kResamplingRMSError, -73.44),
  334. std::make_tuple(96000, 192000, kResamplingRMSError, -73.52),
  335. std::make_tuple(192000, 192000, kResamplingRMSError, -73.52)));
  336. // Verify the resampler properly reports the max number of input frames it would
  337. // request.
  338. TEST(SincResamplerTest, GetMaxInputFramesRequestedTest) {
  339. SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
  340. SincResampler::ReadCB());
  341. EXPECT_EQ(SincResampler::kDefaultRequestSize,
  342. resampler.GetMaxInputFramesRequested(resampler.ChunkSize()));
  343. // Request sizes smaller than ChunkSize should still trigger 1 read.
  344. EXPECT_EQ(SincResampler::kDefaultRequestSize,
  345. resampler.GetMaxInputFramesRequested(resampler.ChunkSize() - 10));
  346. // Request sizes bigger than ChunkSize can trigger multiple reads.
  347. EXPECT_EQ(2 * SincResampler::kDefaultRequestSize,
  348. resampler.GetMaxInputFramesRequested(resampler.ChunkSize() + 10));
  349. // The number of input frames requested should grow proportionally to the
  350. // output frames requested.
  351. EXPECT_EQ(
  352. 5 * SincResampler::kDefaultRequestSize,
  353. resampler.GetMaxInputFramesRequested(4 * resampler.ChunkSize() + 10));
  354. const int kCustomRequestSize = SincResampler::kDefaultRequestSize + 128;
  355. SincResampler custom_size_resampler(kSampleRateRatio, kCustomRequestSize,
  356. SincResampler::ReadCB());
  357. // The input frames requested should be a multiple of the request size.
  358. EXPECT_EQ(2 * kCustomRequestSize,
  359. custom_size_resampler.GetMaxInputFramesRequested(
  360. custom_size_resampler.ChunkSize() + 10));
  361. // Verify we get results with both downsampling and upsampling ratios.
  362. SincResampler inverse_ratio_resampler(1.0 / kSampleRateRatio,
  363. SincResampler::kDefaultRequestSize,
  364. SincResampler::ReadCB());
  365. EXPECT_EQ(2 * SincResampler::kDefaultRequestSize,
  366. inverse_ratio_resampler.GetMaxInputFramesRequested(
  367. inverse_ratio_resampler.ChunkSize() + 10));
  368. }
  369. } // namespace media