Merge branch 'v0.21.x'
This commit is contained in:
commit
4d88bddfe2
97
src/apple/AudioObject.hxx
Normal file
97
src/apple/AudioObject.hxx
Normal 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
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user