From b1fba8d3d781dc00c09acaf117beeea5d644c598 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 13:49:38 +0200 Subject: [PATCH 01/12] apple/AudioObject: add missing `inline` --- src/apple/AudioObject.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apple/AudioObject.hxx b/src/apple/AudioObject.hxx index 99b5b7bf2..4050c2e72 100644 --- a/src/apple/AudioObject.hxx +++ b/src/apple/AudioObject.hxx @@ -37,7 +37,7 @@ #include -std::size_t +inline std::size_t AudioObjectGetPropertyDataSize(AudioObjectID inObjectID, const AudioObjectPropertyAddress &inAddress) { From 756f0b80273910e16749a9cc3014a393909ef153 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 13:39:15 +0200 Subject: [PATCH 02/12] apple: build static library Move build rules from src/output/plugins/meson.build --- meson.build | 2 ++ src/apple/meson.build | 27 +++++++++++++++++++++++++++ src/output/plugins/meson.build | 13 +------------ 3 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/apple/meson.build diff --git a/meson.build b/meson.build index a9eb92798..c1e6b1322 100644 --- a/meson.build +++ b/meson.build @@ -319,6 +319,8 @@ subdir('src/thread') subdir('src/net') subdir('src/event') +subdir('src/apple') + subdir('src/lib/dbus') subdir('src/lib/icu') subdir('src/lib/smbclient') diff --git a/src/apple/meson.build b/src/apple/meson.build new file mode 100644 index 000000000..8f9f46484 --- /dev/null +++ b/src/apple/meson.build @@ -0,0 +1,27 @@ +if not is_darwin + apple_dep = dependency('', required: false) + subdir_done() +endif + +audiounit_dep = declare_dependency( + link_args: ['-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreServices'], + dependencies: [ + boost_dep, + ], +) + +apple = static_library( + 'apple', + 'Throw.cxx', + include_directories: inc, + dependencies: [ + audiounit_dep, + ], +) + +apple_dep = declare_dependency( + link_with: apple, + dependencies: [ + audiounit_dep, + ], +) diff --git a/src/output/plugins/meson.build b/src/output/plugins/meson.build index dde46da79..b0b7ce0b9 100644 --- a/src/output/plugins/meson.build +++ b/src/output/plugins/meson.build @@ -74,18 +74,7 @@ endif if is_darwin output_plugins_sources += [ 'OSXOutputPlugin.cxx', - '../../apple/Throw.cxx', ] - audiounit_dep = declare_dependency( - link_args: [ - '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreServices', - ], - dependencies: [ - boost_dep, - ], - ) -else - audiounit_dep = dependency('', required: false) endif conf.set('HAVE_OSX', is_darwin) @@ -148,7 +137,7 @@ output_plugins = static_library( include_directories: inc, dependencies: [ alsa_dep, - audiounit_dep, + apple_dep, libao_dep, libjack_dep, pulse_dep, From db8bf52f7daaa0c54c69a041050e42d40b7060cc Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 13:37:44 +0200 Subject: [PATCH 03/12] apple/AudioObject: add AudioObjectGetStringProperty() --- src/apple/AudioObject.cxx | 40 ++++++++++++++++++++++++++ src/apple/AudioObject.hxx | 8 ++++++ src/apple/meson.build | 1 + src/output/plugins/OSXOutputPlugin.cxx | 18 +++++------- 4 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 src/apple/AudioObject.cxx diff --git a/src/apple/AudioObject.cxx b/src/apple/AudioObject.cxx new file mode 100644 index 000000000..f81f82e2f --- /dev/null +++ b/src/apple/AudioObject.cxx @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#include "AudioObject.hxx" +#include "StringRef.hxx" + +Apple::StringRef +AudioObjectGetStringProperty(AudioObjectID inObjectID, + const AudioObjectPropertyAddress &inAddress) +{ + auto s = AudioObjectGetPropertyDataT(inObjectID, + inAddress); + return Apple::StringRef(s); +} diff --git a/src/apple/AudioObject.hxx b/src/apple/AudioObject.hxx index 4050c2e72..ff0362dd4 100644 --- a/src/apple/AudioObject.hxx +++ b/src/apple/AudioObject.hxx @@ -37,6 +37,10 @@ #include +namespace Apple { +class StringRef; +} + inline std::size_t AudioObjectGetPropertyDataSize(AudioObjectID inObjectID, const AudioObjectPropertyAddress &inAddress) @@ -69,6 +73,10 @@ AudioObjectGetPropertyDataT(AudioObjectID inObjectID, return value; } +Apple::StringRef +AudioObjectGetStringProperty(AudioObjectID inObjectID, + const AudioObjectPropertyAddress &inAddress); + template AllocatedArray AudioObjectGetPropertyDataArray(AudioObjectID inObjectID, diff --git a/src/apple/meson.build b/src/apple/meson.build index 8f9f46484..160c62bc4 100644 --- a/src/apple/meson.build +++ b/src/apple/meson.build @@ -12,6 +12,7 @@ audiounit_dep = declare_dependency( apple = static_library( 'apple', + 'AudioObject.cxx', 'Throw.cxx', include_directories: inc, dependencies: [ diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 696f5acea..b3a58f972 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -535,19 +535,15 @@ IsAudioDeviceName(AudioDeviceID id, const char *expected_name) noexcept kAudioObjectPropertyElementMaster, }; - 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))) + + try { + auto cfname = AudioObjectGetStringProperty(id, aopa_name); + if (!cfname.GetCString(actual_name, sizeof(actual_name))) + return false; + } catch (...) { return false; + } return StringIsEqual(actual_name, expected_name); } From fb83936feb8777c9f244671be1a19e37a4915911 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 14:45:14 +0200 Subject: [PATCH 04/12] apple/AudioUnit: add AudioUnitSetPropertyT() --- src/apple/AudioUnit.hxx | 56 ++++++++++++++++++++++++++ src/output/plugins/OSXOutputPlugin.cxx | 54 +++++-------------------- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/apple/AudioUnit.hxx b/src/apple/AudioUnit.hxx index bc6f72b53..c4a5b2ef7 100644 --- a/src/apple/AudioUnit.hxx +++ b/src/apple/AudioUnit.hxx @@ -52,4 +52,60 @@ AudioUnitGetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID, return value; } +template +void +AudioUnitSetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const T &value) +{ + OSStatus status = AudioUnitSetProperty(inUnit, inID, inScope, + inElement, + &value, sizeof(value)); + if (status != noErr) + Apple::ThrowOSStatus(status); +} + +inline void +AudioUnitSetCurrentDevice(AudioUnit inUnit, const AudioDeviceID &value) +{ + AudioUnitSetPropertyT(inUnit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, + value); +} + +inline void +AudioUnitSetInputStreamFormat(AudioUnit inUnit, + const AudioStreamBasicDescription &value) +{ + AudioUnitSetPropertyT(inUnit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, + value); +} + +inline void +AudioUnitSetInputRenderCallback(AudioUnit inUnit, + const AURenderCallbackStruct &value) +{ + AudioUnitSetPropertyT(inUnit, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, + value); +} + +inline UInt32 +AudioUnitGetBufferFrameSize(AudioUnit inUnit) +{ + return AudioUnitGetPropertyT(inUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, 0); +} + +inline void +AudioUnitSetBufferFrameSize(AudioUnit inUnit, const UInt32 &value) +{ + AudioUnitSetPropertyT(inUnit, kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, 0, + value); +} + #endif diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index b3a58f972..fa65fa437 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -450,24 +450,14 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc) kAudioUnitScope_Global, 0); - UInt32 buffer_frame_size = value_range.mMaximum; - OSStatus err; - err = AudioUnitSetProperty(au, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0, - &buffer_frame_size, - sizeof(buffer_frame_size)); - if (err != noErr) - FormatWarning(osx_output_domain, - "Failed to set maximum buffer size: %d", - err); - - buffer_frame_size = AudioUnitGetPropertyT(au, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0); + try { + AudioUnitSetBufferFrameSize(au, value_range.mMaximum); + } catch (...) { + LogError(std::current_exception(), + "Failed to set maximum buffer size"); + } + auto buffer_frame_size = AudioUnitGetBufferFrameSize(au); buffer_frame_size *= desc.mBytesPerFrame; // We set the frame size to a power of two integer that @@ -583,15 +573,7 @@ osx_output_set_device(OSXOutput *oo) "found matching device: ID=%u, name=%s", (unsigned)id, oo->device_name); - OSStatus status; - status = AudioUnitSetProperty(oo->au, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &id, sizeof(id)); - if (status != noErr) - Apple::ThrowOSStatus(status, - "Unable to set OS X audio output device"); + AudioUnitSetCurrentDevice(oo->au, id); oo->dev_id = id; FormatDebug(osx_output_domain, @@ -742,29 +724,15 @@ OSXOutput::Open(AudioFormat &audio_format) dop_enabled = params.dop; #endif - OSStatus status = - AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, - &asbd, - sizeof(asbd)); - if (status != noErr) - throw std::runtime_error("Unable to set format on OS X device"); + AudioUnitSetInputStreamFormat(au, asbd); AURenderCallbackStruct callback; callback.inputProc = osx_render; callback.inputProcRefCon = this; - status = - AudioUnitSetProperty(au, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, - &callback, sizeof(callback)); - if (status != noErr) { - AudioComponentInstanceDispose(au); - throw std::runtime_error("Unable to set callback for OS X audio unit"); - } + AudioUnitSetInputRenderCallback(au, callback); - status = AudioUnitInitialize(au); + OSStatus status = AudioUnitInitialize(au); if (status != noErr) Apple::ThrowOSStatus(status, "Unable to initialize OS X audio unit"); From 154170e4755fc53c56652a32b13d82db21ff7c2d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:08:58 +0200 Subject: [PATCH 05/12] output/osx: clear `pause` flag only after successful AudioOutputUnitStart() --- src/output/plugins/OSXOutputPlugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index fa65fa437..e87c5c3db 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -762,12 +762,12 @@ OSXOutput::Play(const void *chunk, size_t size) { assert(size > 0); if (pause) { - pause = false; OSStatus status = AudioOutputUnitStart(au); if (status != 0) { AudioUnitUninitialize(au); throw std::runtime_error("Unable to restart audio output after pause"); } + pause = false; } #ifdef ENABLE_DSD if (dop_enabled) { From 8125a5dddb250b9a9ceb4917b31a33c5813d392e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:09:18 +0200 Subject: [PATCH 06/12] output/osx: don't uninitialize AudioUnit if restart fails This shall be done by Close(), which will be called automatically after an error. --- src/output/plugins/OSXOutputPlugin.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index e87c5c3db..e7b677d04 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -763,10 +763,9 @@ OSXOutput::Play(const void *chunk, size_t size) assert(size > 0); if (pause) { OSStatus status = AudioOutputUnitStart(au); - if (status != 0) { - AudioUnitUninitialize(au); + if (status != noErr) throw std::runtime_error("Unable to restart audio output after pause"); - } + pause = false; } #ifdef ENABLE_DSD From f032925c2d8e82b4fd1836dc1f5cf15979895c47 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:07:39 +0200 Subject: [PATCH 07/12] output/osx: add `started` flag This will keep track of AudioOutputUnitStart() and AudioOutputUnitStop(). This will provide some separation between "not (yet) (re)started" and "paused". --- src/output/plugins/OSXOutputPlugin.cxx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index e7b677d04..10cf419e9 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -73,7 +73,14 @@ struct OSXOutput final : AudioOutput { const char *device_name; const char *const channel_map; const bool hog_device; + bool pause; + + /** + * Is the audio unit "started", i.e. was AudioOutputUnitStart() called? + */ + bool started; + #ifdef ENABLE_DSD /** * Enable DSD over PCM according to the DoP standard? @@ -755,18 +762,22 @@ OSXOutput::Open(AudioFormat &audio_format) Apple::ThrowOSStatus(status, "Unable to start audio output"); pause = false; + started = true; } size_t OSXOutput::Play(const void *chunk, size_t size) { assert(size > 0); - if (pause) { + + pause = false; + + if (!started) { OSStatus status = AudioOutputUnitStart(au); if (status != noErr) throw std::runtime_error("Unable to restart audio output after pause"); - pause = false; + started = true; } #ifdef ENABLE_DSD if (dop_enabled) { @@ -797,10 +808,13 @@ OSXOutput::Delay() const noexcept bool OSXOutput::Pause() { - if (!pause) { - pause = true; + pause = true; + + if (started) { AudioOutputUnitStop(au); + started = false; } + return true; } From fe5b81e1805b15e14865124bcca9543f23f3f3f8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:13:05 +0200 Subject: [PATCH 08/12] output/osx: check `started` in Close() and Cancel() --- src/output/plugins/OSXOutputPlugin.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 10cf419e9..6d5bb3a23 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -667,7 +667,8 @@ OSXOutput::Disable() noexcept void OSXOutput::Close() noexcept { - AudioOutputUnitStop(au); + if (started) + AudioOutputUnitStop(au); AudioUnitUninitialize(au); delete ring_buffer; } @@ -821,7 +822,11 @@ bool OSXOutput::Pause() void OSXOutput::Cancel() noexcept { - AudioOutputUnitStop(au); + if (started) { + AudioOutputUnitStop(au); + started = false; + } + ring_buffer->reset(); #ifdef ENABLE_DSD pcm_export->Reset(); From 80471025429d7a349ff94f5fd09253f4aa61398c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:19:01 +0200 Subject: [PATCH 09/12] output/osx: don't restart AudioUnit at the end of Cancel() We shouldn't restart the AudioUnit while the ring buffer is empty, or else our render callback may emit noise. Closes https://github.com/MusicPlayerDaemon/MPD/issues/771 --- NEWS | 1 + src/output/plugins/OSXOutputPlugin.cxx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cfab38190..bdc81cb38 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ ver 0.21.25 (not yet released) - opus: fix memory leak * output - osx: improve sample rate selection + - osx: fix noise while stopping * Windows/Android: - fix Boost detection after breaking change in Meson 0.54 diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 6d5bb3a23..bcd1008cf 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -831,7 +831,8 @@ OSXOutput::Cancel() noexcept #ifdef ENABLE_DSD pcm_export->Reset(); #endif - AudioOutputUnitStart(au); + + /* the AudioUnit will be restarted by the next Play() call */ } int From bc9e074822d03e38cffcfd6bff06888b9a5ec259 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:21:19 +0200 Subject: [PATCH 10/12] output/osx: postpone start until the first Play() call Wait until there is some data; don't let our render callback be invoked without any data. --- src/output/plugins/OSXOutputPlugin.cxx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index bcd1008cf..dfa042299 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -758,12 +758,8 @@ OSXOutput::Open(AudioFormat &audio_format) #endif ring_buffer = new boost::lockfree::spsc_queue(ring_buffer_size); - status = AudioOutputUnitStart(au); - if (status != 0) - Apple::ThrowOSStatus(status, "Unable to start audio output"); - pause = false; - started = true; + started = false; } size_t From 6fed6e50e4ab0844133ae20ad834b5d65ecd89e4 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:25:45 +0200 Subject: [PATCH 11/12] output/osx: merge some duplicate code --- src/output/plugins/OSXOutputPlugin.cxx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index dfa042299..a041fcbcd 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -776,23 +776,31 @@ OSXOutput::Play(const void *chunk, size_t size) started = true; } + + ConstBuffer input((const uint8_t *)chunk, size); + #ifdef ENABLE_DSD if (dop_enabled) { - const auto e = pcm_export->Export({chunk, size}); + input = ConstBuffer::FromVoid(pcm_export->Export(input.ToVoid())); /* the DoP (DSD over PCM) filter converts two frames at a time and ignores the last odd frame; if there was only one frame (e.g. the last frame in the file), the result is empty; to avoid an endless loop, bail out here, and pretend the one frame has been played */ - if (e.empty()) + if (input.empty()) return size; - - size_t bytes_written = ring_buffer->push((const uint8_t *)e.data, e.size); - return pcm_export->CalcSourceSize(bytes_written); } #endif - return ring_buffer->push((const uint8_t *)chunk, size); + + size_t bytes_written = ring_buffer->push(input.data, input.size); + +#ifdef ENABLE_DSD + if (dop_enabled) + bytes_written = pcm_export->CalcSourceSize(bytes_written); +#endif + + return bytes_written; } std::chrono::steady_clock::duration From daefc61aa4f603418a28994343bf21e335b2e94e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 2 Jul 2020 15:22:16 +0200 Subject: [PATCH 12/12] output/osx: postpone start until the end of Play() Wait until there is data in the ring buffer. --- src/output/plugins/OSXOutputPlugin.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index a041fcbcd..0a51af7a5 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -769,14 +769,6 @@ OSXOutput::Play(const void *chunk, size_t size) pause = false; - if (!started) { - OSStatus status = AudioOutputUnitStart(au); - if (status != noErr) - throw std::runtime_error("Unable to restart audio output after pause"); - - started = true; - } - ConstBuffer input((const uint8_t *)chunk, size); #ifdef ENABLE_DSD @@ -795,6 +787,14 @@ OSXOutput::Play(const void *chunk, size_t size) size_t bytes_written = ring_buffer->push(input.data, input.size); + if (!started) { + OSStatus status = AudioOutputUnitStart(au); + if (status != noErr) + throw std::runtime_error("Unable to restart audio output after pause"); + + started = true; + } + #ifdef ENABLE_DSD if (dop_enabled) bytes_written = pcm_export->CalcSourceSize(bytes_written);