Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioConverter.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=8 sts=2 et sw=2 tw=80: */
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
7
#if !defined(AudioConverter_h)
8
#define AudioConverter_h
9
10
#include "MediaInfo.h"
11
12
// Forward declaration
13
typedef struct SpeexResamplerState_ SpeexResamplerState;
14
15
namespace mozilla {
16
17
template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser;
18
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
19
{ typedef uint8_t Type; };
20
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
21
{ typedef int16_t Type; };
22
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
23
{ typedef int32_t Type; };
24
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
25
{ typedef int32_t Type; };
26
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
27
{ typedef int32_t Type; };
28
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
29
{ typedef float Type; };
30
31
// 'Value' is the type used externally to deal with stored value.
32
// AudioDataBuffer can perform conversion between different SampleFormat content.
33
template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
34
class AudioDataBuffer
35
{
36
public:
37
  AudioDataBuffer() {}
38
  AudioDataBuffer(Value* aBuffer, size_t aLength)
39
    : mBuffer(aBuffer, aLength)
40
  {}
41
  explicit AudioDataBuffer(const AudioDataBuffer& aOther)
42
    : mBuffer(aOther.mBuffer)
43
  {}
44
  AudioDataBuffer(AudioDataBuffer&& aOther)
45
    : mBuffer(std::move(aOther.mBuffer))
46
  {}
47
  template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
48
  explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
49
  {
50
    // TODO: Convert from different type, may use asm routines.
51
    MOZ_CRASH("Conversion not implemented yet");
52
  }
53
54
  // A u8, s16 and float aligned buffer can only be treated as
55
  // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
56
  // So allow them as copy and move constructors.
57
  explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
58
    : mBuffer(aBuffer)
59
  {
60
    static_assert(Format == AudioConfig::FORMAT_U8,
61
                  "Conversion not implemented yet");
62
  }
63
  explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
64
    : mBuffer(aBuffer)
65
  {
66
    static_assert(Format == AudioConfig::FORMAT_S16,
67
                  "Conversion not implemented yet");
68
  }
69
  explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
70
    : mBuffer(aBuffer)
71
  {
72
    static_assert(Format == AudioConfig::FORMAT_FLT,
73
                  "Conversion not implemented yet");
74
  }
75
  explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
76
    : mBuffer(std::move(aBuffer))
77
  {
78
    static_assert(Format == AudioConfig::FORMAT_U8,
79
                  "Conversion not implemented yet");
80
  }
81
  explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
82
    : mBuffer(std::move(aBuffer))
83
  {
84
    static_assert(Format == AudioConfig::FORMAT_S16,
85
                  "Conversion not implemented yet");
86
  }
87
  explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer)
88
    : mBuffer(std::move(aBuffer))
89
  {
90
    static_assert(Format == AudioConfig::FORMAT_FLT,
91
                  "Conversion not implemented yet");
92
  }
93
  AudioDataBuffer& operator=(AudioDataBuffer&& aOther)
94
  {
95
    mBuffer = std::move(aOther.mBuffer);
96
    return *this;
97
  }
98
  AudioDataBuffer& operator=(const AudioDataBuffer& aOther)
99
  {
100
    mBuffer = aOther.mBuffer;
101
    return *this;
102
  }
103
104
  Value* Data() const { return mBuffer.Data(); }
105
  size_t Length() const { return mBuffer.Length(); }
106
  size_t Size() const { return mBuffer.Size(); }
107
  AlignedBuffer<Value> Forget()
108
  {
109
    // Correct type -> Just give values as-is.
110
    return std::move(mBuffer);
111
  }
112
private:
113
  AlignedBuffer<Value> mBuffer;
114
};
115
116
typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
117
118
class AudioConverter {
119
public:
120
  AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
121
  ~AudioConverter();
122
123
  // Convert the AudioDataBuffer.
124
  // Conversion will be done in place if possible. Otherwise a new buffer will
125
  // be returned.
126
  // Providing an empty buffer and resampling is expected, the resampler
127
  // will be drained.
128
  template <AudioConfig::SampleFormat Format, typename Value>
129
  AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer)
130
  {
131
    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format);
132
    AudioDataBuffer<Format, Value> buffer = std::move(aBuffer);
133
    if (CanWorkInPlace()) {
134
      AlignedBuffer<Value> temp = buffer.Forget();
135
      Process(temp, temp.Data(), SamplesInToFrames(temp.Length()));
136
      return AudioDataBuffer<Format, Value>(std::move(temp));;
137
    }
138
    return Process(buffer);
139
  }
140
141
  template <AudioConfig::SampleFormat Format, typename Value>
142
  AudioDataBuffer<Format, Value> Process(const AudioDataBuffer<Format, Value>& aBuffer)
143
  {
144
    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format);
145
    // Perform the downmixing / reordering in temporary buffer.
146
    size_t frames = SamplesInToFrames(aBuffer.Length());
147
    AlignedBuffer<Value> temp1;
148
    if (!temp1.SetLength(FramesOutToSamples(frames))) {
149
      return AudioDataBuffer<Format, Value>(std::move(temp1));
150
    }
151
    frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames);
152
    if (mIn.Rate() == mOut.Rate()) {
153
      MOZ_ALWAYS_TRUE(temp1.SetLength(FramesOutToSamples(frames)));
154
      return AudioDataBuffer<Format, Value>(std::move(temp1));
155
    }
156
157
    // At this point, temp1 contains the buffer reordered and downmixed.
158
    // If we are downsampling we can re-use it.
159
    AlignedBuffer<Value>* outputBuffer = &temp1;
160
    AlignedBuffer<Value> temp2;
161
    if (!frames || mOut.Rate() > mIn.Rate()) {
162
      // We are upsampling or about to drain, we can't work in place.
163
      // Allocate another temporary buffer where the upsampling will occur.
164
      if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) {
165
        return AudioDataBuffer<Format, Value>(std::move(temp2));
166
      }
167
      outputBuffer = &temp2;
168
    }
169
    if (!frames) {
170
      frames = DrainResampler(outputBuffer->Data());
171
    } else {
172
      frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames);
173
    }
174
    MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames)));
175
    return AudioDataBuffer<Format, Value>(std::move(*outputBuffer));
176
  }
177
178
  // Attempt to convert the AudioDataBuffer in place.
179
  // Will return 0 if the conversion wasn't possible.
180
  template <typename Value>
181
  size_t Process(Value* aBuffer, size_t aFrames)
182
0
  {
183
0
    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format());
184
0
    if (!CanWorkInPlace()) {
185
0
      return 0;
186
0
    }
187
0
    size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames);
188
0
    if (frames && mIn.Rate() != mOut.Rate()) {
189
0
      frames = ResampleAudio(aBuffer, aBuffer, aFrames);
190
0
    }
191
0
    return frames;
192
0
  }
193
194
  template <typename Value>
195
  size_t Process(AlignedBuffer<Value>& aOutBuffer, const Value* aInBuffer, size_t aFrames)
196
0
  {
197
0
    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format());
198
0
    MOZ_ASSERT((aFrames && aInBuffer) || !aFrames);
199
0
    // Up/down mixing first
200
0
    if (!aOutBuffer.SetLength(FramesOutToSamples(aFrames))) {
201
0
      MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0));
202
0
      return 0;
203
0
    }
204
0
    size_t frames = ProcessInternal(aOutBuffer.Data(), aInBuffer, aFrames);
205
0
    MOZ_ASSERT(frames == aFrames);
206
0
    // Check if resampling is needed
207
0
    if (mIn.Rate() == mOut.Rate()) {
208
0
      return frames;
209
0
    }
210
0
    // Prepare output in cases of drain or up-sampling
211
0
    if ((!frames || mOut.Rate() > mIn.Rate()) &&
212
0
        !aOutBuffer.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) {
213
0
      MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0));
214
0
      return 0;
215
0
    }
216
0
    if (!frames) {
217
0
      frames = DrainResampler(aOutBuffer.Data());
218
0
    } else {
219
0
      frames = ResampleAudio(aOutBuffer.Data(), aInBuffer, frames);
220
0
    }
221
0
    // Update with the actual buffer length
222
0
    MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(FramesOutToSamples(frames)));
223
0
    return frames;
224
0
  }
225
226
  bool CanWorkInPlace() const;
227
  bool CanReorderAudio() const
228
0
  {
229
0
    return mIn.Layout().MappingTable(mOut.Layout());
230
0
  }
231
232
0
  const AudioConfig& InputConfig() const { return mIn; }
233
0
  const AudioConfig& OutputConfig() const { return mOut; }
234
235
private:
236
  const AudioConfig mIn;
237
  const AudioConfig mOut;
238
  // mChannelOrderMap will be empty if we do not know how to proceed with this
239
  // channel layout.
240
  AutoTArray<uint8_t, AudioConfig::ChannelLayout::MAX_CHANNELS> mChannelOrderMap;
241
  /**
242
   * ProcessInternal
243
   * Parameters:
244
   * aOut  : destination buffer where converted samples will be copied
245
   * aIn   : source buffer
246
   * aSamples: number of frames in source buffer
247
   *
248
   * Return Value: number of frames converted or 0 if error
249
   */
250
  size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames);
251
  void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const;
252
  size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
253
  size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
254
255
  size_t FramesOutToSamples(size_t aFrames) const;
256
  size_t SamplesInToFrames(size_t aSamples) const;
257
  size_t FramesOutToBytes(size_t aFrames) const;
258
259
  // Resampler context.
260
  SpeexResamplerState* mResampler;
261
  size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);
262
  size_t ResampleRecipientFrames(size_t aFrames) const;
263
  void RecreateResampler();
264
  size_t DrainResampler(void* aOut);
265
};
266
267
} // namespace mozilla
268
269
#endif /* AudioConverter_h */