diff options
Diffstat (limited to 'dom/media/AudioConverter.h')
-rw-r--r-- | dom/media/AudioConverter.h | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/dom/media/AudioConverter.h b/dom/media/AudioConverter.h new file mode 100644 index 000000000..2806813df --- /dev/null +++ b/dom/media/AudioConverter.h @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(AudioConverter_h) +#define AudioConverter_h + +#include "MediaInfo.h" + +// Forward declaration +typedef struct SpeexResamplerState_ SpeexResamplerState; + +namespace mozilla { + +template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8> +{ typedef uint8_t Type; }; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16> +{ typedef int16_t Type; }; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB> +{ typedef int32_t Type; }; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24> +{ typedef int32_t Type; }; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32> +{ typedef int32_t Type; }; +template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT> +{ typedef float Type; }; + +// 'Value' is the type used externally to deal with stored value. +// AudioDataBuffer can perform conversion between different SampleFormat content. +template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type> +class AudioDataBuffer +{ +public: + AudioDataBuffer() {} + AudioDataBuffer(Value* aBuffer, size_t aLength) + : mBuffer(aBuffer, aLength) + {} + explicit AudioDataBuffer(const AudioDataBuffer& aOther) + : mBuffer(aOther.mBuffer) + {} + AudioDataBuffer(AudioDataBuffer&& aOther) + : mBuffer(Move(aOther.mBuffer)) + {} + template <AudioConfig::SampleFormat OtherFormat, typename OtherValue> + explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other) + { + // TODO: Convert from different type, may use asm routines. + MOZ_CRASH("Conversion not implemented yet"); + } + + // A u8, s16 and float aligned buffer can only be treated as + // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively. + // So allow them as copy and move constructors. + explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer) + : mBuffer(aBuffer) + { + static_assert(Format == AudioConfig::FORMAT_U8, + "Conversion not implemented yet"); + } + explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer) + : mBuffer(aBuffer) + { + static_assert(Format == AudioConfig::FORMAT_S16, + "Conversion not implemented yet"); + } + explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer) + : mBuffer(aBuffer) + { + static_assert(Format == AudioConfig::FORMAT_FLT, + "Conversion not implemented yet"); + } + explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer) + : mBuffer(Move(aBuffer)) + { + static_assert(Format == AudioConfig::FORMAT_U8, + "Conversion not implemented yet"); + } + explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer) + : mBuffer(Move(aBuffer)) + { + static_assert(Format == AudioConfig::FORMAT_S16, + "Conversion not implemented yet"); + } + explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer) + : mBuffer(Move(aBuffer)) + { + static_assert(Format == AudioConfig::FORMAT_FLT, + "Conversion not implemented yet"); + } + AudioDataBuffer& operator=(AudioDataBuffer&& aOther) + { + mBuffer = Move(aOther.mBuffer); + return *this; + } + AudioDataBuffer& operator=(const AudioDataBuffer& aOther) + { + mBuffer = aOther.mBuffer; + return *this; + } + + Value* Data() const { return mBuffer.Data(); } + size_t Length() const { return mBuffer.Length(); } + size_t Size() const { return mBuffer.Size(); } + AlignedBuffer<Value> Forget() + { + // Correct type -> Just give values as-is. + return Move(mBuffer); + } +private: + AlignedBuffer<Value> mBuffer; +}; + +typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer; + +class AudioConverter { +public: + AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut); + ~AudioConverter(); + + // Convert the AudioDataBuffer. + // Conversion will be done in place if possible. Otherwise a new buffer will + // be returned. + // Providing an empty buffer and resampling is expected, the resampler + // will be drained. + template <AudioConfig::SampleFormat Format, typename Value> + AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer) + { + MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format); + AudioDataBuffer<Format, Value> buffer = Move(aBuffer); + if (CanWorkInPlace()) { + size_t frames = SamplesInToFrames(buffer.Length()); + frames = ProcessInternal(buffer.Data(), buffer.Data(), frames); + if (frames && mIn.Rate() != mOut.Rate()) { + frames = ResampleAudio(buffer.Data(), buffer.Data(), frames); + } + AlignedBuffer<Value> temp = buffer.Forget(); + temp.SetLength(FramesOutToSamples(frames)); + return AudioDataBuffer<Format, Value>(Move(temp));; + } + return Process(buffer); + } + + template <AudioConfig::SampleFormat Format, typename Value> + AudioDataBuffer<Format, Value> Process(const AudioDataBuffer<Format, Value>& aBuffer) + { + MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format); + // Perform the downmixing / reordering in temporary buffer. + size_t frames = SamplesInToFrames(aBuffer.Length()); + AlignedBuffer<Value> temp1; + if (!temp1.SetLength(FramesOutToSamples(frames))) { + return AudioDataBuffer<Format, Value>(Move(temp1)); + } + frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames); + if (mIn.Rate() == mOut.Rate()) { + MOZ_ALWAYS_TRUE(temp1.SetLength(FramesOutToSamples(frames))); + return AudioDataBuffer<Format, Value>(Move(temp1)); + } + + // At this point, temp1 contains the buffer reordered and downmixed. + // If we are downsampling we can re-use it. + AlignedBuffer<Value>* outputBuffer = &temp1; + AlignedBuffer<Value> temp2; + if (!frames || mOut.Rate() > mIn.Rate()) { + // We are upsampling or about to drain, we can't work in place. + // Allocate another temporary buffer where the upsampling will occur. + if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) { + return AudioDataBuffer<Format, Value>(Move(temp2)); + } + outputBuffer = &temp2; + } + if (!frames) { + frames = DrainResampler(outputBuffer->Data()); + } else { + frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); + } + MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames))); + return AudioDataBuffer<Format, Value>(Move(*outputBuffer)); + } + + // Attempt to convert the AudioDataBuffer in place. + // Will return 0 if the conversion wasn't possible. + template <typename Value> + size_t Process(Value* aBuffer, size_t aFrames) + { + MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format()); + if (!CanWorkInPlace()) { + return 0; + } + size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames); + if (frames && mIn.Rate() != mOut.Rate()) { + frames = ResampleAudio(aBuffer, aBuffer, aFrames); + } + return frames; + } + + bool CanWorkInPlace() const; + bool CanReorderAudio() const + { + return mIn.Layout().MappingTable(mOut.Layout()); + } + + const AudioConfig& InputConfig() const { return mIn; } + const AudioConfig& OutputConfig() const { return mOut; } + +private: + const AudioConfig mIn; + const AudioConfig mOut; + uint8_t mChannelOrderMap[MAX_AUDIO_CHANNELS]; + /** + * ProcessInternal + * Parameters: + * aOut : destination buffer where converted samples will be copied + * aIn : source buffer + * aSamples: number of frames in source buffer + * + * Return Value: number of frames converted or 0 if error + */ + size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames); + void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const; + size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const; + size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const; + + size_t FramesOutToSamples(size_t aFrames) const; + size_t SamplesInToFrames(size_t aSamples) const; + size_t FramesOutToBytes(size_t aFrames) const; + + // Resampler context. + SpeexResamplerState* mResampler; + size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames); + size_t ResampleRecipientFrames(size_t aFrames) const; + void RecreateResampler(); + size_t DrainResampler(void* aOut); +}; + +} // namespace mozilla + +#endif /* AudioConverter_h */ |