content/media/AudioChannelFormat.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial