summaryrefslogtreecommitdiff
path: root/media/base/audio_fifo.cc
blob: b6e8f806e056ca5d2e58e22a7b677cb569c8ef8d (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
137
138
139
140
141
142
143
144
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/audio_fifo.h"

#include "base/logging.h"

using base::subtle::Atomic32;
using base::subtle::NoBarrier_Store;

namespace media {

// Given current position in the FIFO, the maximum number of elements in the
// FIFO and the size of the input; this method provides two output results:
// |size| and |wrap_size|. These two results can then be utilized for memcopy
// operations to and from the FIFO.
// Under "normal" circumstances, |size| will be equal to |in_size| and
// |wrap_size| will be zero. This case corresponding to the non-wrapping case
// where we have not yet reached the "edge" of the FIFO. If |pos| + |in_size|
// exceeds the total size of the FIFO, we must wrap around and start reusing
// a part the allocated memory. The size of this part is given by |wrap_size|.
static void GetSizes(
    int pos, int max_size, int in_size, int* size, int* wrap_size) {
  if (pos + in_size > max_size) {
    // Wrapping is required => derive size of each segment.
    *size = max_size - pos;
    *wrap_size = in_size - *size;
  } else {
    // Wrapping is not required.
    *size = in_size;
    *wrap_size = 0;
  }
}

// Updates the read/write position with |step| modulo the maximum number of
// elements in the FIFO to ensure that the position counters wraps around at
// the endpoint.
static int UpdatePos(int pos, int step, int max_size) {
  return ((pos + step) % max_size);
}

AudioFifo::AudioFifo(int channels, int frames)
    : audio_bus_(AudioBus::Create(channels, frames)),
      max_frames_(frames),
      frames_pushed_(0),
      frames_consumed_(0),
      read_pos_(0),
      write_pos_(0) {}

AudioFifo::~AudioFifo() {}

int AudioFifo::frames() const {
  int delta = frames_pushed_ - frames_consumed_;
  base::subtle::MemoryBarrier();
  return delta;
}

void AudioFifo::Push(const AudioBus* source) {
  DCHECK(source);
  DCHECK_EQ(source->channels(), audio_bus_->channels());

  // Ensure that there is space for the new data in the FIFO.
  const int source_size = source->frames();
  CHECK_LE(source_size + frames(), max_frames_);

  // Figure out if wrapping is needed and if so what segment sizes we need
  // when adding the new audio bus content to the FIFO.
  int append_size = 0;
  int wrap_size = 0;
  GetSizes(write_pos_, max_frames(), source_size, &append_size, &wrap_size);

  // Copy all channels from the source to the FIFO. Wrap around if needed.
  for (int ch = 0; ch < source->channels(); ++ch) {
    float* dest = audio_bus_->channel(ch);
    const float* src = source->channel(ch);

    // Append part of (or the complete) source to the FIFO.
    memcpy(&dest[write_pos_], &src[0], append_size * sizeof(src[0]));
    if (wrap_size > 0) {
      // Wrapping is needed: copy remaining part from the source to the FIFO.
      memcpy(&dest[0], &src[append_size], wrap_size * sizeof(src[0]));
    }
  }

  // Ensure the data is *really* written before updating |frames_pushed_|.
  base::subtle::MemoryBarrier();

  Atomic32 new_frames_pushed = frames_pushed_ + source_size;
  NoBarrier_Store(&frames_pushed_, new_frames_pushed);

  DCHECK_LE(frames(), max_frames());
  write_pos_ = UpdatePos(write_pos_, source_size, max_frames());
}

void AudioFifo::Consume(AudioBus* destination,
                        int start_frame,
                        int frames_to_consume) {
  DCHECK(destination);
  DCHECK_EQ(destination->channels(), audio_bus_->channels());

  // It is not possible to ask for more data than what is available in the FIFO.
  CHECK_LE(frames_to_consume, frames());

  // A copy from the FIFO to |destination| will only be performed if the
  // allocated memory in |destination| is sufficient.
  CHECK_LE(frames_to_consume + start_frame, destination->frames());

  // Figure out if wrapping is needed and if so what segment sizes we need
  // when removing audio bus content from the FIFO.
  int consume_size = 0;
  int wrap_size = 0;
  GetSizes(read_pos_, max_frames(), frames_to_consume,
           &consume_size, &wrap_size);

  // For all channels, remove the requested amount of data from the FIFO
  // and copy the content to the destination. Wrap around if needed.
  for (int ch = 0; ch < destination->channels(); ++ch) {
    float* dest = destination->channel(ch);
    const float* src = audio_bus_->channel(ch);

    // Copy a selected part of the FIFO to the destination.
    memcpy(&dest[start_frame], &src[read_pos_], consume_size * sizeof(src[0]));
    if (wrap_size > 0) {
      // Wrapping is needed: copy remaining part to the destination.
      memcpy(&dest[consume_size + start_frame], &src[0],
             wrap_size * sizeof(src[0]));
    }
  }

  Atomic32 new_frames_consumed = frames_consumed_ + frames_to_consume;
  NoBarrier_Store(&frames_consumed_, new_frames_consumed);

  read_pos_ = UpdatePos(read_pos_, frames_to_consume, max_frames());
}

void AudioFifo::Clear() {
  frames_pushed_ = 0;
  frames_consumed_ = 0;
  read_pos_ = 0;
  write_pos_ = 0;
}

}  // namespace media