AudioConverter.h (9992B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef AudioConverter_h 8 #define AudioConverter_h 9 10 #include "MediaInfo.h" 11 12 // Forward declaration 13 typedef struct SpeexResamplerState_ SpeexResamplerState; 14 15 namespace mozilla { 16 17 template <AudioConfig::SampleFormat T> 18 struct AudioDataBufferTypeChooser; 19 template <> 20 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8> { 21 typedef uint8_t Type; 22 }; 23 template <> 24 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16> { 25 typedef int16_t Type; 26 }; 27 template <> 28 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB> { 29 typedef int32_t Type; 30 }; 31 template <> 32 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24> { 33 typedef int32_t Type; 34 }; 35 template <> 36 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32> { 37 typedef int32_t Type; 38 }; 39 template <> 40 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT> { 41 typedef float Type; 42 }; 43 44 // 'Value' is the type used externally to deal with stored value. 45 // AudioDataBuffer can perform conversion between different SampleFormat 46 // content. 47 template <AudioConfig::SampleFormat Format, 48 typename Value = typename AudioDataBufferTypeChooser<Format>::Type> 49 class AudioDataBuffer { 50 public: 51 AudioDataBuffer() = default; 52 AudioDataBuffer(Value* aBuffer, size_t aLength) : mBuffer(aBuffer, aLength) {} 53 explicit AudioDataBuffer(const AudioDataBuffer& aOther) 54 : mBuffer(aOther.mBuffer) {} 55 AudioDataBuffer(AudioDataBuffer&& aOther) 56 : mBuffer(std::move(aOther.mBuffer)) {} 57 template <AudioConfig::SampleFormat OtherFormat, typename OtherValue> 58 explicit AudioDataBuffer( 59 const AudioDataBuffer<OtherFormat, OtherValue>& other) { 60 // TODO: Convert from different type, may use asm routines. 61 MOZ_CRASH("Conversion not implemented yet"); 62 } 63 64 // A u8, s16 and float aligned buffer can only be treated as 65 // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively. 66 // So allow them as copy and move constructors. 67 explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer) 68 : mBuffer(aBuffer) { 69 static_assert(Format == AudioConfig::FORMAT_U8, 70 "Conversion not implemented yet"); 71 } 72 explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer) 73 : mBuffer(aBuffer) { 74 static_assert(Format == AudioConfig::FORMAT_S16, 75 "Conversion not implemented yet"); 76 } 77 explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer) 78 : mBuffer(aBuffer) { 79 static_assert(Format == AudioConfig::FORMAT_FLT, 80 "Conversion not implemented yet"); 81 } 82 explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer) 83 : mBuffer(std::move(aBuffer)) { 84 static_assert(Format == AudioConfig::FORMAT_U8, 85 "Conversion not implemented yet"); 86 } 87 explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer) 88 : mBuffer(std::move(aBuffer)) { 89 static_assert(Format == AudioConfig::FORMAT_S16, 90 "Conversion not implemented yet"); 91 } 92 explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer) 93 : mBuffer(std::move(aBuffer)) { 94 static_assert(Format == AudioConfig::FORMAT_FLT, 95 "Conversion not implemented yet"); 96 } 97 AudioDataBuffer& operator=(AudioDataBuffer&& aOther) { 98 mBuffer = std::move(aOther.mBuffer); 99 return *this; 100 } 101 AudioDataBuffer& operator=(const AudioDataBuffer& aOther) { 102 mBuffer = aOther.mBuffer; 103 return *this; 104 } 105 106 Value* Data() const { return mBuffer.Data(); } 107 size_t Length() const { return mBuffer.Length(); } 108 size_t Size() const { return mBuffer.Size(); } 109 AlignedBuffer<Value> Forget() { 110 // Correct type -> Just give values as-is. 111 return std::move(mBuffer); 112 } 113 114 private: 115 AlignedBuffer<Value> mBuffer; 116 }; 117 118 typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer; 119 120 class AudioConverter { 121 public: 122 AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut); 123 ~AudioConverter(); 124 125 // Convert the AudioDataBuffer. 126 // Conversion will be done in place if possible. Otherwise a new buffer will 127 // be returned. 128 // Providing an empty buffer and resampling is expected, the resampler 129 // will be drained. 130 template <AudioConfig::SampleFormat Format, typename Value> 131 AudioDataBuffer<Format, Value> Process( 132 AudioDataBuffer<Format, Value>&& aBuffer) { 133 MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && 134 mIn.Format() == Format); 135 AudioDataBuffer<Format, Value> buffer = std::move(aBuffer); 136 if (CanWorkInPlace()) { 137 AlignedBuffer<Value> temp = buffer.Forget(); 138 Process(temp, temp.Data(), SamplesInToFrames(temp.Length())); 139 return AudioDataBuffer<Format, Value>(std::move(temp)); 140 } 141 return Process(buffer); 142 } 143 144 template <AudioConfig::SampleFormat Format, typename Value> 145 AudioDataBuffer<Format, Value> Process( 146 const AudioDataBuffer<Format, Value>& aBuffer) { 147 MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && 148 mIn.Format() == Format); 149 // Perform the downmixing / reordering in temporary buffer. 150 size_t frames = SamplesInToFrames(aBuffer.Length()); 151 AlignedBuffer<Value> temp1; 152 if (!temp1.SetLength(FramesOutToSamples(frames))) { 153 return AudioDataBuffer<Format, Value>(std::move(temp1)); 154 } 155 frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames); 156 if (mIn.Rate() == mOut.Rate()) { 157 MOZ_ALWAYS_TRUE(temp1.SetLength(FramesOutToSamples(frames))); 158 return AudioDataBuffer<Format, Value>(std::move(temp1)); 159 } 160 161 // At this point, temp1 contains the buffer reordered and downmixed. 162 // If we are downsampling we can re-use it. 163 AlignedBuffer<Value>* outputBuffer = &temp1; 164 AlignedBuffer<Value> temp2; 165 if (!frames || mOut.Rate() > mIn.Rate()) { 166 // We are upsampling or about to drain, we can't work in place. 167 // Allocate another temporary buffer where the upsampling will occur. 168 if (!temp2.SetLength( 169 FramesOutToSamples(ResampleRecipientFrames(frames)))) { 170 return AudioDataBuffer<Format, Value>(std::move(temp2)); 171 } 172 outputBuffer = &temp2; 173 } 174 if (!frames) { 175 frames = DrainResampler(outputBuffer->Data()); 176 } else { 177 frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); 178 } 179 MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames))); 180 return AudioDataBuffer<Format, Value>(std::move(*outputBuffer)); 181 } 182 183 // Attempt to convert the AudioDataBuffer in place. 184 // Will return 0 if the conversion wasn't possible. 185 template <typename Value> 186 size_t Process(Value* aBuffer, size_t aFrames) { 187 MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format()); 188 if (!CanWorkInPlace()) { 189 return 0; 190 } 191 size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames); 192 if (frames && mIn.Rate() != mOut.Rate()) { 193 frames = ResampleAudio(aBuffer, aBuffer, aFrames); 194 } 195 return frames; 196 } 197 198 template <typename Value> 199 size_t Process(AlignedBuffer<Value>& aOutBuffer, const Value* aInBuffer, 200 size_t aFrames) { 201 MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format()); 202 MOZ_ASSERT((aFrames && aInBuffer) || !aFrames); 203 // Up/down mixing first 204 if (!aOutBuffer.SetLength(FramesOutToSamples(aFrames))) { 205 MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0)); 206 return 0; 207 } 208 size_t frames = ProcessInternal(aOutBuffer.Data(), aInBuffer, aFrames); 209 MOZ_ASSERT(frames == aFrames); 210 // Check if resampling is needed 211 if (mIn.Rate() == mOut.Rate()) { 212 return frames; 213 } 214 // Prepare output in cases of drain or up-sampling 215 if ((!frames || mOut.Rate() > mIn.Rate()) && 216 !aOutBuffer.SetLength( 217 FramesOutToSamples(ResampleRecipientFrames(frames)))) { 218 MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0)); 219 return 0; 220 } 221 if (!frames) { 222 frames = DrainResampler(aOutBuffer.Data()); 223 } else { 224 frames = ResampleAudio(aOutBuffer.Data(), aInBuffer, frames); 225 } 226 // Update with the actual buffer length 227 MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(FramesOutToSamples(frames))); 228 return frames; 229 } 230 231 bool CanWorkInPlace() const; 232 bool CanReorderAudio() const { 233 return mIn.Layout().MappingTable(mOut.Layout()); 234 } 235 static bool CanConvert(const AudioConfig& aIn, const AudioConfig& aOut); 236 237 const AudioConfig& InputConfig() const { return mIn; } 238 const AudioConfig& OutputConfig() const { return mOut; } 239 240 private: 241 const AudioConfig mIn; 242 const AudioConfig mOut; 243 // mChannelOrderMap will be empty if we do not know how to proceed with this 244 // channel layout. 245 AutoTArray<uint8_t, AudioConfig::ChannelLayout::MAX_CHANNELS> 246 mChannelOrderMap; 247 /** 248 * ProcessInternal 249 * Parameters: 250 * aOut : destination buffer where converted samples will be copied 251 * aIn : source buffer 252 * aSamples: number of frames in source buffer 253 * 254 * Return Value: number of frames converted or 0 if error 255 */ 256 size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames); 257 void ReOrderInterleavedChannels(void* aOut, const void* aIn, 258 size_t aFrames) const; 259 size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const; 260 size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const; 261 262 size_t FramesOutToSamples(size_t aFrames) const; 263 size_t SamplesInToFrames(size_t aSamples) const; 264 size_t FramesOutToBytes(size_t aFrames) const; 265 266 // Resampler context. 267 SpeexResamplerState* mResampler; 268 size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames); 269 size_t ResampleRecipientFrames(size_t aFrames) const; 270 void RecreateResampler(); 271 size_t DrainResampler(void* aOut); 272 }; 273 274 } // namespace mozilla 275 276 #endif /* AudioConverter_h */