tor-browser

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

AudioBufferUtils.h (7564B)


      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/. */
      5 
      6 #ifndef MOZILLA_SCRATCHBUFFER_H_
      7 #define MOZILLA_SCRATCHBUFFER_H_
      8 
      9 #include <algorithm>
     10 
     11 #include "AudioSegment.h"
     12 #include "mozilla/PodOperations.h"
     13 #include "mozilla/UniquePtr.h"
     14 #include "nsDebug.h"
     15 
     16 namespace mozilla {
     17 
     18 /**
     19 * The classes in this file provide a interface that uses frames as a unit.
     20 * However, they store their offsets in samples (because it's handy for pointer
     21 * operations). Those functions can convert between the two units.
     22 */
     23 static inline uint32_t FramesToSamples(uint32_t aChannels, uint32_t aFrames) {
     24  return aFrames * aChannels;
     25 }
     26 
     27 static inline uint32_t SamplesToFrames(uint32_t aChannels, uint32_t aSamples) {
     28  MOZ_ASSERT(!(aSamples % aChannels), "Frame alignment is wrong.");
     29  return aSamples / aChannels;
     30 }
     31 
     32 /**
     33 * Class that gets a buffer pointer from an audio callback and provides a safe
     34 * interface to manipulate this buffer, and to ensure we are not missing frames
     35 * by the end of the callback.
     36 */
     37 template <typename T>
     38 class AudioCallbackBufferWrapper {
     39 public:
     40  AudioCallbackBufferWrapper()
     41      : mBuffer(nullptr), mSamples(0), mSampleWriteOffset(1), mChannels(0) {}
     42 
     43  explicit AudioCallbackBufferWrapper(uint32_t aChannels)
     44      : mBuffer(nullptr),
     45        mSamples(0),
     46        mSampleWriteOffset(1),
     47        mChannels(aChannels)
     48 
     49  {
     50    MOZ_ASSERT(aChannels);
     51  }
     52 
     53  AudioCallbackBufferWrapper& operator=(
     54      const AudioCallbackBufferWrapper& aOther) {
     55    MOZ_ASSERT(!aOther.mBuffer,
     56               "Don't use this ctor after AudioCallbackDriver::Init");
     57    MOZ_ASSERT(aOther.mSamples == 0,
     58               "Don't use this ctor after AudioCallbackDriver::Init");
     59    MOZ_ASSERT(aOther.mSampleWriteOffset == 1,
     60               "Don't use this ctor after AudioCallbackDriver::Init");
     61    MOZ_ASSERT(aOther.mChannels != 0);
     62 
     63    mBuffer = nullptr;
     64    mSamples = 0;
     65    mSampleWriteOffset = 1;
     66    mChannels = aOther.mChannels;
     67 
     68    return *this;
     69  }
     70 
     71  /**
     72   * Set the buffer in this wrapper. This is to be called at the beginning of
     73   * the callback.
     74   */
     75  void SetBuffer(T* aBuffer, uint32_t aFrames) {
     76    MOZ_ASSERT(!mBuffer && !mSamples, "SetBuffer called twice.");
     77    mBuffer = aBuffer;
     78    mSamples = FramesToSamples(mChannels, aFrames);
     79    mSampleWriteOffset = 0;
     80  }
     81 
     82  /**
     83   * Write some frames to the internal buffer. Free space in the buffer should
     84   * be checked prior to calling these.
     85   */
     86  void WriteSilence(const uint32_t aFrames) {
     87    MOZ_ASSERT(aFrames <= Available(),
     88               "Writing more than we can in the audio buffer.");
     89 
     90    std::fill_n(mBuffer + mSampleWriteOffset, aFrames, static_cast<T>(0));
     91    mSampleWriteOffset += FramesToSamples(mChannels, aFrames);
     92  }
     93  void WriteFrames(T* aBuffer, uint32_t aFrames) {
     94    MOZ_ASSERT(aFrames <= Available(),
     95               "Writing more than we can in the audio buffer.");
     96 
     97    PodCopy(mBuffer + mSampleWriteOffset, aBuffer,
     98            FramesToSamples(mChannels, aFrames));
     99    mSampleWriteOffset += FramesToSamples(mChannels, aFrames);
    100  }
    101  void WriteFrames(const AudioChunk& aChunk, uint32_t aFrames) {
    102    MOZ_ASSERT(aFrames <= Available(),
    103               "Writing more than we can in the audio buffer.");
    104 
    105    InterleaveAndConvertBuffer(aChunk.ChannelData<T>().Elements(), aFrames,
    106                               aChunk.mVolume, aChunk.ChannelCount(),
    107                               mBuffer + mSampleWriteOffset);
    108    mSampleWriteOffset += FramesToSamples(mChannels, aFrames);
    109  }
    110 
    111  /**
    112   * Number of frames that can be written to the buffer.
    113   */
    114  uint32_t Available() {
    115    return SamplesToFrames(mChannels, mSamples - mSampleWriteOffset);
    116  }
    117 
    118  /**
    119   * Check that the buffer is completly filled, and reset internal state so this
    120   * instance can be reused.
    121   */
    122  void BufferFilled() {
    123    MOZ_ASSERT(Available() == 0, "Frames should have been written");
    124    MOZ_ASSERT(mBuffer, "Buffer not set.");
    125    mSamples = 0;
    126    mSampleWriteOffset = 0;
    127    mBuffer = nullptr;
    128  }
    129 
    130 private:
    131  /* This is not an owned pointer, but the pointer passed to use via the audio
    132   * callback. */
    133  T* mBuffer;
    134  /* The number of samples of this audio buffer. */
    135  uint32_t mSamples;
    136  /* The position at which new samples should be written. We want to return to
    137   * the audio callback iff this is equal to mSamples. */
    138  uint32_t mSampleWriteOffset;
    139  uint32_t mChannels;
    140 };
    141 
    142 /**
    143 * This is a class that interfaces with the AudioCallbackBufferWrapper, and is
    144 * responsible for storing the excess of data produced by the MediaTrackGraph
    145 * because of different rounding constraints, to be used the next time the audio
    146 * backend calls back.
    147 */
    148 template <typename T, uint32_t BLOCK_SIZE>
    149 class SpillBuffer {
    150 public:
    151  SpillBuffer() : mBuffer(nullptr), mPosition(0), mChannels(0) {}
    152 
    153  explicit SpillBuffer(uint32_t aChannels)
    154      : mPosition(0), mChannels(aChannels) {
    155    MOZ_ASSERT(aChannels);
    156    mBuffer = MakeUnique<T[]>(BLOCK_SIZE * mChannels);
    157    PodZero(mBuffer.get(), BLOCK_SIZE * mChannels);
    158  }
    159 
    160  SpillBuffer& operator=(SpillBuffer& aOther) {
    161    MOZ_ASSERT(aOther.mPosition == 0,
    162               "Don't use this ctor after AudioCallbackDriver::Init");
    163    MOZ_ASSERT(aOther.mChannels != 0);
    164    MOZ_ASSERT(aOther.mBuffer);
    165 
    166    mPosition = aOther.mPosition;
    167    mChannels = aOther.mChannels;
    168    mBuffer = std::move(aOther.mBuffer);
    169 
    170    return *this;
    171  }
    172 
    173  SpillBuffer& operator=(SpillBuffer&& aOther) {
    174    return this->operator=(aOther);
    175  }
    176 
    177  bool IsEmpty() const { return mPosition == 0; }
    178  void Empty() { mPosition = 0; }
    179  /* Empty the spill buffer into the buffer of the audio callback. This returns
    180   * the number of frames written. */
    181  uint32_t Empty(AudioCallbackBufferWrapper<T>& aBuffer) {
    182    uint32_t framesToWrite =
    183        std::min(aBuffer.Available(), SamplesToFrames(mChannels, mPosition));
    184 
    185    aBuffer.WriteFrames(mBuffer.get(), framesToWrite);
    186 
    187    mPosition -= FramesToSamples(mChannels, framesToWrite);
    188    // If we didn't empty the spill buffer for some reason, shift the remaining
    189    // data down
    190    if (mPosition > 0) {
    191      MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <=
    192                 BLOCK_SIZE * mChannels);
    193      PodMove(mBuffer.get(),
    194              mBuffer.get() + FramesToSamples(mChannels, framesToWrite),
    195              mPosition);
    196    }
    197 
    198    return framesToWrite;
    199  }
    200  /* Fill the spill buffer from aInput.
    201   * Return the number of frames written to the spill buffer */
    202  uint32_t Fill(const AudioChunk& aInput) {
    203    uint32_t framesToWrite =
    204        std::min(static_cast<uint32_t>(aInput.mDuration),
    205                 BLOCK_SIZE - SamplesToFrames(mChannels, mPosition));
    206 
    207    MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <=
    208               BLOCK_SIZE * mChannels);
    209    InterleaveAndConvertBuffer(
    210        aInput.ChannelData<T>().Elements(), framesToWrite, aInput.mVolume,
    211        aInput.ChannelCount(), mBuffer.get() + mPosition);
    212 
    213    mPosition += FramesToSamples(mChannels, framesToWrite);
    214 
    215    return framesToWrite;
    216  }
    217 
    218 private:
    219  /* The spilled data. */
    220  UniquePtr<T[]> mBuffer;
    221  /* The current write position, in samples, in the buffer when filling, or the
    222   * amount of buffer filled when emptying. */
    223  uint32_t mPosition;
    224  uint32_t mChannels;
    225 };
    226 
    227 }  // namespace mozilla
    228 
    229 #endif  // MOZILLA_SCRATCHBUFFER_H_