summaryrefslogtreecommitdiffstats
path: root/dom/media/driftcontrol/AudioResampler.cpp
blob: ecef033a5cf487f495686e17bea3ad414ed7c4fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "AudioResampler.h"

namespace mozilla {

AudioResampler::AudioResampler(uint32_t aInRate, uint32_t aOutRate,
                               media::TimeUnit aPreBufferDuration,
                               const PrincipalHandle& aPrincipalHandle)
    : mResampler(aInRate, aOutRate, aPreBufferDuration),
      mOutputChunks(aOutRate / 10, STEREO, aPrincipalHandle) {}

void AudioResampler::AppendInput(const AudioSegment& aInSegment) {
  MOZ_ASSERT(aInSegment.GetDuration());
  for (AudioSegment::ConstChunkIterator iter(aInSegment); !iter.IsEnded();
       iter.Next()) {
    const AudioChunk& chunk = *iter;
    if (!mIsSampleFormatSet) {
      // We don't know the format yet and all buffers are empty.
      if (chunk.mBufferFormat == AUDIO_FORMAT_SILENCE) {
        // Only silence has been received and the format is unkown. Igonre it,
        // if Resampler() is called it will return silence too.
        continue;
      }
      // First no silence data, set the format once for lifetime and let it
      // continue the rest of the flow. We will not get in here again.
      mOutputChunks.SetSampleFormat(chunk.mBufferFormat);
      mResampler.SetSampleFormat(chunk.mBufferFormat);
      mIsSampleFormatSet = true;
    }
    MOZ_ASSERT(mIsSampleFormatSet);
    if (chunk.IsNull()) {
      mResampler.AppendInputSilence(chunk.GetDuration());
      continue;
    }
    // Make sure the channel is up to date. An AudioSegment can contain chunks
    // with different channel count.
    UpdateChannels(chunk.mChannelData.Length());
    if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
      mResampler.AppendInput(chunk.ChannelData<float>(), chunk.GetDuration());
    } else {
      mResampler.AppendInput(chunk.ChannelData<int16_t>(), chunk.GetDuration());
    }
  }
}

AudioSegment AudioResampler::Resample(uint32_t aOutFrames, bool* aHasUnderrun) {
  MOZ_ASSERT(aHasUnderrun);

  AudioSegment segment;

  // We don't know what to do yet and we only have received silence if any just
  // return what they want and leave
  if (!mIsSampleFormatSet) {
    segment.AppendNullData(aOutFrames);
    return segment;
  }

  media::TimeUnit outDuration(aOutFrames, mResampler.GetOutRate());
  mResampler.EnsurePreBuffer(outDuration);

  const media::TimeUnit chunkCapacity(mOutputChunks.ChunkCapacity(),
                                      mResampler.GetOutRate());

  while (!outDuration.IsZero()) {
    MOZ_ASSERT(outDuration.IsPositive());
    AudioChunk& chunk = mOutputChunks.GetNext();
    const media::TimeUnit chunkDuration = std::min(outDuration, chunkCapacity);
    outDuration -= chunkDuration;

    const uint32_t outFrames =
        chunkDuration.ToTicksAtRate(mResampler.GetOutRate());
    for (uint32_t i = 0; i < chunk.ChannelCount(); ++i) {
      if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
        *aHasUnderrun |= mResampler.Resample(
            chunk.ChannelDataForWrite<float>(i), outFrames, i);
      } else {
        *aHasUnderrun |= mResampler.Resample(
            chunk.ChannelDataForWrite<int16_t>(i), outFrames, i);
      }
    }
    chunk.mDuration = outFrames;

    // Create a copy in order to consume that copy and not the pre-allocated
    // chunk
    segment.AppendAndConsumeChunk(AudioChunk(chunk));
  }

  return segment;
}

void AudioResampler::Update(uint32_t aOutRate, uint32_t aChannels) {
  mResampler.UpdateResampler(aOutRate, aChannels);
  mOutputChunks.Update(aChannels);
}

uint32_t AudioResampler::InputCapacityFrames() const {
  return mResampler.InFramesBufferSize();
}

uint32_t AudioResampler::InputReadableFrames() const {
  return mResampler.InFramesBuffered(0);
}

}  // namespace mozilla