DynamicResampler.cpp (9891B)
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/. */ 5 6 #include "DynamicResampler.h" 7 8 namespace mozilla { 9 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); 20 } 21 22 DynamicResampler::~DynamicResampler() { 23 if (mResampler) { 24 speex_resampler_destroy(mResampler); 25 } 26 } 27 28 void DynamicResampler::SetSampleFormat(AudioSampleFormat aFormat) { 29 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_SILENCE); 30 MOZ_ASSERT(aFormat == AUDIO_FORMAT_S16 || aFormat == AUDIO_FORMAT_FLOAT32); 31 32 mSampleFormat = aFormat; 33 for (AudioRingBuffer& b : mInternalInBuffer) { 34 b.SetSampleFormat(mSampleFormat); 35 } 36 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); 41 } 42 43 void DynamicResampler::EnsurePreBuffer(media::TimeUnit aDuration) { 44 if (mIsPreBufferSet) { 45 return; 46 } 47 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; 56 } 57 58 mIsPreBufferSet = true; 59 60 uint32_t needed = 61 aDuration.ToTicksAtRate(mInRate) + mInputPreBufferFrameCount; 62 EnsureInputBufferSizeInFrames(needed); 63 64 if (needed > buffered) { 65 for (auto& b : mInternalInBuffer) { 66 b.PrependSilence(needed - buffered); 67 } 68 } else if (needed < buffered) { 69 for (auto& b : mInternalInBuffer) { 70 b.Discard(buffered - needed); 71 } 72 } 73 } 74 75 void DynamicResampler::SetInputPreBufferFrameCount( 76 uint32_t aInputPreBufferFrameCount) { 77 mInputPreBufferFrameCount = aInputPreBufferFrameCount; 78 } 79 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); 84 } 85 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); 90 } 91 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); 100 101 MOZ_ASSERT(aInFrames); 102 MOZ_ASSERT(*aInFrames > 0); 103 MOZ_ASSERT(aOutBuffer); 104 MOZ_ASSERT(aOutFrames); 105 MOZ_ASSERT(*aOutFrames > 0); 106 107 MOZ_ASSERT(aChannelIndex <= mChannels); 108 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); 115 116 if (aChannelIndex == 0 && !mIsWarmingUp) { 117 mInputStreamFile.Write(aInBuffer, *aInFrames); 118 mOutputStreamFile.Write(aOutBuffer, *aOutFrames); 119 } 120 } 121 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); 131 132 MOZ_ASSERT(aInFrames); 133 MOZ_ASSERT(*aInFrames > 0); 134 MOZ_ASSERT(aOutBuffer); 135 MOZ_ASSERT(aOutFrames); 136 MOZ_ASSERT(*aOutFrames > 0); 137 138 MOZ_ASSERT(aChannelIndex <= mChannels); 139 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); 146 147 if (aChannelIndex == 0 && !mIsWarmingUp) { 148 mInputStreamFile.Write(aInBuffer, *aInFrames); 149 mOutputStreamFile.Write(aOutBuffer, *aOutFrames); 150 } 151 } 152 153 void DynamicResampler::UpdateResampler(uint32_t aInRate, uint32_t aChannels) { 154 MOZ_ASSERT(aInRate); 155 MOZ_ASSERT(aChannels); 156 157 if (mChannels != aChannels) { 158 uint32_t bufferSizeInFrames = InFramesBufferSize(); 159 if (mResampler) { 160 speex_resampler_destroy(mResampler); 161 } 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); 182 } 183 } 184 // Maintain stereo size 185 mInputTail.SetLength(STEREO); 186 WarmUpResampler(false); 187 return; 188 } 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); 194 195 if (mSampleFormat != AUDIO_FORMAT_SILENCE) { 196 // In ctor this update is not needed 197 b->SetSampleFormat(mSampleFormat); 198 } 199 } 200 EnsureInputBufferSizeInFrames(bufferSizeInFrames); 201 mInputTail.SetLength(mChannels); 202 return; 203 } 204 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); 210 } 211 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; 218 } 219 } 220 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; 227 } 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()); 240 } 241 } 242 if (aSkipLatency) { 243 // Don't generate output frames corresponding to times before the next 244 // input sample. 245 speex_resampler_skip_zeros(mResampler); 246 } 247 mIsWarmingUp = false; 248 } 249 250 void DynamicResampler::AppendInput(Span<const float* const> aInBuffer, 251 uint32_t aInFrames) { 252 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_FLOAT32); 253 AppendInputInternal(aInBuffer, aInFrames); 254 } 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); 259 } 260 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); 267 } 268 } 269 270 uint32_t DynamicResampler::InFramesBufferSize() const { 271 if (mSampleFormat == AUDIO_FORMAT_SILENCE) { 272 return 0; 273 } 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()); 279 } 280 return min; 281 } 282 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; 289 } 290 return mInternalInBuffer[aChannelIndex].AvailableRead(); 291 } 292 293 } // namespace mozilla