AudioChannelFormat.h (9075B)
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 "AudioSampleFormat.h" 12 #include "nsTArray.h" 13 #include "nsTArrayForwardDeclare.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] = {0, 5, 9, 52 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 GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2); 59 60 /** 61 * DownMixMatrix represents a conversion matrix efficiently by exploiting the 62 * fact that each input channel contributes to at most one output channel, 63 * except possibly for the C input channel in layouts that have one. Also, 64 * every input channel is multiplied by the same coefficient for every output 65 * channel it contributes to. 66 */ 67 const float SQRT_ONE_HALF = 0.7071067811865476f; 68 69 struct DownMixMatrix { 70 // Every input channel c is copied to output channel mInputDestination[c] 71 // after multiplying by mInputCoefficient[c]. 72 uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; 73 // If not IGNORE, then the C channel is copied to this output channel after 74 // multiplying by its coefficient. 75 uint8_t mCExtraDestination; 76 float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS]; 77 }; 78 79 static const DownMixMatrix gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS * 80 (CUSTOM_CHANNEL_LAYOUTS - 1) / 81 2] = { 82 // Downmixes to mono 83 {{0, 0}, IGNORE, {0.5f, 0.5f}}, 84 {{0, IGNORE, IGNORE}, IGNORE, {1.0f, IGNORE_F, IGNORE_F}}, 85 {{0, 0, 0, 0}, IGNORE, {0.25f, 0.25f, 0.25f, 0.25f}}, 86 {{0, IGNORE, IGNORE, IGNORE, IGNORE}, 87 IGNORE, 88 {1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F}}, 89 {{0, 0, 0, IGNORE, 0, 0}, 90 IGNORE, 91 {SQRT_ONE_HALF, SQRT_ONE_HALF, 1.0f, IGNORE_F, 0.5f, 0.5f}}, 92 // Downmixes to stereo 93 {{0, 1, IGNORE}, IGNORE, {1.0f, 1.0f, IGNORE_F}}, 94 {{0, 1, 0, 1}, IGNORE, {0.5f, 0.5f, 0.5f, 0.5f}}, 95 {{0, 1, IGNORE, IGNORE, IGNORE}, 96 IGNORE, 97 {1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F}}, 98 {{0, 1, 0, IGNORE, 0, 1}, 99 1, 100 {1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, SQRT_ONE_HALF, SQRT_ONE_HALF}}, 101 // Downmixes to 3-channel 102 {{0, 1, 2, IGNORE}, IGNORE, {1.0f, 1.0f, 1.0f, IGNORE_F}}, 103 {{0, 1, 2, IGNORE, IGNORE}, IGNORE, {1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F}}, 104 {{0, 1, 2, IGNORE, IGNORE, IGNORE}, 105 IGNORE, 106 {1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F}}, 107 // Downmixes to quad 108 {{0, 1, 2, 3, IGNORE}, IGNORE, {1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F}}, 109 {{0, 1, 0, IGNORE, 2, 3}, 110 1, 111 {1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, 1.0f, 1.0f}}, 112 // Downmixes to 5-channel 113 {{0, 1, 2, 3, 4, IGNORE}, 114 IGNORE, 115 {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F}}}; 116 117 /** 118 * Given an array of input channels, downmix to aOutputChannelCount, and copy 119 * the results to the channel buffers in aOutputChannels. Don't call this with 120 * input count <= output count. 121 */ 122 template <typename SrcT, typename DstT> 123 void AudioChannelsDownMix(Span<const SrcT* const> aInputChannels, 124 Span<DstT* const> aOutputChannels, 125 uint32_t aDuration) { 126 uint32_t inputChannelCount = aInputChannels.Length(); 127 uint32_t outputChannelCount = aOutputChannels.Length(); 128 NS_ASSERTION(inputChannelCount > outputChannelCount, "Nothing to do"); 129 130 if (inputChannelCount > 6) { 131 // Just drop the unknown channels. 132 for (uint32_t o = 0; o < outputChannelCount; ++o) { 133 ConvertAudioSamples(aInputChannels[o], aOutputChannels[o], aDuration); 134 } 135 return; 136 } 137 138 // Ignore unknown channels, they're just dropped. 139 inputChannelCount = std::min<uint32_t>(6, inputChannelCount); 140 141 const DownMixMatrix& m = 142 gDownMixMatrices[gMixingMatrixIndexByChannels[outputChannelCount - 1] + 143 inputChannelCount - outputChannelCount - 1]; 144 145 // This is slow, but general. We can define custom code for special 146 // cases later. 147 for (DstT* outChannel : aOutputChannels) { 148 std::fill_n(outChannel, aDuration, static_cast<DstT>(0)); 149 } 150 for (uint32_t c = 0; c < inputChannelCount; ++c) { 151 uint32_t dstIndex = m.mInputDestination[c]; 152 if (dstIndex == IGNORE) { 153 continue; 154 } 155 AddAudioSamplesWithScale(aInputChannels[c], aOutputChannels[dstIndex], 156 aDuration, m.mInputCoefficient[c]); 157 } 158 // Utilize the fact that in every layout, C is the only channel that may 159 // contribute to more than one output channel. 160 uint32_t dstIndex = m.mCExtraDestination; 161 if (dstIndex != IGNORE) { 162 AddAudioSamplesWithScale(aInputChannels[SURROUND_C], 163 aOutputChannels[dstIndex], aDuration, 164 m.mInputCoefficient[SURROUND_C]); 165 } 166 } 167 168 /** 169 * UpMixMatrix represents a conversion matrix by exploiting the fact that 170 * each output channel comes from at most one input channel. 171 */ 172 struct UpMixMatrix { 173 uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; 174 }; 175 176 static const UpMixMatrix gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS * 177 (CUSTOM_CHANNEL_LAYOUTS - 1) / 2] = { 178 // Upmixes from mono 179 {{0, 0}}, 180 {{0, IGNORE, IGNORE}}, 181 {{0, 0, IGNORE, IGNORE}}, 182 {{0, IGNORE, IGNORE, IGNORE, IGNORE}}, 183 {{IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE}}, 184 // Upmixes from stereo 185 {{0, 1, IGNORE}}, 186 {{0, 1, IGNORE, IGNORE}}, 187 {{0, 1, IGNORE, IGNORE, IGNORE}}, 188 {{0, 1, IGNORE, IGNORE, IGNORE, IGNORE}}, 189 // Upmixes from 3-channel 190 {{0, 1, 2, IGNORE}}, 191 {{0, 1, 2, IGNORE, IGNORE}}, 192 {{0, 1, 2, IGNORE, IGNORE, IGNORE}}, 193 // Upmixes from quad 194 {{0, 1, 2, 3, IGNORE}}, 195 {{0, 1, IGNORE, IGNORE, 2, 3}}, 196 // Upmixes from 5-channel 197 {{0, 1, 2, 3, 4, IGNORE}}}; 198 199 /** 200 * Given an array of input channel data, and an output channel count, 201 * replaces the array with an array of upmixed channels. 202 * This shuffles the array and may set some channel buffers to aZeroChannel. 203 * Don't call this with input count >= output count. 204 * This may return *more* channels than requested. In that case, downmixing 205 * is required to to get to aOutputChannelCount. (This is how we handle 206 * odd cases like 3 -> 4 upmixing.) 207 * If aChannelArray.Length() was the input to one of a series of 208 * GetAudioChannelsSuperset calls resulting in aOutputChannelCount, 209 * no downmixing will be required. 210 */ 211 template <typename T> 212 void AudioChannelsUpMix(nsTArray<const T*>* aChannelArray, 213 uint32_t aOutputChannelCount, const T* aZeroChannel) { 214 uint32_t inputChannelCount = aChannelArray->Length(); 215 uint32_t outputChannelCount = 216 GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount); 217 NS_ASSERTION(outputChannelCount > inputChannelCount, "No up-mix needed"); 218 MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels"); 219 MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels"); 220 221 aChannelArray->SetLength(outputChannelCount); 222 223 if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS && 224 outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) { 225 const UpMixMatrix& m = 226 gUpMixMatrices[gMixingMatrixIndexByChannels[inputChannelCount - 1] + 227 outputChannelCount - inputChannelCount - 1]; 228 229 const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS]; 230 231 for (uint32_t i = 0; i < outputChannelCount; ++i) { 232 uint8_t channelIndex = m.mInputDestination[i]; 233 if (channelIndex == IGNORE) { 234 outputChannels[i] = aZeroChannel; 235 } else { 236 outputChannels[i] = aChannelArray->ElementAt(channelIndex); 237 } 238 } 239 for (uint32_t i = 0; i < outputChannelCount; ++i) { 240 aChannelArray->ElementAt(i) = outputChannels[i]; 241 } 242 return; 243 } 244 245 for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) { 246 aChannelArray->ElementAt(i) = aZeroChannel; 247 } 248 } 249 250 } // namespace mozilla 251 252 #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */