Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / media / AudioChannelFormat.h
blob1b913f55356c534824143af6e8bbc6eef32d3ab6
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_
9 #include <stdint.h>
11 #include "mozilla/PodOperations.h"
12 #include "nsTArrayForwardDeclare.h"
13 #include "AudioSampleFormat.h"
14 #include "nsTArray.h"
16 namespace mozilla {
19 * This file provides utilities for upmixing and downmixing channels.
21 * The channel layouts, upmixing and downmixing are consistent with the
22 * Web Audio spec.
24 * Channel layouts for up to 6 channels:
25 * mono { M }
26 * stereo { L, R }
27 * { L, R, C }
28 * quad { L, R, SL, SR }
29 * { L, R, C, SL, SR }
30 * 5.1 { L, R, C, LFE, SL, SR }
32 * Only 1, 2, 4 and 6 are currently defined in Web Audio.
35 enum {
36 SURROUND_L,
37 SURROUND_R,
38 SURROUND_C,
39 SURROUND_LFE,
40 SURROUND_SL,
41 SURROUND_SR
44 const uint32_t CUSTOM_CHANNEL_LAYOUTS = 6;
46 // This is defined by some Windows SDK header.
47 #undef IGNORE
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,
53 12, 14};
55 /**
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);
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.
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) /
82 2] = {
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},
88 IGNORE,
89 {1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F}},
90 {{0, 0, 0, IGNORE, 0, 0},
91 IGNORE,
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},
97 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},
106 IGNORE,
107 {1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F}},
108 // Downmixes to quad
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},
115 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);
136 return;
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
147 // cases later.
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) {
154 continue;
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.
173 struct UpMixMatrix {
174 uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
177 static const UpMixMatrix gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS *
178 (CUSTOM_CHANNEL_LAYOUTS - 1) / 2] = {
179 // Upmixes from mono
180 {{0, 0}},
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
186 {{0, 1, IGNORE}},
187 {{0, 1, IGNORE, IGNORE}},
188 {{0, 1, IGNORE, IGNORE, IGNORE}},
189 {{0, 1, IGNORE, IGNORE, IGNORE, IGNORE}},
190 // Upmixes from 3-channel
191 {{0, 1, 2, IGNORE}},
192 {{0, 1, 2, IGNORE, IGNORE}},
193 {{0, 1, 2, IGNORE, IGNORE, IGNORE}},
194 // Upmixes from quad
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;
236 } else {
237 outputChannels[i] = aChannelArray->ElementAt(channelIndex);
240 for (uint32_t i = 0; i < outputChannelCount; ++i) {
241 aChannelArray->ElementAt(i) = outputChannels[i];
243 return;
246 for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
247 aChannelArray->ElementAt(i) = aZeroChannel;
251 } // namespace mozilla
253 #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */