chromium/chromecast/media/cma/backend/mixer/post_processors/governor.cc

// Copyright 2017 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/cma/backend/mixer/post_processors/governor.h"

#include <limits>
#include <memory>
#include <string>

#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/numerics/ranges.h"
#include "base/values.h"
#include "chromecast/media/base/slew_volume.h"
#include "chromecast/media/cma/backend/mixer/post_processor_registry.h"

namespace chromecast {
namespace media {

namespace {

const int kNoVolume = -1;
const float kEpsilon = std::numeric_limits<float>::epsilon();
const float kMaxOnSetVolume = 0.989;  // -0.1dB

// Configuration strings:
const char kOnsetVolumeKey[] = "onset_volume";
const char kClampMultiplierKey[] = "clamp_multiplier";

}  // namespace

Governor::Governor(const std::string& config, int input_channels)
    : volume_(kNoVolume) {
  status_.output_channels = input_channels;
  status_.rendering_delay_frames = 0;
  status_.ringing_time_frames = 0;
  auto config_dict = base::JSONReader::ReadDict(config);
  CHECK(config_dict) << "Governor config is not valid json: " << config;
  auto onset_volume = config_dict->FindDouble(kOnsetVolumeKey);
  CHECK(onset_volume);
  onset_volume_ = onset_volume.value();
  auto clamp_multiplier = config_dict->FindDouble(kClampMultiplierKey);
  CHECK(clamp_multiplier);
  clamp_multiplier_ = clamp_multiplier.value();
  CHECK_LE(onset_volume_, clamp_multiplier_);
  CHECK_LE(onset_volume_, kMaxOnSetVolume);
  slew_volume_.SetVolume(1.0);
  LOG(INFO) << "Created a governor: onset_volume = " << onset_volume_
            << ", clamp_multiplier = " << clamp_multiplier_;
}

Governor::~Governor() = default;

bool Governor::SetConfig(const AudioPostProcessor2::Config& config) {
  status_.input_sample_rate = config.output_sample_rate;
  slew_volume_.SetSampleRate(status_.input_sample_rate);
  return true;
}

const AudioPostProcessor2::Status& Governor::GetStatus() {
  return status_;
}

void Governor::ProcessFrames(float* data, int frames, Metadata* metadata) {
  DCHECK(data);
  status_.output_buffer = data;

  // If the volume has changed.
  if (!base::IsApproximatelyEqual(metadata->system_volume, volume_, kEpsilon)) {
    volume_ = metadata->system_volume;
    slew_volume_.SetVolume(GetGovernorMultiplier());
  }

  slew_volume_.ProcessFMUL(false, data, frames, status_.output_channels, data);
}

float Governor::GetGovernorMultiplier() {
  if (volume_ > onset_volume_) {
    float effective_volume =
        onset_volume_ + (volume_ - onset_volume_) *
                            (clamp_multiplier_ - onset_volume_) /
                            (1. - onset_volume_);
    return effective_volume / volume_;
  }
  return 1.0;
}

bool Governor::UpdateParameters(const std::string& message) {
  return false;
}

void Governor::SetSlewTimeMsForTest(int slew_time_ms) {
  slew_volume_.SetMaxSlewTimeMs(slew_time_ms);
}

REGISTER_POSTPROCESSOR(Governor, "libcast_governor_2.0.so");

}  // namespace media
}  // namespace chromecast