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

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

mercurial