tor-browser

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

AudioPacketizer.h (6566B)


      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 AudioPacketizer_h_
      7 #define AudioPacketizer_h_
      8 
      9 #include <AudioSampleFormat.h>
     10 #include <mozilla/Assertions.h>
     11 #include <mozilla/PodOperations.h>
     12 #include <mozilla/UniquePtr.h>
     13 
     14 // Enable this to warn when `Output` has been called but not enough data was
     15 // buffered.
     16 // #define LOG_PACKETIZER_UNDERRUN
     17 
     18 namespace mozilla {
     19 /**
     20 * This class takes arbitrary input data, and returns packets of a specific
     21 * size. In the process, it can convert audio samples from 16bit integers to
     22 * float (or vice-versa).
     23 *
     24 * Input and output, as well as length units in the public interface are
     25 * interleaved frames.
     26 *
     27 * Allocations of output buffer can be performed by this class.  Buffers can
     28 * simply be delete-d.  This is because packets are intended to be sent off to
     29 * non-gecko code using normal pointers/length pairs
     30 *
     31 * Alternatively, consumers can pass in a buffer in which the output is copied.
     32 * The buffer needs to be large enough to store a packet worth of audio.
     33 *
     34 * The implementation uses a circular buffer using absolute virtual indices.
     35 */
     36 template <typename InputType, typename OutputType>
     37 class AudioPacketizer {
     38 public:
     39  AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
     40      : mPacketSize(aPacketSize),
     41        mChannels(aChannels),
     42        mReadIndex(0),
     43        mWriteIndex(0),
     44        // Start off with a single packet
     45        mStorage(new InputType[aPacketSize * aChannels]),
     46        mLength(aPacketSize * aChannels) {
     47    MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
     48               "The packet size and the number of channel should be strictly "
     49               "positive");
     50  }
     51 
     52  void Input(const InputType* aFrames, uint32_t aFrameCount) {
     53    uint32_t inputSamples = aFrameCount * mChannels;
     54    // Need to grow the storage. This should rarely happen, if at all, once the
     55    // array has the right size.
     56    if (inputSamples > EmptySlots()) {
     57      // Calls to Input and Output are roughtly interleaved
     58      // (Input,Output,Input,Output, etc.), or balanced
     59      // (Input,Input,Input,Output,Output,Output), so we update the buffer to
     60      // the exact right size in order to not waste space.
     61      uint32_t newLength = AvailableSamples() + inputSamples;
     62      uint32_t toCopy = AvailableSamples();
     63      UniquePtr<InputType[]> oldStorage = std::move(mStorage);
     64      mStorage = mozilla::MakeUnique<InputType[]>(newLength);
     65      // Copy the old data at the beginning of the new storage.
     66      if (WriteIndex() >= ReadIndex()) {
     67        PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(),
     68                AvailableSamples());
     69      } else {
     70        uint32_t firstPartLength = mLength - ReadIndex();
     71        uint32_t secondPartLength = AvailableSamples() - firstPartLength;
     72        PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(),
     73                firstPartLength);
     74        PodCopy(mStorage.get() + firstPartLength, oldStorage.get(),
     75                secondPartLength);
     76      }
     77      mWriteIndex = toCopy;
     78      mReadIndex = 0;
     79      mLength = newLength;
     80    }
     81 
     82    if (WriteIndex() + inputSamples <= mLength) {
     83      PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
     84    } else {
     85      uint32_t firstPartLength = mLength - WriteIndex();
     86      uint32_t secondPartLength = inputSamples - firstPartLength;
     87      PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
     88      PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
     89    }
     90 
     91    mWriteIndex += inputSamples;
     92  }
     93 
     94  OutputType* Output() {
     95    uint32_t samplesNeeded = mPacketSize * mChannels;
     96    OutputType* out = new OutputType[samplesNeeded];
     97 
     98    Output(out);
     99 
    100    return out;
    101  }
    102 
    103  // Return the number of actual frames dequeued -- this can be lower than the
    104  // packet size when underruning or draining.
    105  size_t Output(OutputType* aOutputBuffer) {
    106    uint32_t samplesNeeded = mPacketSize * mChannels;
    107    size_t rv = 0;
    108 
    109    // Under-run. Pad the end of the buffer with silence.
    110    if (AvailableSamples() < samplesNeeded) {
    111      rv = AvailableSamples() / mChannels;
    112 #ifdef LOG_PACKETIZER_UNDERRUN
    113      char buf[256];
    114      snprintf(buf, 256,
    115               "AudioPacketizer %p underrun: available: %u, needed: %u\n", this,
    116               AvailableSamples(), samplesNeeded);
    117      NS_WARNING(buf);
    118 #endif
    119      uint32_t zeros = samplesNeeded - AvailableSamples();
    120      PodZero(aOutputBuffer + AvailableSamples(), zeros);
    121      samplesNeeded -= zeros;
    122    } else {
    123      rv = mPacketSize;
    124    }
    125    if (ReadIndex() + samplesNeeded <= mLength) {
    126      ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
    127                                                 aOutputBuffer, samplesNeeded);
    128    } else {
    129      uint32_t firstPartLength = mLength - ReadIndex();
    130      uint32_t secondPartLength = samplesNeeded - firstPartLength;
    131      ConvertAudioSamples<InputType, OutputType>(
    132          mStorage.get() + ReadIndex(), aOutputBuffer, firstPartLength);
    133      ConvertAudioSamples<InputType, OutputType>(
    134          mStorage.get(), aOutputBuffer + firstPartLength, secondPartLength);
    135    }
    136    mReadIndex += samplesNeeded;
    137    return rv;
    138  }
    139 
    140  void Clear() {
    141    mReadIndex = 0;
    142    mWriteIndex = 0;
    143  }
    144 
    145  uint32_t PacketsAvailable() const {
    146    return AvailableSamples() / mChannels / mPacketSize;
    147  }
    148 
    149  uint32_t FramesAvailable() const { return AvailableSamples() / mChannels; }
    150 
    151  bool Empty() const { return mWriteIndex == mReadIndex; }
    152 
    153  bool Full() const { return mWriteIndex - mReadIndex == mLength; }
    154 
    155  // Size of one packet of audio, in frames
    156  const uint32_t mPacketSize;
    157  // Number of channels of the stream flowing through this packetizer
    158  const uint32_t mChannels;
    159 
    160 private:
    161  uint32_t ReadIndex() const { return mReadIndex % mLength; }
    162 
    163  uint32_t WriteIndex() const { return mWriteIndex % mLength; }
    164 
    165  uint32_t AvailableSamples() const { return mWriteIndex - mReadIndex; }
    166 
    167  uint32_t EmptySlots() const { return mLength - AvailableSamples(); }
    168 
    169  // Two virtual index into the buffer: the read position and the write
    170  // position.
    171  uint64_t mReadIndex;
    172  uint64_t mWriteIndex;
    173  // Storage for the samples
    174  mozilla::UniquePtr<InputType[]> mStorage;
    175  // Length of the buffer, in samples
    176  uint32_t mLength;
    177 };
    178 
    179 }  // namespace mozilla
    180 
    181 #endif  // AudioPacketizer_h_