tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }