Bug 1943650 - Command-line --help output misformatted after --dbus-service. r=emilio
[gecko.git] / dom / media / driftcontrol / DynamicResampler.cpp
blob29fc5a9c5d81497a060ef58bd930e9ba05e1bf2a
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 uint32_t aInputPreBufferFrameCount)
12 : mOutRate(aOutRate),
13 mInputPreBufferFrameCount(aInputPreBufferFrameCount),
14 mInRate(aInRate) {
15 MOZ_ASSERT(aInRate);
16 MOZ_ASSERT(aOutRate);
17 UpdateResampler(mInRate, STEREO);
18 mInputStreamFile.Open("DynamicResamplerInFirstChannel", 1, mInRate);
19 mOutputStreamFile.Open("DynamicResamplerOutFirstChannel", 1, mOutRate);
22 DynamicResampler::~DynamicResampler() {
23 if (mResampler) {
24 speex_resampler_destroy(mResampler);
28 void DynamicResampler::SetSampleFormat(AudioSampleFormat aFormat) {
29 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_SILENCE);
30 MOZ_ASSERT(aFormat == AUDIO_FORMAT_S16 || aFormat == AUDIO_FORMAT_FLOAT32);
32 mSampleFormat = aFormat;
33 for (AudioRingBuffer& b : mInternalInBuffer) {
34 b.SetSampleFormat(mSampleFormat);
37 // Pre-allocate something big.
38 // EnsureInputBufferDuration() adds 50ms for jitter to this first allocation
39 // so the 50ms argument means at least 100ms.
40 EnsureInputBufferSizeInFrames(mInRate / 20);
43 void DynamicResampler::EnsurePreBuffer(media::TimeUnit aDuration) {
44 if (mIsPreBufferSet) {
45 return;
48 uint32_t buffered = mInternalInBuffer[0].AvailableRead();
49 if (buffered == 0) {
50 // Wait for the first input segment before deciding how much to pre-buffer.
51 // If it is large it indicates high-latency, and the buffer would have to
52 // handle that. This also means that the pre-buffer is not set up just
53 // before a large input segment would extend the buffering beyond the
54 // desired level.
55 return;
58 mIsPreBufferSet = true;
60 uint32_t needed =
61 aDuration.ToTicksAtRate(mInRate) + mInputPreBufferFrameCount;
62 EnsureInputBufferSizeInFrames(needed);
64 if (needed > buffered) {
65 for (auto& b : mInternalInBuffer) {
66 b.PrependSilence(needed - buffered);
68 } else if (needed < buffered) {
69 for (auto& b : mInternalInBuffer) {
70 b.Discard(buffered - needed);
75 void DynamicResampler::SetInputPreBufferFrameCount(
76 uint32_t aInputPreBufferFrameCount) {
77 mInputPreBufferFrameCount = aInputPreBufferFrameCount;
80 bool DynamicResampler::Resample(float* aOutBuffer, uint32_t aOutFrames,
81 uint32_t aChannelIndex) {
82 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_FLOAT32);
83 return ResampleInternal(aOutBuffer, aOutFrames, aChannelIndex);
86 bool DynamicResampler::Resample(int16_t* aOutBuffer, uint32_t aOutFrames,
87 uint32_t aChannelIndex) {
88 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_S16);
89 return ResampleInternal(aOutBuffer, aOutFrames, aChannelIndex);
92 void DynamicResampler::ResampleInternal(const float* aInBuffer,
93 uint32_t* aInFrames, float* aOutBuffer,
94 uint32_t* aOutFrames,
95 uint32_t aChannelIndex) {
96 MOZ_ASSERT(mResampler);
97 MOZ_ASSERT(mChannels);
98 MOZ_ASSERT(mInRate);
99 MOZ_ASSERT(mOutRate);
101 MOZ_ASSERT(aInFrames);
102 MOZ_ASSERT(*aInFrames > 0);
103 MOZ_ASSERT(aOutBuffer);
104 MOZ_ASSERT(aOutFrames);
105 MOZ_ASSERT(*aOutFrames > 0);
107 MOZ_ASSERT(aChannelIndex <= mChannels);
109 #ifdef DEBUG
110 int rv =
111 #endif
112 speex_resampler_process_float(mResampler, aChannelIndex, aInBuffer,
113 aInFrames, aOutBuffer, aOutFrames);
114 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
116 if (aChannelIndex == 0 && !mIsWarmingUp) {
117 mInputStreamFile.Write(aInBuffer, *aInFrames);
118 mOutputStreamFile.Write(aOutBuffer, *aOutFrames);
122 void DynamicResampler::ResampleInternal(const int16_t* aInBuffer,
123 uint32_t* aInFrames,
124 int16_t* aOutBuffer,
125 uint32_t* aOutFrames,
126 uint32_t aChannelIndex) {
127 MOZ_ASSERT(mResampler);
128 MOZ_ASSERT(mChannels);
129 MOZ_ASSERT(mInRate);
130 MOZ_ASSERT(mOutRate);
132 MOZ_ASSERT(aInFrames);
133 MOZ_ASSERT(*aInFrames > 0);
134 MOZ_ASSERT(aOutBuffer);
135 MOZ_ASSERT(aOutFrames);
136 MOZ_ASSERT(*aOutFrames > 0);
138 MOZ_ASSERT(aChannelIndex <= mChannels);
140 #ifdef DEBUG
141 int rv =
142 #endif
143 speex_resampler_process_int(mResampler, aChannelIndex, aInBuffer,
144 aInFrames, aOutBuffer, aOutFrames);
145 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
147 if (aChannelIndex == 0 && !mIsWarmingUp) {
148 mInputStreamFile.Write(aInBuffer, *aInFrames);
149 mOutputStreamFile.Write(aOutBuffer, *aOutFrames);
153 void DynamicResampler::UpdateResampler(uint32_t aInRate, uint32_t aChannels) {
154 MOZ_ASSERT(aInRate);
155 MOZ_ASSERT(aChannels);
157 if (mChannels != aChannels) {
158 uint32_t bufferSizeInFrames = InFramesBufferSize();
159 if (mResampler) {
160 speex_resampler_destroy(mResampler);
162 mResampler = speex_resampler_init(aChannels, aInRate, mOutRate,
163 SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
164 MOZ_ASSERT(mResampler);
165 mChannels = aChannels;
166 mInRate = aInRate;
167 mResamplerIsBypassed &= aInRate == mOutRate;
168 // Between mono and stereo changes, keep always allocated 2 channels to
169 // avoid reallocations in the most common case.
170 if ((mChannels == STEREO || mChannels == 1) &&
171 mInternalInBuffer.Length() == STEREO) {
172 // Don't worry if format is not set it will write silence then.
173 if ((mSampleFormat == AUDIO_FORMAT_S16 ||
174 mSampleFormat == AUDIO_FORMAT_FLOAT32) &&
175 mChannels == STEREO) {
176 // The mono channel is always up to date. When we are going from mono
177 // to stereo upmix the mono to stereo channel
178 uint32_t bufferedDuration = mInternalInBuffer[0].AvailableRead();
179 mInternalInBuffer[1].Clear();
180 if (bufferedDuration) {
181 mInternalInBuffer[1].Write(mInternalInBuffer[0], bufferedDuration);
184 // Maintain stereo size
185 mInputTail.SetLength(STEREO);
186 WarmUpResampler(false);
187 return;
189 // upmix or downmix, for now just clear but it has to be updated
190 // because allocates and this is executed in audio thread.
191 mInternalInBuffer.Clear();
192 for (uint32_t i = 0; i < mChannels; ++i) {
193 AudioRingBuffer* b = mInternalInBuffer.AppendElement(0);
195 if (mSampleFormat != AUDIO_FORMAT_SILENCE) {
196 // In ctor this update is not needed
197 b->SetSampleFormat(mSampleFormat);
200 EnsureInputBufferSizeInFrames(bufferSizeInFrames);
201 mInputTail.SetLength(mChannels);
202 return;
205 if (mInRate != aInRate) {
206 // If the rates was the same the resampler was not being used so warm up.
207 if (mResamplerIsBypassed) {
208 mResamplerIsBypassed = false;
209 WarmUpResampler(true);
212 #ifdef DEBUG
213 int rv =
214 #endif
215 speex_resampler_set_rate(mResampler, aInRate, mOutRate);
216 MOZ_ASSERT(rv == RESAMPLER_ERR_SUCCESS);
217 mInRate = aInRate;
221 void DynamicResampler::WarmUpResampler(bool aSkipLatency) {
222 MOZ_ASSERT(mInputTail.Length());
223 mIsWarmingUp = true;
224 for (uint32_t i = 0; i < mChannels; ++i) {
225 if (!mInputTail[i].Length()) {
226 continue;
228 uint32_t inFrames = mInputTail[i].Length();
229 uint32_t outFrames = 5 * TailBuffer::MAXSIZE; // something big
230 if (mSampleFormat == AUDIO_FORMAT_S16) {
231 short outBuffer[5 * TailBuffer::MAXSIZE] = {};
232 ResampleInternal(mInputTail[i].Buffer<short>(), &inFrames, outBuffer,
233 &outFrames, i);
234 MOZ_ASSERT(inFrames == (uint32_t)mInputTail[i].Length());
235 } else {
236 float outBuffer[100] = {};
237 ResampleInternal(mInputTail[i].Buffer<float>(), &inFrames, outBuffer,
238 &outFrames, i);
239 MOZ_ASSERT(inFrames == (uint32_t)mInputTail[i].Length());
242 if (aSkipLatency) {
243 // Don't generate output frames corresponding to times before the next
244 // input sample.
245 speex_resampler_skip_zeros(mResampler);
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 if (mSampleFormat == AUDIO_FORMAT_SILENCE) {
272 return 0;
274 // Buffers may have different capacities if a memory allocation has failed.
275 MOZ_ASSERT(!mInternalInBuffer.IsEmpty());
276 uint32_t min = std::numeric_limits<uint32_t>::max();
277 for (const auto& b : mInternalInBuffer) {
278 min = std::min(min, b.Capacity());
280 return min;
283 uint32_t DynamicResampler::InFramesBuffered(uint32_t aChannelIndex) const {
284 MOZ_ASSERT(mChannels);
285 MOZ_ASSERT(aChannelIndex <= mChannels);
286 MOZ_ASSERT(aChannelIndex <= mInternalInBuffer.Length());
287 if (!mIsPreBufferSet) {
288 return mInputPreBufferFrameCount;
290 return mInternalInBuffer[aChannelIndex].AvailableRead();
293 } // namespace mozilla