tor-browser

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

AudioConverter.h (9992B)


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