test_resampler.cpp (53031B)
1 /* 2 * Copyright © 2016 Mozilla Foundation 3 * 4 * This program is made available under an ISC-style license. See the 5 * accompanying file LICENSE for details. 6 */ 7 #ifndef NOMINMAX 8 #define NOMINMAX 9 #endif // NOMINMAX 10 #include "cubeb/cubeb.h" 11 #include "cubeb_audio_dump.h" 12 #include "cubeb_log.h" 13 #include "cubeb_resampler.h" 14 // #define ENABLE_NORMAL_LOG 15 // #define ENABLE_VERBOSE_LOG 16 #include "common.h" 17 #include "cubeb_resampler_internal.h" 18 #include "gtest/gtest.h" 19 #include <algorithm> 20 #include <cmath> 21 #include <iostream> 22 #include <queue> 23 #include <stdio.h> 24 #include <thread> 25 26 /* Windows cmath USE_MATH_DEFINE thing... */ 27 const float PI = 3.14159265359f; 28 29 /* Testing all sample rates is very long, so if THOROUGH_TESTING is not defined, 30 * only part of the test suite is ran. */ 31 #ifdef THOROUGH_TESTING 32 /* Some standard sample rates we're testing with. */ 33 const uint32_t sample_rates[] = {8000, 16000, 32000, 44100, 34 48000, 88200, 96000, 192000}; 35 /* The maximum number of channels we're resampling. */ 36 const uint32_t max_channels = 2; 37 /* The minimum an maximum number of milliseconds we're resampling for. This is 38 * used to simulate the fact that the audio stream is resampled in chunks, 39 * because audio is delivered using callbacks. */ 40 const uint32_t min_chunks = 10; /* ms */ 41 const uint32_t max_chunks = 30; /* ms */ 42 const uint32_t chunk_increment = 1; 43 44 #else 45 46 const uint32_t sample_rates[] = { 47 8000, 48 44100, 49 48000, 50 }; 51 const uint32_t max_channels = 2; 52 const uint32_t min_chunks = 10; /* ms */ 53 const uint32_t max_chunks = 30; /* ms */ 54 const uint32_t chunk_increment = 10; 55 #endif 56 57 // #define DUMP_ARRAYS 58 #ifdef DUMP_ARRAYS 59 /** 60 * Files produced by dump(...) can be converted to .wave files using: 61 * 62 * sox -c <channel_count> -r <rate> -e float -b 32 file.raw file.wav 63 * 64 * for floating-point audio, or: 65 * 66 * sox -c <channel_count> -r <rate> -e unsigned -b 16 file.raw file.wav 67 * 68 * for 16bit integer audio. 69 */ 70 71 /* Use the correct implementation of fopen, depending on the platform. */ 72 void 73 fopen_portable(FILE ** f, const char * name, const char * mode) 74 { 75 #ifdef WIN32 76 fopen_s(f, name, mode); 77 #else 78 *f = fopen(name, mode); 79 #endif 80 } 81 82 template <typename T> 83 void 84 dump(const char * name, T * frames, size_t count) 85 { 86 FILE * file; 87 fopen_portable(&file, name, "wb"); 88 89 if (!file) { 90 fprintf(stderr, "error opening %s\n", name); 91 return; 92 } 93 94 if (count != fwrite(frames, sizeof(T), count, file)) { 95 fprintf(stderr, "error writing to %s\n", name); 96 } 97 fclose(file); 98 } 99 #else 100 template <typename T> 101 void 102 dump(const char * name, T * frames, size_t count) 103 { 104 } 105 #endif 106 107 // The more the ratio is far from 1, the more we accept a big error. 108 float 109 epsilon_tweak_ratio(float ratio) 110 { 111 return ratio >= 1 ? ratio : 1 / ratio; 112 } 113 114 // Epsilon values for comparing resampled data to expected data. 115 // The bigger the resampling ratio is, the more lax we are about errors. 116 template <typename T> 117 T 118 epsilon(float ratio); 119 120 template <> 121 float 122 epsilon(float ratio) 123 { 124 return 0.08f * epsilon_tweak_ratio(ratio); 125 } 126 127 template <> 128 int16_t 129 epsilon(float ratio) 130 { 131 return static_cast<int16_t>(10 * epsilon_tweak_ratio(ratio)); 132 } 133 134 void 135 test_delay_lines(uint32_t delay_frames, uint32_t channels, uint32_t chunk_ms) 136 { 137 const size_t length_s = 2; 138 const size_t rate = 44100; 139 const size_t length_frames = rate * length_s; 140 delay_line<float> delay(delay_frames, channels, rate); 141 auto_array<float> input; 142 auto_array<float> output; 143 uint32_t chunk_length = channels * chunk_ms * rate / 1000; 144 uint32_t output_offset = 0; 145 uint32_t channel = 0; 146 147 /** Generate diracs every 100 frames, and check they are delayed. */ 148 input.push_silence(length_frames * channels); 149 for (uint32_t i = 0; i < input.length() - 1; i += 100) { 150 input.data()[i + channel] = 0.5; 151 channel = (channel + 1) % channels; 152 } 153 dump("input.raw", input.data(), input.length()); 154 while (input.length()) { 155 uint32_t to_pop = 156 std::min<uint32_t>(input.length(), chunk_length * channels); 157 float * in = delay.input_buffer(to_pop / channels); 158 input.pop(in, to_pop); 159 delay.written(to_pop / channels); 160 output.push_silence(to_pop); 161 delay.output(output.data() + output_offset, to_pop / channels); 162 output_offset += to_pop; 163 } 164 165 // Check the diracs have been shifted by `delay_frames` frames. 166 for (uint32_t i = 0; i < output.length() - delay_frames * channels + 1; 167 i += 100) { 168 ASSERT_EQ(output.data()[i + channel + delay_frames * channels], 0.5); 169 channel = (channel + 1) % channels; 170 } 171 172 dump("output.raw", output.data(), output.length()); 173 } 174 /** 175 * This takes sine waves with a certain `channels` count, `source_rate`, and 176 * resample them, by chunk of `chunk_duration` milliseconds, to `target_rate`. 177 * Then a sample-wise comparison is performed against a sine wave generated at 178 * the correct rate. 179 */ 180 template <typename T> 181 void 182 test_resampler_one_way(uint32_t channels, uint32_t source_rate, 183 uint32_t target_rate, float chunk_duration) 184 { 185 size_t chunk_duration_in_source_frames = 186 static_cast<uint32_t>(ceil(chunk_duration * source_rate / 1000.)); 187 float resampling_ratio = static_cast<float>(source_rate) / target_rate; 188 cubeb_resampler_speex_one_way<T> resampler(channels, source_rate, target_rate, 189 3); 190 auto_array<T> source(channels * source_rate * 10); 191 auto_array<T> destination(channels * target_rate * 10); 192 auto_array<T> expected(channels * target_rate * 10); 193 uint32_t phase_index = 0; 194 uint32_t offset = 0; 195 const uint32_t buf_len = 2; /* seconds */ 196 197 // generate a sine wave in each channel, at the source sample rate 198 source.push_silence(channels * source_rate * buf_len); 199 while (offset != source.length()) { 200 float p = phase_index++ / static_cast<float>(source_rate); 201 for (uint32_t j = 0; j < channels; j++) { 202 source.data()[offset++] = 0.5 * sin(440. * 2 * PI * p); 203 } 204 } 205 206 dump("input.raw", source.data(), source.length()); 207 208 expected.push_silence(channels * target_rate * buf_len); 209 // generate a sine wave in each channel, at the target sample rate. 210 // Insert silent samples at the beginning to account for the resampler 211 // latency. 212 offset = resampler.latency() * channels; 213 for (uint32_t i = 0; i < offset; i++) { 214 expected.data()[i] = 0.0f; 215 } 216 phase_index = 0; 217 while (offset != expected.length()) { 218 float p = phase_index++ / static_cast<float>(target_rate); 219 for (uint32_t j = 0; j < channels; j++) { 220 expected.data()[offset++] = 0.5 * sin(440. * 2 * PI * p); 221 } 222 } 223 224 dump("expected.raw", expected.data(), expected.length()); 225 226 // resample by chunk 227 uint32_t write_offset = 0; 228 destination.push_silence(channels * target_rate * buf_len); 229 while (write_offset < destination.length()) { 230 size_t output_frames = static_cast<uint32_t>( 231 floor(chunk_duration_in_source_frames / resampling_ratio)); 232 uint32_t input_frames = resampler.input_needed_for_output(output_frames); 233 resampler.input(source.data(), input_frames); 234 source.pop(nullptr, input_frames * channels); 235 resampler.output( 236 destination.data() + write_offset, 237 std::min(output_frames, 238 (destination.length() - write_offset) / channels)); 239 write_offset += output_frames * channels; 240 } 241 242 dump("output.raw", destination.data(), expected.length()); 243 244 // compare, taking the latency into account 245 bool fuzzy_equal = true; 246 for (uint32_t i = resampler.latency() + 1; i < expected.length(); i++) { 247 float diff = fabs(expected.data()[i] - destination.data()[i]); 248 if (diff > epsilon<T>(resampling_ratio)) { 249 fprintf(stderr, "divergence at %d: %f %f (delta %f)\n", i, 250 expected.data()[i], destination.data()[i], diff); 251 fuzzy_equal = false; 252 } 253 } 254 ASSERT_TRUE(fuzzy_equal); 255 } 256 257 template <typename T> 258 cubeb_sample_format 259 cubeb_format(); 260 261 template <> 262 cubeb_sample_format 263 cubeb_format<float>() 264 { 265 return CUBEB_SAMPLE_FLOAT32NE; 266 } 267 268 template <> 269 cubeb_sample_format 270 cubeb_format<short>() 271 { 272 return CUBEB_SAMPLE_S16NE; 273 } 274 275 struct osc_state { 276 osc_state() 277 : input_phase_index(0), output_phase_index(0), output_offset(0), 278 input_channels(0), output_channels(0) 279 { 280 } 281 uint32_t input_phase_index; 282 uint32_t max_output_phase_index; 283 uint32_t output_phase_index; 284 uint32_t output_offset; 285 uint32_t input_channels; 286 uint32_t output_channels; 287 uint32_t output_rate; 288 uint32_t target_rate; 289 auto_array<float> input; 290 auto_array<float> output; 291 }; 292 293 uint32_t 294 fill_with_sine(float * buf, uint32_t rate, uint32_t channels, uint32_t frames, 295 uint32_t initial_phase) 296 { 297 uint32_t offset = 0; 298 for (uint32_t i = 0; i < frames; i++) { 299 float p = initial_phase++ / static_cast<float>(rate); 300 for (uint32_t j = 0; j < channels; j++) { 301 buf[offset++] = 0.5 * sin(440. * 2 * PI * p); 302 } 303 } 304 return initial_phase; 305 } 306 307 long 308 data_cb_resampler(cubeb_stream * /*stm*/, void * user_ptr, 309 const void * input_buffer, void * output_buffer, 310 long frame_count) 311 { 312 osc_state * state = reinterpret_cast<osc_state *>(user_ptr); 313 const float * in = reinterpret_cast<const float *>(input_buffer); 314 float * out = reinterpret_cast<float *>(output_buffer); 315 316 state->input.push(in, frame_count * state->input_channels); 317 318 /* Check how much output frames we need to write */ 319 uint32_t remaining = 320 state->max_output_phase_index - state->output_phase_index; 321 uint32_t to_write = std::min<uint32_t>(remaining, frame_count); 322 state->output_phase_index = 323 fill_with_sine(out, state->target_rate, state->output_channels, to_write, 324 state->output_phase_index); 325 326 return to_write; 327 } 328 329 template <typename T> 330 bool 331 array_fuzzy_equal(const auto_array<T> & lhs, const auto_array<T> & rhs, T epsi) 332 { 333 uint32_t len = std::min(lhs.length(), rhs.length()); 334 335 for (uint32_t i = 0; i < len; i++) { 336 if (fabs(lhs.at(i) - rhs.at(i)) > epsi) { 337 std::cout << "not fuzzy equal at index: " << i << " lhs: " << lhs.at(i) 338 << " rhs: " << rhs.at(i) 339 << " delta: " << fabs(lhs.at(i) - rhs.at(i)) 340 << " epsilon: " << epsi << std::endl; 341 return false; 342 } 343 } 344 return true; 345 } 346 347 template <typename T> 348 void 349 test_resampler_duplex(uint32_t input_channels, uint32_t output_channels, 350 uint32_t input_rate, uint32_t output_rate, 351 uint32_t target_rate, float chunk_duration) 352 { 353 cubeb_stream_params input_params; 354 cubeb_stream_params output_params; 355 osc_state state; 356 357 input_params.format = output_params.format = cubeb_format<T>(); 358 state.input_channels = input_params.channels = input_channels; 359 state.output_channels = output_params.channels = output_channels; 360 input_params.rate = input_rate; 361 state.output_rate = output_params.rate = output_rate; 362 state.target_rate = target_rate; 363 input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE; 364 long got; 365 366 cubeb_resampler * resampler = cubeb_resampler_create( 367 (cubeb_stream *)nullptr, &input_params, &output_params, target_rate, 368 data_cb_resampler, (void *)&state, CUBEB_RESAMPLER_QUALITY_VOIP, 369 CUBEB_RESAMPLER_RECLOCK_NONE); 370 371 long latency = cubeb_resampler_latency(resampler); 372 373 const uint32_t duration_s = 2; 374 int32_t duration_frames = duration_s * target_rate; 375 uint32_t input_array_frame_count = 376 ceil(chunk_duration * input_rate / 1000) + 377 ceilf(static_cast<float>(input_rate) / target_rate) * 2; 378 uint32_t output_array_frame_count = chunk_duration * output_rate / 1000; 379 auto_array<float> input_buffer(input_channels * input_array_frame_count); 380 auto_array<float> output_buffer(output_channels * output_array_frame_count); 381 auto_array<float> expected_resampled_input(input_channels * duration_frames); 382 auto_array<float> expected_resampled_output(output_channels * output_rate * 383 duration_s); 384 385 state.max_output_phase_index = duration_s * target_rate; 386 387 expected_resampled_input.push_silence(input_channels * duration_frames); 388 expected_resampled_output.push_silence(output_channels * output_rate * 389 duration_s); 390 391 /* expected output is a 440Hz sine wave at 16kHz */ 392 fill_with_sine(expected_resampled_input.data() + latency, target_rate, 393 input_channels, duration_frames - latency, 0); 394 /* expected output is a 440Hz sine wave at 32kHz */ 395 fill_with_sine(expected_resampled_output.data() + latency, output_rate, 396 output_channels, output_rate * duration_s - latency, 0); 397 398 while (state.output_phase_index != state.max_output_phase_index) { 399 uint32_t leftover_samples = input_buffer.length() * input_channels; 400 input_buffer.reserve(input_array_frame_count); 401 state.input_phase_index = fill_with_sine( 402 input_buffer.data() + leftover_samples, input_rate, input_channels, 403 input_array_frame_count - leftover_samples, state.input_phase_index); 404 long input_consumed = input_array_frame_count; 405 input_buffer.set_length(input_array_frame_count); 406 407 got = cubeb_resampler_fill(resampler, input_buffer.data(), &input_consumed, 408 output_buffer.data(), output_array_frame_count); 409 410 /* handle leftover input */ 411 if (input_array_frame_count != static_cast<uint32_t>(input_consumed)) { 412 input_buffer.pop(nullptr, input_consumed * input_channels); 413 } else { 414 input_buffer.clear(); 415 } 416 417 state.output.push(output_buffer.data(), got * state.output_channels); 418 } 419 420 dump("input_expected.raw", expected_resampled_input.data(), 421 expected_resampled_input.length()); 422 dump("output_expected.raw", expected_resampled_output.data(), 423 expected_resampled_output.length()); 424 dump("input.raw", state.input.data(), state.input.length()); 425 dump("output.raw", state.output.data(), state.output.length()); 426 427 // This is disabled because the latency estimation in the resampler code is 428 // slightly off so we can generate expected vectors. 429 // See https://github.com/kinetiknz/cubeb/issues/93 430 // ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, 431 // epsilon<T>(input_rate/target_rate))); 432 // ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, 433 // epsilon<T>(output_rate/target_rate))); 434 435 cubeb_resampler_destroy(resampler); 436 } 437 438 #define array_size(x) (sizeof(x) / sizeof(x[0])) 439 440 TEST(cubeb, resampler_one_way) 441 { 442 /* Test one way resamplers */ 443 for (uint32_t channels = 1; channels <= max_channels; channels++) { 444 for (uint32_t source_rate = 0; source_rate < array_size(sample_rates); 445 source_rate++) { 446 for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); 447 dest_rate++) { 448 for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; 449 chunk_duration += chunk_increment) { 450 fprintf(stderr, 451 "one_way: channels: %d, source_rate: %d, dest_rate: %d, " 452 "chunk_duration: %d\n", 453 channels, sample_rates[source_rate], sample_rates[dest_rate], 454 chunk_duration); 455 test_resampler_one_way<float>(channels, sample_rates[source_rate], 456 sample_rates[dest_rate], 457 chunk_duration); 458 } 459 } 460 } 461 } 462 } 463 464 TEST(cubeb, DISABLED_resampler_duplex) 465 { 466 for (uint32_t input_channels = 1; input_channels <= max_channels; 467 input_channels++) { 468 for (uint32_t output_channels = 1; output_channels <= max_channels; 469 output_channels++) { 470 for (uint32_t source_rate_input = 0; 471 source_rate_input < array_size(sample_rates); source_rate_input++) { 472 for (uint32_t source_rate_output = 0; 473 source_rate_output < array_size(sample_rates); 474 source_rate_output++) { 475 for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); 476 dest_rate++) { 477 for (uint32_t chunk_duration = min_chunks; 478 chunk_duration < max_chunks; 479 chunk_duration += chunk_increment) { 480 fprintf(stderr, 481 "input channels:%d output_channels:%d input_rate:%d " 482 "output_rate:%d target_rate:%d chunk_ms:%d\n", 483 input_channels, output_channels, 484 sample_rates[source_rate_input], 485 sample_rates[source_rate_output], sample_rates[dest_rate], 486 chunk_duration); 487 test_resampler_duplex<float>(input_channels, output_channels, 488 sample_rates[source_rate_input], 489 sample_rates[source_rate_output], 490 sample_rates[dest_rate], 491 chunk_duration); 492 } 493 } 494 } 495 } 496 } 497 } 498 } 499 500 TEST(cubeb, resampler_delay_line) 501 { 502 for (uint32_t channel = 1; channel <= 2; channel++) { 503 for (uint32_t delay_frames = 4; delay_frames <= 40; 504 delay_frames += chunk_increment) { 505 for (uint32_t chunk_size = 10; chunk_size <= 30; chunk_size++) { 506 fprintf(stderr, "channel: %d, delay_frames: %d, chunk_size: %d\n", 507 channel, delay_frames, chunk_size); 508 test_delay_lines(delay_frames, channel, chunk_size); 509 } 510 } 511 } 512 } 513 514 long 515 test_output_only_noop_data_cb(cubeb_stream * /*stm*/, void * /*user_ptr*/, 516 const void * input_buffer, void * output_buffer, 517 long frame_count) 518 { 519 EXPECT_TRUE(output_buffer); 520 EXPECT_TRUE(!input_buffer); 521 return frame_count; 522 } 523 524 TEST(cubeb, resampler_output_only_noop) 525 { 526 cubeb_stream_params output_params; 527 int target_rate; 528 529 output_params.rate = 44100; 530 output_params.channels = 1; 531 output_params.format = CUBEB_SAMPLE_FLOAT32NE; 532 target_rate = output_params.rate; 533 534 cubeb_resampler * resampler = cubeb_resampler_create( 535 (cubeb_stream *)nullptr, nullptr, &output_params, target_rate, 536 test_output_only_noop_data_cb, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP, 537 CUBEB_RESAMPLER_RECLOCK_NONE); 538 const long out_frames = 128; 539 float out_buffer[out_frames]; 540 long got; 541 542 got = 543 cubeb_resampler_fill(resampler, nullptr, nullptr, out_buffer, out_frames); 544 545 ASSERT_EQ(got, out_frames); 546 547 cubeb_resampler_destroy(resampler); 548 } 549 550 long 551 test_drain_data_cb(cubeb_stream * /*stm*/, void * user_ptr, 552 const void * input_buffer, void * output_buffer, 553 long frame_count) 554 { 555 EXPECT_TRUE(output_buffer); 556 EXPECT_TRUE(!input_buffer); 557 auto cb_count = static_cast<int *>(user_ptr); 558 (*cb_count)++; 559 return frame_count - 1; 560 } 561 562 TEST(cubeb, resampler_drain) 563 { 564 cubeb_stream_params output_params; 565 int target_rate; 566 567 output_params.rate = 44100; 568 output_params.channels = 1; 569 output_params.format = CUBEB_SAMPLE_FLOAT32NE; 570 target_rate = 48000; 571 int cb_count = 0; 572 573 cubeb_resampler * resampler = cubeb_resampler_create( 574 (cubeb_stream *)nullptr, nullptr, &output_params, target_rate, 575 test_drain_data_cb, &cb_count, CUBEB_RESAMPLER_QUALITY_VOIP, 576 CUBEB_RESAMPLER_RECLOCK_NONE); 577 578 const long out_frames = 128; 579 float out_buffer[out_frames]; 580 long got; 581 582 do { 583 got = cubeb_resampler_fill(resampler, nullptr, nullptr, out_buffer, 584 out_frames); 585 } while (got == out_frames); 586 587 /* The callback should be called once but not again after returning < 588 * frame_count. */ 589 ASSERT_EQ(cb_count, 1); 590 591 cubeb_resampler_destroy(resampler); 592 } 593 594 // gtest does not support using ASSERT_EQ and friend in a function that returns 595 // a value. 596 void 597 check_output(const void * input_buffer, void * output_buffer, long frame_count) 598 { 599 ASSERT_EQ(input_buffer, nullptr); 600 ASSERT_EQ(frame_count, 256); 601 ASSERT_TRUE(!!output_buffer); 602 } 603 604 long 605 cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/, 606 const void * input_buffer, void * output_buffer, 607 long frame_count) 608 { 609 check_output(input_buffer, output_buffer, frame_count); 610 return frame_count; 611 } 612 613 TEST(cubeb, resampler_passthrough_output_only) 614 { 615 // Test that the passthrough resampler works when there is only an output 616 // stream. 617 cubeb_stream_params output_params; 618 619 const size_t output_channels = 2; 620 output_params.channels = output_channels; 621 output_params.rate = 44100; 622 output_params.format = CUBEB_SAMPLE_FLOAT32NE; 623 int target_rate = output_params.rate; 624 625 cubeb_resampler * resampler = cubeb_resampler_create( 626 (cubeb_stream *)nullptr, nullptr, &output_params, target_rate, 627 cb_passthrough_resampler_output, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP, 628 CUBEB_RESAMPLER_RECLOCK_NONE); 629 630 float output_buffer[output_channels * 256]; 631 632 long got; 633 for (uint32_t i = 0; i < 30; i++) { 634 got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256); 635 ASSERT_EQ(got, 256); 636 } 637 638 cubeb_resampler_destroy(resampler); 639 } 640 641 // gtest does not support using ASSERT_EQ and friend in a function that returns 642 // a value. 643 void 644 check_input(const void * input_buffer, void * output_buffer, long frame_count) 645 { 646 ASSERT_EQ(output_buffer, nullptr); 647 ASSERT_EQ(frame_count, 256); 648 ASSERT_TRUE(!!input_buffer); 649 } 650 651 long 652 cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/, 653 const void * input_buffer, void * output_buffer, 654 long frame_count) 655 { 656 check_input(input_buffer, output_buffer, frame_count); 657 return frame_count; 658 } 659 660 TEST(cubeb, resampler_passthrough_input_only) 661 { 662 // Test that the passthrough resampler works when there is only an output 663 // stream. 664 cubeb_stream_params input_params; 665 666 const size_t input_channels = 2; 667 input_params.channels = input_channels; 668 input_params.rate = 44100; 669 input_params.format = CUBEB_SAMPLE_FLOAT32NE; 670 int target_rate = input_params.rate; 671 672 cubeb_resampler * resampler = cubeb_resampler_create( 673 (cubeb_stream *)nullptr, &input_params, nullptr, target_rate, 674 cb_passthrough_resampler_input, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP, 675 CUBEB_RESAMPLER_RECLOCK_NONE); 676 677 float input_buffer[input_channels * 256]; 678 679 long got; 680 for (uint32_t i = 0; i < 30; i++) { 681 long int frames = 256; 682 got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0); 683 ASSERT_EQ(got, 256); 684 } 685 686 cubeb_resampler_destroy(resampler); 687 } 688 689 template <typename T> 690 long 691 seq(T * array, int stride, long start, long count) 692 { 693 uint32_t output_idx = 0; 694 for (int i = 0; i < count; i++) { 695 for (int j = 0; j < stride; j++) { 696 array[output_idx + j] = static_cast<T>(start + i); 697 } 698 output_idx += stride; 699 } 700 return start + count; 701 } 702 703 template <typename T> 704 void 705 is_seq(T * array, int stride, long count, long expected_start) 706 { 707 uint32_t output_index = 0; 708 for (long i = 0; i < count; i++) { 709 for (int j = 0; j < stride; j++) { 710 ASSERT_EQ(array[output_index + j], expected_start + i); 711 } 712 output_index += stride; 713 } 714 } 715 716 template <typename T> 717 void 718 is_not_seq(T * array, int stride, long count, long expected_start) 719 { 720 uint32_t output_index = 0; 721 for (long i = 0; i < count; i++) { 722 for (int j = 0; j < stride; j++) { 723 ASSERT_NE(array[output_index + j], expected_start + i); 724 } 725 output_index += stride; 726 } 727 } 728 729 struct closure { 730 int input_channel_count; 731 }; 732 733 // gtest does not support using ASSERT_EQ and friend in a function that returns 734 // a value. 735 template <typename T> 736 void 737 check_duplex(const T * input_buffer, T * output_buffer, long frame_count, 738 int input_channel_count) 739 { 740 ASSERT_EQ(frame_count, 256); 741 // Silence scan-build warning. 742 ASSERT_TRUE(!!output_buffer); 743 assert(output_buffer); 744 ASSERT_TRUE(!!input_buffer); 745 assert(input_buffer); 746 747 int output_index = 0; 748 int input_index = 0; 749 for (int i = 0; i < frame_count; i++) { 750 // output is two channels, input one or two channels. 751 if (input_channel_count == 1) { 752 output_buffer[output_index] = output_buffer[output_index + 1] = 753 input_buffer[i]; 754 } else if (input_channel_count == 2) { 755 output_buffer[output_index] = input_buffer[input_index]; 756 output_buffer[output_index + 1] = input_buffer[input_index + 1]; 757 } 758 output_index += 2; 759 input_index += input_channel_count; 760 } 761 } 762 763 long 764 cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * user_ptr, 765 const void * input_buffer, void * output_buffer, 766 long frame_count) 767 { 768 closure * c = reinterpret_cast<closure *>(user_ptr); 769 check_duplex<float>(static_cast<const float *>(input_buffer), 770 static_cast<float *>(output_buffer), frame_count, 771 c->input_channel_count); 772 return frame_count; 773 } 774 775 TEST(cubeb, resampler_passthrough_duplex_callback_reordering) 776 { 777 // Test that when pre-buffering on resampler creation, we can survive an input 778 // callback being delayed. 779 780 cubeb_stream_params input_params; 781 cubeb_stream_params output_params; 782 783 const int input_channels = 1; 784 const int output_channels = 2; 785 786 input_params.channels = input_channels; 787 input_params.rate = 44100; 788 input_params.format = CUBEB_SAMPLE_FLOAT32NE; 789 790 output_params.channels = output_channels; 791 output_params.rate = input_params.rate; 792 output_params.format = CUBEB_SAMPLE_FLOAT32NE; 793 794 int target_rate = input_params.rate; 795 796 closure c; 797 c.input_channel_count = input_channels; 798 799 cubeb_resampler * resampler = cubeb_resampler_create( 800 (cubeb_stream *)nullptr, &input_params, &output_params, target_rate, 801 cb_passthrough_resampler_duplex, &c, CUBEB_RESAMPLER_QUALITY_VOIP, 802 CUBEB_RESAMPLER_RECLOCK_NONE); 803 804 const long BUF_BASE_SIZE = 256; 805 float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2]; 806 float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2]; 807 float input_buffer_normal[input_channels * BUF_BASE_SIZE]; 808 float output_buffer[output_channels * BUF_BASE_SIZE]; 809 810 long seq_idx = 0; 811 long output_seq_idx = 0; 812 813 long prebuffer_frames = 814 ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels; 815 seq_idx = 816 seq(input_buffer_prebuffer, input_channels, seq_idx, prebuffer_frames); 817 818 long got = 819 cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames, 820 output_buffer, BUF_BASE_SIZE); 821 822 output_seq_idx += BUF_BASE_SIZE; 823 824 // prebuffer_frames will hold the frames used by the resampler. 825 ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE); 826 ASSERT_EQ(got, BUF_BASE_SIZE); 827 828 for (uint32_t i = 0; i < 300; i++) { 829 long int frames = BUF_BASE_SIZE; 830 // Simulate that sometimes, we don't have the input callback on time 831 if (i != 0 && (i % 100) == 0) { 832 long zero = 0; 833 got = 834 cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */, 835 &zero, output_buffer, BUF_BASE_SIZE); 836 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx); 837 output_seq_idx += BUF_BASE_SIZE; 838 } else if (i != 0 && (i % 100) == 1) { 839 // if this is the case, the on the next iteration, we'll have twice the 840 // amount of input frames 841 seq_idx = 842 seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2); 843 frames = 2 * BUF_BASE_SIZE; 844 got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames, 845 output_buffer, BUF_BASE_SIZE); 846 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx); 847 output_seq_idx += BUF_BASE_SIZE; 848 } else { 849 // normal case 850 seq_idx = 851 seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE); 852 long normal_input_frame_count = 256; 853 got = cubeb_resampler_fill(resampler, input_buffer_normal, 854 &normal_input_frame_count, output_buffer, 855 BUF_BASE_SIZE); 856 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx); 857 output_seq_idx += BUF_BASE_SIZE; 858 } 859 ASSERT_EQ(got, BUF_BASE_SIZE); 860 } 861 862 cubeb_resampler_destroy(resampler); 863 } 864 865 // Artificially simulate output thread underruns, 866 // by building up artificial delay in the input. 867 // Check that the frame drop logic kicks in. 868 TEST(cubeb, resampler_drift_drop_data) 869 { 870 for (uint32_t input_channels = 1; input_channels < 3; input_channels++) { 871 cubeb_stream_params input_params; 872 cubeb_stream_params output_params; 873 874 const int output_channels = 2; 875 const int sample_rate = 44100; 876 877 input_params.channels = input_channels; 878 input_params.rate = sample_rate; 879 input_params.format = CUBEB_SAMPLE_FLOAT32NE; 880 881 output_params.channels = output_channels; 882 output_params.rate = sample_rate; 883 output_params.format = CUBEB_SAMPLE_FLOAT32NE; 884 885 int target_rate = input_params.rate; 886 887 closure c; 888 c.input_channel_count = input_channels; 889 890 cubeb_resampler * resampler = cubeb_resampler_create( 891 (cubeb_stream *)nullptr, &input_params, &output_params, target_rate, 892 cb_passthrough_resampler_duplex, &c, CUBEB_RESAMPLER_QUALITY_VOIP, 893 CUBEB_RESAMPLER_RECLOCK_NONE); 894 895 const long BUF_BASE_SIZE = 256; 896 897 // The factor by which the deadline is missed. This is intentionally 898 // kind of large to trigger the frame drop quickly. In real life, multiple 899 // smaller under-runs would accumulate. 900 const long UNDERRUN_FACTOR = 10; 901 // Number buffer used for pre-buffering, that some backends do. 902 const long PREBUFFER_FACTOR = 2; 903 904 std::vector<float> input_buffer_prebuffer(input_channels * BUF_BASE_SIZE * 905 PREBUFFER_FACTOR); 906 std::vector<float> input_buffer_glitch(input_channels * BUF_BASE_SIZE * 907 UNDERRUN_FACTOR); 908 std::vector<float> input_buffer_normal(input_channels * BUF_BASE_SIZE); 909 std::vector<float> output_buffer(output_channels * BUF_BASE_SIZE); 910 911 long seq_idx = 0; 912 long output_seq_idx = 0; 913 914 long prebuffer_frames = 915 input_buffer_prebuffer.size() / input_params.channels; 916 seq_idx = seq(input_buffer_prebuffer.data(), input_channels, seq_idx, 917 prebuffer_frames); 918 919 long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer.data(), 920 &prebuffer_frames, output_buffer.data(), 921 BUF_BASE_SIZE); 922 923 output_seq_idx += BUF_BASE_SIZE; 924 925 // prebuffer_frames will hold the frames used by the resampler. 926 ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE); 927 ASSERT_EQ(got, BUF_BASE_SIZE); 928 929 for (uint32_t i = 0; i < 300; i++) { 930 long int frames = BUF_BASE_SIZE; 931 if (i != 0 && (i % 100) == 1) { 932 // Once in a while, the output thread misses its deadline. 933 // The input thread still produces data, so it ends up accumulating. 934 // Simulate this by providing a much bigger input buffer. Check that the 935 // sequence is now unaligned, meaning we've dropped data to keep 936 // everything in sync. 937 seq_idx = seq(input_buffer_glitch.data(), input_channels, seq_idx, 938 BUF_BASE_SIZE * UNDERRUN_FACTOR); 939 frames = BUF_BASE_SIZE * UNDERRUN_FACTOR; 940 got = 941 cubeb_resampler_fill(resampler, input_buffer_glitch.data(), &frames, 942 output_buffer.data(), BUF_BASE_SIZE); 943 is_seq(output_buffer.data(), 2, BUF_BASE_SIZE, output_seq_idx); 944 output_seq_idx += BUF_BASE_SIZE; 945 } else if (i != 0 && (i % 100) == 2) { 946 // On the next iteration, the sequence should be broken 947 seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, 948 BUF_BASE_SIZE); 949 long normal_input_frame_count = 256; 950 got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), 951 &normal_input_frame_count, 952 output_buffer.data(), BUF_BASE_SIZE); 953 is_not_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, 954 output_seq_idx); 955 // Reclock so that we can use is_seq again. 956 output_seq_idx = output_buffer[BUF_BASE_SIZE * output_channels - 1] + 1; 957 } else { 958 // normal case 959 seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, 960 BUF_BASE_SIZE); 961 long normal_input_frame_count = 256; 962 got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), 963 &normal_input_frame_count, 964 output_buffer.data(), BUF_BASE_SIZE); 965 is_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, 966 output_seq_idx); 967 output_seq_idx += BUF_BASE_SIZE; 968 } 969 ASSERT_EQ(got, BUF_BASE_SIZE); 970 } 971 972 cubeb_resampler_destroy(resampler); 973 } 974 } 975 976 static long 977 passthrough_resampler_fill_eq_input(cubeb_stream * stream, void * user_ptr, 978 void const * input_buffer, 979 void * output_buffer, long nframes) 980 { 981 // gtest does not support using ASSERT_EQ and friends in a 982 // function that returns a value. 983 [nframes, input_buffer]() { 984 ASSERT_EQ(nframes, 32); 985 const float * input = static_cast<const float *>(input_buffer); 986 for (int i = 0; i < 64; ++i) { 987 ASSERT_FLOAT_EQ(input[i], 0.01 * i); 988 } 989 }(); 990 return nframes; 991 } 992 993 TEST(cubeb, passthrough_resampler_fill_eq_input) 994 { 995 uint32_t channels = 2; 996 uint32_t sample_rate = 44100; 997 passthrough_resampler<float> resampler = 998 passthrough_resampler<float>(nullptr, passthrough_resampler_fill_eq_input, 999 nullptr, channels, sample_rate); 1000 1001 long input_frame_count = 32; 1002 long output_frame_count = 32; 1003 float input[64] = {}; 1004 float output[64] = {}; 1005 for (uint32_t i = 0; i < input_frame_count * channels; ++i) { 1006 input[i] = 0.01 * i; 1007 } 1008 long got = 1009 resampler.fill(input, &input_frame_count, output, output_frame_count); 1010 ASSERT_EQ(got, output_frame_count); 1011 // Input frames used must be equal to output frames. 1012 ASSERT_EQ(input_frame_count, output_frame_count); 1013 } 1014 1015 static long 1016 passthrough_resampler_fill_short_input(cubeb_stream * stream, void * user_ptr, 1017 void const * input_buffer, 1018 void * output_buffer, long nframes) 1019 { 1020 // gtest does not support using ASSERT_EQ and friends in a 1021 // function that returns a value. 1022 [nframes, input_buffer]() { 1023 ASSERT_EQ(nframes, 32); 1024 const float * input = static_cast<const float *>(input_buffer); 1025 // First part contains the input 1026 for (int i = 0; i < 32; ++i) { 1027 ASSERT_FLOAT_EQ(input[i], 0.01 * i); 1028 } 1029 // missing part contains silence 1030 for (int i = 32; i < 64; ++i) { 1031 ASSERT_FLOAT_EQ(input[i], 0.0); 1032 } 1033 }(); 1034 return nframes; 1035 } 1036 1037 TEST(cubeb, passthrough_resampler_fill_short_input) 1038 { 1039 uint32_t channels = 2; 1040 uint32_t sample_rate = 44100; 1041 passthrough_resampler<float> resampler = passthrough_resampler<float>( 1042 nullptr, passthrough_resampler_fill_short_input, nullptr, channels, 1043 sample_rate); 1044 1045 long input_frame_count = 16; 1046 long output_frame_count = 32; 1047 float input[64] = {}; 1048 float output[64] = {}; 1049 for (uint32_t i = 0; i < input_frame_count * channels; ++i) { 1050 input[i] = 0.01 * i; 1051 } 1052 long got = 1053 resampler.fill(input, &input_frame_count, output, output_frame_count); 1054 ASSERT_EQ(got, output_frame_count); 1055 // Input frames used are less than the output frames due to glitch. 1056 ASSERT_EQ(input_frame_count, output_frame_count - 16); 1057 } 1058 1059 static long 1060 passthrough_resampler_fill_input_left(cubeb_stream * stream, void * user_ptr, 1061 void const * input_buffer, 1062 void * output_buffer, long nframes) 1063 { 1064 // gtest does not support using ASSERT_EQ and friends in a 1065 // function that returns a value. 1066 int iteration = *static_cast<int *>(user_ptr); 1067 if (iteration == 1) { 1068 [nframes, input_buffer]() { 1069 ASSERT_EQ(nframes, 32); 1070 const float * input = static_cast<const float *>(input_buffer); 1071 for (int i = 0; i < 64; ++i) { 1072 ASSERT_FLOAT_EQ(input[i], 0.01 * i); 1073 } 1074 }(); 1075 } else if (iteration == 2) { 1076 [nframes, input_buffer]() { 1077 ASSERT_EQ(nframes, 32); 1078 const float * input = static_cast<const float *>(input_buffer); 1079 for (int i = 0; i < 32; ++i) { 1080 // First part contains the reamaining input samples from previous 1081 // iteration (since they were more). 1082 ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 64)); 1083 // next part contains the new buffer 1084 ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i); 1085 } 1086 }(); 1087 } else if (iteration == 3) { 1088 [nframes, input_buffer]() { 1089 ASSERT_EQ(nframes, 32); 1090 const float * input = static_cast<const float *>(input_buffer); 1091 for (int i = 0; i < 32; ++i) { 1092 // First part (16 frames) contains the reamaining input samples 1093 // from previous iteration (since they were more). 1094 ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 32)); 1095 } 1096 for (int i = 0; i < 16; ++i) { 1097 // next part (8 frames) contains the new input buffer. 1098 ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i); 1099 // last part (8 frames) contains silence. 1100 ASSERT_FLOAT_EQ(input[i + 32 + 16], 0.0); 1101 } 1102 }(); 1103 } 1104 return nframes; 1105 } 1106 1107 TEST(cubeb, passthrough_resampler_fill_input_left) 1108 { 1109 const uint32_t channels = 2; 1110 const uint32_t sample_rate = 44100; 1111 int iteration = 0; 1112 passthrough_resampler<float> resampler = passthrough_resampler<float>( 1113 nullptr, passthrough_resampler_fill_input_left, &iteration, channels, 1114 sample_rate); 1115 1116 long input_frame_count = 48; // 32 + 16 1117 const long output_frame_count = 32; 1118 float input[96] = {}; 1119 float output[64] = {}; 1120 for (uint32_t i = 0; i < input_frame_count * channels; ++i) { 1121 input[i] = 0.01 * i; 1122 } 1123 1124 // 1st iteration, add the extra input. 1125 iteration = 1; 1126 long got = 1127 resampler.fill(input, &input_frame_count, output, output_frame_count); 1128 ASSERT_EQ(got, output_frame_count); 1129 // Input frames used must be equal to output frames. 1130 ASSERT_EQ(input_frame_count, output_frame_count); 1131 1132 // 2st iteration, use the extra input from previous iteration, 1133 // 16 frames are remaining in the input buffer. 1134 input_frame_count = 32; // we need 16 input frames but we get more; 1135 iteration = 2; 1136 got = resampler.fill(input, &input_frame_count, output, output_frame_count); 1137 ASSERT_EQ(got, output_frame_count); 1138 // Input frames used must be equal to output frames. 1139 ASSERT_EQ(input_frame_count, output_frame_count); 1140 1141 // 3rd iteration, use the extra input from previous iteration. 1142 // 16 frames are remaining in the input buffer. 1143 input_frame_count = 16 - 8; // We need 16 more input frames but we only get 8. 1144 iteration = 3; 1145 got = resampler.fill(input, &input_frame_count, output, output_frame_count); 1146 ASSERT_EQ(got, output_frame_count); 1147 // Input frames used are less than the output frames due to glitch. 1148 ASSERT_EQ(input_frame_count, output_frame_count - 8); 1149 } 1150 1151 TEST(cubeb, individual_methods) 1152 { 1153 const uint32_t channels = 2; 1154 const uint32_t sample_rate = 44100; 1155 const uint32_t frames = 256; 1156 1157 delay_line<float> dl(10, channels, sample_rate); 1158 uint32_t frames_needed1 = dl.input_needed_for_output(0); 1159 ASSERT_EQ(frames_needed1, 0u); 1160 1161 cubeb_resampler_speex_one_way<float> one_way( 1162 channels, sample_rate, sample_rate, CUBEB_RESAMPLER_QUALITY_DEFAULT); 1163 float buffer[channels * frames] = {0.0}; 1164 // Add all frames in the resampler's internal buffer. 1165 one_way.input(buffer, frames); 1166 // Ask for less than the existing frames, this would create a uint overlflow 1167 // without the fix. 1168 uint32_t frames_needed2 = one_way.input_needed_for_output(0); 1169 ASSERT_EQ(frames_needed2, 0u); 1170 } 1171 1172 struct sine_wave_state { 1173 float frequency; 1174 int sample_rate; 1175 size_t count = 0; 1176 sine_wave_state(float freq, int rate) : frequency(freq), sample_rate(rate) {} 1177 }; 1178 1179 long 1180 data_cb(cubeb_stream * stream, void * user_ptr, void const * input_buffer, 1181 void * output_buffer, long nframes) 1182 { 1183 sine_wave_state * state = static_cast<sine_wave_state *>(user_ptr); 1184 float * out = static_cast<float *>(output_buffer); 1185 double phase_increment = 2.0f * M_PI * state->frequency / state->sample_rate; 1186 1187 for (int i = 0; i < nframes; i++) { 1188 float sample = sin(phase_increment * state->count); 1189 state->count++; 1190 out[i] = sample * 0.8; 1191 } 1192 return nframes; 1193 } 1194 1195 // This implements 4.6.2 from "Standard for Digitizing Waveform Recorders" 1196 // (in particular Annex A), then returns the estimated amplitude, phase, and the 1197 // sum of squared error relative to a sine wave sampled at `sample_rate` and of 1198 // frequency `frequency`. This is also described in "Numerical methods for 1199 // engineers" chapter 19.1, and explained at 1200 // https://www.youtube.com/watch?v=afQszl_OwKo and videos of the same series. 1201 // In practice here we're sending a perfect 1khz sine wave into a good 1202 // resampler, and despite the resampling ratio being quite extreme sometimes, 1203 // we're expecting a very good fit. 1204 float 1205 fit_sine(const std::vector<float> & signal, float sample_rate, float frequency, 1206 float & out_amplitude, float & out_phase) 1207 { 1208 // The formulation below is exact for samples spanning an integer number of 1209 // periods. It can be important for `signal` to be trimmed to an integer 1210 // number of periods if it doesn't contain a lot of periods. 1211 double phase_incr = 2.0 * M_PI * frequency / sample_rate; 1212 1213 double sum_cos = 0.0; 1214 double sum_sin = 0.0; 1215 for (size_t i = 0; i < signal.size(); ++i) { 1216 double c = std::cos(phase_incr * static_cast<double>(i)); 1217 double s = std::sin(phase_incr * static_cast<double>(i)); 1218 sum_cos += signal[i] * c; 1219 sum_sin += signal[i] * s; 1220 } 1221 1222 double amplitude = 2.0f * std::sqrt(sum_cos * sum_cos + sum_sin * sum_sin) / 1223 static_cast<double>(signal.size()); 1224 double phi = std::atan2(sum_cos, sum_sin); 1225 1226 out_amplitude = amplitude; 1227 out_phase = phi; 1228 1229 // Compute sum of squared errors relative to the fitted sine wave 1230 double sse = 0.0; 1231 for (size_t i = 0; i < signal.size(); ++i) { 1232 // Use known amplitude here instead instead of the from the fitted function. 1233 double fit = 0.8 * std::sin(phase_incr * i + phi); 1234 double diff = signal[i] - fit; 1235 sse += diff * diff; 1236 } 1237 1238 return sse; 1239 } 1240 1241 // Finds the offset of the start of an input_freq sine wave sampled at 1242 // target_rate in data. Remove the leading silence from data. 1243 size_t 1244 find_sine_start(const std::vector<float> & data, float input_freq, 1245 float target_rate) 1246 { 1247 const size_t POINTS = 10; 1248 size_t skipped = 0; 1249 1250 while (skipped + POINTS < data.size()) { 1251 double phase = 0; 1252 double phase_increment = 2.0f * M_PI * input_freq / target_rate; 1253 bool fits_sine = true; 1254 1255 for (size_t i = 0; i < POINTS; i++) { 1256 float expected = sin(phase) * 0.8; 1257 float actual = data[skipped + i]; 1258 if (fabs(expected - actual) > 0.1) { 1259 // doesn't fit a sine, skip to next start point 1260 fits_sine = false; 1261 break; 1262 } 1263 phase += phase_increment; 1264 if (phase > 2.0f * M_PI) { 1265 phase -= 2.0f * M_PI; 1266 } 1267 } 1268 1269 if (!fits_sine) { 1270 skipped++; 1271 continue; 1272 } 1273 1274 // Found the start of the sine wave 1275 size_t sine_start = skipped; 1276 return sine_start; 1277 } 1278 1279 return skipped; 1280 } 1281 1282 // This class tracks the monotonicity of a certain value, and reports if it 1283 // increases too much monotonically. 1284 struct monotonic_state { 1285 explicit monotonic_state(const char * what, int source_rate, int target_rate, 1286 int block_size) 1287 : what(what), source_rate(source_rate), target_rate(target_rate), 1288 block_size(block_size) 1289 { 1290 } 1291 ~monotonic_state() 1292 { 1293 float ratio = 1294 static_cast<float>(source_rate) / static_cast<float>(target_rate); 1295 // Only report if there has been a meaningful increase in buffering. Do 1296 // not warn if the buffering was constant and small. 1297 if (monotonic && max_value && max_value != max_step) { 1298 printf("%s is monotonically increasing, max: %zu, max_step: %zu, " 1299 "in: %dHz, out: " 1300 "%dHz, block_size: %d, ratio: %lf\n", 1301 what, max_value, max_step, source_rate, target_rate, block_size, 1302 ratio); 1303 } 1304 // Arbitrary limit: if more than this number of frames has been buffered, 1305 // print a message. 1306 constexpr int BUFFER_SIZE_THRESHOLD = 20; 1307 if (max_value > BUFFER_SIZE_THRESHOLD) { 1308 printf("%s, unexpected large max buffering value, max: %zu, max_step: " 1309 "%zu, in: %dHz, out: %dHz, block_size: %d, ratio: %lf\n", 1310 what, max_value, max_step, source_rate, target_rate, block_size, 1311 ratio); 1312 } 1313 } 1314 void set_new_value(size_t new_value) 1315 { 1316 if (new_value < value) { 1317 monotonic = false; 1318 } else { 1319 max_step = std::max(max_step, new_value - value); 1320 } 1321 value = new_value; 1322 max_value = std::max(value, max_value); 1323 } 1324 // Textual representation of this measurement 1325 const char * what; 1326 // Resampler parameters for this test case 1327 int source_rate = 0; 1328 int target_rate = 0; 1329 int block_size = 0; 1330 // Current buffering value 1331 size_t value = 0; 1332 // Max buffering value increment 1333 size_t max_step = 0; 1334 // Max buffering value observerd 1335 size_t max_value = 0; 1336 // Whether the value has only increased or not 1337 bool monotonic = true; 1338 }; 1339 1340 // Setting this to 1 dumps a bunch of wave file to the local directory for 1341 // manual inspection of the resampled output 1342 constexpr int DUMP_OUTPUT = 0; 1343 1344 // Source and target sample-rates in Hz, typical values. 1345 const int rates[] = {16000, 32000, 44100, 48000, 96000, 192000, 384000}; 1346 // Block size in frames, except the first element, that is in millisecond 1347 // Power of two are typical on Windows WASAPI IAudioClient3, macOS, 1348 // Linux Pipewire and Jack. 10ms is typical on Windows IAudioClient and 1349 // IAudioClient2. 96, 192 are not uncommon on some Android devices. 1350 constexpr int WASAPI_MS_BLOCK = 10; 1351 const int block_sizes[] = {WASAPI_MS_BLOCK, 96, 128, 192, 256, 512, 1024, 2048}; 1352 // Enough iterations to catch rounding/drift issues, but not too many to avoid 1353 // having a test that is too long to run. 1354 constexpr int ITERATION_COUNT = 1000; 1355 // 1 kHz input sine wave 1356 const float input_freq = 1000.0f; 1357 1358 struct ThreadPool { 1359 std::vector<std::thread> workers; 1360 std::queue<std::function<void()>> tasks; 1361 std::mutex queue_mutex; 1362 std::condition_variable condition; 1363 bool stop; 1364 1365 ThreadPool(size_t threads) : stop(false) 1366 { 1367 for (size_t i = 0; i < threads; ++i) { 1368 workers.emplace_back([this] { 1369 while (true) { 1370 std::function<void()> task; 1371 { 1372 std::unique_lock<std::mutex> lock(queue_mutex); 1373 condition.wait(lock, [this] { return stop || !tasks.empty(); }); 1374 if (stop && tasks.empty()) 1375 return; 1376 task = std::move(tasks.front()); 1377 tasks.pop(); 1378 } 1379 task(); 1380 } 1381 }); 1382 } 1383 } 1384 1385 void enqueue(std::function<void()> task) 1386 { 1387 { 1388 std::unique_lock<std::mutex> lock(queue_mutex); 1389 tasks.push(std::move(task)); 1390 } 1391 condition.notify_one(); 1392 } 1393 1394 ~ThreadPool() 1395 { 1396 { 1397 std::unique_lock<std::mutex> lock(queue_mutex); 1398 stop = true; 1399 } 1400 condition.notify_all(); 1401 for (std::thread & worker : workers) { 1402 worker.join(); 1403 } 1404 } 1405 }; 1406 1407 static void 1408 run_test(int source_rate, int target_rate, int block_size) 1409 { 1410 int effective_block_size = block_size; 1411 // special case: Windows/WASAPI works in blocks of 10ms regardless of 1412 // the rate. 1413 if (effective_block_size == WASAPI_MS_BLOCK) { 1414 effective_block_size = target_rate / 100; // 10ms 1415 } 1416 sine_wave_state state(input_freq, source_rate); 1417 cubeb_stream_params out_params = {}; 1418 out_params.channels = 1; 1419 out_params.rate = target_rate; 1420 out_params.format = CUBEB_SAMPLE_FLOAT32NE; 1421 1422 cubeb_audio_dump_session_t session = nullptr; 1423 cubeb_audio_dump_stream_t dump_stream = nullptr; 1424 if constexpr (DUMP_OUTPUT) { 1425 cubeb_audio_dump_init(&session); 1426 char buf[256]; 1427 snprintf(buf, 256, "test-%dHz-to-%dhz-%d-block.wav", source_rate, 1428 target_rate, effective_block_size); 1429 cubeb_audio_dump_stream_init(session, &dump_stream, out_params, buf); 1430 cubeb_audio_dump_start(session); 1431 } 1432 cubeb_resampler * resampler = cubeb_resampler_create( 1433 nullptr, nullptr, &out_params, source_rate, data_cb, &state, 1434 CUBEB_RESAMPLER_QUALITY_DEFAULT, CUBEB_RESAMPLER_RECLOCK_NONE); 1435 ASSERT_NE(resampler, nullptr); 1436 1437 std::vector<float> data(effective_block_size * out_params.channels); 1438 int i = ITERATION_COUNT; 1439 // For now this only tests the output side (out_... measurements). 1440 // We could expect the resampler to be symmetrical, but we could 1441 // test both sides at once. 1442 // - ..._in is the input buffer of the resampler, containing 1443 // unresampled frames 1444 // - ..._out is the output buffer, containing resampled frames. 1445 monotonic_state in_in_max("in_in", source_rate, target_rate, 1446 effective_block_size); 1447 monotonic_state in_out_max("in_out", source_rate, target_rate, 1448 effective_block_size); 1449 monotonic_state out_in_max("out_in", source_rate, target_rate, 1450 effective_block_size); 1451 monotonic_state out_out_max("out_out", source_rate, target_rate, 1452 effective_block_size); 1453 1454 std::vector<float> resampled; 1455 resampled.reserve(ITERATION_COUNT * effective_block_size * 1456 out_params.channels); 1457 while (i--) { 1458 int64_t got = cubeb_resampler_fill(resampler, nullptr, nullptr, data.data(), 1459 effective_block_size); 1460 ASSERT_EQ(got, effective_block_size); 1461 cubeb_resampler_stats stats = cubeb_resampler_stats_get(resampler); 1462 1463 resampled.insert(resampled.end(), data.begin(), data.end()); 1464 1465 in_in_max.set_new_value(stats.input_input_buffer_size); 1466 in_out_max.set_new_value(stats.input_output_buffer_size); 1467 out_in_max.set_new_value(stats.output_input_buffer_size); 1468 out_out_max.set_new_value(stats.output_output_buffer_size); 1469 } 1470 1471 cubeb_resampler_destroy(resampler); 1472 1473 // Example of an error, off by one every block or so, resulting in a 1474 // silent sample. This is enough to make all the tests fail. 1475 // 1476 // for (uint32_t i = 0; i < resampled.size(); i++) { 1477 // if (!(i % (effective_block_size))) { 1478 // resampled[i] = 0.0; 1479 // } 1480 // } 1481 1482 // This roughly finds the start of the sine wave and strips it from 1483 // data. 1484 size_t skipped = 0; 1485 skipped = find_sine_start(resampled, input_freq, target_rate); 1486 1487 resampled.erase(resampled.begin(), resampled.begin() + skipped); 1488 1489 if constexpr (DUMP_OUTPUT) { 1490 cubeb_audio_dump_write(dump_stream, resampled.data(), resampled.size()); 1491 } 1492 1493 float amplitude = 0; 1494 float phase = 0; 1495 1496 // Fit our resampled sine wave, get an MSE value 1497 double sse = fit_sine(resampled, target_rate, input_freq, amplitude, phase); 1498 double mse = sse / resampled.size(); 1499 1500 // Code to print JSON to plot externally 1501 // printf("\t[%d,%d,%d,%.10e,%lf,%lf],\n", source_rate, target_rate, 1502 // effective_block_size, mse, amplitude, phase); 1503 1504 // Value found after running the tests on Linux x64 1505 ASSERT_LT(mse, 3.22e-07); 1506 1507 if constexpr (DUMP_OUTPUT) { 1508 cubeb_audio_dump_stop(session); 1509 cubeb_audio_dump_stream_shutdown(session, dump_stream); 1510 cubeb_audio_dump_shutdown(session); 1511 } 1512 } 1513 1514 // This tests checks three things: 1515 // - Whenever resampling from a source rate to a target rate with a certain 1516 // block size, the correct number of frames is provided back from the 1517 // resampler, to the backend. 1518 // - While resampling, internal buffers are kept under control and aren't 1519 // growing unbounded. 1520 // - The output signal is a 1khz sine (as is the input) 1521 TEST(cubeb, resampler_typical_uses) 1522 { 1523 cubeb * ctx; 1524 common_init(&ctx, "Cubeb resampler test"); 1525 1526 size_t concurrency = std::max(1u, std::thread::hardware_concurrency()); 1527 std::condition_variable cv; 1528 std::mutex mutex; 1529 size_t task_count = 0; 1530 ThreadPool pool(concurrency); 1531 1532 for (int source_rate : rates) { 1533 for (int target_rate : rates) { 1534 for (int block_size : block_sizes) { 1535 { 1536 std::unique_lock<std::mutex> lock(mutex); 1537 ++task_count; 1538 } 1539 pool.enqueue([&, source_rate, target_rate, block_size] { 1540 run_test(source_rate, target_rate, block_size); 1541 { 1542 std::unique_lock<std::mutex> lock(mutex); 1543 --task_count; 1544 } 1545 cv.notify_one(); 1546 }); 1547 } 1548 } 1549 } 1550 1551 std::unique_lock<std::mutex> lock(mutex); 1552 cv.wait(lock, [&] { return task_count == 0; }); 1553 cubeb_destroy(ctx); 1554 } 1555 #undef NOMINMAX 1556 #undef DUMP_ARRAYS