1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/AudioChannelFormat.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,201 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "AudioChannelFormat.h" 1.10 +#include "nsTArray.h" 1.11 + 1.12 +#include <algorithm> 1.13 + 1.14 +namespace mozilla { 1.15 + 1.16 +enum { 1.17 + SURROUND_L, 1.18 + SURROUND_R, 1.19 + SURROUND_C, 1.20 + SURROUND_LFE, 1.21 + SURROUND_SL, 1.22 + SURROUND_SR 1.23 +}; 1.24 + 1.25 +static const uint32_t CUSTOM_CHANNEL_LAYOUTS = 6; 1.26 + 1.27 +static const int IGNORE = CUSTOM_CHANNEL_LAYOUTS; 1.28 +static const float IGNORE_F = 0.0f; 1.29 + 1.30 +uint32_t 1.31 +GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2) 1.32 +{ 1.33 + return std::max(aChannels1, aChannels2); 1.34 +} 1.35 + 1.36 +/** 1.37 + * UpMixMatrix represents a conversion matrix by exploiting the fact that 1.38 + * each output channel comes from at most one input channel. 1.39 + */ 1.40 +struct UpMixMatrix { 1.41 + uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; 1.42 +}; 1.43 + 1.44 +static const UpMixMatrix 1.45 +gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] = 1.46 +{ 1.47 + // Upmixes from mono 1.48 + { { 0, 0 } }, 1.49 + { { 0, IGNORE, IGNORE } }, 1.50 + { { 0, 0, IGNORE, IGNORE } }, 1.51 + { { 0, IGNORE, IGNORE, IGNORE, IGNORE } }, 1.52 + { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } }, 1.53 + // Upmixes from stereo 1.54 + { { 0, 1, IGNORE } }, 1.55 + { { 0, 1, IGNORE, IGNORE } }, 1.56 + { { 0, 1, IGNORE, IGNORE, IGNORE } }, 1.57 + { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } }, 1.58 + // Upmixes from 3-channel 1.59 + { { 0, 1, 2, IGNORE } }, 1.60 + { { 0, 1, 2, IGNORE, IGNORE } }, 1.61 + { { 0, 1, 2, IGNORE, IGNORE, IGNORE } }, 1.62 + // Upmixes from quad 1.63 + { { 0, 1, 2, 3, IGNORE } }, 1.64 + { { 0, 1, IGNORE, IGNORE, 2, 3 } }, 1.65 + // Upmixes from 5-channel 1.66 + { { 0, 1, 2, 3, 4, IGNORE } } 1.67 +}; 1.68 + 1.69 +static const int gMixingMatrixIndexByChannels[CUSTOM_CHANNEL_LAYOUTS - 1] = 1.70 + { 0, 5, 9, 12, 14 }; 1.71 + 1.72 +void 1.73 +AudioChannelsUpMix(nsTArray<const void*>* aChannelArray, 1.74 + uint32_t aOutputChannelCount, 1.75 + const void* aZeroChannel) 1.76 +{ 1.77 + uint32_t inputChannelCount = aChannelArray->Length(); 1.78 + uint32_t outputChannelCount = 1.79 + GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount); 1.80 + NS_ASSERTION(outputChannelCount > inputChannelCount, 1.81 + "No up-mix needed"); 1.82 + NS_ASSERTION(inputChannelCount > 0, "Bad number of channels"); 1.83 + NS_ASSERTION(outputChannelCount > 0, "Bad number of channels"); 1.84 + 1.85 + aChannelArray->SetLength(outputChannelCount); 1.86 + 1.87 + if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS && 1.88 + outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) { 1.89 + const UpMixMatrix& m = gUpMixMatrices[ 1.90 + gMixingMatrixIndexByChannels[inputChannelCount - 1] + 1.91 + outputChannelCount - inputChannelCount - 1]; 1.92 + 1.93 + const void* outputChannels[CUSTOM_CHANNEL_LAYOUTS]; 1.94 + 1.95 + for (uint32_t i = 0; i < outputChannelCount; ++i) { 1.96 + uint8_t channelIndex = m.mInputDestination[i]; 1.97 + if (channelIndex == IGNORE) { 1.98 + outputChannels[i] = aZeroChannel; 1.99 + } else { 1.100 + outputChannels[i] = aChannelArray->ElementAt(channelIndex); 1.101 + } 1.102 + } 1.103 + for (uint32_t i = 0; i < outputChannelCount; ++i) { 1.104 + aChannelArray->ElementAt(i) = outputChannels[i]; 1.105 + } 1.106 + return; 1.107 + } 1.108 + 1.109 + for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) { 1.110 + aChannelArray->ElementAt(i) = aZeroChannel; 1.111 + } 1.112 +} 1.113 + 1.114 +/** 1.115 + * DownMixMatrix represents a conversion matrix efficiently by exploiting the 1.116 + * fact that each input channel contributes to at most one output channel, 1.117 + * except possibly for the C input channel in layouts that have one. Also, 1.118 + * every input channel is multiplied by the same coefficient for every output 1.119 + * channel it contributes to. 1.120 + */ 1.121 +struct DownMixMatrix { 1.122 + // Every input channel c is copied to output channel mInputDestination[c] 1.123 + // after multiplying by mInputCoefficient[c]. 1.124 + uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS]; 1.125 + // If not IGNORE, then the C channel is copied to this output channel after 1.126 + // multiplying by its coefficient. 1.127 + uint8_t mCExtraDestination; 1.128 + float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS]; 1.129 +}; 1.130 + 1.131 +static const DownMixMatrix 1.132 +gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] = 1.133 +{ 1.134 + // Downmixes to mono 1.135 + { { 0, 0 }, IGNORE, { 0.5f, 0.5f } }, 1.136 + { { 0, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F } }, 1.137 + { { 0, 0, 0, 0 }, IGNORE, { 0.25f, 0.25f, 0.25f, 0.25f } }, 1.138 + { { 0, IGNORE, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F } }, 1.139 + { { 0, 0, 0, IGNORE, 0, 0 }, IGNORE, { 0.7071f, 0.7071f, 1.0f, IGNORE_F, 0.5f, 0.5f } }, 1.140 + // Downmixes to stereo 1.141 + { { 0, 1, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F } }, 1.142 + { { 0, 1, 0, 1 }, IGNORE, { 0.5f, 0.5f, 0.5f, 0.5f } }, 1.143 + { { 0, 1, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } }, 1.144 + { { 0, 1, 0, IGNORE, 0, 1 }, 1, { 1.0f, 1.0f, 0.7071f, IGNORE_F, 0.7071f, 0.7071f } }, 1.145 + // Downmixes to 3-channel 1.146 + { { 0, 1, 2, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F } }, 1.147 + { { 0, 1, 2, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F } }, 1.148 + { { 0, 1, 2, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } }, 1.149 + // Downmixes to quad 1.150 + { { 0, 1, 2, 3, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } }, 1.151 + { { 0, 1, 0, IGNORE, 2, 3 }, 1, { 1.0f, 1.0f, 0.7071f, IGNORE_F, 1.0f, 1.0f } }, 1.152 + // Downmixes to 5-channel 1.153 + { { 0, 1, 2, 3, 4, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } } 1.154 +}; 1.155 + 1.156 +void 1.157 +AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray, 1.158 + float** aOutputChannels, 1.159 + uint32_t aOutputChannelCount, 1.160 + uint32_t aDuration) 1.161 +{ 1.162 + uint32_t inputChannelCount = aChannelArray.Length(); 1.163 + const void* const* inputChannels = aChannelArray.Elements(); 1.164 + NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do"); 1.165 + 1.166 + if (inputChannelCount > 6) { 1.167 + // Just drop the unknown channels. 1.168 + for (uint32_t o = 0; o < aOutputChannelCount; ++o) { 1.169 + memcpy(aOutputChannels[o], inputChannels[o], aDuration*sizeof(float)); 1.170 + } 1.171 + return; 1.172 + } 1.173 + 1.174 + // Ignore unknown channels, they're just dropped. 1.175 + inputChannelCount = std::min<uint32_t>(6, inputChannelCount); 1.176 + 1.177 + const DownMixMatrix& m = gDownMixMatrices[ 1.178 + gMixingMatrixIndexByChannels[aOutputChannelCount - 1] + 1.179 + inputChannelCount - aOutputChannelCount - 1]; 1.180 + 1.181 + // This is slow, but general. We can define custom code for special 1.182 + // cases later. 1.183 + for (uint32_t s = 0; s < aDuration; ++s) { 1.184 + // Reserve an extra junk channel at the end for the cases where we 1.185 + // want an input channel to contribute to nothing 1.186 + float outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1]; 1.187 + memset(outputChannels, 0, sizeof(float)*(CUSTOM_CHANNEL_LAYOUTS)); 1.188 + for (uint32_t c = 0; c < inputChannelCount; ++c) { 1.189 + outputChannels[m.mInputDestination[c]] += 1.190 + m.mInputCoefficient[c]*(static_cast<const float*>(inputChannels[c]))[s]; 1.191 + } 1.192 + // Utilize the fact that in every layout, C is the third channel. 1.193 + if (m.mCExtraDestination != IGNORE) { 1.194 + outputChannels[m.mCExtraDestination] += 1.195 + m.mInputCoefficient[SURROUND_C]*(static_cast<const float*>(inputChannels[SURROUND_C]))[s]; 1.196 + } 1.197 + 1.198 + for (uint32_t c = 0; c < aOutputChannelCount; ++c) { 1.199 + aOutputChannels[c][s] = outputChannels[c]; 1.200 + } 1.201 + } 1.202 +} 1.203 + 1.204 +}