chromium/chromecast/media/audio/audio_fader.cc

// 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