Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 : #ifndef MOZILLA_AUDIOCHANNELFORMAT_H_
7 : #define MOZILLA_AUDIOCHANNELFORMAT_H_
8 :
9 : #include <stdint.h>
10 :
11 : #include "nsTArrayForwardDeclare.h"
12 : #include "AudioSampleFormat.h"
13 : #include "nsTArray.h"
14 :
15 : namespace mozilla {
16 :
17 : /*
18 : * This file provides utilities for upmixing and downmixing channels.
19 : *
20 : * The channel layouts, upmixing and downmixing are consistent with the
21 : * Web Audio spec.
22 : *
23 : * Channel layouts for up to 6 channels:
24 : * mono { M }
25 : * stereo { L, R }
26 : * { L, R, C }
27 : * quad { L, R, SL, SR }
28 : * { L, R, C, SL, SR }
29 : * 5.1 { L, R, C, LFE, SL, SR }
30 : *
31 : * Only 1, 2, 4 and 6 are currently defined in Web Audio.
32 : */
33 :
34 : enum {
35 : SURROUND_L,
36 : SURROUND_R,
37 : SURROUND_C,
38 : SURROUND_LFE,
39 : SURROUND_SL,
40 : SURROUND_SR
41 : };
42 :
43 : const uint32_t CUSTOM_CHANNEL_LAYOUTS = 6;
44 :
45 : // This is defined by some Windows SDK header.
46 : #undef IGNORE
47 :
48 : const int IGNORE = CUSTOM_CHANNEL_LAYOUTS;
49 : const float IGNORE_F = 0.0f;
50 :
51 : const int gMixingMatrixIndexByChannels[CUSTOM_CHANNEL_LAYOUTS - 1] =
52 : { 0, 5, 9, 12, 14 };
53 :
54 : /**
55 : * Return a channel count whose channel layout includes all the channels from
56 : * aChannels1 and aChannels2.
57 : */
58 : uint32_t
59 : GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2);
60 :
61 : /**
62 : * DownMixMatrix represents a conversion matrix efficiently by exploiting the
63 : * fact that each input channel contributes to at most one output channel,
64 : * except possibly for the C input channel in layouts that have one. Also,
65 : * every input channel is multiplied by the same coefficient for every output
66 : * channel it contributes to.
67 : */
68 : const float SQRT_ONE_HALF = 0.7071067811865476f;
69 :
70 : struct DownMixMatrix {
71 : // Every input channel c is copied to output channel mInputDestination[c]
72 : // after multiplying by mInputCoefficient[c].
73 : uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
74 : // If not IGNORE, then the C channel is copied to this output channel after
75 : // multiplying by its coefficient.
76 : uint8_t mCExtraDestination;
77 : float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS];
78 : };
79 :
80 : static const DownMixMatrix
81 : gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
82 : {
83 : // Downmixes to mono
84 : { { 0, 0 }, IGNORE, { 0.5f, 0.5f } },
85 : { { 0, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F } },
86 : { { 0, 0, 0, 0 }, IGNORE, { 0.25f, 0.25f, 0.25f, 0.25f } },
87 : { { 0, IGNORE, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F } },
88 : { { 0, 0, 0, IGNORE, 0, 0 }, IGNORE, { SQRT_ONE_HALF, SQRT_ONE_HALF, 1.0f, IGNORE_F, 0.5f, 0.5f } },
89 : // Downmixes to stereo
90 : { { 0, 1, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F } },
91 : { { 0, 1, 0, 1 }, IGNORE, { 0.5f, 0.5f, 0.5f, 0.5f } },
92 : { { 0, 1, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } },
93 : { { 0, 1, 0, IGNORE, 0, 1 }, 1, { 1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, SQRT_ONE_HALF, SQRT_ONE_HALF } },
94 : // Downmixes to 3-channel
95 : { { 0, 1, 2, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F } },
96 : { { 0, 1, 2, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F } },
97 : { { 0, 1, 2, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } },
98 : // Downmixes to quad
99 : { { 0, 1, 2, 3, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } },
100 : { { 0, 1, 0, IGNORE, 2, 3 }, 1, { 1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, 1.0f, 1.0f } },
101 : // Downmixes to 5-channel
102 : { { 0, 1, 2, 3, 4, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } }
103 : };
104 :
105 : /**
106 : * Given an array of input channels, downmix to aOutputChannelCount, and copy
107 : * the results to the channel buffers in aOutputChannels. Don't call this with
108 : * input count <= output count.
109 : */
110 : template<typename T>
111 0 : void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
112 : T** aOutputChannels,
113 : uint32_t aOutputChannelCount,
114 : uint32_t aDuration)
115 : {
116 0 : uint32_t inputChannelCount = aChannelArray.Length();
117 0 : const T* const* inputChannels = aChannelArray.Elements();
118 0 : NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do");
119 :
120 0 : if (inputChannelCount > 6) {
121 : // Just drop the unknown channels.
122 0 : for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
123 0 : PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
124 : }
125 0 : return;
126 : }
127 :
128 : // Ignore unknown channels, they're just dropped.
129 0 : inputChannelCount = std::min<uint32_t>(6, inputChannelCount);
130 :
131 : const DownMixMatrix& m = gDownMixMatrices[
132 0 : gMixingMatrixIndexByChannels[aOutputChannelCount - 1] +
133 0 : inputChannelCount - aOutputChannelCount - 1];
134 :
135 : // This is slow, but general. We can define custom code for special
136 : // cases later.
137 0 : for (uint32_t s = 0; s < aDuration; ++s) {
138 : // Reserve an extra junk channel at the end for the cases where we
139 : // want an input channel to contribute to nothing
140 0 : T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
141 0 : for (uint32_t c = 0; c < inputChannelCount; ++c) {
142 0 : outputChannels[m.mInputDestination[c]] +=
143 0 : m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
144 : }
145 : // Utilize the fact that in every layout, C is the third channel.
146 0 : if (m.mCExtraDestination != IGNORE) {
147 0 : outputChannels[m.mCExtraDestination] +=
148 0 : m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
149 : }
150 :
151 0 : for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
152 0 : aOutputChannels[c][s] = outputChannels[c];
153 : }
154 : }
155 : }
156 :
157 : /**
158 : * UpMixMatrix represents a conversion matrix by exploiting the fact that
159 : * each output channel comes from at most one input channel.
160 : */
161 : struct UpMixMatrix {
162 : uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
163 : };
164 :
165 : static const UpMixMatrix
166 : gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
167 : {
168 : // Upmixes from mono
169 : { { 0, 0 } },
170 : { { 0, IGNORE, IGNORE } },
171 : { { 0, 0, IGNORE, IGNORE } },
172 : { { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
173 : { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
174 : // Upmixes from stereo
175 : { { 0, 1, IGNORE } },
176 : { { 0, 1, IGNORE, IGNORE } },
177 : { { 0, 1, IGNORE, IGNORE, IGNORE } },
178 : { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
179 : // Upmixes from 3-channel
180 : { { 0, 1, 2, IGNORE } },
181 : { { 0, 1, 2, IGNORE, IGNORE } },
182 : { { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
183 : // Upmixes from quad
184 : { { 0, 1, 2, 3, IGNORE } },
185 : { { 0, 1, IGNORE, IGNORE, 2, 3 } },
186 : // Upmixes from 5-channel
187 : { { 0, 1, 2, 3, 4, IGNORE } }
188 : };
189 :
190 :
191 : /**
192 : * Given an array of input channel data, and an output channel count,
193 : * replaces the array with an array of upmixed channels.
194 : * This shuffles the array and may set some channel buffers to aZeroChannel.
195 : * Don't call this with input count >= output count.
196 : * This may return *more* channels than requested. In that case, downmixing
197 : * is required to to get to aOutputChannelCount. (This is how we handle
198 : * odd cases like 3 -> 4 upmixing.)
199 : * If aChannelArray.Length() was the input to one of a series of
200 : * GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
201 : * no downmixing will be required.
202 : */
203 : template<typename T>
204 : void
205 0 : AudioChannelsUpMix(nsTArray<const T*>* aChannelArray,
206 : uint32_t aOutputChannelCount,
207 : const T* aZeroChannel)
208 : {
209 0 : uint32_t inputChannelCount = aChannelArray->Length();
210 : uint32_t outputChannelCount =
211 0 : GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
212 0 : NS_ASSERTION(outputChannelCount > inputChannelCount,
213 : "No up-mix needed");
214 0 : MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
215 0 : MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
216 :
217 0 : aChannelArray->SetLength(outputChannelCount);
218 :
219 0 : if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
220 : outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
221 : const UpMixMatrix& m = gUpMixMatrices[
222 0 : gMixingMatrixIndexByChannels[inputChannelCount - 1] +
223 0 : outputChannelCount - inputChannelCount - 1];
224 :
225 : const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
226 :
227 0 : for (uint32_t i = 0; i < outputChannelCount; ++i) {
228 0 : uint8_t channelIndex = m.mInputDestination[i];
229 0 : if (channelIndex == IGNORE) {
230 0 : outputChannels[i] = aZeroChannel;
231 : } else {
232 0 : outputChannels[i] = aChannelArray->ElementAt(channelIndex);
233 : }
234 : }
235 0 : for (uint32_t i = 0; i < outputChannelCount; ++i) {
236 0 : aChannelArray->ElementAt(i) = outputChannels[i];
237 : }
238 0 : return;
239 : }
240 :
241 0 : for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
242 0 : aChannelArray->ElementAt(i) = aZeroChannel;
243 : }
244 : }
245 :
246 : } // namespace mozilla
247 :
248 : #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */
|