diff --git a/src/apple/AudioObject.hxx b/src/apple/AudioObject.hxx new file mode 100644 index 000000000..99b5b7bf2 --- /dev/null +++ b/src/apple/AudioObject.hxx @@ -0,0 +1,97 @@ +/* + * 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_OBJECT_HXX +#define APPLE_AUDIO_OBJECT_HXX + +#include "Throw.hxx" +#include "util/AllocatedArray.hxx" + +#include + +#include + +std::size_t +AudioObjectGetPropertyDataSize(AudioObjectID inObjectID, + const AudioObjectPropertyAddress &inAddress) +{ + UInt32 size; + OSStatus status = AudioObjectGetPropertyDataSize(inObjectID, + &inAddress, + 0, nullptr, &size); + if (status != noErr) + Apple::ThrowOSStatus(status); + + return size; +} + +template +T +AudioObjectGetPropertyDataT(AudioObjectID inObjectID, + const AudioObjectPropertyAddress &inAddress) +{ + OSStatus status; + UInt32 size = sizeof(T); + T value; + + status = AudioObjectGetPropertyData(inObjectID, &inAddress, + 0, nullptr, + &size, &value); + if (status != noErr) + Apple::ThrowOSStatus(status); + + return value; +} + +template +AllocatedArray +AudioObjectGetPropertyDataArray(AudioObjectID inObjectID, + const AudioObjectPropertyAddress &inAddress) +{ + OSStatus status; + UInt32 size; + + status = AudioObjectGetPropertyDataSize(inObjectID, + &inAddress, + 0, nullptr, &size); + if (status != noErr) + Apple::ThrowOSStatus(status); + + AllocatedArray result(size / sizeof(T)); + + status = AudioObjectGetPropertyData(inObjectID, &inAddress, + 0, nullptr, + &size, result.begin()); + if (status != noErr) + Apple::ThrowOSStatus(status); + + return result; +} + +#endif diff --git a/src/apple/AudioUnit.hxx b/src/apple/AudioUnit.hxx index 99b5b7bf2..bc6f72b53 100644 --- a/src/apple/AudioUnit.hxx +++ b/src/apple/AudioUnit.hxx @@ -27,71 +27,29 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef APPLE_AUDIO_OBJECT_HXX -#define APPLE_AUDIO_OBJECT_HXX +#ifndef APPLE_AUDIO_UNIT_HXX +#define APPLE_AUDIO_UNIT_HXX #include "Throw.hxx" -#include "util/AllocatedArray.hxx" -#include - -#include - -std::size_t -AudioObjectGetPropertyDataSize(AudioObjectID inObjectID, - const AudioObjectPropertyAddress &inAddress) -{ - UInt32 size; - OSStatus status = AudioObjectGetPropertyDataSize(inObjectID, - &inAddress, - 0, nullptr, &size); - if (status != noErr) - Apple::ThrowOSStatus(status); - - return size; -} +#include template T -AudioObjectGetPropertyDataT(AudioObjectID inObjectID, - const AudioObjectPropertyAddress &inAddress) +AudioUnitGetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) { - OSStatus status; UInt32 size = sizeof(T); T value; - status = AudioObjectGetPropertyData(inObjectID, &inAddress, - 0, nullptr, - &size, &value); + OSStatus status = AudioUnitGetProperty(inUnit, inID, inScope, + inElement, + &value, &size); if (status != noErr) Apple::ThrowOSStatus(status); return value; } -template -AllocatedArray -AudioObjectGetPropertyDataArray(AudioObjectID inObjectID, - const AudioObjectPropertyAddress &inAddress) -{ - OSStatus status; - UInt32 size; - - status = AudioObjectGetPropertyDataSize(inObjectID, - &inAddress, - 0, nullptr, &size); - if (status != noErr) - Apple::ThrowOSStatus(status); - - AllocatedArray result(size / sizeof(T)); - - status = AudioObjectGetPropertyData(inObjectID, &inAddress, - 0, nullptr, - &size, result.begin()); - if (status != noErr) - Apple::ThrowOSStatus(status); - - return result; -} - #endif diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 72c07796a..ae2bdf6c7 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "OSXOutputPlugin.hxx" +#include "apple/AudioObject.hxx" #include "apple/AudioUnit.hxx" #include "apple/StringRef.hxx" #include "apple/Throw.hxx" @@ -32,6 +33,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" @@ -44,7 +46,6 @@ #include #include -#include static constexpr unsigned MPD_OSX_BUFFER_TIME_MS = 100; @@ -240,7 +241,7 @@ osx_output_parse_channel_map(const char *device_name, } if (want_number && - (std::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); @@ -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 @@ -569,7 +552,7 @@ IsAudioDeviceName(AudioDeviceID id, const char *expected_name) noexcept return StringIsEqual(actual_name, expected_name); } -static std::optional +static AudioDeviceID FindAudioDeviceByName(const char *name) { /* what are the available audio device IDs? */ @@ -588,7 +571,8 @@ FindAudioDeviceByName(const char *name) return id; } - return {}; + throw FormatRuntimeError("Found no audio device with name '%s' ", + name); } static void @@ -598,29 +582,25 @@ osx_output_set_device(OSXOutput *oo) return; const auto id = FindAudioDeviceByName(oo->device_name); - if (!id.has_value()) - throw FormatRuntimeError("Found no audio device with name '%s' ", - oo->device_name); FormatDebug(osx_output_domain, "found matching device: ID=%u, name=%s", - (unsigned)id.value(), oo->device_name); + (unsigned)id, oo->device_name); OSStatus status; status = AudioUnitSetProperty(oo->au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, - &id.value(), - sizeof(id.value())); + &id, sizeof(id)); if (status != noErr) Apple::ThrowOSStatus(status, "Unable to set OS X audio output device"); - oo->dev_id = id.value(); + oo->dev_id = id; FormatDebug(osx_output_domain, "set OS X audio output device ID=%u, name=%s", - (unsigned)id.value(), oo->device_name); + (unsigned)id, oo->device_name); if (oo->channel_map) osx_output_set_channel_map(oo); @@ -791,10 +771,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);