From c5dc615efefdadb2f9321b140a8f1ae23c3670ed Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Jul 2020 22:39:23 +0200 Subject: [PATCH 1/5] output/osx: use IsDigitASCII() --- src/output/plugins/OSXOutputPlugin.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 6eeb4392b..d8684da16 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -32,6 +32,7 @@ #include "thread/Mutex.hxx" #include "thread/Cond.hxx" #include "util/ByteOrder.hxx" +#include "util/CharUtil.hxx" #include "util/StringAPI.hxx" #include "util/StringBuffer.hxx" #include "util/StringFormat.hxx" @@ -239,7 +240,7 @@ osx_output_parse_channel_map(const char *device_name, } if (want_number && - (isdigit(*channel_map_str) || *channel_map_str == '-') + (IsDigitASCII(*channel_map_str) || *channel_map_str == '-') ) { char *endptr; channel_map[inserted_channels] = strtol(channel_map_str, &endptr, 10); From 745e492d159f34b8c643ebd0d70ab57dc6d5f493 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Jul 2020 22:40:24 +0200 Subject: [PATCH 2/5] output/osx: use [[maybe_unused]] --- src/output/plugins/OSXOutputPlugin.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index d8684da16..8034575f1 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -628,9 +628,9 @@ osx_output_set_device(OSXOutput *oo) */ static OSStatus osx_render(void *vdata, - gcc_unused AudioUnitRenderActionFlags *io_action_flags, - gcc_unused const AudioTimeStamp *in_timestamp, - gcc_unused UInt32 in_bus_number, + [[maybe_unused]] AudioUnitRenderActionFlags *io_action_flags, + [[maybe_unused]] const AudioTimeStamp *in_timestamp, + [[maybe_unused]] UInt32 in_bus_number, UInt32 in_number_frames, AudioBufferList *buffer_list) { From de632882d13fae5ad79acafc4531b707b47ae0f0 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 28 May 2020 15:46:48 +0200 Subject: [PATCH 3/5] output/osx: move code to FindAudioDeviceByName() --- src/output/plugins/OSXOutputPlugin.cxx | 95 ++++++++++++++------------ 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 8034575f1..9ccc45cd2 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -542,16 +542,36 @@ osx_output_hog_device(AudioDeviceID dev_id, bool hog) } } - -static void -osx_output_set_device(OSXOutput *oo) +gcc_pure +static bool +IsAudioDeviceName(AudioDeviceID id, const char *expected_name) noexcept { - OSStatus status; - UInt32 size; + static constexpr AudioObjectPropertyAddress aopa_name{ + kAudioObjectPropertyName, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster, + }; - if (oo->component_subtype != kAudioUnitSubType_HALOutput) - return; + CFStringRef cfname; + UInt32 size = sizeof(cfname); + if (AudioObjectGetPropertyData(id, &aopa_name, + 0, nullptr, + &size, &cfname) != noErr) + return false; + + const Apple::StringRef cfname_(cfname); + + char actual_name[256]; + if (!cfname_.GetCString(actual_name, sizeof(actual_name))) + return false; + + return StringIsEqual(actual_name, expected_name); +} + +static AudioDeviceID +FindAudioDeviceByName(const char *name) +{ /* what are the available audio device IDs? */ static constexpr AudioObjectPropertyAddress aopa_hw_devices{ kAudioHardwarePropertyDevices, @@ -559,60 +579,45 @@ osx_output_set_device(OSXOutput *oo) kAudioObjectPropertyElementMaster, }; - const auto deviceids = + const auto ids = AudioObjectGetPropertyDataArray(kAudioObjectSystemObject, aopa_hw_devices); - /* which audio device matches oo->device_name? */ - static constexpr AudioObjectPropertyAddress aopa_name{ - kAudioObjectPropertyName, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster, - }; - - const unsigned numdevices = deviceids.size(); - unsigned i; - size = sizeof(CFStringRef); - for (i = 0; i < numdevices; i++) { - CFStringRef cfname = nullptr; - status = AudioObjectGetPropertyData(deviceids[i], &aopa_name, - 0, nullptr, - &size, &cfname); - if (status != noErr) - continue; - - const Apple::StringRef cfname_(cfname); - - char name[256]; - if (!cfname_.GetCString(name, sizeof(name))) - continue; - - if (StringIsEqual(oo->device_name, name)) { - FormatDebug(osx_output_domain, - "found matching device: ID=%u, name=%s", - (unsigned)deviceids[i], name); - break; - } + for (const auto id : ids) { + if (IsAudioDeviceName(id, name)) + return id; } - if (i == numdevices) - throw FormatRuntimeError("Found no audio device with name '%s' ", - oo->device_name); + throw FormatRuntimeError("Found no audio device with name '%s' ", + name); +} +static void +osx_output_set_device(OSXOutput *oo) +{ + if (oo->component_subtype != kAudioUnitSubType_HALOutput) + return; + + const auto id = FindAudioDeviceByName(oo->device_name); + + FormatDebug(osx_output_domain, + "found matching device: ID=%u, name=%s", + (unsigned)id, oo->device_name); + + OSStatus status; status = AudioUnitSetProperty(oo->au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, - &(deviceids[i]), - sizeof(AudioDeviceID)); + &id, sizeof(id)); if (status != noErr) Apple::ThrowOSStatus(status, "Unable to set OS X audio output device"); - oo->dev_id = deviceids[i]; + oo->dev_id = id; FormatDebug(osx_output_domain, "set OS X audio output device ID=%u, name=%s", - (unsigned)deviceids[i], oo->device_name); + (unsigned)id, oo->device_name); if (oo->channel_map) osx_output_set_channel_map(oo); From bcbb3371ff24a1bca63264fcf6f2f78d95fcd10a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Jul 2020 22:48:49 +0200 Subject: [PATCH 4/5] apple/AudioUnit: rename to AudioObject.hxx --- src/apple/{AudioUnit.hxx => AudioObject.hxx} | 0 src/output/plugins/OSXOutputPlugin.cxx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/apple/{AudioUnit.hxx => AudioObject.hxx} (100%) diff --git a/src/apple/AudioUnit.hxx b/src/apple/AudioObject.hxx similarity index 100% rename from src/apple/AudioUnit.hxx rename to src/apple/AudioObject.hxx diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 9ccc45cd2..c3c25a965 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "OSXOutputPlugin.hxx" -#include "apple/AudioUnit.hxx" +#include "apple/AudioObject.hxx" #include "apple/StringRef.hxx" #include "apple/Throw.hxx" #include "../OutputAPI.hxx" From e606044271458f6ce335a6ff54832f81523baf0c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Jul 2020 22:49:53 +0200 Subject: [PATCH 5/5] apple/AudioUnit: library wrapping AudioUnit*() functions --- src/apple/AudioUnit.hxx | 55 ++++++++++++++++++ src/output/plugins/OSXOutputPlugin.cxx | 78 ++++++++++---------------- 2 files changed, 84 insertions(+), 49 deletions(-) create mode 100644 src/apple/AudioUnit.hxx diff --git a/src/apple/AudioUnit.hxx b/src/apple/AudioUnit.hxx new file mode 100644 index 000000000..bc6f72b53 --- /dev/null +++ b/src/apple/AudioUnit.hxx @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef APPLE_AUDIO_UNIT_HXX +#define APPLE_AUDIO_UNIT_HXX + +#include "Throw.hxx" + +#include + +template +T +AudioUnitGetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) +{ + UInt32 size = sizeof(T); + T value; + + OSStatus status = AudioUnitGetProperty(inUnit, inID, inScope, + inElement, + &value, &size); + if (status != noErr) + Apple::ThrowOSStatus(status); + + return value; +} + +#endif diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index c3c25a965..696f5acea 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "OSXOutputPlugin.hxx" #include "apple/AudioObject.hxx" +#include "apple/AudioUnit.hxx" #include "apple/StringRef.hxx" #include "apple/Throw.hxx" #include "../OutputAPI.hxx" @@ -266,32 +267,29 @@ osx_output_parse_channel_map(const char *device_name, device_name, num_channels); } +static UInt32 +AudioUnitGetChannelsPerFrame(AudioUnit inUnit) +{ + const auto desc = AudioUnitGetPropertyT(inUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0); + return desc.mChannelsPerFrame; +} + static void osx_output_set_channel_map(OSXOutput *oo) { OSStatus status; - AudioStreamBasicDescription desc; - UInt32 size = sizeof(desc); - memset(&desc, 0, size); - status = AudioUnitGetProperty(oo->au, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, - &desc, - &size); - if (status != noErr) - Apple::ThrowOSStatus(status, - "unable to get number of output device channels"); - - UInt32 num_channels = desc.mChannelsPerFrame; + const UInt32 num_channels = AudioUnitGetChannelsPerFrame(oo->au); std::unique_ptr channel_map(new SInt32[num_channels]); osx_output_parse_channel_map(oo->device_name, oo->channel_map, channel_map.get(), num_channels); - size = num_channels * sizeof(SInt32); + UInt32 size = num_channels * sizeof(SInt32); status = AudioUnitSetProperty(oo->au, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, @@ -444,22 +442,16 @@ osx_output_set_device_format(AudioDeviceID dev_id, return output_format.mSampleRate; } -static OSStatus -osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, - UInt32 *frame_size) +static UInt32 +osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc) { - AudioValueRange value_range = {0, 0}; - UInt32 property_size = sizeof(AudioValueRange); - OSStatus err = AudioUnitGetProperty(au, - kAudioDevicePropertyBufferFrameSizeRange, - kAudioUnitScope_Global, - 0, - &value_range, - &property_size); - if (err != noErr) - return err; + const auto value_range = AudioUnitGetPropertyT(au, + kAudioDevicePropertyBufferFrameSizeRange, + kAudioUnitScope_Global, + 0); UInt32 buffer_frame_size = value_range.mMaximum; + OSStatus err; err = AudioUnitSetProperty(au, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, @@ -471,29 +463,20 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, "Failed to set maximum buffer size: %d", err); - property_size = sizeof(buffer_frame_size); - err = AudioUnitGetProperty(au, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0, - &buffer_frame_size, - &property_size); - if (err != noErr) { - FormatWarning(osx_output_domain, - "Cannot get the buffer frame size: %d", - err); - return err; - } + buffer_frame_size = AudioUnitGetPropertyT(au, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0); buffer_frame_size *= desc.mBytesPerFrame; // We set the frame size to a power of two integer that // is larger than buffer_frame_size. - while (*frame_size < buffer_frame_size + 1) { - *frame_size <<= 1; - } + UInt32 frame_size = 1; + while (frame_size < buffer_frame_size + 1) + frame_size <<= 1; - return noErr; + return frame_size; } static void @@ -789,10 +772,7 @@ OSXOutput::Open(AudioFormat &audio_format) if (status != noErr) Apple::ThrowOSStatus(status, "Unable to initialize OS X audio unit"); - UInt32 buffer_frame_size = 1; - status = osx_output_set_buffer_size(au, asbd, &buffer_frame_size); - if (status != noErr) - Apple::ThrowOSStatus(status, "Unable to set frame size"); + UInt32 buffer_frame_size = osx_output_set_buffer_size(au, asbd); size_t ring_buffer_size = std::max(buffer_frame_size, MPD_OSX_BUFFER_TIME_MS * audio_format.GetFrameSize() * audio_format.sample_rate / 1000);