Merge branch 'v0.21.x'

This commit is contained in:
Max Kellermann 2020-07-01 23:06:54 +02:00
commit 4d88bddfe2
3 changed files with 144 additions and 112 deletions

97
src/apple/AudioObject.hxx Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* 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 <CoreAudio/AudioHardware.h>
#include <cstddef>
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<typename T>
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<typename T>
AllocatedArray<T>
AudioObjectGetPropertyDataArray(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
OSStatus status;
UInt32 size;
status = AudioObjectGetPropertyDataSize(inObjectID,
&inAddress,
0, nullptr, &size);
if (status != noErr)
Apple::ThrowOSStatus(status);
AllocatedArray<T> result(size / sizeof(T));
status = AudioObjectGetPropertyData(inObjectID, &inAddress,
0, nullptr,
&size, result.begin());
if (status != noErr)
Apple::ThrowOSStatus(status);
return result;
}
#endif

View File

@ -27,71 +27,29 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef APPLE_AUDIO_OBJECT_HXX #ifndef APPLE_AUDIO_UNIT_HXX
#define APPLE_AUDIO_OBJECT_HXX #define APPLE_AUDIO_UNIT_HXX
#include "Throw.hxx" #include "Throw.hxx"
#include "util/AllocatedArray.hxx"
#include <CoreAudio/AudioHardware.h> #include <AudioUnit/AudioUnit.h>
#include <cstddef>
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<typename T> template<typename T>
T T
AudioObjectGetPropertyDataT(AudioObjectID inObjectID, AudioUnitGetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID,
const AudioObjectPropertyAddress &inAddress) AudioUnitScope inScope,
AudioUnitElement inElement)
{ {
OSStatus status;
UInt32 size = sizeof(T); UInt32 size = sizeof(T);
T value; T value;
status = AudioObjectGetPropertyData(inObjectID, &inAddress, OSStatus status = AudioUnitGetProperty(inUnit, inID, inScope,
0, nullptr, inElement,
&size, &value); &value, &size);
if (status != noErr) if (status != noErr)
Apple::ThrowOSStatus(status); Apple::ThrowOSStatus(status);
return value; return value;
} }
template<typename T>
AllocatedArray<T>
AudioObjectGetPropertyDataArray(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
OSStatus status;
UInt32 size;
status = AudioObjectGetPropertyDataSize(inObjectID,
&inAddress,
0, nullptr, &size);
if (status != noErr)
Apple::ThrowOSStatus(status);
AllocatedArray<T> result(size / sizeof(T));
status = AudioObjectGetPropertyData(inObjectID, &inAddress,
0, nullptr,
&size, result.begin());
if (status != noErr)
Apple::ThrowOSStatus(status);
return result;
}
#endif #endif

View File

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "OSXOutputPlugin.hxx" #include "OSXOutputPlugin.hxx"
#include "apple/AudioObject.hxx"
#include "apple/AudioUnit.hxx" #include "apple/AudioUnit.hxx"
#include "apple/StringRef.hxx" #include "apple/StringRef.hxx"
#include "apple/Throw.hxx" #include "apple/Throw.hxx"
@ -32,6 +33,7 @@
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include "util/ByteOrder.hxx" #include "util/ByteOrder.hxx"
#include "util/CharUtil.hxx"
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "util/StringBuffer.hxx" #include "util/StringBuffer.hxx"
#include "util/StringFormat.hxx" #include "util/StringFormat.hxx"
@ -44,7 +46,6 @@
#include <boost/lockfree/spsc_queue.hpp> #include <boost/lockfree/spsc_queue.hpp>
#include <memory> #include <memory>
#include <optional>
static constexpr unsigned MPD_OSX_BUFFER_TIME_MS = 100; 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 && if (want_number &&
(std::isdigit(*channel_map_str) || *channel_map_str == '-') (IsDigitASCII(*channel_map_str) || *channel_map_str == '-')
) { ) {
char *endptr; char *endptr;
channel_map[inserted_channels] = strtol(channel_map_str, &endptr, 10); 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); device_name, num_channels);
} }
static UInt32
AudioUnitGetChannelsPerFrame(AudioUnit inUnit)
{
const auto desc = AudioUnitGetPropertyT<AudioStreamBasicDescription>(inUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0);
return desc.mChannelsPerFrame;
}
static void static void
osx_output_set_channel_map(OSXOutput *oo) osx_output_set_channel_map(OSXOutput *oo)
{ {
OSStatus status; OSStatus status;
AudioStreamBasicDescription desc; const UInt32 num_channels = AudioUnitGetChannelsPerFrame(oo->au);
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;
std::unique_ptr<SInt32[]> channel_map(new SInt32[num_channels]); std::unique_ptr<SInt32[]> channel_map(new SInt32[num_channels]);
osx_output_parse_channel_map(oo->device_name, osx_output_parse_channel_map(oo->device_name,
oo->channel_map, oo->channel_map,
channel_map.get(), channel_map.get(),
num_channels); num_channels);
size = num_channels * sizeof(SInt32); UInt32 size = num_channels * sizeof(SInt32);
status = AudioUnitSetProperty(oo->au, status = AudioUnitSetProperty(oo->au,
kAudioOutputUnitProperty_ChannelMap, kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Input, kAudioUnitScope_Input,
@ -444,22 +442,16 @@ osx_output_set_device_format(AudioDeviceID dev_id,
return output_format.mSampleRate; return output_format.mSampleRate;
} }
static OSStatus static UInt32
osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc)
UInt32 *frame_size)
{ {
AudioValueRange value_range = {0, 0}; const auto value_range = AudioUnitGetPropertyT<AudioValueRange>(au,
UInt32 property_size = sizeof(AudioValueRange); kAudioDevicePropertyBufferFrameSizeRange,
OSStatus err = AudioUnitGetProperty(au, kAudioUnitScope_Global,
kAudioDevicePropertyBufferFrameSizeRange, 0);
kAudioUnitScope_Global,
0,
&value_range,
&property_size);
if (err != noErr)
return err;
UInt32 buffer_frame_size = value_range.mMaximum; UInt32 buffer_frame_size = value_range.mMaximum;
OSStatus err;
err = AudioUnitSetProperty(au, err = AudioUnitSetProperty(au,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global, kAudioUnitScope_Global,
@ -471,29 +463,20 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc,
"Failed to set maximum buffer size: %d", "Failed to set maximum buffer size: %d",
err); err);
property_size = sizeof(buffer_frame_size); buffer_frame_size = AudioUnitGetPropertyT<UInt32>(au,
err = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize,
kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global,
kAudioUnitScope_Global, 0);
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 *= desc.mBytesPerFrame; buffer_frame_size *= desc.mBytesPerFrame;
// We set the frame size to a power of two integer that // We set the frame size to a power of two integer that
// is larger than buffer_frame_size. // is larger than buffer_frame_size.
while (*frame_size < buffer_frame_size + 1) { UInt32 frame_size = 1;
*frame_size <<= 1; while (frame_size < buffer_frame_size + 1)
} frame_size <<= 1;
return noErr; return frame_size;
} }
static void static void
@ -569,7 +552,7 @@ IsAudioDeviceName(AudioDeviceID id, const char *expected_name) noexcept
return StringIsEqual(actual_name, expected_name); return StringIsEqual(actual_name, expected_name);
} }
static std::optional<AudioDeviceID> static AudioDeviceID
FindAudioDeviceByName(const char *name) FindAudioDeviceByName(const char *name)
{ {
/* what are the available audio device IDs? */ /* what are the available audio device IDs? */
@ -588,7 +571,8 @@ FindAudioDeviceByName(const char *name)
return id; return id;
} }
return {}; throw FormatRuntimeError("Found no audio device with name '%s' ",
name);
} }
static void static void
@ -598,29 +582,25 @@ osx_output_set_device(OSXOutput *oo)
return; return;
const auto id = FindAudioDeviceByName(oo->device_name); 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, FormatDebug(osx_output_domain,
"found matching device: ID=%u, name=%s", "found matching device: ID=%u, name=%s",
(unsigned)id.value(), oo->device_name); (unsigned)id, oo->device_name);
OSStatus status; OSStatus status;
status = AudioUnitSetProperty(oo->au, status = AudioUnitSetProperty(oo->au,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, kAudioUnitScope_Global,
0, 0,
&id.value(), &id, sizeof(id));
sizeof(id.value()));
if (status != noErr) if (status != noErr)
Apple::ThrowOSStatus(status, Apple::ThrowOSStatus(status,
"Unable to set OS X audio output device"); "Unable to set OS X audio output device");
oo->dev_id = id.value(); oo->dev_id = id;
FormatDebug(osx_output_domain, FormatDebug(osx_output_domain,
"set OS X audio output device ID=%u, name=%s", "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) if (oo->channel_map)
osx_output_set_channel_map(oo); osx_output_set_channel_map(oo);
@ -791,10 +771,7 @@ OSXOutput::Open(AudioFormat &audio_format)
if (status != noErr) if (status != noErr)
Apple::ThrowOSStatus(status, "Unable to initialize OS X audio unit"); Apple::ThrowOSStatus(status, "Unable to initialize OS X audio unit");
UInt32 buffer_frame_size = 1; UInt32 buffer_frame_size = osx_output_set_buffer_size(au, asbd);
status = osx_output_set_buffer_size(au, asbd, &buffer_frame_size);
if (status != noErr)
Apple::ThrowOSStatus(status, "Unable to set frame size");
size_t ring_buffer_size = std::max<size_t>(buffer_frame_size, size_t ring_buffer_size = std::max<size_t>(buffer_frame_size,
MPD_OSX_BUFFER_TIME_MS * audio_format.GetFrameSize() * audio_format.sample_rate / 1000); MPD_OSX_BUFFER_TIME_MS * audio_format.GetFrameSize() * audio_format.sample_rate / 1000);