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 media::TimeUnit aPreBufferDuration
)
13 mPreBufferDuration(aPreBufferDuration
),
17 MOZ_ASSERT(aPreBufferDuration
.IsPositiveOrZero());
18 UpdateResampler(mOutRate
, STEREO
);
19 mInputStreamFile
.Open("DynamicResamplerInFirstChannel", 1, mInRate
);
20 mOutputStreamFile
.Open("DynamicResamplerOutFirstChannel", 1, mOutRate
);
23 DynamicResampler::~DynamicResampler() {
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
) {
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
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
,
92 uint32_t aChannelIndex
) {
93 MOZ_ASSERT(mResampler
);
94 MOZ_ASSERT(mChannels
);
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
);
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
,
122 uint32_t* aOutFrames
,
123 uint32_t aChannelIndex
) {
124 MOZ_ASSERT(mResampler
);
125 MOZ_ASSERT(mChannels
);
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
);
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
) {
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
;
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);
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
);
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);
211 speex_resampler_set_rate(mResampler
, mInRate
, aOutRate
);
212 MOZ_ASSERT(rv
== RESAMPLER_ERR_SUCCESS
);
217 void DynamicResampler::WarmUpResampler(bool aSkipLatency
) {
218 MOZ_ASSERT(mInputTail
.Length());
220 for (uint32_t i
= 0; i
< mChannels
; ++i
) {
221 if (!mInputTail
[i
].Length()) {
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
,
230 MOZ_ASSERT(inFrames
== (uint32_t)mInputTail
[i
].Length());
232 float outBuffer
[100] = {};
233 ResampleInternal(mInputTail
[i
].Buffer
<float>(), &inFrames
, outBuffer
,
235 MOZ_ASSERT(inFrames
== (uint32_t)mInputTail
[i
].Length());
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