TestAudioResampler.cpp (21787B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "AudioResampler.h" 7 #include "gtest/gtest.h" 8 #include "nsContentUtils.h" 9 10 using namespace mozilla; 11 12 template <class T> 13 AudioChunk CreateAudioChunk(uint32_t aFrames, uint32_t aChannels, 14 AudioSampleFormat aSampleFormat) { 15 AudioChunk chunk; 16 nsTArray<nsTArray<T>> buffer; 17 buffer.AppendElements(aChannels); 18 19 nsTArray<const T*> bufferPtrs; 20 bufferPtrs.AppendElements(aChannels); 21 22 for (uint32_t i = 0; i < aChannels; ++i) { 23 T* ptr = buffer[i].AppendElements(aFrames); 24 bufferPtrs[i] = ptr; 25 for (uint32_t j = 0; j < aFrames; ++j) { 26 if (aSampleFormat == AUDIO_FORMAT_FLOAT32) { 27 ptr[j] = 0.01 * j; 28 } else { 29 ptr[j] = j; 30 } 31 } 32 } 33 34 chunk.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer)); 35 chunk.mBufferFormat = aSampleFormat; 36 chunk.mChannelData.AppendElements(aChannels); 37 for (uint32_t i = 0; i < aChannels; ++i) { 38 chunk.mChannelData[i] = bufferPtrs[i]; 39 } 40 chunk.mDuration = aFrames; 41 return chunk; 42 } 43 44 template <class T> 45 AudioSegment CreateAudioSegment(uint32_t aFrames, uint32_t aChannels, 46 AudioSampleFormat aSampleFormat) { 47 AudioSegment segment; 48 AudioChunk chunk = CreateAudioChunk<T>(aFrames, aChannels, aSampleFormat); 49 segment.AppendAndConsumeChunk(std::move(chunk)); 50 return segment; 51 } 52 53 TEST(TestAudioResampler, OutAudioSegment_Float) 54 { 55 const PrincipalHandle testPrincipal = 56 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 57 58 uint32_t in_frames = 10; 59 uint32_t out_frames = 40; 60 uint32_t channels = 2; 61 uint32_t in_rate = 24000; 62 uint32_t out_rate = 48000; 63 64 uint32_t pre_buffer = 21; 65 66 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 67 68 AudioSegment inSegment = 69 CreateAudioSegment<float>(in_frames, channels, AUDIO_FORMAT_FLOAT32); 70 dr.AppendInput(inSegment); 71 72 out_frames = 20u; 73 bool hasUnderrun = false; 74 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 75 EXPECT_FALSE(hasUnderrun); 76 EXPECT_EQ(s.GetDuration(), 20); 77 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 78 79 for (AudioSegment::ChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 80 AudioChunk& c = *ci; 81 EXPECT_EQ(c.mPrincipalHandle, testPrincipal); 82 EXPECT_EQ(c.ChannelCount(), 2u); 83 for (uint32_t i = 0; i < out_frames; ++i) { 84 // The first input segment is part of the pre buffer, so 21-10=11 of the 85 // input is silence. They make up 22 silent output frames after 86 // resampling. 87 EXPECT_FLOAT_EQ(c.ChannelData<float>()[0][i], 0.0); 88 EXPECT_FLOAT_EQ(c.ChannelData<float>()[1][i], 0.0); 89 } 90 } 91 92 // Update in rate 93 in_rate = 26122; 94 dr.UpdateInRate(in_rate); 95 out_frames = in_frames * out_rate / in_rate; 96 EXPECT_EQ(out_frames, 18u); 97 // Even if we provide no input if we have enough buffered input, we can create 98 // output 99 hasUnderrun = false; 100 AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun); 101 EXPECT_FALSE(hasUnderrun); 102 EXPECT_EQ(s1.GetDuration(), out_frames); 103 EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO); 104 for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) { 105 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 106 } 107 } 108 109 TEST(TestAudioResampler, OutAudioSegment_Short) 110 { 111 const PrincipalHandle testPrincipal = 112 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 113 114 uint32_t in_frames = 10; 115 uint32_t out_frames = 40; 116 uint32_t channels = 2; 117 uint32_t in_rate = 24000; 118 uint32_t out_rate = 48000; 119 120 uint32_t pre_buffer = 21; 121 122 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 123 124 AudioSegment inSegment = 125 CreateAudioSegment<short>(in_frames, channels, AUDIO_FORMAT_S16); 126 dr.AppendInput(inSegment); 127 128 out_frames = 20u; 129 bool hasUnderrun = false; 130 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 131 EXPECT_FALSE(hasUnderrun); 132 EXPECT_EQ(s.GetDuration(), 20); 133 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 134 135 for (AudioSegment::ChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 136 AudioChunk& c = *ci; 137 EXPECT_EQ(c.mPrincipalHandle, testPrincipal); 138 EXPECT_EQ(c.ChannelCount(), 2u); 139 for (uint32_t i = 0; i < out_frames; ++i) { 140 // The first input segment is part of the pre buffer, so 21-10=11 of the 141 // input is silence. They make up 22 silent output frames after 142 // resampling. 143 EXPECT_FLOAT_EQ(c.ChannelData<short>()[0][i], 0.0); 144 EXPECT_FLOAT_EQ(c.ChannelData<short>()[1][i], 0.0); 145 } 146 } 147 148 // Update in rate 149 in_rate = 26122; 150 dr.UpdateInRate(out_rate); 151 out_frames = in_frames * out_rate / in_rate; 152 EXPECT_EQ(out_frames, 18u); 153 // Even if we provide no input if we have enough buffered input, we can create 154 // output 155 hasUnderrun = false; 156 AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun); 157 EXPECT_FALSE(hasUnderrun); 158 EXPECT_EQ(s1.GetDuration(), out_frames); 159 EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO); 160 for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) { 161 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 162 } 163 } 164 165 TEST(TestAudioResampler, OutAudioSegmentLargerThanResampledInput_Float) 166 { 167 const uint32_t in_frames = 130; 168 const uint32_t out_frames = 300; 169 uint32_t channels = 2; 170 uint32_t in_rate = 24000; 171 uint32_t out_rate = 48000; 172 173 uint32_t pre_buffer = 5; 174 175 AudioResampler dr(in_rate, out_rate, pre_buffer, PRINCIPAL_HANDLE_NONE); 176 177 AudioSegment inSegment = 178 CreateAudioSegment<float>(in_frames, channels, AUDIO_FORMAT_FLOAT32); 179 180 // Set the pre-buffer. 181 dr.AppendInput(inSegment); 182 bool hasUnderrun = false; 183 AudioSegment s = dr.Resample(300, &hasUnderrun); 184 EXPECT_FALSE(hasUnderrun); 185 EXPECT_EQ(s.GetDuration(), 300); 186 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 187 188 dr.AppendInput(inSegment); 189 190 AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun); 191 EXPECT_TRUE(hasUnderrun); 192 EXPECT_EQ(s2.GetDuration(), 300); 193 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 194 } 195 196 TEST(TestAudioResampler, InAudioSegment_Float) 197 { 198 const PrincipalHandle testPrincipal = 199 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 200 201 uint32_t in_frames = 10; 202 uint32_t out_frames = 20; 203 uint32_t channels = 2; 204 uint32_t in_rate = 24000; 205 uint32_t out_rate = 48000; 206 207 uint32_t pre_buffer = 10; 208 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 209 210 AudioSegment inSegment; 211 212 AudioChunk chunk1; 213 chunk1.SetNull(in_frames / 2); 214 inSegment.AppendAndConsumeChunk(std::move(chunk1)); 215 216 AudioChunk chunk2; 217 nsTArray<nsTArray<float>> buffer; 218 buffer.AppendElements(channels); 219 220 nsTArray<const float*> bufferPtrs; 221 bufferPtrs.AppendElements(channels); 222 223 for (uint32_t i = 0; i < channels; ++i) { 224 float* ptr = buffer[i].AppendElements(5); 225 bufferPtrs[i] = ptr; 226 for (uint32_t j = 0; j < 5; ++j) { 227 ptr[j] = 0.01f * j; 228 } 229 } 230 231 chunk2.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer)); 232 chunk2.mBufferFormat = AUDIO_FORMAT_FLOAT32; 233 chunk2.mChannelData.AppendElements(channels); 234 for (uint32_t i = 0; i < channels; ++i) { 235 chunk2.mChannelData[i] = bufferPtrs[i]; 236 } 237 chunk2.mDuration = in_frames / 2; 238 inSegment.AppendAndConsumeChunk(std::move(chunk2)); 239 240 dr.AppendInput(inSegment); 241 bool hasUnderrun = false; 242 AudioSegment outSegment = dr.Resample(out_frames, &hasUnderrun); 243 EXPECT_FALSE(hasUnderrun); 244 // inSegment contains 10 frames, 5 null, 5 non-null. They're part of the pre 245 // buffer which is 10, meaning there are no extra pre buffered silence frames. 246 EXPECT_EQ(outSegment.GetDuration(), out_frames); 247 EXPECT_EQ(outSegment.MaxChannelCount(), 2u); 248 249 // Add another 5 null and 5 non-null frames. 250 dr.AppendInput(inSegment); 251 AudioSegment outSegment2 = dr.Resample(out_frames, &hasUnderrun); 252 EXPECT_FALSE(hasUnderrun); 253 EXPECT_EQ(outSegment2.GetDuration(), out_frames); 254 EXPECT_EQ(outSegment2.MaxChannelCount(), 2u); 255 for (AudioSegment::ConstChunkIterator ci(outSegment2); !ci.IsEnded(); 256 ci.Next()) { 257 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 258 } 259 } 260 261 TEST(TestAudioResampler, InAudioSegment_Short) 262 { 263 const PrincipalHandle testPrincipal = 264 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 265 266 uint32_t in_frames = 10; 267 uint32_t out_frames = 20; 268 uint32_t channels = 2; 269 uint32_t in_rate = 24000; 270 uint32_t out_rate = 48000; 271 272 uint32_t pre_buffer = 10; 273 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 274 275 AudioSegment inSegment; 276 277 // The null chunk at the beginning will be ignored. 278 AudioChunk chunk1; 279 chunk1.SetNull(in_frames / 2); 280 inSegment.AppendAndConsumeChunk(std::move(chunk1)); 281 282 AudioChunk chunk2; 283 nsTArray<nsTArray<short>> buffer; 284 buffer.AppendElements(channels); 285 286 nsTArray<const short*> bufferPtrs; 287 bufferPtrs.AppendElements(channels); 288 289 for (uint32_t i = 0; i < channels; ++i) { 290 short* ptr = buffer[i].AppendElements(5); 291 bufferPtrs[i] = ptr; 292 for (uint32_t j = 0; j < 5; ++j) { 293 ptr[j] = j; 294 } 295 } 296 297 chunk2.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer)); 298 chunk2.mBufferFormat = AUDIO_FORMAT_S16; 299 chunk2.mChannelData.AppendElements(channels); 300 for (uint32_t i = 0; i < channels; ++i) { 301 chunk2.mChannelData[i] = bufferPtrs[i]; 302 } 303 chunk2.mDuration = in_frames / 2; 304 inSegment.AppendAndConsumeChunk(std::move(chunk2)); 305 306 dr.AppendInput(inSegment); 307 bool hasUnderrun = false; 308 AudioSegment outSegment = dr.Resample(out_frames, &hasUnderrun); 309 EXPECT_FALSE(hasUnderrun); 310 // inSegment contains 10 frames, 5 null, 5 non-null. They're part of the pre 311 // buffer which is 10, meaning there are no extra pre buffered silence frames. 312 EXPECT_EQ(outSegment.GetDuration(), out_frames); 313 EXPECT_EQ(outSegment.MaxChannelCount(), 2u); 314 315 // Add another 5 null and 5 non-null frames. 316 dr.AppendInput(inSegment); 317 AudioSegment outSegment2 = dr.Resample(out_frames, &hasUnderrun); 318 EXPECT_FALSE(hasUnderrun); 319 EXPECT_EQ(outSegment2.GetDuration(), out_frames); 320 EXPECT_EQ(outSegment2.MaxChannelCount(), 2u); 321 for (AudioSegment::ConstChunkIterator ci(outSegment2); !ci.IsEnded(); 322 ci.Next()) { 323 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 324 } 325 } 326 327 TEST(TestAudioResampler, ChannelChange_MonoToStereo) 328 { 329 const PrincipalHandle testPrincipal = 330 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 331 332 uint32_t in_frames = 10; 333 uint32_t out_frames = 40; 334 uint32_t in_rate = 24000; 335 uint32_t out_rate = 48000; 336 337 uint32_t pre_buffer = 0; 338 339 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 340 341 AudioChunk monoChunk = 342 CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32); 343 AudioChunk stereoChunk = 344 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 345 346 AudioSegment inSegment; 347 inSegment.AppendAndConsumeChunk(std::move(monoChunk)); 348 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 349 dr.AppendInput(inSegment); 350 351 bool hasUnderrun = false; 352 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 353 EXPECT_FALSE(hasUnderrun); 354 EXPECT_EQ(s.GetDuration(), 40); 355 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 356 EXPECT_EQ(s.MaxChannelCount(), 2u); 357 for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 358 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 359 } 360 } 361 362 TEST(TestAudioResampler, ChannelChange_StereoToMono) 363 { 364 const PrincipalHandle testPrincipal = 365 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 366 367 uint32_t in_frames = 10; 368 uint32_t out_frames = 40; 369 uint32_t in_rate = 24000; 370 uint32_t out_rate = 48000; 371 372 uint32_t pre_buffer = 0; 373 374 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 375 376 AudioChunk monoChunk = 377 CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32); 378 AudioChunk stereoChunk = 379 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 380 381 AudioSegment inSegment; 382 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 383 inSegment.AppendAndConsumeChunk(std::move(monoChunk)); 384 dr.AppendInput(inSegment); 385 386 bool hasUnderrun = false; 387 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 388 EXPECT_FALSE(hasUnderrun); 389 EXPECT_EQ(s.GetDuration(), 40); 390 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 391 EXPECT_EQ(s.MaxChannelCount(), 1u); 392 for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 393 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 394 } 395 } 396 397 TEST(TestAudioResampler, ChannelChange_StereoToQuad) 398 { 399 const PrincipalHandle testPrincipal = 400 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 401 402 uint32_t in_frames = 10; 403 uint32_t out_frames = 40; 404 uint32_t in_rate = 24000; 405 uint32_t out_rate = 48000; 406 407 uint32_t pre_buffer = 0; 408 409 AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal); 410 411 AudioChunk stereoChunk = 412 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 413 AudioChunk quadChunk = 414 CreateAudioChunk<float>(in_frames, 4, AUDIO_FORMAT_FLOAT32); 415 416 AudioSegment inSegment; 417 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 418 inSegment.AppendAndConsumeChunk(std::move(quadChunk)); 419 dr.AppendInput(inSegment); 420 421 bool hasUnderrun = false; 422 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 423 EXPECT_FALSE(hasUnderrun); 424 EXPECT_EQ(s.GetDuration(), 40u); 425 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 426 427 AudioSegment s2 = dr.Resample(out_frames / 2, &hasUnderrun); 428 EXPECT_TRUE(hasUnderrun); 429 EXPECT_EQ(s2.GetDuration(), 20u); 430 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 431 for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 432 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 433 } 434 } 435 436 TEST(TestAudioResampler, ChannelChange_QuadToStereo) 437 { 438 const PrincipalHandle testPrincipal = 439 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 440 441 uint32_t in_frames = 10; 442 uint32_t out_frames = 40; 443 uint32_t in_rate = 24000; 444 uint32_t out_rate = 48000; 445 446 AudioResampler dr(in_rate, out_rate, 0, testPrincipal); 447 448 AudioChunk stereoChunk = 449 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 450 AudioChunk quadChunk = 451 CreateAudioChunk<float>(in_frames, 4, AUDIO_FORMAT_FLOAT32); 452 453 AudioSegment inSegment; 454 inSegment.AppendAndConsumeChunk(std::move(quadChunk)); 455 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 456 dr.AppendInput(inSegment); 457 458 bool hasUnderrun = false; 459 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 460 EXPECT_FALSE(hasUnderrun); 461 EXPECT_EQ(s.GetDuration(), 40u); 462 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 463 464 AudioSegment s2 = dr.Resample(out_frames / 2, &hasUnderrun); 465 EXPECT_TRUE(hasUnderrun); 466 EXPECT_EQ(s2.GetDuration(), 20u); 467 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 468 for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) { 469 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 470 } 471 } 472 473 void printAudioSegment(const AudioSegment& segment); 474 475 TEST(TestAudioResampler, ChannelChange_Discontinuity) 476 { 477 const PrincipalHandle testPrincipal = 478 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 479 480 uint32_t in_rate = 24000; 481 uint32_t out_rate = 48000; 482 483 const float amplitude = 0.5; 484 const float frequency = 200; 485 const float phase = 0.0; 486 float time = 0.0; 487 const float deltaTime = 1.0f / static_cast<float>(in_rate); 488 489 uint32_t in_frames = in_rate / 100; 490 uint32_t out_frames = out_rate / 100; 491 AudioResampler dr(in_rate, out_rate, 0, testPrincipal); 492 493 AudioChunk monoChunk = 494 CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32); 495 for (uint32_t i = 0; i < monoChunk.GetDuration(); ++i) { 496 double value = amplitude * sin(2 * M_PI * frequency * time + phase); 497 monoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value); 498 time += deltaTime; 499 } 500 AudioChunk stereoChunk = 501 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 502 for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) { 503 double value = amplitude * sin(2 * M_PI * frequency * time + phase); 504 stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value); 505 if (stereoChunk.ChannelCount() == 2) { 506 stereoChunk.ChannelDataForWrite<float>(1)[i] = value; 507 } 508 time += deltaTime; 509 } 510 511 AudioSegment inSegment; 512 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 513 // printAudioSegment(inSegment); 514 515 dr.AppendInput(inSegment); 516 bool hasUnderrun = false; 517 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 518 EXPECT_FALSE(hasUnderrun); 519 // printAudioSegment(s); 520 521 AudioSegment inSegment2; 522 inSegment2.AppendAndConsumeChunk(std::move(monoChunk)); 523 // The resampler here is updated due to the channel change and that creates 524 // discontinuity. 525 dr.AppendInput(inSegment2); 526 AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun); 527 EXPECT_FALSE(hasUnderrun); 528 // printAudioSegment(s2); 529 530 EXPECT_EQ(s2.GetDuration(), 480); 531 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 532 EXPECT_EQ(s2.MaxChannelCount(), 1u); 533 for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) { 534 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 535 } 536 } 537 538 TEST(TestAudioResampler, ChannelChange_Discontinuity2) 539 { 540 const PrincipalHandle testPrincipal = 541 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 542 543 uint32_t in_rate = 24000; 544 uint32_t out_rate = 48000; 545 546 const float amplitude = 0.5; 547 const float frequency = 200; 548 const float phase = 0.0; 549 float time = 0.0; 550 const float deltaTime = 1.0f / static_cast<float>(in_rate); 551 552 uint32_t in_frames = in_rate / 100; 553 uint32_t out_frames = out_rate / 100; 554 AudioResampler dr(in_rate, out_rate, 10, testPrincipal); 555 556 AudioChunk monoChunk = 557 CreateAudioChunk<float>(in_frames / 2, 1, AUDIO_FORMAT_FLOAT32); 558 for (uint32_t i = 0; i < monoChunk.GetDuration(); ++i) { 559 double value = amplitude * sin(2 * M_PI * frequency * time + phase); 560 monoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value); 561 time += deltaTime; 562 } 563 AudioChunk stereoChunk = 564 CreateAudioChunk<float>(in_frames / 2, 2, AUDIO_FORMAT_FLOAT32); 565 for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) { 566 double value = amplitude * sin(2 * M_PI * frequency * time + phase); 567 stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value); 568 if (stereoChunk.ChannelCount() == 2) { 569 stereoChunk.ChannelDataForWrite<float>(1)[i] = value; 570 } 571 time += deltaTime; 572 } 573 574 AudioSegment inSegment; 575 inSegment.AppendAndConsumeChunk(std::move(monoChunk)); 576 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 577 // printAudioSegment(inSegment); 578 579 dr.AppendInput(inSegment); 580 bool hasUnderrun = false; 581 AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun); 582 EXPECT_FALSE(hasUnderrun); 583 // printAudioSegment(s1); 584 585 EXPECT_EQ(s1.GetDuration(), 480); 586 EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO); 587 EXPECT_EQ(s1.MaxChannelCount(), 2u); 588 for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) { 589 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 590 } 591 592 // The resampler here is updated due to the channel change and that creates 593 // discontinuity. 594 dr.AppendInput(inSegment); 595 AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun); 596 EXPECT_FALSE(hasUnderrun); 597 // printAudioSegment(s2); 598 599 EXPECT_EQ(s2.GetDuration(), 480); 600 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 601 EXPECT_EQ(s2.MaxChannelCount(), 2u); 602 for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) { 603 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 604 } 605 } 606 607 TEST(TestAudioResampler, ChannelChange_Discontinuity3) 608 { 609 const PrincipalHandle testPrincipal = 610 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 611 612 uint32_t in_rate = 48000; 613 uint32_t out_rate = 48000; 614 615 const float amplitude = 0.5; 616 const float frequency = 200; 617 const float phase = 0.0; 618 float time = 0.0; 619 const float deltaTime = 1.0f / static_cast<float>(in_rate); 620 621 uint32_t in_frames = in_rate / 100; 622 uint32_t out_frames = out_rate / 100; 623 AudioResampler dr(in_rate, out_rate, 10, testPrincipal); 624 625 AudioChunk stereoChunk = 626 CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32); 627 for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) { 628 double value = amplitude * sin(2 * M_PI * frequency * time + phase); 629 stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value); 630 if (stereoChunk.ChannelCount() == 2) { 631 stereoChunk.ChannelDataForWrite<float>(1)[i] = value; 632 } 633 time += deltaTime; 634 } 635 636 AudioSegment inSegment; 637 inSegment.AppendAndConsumeChunk(std::move(stereoChunk)); 638 // printAudioSegment(inSegment); 639 640 dr.AppendInput(inSegment); 641 bool hasUnderrun = false; 642 AudioSegment s = dr.Resample(out_frames, &hasUnderrun); 643 EXPECT_FALSE(hasUnderrun); 644 // printAudioSegment(s); 645 646 EXPECT_EQ(s.GetDuration(), 480); 647 EXPECT_EQ(s.GetType(), MediaSegment::AUDIO); 648 EXPECT_EQ(s.MaxChannelCount(), 2u); 649 650 // The resampler here is updated due to the rate change. This is because the 651 // in and out rate was the same so a pass through logic was used. By updating 652 // the in rate to something different than the out rate, the resampler will 653 // start being used and discontinuity will exist. 654 dr.UpdateInRate(in_rate - 400); 655 dr.AppendInput(inSegment); 656 AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun); 657 EXPECT_FALSE(hasUnderrun); 658 // printAudioSegment(s2); 659 660 EXPECT_EQ(s2.GetDuration(), 480); 661 EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO); 662 EXPECT_EQ(s2.MaxChannelCount(), 2u); 663 for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) { 664 EXPECT_EQ(ci->mPrincipalHandle, testPrincipal); 665 } 666 }