summaryrefslogtreecommitdiff
path: root/dom/media/webaudio/AudioBlock.h
blob: c9a5bb40098388a506c3bb03dd94057d23e5dd23 (plain)
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
/* -*- 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/. */
#ifndef MOZILLA_AUDIOBLOCK_H_
#define MOZILLA_AUDIOBLOCK_H_

#include "AudioSegment.h"

namespace mozilla {

/**
 * An AudioChunk whose buffer contents need to be valid only for one
 * processing block iteration, after which contents can be overwritten if the
 * buffer has not been passed to longer term storage or to another thread,
 * which may happen though AsAudioChunk() or AsMutableChunk().
 *
 * Use on graph thread only.
 */
class AudioBlock : private AudioChunk
{
public:
  AudioBlock() {
    mDuration = WEBAUDIO_BLOCK_SIZE;
    mBufferFormat = AUDIO_FORMAT_SILENCE;
  }
  // No effort is made in constructors to ensure that mBufferIsDownstreamRef
  // is set because the block is expected to be a temporary and so the
  // reference will be released before the next iteration.
  // The custom copy constructor is required so as not to set
  // mBufferIsDownstreamRef without notifying AudioBlockBuffer.
  AudioBlock(const AudioBlock& aBlock) : AudioChunk(aBlock.AsAudioChunk()) {}
  explicit AudioBlock(const AudioChunk& aChunk)
    : AudioChunk(aChunk)
  {
    MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
  }
  ~AudioBlock();

  using AudioChunk::GetDuration;
  using AudioChunk::IsNull;
  using AudioChunk::ChannelCount;
  using AudioChunk::ChannelData;
  using AudioChunk::SizeOfExcludingThisIfUnshared;
  using AudioChunk::SizeOfExcludingThis;
  // mDuration is not exposed.  Use GetDuration().
  // mBuffer is not exposed.  Use SetBuffer().
  using AudioChunk::mChannelData;
  using AudioChunk::mVolume;
  using AudioChunk::mBufferFormat;

  const AudioChunk& AsAudioChunk() const { return *this; }
  AudioChunk* AsMutableChunk() {
    ClearDownstreamMark();
    return this;
  }

  /**
   * Allocates, if necessary, aChannelCount buffers of WEBAUDIO_BLOCK_SIZE float
   * samples for writing.
   */
  void AllocateChannels(uint32_t aChannelCount);

  /**
   * ChannelFloatsForWrite() should only be used when the buffers have been
   * created with AllocateChannels().
   */
  float* ChannelFloatsForWrite(size_t aChannel)
  {
    MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
    MOZ_ASSERT(CanWrite());
    return static_cast<float*>(const_cast<void*>(mChannelData[aChannel]));
  }

  void SetBuffer(ThreadSharedObject* aNewBuffer);
  void SetNull(StreamTime aDuration) {
    MOZ_ASSERT(aDuration == WEBAUDIO_BLOCK_SIZE);
    SetBuffer(nullptr);
    mChannelData.Clear();
    mVolume = 1.0f;
    mBufferFormat = AUDIO_FORMAT_SILENCE;
  }

  AudioBlock& operator=(const AudioBlock& aBlock) {
    // Instead of just copying, mBufferIsDownstreamRef must be first cleared
    // if set.  It is set again for the new mBuffer if possible.  This happens
    // in SetBuffer().
    return *this = aBlock.AsAudioChunk();
  }
  AudioBlock& operator=(const AudioChunk& aChunk) {
    MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
    SetBuffer(aChunk.mBuffer);
    mChannelData = aChunk.mChannelData;
    mVolume = aChunk.mVolume;
    mBufferFormat = aChunk.mBufferFormat;
    return *this;
  }

  bool IsMuted() const { return mVolume == 0.0f; }

  bool IsSilentOrSubnormal() const
  {
    if (!mBuffer) {
      return true;
    }

    for (uint32_t i = 0, length = mChannelData.Length(); i < length; ++i) {
      const float* channel = static_cast<const float*>(mChannelData[i]);
      for (StreamTime frame = 0; frame < mDuration; ++frame) {
        if (fabs(channel[frame]) >= FLT_MIN) {
          return false;
        }
      }
    }

    return true;
  }

private:
  void ClearDownstreamMark();
  bool CanWrite();

  // mBufferIsDownstreamRef is set only when mBuffer references an
  // AudioBlockBuffer created in a different AudioBlock.  That can happen when
  // this AudioBlock is on a node downstream from the node which created the
  // buffer.  When this is set, the AudioBlockBuffer is notified that this
  // reference does prevent the upstream node from re-using the buffer next
  // iteration and modifying its contents.  The AudioBlockBuffer is also
  // notified when mBuffer releases this reference.
  bool mBufferIsDownstreamRef = false;
};

} // namespace mozilla

#endif // MOZILLA_AUDIOBLOCK_H_