tor-browser

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

AudioBlock.cpp (5709B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 #include "AudioBlock.h"
      8 
      9 #include "AlignmentUtils.h"
     10 
     11 namespace mozilla {
     12 
     13 /**
     14 * Heap-allocated buffer of channels of 128-sample float arrays, with
     15 * threadsafe refcounting.  Typically you would allocate one of these, fill it
     16 * in, and then treat it as immutable while it's shared.
     17 *
     18 * Downstream references are accounted specially so that the creator of the
     19 * buffer can reuse and modify its contents next iteration if other references
     20 * are all downstream temporary references held by AudioBlock.
     21 *
     22 * We guarantee 16 byte alignment of the channel data.
     23 */
     24 class AudioBlockBuffer final : public ThreadSharedObject {
     25 public:
     26  virtual AudioBlockBuffer* AsAudioBlockBuffer() override { return this; };
     27 
     28  uint32_t ChannelsAllocated() const { return mChannelsAllocated; }
     29  float* ChannelData(uint32_t aChannel) const {
     30    float* base =
     31        reinterpret_cast<float*>(((uintptr_t)(this + 1) + 15) & ~0x0F);
     32    ASSERT_ALIGNED16(base);
     33    return base + aChannel * WEBAUDIO_BLOCK_SIZE;
     34  }
     35 
     36  static already_AddRefed<AudioBlockBuffer> Create(uint32_t aChannelCount) {
     37    CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
     38    size *= aChannelCount;
     39    size *= sizeof(float);
     40    size += sizeof(AudioBlockBuffer);
     41    size += 15;  // padding for alignment
     42    if (!size.isValid()) {
     43      MOZ_CRASH();
     44    }
     45 
     46    void* m = operator new(size.value());
     47    RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer(aChannelCount);
     48    NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) -
     49                  reinterpret_cast<char*>(p.get())) %
     50                         4 ==
     51                     0,
     52                 "AudioBlockBuffers should be at least 4-byte aligned");
     53    return p.forget();
     54  }
     55 
     56  // Graph thread only.
     57  void DownstreamRefAdded() { ++mDownstreamRefCount; }
     58  void DownstreamRefRemoved() {
     59    MOZ_ASSERT(mDownstreamRefCount > 0);
     60    --mDownstreamRefCount;
     61  }
     62  // Whether this is shared by any owners that are not downstream.
     63  // Called only from owners with a reference that is not a downstream
     64  // reference.  Graph thread only.
     65  bool HasLastingShares() const {
     66    // mRefCnt is atomic and so reading its value is defined even when
     67    // modifications may happen on other threads.  mDownstreamRefCount is
     68    // not modified on any other thread.
     69    //
     70    // If all other references are downstream references (managed on this, the
     71    // graph thread), then other threads are not using this buffer and cannot
     72    // add further references.  This method can safely return false.  The
     73    // buffer contents can be modified.
     74    //
     75    // If there are other references that are not downstream references, then
     76    // this method will return true.  The buffer will be assumed to be still
     77    // in use and so will not be reused.
     78    nsrefcnt count = mRefCnt;
     79    // This test is strictly less than because the caller has a reference
     80    // that is not a downstream reference.
     81    MOZ_ASSERT(mDownstreamRefCount < count);
     82    return count != mDownstreamRefCount + 1;
     83  }
     84 
     85  virtual size_t SizeOfIncludingThis(
     86      MallocSizeOf aMallocSizeOf) const override {
     87    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     88  }
     89 
     90 private:
     91  explicit AudioBlockBuffer(uint32_t aChannelsAllocated)
     92      : mChannelsAllocated(aChannelsAllocated) {}
     93  ~AudioBlockBuffer() override { MOZ_ASSERT(mDownstreamRefCount == 0); }
     94 
     95  nsAutoRefCnt mDownstreamRefCount;
     96  const uint32_t mChannelsAllocated;
     97 };
     98 
     99 AudioBlock::~AudioBlock() { ClearDownstreamMark(); }
    100 
    101 void AudioBlock::SetBuffer(ThreadSharedObject* aNewBuffer) {
    102  if (aNewBuffer == mBuffer) {
    103    return;
    104  }
    105 
    106  ClearDownstreamMark();
    107 
    108  mBuffer = aNewBuffer;
    109 
    110  if (!aNewBuffer) {
    111    return;
    112  }
    113 
    114  AudioBlockBuffer* buffer = aNewBuffer->AsAudioBlockBuffer();
    115  if (buffer) {
    116    buffer->DownstreamRefAdded();
    117    mBufferIsDownstreamRef = true;
    118  }
    119 }
    120 
    121 void AudioBlock::ClearDownstreamMark() {
    122  if (mBufferIsDownstreamRef) {
    123    mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
    124    mBufferIsDownstreamRef = false;
    125  }
    126 }
    127 
    128 bool AudioBlock::CanWrite() {
    129  // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
    130  // It may be in use by another node which is not downstream.
    131  return !mBufferIsDownstreamRef &&
    132         !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
    133 }
    134 
    135 void AudioBlock::AllocateChannels(uint32_t aChannelCount) {
    136  MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);
    137 
    138  if (mBufferIsDownstreamRef) {
    139    // This is not our buffer to re-use.
    140    ClearDownstreamMark();
    141  } else if (mBuffer) {
    142    AudioBlockBuffer* buffer = mBuffer->AsAudioBlockBuffer();
    143    if (buffer && !buffer->HasLastingShares() &&
    144        buffer->ChannelsAllocated() >= aChannelCount) {
    145      MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
    146      // No need to allocate again.
    147      uint32_t previousChannelCount = ChannelCount();
    148      mChannelData.SetLength(aChannelCount);
    149      for (uint32_t i = previousChannelCount; i < aChannelCount; ++i) {
    150        mChannelData[i] = buffer->ChannelData(i);
    151      }
    152      mVolume = 1.0f;
    153      return;
    154    }
    155  }
    156 
    157  RefPtr<AudioBlockBuffer> buffer = AudioBlockBuffer::Create(aChannelCount);
    158  mChannelData.SetLength(aChannelCount);
    159  for (uint32_t i = 0; i < aChannelCount; ++i) {
    160    mChannelData[i] = buffer->ChannelData(i);
    161  }
    162  mBuffer = std::move(buffer);
    163  mVolume = 1.0f;
    164  mBufferFormat = AUDIO_FORMAT_FLOAT32;
    165 }
    166 
    167 }  // namespace mozilla