#include "media/base/audio_fifo.h"
#include <cstring>
#include "base/check_op.h"
#include "base/numerics/safe_math.h"
#include "base/trace_event/trace_event.h"
#include "base/types/zip.h"
#include "media/base/audio_bus.h"
namespace media {
static void GetSizes(size_t pos,
size_t max_size,
size_t in_size,
size_t* size,
size_t* wrap_size) {
const size_t frames_before_end = base::CheckSub(max_size, pos).ValueOrDie();
if (in_size > frames_before_end) {
*size = max_size - pos;
*wrap_size = in_size - *size;
} else {
*size = in_size;
*wrap_size = 0u;
}
}
static size_t UpdatePos(size_t pos, size_t step, int max_size) {
return ((pos + step) % max_size);
}
AudioFifo::AudioFifo(int channels, int frames)
: audio_bus_(AudioBus::Create(channels, frames)),
max_frames_(base::checked_cast<size_t>(frames)) {}
AudioFifo::~AudioFifo() = default;
void AudioFifo::Push(const AudioBus* source) {
Push(source, source->frames());
}
void AudioFifo::Push(const AudioBus* source, int source_size) {
DCHECK(source);
DCHECK_EQ(source->channels(), audio_bus_->channels());
DCHECK_LE(source_size, source->frames());
const size_t source_frames = base::checked_cast<size_t>(source_size);
const size_t remaining_frames =
base::CheckSub(max_frames_, frames_).ValueOrDie();
CHECK_LE(source_frames, remaining_frames);
TRACE_EVENT_BEGIN2(TRACE_DISABLED_BY_DEFAULT("audio"), "AudioFifo::Push",
"this", static_cast<void*>(this), "fifo frames", frames_);
size_t append_size = 0u;
size_t wrap_size = 0u;
GetSizes(write_pos_, max_frames_, source_frames, &append_size, &wrap_size);
for (auto [data_src, fifo_dest] :
base::zip(source->AllChannels(), audio_bus_->AllChannels())) {
auto [append_data, wrap_data] = data_src.split_at(append_size);
fifo_dest.subspan(write_pos_, append_size)
.copy_from_nonoverlapping(append_data);
if (wrap_size > 0) {
fifo_dest.first(wrap_size).copy_from_nonoverlapping(wrap_data);
}
}
frames_ += source_frames;
DCHECK_LE(frames_, max_frames_);
write_pos_ = UpdatePos(write_pos_, source_frames, max_frames_);
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("audio"), "AudioFifo::Push",
"frames", source_frames);
}
void AudioFifo::Consume(AudioBus* destination,
int start_frame,
int frames_to_consume) {
DCHECK(destination);
DCHECK_EQ(destination->channels(), audio_bus_->channels());
const size_t dest_offset = base::checked_cast<size_t>(start_frame);
const size_t frame_count = base::checked_cast<size_t>(frames_to_consume);
CHECK_LE(frame_count, frames_);
const size_t free_frames_in_dest =
base::CheckSub(destination->frames(), dest_offset).ValueOrDie();
CHECK_LE(frame_count, free_frames_in_dest);
TRACE_EVENT_BEGIN2(TRACE_DISABLED_BY_DEFAULT("audio"), "AudioFifo::Consume",
"this", static_cast<void*>(this), "fifo frames", frames_);
size_t consume_size = 0u;
size_t wrap_size = 0u;
GetSizes(read_pos_, max_frames_, frame_count, &consume_size, &wrap_size);
for (auto [data_dest, fifo_src] :
base::zip(destination->AllChannels(), audio_bus_->AllChannels())) {
auto [consume_data, wrap_data] =
data_dest.subspan(dest_offset, frame_count).split_at(consume_size);
consume_data.copy_from_nonoverlapping(
fifo_src.subspan(read_pos_, consume_size));
if (wrap_size > 0) {
wrap_data.copy_from_nonoverlapping(fifo_src.first(wrap_size));
}
}
frames_ -= frame_count;
read_pos_ = UpdatePos(read_pos_, frame_count, max_frames_);
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("audio"), "AudioFifo::Consume",
"frames", frame_count);
}
void AudioFifo::Clear() {
frames_ = 0u;
read_pos_ = 0u;
write_pos_ = 0u;
}
}