Coverage Report

Created: 2018-09-25 14:53

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