/src/mozilla-central/dom/media/AudioChannelFormat.h
Line | Count | Source (jump to first uncovered line) |
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 | | void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray, |
112 | | T** aOutputChannels, |
113 | | uint32_t aOutputChannelCount, |
114 | | uint32_t aDuration) |
115 | 0 | { |
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 | 0 |
|
120 | 0 | if (inputChannelCount > 6) { |
121 | 0 | // Just drop the unknown channels. |
122 | 0 | for (uint32_t o = 0; o < aOutputChannelCount; ++o) { |
123 | 0 | PodCopy(aOutputChannels[o], inputChannels[o], aDuration); |
124 | 0 | } |
125 | 0 | return; |
126 | 0 | } |
127 | 0 |
|
128 | 0 | // Ignore unknown channels, they're just dropped. |
129 | 0 | inputChannelCount = std::min<uint32_t>(6, inputChannelCount); |
130 | 0 |
|
131 | 0 | const DownMixMatrix& m = gDownMixMatrices[ |
132 | 0 | gMixingMatrixIndexByChannels[aOutputChannelCount - 1] + |
133 | 0 | inputChannelCount - aOutputChannelCount - 1]; |
134 | 0 |
|
135 | 0 | // This is slow, but general. We can define custom code for special |
136 | 0 | // cases later. |
137 | 0 | for (uint32_t s = 0; s < aDuration; ++s) { |
138 | 0 | // Reserve an extra junk channel at the end for the cases where we |
139 | 0 | // 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 | 0 | } |
145 | 0 | // 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 | 0 | } |
150 | 0 |
|
151 | 0 | for (uint32_t c = 0; c < aOutputChannelCount; ++c) { |
152 | 0 | aOutputChannels[c][s] = outputChannels[c]; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } Unexecuted instantiation: void mozilla::AudioChannelsDownMix<float>(nsTArray<float const*> const&, float**, unsigned int, unsigned int) Unexecuted instantiation: void mozilla::AudioChannelsDownMix<short>(nsTArray<short const*> const&, short**, unsigned int, unsigned int) |
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 | | AudioChannelsUpMix(nsTArray<const T*>* aChannelArray, |
206 | | uint32_t aOutputChannelCount, |
207 | | const T* aZeroChannel) |
208 | 0 | { |
209 | 0 | uint32_t inputChannelCount = aChannelArray->Length(); |
210 | 0 | uint32_t outputChannelCount = |
211 | 0 | GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount); |
212 | 0 | NS_ASSERTION(outputChannelCount > inputChannelCount, |
213 | 0 | "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 | 0 |
|
217 | 0 | aChannelArray->SetLength(outputChannelCount); |
218 | 0 |
|
219 | 0 | if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS && |
220 | 0 | outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) { |
221 | 0 | const UpMixMatrix& m = gUpMixMatrices[ |
222 | 0 | gMixingMatrixIndexByChannels[inputChannelCount - 1] + |
223 | 0 | outputChannelCount - inputChannelCount - 1]; |
224 | 0 |
|
225 | 0 | const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS]; |
226 | 0 |
|
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 | 0 | } else { |
232 | 0 | outputChannels[i] = aChannelArray->ElementAt(channelIndex); |
233 | 0 | } |
234 | 0 | } |
235 | 0 | for (uint32_t i = 0; i < outputChannelCount; ++i) { |
236 | 0 | aChannelArray->ElementAt(i) = outputChannels[i]; |
237 | 0 | } |
238 | 0 | return; |
239 | 0 | } |
240 | 0 |
|
241 | 0 | for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) { |
242 | 0 | aChannelArray->ElementAt(i) = aZeroChannel; |
243 | 0 | } |
244 | 0 | } Unexecuted instantiation: void mozilla::AudioChannelsUpMix<float>(nsTArray<float const*>*, unsigned int, float const*) Unexecuted instantiation: void mozilla::AudioChannelsUpMix<short>(nsTArray<short const*>*, unsigned int, short const*) |
245 | | |
246 | | } // namespace mozilla |
247 | | |
248 | | #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */ |