Skip to content

Commit

Permalink
[mac]Move volume and channel setting from audio_low_latency_input_mac.h
Browse files Browse the repository at this point in the history
This CL is part of an effort to make low latency (AUAudioInputStream)
independent of Mac platform specific code so that it can be easily
adapted to iOS. There is a plan to move mac-specific code to
audio_manager_mac.h/.cc and core_audio_util_mac.h/.cc.

Bug: 1413450
Change-Id: I8c706834ff076c9876095bdd700d64a4d8ea6903
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4980705
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Commit-Queue: Abhijeet Kandalkar <abhijeet@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1216591}
  • Loading branch information
abhijeetk authored and Chromium LUCI CQ committed Oct 28, 2023
1 parent 28006d3 commit 975777a
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 144 deletions.
140 changes: 5 additions & 135 deletions media/audio/mac/audio_low_latency_input_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ AUAudioInputStream::AUAudioInputStream(
sink_(nullptr),
audio_unit_(0),
input_device_id_(audio_device_id),
number_of_channels_in_frame_(0),
fifo_(input_params.channels(),
input_params.frames_per_buffer(),
kNumberOfBlocksBufferInFifo),
Expand Down Expand Up @@ -283,10 +282,6 @@ AudioInputStream::OpenOutcome AUAudioInputStream::Open() {
audio_unit_, input_device_id_, kAudioDevicePropertyScopeInput,
format_.mSampleRate);

// The master channel is 0, Left and right are channels 1 and 2.
// And the master channel is not counted in |number_of_channels_in_frame_|.
number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();

return OpenOutcome::kSuccess;
}

Expand Down Expand Up @@ -729,65 +724,13 @@ void AUAudioInputStream::Close() {
}

double AUAudioInputStream::GetMaxVolume() {
// Verify that we have a valid device.
if (input_device_id_ == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return 0.0;
}

// Query if any of the master, left or right channels has volume control.
for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
// If the volume is settable, the valid volume range is [0.0, 1.0].
if (IsVolumeSettableOnChannel(i))
return 1.0;
}

// Volume control is not available for the audio stream.
return 0.0;
return AudioManagerMac::GetMaxInputVolume(input_device_id_);
}

void AUAudioInputStream::SetVolume(double volume) {
DVLOG(1) << __FUNCTION__ << " this " << this << " volume=" << volume << ")";
DCHECK_GE(volume, 0.0);
DCHECK_LE(volume, 1.0);

// Verify that we have a valid device.
if (input_device_id_ == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return;
}

Float32 volume_float32 = static_cast<Float32>(volume);
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

// Try to set the volume for master volume channel.
if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMain)) {
OSStatus result = AudioObjectSetPropertyData(
input_device_id_, &property_address, 0, nullptr, sizeof(volume_float32),
&volume_float32);
if (result != noErr) {
DLOG(WARNING) << "Failed to set volume to " << volume_float32;
}
return;
}

// There is no master volume control, try to set volume for each channel.
[[maybe_unused]] int successful_channels = 0;
for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
property_address.mElement = static_cast<UInt32>(i);
if (IsVolumeSettableOnChannel(i)) {
OSStatus result = AudioObjectSetPropertyData(
input_device_id_, &property_address, 0, NULL, sizeof(volume_float32),
&volume_float32);
if (result == noErr)
++successful_channels;
}
}

DLOG_IF(WARNING, successful_channels == 0)
<< "Failed to set volume to " << volume_float32;
AudioManagerMac::SetInputVolume(input_device_id_, volume);

// Update the AGC volume level based on the last setting above. Note that,
// the volume-level resolution is not infinite and it is therefore not
Expand All @@ -798,74 +741,11 @@ void AUAudioInputStream::SetVolume(double volume) {
}

double AUAudioInputStream::GetVolume() {
// Verify that we have a valid device.
if (input_device_id_ == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return 0.0;
}

AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

if (AudioObjectHasProperty(input_device_id_, &property_address)) {
// The device supports master volume control, get the volume from the
// master channel.
Float32 volume_float32 = 0.0;
UInt32 size = sizeof(volume_float32);
OSStatus result =
AudioObjectGetPropertyData(input_device_id_, &property_address, 0,
nullptr, &size, &volume_float32);
if (result == noErr)
return static_cast<double>(volume_float32);
} else {
// There is no master volume control, try to get the average volume of
// all the channels.
Float32 volume_float32 = 0.0;
int successful_channels = 0;
for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
property_address.mElement = static_cast<UInt32>(i);
if (AudioObjectHasProperty(input_device_id_, &property_address)) {
Float32 channel_volume = 0;
UInt32 size = sizeof(channel_volume);
OSStatus result =
AudioObjectGetPropertyData(input_device_id_, &property_address, 0,
nullptr, &size, &channel_volume);
if (result == noErr) {
volume_float32 += channel_volume;
++successful_channels;
}
}
}

// Get the average volume of the channels.
if (successful_channels != 0)
return static_cast<double>(volume_float32 / successful_channels);
}

DLOG(WARNING) << "Failed to get volume";
return 0.0;
return AudioManagerMac::GetInputVolume(input_device_id_);
}

bool AUAudioInputStream::IsMuted() {
// Verify that we have a valid device.
DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown";

AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

if (!AudioObjectHasProperty(input_device_id_, &property_address)) {
DLOG(ERROR) << "Device does not support checking master mute state";
return false;
}

UInt32 muted = 0;
UInt32 size = sizeof(muted);
OSStatus result = AudioObjectGetPropertyData(
input_device_id_, &property_address, 0, nullptr, &size, &muted);
DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
return result == noErr && muted != 0;
return AudioManagerMac::IsMuted(input_device_id_);
}

void AUAudioInputStream::SetOutputDeviceForAec(
Expand Down Expand Up @@ -1173,7 +1053,7 @@ base::TimeTicks AUAudioInputStream::GetCaptureTime(
hardware_latency_;
}

int AUAudioInputStream::GetNumberOfChannelsFromStream() {
int AUAudioInputStream::GetNumberOfChannelsForDevice() {
// Get the stream format, to be able to read the number of channels.
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput,
Expand Down Expand Up @@ -1217,16 +1097,6 @@ void AUAudioInputStream::HandleError(OSStatus err) {
sink_->OnError();
}

bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
Boolean is_settable = false;
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
static_cast<UInt32>(channel)};
OSStatus result = AudioObjectIsPropertySettable(
input_device_id_, &property_address, &is_settable);
return (result == noErr) ? is_settable : false;
}

void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) {
base::subtle::Release_Store(&input_callback_is_active_, enabled);
}
Expand Down
10 changes: 1 addition & 9 deletions media/audio/mac/audio_low_latency_input_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,11 @@ class MEDIA_EXPORT AUAudioInputStream
base::TimeTicks GetCaptureTime(const AudioTimeStamp* input_time_stamp);

// Gets the number of channels for a stream of audio data.
int GetNumberOfChannelsFromStream();
int GetNumberOfChannelsForDevice();

// Issues the OnError() callback to the |sink_|.
void HandleError(OSStatus err);

// Helper function to check if the volume control is available on specific
// channel.
bool IsVolumeSettableOnChannel(int channel);

// Helper methods to set and get atomic |input_callback_is_active_|.
void SetInputCallbackIsActive(bool active);
bool GetInputCallbackIsActive();
Expand Down Expand Up @@ -208,10 +204,6 @@ class MEDIA_EXPORT AUAudioInputStream
// Fixed capture hardware latency.
base::TimeDelta hardware_latency_;

// The number of channels in each frame of audio data, which is used
// when querying the volume of each channel.
int number_of_channels_in_frame_;

// FIFO used to accumulates recorded data.
media::AudioBlockFifo fifo_;

Expand Down
176 changes: 176 additions & 0 deletions media/audio/mac/audio_manager_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1310,4 +1310,180 @@ std::unique_ptr<AudioManager> CreateAudioManager(
audio_log_factory);
}

// static
bool AudioManagerMac::IsVolumeSettableOnChannel(AudioDeviceID device_id,
int channel) {
Boolean is_settable = false;
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
static_cast<UInt32>(channel)};
OSStatus result =
AudioObjectIsPropertySettable(device_id, &property_address, &is_settable);
return (result == noErr) ? is_settable : false;
}

// static
double AudioManagerMac::GetMaxInputVolume(AudioDeviceID device_id) {
// Verify that we have a valid device.
if (device_id == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return 0.0;
}

// The master channel is 0, Left and right are channels 1 and 2.
// Query if any of the master, left or right channels has volume control.
for (int channel = 0; channel <= GetNumberOfChannelsForDevice(device_id);
++channel) {
// If the volume is settable, the valid volume range is [0.0, 1.0].
if (IsVolumeSettableOnChannel(device_id, channel)) {
return 1.0;
}
}

// Volume control is not available for the audio stream.
return 0.0;
}

// static
void AudioManagerMac::SetInputVolume(AudioDeviceID device_id, double volume) {
CHECK_GE(volume, 0.0);
CHECK_LE(volume, 1.0);

// Verify that we have a valid device.
if (device_id == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return;
}

Float32 volume_float32 = static_cast<Float32>(volume);
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

// Try to set the volume for master volume channel.
if (IsVolumeSettableOnChannel(device_id, kAudioObjectPropertyElementMain)) {
OSStatus result =
AudioObjectSetPropertyData(device_id, &property_address, 0, nullptr,
sizeof(volume_float32), &volume_float32);
if (result != noErr) {
DLOG(WARNING) << "Failed to set volume to " << volume_float32;
}
return;
}

// The master channel is 0, Left and right are channels 1 and 2.
// There is no master volume control, try to set volume for each channel.
[[maybe_unused]] int successful_channels = 0;
for (int channel = 1; channel <= GetNumberOfChannelsForDevice(device_id);
++channel) {
property_address.mElement = static_cast<UInt32>(channel);
if (IsVolumeSettableOnChannel(device_id, channel)) {
OSStatus result =
AudioObjectSetPropertyData(device_id, &property_address, 0, NULL,
sizeof(volume_float32), &volume_float32);
if (result == noErr) {
++successful_channels;
}
}
}

DLOG_IF(WARNING, successful_channels == 0)
<< "Failed to set volume to " << volume_float32;
}

// static
double AudioManagerMac::GetInputVolume(AudioDeviceID device_id) {
// Verify that we have a valid device.
if (device_id == kAudioObjectUnknown) {
LOG(ERROR) << "Device ID is unknown";
return 0.0;
}

AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

if (AudioObjectHasProperty(device_id, &property_address)) {
// The device supports master volume control, get the volume from the
// master channel.
Float32 volume_float32 = 0.0;
UInt32 size = sizeof(volume_float32);
OSStatus result = AudioObjectGetPropertyData(
device_id, &property_address, 0, nullptr, &size, &volume_float32);
if (result == noErr) {
return static_cast<double>(volume_float32);
}
return 0.0;
} else {
// There is no master volume control, try to get the average volume of
// all the channels.
Float32 volume_float32 = 0.0;
int successful_channels = 0;
for (int i = 1; i <= GetNumberOfChannelsForDevice(device_id); ++i) {
property_address.mElement = static_cast<UInt32>(i);
if (AudioObjectHasProperty(device_id, &property_address)) {
Float32 channel_volume = 0;
UInt32 size = sizeof(channel_volume);
OSStatus result = AudioObjectGetPropertyData(
device_id, &property_address, 0, nullptr, &size, &channel_volume);
if (result == noErr) {
volume_float32 += channel_volume;
++successful_channels;
}
}
}

// Get the average volume of the channels.
if (successful_channels != 0) {
return static_cast<double>(volume_float32 / successful_channels);
}
}

DLOG(WARNING) << "Failed to get volume";
return 0.0;
}

// static
bool AudioManagerMac::IsMuted(AudioDeviceID device_id) {
// Verify that we have a valid device.
DCHECK_NE(device_id, kAudioObjectUnknown) << "Device ID is unknown";

AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};

if (!AudioObjectHasProperty(device_id, &property_address)) {
DLOG(ERROR) << "Device does not support checking master mute state";
return false;
}

UInt32 muted = 0;
UInt32 size = sizeof(muted);
OSStatus result = AudioObjectGetPropertyData(device_id, &property_address, 0,
nullptr, &size, &muted);
DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
return result == noErr && muted != 0;
}

// static
int AudioManagerMac::GetNumberOfChannelsForDevice(AudioDeviceID device_id) {
// The master channel is 0, Left and right are channels 1 and 2. And the
// master channel is not counted.

// Get the stream format, to be able to read the number of channels.
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMain};
AudioStreamBasicDescription stream_format;
UInt32 size = sizeof(stream_format);
OSStatus result = AudioObjectGetPropertyData(device_id, &property_address, 0,
nullptr, &size, &stream_format);
if (result != noErr) {
DLOG(WARNING) << "Could not get stream format";
return 0;
}

return static_cast<int>(stream_format.mChannelsPerFrame);
}

} // namespace media

0 comments on commit 975777a

Please sign in to comment.