Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioChannelFormat.h
Line
Count
Source (jump to first uncovered line)
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 "nsTArrayForwardDeclare.h"
12
#include "AudioSampleFormat.h"
13
#include "nsTArray.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] =
52
  { 0, 5, 9, 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
59
GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2);
60
61
/**
62
 * DownMixMatrix represents a conversion matrix efficiently by exploiting the
63
 * fact that each input channel contributes to at most one output channel,
64
 * except possibly for the C input channel in layouts that have one. Also,
65
 * every input channel is multiplied by the same coefficient for every output
66
 * channel it contributes to.
67
 */
68
const float SQRT_ONE_HALF = 0.7071067811865476f;
69
70
struct DownMixMatrix {
71
  // Every input channel c is copied to output channel mInputDestination[c]
72
  // after multiplying by mInputCoefficient[c].
73
  uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
74
  // If not IGNORE, then the C channel is copied to this output channel after
75
  // multiplying by its coefficient.
76
  uint8_t mCExtraDestination;
77
  float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS];
78
};
79
80
static const DownMixMatrix
81
gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
82
{
83
  // Downmixes to mono
84
  { { 0, 0 }, IGNORE, { 0.5f, 0.5f } },
85
  { { 0, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F } },
86
  { { 0, 0, 0, 0 }, IGNORE, { 0.25f, 0.25f, 0.25f, 0.25f } },
87
  { { 0, IGNORE, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F } },
88
  { { 0, 0, 0, IGNORE, 0, 0 }, IGNORE, { SQRT_ONE_HALF, SQRT_ONE_HALF, 1.0f, IGNORE_F, 0.5f, 0.5f } },
89
  // Downmixes to stereo
90
  { { 0, 1, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F } },
91
  { { 0, 1, 0, 1 }, IGNORE, { 0.5f, 0.5f, 0.5f, 0.5f } },
92
  { { 0, 1, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } },
93
  { { 0, 1, 0, IGNORE, 0, 1 }, 1, { 1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, SQRT_ONE_HALF, SQRT_ONE_HALF } },
94
  // Downmixes to 3-channel
95
  { { 0, 1, 2, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F } },
96
  { { 0, 1, 2, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F } },
97
  { { 0, 1, 2, IGNORE, IGNORE, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, IGNORE_F, IGNORE_F, IGNORE_F } },
98
  // Downmixes to quad
99
  { { 0, 1, 2, 3, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } },
100
  { { 0, 1, 0, IGNORE, 2, 3 }, 1, { 1.0f, 1.0f, SQRT_ONE_HALF, IGNORE_F, 1.0f, 1.0f } },
101
  // Downmixes to 5-channel
102
  { { 0, 1, 2, 3, 4, IGNORE }, IGNORE, { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, IGNORE_F } }
103
};
104
105
/**
106
 * Given an array of input channels, downmix to aOutputChannelCount, and copy
107
 * the results to the channel buffers in aOutputChannels.  Don't call this with
108
 * input count <= output count.
109
 */
110
template<typename T>
111
void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
112
                          T** aOutputChannels,
113
                          uint32_t aOutputChannelCount,
114
                          uint32_t aDuration)
115
0
{
116
0
  uint32_t inputChannelCount = aChannelArray.Length();
117
0
  const T* const* inputChannels = aChannelArray.Elements();
118
0
  NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do");
119
0
120
0
  if (inputChannelCount > 6) {
121
0
    // Just drop the unknown channels.
122
0
    for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
123
0
      PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
124
0
    }
125
0
    return;
126
0
  }
127
0
128
0
  // Ignore unknown channels, they're just dropped.
129
0
  inputChannelCount = std::min<uint32_t>(6, inputChannelCount);
130
0
131
0
  const DownMixMatrix& m = gDownMixMatrices[
132
0
    gMixingMatrixIndexByChannels[aOutputChannelCount - 1] +
133
0
    inputChannelCount - aOutputChannelCount - 1];
134
0
135
0
  // This is slow, but general. We can define custom code for special
136
0
  // cases later.
137
0
  for (uint32_t s = 0; s < aDuration; ++s) {
138
0
    // Reserve an extra junk channel at the end for the cases where we
139
0
    // want an input channel to contribute to nothing
140
0
    T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
141
0
    for (uint32_t c = 0; c < inputChannelCount; ++c) {
142
0
      outputChannels[m.mInputDestination[c]] +=
143
0
        m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
144
0
    }
145
0
    // Utilize the fact that in every layout, C is the third channel.
146
0
    if (m.mCExtraDestination != IGNORE) {
147
0
      outputChannels[m.mCExtraDestination] +=
148
0
        m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
149
0
    }
150
0
151
0
    for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
152
0
      aOutputChannels[c][s] = outputChannels[c];
153
0
    }
154
0
  }
155
0
}
Unexecuted instantiation: void mozilla::AudioChannelsDownMix<float>(nsTArray<float const*> const&, float**, unsigned int, unsigned int)
Unexecuted instantiation: void mozilla::AudioChannelsDownMix<short>(nsTArray<short const*> const&, short**, unsigned int, unsigned int)
156
157
/**
158
 * UpMixMatrix represents a conversion matrix by exploiting the fact that
159
 * each output channel comes from at most one input channel.
160
 */
161
struct UpMixMatrix {
162
  uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
163
};
164
165
static const UpMixMatrix
166
gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
167
{
168
  // Upmixes from mono
169
  { { 0, 0 } },
170
  { { 0, IGNORE, IGNORE } },
171
  { { 0, 0, IGNORE, IGNORE } },
172
  { { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
173
  { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
174
  // Upmixes from stereo
175
  { { 0, 1, IGNORE } },
176
  { { 0, 1, IGNORE, IGNORE } },
177
  { { 0, 1, IGNORE, IGNORE, IGNORE } },
178
  { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
179
  // Upmixes from 3-channel
180
  { { 0, 1, 2, IGNORE } },
181
  { { 0, 1, 2, IGNORE, IGNORE } },
182
  { { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
183
  // Upmixes from quad
184
  { { 0, 1, 2, 3, IGNORE } },
185
  { { 0, 1, IGNORE, IGNORE, 2, 3 } },
186
  // Upmixes from 5-channel
187
  { { 0, 1, 2, 3, 4, IGNORE } }
188
};
189
190
191
/**
192
 * Given an array of input channel data, and an output channel count,
193
 * replaces the array with an array of upmixed channels.
194
 * This shuffles the array and may set some channel buffers to aZeroChannel.
195
 * Don't call this with input count >= output count.
196
 * This may return *more* channels than requested. In that case, downmixing
197
 * is required to to get to aOutputChannelCount. (This is how we handle
198
 * odd cases like 3 -> 4 upmixing.)
199
 * If aChannelArray.Length() was the input to one of a series of
200
 * GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
201
 * no downmixing will be required.
202
 */
203
template<typename T>
204
void
205
AudioChannelsUpMix(nsTArray<const T*>* aChannelArray,
206
                   uint32_t aOutputChannelCount,
207
                   const T* aZeroChannel)
208
0
{
209
0
  uint32_t inputChannelCount = aChannelArray->Length();
210
0
  uint32_t outputChannelCount =
211
0
    GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
212
0
  NS_ASSERTION(outputChannelCount > inputChannelCount,
213
0
               "No up-mix needed");
214
0
  MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
215
0
  MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
216
0
217
0
  aChannelArray->SetLength(outputChannelCount);
218
0
219
0
  if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
220
0
      outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
221
0
    const UpMixMatrix& m = gUpMixMatrices[
222
0
      gMixingMatrixIndexByChannels[inputChannelCount - 1] +
223
0
      outputChannelCount - inputChannelCount - 1];
224
0
225
0
    const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
226
0
227
0
    for (uint32_t i = 0; i < outputChannelCount; ++i) {
228
0
      uint8_t channelIndex = m.mInputDestination[i];
229
0
      if (channelIndex == IGNORE) {
230
0
        outputChannels[i] = aZeroChannel;
231
0
      } else {
232
0
        outputChannels[i] = aChannelArray->ElementAt(channelIndex);
233
0
      }
234
0
    }
235
0
    for (uint32_t i = 0; i < outputChannelCount; ++i) {
236
0
      aChannelArray->ElementAt(i) = outputChannels[i];
237
0
    }
238
0
    return;
239
0
  }
240
0
241
0
  for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
242
0
    aChannelArray->ElementAt(i) = aZeroChannel;
243
0
  }
244
0
}
Unexecuted instantiation: void mozilla::AudioChannelsUpMix<float>(nsTArray<float const*>*, unsigned int, float const*)
Unexecuted instantiation: void mozilla::AudioChannelsUpMix<short>(nsTArray<short const*>*, unsigned int, short const*)
245
246
} // namespace mozilla
247
248
#endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */