blob: fdfafd7a37b7ebf3252a98ece9924cee92556794 [file] [log] [blame]
// Copyright 2017 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 "platform/audio/PushPullFIFO.h"
#include <memory>
#include "platform/audio/AudioUtilities.h"
#include "platform/wtf/PtrUtil.h"
namespace blink {
namespace {
// Suppress the warning log if over/underflow happens more than 100 times.
const unsigned kMaxMessagesToLog = 100;
}
const size_t PushPullFIFO::kMaxFIFOLength = 65536;
PushPullFIFO::PushPullFIFO(unsigned number_of_channels, size_t fifo_length)
: fifo_length_(fifo_length),
frames_available_(0),
index_read_(0),
index_write_(0),
overflow_count_(0),
underflow_count_(0) {
CHECK_LE(fifo_length_, kMaxFIFOLength);
fifo_bus_ = AudioBus::Create(number_of_channels, fifo_length_);
}
PushPullFIFO::~PushPullFIFO() {}
// Push the data from |inputBus| to FIFO. The size of push is determined by
// the length of |inputBus|.
void PushPullFIFO::Push(const AudioBus* input_bus) {
CHECK(input_bus);
CHECK_EQ(input_bus->length(), AudioUtilities::kRenderQuantumFrames);
SECURITY_CHECK(input_bus->length() <= fifo_length_);
SECURITY_CHECK(index_write_ < fifo_length_);
const size_t input_bus_length = input_bus->length();
const size_t remainder = fifo_length_ - index_write_;
for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
float* fifo_bus_channel = fifo_bus_->Channel(i)->MutableData();
const float* input_bus_channel = input_bus->Channel(i)->Data();
if (remainder >= input_bus_length) {
// The remainder is big enough for the input data.
memcpy(fifo_bus_channel + index_write_, input_bus_channel,
input_bus_length * sizeof(*fifo_bus_channel));
} else {
// The input data overflows the remainder size. Wrap around the index.
memcpy(fifo_bus_channel + index_write_, input_bus_channel,
remainder * sizeof(*fifo_bus_channel));
memcpy(fifo_bus_channel, input_bus_channel + remainder,
(input_bus_length - remainder) * sizeof(*fifo_bus_channel));
}
}
// Update the write index; wrap it around if necessary.
index_write_ = (index_write_ + input_bus_length) % fifo_length_;
// In case of overflow, move the |indexRead| to the updated |indexWrite| to
// avoid reading overwritten frames by the next pull.
if (input_bus_length > fifo_length_ - frames_available_) {
index_read_ = index_write_;
if (++overflow_count_ < kMaxMessagesToLog) {
LOG(WARNING) << "PushPullFIFO: overflow while pushing ("
<< "overflowCount=" << overflow_count_
<< ", availableFrames=" << frames_available_
<< ", inputFrames=" << input_bus_length
<< ", fifoLength=" << fifo_length_ << ")";
}
}
// Update the number of frames available in FIFO.
frames_available_ =
std::min(frames_available_ + input_bus_length, fifo_length_);
DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
}
// Pull the data out of FIFO to |outputBus|. If remaining frame in the FIFO
// is less than the frames to pull, provides remaining frame plus the silence.
void PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
#if OS(ANDROID)
if (!output_bus) {
// Log when outputBus or FIFO object is invalid. (crbug.com/692423)
LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
<< ">] |outputBus| is invalid.";
// Silently return to avoid crash.
return;
}
// The following checks are in place to catch the inexplicable crash.
// (crbug.com/692423)
if (frames_requested > output_bus->length()) {
LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
<< ">] framesRequested > outputBus->length() ("
<< frames_requested << " > " << output_bus->length() << ")";
}
if (frames_requested > fifo_length_) {
LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
<< ">] framesRequested > m_fifoLength (" << frames_requested
<< " > " << fifo_length_ << ")";
}
if (index_read_ >= fifo_length_) {
LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
<< ">] m_indexRead >= m_fifoLength (" << index_read_
<< " >= " << fifo_length_ << ")";
}
#endif
CHECK(output_bus);
SECURITY_CHECK(frames_requested <= output_bus->length());
SECURITY_CHECK(frames_requested <= fifo_length_);
SECURITY_CHECK(index_read_ < fifo_length_);
const size_t remainder = fifo_length_ - index_read_;
const size_t frames_to_fill = std::min(frames_available_, frames_requested);
for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
const float* fifo_bus_channel = fifo_bus_->Channel(i)->Data();
float* output_bus_channel = output_bus->Channel(i)->MutableData();
// Fill up the output bus with the available frames first.
if (remainder >= frames_to_fill) {
// The remainder is big enough for the frames to pull.
memcpy(output_bus_channel, fifo_bus_channel + index_read_,
frames_to_fill * sizeof(*fifo_bus_channel));
} else {
// The frames to pull is bigger than the remainder size.
// Wrap around the index.
memcpy(output_bus_channel, fifo_bus_channel + index_read_,
remainder * sizeof(*fifo_bus_channel));
memcpy(output_bus_channel + remainder, fifo_bus_channel,
(frames_to_fill - remainder) * sizeof(*fifo_bus_channel));
}
// The frames available was not enough to fulfill the requested frames. Fill
// the rest of the channel with silence.
if (frames_requested > frames_to_fill) {
memset(output_bus_channel + frames_to_fill, 0,
(frames_requested - frames_to_fill) * sizeof(*output_bus_channel));
}
}
// Update the read index; wrap it around if necessary.
index_read_ = (index_read_ + frames_to_fill) % fifo_length_;
// In case of underflow, move the |indexWrite| to the updated |indexRead|.
if (frames_requested > frames_to_fill) {
index_write_ = index_read_;
if (underflow_count_++ < kMaxMessagesToLog) {
LOG(WARNING) << "PushPullFIFO: underflow while pulling ("
<< "underflowCount=" << underflow_count_
<< ", availableFrames=" << frames_available_
<< ", requestedFrames=" << frames_requested
<< ", fifoLength=" << fifo_length_ << ")";
}
}
// Update the number of frames in FIFO.
frames_available_ -= frames_to_fill;
DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
}
const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() const {
return {length(), NumberOfChannels(), FramesAvailable(), index_read_,
index_write_, overflow_count_, underflow_count_};
}
} // namespace blink