From 43307b667d911677e41c10459274c617409cf82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Tue, 5 Mar 2024 18:22:17 +0100 Subject: [PATCH] Audaspace: porting changes from upstream. Developed in Blender, see: #107607 --- extern/audaspace/CMakeLists.txt | 2 + .../include/devices/OpenCloseDevice.h | 103 ++++++++++++++++++ .../plugins/coreaudio/CoreAudioDevice.cpp | 67 ++++++------ .../plugins/coreaudio/CoreAudioDevice.h | 17 +-- .../audaspace/src/devices/OpenCloseDevice.cpp | 66 +++++++++++ 5 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 extern/audaspace/include/devices/OpenCloseDevice.h create mode 100644 extern/audaspace/src/devices/OpenCloseDevice.cpp diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index ea16fbf8fd2..555278b2161 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC src/devices/DefaultSynchronizer.cpp src/devices/DeviceManager.cpp src/devices/NULLDevice.cpp + src/devices/OpenCloseDevice.cpp src/devices/ReadDevice.cpp src/devices/SoftwareDevice.cpp src/devices/ThreadedDevice.cpp @@ -150,6 +151,7 @@ set(PUBLIC_HDR include/devices/IHandle.h include/devices/ISynchronizer.h include/devices/NULLDevice.h + include/devices/OpenCloseDevice.h include/devices/ReadDevice.h include/devices/SoftwareDevice.h include/devices/ThreadedDevice.h diff --git a/extern/audaspace/include/devices/OpenCloseDevice.h b/extern/audaspace/include/devices/OpenCloseDevice.h new file mode 100644 index 00000000000..5d127ea295a --- /dev/null +++ b/extern/audaspace/include/devices/OpenCloseDevice.h @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright 2009-2024 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +/** + * @file OpenCloseDevice.h + * @ingroup devices + * The OpenCloseDevice class. + */ + +#include +#include + +#include "devices/SoftwareDevice.h" + +AUD_NAMESPACE_BEGIN + +/** + * This device extends the SoftwareDevice with code for running mixing in a separate thread. + */ +class AUD_PLUGIN_API OpenCloseDevice : public SoftwareDevice +{ +private: + /** + * Whether the device is opened. + */ + bool m_device_opened{false}; + + /** + * Whether there is currently playback. + */ + bool m_playing{false}; + + /** + * Whether thread released the device. + */ + bool m_delayed_close_finished{false}; + + /** + * Thread used to release the device after time delay. + */ + std::thread m_delayed_close_thread; + + /** + * How long to wait until closing the device.. + */ + std::chrono::milliseconds m_device_close_delay{std::chrono::milliseconds(10000)}; + + /** + * Time when playback has stopped. + */ + std::chrono::time_point m_playback_stopped_time; + + /** + * Releases the device after time delay. + */ + void closeAfterDelay(); + + /** + * Starts the playback. + */ + AUD_LOCAL virtual void start() = 0; + + /** + * Stops the playbsck. + */ + AUD_LOCAL virtual void stop() = 0; + + /** + * Acquires the device. + */ + AUD_LOCAL virtual void open() = 0; + + /** + * Releases the device. + */ + AUD_LOCAL virtual void close() = 0; + + // delete copy constructor and operator= + OpenCloseDevice(const OpenCloseDevice&) = delete; + OpenCloseDevice& operator=(const OpenCloseDevice&) = delete; + +protected: + OpenCloseDevice() = default; + + virtual void playing(bool playing); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp index cbb7c133fea..340b22ba8fb 100644 --- a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp @@ -36,22 +36,17 @@ OSStatus CoreAudioDevice::CoreAudio_mix(void* data, AudioUnitRenderActionFlags* return noErr; } -void CoreAudioDevice::playing(bool playing) +void CoreAudioDevice::start() { - if(m_playback != playing) - { - if(playing) - AudioOutputUnitStart(m_audio_unit); - else - AudioOutputUnitStop(m_audio_unit); - } - - m_playback = playing; + AudioOutputUnitStart(m_audio_unit); } -CoreAudioDevice::CoreAudioDevice(DeviceSpecs specs, int buffersize) : -m_playback(false), -m_audio_unit(nullptr) +void CoreAudioDevice::stop() +{ + AudioOutputUnitStop(m_audio_unit); +} + +void CoreAudioDevice::open() { AudioComponentDescription component_description = {}; @@ -71,14 +66,7 @@ m_audio_unit(nullptr) AudioStreamBasicDescription stream_basic_description = {}; - if(specs.channels == CHANNELS_INVALID) - specs.channels = CHANNELS_STEREO; - if(specs.format == FORMAT_INVALID) - specs.format = FORMAT_FLOAT32; - if(specs.rate == RATE_INVALID) - specs.rate = RATE_48000; - - switch(specs.format) + switch(m_specs.format) { case FORMAT_U8: stream_basic_description.mFormatFlags = 0; @@ -105,18 +93,18 @@ m_audio_unit(nullptr) stream_basic_description.mBitsPerChannel = 64; break; default: - specs.format = FORMAT_FLOAT32; + m_specs.format = FORMAT_FLOAT32; stream_basic_description.mFormatFlags = kLinearPCMFormatFlagIsFloat; stream_basic_description.mBitsPerChannel = 32; break; } - stream_basic_description.mSampleRate = specs.rate; + stream_basic_description.mSampleRate = m_specs.rate; stream_basic_description.mFormatID = kAudioFormatLinearPCM; stream_basic_description.mFormatFlags |= kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked; - stream_basic_description.mBytesPerPacket = stream_basic_description.mBytesPerFrame = AUD_DEVICE_SAMPLE_SIZE(specs); + stream_basic_description.mBytesPerPacket = stream_basic_description.mBytesPerFrame = AUD_DEVICE_SAMPLE_SIZE(m_specs); stream_basic_description.mFramesPerPacket = 1; - stream_basic_description.mChannelsPerFrame = specs.channels; + stream_basic_description.mChannelsPerFrame = m_specs.channels; status = AudioUnitSetProperty(m_audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_basic_description, sizeof(stream_basic_description)); @@ -126,8 +114,6 @@ m_audio_unit(nullptr) AUD_THROW(DeviceException, "The audio device couldn't be opened with CoreAudio."); } - m_specs = specs; - AURenderCallbackStruct render_callback_struct; render_callback_struct.inputProc = CoreAudioDevice::CoreAudio_mix; render_callback_struct.inputProcRefCon = this; @@ -157,16 +143,35 @@ m_audio_unit(nullptr) AudioComponentInstanceDispose(m_audio_unit); throw; } +} +void CoreAudioDevice::close() +{ + AudioOutputUnitStop(m_audio_unit); + AudioUnitUninitialize(m_audio_unit); + AudioComponentInstanceDispose(m_audio_unit); +} + +CoreAudioDevice::CoreAudioDevice(DeviceSpecs specs, int buffersize) : +m_playback(false), +m_audio_unit(nullptr) +{ + if(specs.channels == CHANNELS_INVALID) + specs.channels = CHANNELS_STEREO; + if(specs.format == FORMAT_INVALID) + specs.format = FORMAT_FLOAT32; + if(specs.rate == RATE_INVALID) + specs.rate = RATE_48000; + + m_specs = specs; + open(); + close(); create(); } CoreAudioDevice::~CoreAudioDevice() { - AudioOutputUnitStop(m_audio_unit); - AudioUnitUninitialize(m_audio_unit); - AudioComponentInstanceDispose(m_audio_unit); - + close(); destroy(); } diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h index 3770228db6f..c70a49d0002 100644 --- a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h @@ -26,19 +26,20 @@ * The CoreAudioDevice class. */ -#include "CoreAudioSynchronizer.h" -#include "devices/SoftwareDevice.h" - #include #include +#include "CoreAudioSynchronizer.h" + +#include "devices/OpenCloseDevice.h" + AUD_NAMESPACE_BEGIN /** * This device plays back through CoreAudio, the Apple audio API. */ -class AUD_PLUGIN_API CoreAudioDevice : public SoftwareDevice +class AUD_PLUGIN_API CoreAudioDevice : public OpenCloseDevice { private: /** @@ -67,13 +68,15 @@ private: */ AUD_LOCAL static OSStatus CoreAudio_mix(void* data, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time_stamp, UInt32 bus_number, UInt32 number_frames, AudioBufferList* buffer_list); + AUD_LOCAL void start(); + AUD_LOCAL void stop(); + AUD_LOCAL void open(); + AUD_LOCAL void close(); + // delete copy constructor and operator= CoreAudioDevice(const CoreAudioDevice&) = delete; CoreAudioDevice& operator=(const CoreAudioDevice&) = delete; -protected: - virtual void playing(bool playing); - public: /** * Opens the CoreAudio audio device for playback. diff --git a/extern/audaspace/src/devices/OpenCloseDevice.cpp b/extern/audaspace/src/devices/OpenCloseDevice.cpp new file mode 100644 index 00000000000..f1dd83bbdc5 --- /dev/null +++ b/extern/audaspace/src/devices/OpenCloseDevice.cpp @@ -0,0 +1,66 @@ + +/******************************************************************************* + * Copyright 2009-2024 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/OpenCloseDevice.h" + +AUD_NAMESPACE_BEGIN + +void OpenCloseDevice::closeAfterDelay() +{ + for(;;) + { + std::this_thread::sleep_for(m_device_close_delay / 10); + if(m_playing || m_playback_stopped_time.time_since_epoch().count() == 0) + m_playback_stopped_time = std::chrono::steady_clock::now(); + if(std::chrono::steady_clock::now() < m_playback_stopped_time + m_device_close_delay) + continue; + + break; + } + close(); + m_delayed_close_finished = true; + m_device_opened = false; +} + +void OpenCloseDevice::playing(bool playing) +{ + if(m_playing != playing) + { + m_playing = playing; + if(playing) + { + if(!m_device_opened) + open(); + m_device_opened = true; + start(); + } + else + { + stop(); + m_playback_stopped_time = std::chrono::steady_clock::now(); + if(m_delayed_close_thread.joinable() && m_delayed_close_finished) + { + m_delayed_close_thread.join(); + m_delayed_close_finished = false; + } + + if(m_device_opened && !m_delayed_close_thread.joinable()) + m_delayed_close_thread = std::thread(&OpenCloseDevice::closeAfterDelay, this); + } + } +} +AUD_NAMESPACE_END