Line data Source code
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 : #if !defined(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> struct AudioDataBufferTypeChooser;
18 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
19 : { typedef uint8_t Type; };
20 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
21 : { typedef int16_t Type; };
22 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
23 : { typedef int32_t Type; };
24 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
25 : { typedef int32_t Type; };
26 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
27 : { typedef int32_t Type; };
28 : template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
29 : { typedef float Type; };
30 :
31 : // 'Value' is the type used externally to deal with stored value.
32 : // AudioDataBuffer can perform conversion between different SampleFormat content.
33 : template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
34 0 : class AudioDataBuffer
35 : {
36 : public:
37 : AudioDataBuffer() {}
38 : AudioDataBuffer(Value* aBuffer, size_t aLength)
39 : : mBuffer(aBuffer, aLength)
40 : {}
41 : explicit AudioDataBuffer(const AudioDataBuffer& aOther)
42 : : mBuffer(aOther.mBuffer)
43 : {}
44 0 : AudioDataBuffer(AudioDataBuffer&& aOther)
45 0 : : mBuffer(Move(aOther.mBuffer))
46 0 : {}
47 : template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
48 : explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
49 : {
50 : // TODO: Convert from different type, may use asm routines.
51 : MOZ_CRASH("Conversion not implemented yet");
52 : }
53 :
54 : // A u8, s16 and float aligned buffer can only be treated as
55 : // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
56 : // So allow them as copy and move constructors.
57 : explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
58 : : mBuffer(aBuffer)
59 : {
60 : static_assert(Format == AudioConfig::FORMAT_U8,
61 : "Conversion not implemented yet");
62 : }
63 : explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
64 : : mBuffer(aBuffer)
65 : {
66 : static_assert(Format == AudioConfig::FORMAT_S16,
67 : "Conversion not implemented yet");
68 : }
69 : explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
70 : : mBuffer(aBuffer)
71 : {
72 : static_assert(Format == AudioConfig::FORMAT_FLT,
73 : "Conversion not implemented yet");
74 : }
75 : explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
76 : : mBuffer(Move(aBuffer))
77 : {
78 : static_assert(Format == AudioConfig::FORMAT_U8,
79 : "Conversion not implemented yet");
80 : }
81 : explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
82 : : mBuffer(Move(aBuffer))
83 : {
84 : static_assert(Format == AudioConfig::FORMAT_S16,
85 : "Conversion not implemented yet");
86 : }
87 0 : explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer)
88 0 : : mBuffer(Move(aBuffer))
89 : {
90 : static_assert(Format == AudioConfig::FORMAT_FLT,
91 : "Conversion not implemented yet");
92 0 : }
93 0 : AudioDataBuffer& operator=(AudioDataBuffer&& aOther)
94 : {
95 0 : mBuffer = Move(aOther.mBuffer);
96 0 : return *this;
97 : }
98 : AudioDataBuffer& operator=(const AudioDataBuffer& aOther)
99 : {
100 : mBuffer = aOther.mBuffer;
101 : return *this;
102 : }
103 :
104 0 : Value* Data() const { return mBuffer.Data(); }
105 0 : size_t Length() const { return mBuffer.Length(); }
106 : size_t Size() const { return mBuffer.Size(); }
107 0 : AlignedBuffer<Value> Forget()
108 : {
109 : // Correct type -> Just give values as-is.
110 0 : return Move(mBuffer);
111 : }
112 : private:
113 : AlignedBuffer<Value> mBuffer;
114 : };
115 :
116 : typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
117 :
118 : class AudioConverter {
119 : public:
120 : AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
121 : ~AudioConverter();
122 :
123 : // Convert the AudioDataBuffer.
124 : // Conversion will be done in place if possible. Otherwise a new buffer will
125 : // be returned.
126 : // Providing an empty buffer and resampling is expected, the resampler
127 : // will be drained.
128 : template <AudioConfig::SampleFormat Format, typename Value>
129 0 : AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer)
130 : {
131 0 : MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format);
132 0 : AudioDataBuffer<Format, Value> buffer = Move(aBuffer);
133 0 : if (CanWorkInPlace()) {
134 0 : size_t frames = SamplesInToFrames(buffer.Length());
135 0 : frames = ProcessInternal(buffer.Data(), buffer.Data(), frames);
136 0 : if (frames && mIn.Rate() != mOut.Rate()) {
137 0 : frames = ResampleAudio(buffer.Data(), buffer.Data(), frames);
138 : }
139 0 : AlignedBuffer<Value> temp = buffer.Forget();
140 0 : temp.SetLength(FramesOutToSamples(frames));
141 0 : return AudioDataBuffer<Format, Value>(Move(temp));;
142 : }
143 0 : return Process(buffer);
144 : }
145 :
146 : template <AudioConfig::SampleFormat Format, typename Value>
147 0 : AudioDataBuffer<Format, Value> Process(const AudioDataBuffer<Format, Value>& aBuffer)
148 : {
149 0 : MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format);
150 : // Perform the downmixing / reordering in temporary buffer.
151 0 : size_t frames = SamplesInToFrames(aBuffer.Length());
152 0 : AlignedBuffer<Value> temp1;
153 0 : if (!temp1.SetLength(FramesOutToSamples(frames))) {
154 0 : return AudioDataBuffer<Format, Value>(Move(temp1));
155 : }
156 0 : frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames);
157 0 : if (mIn.Rate() == mOut.Rate()) {
158 0 : MOZ_ALWAYS_TRUE(temp1.SetLength(FramesOutToSamples(frames)));
159 0 : return AudioDataBuffer<Format, Value>(Move(temp1));
160 : }
161 :
162 : // At this point, temp1 contains the buffer reordered and downmixed.
163 : // If we are downsampling we can re-use it.
164 0 : AlignedBuffer<Value>* outputBuffer = &temp1;
165 0 : AlignedBuffer<Value> temp2;
166 0 : if (!frames || mOut.Rate() > mIn.Rate()) {
167 : // We are upsampling or about to drain, we can't work in place.
168 : // Allocate another temporary buffer where the upsampling will occur.
169 0 : if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) {
170 0 : return AudioDataBuffer<Format, Value>(Move(temp2));
171 : }
172 0 : outputBuffer = &temp2;
173 : }
174 0 : if (!frames) {
175 0 : frames = DrainResampler(outputBuffer->Data());
176 : } else {
177 0 : frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames);
178 : }
179 0 : MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames)));
180 0 : return AudioDataBuffer<Format, Value>(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 : {
188 : MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format());
189 : if (!CanWorkInPlace()) {
190 : return 0;
191 : }
192 : size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames);
193 : if (frames && mIn.Rate() != mOut.Rate()) {
194 : frames = ResampleAudio(aBuffer, aBuffer, aFrames);
195 : }
196 : return frames;
197 : }
198 :
199 : bool CanWorkInPlace() const;
200 0 : bool CanReorderAudio() const
201 : {
202 0 : return mIn.Layout().MappingTable(mOut.Layout());
203 : }
204 :
205 0 : const AudioConfig& InputConfig() const { return mIn; }
206 0 : const AudioConfig& OutputConfig() const { return mOut; }
207 :
208 : private:
209 : const AudioConfig mIn;
210 : const AudioConfig mOut;
211 : uint8_t mChannelOrderMap[MAX_AUDIO_CHANNELS];
212 : /**
213 : * ProcessInternal
214 : * Parameters:
215 : * aOut : destination buffer where converted samples will be copied
216 : * aIn : source buffer
217 : * aSamples: number of frames in source buffer
218 : *
219 : * Return Value: number of frames converted or 0 if error
220 : */
221 : size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames);
222 : void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const;
223 : size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
224 : size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
225 :
226 : size_t FramesOutToSamples(size_t aFrames) const;
227 : size_t SamplesInToFrames(size_t aSamples) const;
228 : size_t FramesOutToBytes(size_t aFrames) const;
229 :
230 : // Resampler context.
231 : SpeexResamplerState* mResampler;
232 : size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);
233 : size_t ResampleRecipientFrames(size_t aFrames) const;
234 : void RecreateResampler();
235 : size_t DrainResampler(void* aOut);
236 : };
237 :
238 : } // namespace mozilla
239 :
240 : #endif /* AudioConverter_h */
|