tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_ */