Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / media / AudioConverter.h
blobc8cbbb794981978da4d0457d2a54b33ea1dffeaf
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/. */
7 #if !defined(AudioConverter_h)
8 # define AudioConverter_h
10 # include "MediaInfo.h"
12 // Forward declaration
13 typedef struct SpeexResamplerState_ SpeexResamplerState;
15 namespace mozilla {
17 template <AudioConfig::SampleFormat T>
18 struct AudioDataBufferTypeChooser;
19 template <>
20 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8> {
21 typedef uint8_t Type;
23 template <>
24 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16> {
25 typedef int16_t Type;
27 template <>
28 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB> {
29 typedef int32_t Type;
31 template <>
32 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24> {
33 typedef int32_t Type;
35 template <>
36 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32> {
37 typedef int32_t Type;
39 template <>
40 struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT> {
41 typedef float Type;
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");
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");
72 explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
73 : mBuffer(aBuffer) {
74 static_assert(Format == AudioConfig::FORMAT_S16,
75 "Conversion not implemented yet");
77 explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
78 : mBuffer(aBuffer) {
79 static_assert(Format == AudioConfig::FORMAT_FLT,
80 "Conversion not implemented yet");
82 explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
83 : mBuffer(std::move(aBuffer)) {
84 static_assert(Format == AudioConfig::FORMAT_U8,
85 "Conversion not implemented yet");
87 explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
88 : mBuffer(std::move(aBuffer)) {
89 static_assert(Format == AudioConfig::FORMAT_S16,
90 "Conversion not implemented yet");
92 explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer)
93 : mBuffer(std::move(aBuffer)) {
94 static_assert(Format == AudioConfig::FORMAT_FLT,
95 "Conversion not implemented yet");
97 AudioDataBuffer& operator=(AudioDataBuffer&& aOther) {
98 mBuffer = std::move(aOther.mBuffer);
99 return *this;
101 AudioDataBuffer& operator=(const AudioDataBuffer& aOther) {
102 mBuffer = aOther.mBuffer;
103 return *this;
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);
114 private:
115 AlignedBuffer<Value> mBuffer;
118 typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
120 class AudioConverter {
121 public:
122 AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
123 ~AudioConverter();
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));
141 return Process(buffer);
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));
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));
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));
172 outputBuffer = &temp2;
174 if (!frames) {
175 frames = DrainResampler(outputBuffer->Data());
176 } else {
177 frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames);
179 MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames)));
180 return AudioDataBuffer<Format, Value>(std::move(*outputBuffer));
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;
191 size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames);
192 if (frames && mIn.Rate() != mOut.Rate()) {
193 frames = ResampleAudio(aBuffer, aBuffer, aFrames);
195 return frames;
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;
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;
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;
221 if (!frames) {
222 frames = DrainResampler(aOutBuffer.Data());
223 } else {
224 frames = ResampleAudio(aOutBuffer.Data(), aInBuffer, frames);
226 // Update with the actual buffer length
227 MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(FramesOutToSamples(frames)));
228 return frames;
231 bool CanWorkInPlace() const;
232 bool CanReorderAudio() const {
233 return mIn.Layout().MappingTable(mOut.Layout());
235 static bool CanConvert(const AudioConfig& aIn, const AudioConfig& aOut);
237 const AudioConfig& InputConfig() const { return mIn; }
238 const AudioConfig& OutputConfig() const { return mOut; }
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;
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
254 * Return Value: number of frames converted or 0 if error
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;
262 size_t FramesOutToSamples(size_t aFrames) const;
263 size_t SamplesInToFrames(size_t aSamples) const;
264 size_t FramesOutToBytes(size_t aFrames) const;
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);
274 } // namespace mozilla
276 #endif /* AudioConverter_h */