1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/base/audio_fifo.h" 6 7#include "base/logging.h" 8 9using base::subtle::Atomic32; 10using base::subtle::NoBarrier_Store; 11 12namespace media { 13 14// Given current position in the FIFO, the maximum number of elements in the 15// FIFO and the size of the input; this method provides two output results: 16// |size| and |wrap_size|. These two results can then be utilized for memcopy 17// operations to and from the FIFO. 18// Under "normal" circumstances, |size| will be equal to |in_size| and 19// |wrap_size| will be zero. This case corresponding to the non-wrapping case 20// where we have not yet reached the "edge" of the FIFO. If |pos| + |in_size| 21// exceeds the total size of the FIFO, we must wrap around and start reusing 22// a part the allocated memory. The size of this part is given by |wrap_size|. 23static void GetSizes( 24 int pos, int max_size, int in_size, int* size, int* wrap_size) { 25 if (pos + in_size > max_size) { 26 // Wrapping is required => derive size of each segment. 27 *size = max_size - pos; 28 *wrap_size = in_size - *size; 29 } else { 30 // Wrapping is not required. 31 *size = in_size; 32 *wrap_size = 0; 33 } 34} 35 36// Updates the read/write position with |step| modulo the maximum number of 37// elements in the FIFO to ensure that the position counters wraps around at 38// the endpoint. 39static int UpdatePos(int pos, int step, int max_size) { 40 return ((pos + step) % max_size); 41} 42 43AudioFifo::AudioFifo(int channels, int frames) 44 : audio_bus_(AudioBus::Create(channels, frames)), 45 max_frames_(frames), 46 frames_pushed_(0), 47 frames_consumed_(0), 48 read_pos_(0), 49 write_pos_(0) {} 50 51AudioFifo::~AudioFifo() {} 52 53int AudioFifo::frames() const { 54 int delta = frames_pushed_ - frames_consumed_; 55 base::subtle::MemoryBarrier(); 56 return delta; 57} 58 59void AudioFifo::Push(const AudioBus* source) { 60 DCHECK(source); 61 DCHECK_EQ(source->channels(), audio_bus_->channels()); 62 63 // Ensure that there is space for the new data in the FIFO. 64 const int source_size = source->frames(); 65 CHECK_LE(source_size + frames(), max_frames_); 66 67 // Figure out if wrapping is needed and if so what segment sizes we need 68 // when adding the new audio bus content to the FIFO. 69 int append_size = 0; 70 int wrap_size = 0; 71 GetSizes(write_pos_, max_frames(), source_size, &append_size, &wrap_size); 72 73 // Copy all channels from the source to the FIFO. Wrap around if needed. 74 for (int ch = 0; ch < source->channels(); ++ch) { 75 float* dest = audio_bus_->channel(ch); 76 const float* src = source->channel(ch); 77 78 // Append part of (or the complete) source to the FIFO. 79 memcpy(&dest[write_pos_], &src[0], append_size * sizeof(src[0])); 80 if (wrap_size > 0) { 81 // Wrapping is needed: copy remaining part from the source to the FIFO. 82 memcpy(&dest[0], &src[append_size], wrap_size * sizeof(src[0])); 83 } 84 } 85 86 // Ensure the data is *really* written before updating |frames_pushed_|. 87 base::subtle::MemoryBarrier(); 88 89 Atomic32 new_frames_pushed = frames_pushed_ + source_size; 90 NoBarrier_Store(&frames_pushed_, new_frames_pushed); 91 92 DCHECK_LE(frames(), max_frames()); 93 write_pos_ = UpdatePos(write_pos_, source_size, max_frames()); 94} 95 96void AudioFifo::Consume(AudioBus* destination, 97 int start_frame, 98 int frames_to_consume) { 99 DCHECK(destination); 100 DCHECK_EQ(destination->channels(), audio_bus_->channels()); 101 102 // It is not possible to ask for more data than what is available in the FIFO. 103 CHECK_LE(frames_to_consume, frames()); 104 105 // A copy from the FIFO to |destination| will only be performed if the 106 // allocated memory in |destination| is sufficient. 107 CHECK_LE(frames_to_consume + start_frame, destination->frames()); 108 109 // Figure out if wrapping is needed and if so what segment sizes we need 110 // when removing audio bus content from the FIFO. 111 int consume_size = 0; 112 int wrap_size = 0; 113 GetSizes(read_pos_, max_frames(), frames_to_consume, 114 &consume_size, &wrap_size); 115 116 // For all channels, remove the requested amount of data from the FIFO 117 // and copy the content to the destination. Wrap around if needed. 118 for (int ch = 0; ch < destination->channels(); ++ch) { 119 float* dest = destination->channel(ch); 120 const float* src = audio_bus_->channel(ch); 121 122 // Copy a selected part of the FIFO to the destination. 123 memcpy(&dest[start_frame], &src[read_pos_], consume_size * sizeof(src[0])); 124 if (wrap_size > 0) { 125 // Wrapping is needed: copy remaining part to the destination. 126 memcpy(&dest[consume_size + start_frame], &src[0], 127 wrap_size * sizeof(src[0])); 128 } 129 } 130 131 Atomic32 new_frames_consumed = frames_consumed_ + frames_to_consume; 132 NoBarrier_Store(&frames_consumed_, new_frames_consumed); 133 134 read_pos_ = UpdatePos(read_pos_, frames_to_consume, max_frames()); 135} 136 137void AudioFifo::Clear() { 138 frames_pushed_ = 0; 139 frames_consumed_ = 0; 140 read_pos_ = 0; 141 write_pos_ = 0; 142} 143 144} // namespace media 145