DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef AudioPacketizer_h_
#define AudioPacketizer_h_

#include <mozilla/PodOperations.h>
#include <mozilla/Assertions.h>
#include <mozilla/UniquePtr.h>
#include <AudioSampleFormat.h>

// Enable this to warn when `Output` has been called but not enough data was
// buffered.
// #define LOG_PACKETIZER_UNDERRUN

namespace mozilla {
/**
 * This class takes arbitrary input data, and returns packets of a specific
 * size. In the process, it can convert audio samples from 16bit integers to
 * float (or vice-versa).
 *
 * Input and output, as well as length units in the public interface are
 * interleaved frames.
 *
 * Allocations of output buffer can be performed by this class.  Buffers can
 * simply be delete-d.  This is because packets are intended to be sent off to
 * non-gecko code using normal pointers/length pairs
 *
 * Alternatively, consumers can pass in a buffer in which the output is copied.
 * The buffer needs to be large enough to store a packet worth of audio.
 *
 * The implementation uses a circular buffer using absolute virtual indices.
 */
template <typename InputType, typename OutputType>
class AudioPacketizer
{
public:
  AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
    : mPacketSize(aPacketSize)
    , mChannels(aChannels)
    , mReadIndex(0)
    , mWriteIndex(0)
    // Start off with a single packet
    , mStorage(new InputType[aPacketSize * aChannels])
    , mLength(aPacketSize * aChannels)
  {
     MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
       "The packet size and the number of channel should be strictly positive");
  }

  void Input(const InputType* aFrames, uint32_t aFrameCount)
  {
    uint32_t inputSamples = aFrameCount * mChannels;
    // Need to grow the storage. This should rarely happen, if at all, once the
    // array has the right size.
    if (inputSamples > EmptySlots()) {
      // Calls to Input and Output are roughtly interleaved
      // (Input,Output,Input,Output, etc.), or balanced
      // (Input,Input,Input,Output,Output,Output), so we update the buffer to
      // the exact right size in order to not waste space.
      uint32_t newLength = AvailableSamples() + inputSamples;
      uint32_t toCopy = AvailableSamples();
      UniquePtr<InputType[]> oldStorage = std::move(mStorage);
      mStorage = mozilla::MakeUnique<InputType[]>(newLength);
      // Copy the old data at the beginning of the new storage.
      if (WriteIndex() >= ReadIndex()) {
        PodCopy(mStorage.get(),
                oldStorage.get() + ReadIndex(),
                AvailableSamples());
      } else {
        uint32_t firstPartLength = mLength - ReadIndex();
        uint32_t secondPartLength = AvailableSamples() - firstPartLength;
        PodCopy(mStorage.get(),
                oldStorage.get() + ReadIndex(),
                firstPartLength);
        PodCopy(mStorage.get() + firstPartLength,
                oldStorage.get(),
                secondPartLength);
      }
      mWriteIndex = toCopy;
      mReadIndex = 0;
      mLength = newLength;
    }

    if (WriteIndex() + inputSamples <= mLength) {
      PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
    } else {
      uint32_t firstPartLength = mLength - WriteIndex();
      uint32_t secondPartLength = inputSamples - firstPartLength;
      PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
      PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
    }

    mWriteIndex += inputSamples;
  }

  OutputType* Output()
  {
    uint32_t samplesNeeded = mPacketSize * mChannels;
    OutputType* out = new OutputType[samplesNeeded];

    Output(out);

    return out;
  }

  void Output(OutputType* aOutputBuffer)
  {
    uint32_t samplesNeeded = mPacketSize * mChannels;

    // Under-run. Pad the end of the buffer with silence.
    if (AvailableSamples() < samplesNeeded) {
#ifdef LOG_PACKETIZER_UNDERRUN
      char buf[256];
      snprintf(buf, 256,
               "AudioPacketizer %p underrun: available: %u, needed: %u\n",
               this, AvailableSamples(), samplesNeeded);
      NS_WARNING(buf);
#endif
      uint32_t zeros = samplesNeeded - AvailableSamples();
      PodZero(aOutputBuffer + AvailableSamples(), zeros);
      samplesNeeded -= zeros;
    }
    if (ReadIndex() + samplesNeeded <= mLength) {
      ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(),
                                                aOutputBuffer,
                                                samplesNeeded);
    } else {
      uint32_t firstPartLength = mLength - ReadIndex();
      uint32_t secondPartLength = samplesNeeded - firstPartLength;
      ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
                                                 aOutputBuffer,
                                                 firstPartLength);
      ConvertAudioSamples<InputType, OutputType>(mStorage.get(),
                                                 aOutputBuffer + firstPartLength,
                                                 secondPartLength);
    }
    mReadIndex += samplesNeeded;
  }

  uint32_t PacketsAvailable() const {
    return AvailableSamples() / mChannels / mPacketSize;
  }

  bool Empty() const {
   return mWriteIndex == mReadIndex;
  }

  bool Full() const {
    return mWriteIndex - mReadIndex == mLength;
  }

  uint32_t PacketSize() const {
    return mPacketSize;
  }

  uint32_t Channels() const {
    return mChannels;
  }

private:
  uint32_t ReadIndex() const {
    return mReadIndex % mLength;
  }

  uint32_t WriteIndex() const {
    return mWriteIndex % mLength;
  }

  uint32_t AvailableSamples() const {
    return mWriteIndex - mReadIndex;
  }

  uint32_t EmptySlots() const {
    return mLength - AvailableSamples();
  }

  // Size of one packet of audio, in frames
  uint32_t mPacketSize;
  // Number of channels of the stream flowing through this packetizer
  uint32_t mChannels;
  // Two virtual index into the buffer: the read position and the write
  // position.
  uint64_t mReadIndex;
  uint64_t mWriteIndex;
  // Storage for the samples
  mozilla::UniquePtr<InputType[]> mStorage;
  // Length of the buffer, in samples
  uint32_t mLength;
};

} // mozilla

#endif // AudioPacketizer_h_