content/media/AudioChannelFormat.cpp

changeset 0
6474c204b198
     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 +}

mercurial