DXR will be turned off on Tuesday, December 29th. It will redirect to Searchfox.
See the announcement on Discourse.

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.

Header

Mercurial (d38398e5144e)

VCS Links

AudioBlockBuffer

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
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */

#include "AudioBlock.h"
#include "AlignmentUtils.h"

namespace mozilla {

/**
 * Heap-allocated buffer of channels of 128-sample float arrays, with
 * threadsafe refcounting.  Typically you would allocate one of these, fill it
 * in, and then treat it as immutable while it's shared.
 *
 * Downstream references are accounted specially so that the creator of the
 * buffer can reuse and modify its contents next iteration if other references
 * are all downstream temporary references held by AudioBlock.
 *
 * We guarantee 16 byte alignment of the channel data.
 */
class AudioBlockBuffer final : public ThreadSharedObject {
public:

  virtual AudioBlockBuffer* AsAudioBlockBuffer() override { return this; };

  float* ChannelData(uint32_t aChannel)
  {
    float* base = reinterpret_cast<float*>(((uintptr_t)(this + 1) + 15) & ~0x0F);
    ASSERT_ALIGNED16(base);
    return base + aChannel * WEBAUDIO_BLOCK_SIZE;
  }

  static already_AddRefed<AudioBlockBuffer> Create(uint32_t aChannelCount)
  {
    CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
    size *= aChannelCount;
    size *= sizeof(float);
    size += sizeof(AudioBlockBuffer);
    size += 15;  //padding for alignment
    if (!size.isValid()) {
      MOZ_CRASH();
    }

    void* m = moz_xmalloc(size.value());
    RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer();
    NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
                 "AudioBlockBuffers should be at least 4-byte aligned");
    return p.forget();
  }

  // Graph thread only.
  void DownstreamRefAdded() { ++mDownstreamRefCount; }
  void DownstreamRefRemoved() {
    MOZ_ASSERT(mDownstreamRefCount > 0);
    --mDownstreamRefCount;
  }
  // Whether this is shared by any owners that are not downstream.
  // Called only from owners with a reference that is not a downstream
  // reference.  Graph thread only.
  bool HasLastingShares()
  {
    // mRefCnt is atomic and so reading its value is defined even when
    // modifications may happen on other threads.  mDownstreamRefCount is
    // not modified on any other thread.
    //
    // If all other references are downstream references (managed on this, the
    // graph thread), then other threads are not using this buffer and cannot
    // add further references.  This method can safely return false.  The
    // buffer contents can be modified.
    //
    // If there are other references that are not downstream references, then
    // this method will return true.  The buffer will be assumed to be still
    // in use and so will not be reused.
    nsrefcnt count = mRefCnt;
    // This test is strictly less than because the caller has a reference
    // that is not a downstream reference.
    MOZ_ASSERT(mDownstreamRefCount < count);
    return count != mDownstreamRefCount + 1;
  }

  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
  {
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  }

private:
  AudioBlockBuffer() {}
  ~AudioBlockBuffer() override { MOZ_ASSERT(mDownstreamRefCount == 0); }

  nsAutoRefCnt mDownstreamRefCount;
};

AudioBlock::~AudioBlock()
{
  ClearDownstreamMark();
}

void
AudioBlock::SetBuffer(ThreadSharedObject* aNewBuffer)
{
  if (aNewBuffer == mBuffer) {
    return;
  }

  ClearDownstreamMark();

  mBuffer = aNewBuffer;

  if (!aNewBuffer) {
    return;
  }

  AudioBlockBuffer* buffer = aNewBuffer->AsAudioBlockBuffer();
  if (buffer) {
    buffer->DownstreamRefAdded();
    mBufferIsDownstreamRef = true;
  }
}

void
AudioBlock::ClearDownstreamMark() {
  if (mBufferIsDownstreamRef) {
    mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
    mBufferIsDownstreamRef = false;
  }
}

bool
AudioBlock::CanWrite() {
  // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
  // It may be in use by another node which is not downstream.
  return !mBufferIsDownstreamRef &&
    !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
}

void
AudioBlock::AllocateChannels(uint32_t aChannelCount)
{
  MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);

  if (mBufferIsDownstreamRef) {
    // This is not our buffer to re-use.
    ClearDownstreamMark();
  } else if (mBuffer && ChannelCount() == aChannelCount) {
    AudioBlockBuffer* buffer = mBuffer->AsAudioBlockBuffer();
    if (buffer && !buffer->HasLastingShares()) {
      MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
      // No need to allocate again.
      mVolume = 1.0f;
      return;
    }
  }

  RefPtr<AudioBlockBuffer> buffer = AudioBlockBuffer::Create(aChannelCount);
  mChannelData.SetLength(aChannelCount);
  for (uint32_t i = 0; i < aChannelCount; ++i) {
    mChannelData[i] = buffer->ChannelData(i);
  }
  mBuffer = buffer.forget();
  mVolume = 1.0f;
  mBufferFormat = AUDIO_FORMAT_FLOAT32;
}

} // namespace mozilla