// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/audio/audio_fader.h"
#include <algorithm>
#include "base/bits.h"
#include "base/check_op.h"
namespace chromecast {
namespace media {
namespace {
constexpr size_t kMaxChannels = 32;
} // namespace
AudioFader::AudioFader(AudioProvider* provider,
base::TimeDelta fade_time,
double playback_rate)
: AudioFader(provider,
std::round(fade_time.InSecondsF() * provider->sample_rate() *
playback_rate),
playback_rate) {}
AudioFader::AudioFader(AudioProvider* provider,
int fade_frames,
double playback_rate)
: provider_(provider),
// Ensure that fade_frames_ is a multiple of 4 to keep correct alignment.
fade_frames_(base::bits::AlignUpDeprecatedDoNotUse(fade_frames, 4)),
num_channels_(provider_->num_channels()),
sample_rate_(provider_->sample_rate()),
playback_rate_(playback_rate) {
DCHECK(provider_);
DCHECK_GT(fade_frames_, 0);
DCHECK_GT(num_channels_, 0u);
DCHECK_LE(num_channels_, kMaxChannels);
DCHECK_GT(sample_rate_, 0);
fade_buffer_ = CastAudioBus::Create(num_channels_, fade_frames_);
fade_buffer_->Zero();
}
AudioFader::~AudioFader() = default;
int AudioFader::FramesNeededFromSource(int num_fill_frames) const {
DCHECK_GE(num_fill_frames, 0);
DCHECK_GE(fade_frames_, buffered_frames_);
return num_fill_frames + fade_frames_ - buffered_frames_;
}
int64_t AudioFader::FramesToMicroseconds(int64_t frames) {
return frames * base::Time::kMicrosecondsPerSecond /
(sample_rate_ * playback_rate_);
}
size_t AudioFader::num_channels() const {
return num_channels_;
}
int AudioFader::sample_rate() const {
return sample_rate_;
}
int AudioFader::FillFrames(int num_frames,
int64_t playout_timestamp,
float* const* channel_data) {
DCHECK(channel_data);
int filled_frames = std::min(buffered_frames_, num_frames);
if (filled_frames > 0) {
for (size_t c = 0; c < num_channels_; ++c) {
float* fade_channel = fade_buffer_->channel(c);
// First, copy data from fade_buffer_.
std::copy_n(fade_channel, filled_frames, channel_data[c]);
// Move data in fade_buffer_ to start.
std::copy(fade_channel + filled_frames, fade_channel + buffered_frames_,
fade_channel);
}
buffered_frames_ -= filled_frames;
num_frames -= filled_frames;
}
float* fill_channel_data[kMaxChannels];
if (num_frames > 0) {
// Still need more frames; ask source to fill.
for (size_t c = 0; c < num_channels_; ++c) {
fill_channel_data[c] = channel_data[c] + filled_frames;
}
int64_t timestamp = playout_timestamp +
FramesToMicroseconds(filled_frames + buffered_frames_);
int filled =
provider_->FillFrames(num_frames, timestamp, fill_channel_data);
filled_frames += filled;
num_frames -= filled;
}
// Refill fade_buffer_ from source.
for (size_t c = 0; c < num_channels_; ++c) {
fill_channel_data[c] = fade_buffer_->channel(c) + buffered_frames_;
}
int64_t timestamp = playout_timestamp +
FramesToMicroseconds(filled_frames + buffered_frames_);
buffered_frames_ += provider_->FillFrames(fade_frames_ - buffered_frames_,
timestamp, fill_channel_data);
const bool complete = (num_frames == 0 && buffered_frames_ == fade_frames_);
if (complete) {
CompleteFill(channel_data, filled_frames);
} else {
if (state_ == State::kPlaying) {
int extra_frames =
std::max(filled_frames + buffered_frames_ - fade_frames_, 0);
for (size_t c = 0; c < num_channels_; ++c) {
fill_channel_data[c] = channel_data[c] + extra_frames;
}
IncompleteFill(fill_channel_data, filled_frames - extra_frames);
} else {
IncompleteFill(channel_data, filled_frames);
}
}
return filled_frames;
}
void AudioFader::CompleteFill(float* const* channel_data, int filled_frames) {
switch (state_) {
case State::kSilent:
// Fade in.
state_ = State::kFadingIn;
fade_frames_remaining_ = fade_frames_;
break;
case State::kFadingIn:
// Continue fading in.
break;
case State::kPlaying:
// Nothing to do in this case.
return;
case State::kFadingOut:
// Fade back in.
state_ = State::kFadingIn;
fade_frames_remaining_ =
std::max(0, fade_frames_ - fade_frames_remaining_ - 1);
break;
}
FadeIn(channel_data, filled_frames);
}
void AudioFader::IncompleteFill(float* const* channel_data, int filled_frames) {
switch (state_) {
case State::kSilent:
// Remain silent.
buffered_frames_ = 0;
for (size_t c = 0; c < num_channels_; ++c) {
std::fill_n(channel_data[c], filled_frames, 0);
}
return;
case State::kFadingIn:
// Fade back out.
state_ = State::kFadingOut;
fade_frames_remaining_ =
std::max(0, fade_frames_ - fade_frames_remaining_ - 1);
break;
case State::kPlaying:
// Fade out.
state_ = State::kFadingOut;
fade_frames_remaining_ = fade_frames_;
break;
case State::kFadingOut:
// Continue fading out.
break;
}
FadeOut(channel_data, filled_frames);
}
void AudioFader::FadeIn(float* const* channel_data, int filled_frames) {
DCHECK(state_ == State::kFadingIn);
FadeInHelper(channel_data, num_channels_, filled_frames, fade_frames_,
fade_frames_remaining_);
fade_frames_remaining_ = std::max(0, fade_frames_remaining_ - filled_frames);
if (fade_frames_remaining_ == 0) {
state_ = State::kPlaying;
}
}
// static
void AudioFader::FadeInHelper(float* const* channel_data,
size_t num_channels,
int filled_frames,
int fade_frames,
int fade_frames_remaining) {
const float inverse_fade_frames = 1.0f / static_cast<float>(fade_frames);
const int fade_limit = std::min(filled_frames, fade_frames_remaining + 1);
for (size_t c = 0; c < num_channels; ++c) {
float* channel = channel_data[c];
for (int f = 0; f < fade_limit; ++f) {
const float fade_multiplier =
1.0 - (fade_frames_remaining - f) * inverse_fade_frames;
channel[f] *= fade_multiplier;
}
}
}
void AudioFader::FadeOut(float* const* channel_data, int filled_frames) {
DCHECK(state_ == State::kFadingOut);
FadeOutHelper(channel_data, num_channels_, filled_frames, fade_frames_,
fade_frames_remaining_);
fade_frames_remaining_ = std::max(0, fade_frames_remaining_ - filled_frames);
if (fade_frames_remaining_ == 0) {
state_ = State::kSilent;
buffered_frames_ = 0;
}
}
// static
void AudioFader::FadeOutHelper(float* const* channel_data,
size_t num_channels,
int filled_frames,
int fade_frames,
int fade_frames_remaining) {
const float inverse_fade_frames = 1.0f / static_cast<float>(fade_frames);
const int fade_limit = std::min(filled_frames, fade_frames_remaining + 1);
for (size_t c = 0; c < num_channels; ++c) {
float* channel = channel_data[c];
for (int f = 0; f < fade_limit; ++f) {
const float fade_multiplier =
(fade_frames_remaining - f) * inverse_fade_frames;
channel[f] *= fade_multiplier;
}
}
if (filled_frames > fade_frames_remaining) {
for (size_t c = 0; c < num_channels; ++c) {
std::fill_n(channel_data[c] + fade_frames_remaining,
filled_frames - fade_frames_remaining, 0);
}
}
}
} // namespace media
} // namespace chromecast