chromium/media/audio/ios/audio_manager_ios.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/ios/audio_manager_ios.h"

#include <memory>

#include "media/audio/apple/audio_input.h"
#include "media/audio/apple/audio_low_latency_input.h"
#include "media/audio/apple/audio_manager_apple.h"
#include "media/audio/ios/audio_session_manager_ios.h"

namespace media {

// Default buffer size.
constexpr int kDefaultInputBufferSize = 1024;

std::unique_ptr<media::AudioManager> CreateAudioManager(
    std::unique_ptr<AudioThread> audio_thread,
    AudioLogFactory* audio_log_factory) {
  return std::make_unique<AudioManagerIOS>(std::move(audio_thread),
                                           audio_log_factory);
}

AudioManagerIOS::AudioManagerIOS(std::unique_ptr<AudioThread> audio_thread,
                                 AudioLogFactory* audio_log_factory)
    : AudioManagerApple(std::move(audio_thread), audio_log_factory) {
  AudioSessionManagerIOS::GetInstance().SetActive(true);
}

AudioManagerIOS::~AudioManagerIOS() {
  AudioSessionManagerIOS::GetInstance().SetActive(false);
}

bool AudioManagerIOS::HasAudioOutputDevices() {
  return AudioSessionManagerIOS::GetInstance().HasAudioHardware(
      /*is_input=*/false);
}

bool AudioManagerIOS::HasAudioInputDevices() {
  return AudioSessionManagerIOS::GetInstance().HasAudioHardware(
      /*is_input=*/true);
}

void AudioManagerIOS::GetAudioInputDeviceNames(AudioDeviceNames* device_names) {
  DCHECK(device_names->empty());
  AudioSessionManagerIOS::GetInstance().GetAudioDeviceInfo(true, device_names);
}

void AudioManagerIOS::GetAudioOutputDeviceNames(
    AudioDeviceNames* device_names) {
  DCHECK(device_names->empty());
  AudioSessionManagerIOS::GetInstance().GetAudioDeviceInfo(false, device_names);
}

AudioParameters AudioManagerIOS::GetInputStreamParameters(
    const std::string& input_device_id) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  const int hardware_sample_rate =
      AudioSessionManagerIOS::GetInstance().HardwareSampleRate();

  // Try to use mono to save resources. Also avoids channel format conversion in
  // the I/O audio unit.
  ChannelLayoutConfig channel_layout_config = ChannelLayoutConfig::Mono();

  AudioParameters params = AudioParameters(
      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout_config,
      hardware_sample_rate, kDefaultInputBufferSize);
  return params;
}

std::string AudioManagerIOS::GetAssociatedOutputDeviceID(
    const std::string& input_device_unique_id) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  return std::string();
}

const char* media::AudioManagerIOS::GetName() {
  return "iOS";
}

AudioOutputStream* AudioManagerIOS::MakeLinearOutputStream(
    const AudioParameters& params,
    const LogCallback& log_callback) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  return MakeLowLatencyOutputStream(params, std::string(), log_callback);
}

AudioOutputStream* AudioManagerIOS::MakeLowLatencyOutputStream(
    const AudioParameters& params,
    const std::string& device_id,
    const LogCallback& log_callback) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  AUHALStream* stream =
      new AUHALStream(this, params, kAudioObjectUnknown, log_callback);
  return stream;
}

AudioInputStream* AudioManagerIOS::MakeLinearInputStream(
    const AudioParameters& params,
    const std::string& device_id,
    const LogCallback& log_callback) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
  return stream;
}

AudioInputStream* AudioManagerIOS::MakeLowLatencyInputStream(
    const AudioParameters& params,
    const std::string& device_id,
    const LogCallback& log_callback) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());

  VoiceProcessingMode voice_processing_mode = VoiceProcessingMode::kDisabled;

  auto* stream = new AUAudioInputStream(this, params, kAudioObjectUnknown,
                                        log_callback, voice_processing_mode);
  return stream;
}

std::string AudioManagerIOS::GetDefaultInputDeviceID() {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  return AudioSessionManagerIOS::GetInstance().GetDefaultInputDeviceID();
}

std::string AudioManagerIOS::GetDefaultOutputDeviceID() {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  return AudioSessionManagerIOS::GetInstance().GetDefaultOutputDeviceID();
}

bool AudioManagerIOS::MaybeChangeBufferSize(AudioDeviceID device_id,
                                            AudioUnit audio_unit,
                                            AudioUnitElement element,
                                            size_t desired_buffer_size) {
  // TODO: Add IO buffer size  handling based on `desired_buffer_size`. Refer
  // comments at audio_manager_mac.h for more information.
  return true;
}

bool AudioManagerIOS::DeviceSupportsAmbientNoiseReduction(
    AudioDeviceID device_id) {
  return false;
}

bool AudioManagerIOS::SuppressNoiseReduction(AudioDeviceID device_id) {
  return false;
}

void AudioManagerIOS::UnsuppressNoiseReduction(AudioDeviceID device_id) {
  NOTIMPLEMENTED();
}

double AudioManagerIOS::GetMaxInputVolume(AudioDeviceID device_id) {
  return 1.0;
}

bool AudioManagerIOS::ShouldDeferStreamStart() const {
  return false;
}

// static
double AudioManagerIOS::HardwareIOBufferDuration() {
  return AudioSessionManagerIOS::GetInstance().HardwareIOBufferDuration();
}

double AudioManagerIOS::HardwareLatency(bool is_input) {
  return AudioSessionManagerIOS::GetInstance().HardwareLatency(is_input);
}

long AudioManagerIOS::GetDeviceChannels(bool is_input) {
  return AudioSessionManagerIOS::GetInstance().GetDeviceChannels(is_input);
}

double AudioManagerIOS::GetInputVolume(AudioDeviceID device_id) {
  return static_cast<double>(
      AudioSessionManagerIOS::GetInstance().GetInputGain());
}

void AudioManagerIOS::SetInputVolume(AudioDeviceID device_id, double volume) {
  AudioSessionManagerIOS::GetInstance().SetInputGain(
      static_cast<float>(volume));
}

bool AudioManagerIOS::IsInputMuted(AudioDeviceID device_id) {
  return AudioSessionManagerIOS::GetInstance().IsInputMuted();
}

int AudioManagerIOS::HardwareSampleRateForDevice(AudioDeviceID device_id) {
  return static_cast<int>(
      AudioSessionManagerIOS::GetInstance().HardwareSampleRate());
}

bool AudioManagerIOS::IsInputGainSettable() {
  return AudioSessionManagerIOS::GetInstance().IsInputGainSettable();
}

OSStatus AudioManagerIOS::GetInputDeviceStreamFormat(
    AudioUnit audio_unit,
    AudioStreamBasicDescription* input_format) {
  // Configure audio stream format for 16-bit PCM
  const SampleFormat kSampleFormat = kSampleFormatS16;
  input_format->mSampleRate =
      AudioSessionManagerIOS::GetInstance().HardwareSampleRate();
  input_format->mFormatID = kAudioFormatLinearPCM;
  input_format->mFormatFlags =
      kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
  input_format->mChannelsPerFrame =
      AudioSessionManagerIOS::GetInstance().GetDeviceChannels(
          /*is_input=*/true);
  input_format->mBitsPerChannel = SampleFormatToBitsPerChannel(kSampleFormat);

  // Calculate other fields based on the above settings
  input_format->mBytesPerPacket = SampleFormatToBytesPerChannel(kSampleFormat) *
                                  input_format->mChannelsPerFrame;
  input_format->mBytesPerFrame = input_format->mBytesPerPacket;
  input_format->mFramesPerPacket = 1;  // uncompressed audio
  return noErr;
}

// protected
AudioParameters AudioManagerIOS::GetPreferredOutputStreamParameters(
    const std::string& output_device_id,
    const AudioParameters& input_params) {
  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
  const bool has_valid_input_params = input_params.IsValid();
  const int hardware_sample_rate =
      AudioSessionManagerIOS::GetInstance().HardwareSampleRate();

  const int hardware_channels =
      AudioSessionManagerIOS::GetInstance().GetDeviceChannels(
          /*is_input=*/false);

  // Use the input channel count and channel layout if possible.  Let OSX take
  // care of remapping the channels; this lets user specified channel layouts
  // work correctly.
  int output_channels = input_params.channels();
  ChannelLayout channel_layout = input_params.channel_layout();
  if (!has_valid_input_params || output_channels > hardware_channels) {
    output_channels = hardware_channels;
    channel_layout = GuessChannelLayout(output_channels);
    if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
      channel_layout = CHANNEL_LAYOUT_DISCRETE;
    }
  }

  AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
                         {channel_layout, output_channels},
                         hardware_sample_rate, kDefaultInputBufferSize);
  return params;
}

}  // namespace media