|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "AudioChannelFormat.h" |
|
7 #include "nsTArray.h" |
|
8 |
|
9 #include <algorithm> |
|
10 |
|
11 namespace mozilla { |
|
12 |
|
13 enum { |
|
14 SURROUND_L, |
|
15 SURROUND_R, |
|
16 SURROUND_C, |
|
17 SURROUND_LFE, |
|
18 SURROUND_SL, |
|
19 SURROUND_SR |
|
20 }; |
|
21 |
|
22 static const uint32_t CUSTOM_CHANNEL_LAYOUTS = 6; |
|
23 |
|
24 static const int IGNORE = CUSTOM_CHANNEL_LAYOUTS; |
|
25 static const float IGNORE_F = 0.0f; |
|
26 |
|
27 uint32_t |
|
28 GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2) |
|
29 { |
|
30 return std::max(aChannels1, aChannels2); |
|
31 } |
|
32 |
|
33 /** |
|
34 * UpMixMatrix represents a conversion matrix by exploiting the fact that |
|
35 * each output channel comes from at most one input channel. |
|
36 */ |
|
37 struct UpMixMatrix { |
|
38 uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; |
|
39 }; |
|
40 |
|
41 static const UpMixMatrix |
|
42 gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] = |
|
43 { |
|
44 // Upmixes from mono |
|
45 { { 0, 0 } }, |
|
46 { { 0, IGNORE, IGNORE } }, |
|
47 { { 0, 0, IGNORE, IGNORE } }, |
|
48 { { 0, IGNORE, IGNORE, IGNORE, IGNORE } }, |
|
49 { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } }, |
|
50 // Upmixes from stereo |
|
51 { { 0, 1, IGNORE } }, |
|
52 { { 0, 1, IGNORE, IGNORE } }, |
|
53 { { 0, 1, IGNORE, IGNORE, IGNORE } }, |
|
54 { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } }, |
|
55 // Upmixes from 3-channel |
|
56 { { 0, 1, 2, IGNORE } }, |
|
57 { { 0, 1, 2, IGNORE, IGNORE } }, |
|
58 { { 0, 1, 2, IGNORE, IGNORE, IGNORE } }, |
|
59 // Upmixes from quad |
|
60 { { 0, 1, 2, 3, IGNORE } }, |
|
61 { { 0, 1, IGNORE, IGNORE, 2, 3 } }, |
|
62 // Upmixes from 5-channel |
|
63 { { 0, 1, 2, 3, 4, IGNORE } } |
|
64 }; |
|
65 |
|
66 static const int gMixingMatrixIndexByChannels[CUSTOM_CHANNEL_LAYOUTS - 1] = |
|
67 { 0, 5, 9, 12, 14 }; |
|
68 |
|
69 void |
|
70 AudioChannelsUpMix(nsTArray<const void*>* aChannelArray, |
|
71 uint32_t aOutputChannelCount, |
|
72 const void* aZeroChannel) |
|
73 { |
|
74 uint32_t inputChannelCount = aChannelArray->Length(); |
|
75 uint32_t outputChannelCount = |
|
76 GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount); |
|
77 NS_ASSERTION(outputChannelCount > inputChannelCount, |
|
78 "No up-mix needed"); |
|
79 NS_ASSERTION(inputChannelCount > 0, "Bad number of channels"); |
|
80 NS_ASSERTION(outputChannelCount > 0, "Bad number of channels"); |
|
81 |
|
82 aChannelArray->SetLength(outputChannelCount); |
|
83 |
|
84 if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS && |
|
85 outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) { |
|
86 const UpMixMatrix& m = gUpMixMatrices[ |
|
87 gMixingMatrixIndexByChannels[inputChannelCount - 1] + |
|
88 outputChannelCount - inputChannelCount - 1]; |
|
89 |
|
90 const void* outputChannels[CUSTOM_CHANNEL_LAYOUTS]; |
|
91 |
|
92 for (uint32_t i = 0; i < outputChannelCount; ++i) { |
|
93 uint8_t channelIndex = m.mInputDestination[i]; |
|
94 if (channelIndex == IGNORE) { |
|
95 outputChannels[i] = aZeroChannel; |
|
96 } else { |
|
97 outputChannels[i] = aChannelArray->ElementAt(channelIndex); |
|
98 } |
|
99 } |
|
100 for (uint32_t i = 0; i < outputChannelCount; ++i) { |
|
101 aChannelArray->ElementAt(i) = outputChannels[i]; |
|
102 } |
|
103 return; |
|
104 } |
|
105 |
|
106 for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) { |
|
107 aChannelArray->ElementAt(i) = aZeroChannel; |
|
108 } |
|
109 } |
|
110 |
|
111 /** |
|
112 * DownMixMatrix represents a conversion matrix efficiently by exploiting the |
|
113 * fact that each input channel contributes to at most one output channel, |
|
114 * except possibly for the C input channel in layouts that have one. Also, |
|
115 * every input channel is multiplied by the same coefficient for every output |
|
116 * channel it contributes to. |
|
117 */ |
|
118 struct DownMixMatrix { |
|
119 // Every input channel c is copied to output channel mInputDestination[c] |
|
120 // after multiplying by mInputCoefficient[c]. |
|
121 uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; |
|
122 // If not IGNORE, then the C channel is copied to this output channel after |
|
123 // multiplying by its coefficient. |
|
124 uint8_t mCExtraDestination; |
|
125 float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS]; |
|
126 }; |
|
127 |
|
128 static const DownMixMatrix |
|
129 gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] = |
|
130 { |
|
131 // Downmixes to mono |
|
132 { { 0, 0 }, IGNORE, { 0.5f, 0.5f } }, |
|
133 { { 0, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F } }, |
|
134 { { 0, 0, 0, 0 }, IGNORE, { 0.25f, 0.25f, 0.25f, 0.25f } }, |
|
135 { { 0, IGNORE, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F } }, |
|
136 { { 0, 0, 0, IGNORE, 0, 0 }, IGNORE, { 0.7071f, 0.7071f, 1.0f, IGNORE_F, 0.5f, 0.5f } }, |
|
137 // Downmixes to stereo |
|
138 { { 0, 1, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F } }, |
|
139 { { 0, 1, 0, 1 }, IGNORE, { 0.5f, 0.5f, 0.5f, 0.5f } }, |
|
140 { { 0, 1, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } }, |
|
141 { { 0, 1, 0, IGNORE, 0, 1 }, 1, { 1.0f, 1.0f, 0.7071f, IGNORE_F, 0.7071f, 0.7071f } }, |
|
142 // Downmixes to 3-channel |
|
143 { { 0, 1, 2, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F } }, |
|
144 { { 0, 1, 2, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F } }, |
|
145 { { 0, 1, 2, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } }, |
|
146 // Downmixes to quad |
|
147 { { 0, 1, 2, 3, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } }, |
|
148 { { 0, 1, 0, IGNORE, 2, 3 }, 1, { 1.0f, 1.0f, 0.7071f, IGNORE_F, 1.0f, 1.0f } }, |
|
149 // Downmixes to 5-channel |
|
150 { { 0, 1, 2, 3, 4, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } } |
|
151 }; |
|
152 |
|
153 void |
|
154 AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray, |
|
155 float** aOutputChannels, |
|
156 uint32_t aOutputChannelCount, |
|
157 uint32_t aDuration) |
|
158 { |
|
159 uint32_t inputChannelCount = aChannelArray.Length(); |
|
160 const void* const* inputChannels = aChannelArray.Elements(); |
|
161 NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do"); |
|
162 |
|
163 if (inputChannelCount > 6) { |
|
164 // Just drop the unknown channels. |
|
165 for (uint32_t o = 0; o < aOutputChannelCount; ++o) { |
|
166 memcpy(aOutputChannels[o], inputChannels[o], aDuration*sizeof(float)); |
|
167 } |
|
168 return; |
|
169 } |
|
170 |
|
171 // Ignore unknown channels, they're just dropped. |
|
172 inputChannelCount = std::min<uint32_t>(6, inputChannelCount); |
|
173 |
|
174 const DownMixMatrix& m = gDownMixMatrices[ |
|
175 gMixingMatrixIndexByChannels[aOutputChannelCount - 1] + |
|
176 inputChannelCount - aOutputChannelCount - 1]; |
|
177 |
|
178 // This is slow, but general. We can define custom code for special |
|
179 // cases later. |
|
180 for (uint32_t s = 0; s < aDuration; ++s) { |
|
181 // Reserve an extra junk channel at the end for the cases where we |
|
182 // want an input channel to contribute to nothing |
|
183 float outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1]; |
|
184 memset(outputChannels, 0, sizeof(float)*(CUSTOM_CHANNEL_LAYOUTS)); |
|
185 for (uint32_t c = 0; c < inputChannelCount; ++c) { |
|
186 outputChannels[m.mInputDestination[c]] += |
|
187 m.mInputCoefficient[c]*(static_cast<const float*>(inputChannels[c]))[s]; |
|
188 } |
|
189 // Utilize the fact that in every layout, C is the third channel. |
|
190 if (m.mCExtraDestination != IGNORE) { |
|
191 outputChannels[m.mCExtraDestination] += |
|
192 m.mInputCoefficient[SURROUND_C]*(static_cast<const float*>(inputChannels[SURROUND_C]))[s]; |
|
193 } |
|
194 |
|
195 for (uint32_t c = 0; c < aOutputChannelCount; ++c) { |
|
196 aOutputChannels[c][s] = outputChannels[c]; |
|
197 } |
|
198 } |
|
199 } |
|
200 |
|
201 } |