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"
10 DynamicResampler::DynamicResampler(uint32_t aInRate
, uint32_t aOutRate
,
11 uint32_t aPreBufferFrames
)
12 : mInRate(aInRate
), mPreBufferFrames(aPreBufferFrames
), mOutRate(aOutRate
) {
15 UpdateResampler(mOutRate
, STEREO
);
18 DynamicResampler::~DynamicResampler() {
20 speex_resampler_destroy(mResampler
);
24 void DynamicResampler::SetSampleFormat(AudioSampleFormat aFormat
) {
25 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_SILENCE
);
26 MOZ_ASSERT(aFormat
== AUDIO_FORMAT_S16
|| aFormat
== AUDIO_FORMAT_FLOAT32
);
28 mSampleFormat
= aFormat
;
29 for (AudioRingBuffer
& b
: mInternalInBuffer
) {
30 b
.SetSampleFormat(mSampleFormat
);
32 if (mPreBufferFrames
) {
33 AppendInputSilence(mPreBufferFrames
);
37 bool DynamicResampler::Resample(float* aOutBuffer
, uint32_t* aOutFrames
,
38 uint32_t aChannelIndex
) {
39 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_FLOAT32
);
40 return ResampleInternal(aOutBuffer
, aOutFrames
, aChannelIndex
);
43 bool DynamicResampler::Resample(int16_t* aOutBuffer
, uint32_t* aOutFrames
,
44 uint32_t aChannelIndex
) {
45 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_S16
);
46 return ResampleInternal(aOutBuffer
, aOutFrames
, aChannelIndex
);
49 void DynamicResampler::ResampleInternal(const float* aInBuffer
,
50 uint32_t* aInFrames
, float* aOutBuffer
,
52 uint32_t aChannelIndex
) {
53 MOZ_ASSERT(mResampler
);
54 MOZ_ASSERT(mChannels
);
58 MOZ_ASSERT(aInBuffer
);
59 MOZ_ASSERT(aInFrames
);
60 MOZ_ASSERT(*aInFrames
> 0);
61 MOZ_ASSERT(aOutBuffer
);
62 MOZ_ASSERT(aOutFrames
);
63 MOZ_ASSERT(*aOutFrames
> 0);
65 MOZ_ASSERT(aChannelIndex
<= mChannels
);
70 speex_resampler_process_float(mResampler
, aChannelIndex
, aInBuffer
,
71 aInFrames
, aOutBuffer
, aOutFrames
);
72 MOZ_ASSERT(rv
== RESAMPLER_ERR_SUCCESS
);
75 void DynamicResampler::ResampleInternal(const int16_t* aInBuffer
,
79 uint32_t aChannelIndex
) {
80 MOZ_ASSERT(mResampler
);
81 MOZ_ASSERT(mChannels
);
85 MOZ_ASSERT(aInBuffer
);
86 MOZ_ASSERT(aInFrames
);
87 MOZ_ASSERT(*aInFrames
> 0);
88 MOZ_ASSERT(aOutBuffer
);
89 MOZ_ASSERT(aOutFrames
);
90 MOZ_ASSERT(*aOutFrames
> 0);
92 MOZ_ASSERT(aChannelIndex
<= mChannels
);
97 speex_resampler_process_int(mResampler
, aChannelIndex
, aInBuffer
,
98 aInFrames
, aOutBuffer
, aOutFrames
);
99 MOZ_ASSERT(rv
== RESAMPLER_ERR_SUCCESS
);
102 void DynamicResampler::UpdateResampler(uint32_t aOutRate
, uint32_t aChannels
) {
103 MOZ_ASSERT(aOutRate
);
104 MOZ_ASSERT(aChannels
);
106 if (mChannels
!= aChannels
) {
108 speex_resampler_destroy(mResampler
);
110 mResampler
= speex_resampler_init(aChannels
, mInRate
, aOutRate
,
111 SPEEX_RESAMPLER_QUALITY_MIN
, nullptr);
112 MOZ_ASSERT(mResampler
);
113 mChannels
= aChannels
;
115 // Between mono and stereo changes, keep always allocated 2 channels to
116 // avoid reallocations in the most common case.
117 if ((mChannels
== STEREO
|| mChannels
== 1) &&
118 mInternalInBuffer
.Length() == STEREO
) {
119 // Don't worry if format is not set it will write silence then.
120 if ((mSampleFormat
== AUDIO_FORMAT_S16
||
121 mSampleFormat
== AUDIO_FORMAT_FLOAT32
) &&
122 mChannels
== STEREO
) {
123 // The mono channel is always up to date. When we are going from mono
124 // to stereo upmix the mono to stereo channel
125 uint32_t bufferedDuration
= mInternalInBuffer
[0].AvailableRead();
126 mInternalInBuffer
[1].Clear();
127 if (bufferedDuration
) {
128 mInternalInBuffer
[1].Write(mInternalInBuffer
[0], bufferedDuration
);
131 // Maintain stereo size
132 mInputTail
.SetLength(STEREO
);
133 WarmUpResampler(false);
136 // upmix or downmix, for now just clear but it has to be updated
137 // because allocates and this is executed in audio thread.
138 mInternalInBuffer
.Clear();
139 for (uint32_t i
= 0; i
< mChannels
; ++i
) {
140 // Pre-allocate something big, twice the pre-buffer, or at least 100ms.
141 AudioRingBuffer
* b
= mInternalInBuffer
.AppendElement(
142 sizeof(float) * std::max(2 * mPreBufferFrames
, mInRate
/ 10));
143 if (mSampleFormat
!= AUDIO_FORMAT_SILENCE
) {
144 // In ctor this update is not needed
145 b
->SetSampleFormat(mSampleFormat
);
148 mInputTail
.SetLength(mChannels
);
152 if (mOutRate
!= aOutRate
) {
153 // If the rates was the same the resampler was not being used so warm up.
154 if (mOutRate
== mInRate
) {
155 WarmUpResampler(true);
161 speex_resampler_set_rate(mResampler
, mInRate
, aOutRate
);
162 MOZ_ASSERT(rv
== RESAMPLER_ERR_SUCCESS
);
167 void DynamicResampler::WarmUpResampler(bool aSkipLatency
) {
168 MOZ_ASSERT(mInputTail
.Length());
169 for (uint32_t i
= 0; i
< mChannels
; ++i
) {
170 if (!mInputTail
[i
].Length()) {
173 uint32_t inFrames
= mInputTail
[i
].Length();
174 uint32_t outFrames
= 5 * TailBuffer::MAXSIZE
; // something big
175 if (mSampleFormat
== AUDIO_FORMAT_S16
) {
176 short outBuffer
[5 * TailBuffer::MAXSIZE
] = {};
177 ResampleInternal(mInputTail
[i
].Buffer
<short>(), &inFrames
, outBuffer
,
179 MOZ_ASSERT(inFrames
== (uint32_t)mInputTail
[i
].Length());
181 float outBuffer
[100] = {};
182 ResampleInternal(mInputTail
[i
].Buffer
<float>(), &inFrames
, outBuffer
,
184 MOZ_ASSERT(inFrames
== (uint32_t)mInputTail
[i
].Length());
188 int inputLatency
= speex_resampler_get_input_latency(mResampler
);
189 MOZ_ASSERT(inputLatency
> 0);
190 uint32_t ratioNum
, ratioDen
;
191 speex_resampler_get_ratio(mResampler
, &ratioNum
, &ratioDen
);
192 // Ratio at this point is one so only skip the input latency. No special
193 // calculations are needed.
194 speex_resampler_set_skip_frac_num(mResampler
, inputLatency
* ratioDen
);
198 void DynamicResampler::AppendInput(const nsTArray
<const float*>& aInBuffer
,
199 uint32_t aInFrames
) {
200 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_FLOAT32
);
201 AppendInputInternal(aInBuffer
, aInFrames
);
203 void DynamicResampler::AppendInput(const nsTArray
<const int16_t*>& aInBuffer
,
204 uint32_t aInFrames
) {
205 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_S16
);
206 AppendInputInternal(aInBuffer
, aInFrames
);
209 bool DynamicResampler::EnoughInFrames(uint32_t aOutFrames
,
210 uint32_t aChannelIndex
) const {
211 if (mInRate
== mOutRate
) {
212 return InFramesBuffered(aChannelIndex
) >= aOutFrames
;
214 if (!(mOutRate
% mInRate
) && !(aOutFrames
% mOutRate
/ mInRate
)) {
215 return InFramesBuffered(aChannelIndex
) >= aOutFrames
/ (mOutRate
/ mInRate
);
217 if (!(mInRate
% mOutRate
) && !(aOutFrames
% mOutRate
/ mInRate
)) {
218 return InFramesBuffered(aChannelIndex
) >= aOutFrames
* mInRate
/ mOutRate
;
220 return InFramesBuffered(aChannelIndex
) > aOutFrames
* mInRate
/ mOutRate
;
223 bool DynamicResampler::CanResample(uint32_t aOutFrames
) const {
224 for (uint32_t i
= 0; i
< mChannels
; ++i
) {
225 if (!EnoughInFrames(aOutFrames
, i
)) {
232 void DynamicResampler::AppendInputSilence(const uint32_t aInFrames
) {
233 MOZ_ASSERT(aInFrames
);
234 MOZ_ASSERT(mChannels
);
235 MOZ_ASSERT(mInternalInBuffer
.Length() >= (uint32_t)mChannels
);
236 for (uint32_t i
= 0; i
< mChannels
; ++i
) {
237 mInternalInBuffer
[i
].WriteSilence(aInFrames
);
241 uint32_t DynamicResampler::InFramesBuffered(uint32_t aChannelIndex
) const {
242 MOZ_ASSERT(mChannels
);
243 MOZ_ASSERT(aChannelIndex
<= mChannels
);
244 MOZ_ASSERT(aChannelIndex
<= mInternalInBuffer
.Length());
245 return mInternalInBuffer
[aChannelIndex
].AvailableRead();
248 uint32_t DynamicResampler::InFramesLeftToBuffer(uint32_t aChannelIndex
) const {
249 MOZ_ASSERT(mChannels
);
250 MOZ_ASSERT(aChannelIndex
<= mChannels
);
251 MOZ_ASSERT(aChannelIndex
<= mInternalInBuffer
.Length());
252 return mInternalInBuffer
[aChannelIndex
].AvailableWrite();
255 AudioChunkList::AudioChunkList(uint32_t aTotalDuration
, uint32_t aChannels
,
256 const PrincipalHandle
& aPrincipalHandle
)
257 : mPrincipalHandle(aPrincipalHandle
) {
258 uint32_t numOfChunks
= aTotalDuration
/ mChunkCapacity
;
259 if (aTotalDuration
% mChunkCapacity
) {
262 CreateChunks(numOfChunks
, aChannels
);
265 void AudioChunkList::CreateChunks(uint32_t aNumOfChunks
, uint32_t aChannels
) {
266 MOZ_ASSERT(!mChunks
.Length());
267 MOZ_ASSERT(aNumOfChunks
);
268 MOZ_ASSERT(aChannels
);
269 mChunks
.AppendElements(aNumOfChunks
);
271 for (AudioChunk
& chunk
: mChunks
) {
272 AutoTArray
<nsTArray
<float>, STEREO
> buffer
;
273 buffer
.AppendElements(aChannels
);
275 AutoTArray
<const float*, STEREO
> bufferPtrs
;
276 bufferPtrs
.AppendElements(aChannels
);
278 for (uint32_t i
= 0; i
< aChannels
; ++i
) {
279 float* ptr
= buffer
[i
].AppendElements(mChunkCapacity
);
283 chunk
.mBuffer
= new mozilla::SharedChannelArrayBuffer(std::move(buffer
));
284 chunk
.mChannelData
.AppendElements(aChannels
);
285 for (uint32_t i
= 0; i
< aChannels
; ++i
) {
286 chunk
.mChannelData
[i
] = bufferPtrs
[i
];
291 void AudioChunkList::UpdateToMonoOrStereo(uint32_t aChannels
) {
292 MOZ_ASSERT(mChunks
.Length());
293 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_S16
||
294 mSampleFormat
== AUDIO_FORMAT_FLOAT32
);
295 MOZ_ASSERT(aChannels
== 1 || aChannels
== 2);
297 for (AudioChunk
& chunk
: mChunks
) {
298 MOZ_ASSERT(chunk
.ChannelCount() != (uint32_t)aChannels
);
299 MOZ_ASSERT(chunk
.ChannelCount() == 1 || chunk
.ChannelCount() == 2);
300 chunk
.mChannelData
.SetLengthAndRetainStorage(aChannels
);
301 if (mSampleFormat
== AUDIO_FORMAT_S16
) {
302 SharedChannelArrayBuffer
<short>* channelArray
=
303 static_cast<SharedChannelArrayBuffer
<short>*>(chunk
.mBuffer
.get());
304 channelArray
->mBuffers
.SetLengthAndRetainStorage(aChannels
);
305 if (aChannels
== 2) {
306 // This an indirect allocation, unfortunately.
307 channelArray
->mBuffers
[1].SetLength(mChunkCapacity
);
308 chunk
.mChannelData
[1] = channelArray
->mBuffers
[1].Elements();
311 SharedChannelArrayBuffer
<float>* channelArray
=
312 static_cast<SharedChannelArrayBuffer
<float>*>(chunk
.mBuffer
.get());
313 channelArray
->mBuffers
.SetLengthAndRetainStorage(aChannels
);
314 if (aChannels
== 2) {
315 // This an indirect allocation, unfortunately.
316 channelArray
->mBuffers
[1].SetLength(mChunkCapacity
);
317 chunk
.mChannelData
[1] = channelArray
->mBuffers
[1].Elements();
323 void AudioChunkList::SetSampleFormat(AudioSampleFormat aFormat
) {
324 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_SILENCE
);
325 MOZ_ASSERT(aFormat
== AUDIO_FORMAT_S16
|| aFormat
== AUDIO_FORMAT_FLOAT32
);
326 mSampleFormat
= aFormat
;
327 if (mSampleFormat
== AUDIO_FORMAT_S16
) {
328 mChunkCapacity
= 2 * mChunkCapacity
;
332 AudioChunk
& AudioChunkList::GetNext() {
333 AudioChunk
& chunk
= mChunks
[mIndex
];
334 MOZ_ASSERT(!chunk
.mChannelData
.IsEmpty());
335 MOZ_ASSERT(chunk
.mBuffer
);
336 MOZ_ASSERT(!chunk
.mBuffer
->IsShared());
337 MOZ_ASSERT(mSampleFormat
== AUDIO_FORMAT_S16
||
338 mSampleFormat
== AUDIO_FORMAT_FLOAT32
);
340 chunk
.mVolume
= 1.0f
;
341 chunk
.mPrincipalHandle
= mPrincipalHandle
;
342 chunk
.mBufferFormat
= mSampleFormat
;
347 void AudioChunkList::Update(uint32_t aChannels
) {
348 MOZ_ASSERT(mChunks
.Length());
349 if (mChunks
[0].ChannelCount() == aChannels
) {
353 // Special handling between mono and stereo to avoid reallocations.
354 if (aChannels
<= 2 && mChunks
[0].ChannelCount() <= 2) {
355 UpdateToMonoOrStereo(aChannels
);
359 uint32_t numOfChunks
= mChunks
.Length();
360 mChunks
.ClearAndRetainStorage();
361 CreateChunks(numOfChunks
, aChannels
);
364 AudioResampler::AudioResampler(uint32_t aInRate
, uint32_t aOutRate
,
365 uint32_t aPreBufferFrames
,
366 const PrincipalHandle
& aPrincipalHandle
)
367 : mResampler(aInRate
, aOutRate
, aPreBufferFrames
),
368 mOutputChunks(aOutRate
/ 10, STEREO
, aPrincipalHandle
) {}
370 void AudioResampler::AppendInput(const AudioSegment
& aInSegment
) {
371 MOZ_ASSERT(aInSegment
.GetDuration());
372 for (AudioSegment::ConstChunkIterator
iter(aInSegment
); !iter
.IsEnded();
374 const AudioChunk
& chunk
= *iter
;
375 if (!mIsSampleFormatSet
) {
376 // We don't know the format yet and all buffers are empty.
377 if (chunk
.mBufferFormat
== AUDIO_FORMAT_SILENCE
) {
378 // Only silence has been received and the format is unkown. Igonre it,
379 // if Resampler() is called it will return silence too.
382 // First no silence data, set the format once for lifetime and let it
383 // continue the rest of the flow. We will not get in here again.
384 mOutputChunks
.SetSampleFormat(chunk
.mBufferFormat
);
385 mResampler
.SetSampleFormat(chunk
.mBufferFormat
);
386 mIsSampleFormatSet
= true;
388 MOZ_ASSERT(mIsSampleFormatSet
);
389 if (chunk
.IsNull()) {
390 mResampler
.AppendInputSilence(chunk
.GetDuration());
393 // Make sure the channel is up to date. An AudioSegment can contain chunks
394 // with different channel count.
395 UpdateChannels(chunk
.mChannelData
.Length());
396 if (chunk
.mBufferFormat
== AUDIO_FORMAT_FLOAT32
) {
397 mResampler
.AppendInput(chunk
.ChannelData
<float>(), chunk
.GetDuration());
399 mResampler
.AppendInput(chunk
.ChannelData
<int16_t>(), chunk
.GetDuration());
404 AudioSegment
AudioResampler::Resample(uint32_t aOutFrames
) {
405 AudioSegment segment
;
407 // We don't know what to do yet and we only have received silence if any just
408 // return what they want and leave
409 if (!mIsSampleFormatSet
) {
410 segment
.AppendNullData(aOutFrames
);
414 // Not enough input frames abort. We check for the requested frames plus one.
415 // This is to make sure that the individual resample iteration that will
416 // follow up, will have enough frames even if one of them consume an extra
418 if (!mResampler
.CanResample(aOutFrames
+ 1)) {
422 uint32_t totalFrames
= aOutFrames
;
423 while (totalFrames
) {
424 MOZ_ASSERT(totalFrames
> 0);
425 AudioChunk
& chunk
= mOutputChunks
.GetNext();
426 uint32_t outFrames
= std::min(totalFrames
, mOutputChunks
.ChunkCapacity());
427 totalFrames
-= outFrames
;
429 for (uint32_t i
= 0; i
< chunk
.ChannelCount(); ++i
) {
430 uint32_t outFramesUsed
= outFrames
;
431 if (chunk
.mBufferFormat
== AUDIO_FORMAT_FLOAT32
) {
435 mResampler
.Resample(chunk
.ChannelDataForWrite
<float>(i
),
442 mResampler
.Resample(chunk
.ChannelDataForWrite
<int16_t>(i
),
446 MOZ_ASSERT(outFramesUsed
== outFrames
);
447 chunk
.mDuration
= outFrames
;
450 // Create a copy in order to consume that copy and not the pre-allocated
452 segment
.AppendAndConsumeChunk(AudioChunk(chunk
));
458 void AudioResampler::Update(uint32_t aOutRate
, uint32_t aChannels
) {
459 mResampler
.UpdateResampler(aOutRate
, aChannels
);
460 mOutputChunks
.Update(aChannels
);
463 uint32_t AudioResampler::InputReadableFrames() const {
464 if (!mIsSampleFormatSet
) {
465 return mResampler
.mPreBufferFrames
;
467 return mResampler
.InFramesBuffered(0);
470 uint32_t AudioResampler::InputWritableFrames() const {
471 if (!mIsSampleFormatSet
) {
472 return mResampler
.mPreBufferFrames
;
474 return mResampler
.InFramesLeftToBuffer(0);
477 } // namespace mozilla