diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index c8370f03d76..4964c9a45d3 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -37,7 +37,6 @@ endif() # sources set(SRC - src/devices/DefaultSynchronizer.cpp src/devices/DeviceManager.cpp src/devices/NULLDevice.cpp src/devices/OpenCloseDevice.cpp @@ -142,14 +141,12 @@ set(PRIVATE_HDR ) set(PUBLIC_HDR - include/devices/DefaultSynchronizer.h include/devices/DeviceManager.h include/devices/I3DDevice.h include/devices/I3DHandle.h include/devices/IDeviceFactory.h include/devices/IDevice.h include/devices/IHandle.h - include/devices/ISynchronizer.h include/devices/NULLDevice.h include/devices/OpenCloseDevice.h include/devices/ReadDevice.h @@ -435,11 +432,9 @@ if(WITH_COREAUDIO) set(COREAUDIO_SRC plugins/coreaudio/CoreAudioDevice.cpp - plugins/coreaudio/CoreAudioSynchronizer.cpp ) set(COREAUDIO_HDR plugins/coreaudio/CoreAudioDevice.h - plugins/coreaudio/CoreAudioSynchronizer.h ) if(NOT PLUGIN_COREAUDIO) @@ -567,12 +562,10 @@ if(WITH_JACK) if(JACK_FOUND) set(JACK_SRC plugins/jack/JackDevice.cpp - plugins/jack/JackSynchronizer.cpp plugins/jack/JackLibrary.cpp ) set(JACK_HDR plugins/jack/JackDevice.h - plugins/jack/JackSynchronizer.h plugins/jack/JackLibrary.h plugins/jack/JackSymbols.h ) diff --git a/extern/audaspace/bindings/C/AUD_Device.cpp b/extern/audaspace/bindings/C/AUD_Device.cpp index d4643094bc2..f4917942b28 100644 --- a/extern/audaspace/bindings/C/AUD_Device.cpp +++ b/extern/audaspace/bindings/C/AUD_Device.cpp @@ -290,47 +290,32 @@ AUD_API AUD_Device* AUD_Device_getCurrent() return new AUD_Device(device); } -AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, double time) +AUD_API void AUD_seekSynchronizer(double time) { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - synchronizer->seek(*reinterpret_cast*>(handle), time); + DeviceManager::getDevice()->seekSynchronizer(time); } -AUD_API double AUD_getSynchronizerPosition(AUD_Handle* handle) +AUD_API double AUD_getSynchronizerPosition() { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - return synchronizer->getPosition(*reinterpret_cast*>(handle)); - return (*reinterpret_cast*>(handle))->getPosition(); + return DeviceManager::getDevice()->getSynchronizerPosition(); } AUD_API void AUD_playSynchronizer() { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - synchronizer->play(); + DeviceManager::getDevice()->playSynchronizer(); } AUD_API void AUD_stopSynchronizer() { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - synchronizer->stop(); + DeviceManager::getDevice()->stopSynchronizer(); } AUD_API void AUD_setSynchronizerCallback(AUD_syncFunction function, void* data) { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - synchronizer->setSyncCallback(function, data); + DeviceManager::getDevice()->setSyncCallback(function, data); } AUD_API int AUD_isSynchronizerPlaying() { - auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); - if(synchronizer) - return synchronizer->isPlaying(); - return false; + return DeviceManager::getDevice()->isSynchronizerPlaying(); } - diff --git a/extern/audaspace/bindings/C/AUD_Device.h b/extern/audaspace/bindings/C/AUD_Device.h index 05e004a17db..0367e90d9a3 100644 --- a/extern/audaspace/bindings/C/AUD_Device.h +++ b/extern/audaspace/bindings/C/AUD_Device.h @@ -221,14 +221,14 @@ extern AUD_API AUD_Device* AUD_Device_getCurrent(); * \param handle Playback handle. * \param time Time in seconds to seek to. */ -extern AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, double time); +extern AUD_API void AUD_seekSynchronizer(double time); /** * Returns the current sound scene playback time. * \param handle Playback handle. * \return The playback time in seconds. */ -extern AUD_API double AUD_getSynchronizerPosition(AUD_Handle* handle); +extern AUD_API double AUD_getSynchronizerPosition(); /** * Starts the playback of jack transport if possible. diff --git a/extern/audaspace/include/devices/DefaultSynchronizer.h b/extern/audaspace/include/devices/DefaultSynchronizer.h deleted file mode 100644 index e818306603c..00000000000 --- a/extern/audaspace/include/devices/DefaultSynchronizer.h +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 DefaultSynchronizer.h - * @ingroup devices - * The DefaultSynchronizer class. - */ - -#include "ISynchronizer.h" - -AUD_NAMESPACE_BEGIN - -/** - * This class is a default ISynchronizer implementation that actually does no - * synchronization and is intended for devices that don't support it. - */ -class AUD_API DefaultSynchronizer : public ISynchronizer -{ -public: - virtual void seek(std::shared_ptr handle, double time); - virtual double getPosition(std::shared_ptr handle); - virtual void play(); - virtual void stop(); - virtual void setSyncCallback(syncFunction function, void* data); - virtual int isPlaying(); -}; - -AUD_NAMESPACE_END diff --git a/extern/audaspace/include/devices/IDevice.h b/extern/audaspace/include/devices/IDevice.h index b9414e7d187..8facb4f8288 100644 --- a/extern/audaspace/include/devices/IDevice.h +++ b/extern/audaspace/include/devices/IDevice.h @@ -22,17 +22,16 @@ * The IDevice interface. */ +#include + #include "respec/Specification.h" #include "util/ILockable.h" -#include - AUD_NAMESPACE_BEGIN class IHandle; class IReader; class ISound; -class ISynchronizer; /** * @interface IDevice @@ -110,14 +109,22 @@ public: * Sets the overall device volume. * \param volume The overall device volume. */ - virtual void setVolume(float volume)=0; + virtual void setVolume(float volume) = 0; /** - * Retrieves the synchronizer for this device, which enables accurate synchronization - * between audio playback and video playback for example. - * @return The synchronizer which will be the DefaultSynchronizer if synchonization is not supported. + * The syncFunction is called when a synchronization event happens. + * The function awaits three parameters. The first one is a user defined + * pointer, the second informs about whether playback is on and the third + * is the current playback time in seconds. */ - virtual ISynchronizer* getSynchronizer()=0; + typedef void (*syncFunction)(void*, int, float); + + virtual void seekSynchronizer(double time) = 0; + virtual double getSynchronizerPosition() = 0; + virtual void playSynchronizer() = 0; + virtual void stopSynchronizer() = 0; + virtual void setSyncCallback(syncFunction function, void* data) = 0; + virtual int isSynchronizerPlaying() = 0; }; AUD_NAMESPACE_END diff --git a/extern/audaspace/include/devices/ISynchronizer.h b/extern/audaspace/include/devices/ISynchronizer.h deleted file mode 100644 index 430230fbcb3..00000000000 --- a/extern/audaspace/include/devices/ISynchronizer.h +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 ISynchronizer.h - * @ingroup devices - * The ISynchronizer interface. - */ - -#include "Audaspace.h" - -#include - -AUD_NAMESPACE_BEGIN - -class IHandle; - -/** - * @interface ISynchronizer - * This class enables global synchronization of several audio applications if supported. - * JACK for example supports synchronization through JACK Transport. - */ -class AUD_API ISynchronizer -{ -public: - /** - * Destroys the synchronizer. - */ - virtual ~ISynchronizer() {} - - /** - * The syncFunction is called when a synchronization event happens. - * The function awaits three parameters. The first one is a user defined - * pointer, the second informs about whether playback is on and the third - * is the current playback time in seconds. - */ - typedef void (*syncFunction)(void*, int, float); - - /** - * Sets the playback position of a handle and the synchronizer to a specific time. - * @param handle The handle that should be synchronized/seeked. - * @param time The absolute time to synchronize to. - */ - virtual void seek(std::shared_ptr handle, double time) = 0; - - /** - * Retrieves the position of the synchronizer. - * @param handle The handle which is synchronized. - * @return The position in seconds. - */ - virtual double getPosition(std::shared_ptr handle) = 0; - - /** - * Starts the synchronizer playback. - */ - virtual void play() = 0; - - /** - * Stops the synchronizer playback. - */ - virtual void stop() = 0; - - /** - * Sets the callback function that is called when a synchronization event happens. - * @param function The function to be called. - * @param data User data to be passed to the callback. - */ - virtual void setSyncCallback(syncFunction function, void* data) = 0; - - /** - * Retrieves whether the synchronizer is playing back. - * @return Whether the synchronizer plays back. - */ - virtual int isPlaying() = 0; -}; - -AUD_NAMESPACE_END diff --git a/extern/audaspace/include/devices/NULLDevice.h b/extern/audaspace/include/devices/NULLDevice.h index 9af78919f88..bf7c6ede2c7 100644 --- a/extern/audaspace/include/devices/NULLDevice.h +++ b/extern/audaspace/include/devices/NULLDevice.h @@ -85,7 +85,13 @@ public: virtual void unlock(); virtual float getVolume() const; virtual void setVolume(float volume); - virtual ISynchronizer* getSynchronizer(); + + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); + virtual void setSyncCallback(syncFunction function, void* data); + virtual int isSynchronizerPlaying(); /** * Registers this plugin. diff --git a/extern/audaspace/include/devices/SoftwareDevice.h b/extern/audaspace/include/devices/SoftwareDevice.h index 6fe956cac02..47176d0facb 100644 --- a/extern/audaspace/include/devices/SoftwareDevice.h +++ b/extern/audaspace/include/devices/SoftwareDevice.h @@ -26,7 +26,6 @@ #include "devices/IHandle.h" #include "devices/I3DDevice.h" #include "devices/I3DHandle.h" -#include "devices/DefaultSynchronizer.h" #include "util/Buffer.h" #include @@ -330,7 +329,8 @@ private: int m_flags; /// Synchronizer. - DefaultSynchronizer m_synchronizer; + uint64_t m_synchronizerPosition{0}; + int m_synchronizerState{0}; // delete copy constructor and operator= SoftwareDevice(const SoftwareDevice&) = delete; @@ -359,7 +359,6 @@ public: virtual void unlock(); virtual float getVolume() const; virtual void setVolume(float volume); - virtual ISynchronizer* getSynchronizer(); virtual Vector3 getListenerLocation() const; virtual void setListenerLocation(const Vector3& location); @@ -373,6 +372,13 @@ public: virtual void setDopplerFactor(float factor); virtual DistanceModel getDistanceModel() const; virtual void setDistanceModel(DistanceModel model); + + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); + virtual void setSyncCallback(syncFunction function, void* data); + virtual int isSynchronizerPlaying(); }; AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp index 5cbecdf46eb..b923e8951da 100644 --- a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.cpp @@ -136,7 +136,38 @@ void CoreAudioDevice::open() try { - m_synchronizer = std::unique_ptr(new CoreAudioSynchronizer(m_audio_unit)); + OSStatus status = CAClockNew(0, &m_clock_ref); + + if(status != noErr) + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + + CAClockTimebase timebase = kCAClockTimebase_AudioOutputUnit; + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_InternalTimebase, sizeof(timebase), &timebase); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_TimebaseSource, sizeof(m_audio_unit), &m_audio_unit); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } + + CAClockSyncMode sync_mode = kCAClockSyncMode_Internal; + + status = CAClockSetProperty(m_clock_ref, kCAClockProperty_SyncMode, sizeof(sync_mode), &sync_mode); + + if(status != noErr) + { + CAClockDispose(m_clock_ref); + AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); + } } catch(Exception&) { @@ -150,6 +181,7 @@ void CoreAudioDevice::close() // NOTE: Keep the device open for buggy MacOS versions (see blender issue #121911). if(__builtin_available(macOS 15.2, *)) { + CAClockDispose(m_clock_ref); AudioOutputUnitStop(m_audio_unit); AudioUnitUninitialize(m_audio_unit); AudioComponentInstanceDispose(m_audio_unit); @@ -179,9 +211,57 @@ CoreAudioDevice::~CoreAudioDevice() closeNow(); } -ISynchronizer* CoreAudioDevice::getSynchronizer() +void CoreAudioDevice::seekSynchronizer(double time) { - return m_synchronizer.get(); + if(isSynchronizerPlaying()) + CAClockStop(m_clock_ref); + + CAClockTime clock_time; + clock_time.format = kCAClockTimeFormat_Seconds; + clock_time.time.seconds = time; + CAClockSetCurrentTime(m_clock_ref, &clock_time); + + if(isSynchronizerPlaying()) + CAClockStart(m_clock_ref); + + SoftwareDevice::seekSynchronizer(time); +} + +double CoreAudioDevice::getSynchronizerPosition() +{ + CAClockTime clock_time; + + OSStatus status; + + if(isSynchronizerPlaying()) + status = CAClockGetCurrentTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); + else + status = CAClockGetStartTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); + + if(status != noErr) + return 0; + + return clock_time.time.seconds; +} + +void CoreAudioDevice::playSynchronizer() +{ + if(isSynchronizerPlaying()) + return; + + CAClockStart(m_clock_ref); + + SoftwareDevice::playSynchronizer(); +} + +void CoreAudioDevice::stopSynchronizer() +{ + if(!isSynchronizerPlaying()) + return; + + CAClockStop(m_clock_ref); + + SoftwareDevice::stopSynchronizer(); } class CoreAudioDeviceFactory : public IDeviceFactory diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h index c70a49d0002..92300772942 100644 --- a/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h +++ b/extern/audaspace/plugins/coreaudio/CoreAudioDevice.h @@ -28,10 +28,9 @@ #include +#include #include -#include "CoreAudioSynchronizer.h" - #include "devices/OpenCloseDevice.h" AUD_NAMESPACE_BEGIN @@ -52,10 +51,8 @@ private: */ AudioUnit m_audio_unit; - /** - * The Synchronizer. - */ - std::unique_ptr m_synchronizer; + /// The CoreAudio clock referene. + CAClockRef m_clock_ref; /** * Mixes the next bytes into the buffer. @@ -92,7 +89,10 @@ public: */ virtual ~CoreAudioDevice(); - virtual ISynchronizer* getSynchronizer(); + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); /** * Registers this plugin. diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp deleted file mode 100644 index fcfa7fde9be..00000000000 --- a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 "CoreAudioSynchronizer.h" - -#include "CoreAudioDevice.h" -#include "Exception.h" - -AUD_NAMESPACE_BEGIN - -CoreAudioSynchronizer::CoreAudioSynchronizer(AudioUnit& audio_unit) : - m_clock_ref(nullptr), - m_playing(false) -{ - OSStatus status = CAClockNew(0, &m_clock_ref); - - if(status != noErr) - AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); - - CAClockTimebase timebase = kCAClockTimebase_AudioOutputUnit; - - status = CAClockSetProperty(m_clock_ref, kCAClockProperty_InternalTimebase, sizeof(timebase), &timebase); - - if(status != noErr) - { - CAClockDispose(m_clock_ref); - AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); - } - - status = CAClockSetProperty(m_clock_ref, kCAClockProperty_TimebaseSource, sizeof(audio_unit), &audio_unit); - - if(status != noErr) - { - CAClockDispose(m_clock_ref); - AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); - } - - CAClockSyncMode sync_mode = kCAClockSyncMode_Internal; - - status = CAClockSetProperty(m_clock_ref, kCAClockProperty_SyncMode, sizeof(sync_mode), &sync_mode); - - if(status != noErr) - { - CAClockDispose(m_clock_ref); - AUD_THROW(DeviceException, "Could not create a CoreAudio clock."); - } -} - -CoreAudioSynchronizer::~CoreAudioSynchronizer() -{ - CAClockDispose(m_clock_ref); -} - -void CoreAudioSynchronizer::seek(std::shared_ptr handle, double time) -{ - if(m_playing) - CAClockStop(m_clock_ref); - - CAClockTime clock_time; - clock_time.format = kCAClockTimeFormat_Seconds; - clock_time.time.seconds = time; - CAClockSetCurrentTime(m_clock_ref, &clock_time); - - handle->seek(time); - - if(m_playing) - CAClockStart(m_clock_ref); -} - -double CoreAudioSynchronizer::getPosition(std::shared_ptr handle) -{ - CAClockTime clock_time; - - OSStatus status; - - if(m_playing) - status = CAClockGetCurrentTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); - else - status = CAClockGetStartTime(m_clock_ref, kCAClockTimeFormat_Seconds, &clock_time); - - if(status != noErr) - return 0; - - return clock_time.time.seconds; -} - -void CoreAudioSynchronizer::play() -{ - if(m_playing) - return; - - m_playing = true; - CAClockStart(m_clock_ref); -} - -void CoreAudioSynchronizer::stop() -{ - if(!m_playing) - return; - - m_playing = false; - CAClockStop(m_clock_ref); -} - -void CoreAudioSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data) -{ -} - -int CoreAudioSynchronizer::isPlaying() -{ - return m_playing; -} - -AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h b/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h deleted file mode 100644 index 4f9d9b28ea5..00000000000 --- a/extern/audaspace/plugins/coreaudio/CoreAudioSynchronizer.h +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 - -#ifdef COREAUDIO_PLUGIN -#define AUD_BUILD_PLUGIN -#endif - -/** - * @file CoreAudioSynchronizer.h - * @ingroup plugin - * The CoreAudioSynchronizer class. - */ - -#include "devices/ISynchronizer.h" - -#include -#include - -AUD_NAMESPACE_BEGIN - -/** - * This class is a Synchronizer implementation using a CoreAudio clock. - */ -class AUD_PLUGIN_API CoreAudioSynchronizer : public ISynchronizer -{ -private: - /// The CoreAudio clock referene. - CAClockRef m_clock_ref; - - /// Whether the clock is currently playing. - bool m_playing; - -public: - /** - * Creates a new CoreAudioSynchronizer. - * @param device The device that should be synchronized. - */ - CoreAudioSynchronizer(AudioUnit& audio_unit); - virtual ~CoreAudioSynchronizer(); - - virtual void seek(std::shared_ptr handle, double time); - virtual double getPosition(std::shared_ptr handle); - virtual void play(); - virtual void stop(); - virtual void setSyncCallback(syncFunction function, void* data); - virtual int isPlaying(); -}; - -AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/jack/JackDevice.cpp b/extern/audaspace/plugins/jack/JackDevice.cpp index e949d9e64e9..f10a733fd30 100644 --- a/extern/audaspace/plugins/jack/JackDevice.cpp +++ b/extern/audaspace/plugins/jack/JackDevice.cpp @@ -19,7 +19,6 @@ #include "devices/DeviceManager.h" #include "devices/IDeviceFactory.h" #include "Exception.h" -#include "IReader.h" #include #include @@ -41,18 +40,29 @@ void JackDevice::updateRingBuffers() while(m_valid) { - if(m_sync > 1) - { - if(m_syncFunc) - { - state = AUD_jack_transport_query(m_client, &position); - m_syncFunc(m_syncFuncData, state != JackTransportStopped, position.frame / (float) m_specs.rate); - } + state = AUD_jack_transport_query(m_client, &position); + // we sync either when: + // - there was a jack sync callback that requests a playing sync (either start playback or seek during playback) - caused by a m_syncCallRevision change in jack_sync + // - the jack transport state changed to stop from not stopped (i.e. external stopping) - checked here + // - the sync time changes when seeking during the stopped state - caused by a m_syncCallRevision change in jack_mix + if((m_syncCallRevision != m_lastSyncCallRevision) || (state == JackTransportStopped && m_lastState != JackTransportStopped)) + { + int syncRevision = m_syncCallRevision; + float syncTime = m_syncTime; + + if(m_syncFunc) + m_syncFunc(m_syncFuncData, state != JackTransportStopped, syncTime); + + // we reset the ring buffers when we sync to start from the correct position for(i = 0; i < channels; i++) AUD_jack_ringbuffer_reset(m_ringbuffers[i]); + + m_lastSyncCallRevision = syncRevision; } + m_lastState = state; + size = AUD_jack_ringbuffer_write_space(m_ringbuffers[0]); for(i = 1; i < channels; i++) if((temp = AUD_jack_ringbuffer_write_space(m_ringbuffers[i])) < size) @@ -75,11 +85,6 @@ void JackDevice::updateRingBuffers() size = temp; } - if(m_sync > 1) - { - m_sync = 3; - } - m_mixingCondition.wait(lock); } } @@ -91,7 +96,10 @@ int JackDevice::jack_mix(jack_nframes_t length, void* data) int count = device->m_specs.channels; char* buffer; - if(device->m_sync) + jack_position_t position; + jack_transport_state_t state = AUD_jack_transport_query(device->m_client, &position); + + if(state == JackTransportStarting) { // play silence while syncing for(unsigned int i = 0; i < count; i++) @@ -99,6 +107,10 @@ int JackDevice::jack_mix(jack_nframes_t length, void* data) } else { + // ensure that if two consecutive seeks to exactly the same position result in a sync callback call in jack_sync + if((state == JackTransportRolling) && (device->m_lastMixState != JackTransportRolling)) + ++device->m_rollingSyncRevision; + size_t temp; size_t readsamples = AUD_jack_ringbuffer_read_space(device->m_ringbuffers[0]); for(i = 1; i < count; i++) @@ -115,13 +127,23 @@ int JackDevice::jack_mix(jack_nframes_t length, void* data) std::memset(buffer + readsamples * sizeof(float), 0, (length - readsamples) * sizeof(float)); } - if(device->m_mixingLock.try_lock()) + // if we are stopped and the jack transport position changes, we need to notify the mixing thread to call the sync callback + if(state == JackTransportStopped) { - device->m_mixingCondition.notify_all(); - device->m_mixingLock.unlock(); + float syncTime = position.frame / (float) position.frame_rate; + + if(syncTime != device->m_syncTime) + { + device->m_syncTime = syncTime; + ++device->m_syncCallRevision; + } } + + device->m_mixingCondition.notify_all(); } + device->m_lastMixState = state; + return 0; } @@ -129,31 +151,26 @@ int JackDevice::jack_sync(jack_transport_state_t state, jack_position_t* pos, vo { JackDevice* device = (JackDevice*)data; + // we return immediately when the state is stopped as this is handled in the mixing thread separately, as not all stops result in a call here from jack. if(state == JackTransportStopped) return 1; - if(device->m_mixingLock.try_lock()) - { - if(device->m_sync > 2) - { - if(device->m_sync == 3) - { - device->m_sync = 0; - device->m_mixingLock.unlock(); - return 1; - } - } - else - { - device->m_sync = 2; - device->m_mixingCondition.notify_all(); - } - device->m_mixingLock.unlock(); - } - else if(!device->m_sync) - device->m_sync = 1; + float syncTime = pos->frame / (float) pos->frame_rate; - return 0; + // We need to call the sync callback in the mixing thread if + // - the sync time is different, i.e., a new sync to a different time is done + // - if the last state is stopped, i.e., we are starting playback + // - if the sync time is the same but the rolling revision is increased, i.e., we are syncing repeatedly to the same time (happens especially when jumping back to the start) + if((syncTime != device->m_syncTime) || (device->m_lastMixState == JackTransportStopped) || (device->m_rollingSyncRevision != device->m_lastRollingSyncRevision)) + { + device->m_syncTime = syncTime; + ++device->m_syncCallRevision; + device->m_mixingCondition.notify_all(); + device->m_lastRollingSyncRevision = device->m_rollingSyncRevision; + return 0; + } + + return device->m_syncCallRevision == device->m_lastSyncCallRevision; } void JackDevice::jack_shutdown(void* data) @@ -162,8 +179,7 @@ void JackDevice::jack_shutdown(void* data) device->m_valid = false; } -JackDevice::JackDevice(const std::string &name, DeviceSpecs specs, int buffersize) : - m_synchronizer(this) +JackDevice::JackDevice(const std::string& name, DeviceSpecs specs, int buffersize) { if(specs.channels == CHANNELS_INVALID) specs.channels = CHANNELS_STEREO; @@ -218,10 +234,15 @@ JackDevice::JackDevice(const std::string &name, DeviceSpecs specs, int buffersiz create(); + m_lastState = JackTransportStopped; + m_lastMixState = JackTransportStopped; m_valid = true; - m_sync = 0; m_syncFunc = nullptr; - m_nextState = m_state = AUD_jack_transport_query(m_client, nullptr); + m_syncTime = 0; + m_syncCallRevision = 0; + m_lastSyncCallRevision = 0; + m_rollingSyncRevision = 0; + m_lastRollingSyncRevision = 0; // activate the client if(AUD_jack_activate(m_client)) @@ -257,9 +278,7 @@ JackDevice::~JackDevice() delete[] m_ports; - m_mixingLock.lock(); m_mixingCondition.notify_all(); - m_mixingLock.unlock(); m_mixingThread.join(); @@ -270,55 +289,50 @@ JackDevice::~JackDevice() destroy(); } -ISynchronizer* JackDevice::getSynchronizer() -{ - return &m_synchronizer; -} - void JackDevice::playing(bool playing) { // Do nothing. } -void JackDevice::startPlayback() +void JackDevice::playSynchronizer() { AUD_jack_transport_start(m_client); - m_nextState = JackTransportRolling; } -void JackDevice::stopPlayback() +void JackDevice::stopSynchronizer() { AUD_jack_transport_stop(m_client); - m_nextState = JackTransportStopped; } -void JackDevice::seekPlayback(double time) +void JackDevice::seekSynchronizer(double time) { if(time >= 0.0f) AUD_jack_transport_locate(m_client, time * m_specs.rate); } -void JackDevice::setSyncCallback(ISynchronizer::syncFunction sync, void* data) +void JackDevice::setSyncCallback(syncFunction sync, void* data) { m_syncFunc = sync; m_syncFuncData = data; } -double JackDevice::getPlaybackPosition() +double JackDevice::getSynchronizerPosition() { jack_position_t position; - AUD_jack_transport_query(m_client, &position); - return position.frame / (double) m_specs.rate; + jack_transport_state_t state = AUD_jack_transport_query(m_client, &position); + double result = position.frame / (double) position.frame_rate; + + if(state == JackTransportRolling) + { + result += AUD_jack_frames_since_cycle_start(m_client) / (double) position.frame_rate; + } + + return result; } -bool JackDevice::doesPlayback() +int JackDevice::isSynchronizerPlaying() { - jack_transport_state_t state = AUD_jack_transport_query(m_client, nullptr); - - if(state != m_state) - m_nextState = m_state = state; - - return m_nextState != JackTransportStopped; + return AUD_jack_transport_query(m_client, nullptr); } class JackDeviceFactory : public IDeviceFactory diff --git a/extern/audaspace/plugins/jack/JackDevice.h b/extern/audaspace/plugins/jack/JackDevice.h index 7fd1e062846..50d2f95cfad 100644 --- a/extern/audaspace/plugins/jack/JackDevice.h +++ b/extern/audaspace/plugins/jack/JackDevice.h @@ -26,16 +26,18 @@ * The JackDevice class. */ -#include "JackSynchronizer.h" -#include "devices/SoftwareDevice.h" -#include "util/Buffer.h" - -#include +#include #include +#include #include +#include + #include #include +#include "devices/SoftwareDevice.h" +#include "util/Buffer.h" + AUD_NAMESPACE_BEGIN /** @@ -71,9 +73,6 @@ private: */ bool m_valid; - /// Synchronizer. - JackSynchronizer m_synchronizer; - /** * Invalidates the jack device. * \param data The jack device that gets invalidet by jack. @@ -91,24 +90,46 @@ private: AUD_LOCAL static int jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data); /** - * Next JACK Transport state (-1 if not expected to change). + * Last known JACK Transport state used for stop callbacks. */ - jack_transport_state_t m_nextState; + jack_transport_state_t m_lastState; /** - * Current jack transport status. + * Last known JACK Transport state used for stop callbacks. */ - jack_transport_state_t m_state; + jack_transport_state_t m_lastMixState; /** - * Syncronisation state. + * Time for a synchronisation request. */ - int m_sync; + std::atomic m_syncTime; + + /** + * Sync revision used to notify the mixing thread that a sync call is necessary. + */ + std::atomic m_syncCallRevision; + + /** + * The sync revision that the last sync call in the mixing thread handled. + */ + std::atomic m_lastSyncCallRevision; + + /** + * Sync revision that is increased every time jack transport enters the rolling state. + */ + int m_rollingSyncRevision; + + /** + * The last time the jack_sync callback saw the rolling sync revision. + * + * Used to ensure the sync callback will be called when consecutive syncs target the same sync time. + */ + int m_lastRollingSyncRevision; /** * External syncronisation callback function. */ - ISynchronizer::syncFunction m_syncFunc; + syncFunction m_syncFunc; /** * Data for the sync function. @@ -158,42 +179,40 @@ public: */ virtual ~JackDevice(); - virtual ISynchronizer* getSynchronizer(); - /** * Starts jack transport playback. */ - void startPlayback(); + void playSynchronizer(); /** * Stops jack transport playback. */ - void stopPlayback(); + void stopSynchronizer(); /** * Seeks jack transport playback. * \param time The time to seek to. */ - void seekPlayback(double time); + void seekSynchronizer(double time); /** * Sets the sync callback for jack transport playback. * \param sync The callback function. * \param data The data for the function. */ - void setSyncCallback(ISynchronizer::syncFunction sync, void* data); + void setSyncCallback(syncFunction sync, void* data); /** * Retrieves the jack transport playback time. * \return The current time position. */ - double getPlaybackPosition(); + double getSynchronizerPosition(); /** * Returns whether jack transport plays back. * \return Whether jack transport plays back. */ - bool doesPlayback(); + int isSynchronizerPlaying(); /** * Registers this plugin. diff --git a/extern/audaspace/plugins/jack/JackSymbols.h b/extern/audaspace/plugins/jack/JackSymbols.h index f8e22a7da34..0e4c11a139f 100644 --- a/extern/audaspace/plugins/jack/JackSymbols.h +++ b/extern/audaspace/plugins/jack/JackSymbols.h @@ -14,6 +14,7 @@ * limitations under the License. ******************************************************************************/ +JACK_SYMBOL(jack_frames_since_cycle_start); JACK_SYMBOL(jack_transport_query); JACK_SYMBOL(jack_transport_locate); diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.cpp b/extern/audaspace/plugins/jack/JackSynchronizer.cpp deleted file mode 100644 index 0bcafa19ca5..00000000000 --- a/extern/audaspace/plugins/jack/JackSynchronizer.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 "JackSynchronizer.h" - -#include "JackDevice.h" - -AUD_NAMESPACE_BEGIN - -JackSynchronizer::JackSynchronizer(JackDevice* device) : - m_device(device) -{ -} - -void JackSynchronizer::seek(std::shared_ptr handle, double time) -{ - m_device->seekPlayback(time); -} - -double JackSynchronizer::getPosition(std::shared_ptr handle) -{ - return m_device->getPlaybackPosition(); -} - -void JackSynchronizer::play() -{ - m_device->startPlayback(); -} - -void JackSynchronizer::stop() -{ - m_device->stopPlayback(); -} - -void JackSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data) -{ - m_device->setSyncCallback(function, data); -} - -int JackSynchronizer::isPlaying() -{ - return m_device->doesPlayback(); -} - -AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.h b/extern/audaspace/plugins/jack/JackSynchronizer.h deleted file mode 100644 index 8a1f930ebed..00000000000 --- a/extern/audaspace/plugins/jack/JackSynchronizer.h +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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 - -#ifdef JACK_PLUGIN -#define AUD_BUILD_PLUGIN -#endif - -/** - * @file JackSynchronizer.h - * @ingroup plugin - * The JackSynchronizer class. - */ - -#include "devices/ISynchronizer.h" - -AUD_NAMESPACE_BEGIN - -class JackDevice; - -/** - * This class is a Synchronizer implementation using JACK Transport. - */ -class AUD_PLUGIN_API JackSynchronizer : public ISynchronizer -{ -private: - /// The device that is being synchronized. - JackDevice* m_device; - -public: - /** - * Creates a new JackSynchronizer. - * @param device The device that should be synchronized. - */ - JackSynchronizer(JackDevice* device); - - virtual void seek(std::shared_ptr handle, double time); - virtual double getPosition(std::shared_ptr handle); - virtual void play(); - virtual void stop(); - virtual void setSyncCallback(syncFunction function, void* data); - virtual int isPlaying(); -}; - -AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/openal/OpenALDevice.cpp b/extern/audaspace/plugins/openal/OpenALDevice.cpp index 2e9c9d63631..399f463380f 100644 --- a/extern/audaspace/plugins/openal/OpenALDevice.cpp +++ b/extern/audaspace/plugins/openal/OpenALDevice.cpp @@ -15,16 +15,19 @@ ******************************************************************************/ #include "OpenALDevice.h" -#include "devices/DeviceManager.h" -#include "devices/IDeviceFactory.h" -#include "respec/ConverterReader.h" -#include "Exception.h" -#include "ISound.h" #include #include #include +#include "Exception.h" +#include "ISound.h" + +#include "devices/DeviceManager.h" +#include "devices/IDeviceFactory.h" +#include "generator/SilenceReader.h" +#include "respec/ConverterReader.h" + AUD_NAMESPACE_BEGIN /******************************************************************************/ @@ -1388,9 +1391,63 @@ void OpenALDevice::setVolume(float volume) alListenerf(AL_GAIN, volume); } -ISynchronizer* OpenALDevice::getSynchronizer() +void OpenALDevice::seekSynchronizer(double time) { - return &m_synchronizer; + std::lock_guard lock(*this); + + m_synchronizerPosition = uint64_t(time * m_specs.rate); + if(m_silenceHandle) + m_silenceHandle->seek(time); +} + +double OpenALDevice::getSynchronizerPosition() +{ + std::lock_guard lock(*this); + + if(m_silenceHandle) + return m_silenceHandle->getPosition(); + + return m_synchronizerPosition; +} + +void OpenALDevice::playSynchronizer() +{ + std::lock_guard lock(*this); + + if(m_silenceHandle) + m_silenceHandle->resume(); + else + { + auto reader = std::make_shared(m_specs.rate); + reader->seek(m_synchronizerPosition); + m_silenceHandle = play(reader); + } +} + +void OpenALDevice::stopSynchronizer() +{ + std::lock_guard lock(*this); + + if(m_silenceHandle) + { + m_synchronizerPosition = m_silenceHandle->getPosition(); + m_silenceHandle->stop(); + m_silenceHandle.reset(); + } +} + +void OpenALDevice::setSyncCallback(syncFunction function, void* data) +{ +} + +int OpenALDevice::isSynchronizerPlaying() +{ + std::lock_guard lock(*this); + + if(m_silenceHandle) + return m_silenceHandle->getStatus() == STATUS_PLAYING; + + return 0; } /******************************************************************************/ diff --git a/extern/audaspace/plugins/openal/OpenALDevice.h b/extern/audaspace/plugins/openal/OpenALDevice.h index f16851d12eb..99823d12735 100644 --- a/extern/audaspace/plugins/openal/OpenALDevice.h +++ b/extern/audaspace/plugins/openal/OpenALDevice.h @@ -30,7 +30,6 @@ #include "devices/IHandle.h" #include "devices/I3DDevice.h" #include "devices/I3DHandle.h" -#include "devices/DefaultSynchronizer.h" #include "util/Buffer.h" #include @@ -234,7 +233,8 @@ private: Quaternion m_orientation; /// Synchronizer. - DefaultSynchronizer m_synchronizer; + uint64_t m_synchronizerPosition{0}; + std::shared_ptr m_silenceHandle; /** * Starts the streaming thread. @@ -281,7 +281,13 @@ public: virtual void unlock(); virtual float getVolume() const; virtual void setVolume(float volume); - virtual ISynchronizer* getSynchronizer(); + + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); + virtual void setSyncCallback(syncFunction function, void* data); + virtual int isSynchronizerPlaying(); virtual Vector3 getListenerLocation() const; virtual void setListenerLocation(const Vector3& location); diff --git a/extern/audaspace/plugins/pipewire/PipeWireDevice.cpp b/extern/audaspace/plugins/pipewire/PipeWireDevice.cpp index ccb7f4ee910..7f06a62e020 100644 --- a/extern/audaspace/plugins/pipewire/PipeWireDevice.cpp +++ b/extern/audaspace/plugins/pipewire/PipeWireDevice.cpp @@ -19,7 +19,6 @@ #include #include "Exception.h" -#include "IReader.h" #include "PipeWireLibrary.h" #include "devices/DeviceManager.h" @@ -27,61 +26,6 @@ AUD_NAMESPACE_BEGIN -PipeWireDevice::PipeWireSynchronizer::PipeWireSynchronizer(PipeWireDevice* device) : m_device(device) -{ -} - -void PipeWireDevice::PipeWireSynchronizer::updateTickStart() -{ - if (!m_get_tick_start) - { - return; - } - pw_time tm; - AUD_pw_stream_get_time_n(m_device->m_stream, &tm, sizeof(tm)); - m_tick_start = tm.ticks; - m_get_tick_start = false; -} - -void PipeWireDevice::PipeWireSynchronizer::play() -{ - m_playing = true; - m_get_tick_start = true; -} - -void PipeWireDevice::PipeWireSynchronizer::stop() -{ - std::shared_ptr dummy_handle; - m_seek_pos = getPosition(dummy_handle); - m_playing = false; -} - -void PipeWireDevice::PipeWireSynchronizer::seek(std::shared_ptr handle, double time) -{ - /* Update start time here as we might update the seek position while playing back. */ - m_get_tick_start = true; - m_seek_pos = time; - handle->seek(time); -} - -double PipeWireDevice::PipeWireSynchronizer::getPosition(std::shared_ptr handle) -{ - if (!m_playing || m_get_tick_start) - { - return m_seek_pos; - } - pw_time tm; - AUD_pw_stream_get_time_n(m_device->m_stream, &tm, sizeof(tm)); - uint64_t now = AUD_pw_stream_get_nsec(m_device->m_stream); - int64_t diff = now - tm.now; - /* Elapsed time since the last sample was queued. */ - int64_t elapsed = (tm.rate.denom * diff) / (tm.rate.num * SPA_NSEC_PER_SEC); - - /* Calculate the elapsed time in seconds from the last seek position. */ - double elapsed_time = (tm.ticks - m_tick_start + elapsed) * tm.rate.num / double(tm.rate.denom); - return elapsed_time + m_seek_pos; -} - void PipeWireDevice::handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error) { PipeWireDevice* device = (PipeWireDevice*) device_ptr; @@ -139,10 +83,16 @@ void PipeWireDevice::mixAudioBuffer(void* device_ptr) return; } - /* We call this here as the tick is not guaranteed to be up to date + /* We compute this here as the tick is not guaranteed to be up to date * until the "process" callback is triggered. */ - device->m_synchronizer.updateTickStart(); + if(device->m_getSynchronizerStartTime) + { + pw_time tm; + AUD_pw_stream_get_time_n(device->m_stream, &tm, sizeof(tm)); + device->m_synchronizerStartTime = tm.ticks; + device->m_getSynchronizerStartTime = false; + } spa_data& spa_data = pw_buf->buffer->datas[0]; spa_chunk* chunk = spa_data.chunk; @@ -202,10 +152,7 @@ void PipeWireDevice::playing(bool playing) m_mixingCondition.notify_all(); } -PipeWireDevice::PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize) : - m_synchronizer(this), - m_fill_ringbuffer(false), - m_run_mixing_thread(true) +PipeWireDevice::PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize) : m_fill_ringbuffer(false), m_run_mixing_thread(true) { if(specs.channels == CHANNELS_INVALID) specs.channels = CHANNELS_STEREO; @@ -320,9 +267,45 @@ PipeWireDevice::~PipeWireDevice() m_mixingThread.join(); } -ISynchronizer* PipeWireDevice::getSynchronizer() +void PipeWireDevice::seekSynchronizer(double time) { - return &m_synchronizer; + /* Update start time here as we might update the seek position while playing back. */ + m_getSynchronizerStartTime = true; + m_synchronizerStartPosition = time; + + SoftwareDevice::seekSynchronizer(time); +} + +double PipeWireDevice::getSynchronizerPosition() +{ + if(!isSynchronizerPlaying() || m_getSynchronizerStartTime) + { + return m_synchronizerStartPosition; + } + + pw_time tm; + AUD_pw_stream_get_time_n(m_stream, &tm, sizeof(tm)); + uint64_t now = AUD_pw_stream_get_nsec(m_stream); + int64_t diff = now - tm.now; + /* Elapsed time since the last sample was queued. */ + int64_t elapsed = (tm.rate.denom * diff) / (tm.rate.num * SPA_NSEC_PER_SEC); + + /* Calculate the elapsed time in seconds from the last seek position. */ + double elapsed_time = (tm.ticks - m_synchronizerStartTime + elapsed) * tm.rate.num / double(tm.rate.denom); + return elapsed_time + m_synchronizerStartPosition; +} + +void PipeWireDevice::playSynchronizer() +{ + /* Make sure that our start time is up to date. */ + m_getSynchronizerStartTime = true; + SoftwareDevice::playSynchronizer(); +} + +void PipeWireDevice::stopSynchronizer() +{ + m_synchronizerStartPosition = getSynchronizerPosition(); + SoftwareDevice::stopSynchronizer(); } class PipeWireDeviceFactory : public IDeviceFactory diff --git a/extern/audaspace/plugins/pipewire/PipeWireDevice.h b/extern/audaspace/plugins/pipewire/PipeWireDevice.h index 59e57c5bfc7..f9423d34df5 100644 --- a/extern/audaspace/plugins/pipewire/PipeWireDevice.h +++ b/extern/audaspace/plugins/pipewire/PipeWireDevice.h @@ -41,27 +41,6 @@ AUD_NAMESPACE_BEGIN class AUD_PLUGIN_API PipeWireDevice : public SoftwareDevice { private: - class PipeWireSynchronizer : public DefaultSynchronizer - { - PipeWireDevice* m_device; - bool m_playing = false; - bool m_get_tick_start = false; - int64_t m_tick_start = 0.0f; - double m_seek_pos = 0.0f; - - public: - PipeWireSynchronizer(PipeWireDevice* device); - - void updateTickStart(); - virtual void play(); - virtual void stop(); - virtual void seek(std::shared_ptr handle, double time); - virtual double getPosition(std::shared_ptr handle); - }; - - /// Synchronizer. - PipeWireSynchronizer m_synchronizer; - /** * Whether we should start filling our ringbuffer with audio. */ @@ -89,6 +68,11 @@ private: Buffer m_ringbuffer_data; std::condition_variable m_mixingCondition; + /// Synchronizer. + bool m_getSynchronizerStartTime{false}; + int64_t m_synchronizerStartTime{0}; + double m_synchronizerStartPosition{0.0}; + AUD_LOCAL static void handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error); /** @@ -124,7 +108,11 @@ public: */ virtual ~PipeWireDevice(); - virtual ISynchronizer* getSynchronizer(); + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); + /** * Registers this plugin. */ diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index 65acd222c44..03ecf5c15a8 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -25,43 +25,6 @@ AUD_NAMESPACE_BEGIN -PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice* device) : m_device(device) -{ -} - -void PulseAudioDevice::PulseAudioSynchronizer::play() -{ - /* Make sure that our start time is up to date. */ - AUD_pa_stream_get_time(m_device->m_stream, &m_time_start); - m_playing = true; -} - -void PulseAudioDevice::PulseAudioSynchronizer::stop() -{ - std::shared_ptr dummy_handle; - m_seek_pos = getPosition(dummy_handle); - m_playing = false; -} - -void PulseAudioDevice::PulseAudioSynchronizer::seek(std::shared_ptr handle, double time) -{ - /* Update start time here as we might update the seek position while playing back. */ - AUD_pa_stream_get_time(m_device->m_stream, &m_time_start); - m_seek_pos = time; - handle->seek(time); -} - -double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr /*handle*/) -{ - pa_usec_t time; - if(!m_playing) - { - return m_seek_pos; - } - AUD_pa_stream_get_time(m_device->m_stream, &time); - return (time - m_time_start) * 1.0e-6 + m_seek_pos; -} - void PulseAudioDevice::updateRingBuffer() { unsigned int samplesize = AUD_DEVICE_SAMPLE_SIZE(m_specs); @@ -168,13 +131,8 @@ void PulseAudioDevice::playing(bool playing) } } -PulseAudioDevice::PulseAudioDevice(const std::string &name, DeviceSpecs specs, int buffersize) : - m_synchronizer(this), - m_playback(false), - m_corked(true), - m_state(PA_CONTEXT_UNCONNECTED), - m_valid(true), - m_underflows(0) +PulseAudioDevice::PulseAudioDevice(const std::string& name, DeviceSpecs specs, int buffersize) : + m_playback(false), m_corked(true), m_state(PA_CONTEXT_UNCONNECTED), m_valid(true), m_underflows(0) { m_mainloop = AUD_pa_threaded_mainloop_new(); @@ -327,9 +285,37 @@ PulseAudioDevice::~PulseAudioDevice() destroy(); } -ISynchronizer *PulseAudioDevice::getSynchronizer() +void PulseAudioDevice::seekSynchronizer(double time) { - return &m_synchronizer; + /* Update start time here as we might update the seek position while playing back. */ + AUD_pa_stream_get_time(m_stream, &m_synchronizerStartTime); + m_synchronizerStartPosition = time; + + SoftwareDevice::seekSynchronizer(time); +} + +double PulseAudioDevice::getSynchronizerPosition() +{ + pa_usec_t time; + if(!isSynchronizerPlaying()) + { + return m_synchronizerStartPosition; + } + AUD_pa_stream_get_time(m_stream, &time); + return (time - m_synchronizerStartTime) * 1.0e-6 + m_synchronizerStartPosition; +} + +void PulseAudioDevice::playSynchronizer() +{ + /* Make sure that our start time is up to date. */ + AUD_pa_stream_get_time(m_stream, &m_synchronizerStartTime); + SoftwareDevice::playSynchronizer(); +} + +void PulseAudioDevice::stopSynchronizer() +{ + m_synchronizerStartPosition = getSynchronizerPosition(); + SoftwareDevice::stopSynchronizer(); } class PulseAudioDeviceFactory : public IDeviceFactory diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index 37a3897c0da..53d5da3ffa6 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -42,25 +42,6 @@ AUD_NAMESPACE_BEGIN class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice { private: - class PulseAudioSynchronizer : public DefaultSynchronizer - { - PulseAudioDevice* m_device; - bool m_playing = false; - pa_usec_t m_time_start = 0; - double m_seek_pos = 0.0f; - - public: - PulseAudioSynchronizer(PulseAudioDevice* device); - - virtual void play(); - virtual void stop(); - virtual void seek(std::shared_ptr handle, double time); - virtual double getPosition(std::shared_ptr handle); - }; - - /// Synchronizer. - PulseAudioSynchronizer m_synchronizer; - /** * Whether there is currently playback. */ @@ -101,6 +82,10 @@ private: */ std::condition_variable m_mixingCondition; + /// Synchronizer. + pa_usec_t m_synchronizerStartTime{0}; + double m_synchronizerStartPosition{0.0}; + /** * Updates the ring buffer. */ @@ -143,7 +128,10 @@ public: */ virtual ~PulseAudioDevice(); - virtual ISynchronizer* getSynchronizer(); + virtual void seekSynchronizer(double time); + virtual double getSynchronizerPosition(); + virtual void playSynchronizer(); + virtual void stopSynchronizer(); /** * Registers this plugin. diff --git a/extern/audaspace/src/devices/DefaultSynchronizer.cpp b/extern/audaspace/src/devices/DefaultSynchronizer.cpp deleted file mode 100644 index 3ef1f0bfc41..00000000000 --- a/extern/audaspace/src/devices/DefaultSynchronizer.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright 2009-2016 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/DefaultSynchronizer.h" -#include "devices/IHandle.h" - -AUD_NAMESPACE_BEGIN - -void DefaultSynchronizer::seek(std::shared_ptr handle, double time) -{ - handle->seek(time); -} - -double DefaultSynchronizer::getPosition(std::shared_ptr handle) -{ - return handle->getPosition(); -} - -void DefaultSynchronizer::play() -{ -} - -void DefaultSynchronizer::stop() -{ -} - -void DefaultSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data) -{ -} - -int DefaultSynchronizer::isPlaying() -{ - return -1; -} - -AUD_NAMESPACE_END diff --git a/extern/audaspace/src/devices/NULLDevice.cpp b/extern/audaspace/src/devices/NULLDevice.cpp index c90dfff6d75..03d5c79b51c 100644 --- a/extern/audaspace/src/devices/NULLDevice.cpp +++ b/extern/audaspace/src/devices/NULLDevice.cpp @@ -150,9 +150,30 @@ void NULLDevice::setVolume(float volume) { } -ISynchronizer* NULLDevice::getSynchronizer() +void NULLDevice::seekSynchronizer(double time) { - return nullptr; +} + +double NULLDevice::getSynchronizerPosition() +{ + return std::numeric_limits::quiet_NaN(); +} + +void NULLDevice::playSynchronizer() +{ +} + +void NULLDevice::stopSynchronizer() +{ +} + +void NULLDevice::setSyncCallback(syncFunction function, void* data) +{ +} + +int NULLDevice::isSynchronizerPlaying() +{ + return 0; } class NULLDeviceFactory : public IDeviceFactory diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp index cf44965deea..f12b5d92474 100644 --- a/extern/audaspace/src/devices/SoftwareDevice.cpp +++ b/extern/audaspace/src/devices/SoftwareDevice.cpp @@ -473,8 +473,6 @@ bool SoftwareDevice::SoftwareHandle::setStopCallback(stopCallback callback, void return true; } - - /******************************************************************************/ /******************** SoftwareHandle 3DHandle Code ************************/ /******************************************************************************/ @@ -816,6 +814,9 @@ void SoftwareDevice::mix(data_t* buffer, int length) pauseSounds.clear(); stopSounds.clear(); + + if(m_synchronizerState) + m_synchronizerPosition += length; } } @@ -948,11 +949,6 @@ void SoftwareDevice::setVolume(float volume) m_volume = volume; } -ISynchronizer* SoftwareDevice::getSynchronizer() -{ - return &m_synchronizer; -} - /******************************************************************************/ /**************************** 3D Device Code **********************************/ /******************************************************************************/ @@ -1025,4 +1021,39 @@ void SoftwareDevice::setDistanceModel(DistanceModel model) m_flags &= ~RENDER_DISTANCE; } +void SoftwareDevice::seekSynchronizer(double time) +{ + std::lock_guard lock(*this); + + m_synchronizerPosition = uint64_t(time * m_specs.rate); +} + +double SoftwareDevice::getSynchronizerPosition() +{ + return m_synchronizerPosition / m_specs.rate; +} + +void SoftwareDevice::playSynchronizer() +{ + std::lock_guard lock(*this); + + m_synchronizerState = 1; +} + +void SoftwareDevice::stopSynchronizer() +{ + std::lock_guard lock(*this); + + m_synchronizerState = 0; +} + +void SoftwareDevice::setSyncCallback(syncFunction function, void* data) +{ +} + +int SoftwareDevice::isSynchronizerPlaying() +{ + return m_synchronizerState; +} + AUD_NAMESPACE_END diff --git a/source/blender/blenkernel/intern/sound.cc b/source/blender/blenkernel/intern/sound.cc index baa3666dfb2..2f2949d245d 100644 --- a/source/blender/blenkernel/intern/sound.cc +++ b/source/blender/blenkernel/intern/sound.cc @@ -950,7 +950,8 @@ void BKE_sound_play_scene(Scene *scene) if (status != AUD_STATUS_PLAYING) { /* Seeking the synchronizer will also seek the playback handle. * Even if we don't have A/V sync on, keep the synchronizer and handle seek time in sync. */ - AUD_seekSynchronizer(scene->playback_handle, cur_time); + AUD_seekSynchronizer(cur_time); + AUD_Handle_setPosition(scene->playback_handle, cur_time); AUD_Handle_resume(scene->playback_handle); } @@ -1031,7 +1032,8 @@ void BKE_sound_seek_scene(Main *bmain, Scene *scene) * Even if we don't have A/V sync on, keep the synchronizer and handle * seek time in sync. */ - AUD_seekSynchronizer(scene->playback_handle, cur_time); + AUD_seekSynchronizer(cur_time); + AUD_Handle_setPosition(scene->playback_handle, cur_time); } AUD_Device_unlock(sound_device); @@ -1048,7 +1050,7 @@ double BKE_sound_sync_scene(Scene *scene) if (scene->playback_handle) { if (scene->audio.flag & AUDIO_SYNC) { - return AUD_getSynchronizerPosition(scene->playback_handle); + return AUD_getSynchronizerPosition(); } return AUD_Handle_getPosition(scene->playback_handle);