chromium/chromecast/media/cma/backend/android/audio_sink_manager.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/android/audio_sink_manager.h"

#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "chromecast/media/cma/backend/android/audio_sink_android.h"

namespace chromecast {
namespace media {

namespace {

class AudioSinkManagerInstance : public AudioSinkManager {
 public:
  AudioSinkManagerInstance() {}

  AudioSinkManagerInstance(const AudioSinkManagerInstance&) = delete;
  AudioSinkManagerInstance& operator=(const AudioSinkManagerInstance&) = delete;

  ~AudioSinkManagerInstance() override {}
};

}  // namespace

// static
AudioSinkManager* AudioSinkManager::Get() {
  static base::NoDestructor<AudioSinkManagerInstance> sink_manager_instance;
  return sink_manager_instance.get();
}

AudioSinkManager::AudioSinkManager() {}
AudioSinkManager::~AudioSinkManager() {}

void AudioSinkManager::Add(AudioSinkAndroid* sink) {
  DCHECK(sink);

  LOG(INFO) << __func__ << " sink(" << sink << "): id=" << sink->device_id()
            << " primary=" << sink->primary()
            << " type=" << sink->content_type();

  base::AutoLock lock(lock_);
  UpdateLimiterMultiplier(sink);
  sinks_.push_back(sink);
}

void AudioSinkManager::Remove(AudioSinkAndroid* sink) {
  DCHECK(sink);

  LOG(INFO) << __func__ << " sink(" << sink << "): id=" << sink->device_id()
            << " type=" << sink->content_type();

  base::AutoLock lock(lock_);

  auto it = base::ranges::find(sinks_, sink);
  if (it == sinks_.end()) {
    LOG(WARNING) << __func__ << ": Cannot find sink";
    return;
  }
  sinks_.erase(it);
}

void AudioSinkManager::SetTypeVolumeDb(AudioContentType type, float level_db) {
  LOG(INFO) << __func__ << ": level_db=" << level_db << " type=" << type;
  base::AutoLock lock(lock_);
  volume_info_[type].volume_db = level_db;
  // Since the type volume changed we need to reflect that in the limiter
  // multipliers.
  UpdateAllLimiterMultipliers(type);
}

void AudioSinkManager::SetOutputLimitDb(AudioContentType type, float limit_db) {
  LOG(INFO) << __func__ << ": limit_db=" << limit_db << " type=" << type;
  base::AutoLock lock(lock_);
  volume_info_[type].limit_db = limit_db;
  UpdateAllLimiterMultipliers(type);
}

void AudioSinkManager::UpdateAllLimiterMultipliers(AudioContentType type) {
  for (auto* sink : sinks_) {
    if (sink->content_type() == type)
      UpdateLimiterMultiplier(sink);
  }
}

void AudioSinkManager::UpdateLimiterMultiplier(AudioSinkAndroid* sink) {
  AudioContentType type = sink->content_type();
  if (sink->primary()) {
    sink->SetLimiterVolumeMultiplier(GetLimiterMultiplier(type));
  } else {
    // Volume limits don't apply to effects streams.
    sink->SetLimiterVolumeMultiplier(1.0f);
  }
}

float AudioSinkManager::GetLimiterMultiplier(AudioContentType type) {
  // Set multiplier so the effective volume is min(level_db, limit_db).
  VolumeInfo v = volume_info_[type];
  if (v.volume_db <= v.limit_db)
    return 1.0f;
  return std::pow(10, (v.limit_db - v.volume_db) / 20);
}

}  // namespace media
}  // namespace chromecast