Bug 1891885 don't use mInternalInBuffer to drain resampler r=pehrsons
[gecko.git] / dom / media / driftcontrol / DynamicResampler.cpp
bloba2eddb94fbb8f957d8c42209549205f27005af0b
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/. */
6 #include "DynamicResampler.h"
8 namespace mozilla {
10 DynamicResampler::DynamicResampler(uint32_t aInRate, uint32_t aOutRate,
11 media::TimeUnit aPreBufferDuration)
12 : mInRate(aInRate),
13 mPreBufferDuration(aPreBufferDuration),
14 mOutRate(aOutRate) {
15 MOZ_ASSERT(aInRate);
16 MOZ_ASSERT(aOutRate);
17 MOZ_ASSERT(aPreBufferDuration.IsPositiveOrZero());
18 UpdateResampler(mOutRate, STEREO);
19 mInputStreamFile.Open("DynamicResamplerInFirstChannel", 1, mInRate);
20 mOutputStreamFile.Open("DynamicResamplerOutFirstChannel", 1, mOutRate);
23 DynamicResampler::~DynamicResampler() {
24 if (mResampler) {
25 speex_resampler_destroy(mResampler);
29 void DynamicResampler::SetSampleFormat(AudioSampleFormat aFormat) {
30 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_SILENCE);
31 MOZ_ASSERT(aFormat == AUDIO_FORMAT_S16 || aFormat == AUDIO_FORMAT_FLOAT32);
33 mSampleFormat = aFormat;
34 for (AudioRingBuffer& b : mInternalInBuffer) {
35 b.SetSampleFormat(mSampleFormat);
38 EnsureInputBufferDuration(CalculateInputBufferDuration());
41 void DynamicResampler::EnsurePreBuffer(media::TimeUnit aDuration) {
42 if (mIsPreBufferSet) {
43 return;
46 media::TimeUnit buffered(mInternalInBuffer[0].AvailableRead(), mInRate);
47 if (buffered.IsZero()) {
48 // Wait for the first input segment before deciding how much to pre-buffer.
49 // If it is large it indicates high-latency, and the buffer would have to
50 // handle that. This also means that the pre-buffer is not set up just
51 // before a large input segment would extend the buffering beyond the
52 // desired level.
53 return;
56 mIsPreBufferSet = true;
58 media::TimeUnit needed = aDuration + mPreBufferDuration;
59 EnsureInputBufferDuration(needed);
61 if (needed > buffered) {
62 for (auto& b : mInternalInBuffer) {
63 b.PrependSilence((needed - buffered).ToTicksAtRate(mInRate));
65 } else if (needed < buffered) {
66 for (auto& b : mInternalInBuffer) {
67 b.Discard((buffered - needed).ToTicksAtRate(mInRate));
72 void DynamicResampler::SetPreBufferDuration(media::TimeUnit aDuration) {
73 MOZ_ASSERT(aDuration.IsPositive());
74 mPreBufferDuration = aDuration;
77 bool DynamicResampler::Resample(float* aOutBuffer, uint32_t aOutFrames,
78 uint32_t aChannelIndex) {
79 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_FLOAT32);
80 return ResampleInternal(aOutBuffer, aOutFrames, aChannelIndex);
83 bool DynamicResampler::Resample(int16_t* aOutBuffer, uint32_t aOutFrames,
84 uint32_t aChannelIndex) {
85 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_S16);
86 return ResampleInternal(aOutBuffer, aOutFrames, aChannelIndex);
89 void DynamicResampler::ResampleInternal(const float* aInBuffer,
90 uint32_t* aInFrames, float* aOutBuffer,
91 uint32_t* aOutFrames,
92 uint32_t aChannelIndex) {
93 MOZ_ASSERT(mResampler);
94 MOZ_ASSERT(mChannels);
95 MOZ_ASSERT(mInRate);
96 MOZ_ASSERT(mOutRate);
98 MOZ_ASSERT(aInFrames);
99 MOZ_ASSERT(*aInFrames > 0);
100 MOZ_ASSERT(aOutBuffer);
101 MOZ_ASSERT(aOutFrames);
102 MOZ_ASSERT(*aOutFrames > 0);
104 MOZ_ASSERT(aChannelIndex <= mChannels);
106 #ifdef DEBUG
107 int rv =
108 #endif
109 speex_resampler_process_float(mResampler, aChannelIndex, aInBuffer,
110 aInFrames, aOutBuffer, aOutFrames);
111 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
113 if (aChannelIndex == 0 && !mIsWarmingUp) {
114 mInputStreamFile.Write(aInBuffer, *aInFrames);
115 mOutputStreamFile.Write(aOutBuffer, *aOutFrames);
119 void DynamicResampler::ResampleInternal(const int16_t* aInBuffer,
120 uint32_t* aInFrames,
121 int16_t* aOutBuffer,
122 uint32_t* aOutFrames,
123 uint32_t aChannelIndex) {
124 MOZ_ASSERT(mResampler);
125 MOZ_ASSERT(mChannels);
126 MOZ_ASSERT(mInRate);
127 MOZ_ASSERT(mOutRate);
129 MOZ_ASSERT(aInFrames);
130 MOZ_ASSERT(*aInFrames > 0);
131 MOZ_ASSERT(aOutBuffer);
132 MOZ_ASSERT(aOutFrames);
133 MOZ_ASSERT(*aOutFrames > 0);
135 MOZ_ASSERT(aChannelIndex <= mChannels);
137 #ifdef DEBUG
138 int rv =
139 #endif
140 speex_resampler_process_int(mResampler, aChannelIndex, aInBuffer,
141 aInFrames, aOutBuffer, aOutFrames);
142 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
144 if (aChannelIndex == 0 && !mIsWarmingUp) {
145 mInputStreamFile.Write(aInBuffer, *aInFrames);
146 mOutputStreamFile.Write(aOutBuffer, *aOutFrames);
150 void DynamicResampler::UpdateResampler(uint32_t aOutRate, uint32_t aChannels) {
151 MOZ_ASSERT(aOutRate);
152 MOZ_ASSERT(aChannels);
154 if (mChannels != aChannels) {
155 if (mResampler) {
156 speex_resampler_destroy(mResampler);
158 mResampler = speex_resampler_init(aChannels, mInRate, aOutRate,
159 SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
160 MOZ_ASSERT(mResampler);
161 mChannels = aChannels;
162 mOutRate = aOutRate;
163 // Between mono and stereo changes, keep always allocated 2 channels to
164 // avoid reallocations in the most common case.
165 if ((mChannels == STEREO || mChannels == 1) &&
166 mInternalInBuffer.Length() == STEREO) {
167 // Don't worry if format is not set it will write silence then.
168 if ((mSampleFormat == AUDIO_FORMAT_S16 ||
169 mSampleFormat == AUDIO_FORMAT_FLOAT32) &&
170 mChannels == STEREO) {
171 // The mono channel is always up to date. When we are going from mono
172 // to stereo upmix the mono to stereo channel
173 uint32_t bufferedDuration = mInternalInBuffer[0].AvailableRead();
174 mInternalInBuffer[1].Clear();
175 if (bufferedDuration) {
176 mInternalInBuffer[1].Write(mInternalInBuffer[0], bufferedDuration);
179 // Maintain stereo size
180 mInputTail.SetLength(STEREO);
181 WarmUpResampler(false);
182 return;
184 // upmix or downmix, for now just clear but it has to be updated
185 // because allocates and this is executed in audio thread.
186 mInternalInBuffer.Clear();
187 for (uint32_t i = 0; i < mChannels; ++i) {
188 AudioRingBuffer* b = mInternalInBuffer.AppendElement(0);
190 if (mSampleFormat != AUDIO_FORMAT_SILENCE) {
191 // In ctor this update is not needed
192 b->SetSampleFormat(mSampleFormat);
195 media::TimeUnit d = mSetBufferDuration;
196 mSetBufferDuration = media::TimeUnit::Zero();
197 EnsureInputBufferDuration(d);
198 mInputTail.SetLength(mChannels);
199 return;
202 if (mOutRate != aOutRate) {
203 // If the rates was the same the resampler was not being used so warm up.
204 if (mOutRate == mInRate) {
205 WarmUpResampler(true);
208 #ifdef DEBUG
209 int rv =
210 #endif
211 speex_resampler_set_rate(mResampler, mInRate, aOutRate);
212 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
213 mOutRate = aOutRate;
217 void DynamicResampler::WarmUpResampler(bool aSkipLatency) {
218 MOZ_ASSERT(mInputTail.Length());
219 mIsWarmingUp = true;
220 for (uint32_t i = 0; i < mChannels; ++i) {
221 if (!mInputTail[i].Length()) {
222 continue;
224 uint32_t inFrames = mInputTail[i].Length();
225 uint32_t outFrames = 5 * TailBuffer::MAXSIZE; // something big
226 if (mSampleFormat == AUDIO_FORMAT_S16) {
227 short outBuffer[5 * TailBuffer::MAXSIZE] = {};
228 ResampleInternal(mInputTail[i].Buffer<short>(), &inFrames, outBuffer,
229 &outFrames, i);
230 MOZ_ASSERT(inFrames == (uint32_t)mInputTail[i].Length());
231 } else {
232 float outBuffer[100] = {};
233 ResampleInternal(mInputTail[i].Buffer<float>(), &inFrames, outBuffer,
234 &outFrames, i);
235 MOZ_ASSERT(inFrames == (uint32_t)mInputTail[i].Length());
238 if (aSkipLatency) {
239 int inputLatency = speex_resampler_get_input_latency(mResampler);
240 MOZ_ASSERT(inputLatency > 0);
241 uint32_t ratioNum, ratioDen;
242 speex_resampler_get_ratio(mResampler, &ratioNum, &ratioDen);
243 // Ratio at this point is one so only skip the input latency. No special
244 // calculations are needed.
245 speex_resampler_set_skip_frac_num(mResampler, inputLatency * ratioDen);
247 mIsWarmingUp = false;
250 void DynamicResampler::AppendInput(Span<const float* const> aInBuffer,
251 uint32_t aInFrames) {
252 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_FLOAT32);
253 AppendInputInternal(aInBuffer, aInFrames);
255 void DynamicResampler::AppendInput(Span<const int16_t* const> aInBuffer,
256 uint32_t aInFrames) {
257 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_S16);
258 AppendInputInternal(aInBuffer, aInFrames);
261 void DynamicResampler::AppendInputSilence(const uint32_t aInFrames) {
262 MOZ_ASSERT(aInFrames);
263 MOZ_ASSERT(mChannels);
264 MOZ_ASSERT(mInternalInBuffer.Length() >= (uint32_t)mChannels);
265 for (uint32_t i = 0; i < mChannels; ++i) {
266 mInternalInBuffer[i].WriteSilence(aInFrames);
270 uint32_t DynamicResampler::InFramesBufferSize() const {
271 return mSetBufferDuration.ToTicksAtRate(mInRate);
274 uint32_t DynamicResampler::InFramesBuffered(uint32_t aChannelIndex) const {
275 MOZ_ASSERT(mChannels);
276 MOZ_ASSERT(aChannelIndex <= mChannels);
277 MOZ_ASSERT(aChannelIndex <= mInternalInBuffer.Length());
278 if (!mIsPreBufferSet) {
279 return mPreBufferDuration.ToTicksAtRate(mInRate);
281 return mInternalInBuffer[aChannelIndex].AvailableRead();
284 } // namespace mozilla