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_
11 #include "mozilla/PodOperations.h"
12 #include "nsTArrayForwardDeclare.h"
13 #include "AudioSampleFormat.h"
19 * This file provides utilities for upmixing and downmixing channels.
21 * The channel layouts, upmixing and downmixing are consistent with the
24 * Channel layouts for up to 6 channels:
28 * quad { L, R, SL, SR }
30 * 5.1 { L, R, C, LFE, SL, SR }
32 * Only 1, 2, 4 and 6 are currently defined in Web Audio.
44 const uint32_t CUSTOM_CHANNEL_LAYOUTS
= 6;
46 // This is defined by some Windows SDK header.
49 const int IGNORE
= CUSTOM_CHANNEL_LAYOUTS
;
50 const float IGNORE_F
= 0.0f
;
52 const int gMixingMatrixIndexByChannels
[CUSTOM_CHANNEL_LAYOUTS
- 1] = {0, 5, 9,
56 * Return a channel count whose channel layout includes all the channels from
57 * aChannels1 and aChannels2.
59 uint32_t GetAudioChannelsSuperset(uint32_t aChannels1
, uint32_t aChannels2
);
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.
68 const float SQRT_ONE_HALF
= 0.7071067811865476f
;
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
];
80 static const DownMixMatrix gDownMixMatrices
[CUSTOM_CHANNEL_LAYOUTS
*
81 (CUSTOM_CHANNEL_LAYOUTS
- 1) /
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
},
89 {1.0f
, IGNORE_F
, IGNORE_F
, IGNORE_F
, IGNORE_F
}},
90 {{0, 0, 0, IGNORE
, 0, 0},
92 {SQRT_ONE_HALF
, SQRT_ONE_HALF
, 1.0f
, IGNORE_F
, 0.5f
, 0.5f
}},
93 // Downmixes to stereo
94 {{0, 1, IGNORE
}, IGNORE
, {1.0f
, 1.0f
, IGNORE_F
}},
95 {{0, 1, 0, 1}, IGNORE
, {0.5f
, 0.5f
, 0.5f
, 0.5f
}},
96 {{0, 1, IGNORE
, IGNORE
, IGNORE
},
98 {1.0f
, 1.0f
, IGNORE_F
, IGNORE_F
, IGNORE_F
}},
99 {{0, 1, 0, IGNORE
, 0, 1},
101 {1.0f
, 1.0f
, SQRT_ONE_HALF
, IGNORE_F
, SQRT_ONE_HALF
, SQRT_ONE_HALF
}},
102 // Downmixes to 3-channel
103 {{0, 1, 2, IGNORE
}, IGNORE
, {1.0f
, 1.0f
, 1.0f
, IGNORE_F
}},
104 {{0, 1, 2, IGNORE
, IGNORE
}, IGNORE
, {1.0f
, 1.0f
, 1.0f
, IGNORE_F
, IGNORE_F
}},
105 {{0, 1, 2, IGNORE
, IGNORE
, IGNORE
},
107 {1.0f
, 1.0f
, 1.0f
, IGNORE_F
, IGNORE_F
, IGNORE_F
}},
109 {{0, 1, 2, 3, IGNORE
}, IGNORE
, {1.0f
, 1.0f
, 1.0f
, 1.0f
, IGNORE_F
}},
110 {{0, 1, 0, IGNORE
, 2, 3},
112 {1.0f
, 1.0f
, SQRT_ONE_HALF
, IGNORE_F
, 1.0f
, 1.0f
}},
113 // Downmixes to 5-channel
114 {{0, 1, 2, 3, 4, IGNORE
},
116 {1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, IGNORE_F
}}};
119 * Given an array of input channels, downmix to aOutputChannelCount, and copy
120 * the results to the channel buffers in aOutputChannels. Don't call this with
121 * input count <= output count.
123 template <typename SrcT
, typename DstT
>
124 void AudioChannelsDownMix(Span
<const SrcT
* const> aInputChannels
,
125 Span
<DstT
* const> aOutputChannels
,
126 uint32_t aDuration
) {
127 uint32_t inputChannelCount
= aInputChannels
.Length();
128 uint32_t outputChannelCount
= aOutputChannels
.Length();
129 NS_ASSERTION(inputChannelCount
> outputChannelCount
, "Nothing to do");
131 if (inputChannelCount
> 6) {
132 // Just drop the unknown channels.
133 for (uint32_t o
= 0; o
< outputChannelCount
; ++o
) {
134 ConvertAudioSamples(aInputChannels
[o
], aOutputChannels
[o
], aDuration
);
139 // Ignore unknown channels, they're just dropped.
140 inputChannelCount
= std::min
<uint32_t>(6, inputChannelCount
);
142 const DownMixMatrix
& m
=
143 gDownMixMatrices
[gMixingMatrixIndexByChannels
[outputChannelCount
- 1] +
144 inputChannelCount
- outputChannelCount
- 1];
146 // This is slow, but general. We can define custom code for special
148 for (DstT
* outChannel
: aOutputChannels
) {
149 std::fill_n(outChannel
, aDuration
, static_cast<DstT
>(0));
151 for (uint32_t c
= 0; c
< inputChannelCount
; ++c
) {
152 uint32_t dstIndex
= m
.mInputDestination
[c
];
153 if (dstIndex
== IGNORE
) {
156 AddAudioSamplesWithScale(aInputChannels
[c
], aOutputChannels
[dstIndex
],
157 aDuration
, m
.mInputCoefficient
[c
]);
159 // Utilize the fact that in every layout, C is the only channel that may
160 // contribute to more than one output channel.
161 uint32_t dstIndex
= m
.mCExtraDestination
;
162 if (dstIndex
!= IGNORE
) {
163 AddAudioSamplesWithScale(aInputChannels
[SURROUND_C
],
164 aOutputChannels
[dstIndex
], aDuration
,
165 m
.mInputCoefficient
[SURROUND_C
]);
170 * UpMixMatrix represents a conversion matrix by exploiting the fact that
171 * each output channel comes from at most one input channel.
174 uint8_t mInputDestination
[CUSTOM_CHANNEL_LAYOUTS
];
177 static const UpMixMatrix gUpMixMatrices
[CUSTOM_CHANNEL_LAYOUTS
*
178 (CUSTOM_CHANNEL_LAYOUTS
- 1) / 2] = {
181 {{0, IGNORE
, IGNORE
}},
182 {{0, 0, IGNORE
, IGNORE
}},
183 {{0, IGNORE
, IGNORE
, IGNORE
, IGNORE
}},
184 {{IGNORE
, IGNORE
, 0, IGNORE
, IGNORE
, IGNORE
}},
185 // Upmixes from stereo
187 {{0, 1, IGNORE
, IGNORE
}},
188 {{0, 1, IGNORE
, IGNORE
, IGNORE
}},
189 {{0, 1, IGNORE
, IGNORE
, IGNORE
, IGNORE
}},
190 // Upmixes from 3-channel
192 {{0, 1, 2, IGNORE
, IGNORE
}},
193 {{0, 1, 2, IGNORE
, IGNORE
, IGNORE
}},
195 {{0, 1, 2, 3, IGNORE
}},
196 {{0, 1, IGNORE
, IGNORE
, 2, 3}},
197 // Upmixes from 5-channel
198 {{0, 1, 2, 3, 4, IGNORE
}}};
201 * Given an array of input channel data, and an output channel count,
202 * replaces the array with an array of upmixed channels.
203 * This shuffles the array and may set some channel buffers to aZeroChannel.
204 * Don't call this with input count >= output count.
205 * This may return *more* channels than requested. In that case, downmixing
206 * is required to to get to aOutputChannelCount. (This is how we handle
207 * odd cases like 3 -> 4 upmixing.)
208 * If aChannelArray.Length() was the input to one of a series of
209 * GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
210 * no downmixing will be required.
212 template <typename T
>
213 void AudioChannelsUpMix(nsTArray
<const T
*>* aChannelArray
,
214 uint32_t aOutputChannelCount
, const T
* aZeroChannel
) {
215 uint32_t inputChannelCount
= aChannelArray
->Length();
216 uint32_t outputChannelCount
=
217 GetAudioChannelsSuperset(aOutputChannelCount
, inputChannelCount
);
218 NS_ASSERTION(outputChannelCount
> inputChannelCount
, "No up-mix needed");
219 MOZ_ASSERT(inputChannelCount
> 0, "Bad number of channels");
220 MOZ_ASSERT(outputChannelCount
> 0, "Bad number of channels");
222 aChannelArray
->SetLength(outputChannelCount
);
224 if (inputChannelCount
< CUSTOM_CHANNEL_LAYOUTS
&&
225 outputChannelCount
<= CUSTOM_CHANNEL_LAYOUTS
) {
226 const UpMixMatrix
& m
=
227 gUpMixMatrices
[gMixingMatrixIndexByChannels
[inputChannelCount
- 1] +
228 outputChannelCount
- inputChannelCount
- 1];
230 const T
* outputChannels
[CUSTOM_CHANNEL_LAYOUTS
];
232 for (uint32_t i
= 0; i
< outputChannelCount
; ++i
) {
233 uint8_t channelIndex
= m
.mInputDestination
[i
];
234 if (channelIndex
== IGNORE
) {
235 outputChannels
[i
] = aZeroChannel
;
237 outputChannels
[i
] = aChannelArray
->ElementAt(channelIndex
);
240 for (uint32_t i
= 0; i
< outputChannelCount
; ++i
) {
241 aChannelArray
->ElementAt(i
) = outputChannels
[i
];
246 for (uint32_t i
= inputChannelCount
; i
< outputChannelCount
; ++i
) {
247 aChannelArray
->ElementAt(i
) = aZeroChannel
;
251 } // namespace mozilla
253 #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */