Audaspace: Synchronizer API change
Removes the playback handle from the synchronizer API and integrates it into the device, removing the ISynchronizer interface completely. This has been discussed in more detail in #126047 and its main purpose is to unify the handling of synchronizer events, especially seek which needed different logic for jack vs other backends. OpenAL now needs a silence playback handle for synchronization but all other backends are pretty straightforward just counting the mixed samples in the SoftwareDevice with some specializations for specific backends that had their own synchronizers before. Note: CoreAudio changes are untested as I don't have a Mac. Pull Request: https://projects.blender.org/blender/blender/pulls/133191
This commit is contained in:
committed by
Sebastian Parborg
parent
17e0d88c05
commit
57117497fe
7
extern/audaspace/CMakeLists.txt
vendored
7
extern/audaspace/CMakeLists.txt
vendored
@@ -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
|
||||
)
|
||||
|
||||
31
extern/audaspace/bindings/C/AUD_Device.cpp
vendored
31
extern/audaspace/bindings/C/AUD_Device.cpp
vendored
@@ -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<std::shared_ptr<IHandle>*>(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<std::shared_ptr<IHandle>*>(handle));
|
||||
return (*reinterpret_cast<std::shared_ptr<IHandle>*>(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();
|
||||
}
|
||||
|
||||
|
||||
4
extern/audaspace/bindings/C/AUD_Device.h
vendored
4
extern/audaspace/bindings/C/AUD_Device.h
vendored
@@ -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.
|
||||
|
||||
@@ -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<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> handle);
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void setSyncCallback(syncFunction function, void* data);
|
||||
virtual int isPlaying();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
23
extern/audaspace/include/devices/IDevice.h
vendored
23
extern/audaspace/include/devices/IDevice.h
vendored
@@ -22,17 +22,16 @@
|
||||
* The IDevice interface.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "respec/Specification.h"
|
||||
#include "util/ILockable.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
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
|
||||
|
||||
92
extern/audaspace/include/devices/ISynchronizer.h
vendored
92
extern/audaspace/include/devices/ISynchronizer.h
vendored
@@ -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 <memory>
|
||||
|
||||
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<IHandle> 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<IHandle> 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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 <list>
|
||||
@@ -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
|
||||
|
||||
@@ -136,7 +136,38 @@ void CoreAudioDevice::open()
|
||||
|
||||
try
|
||||
{
|
||||
m_synchronizer = std::unique_ptr<CoreAudioSynchronizer>(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
|
||||
|
||||
@@ -28,10 +28,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <AudioToolbox/CoreAudioClock.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
#include "CoreAudioSynchronizer.h"
|
||||
|
||||
#include "devices/OpenCloseDevice.h"
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
@@ -52,10 +51,8 @@ private:
|
||||
*/
|
||||
AudioUnit m_audio_unit;
|
||||
|
||||
/**
|
||||
* The Synchronizer.
|
||||
*/
|
||||
std::unique_ptr<CoreAudioSynchronizer> 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.
|
||||
|
||||
@@ -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<IHandle> 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<IHandle> 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
|
||||
@@ -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 <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/CoreAudioClock.h>
|
||||
|
||||
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<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> handle);
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void setSyncCallback(syncFunction function, void* data);
|
||||
virtual int isPlaying();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
144
extern/audaspace/plugins/jack/JackDevice.cpp
vendored
144
extern/audaspace/plugins/jack/JackDevice.cpp
vendored
@@ -19,7 +19,6 @@
|
||||
#include "devices/DeviceManager.h"
|
||||
#include "devices/IDeviceFactory.h"
|
||||
#include "Exception.h"
|
||||
#include "IReader.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
@@ -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
|
||||
|
||||
65
extern/audaspace/plugins/jack/JackDevice.h
vendored
65
extern/audaspace/plugins/jack/JackDevice.h
vendored
@@ -26,16 +26,18 @@
|
||||
* The JackDevice class.
|
||||
*/
|
||||
|
||||
#include "JackSynchronizer.h"
|
||||
#include "devices/SoftwareDevice.h"
|
||||
#include "util/Buffer.h"
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
||||
#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<float> m_syncTime;
|
||||
|
||||
/**
|
||||
* Sync revision used to notify the mixing thread that a sync call is necessary.
|
||||
*/
|
||||
std::atomic<int> m_syncCallRevision;
|
||||
|
||||
/**
|
||||
* The sync revision that the last sync call in the mixing thread handled.
|
||||
*/
|
||||
std::atomic<int> 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.
|
||||
|
||||
1
extern/audaspace/plugins/jack/JackSymbols.h
vendored
1
extern/audaspace/plugins/jack/JackSymbols.h
vendored
@@ -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);
|
||||
|
||||
|
||||
@@ -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<IHandle> handle, double time)
|
||||
{
|
||||
m_device->seekPlayback(time);
|
||||
}
|
||||
|
||||
double JackSynchronizer::getPosition(std::shared_ptr<IHandle> 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
|
||||
59
extern/audaspace/plugins/jack/JackSynchronizer.h
vendored
59
extern/audaspace/plugins/jack/JackSynchronizer.h
vendored
@@ -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<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> handle);
|
||||
virtual void play();
|
||||
virtual void stop();
|
||||
virtual void setSyncCallback(syncFunction function, void* data);
|
||||
virtual int isPlaying();
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
71
extern/audaspace/plugins/openal/OpenALDevice.cpp
vendored
71
extern/audaspace/plugins/openal/OpenALDevice.cpp
vendored
@@ -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 <chrono>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#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<ILockable> lock(*this);
|
||||
|
||||
m_synchronizerPosition = uint64_t(time * m_specs.rate);
|
||||
if(m_silenceHandle)
|
||||
m_silenceHandle->seek(time);
|
||||
}
|
||||
|
||||
double OpenALDevice::getSynchronizerPosition()
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
if(m_silenceHandle)
|
||||
return m_silenceHandle->getPosition();
|
||||
|
||||
return m_synchronizerPosition;
|
||||
}
|
||||
|
||||
void OpenALDevice::playSynchronizer()
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
if(m_silenceHandle)
|
||||
m_silenceHandle->resume();
|
||||
else
|
||||
{
|
||||
auto reader = std::make_shared<SilenceReader>(m_specs.rate);
|
||||
reader->seek(m_synchronizerPosition);
|
||||
m_silenceHandle = play(reader);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenALDevice::stopSynchronizer()
|
||||
{
|
||||
std::lock_guard<ILockable> 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<ILockable> lock(*this);
|
||||
|
||||
if(m_silenceHandle)
|
||||
return m_silenceHandle->getStatus() == STATUS_PLAYING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
12
extern/audaspace/plugins/openal/OpenALDevice.h
vendored
12
extern/audaspace/plugins/openal/OpenALDevice.h
vendored
@@ -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 <al.h>
|
||||
@@ -234,7 +233,8 @@ private:
|
||||
Quaternion m_orientation;
|
||||
|
||||
/// Synchronizer.
|
||||
DefaultSynchronizer m_synchronizer;
|
||||
uint64_t m_synchronizerPosition{0};
|
||||
std::shared_ptr<IHandle> 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);
|
||||
|
||||
111
extern/audaspace/plugins/pipewire/PipeWireDevice.cpp
vendored
111
extern/audaspace/plugins/pipewire/PipeWireDevice.cpp
vendored
@@ -19,7 +19,6 @@
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
|
||||
#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<IHandle> dummy_handle;
|
||||
m_seek_pos = getPosition(dummy_handle);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void PipeWireDevice::PipeWireSynchronizer::seek(std::shared_ptr<IHandle> 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<IHandle> 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
|
||||
|
||||
@@ -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<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> 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.
|
||||
*/
|
||||
|
||||
@@ -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<IHandle> dummy_handle;
|
||||
m_seek_pos = getPosition(dummy_handle);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void PulseAudioDevice::PulseAudioSynchronizer::seek(std::shared_ptr<IHandle> 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<IHandle> /*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
|
||||
|
||||
@@ -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<IHandle> handle, double time);
|
||||
virtual double getPosition(std::shared_ptr<IHandle> 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.
|
||||
|
||||
@@ -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<IHandle> handle, double time)
|
||||
{
|
||||
handle->seek(time);
|
||||
}
|
||||
|
||||
double DefaultSynchronizer::getPosition(std::shared_ptr<IHandle> 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
|
||||
25
extern/audaspace/src/devices/NULLDevice.cpp
vendored
25
extern/audaspace/src/devices/NULLDevice.cpp
vendored
@@ -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<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
void NULLDevice::playSynchronizer()
|
||||
{
|
||||
}
|
||||
|
||||
void NULLDevice::stopSynchronizer()
|
||||
{
|
||||
}
|
||||
|
||||
void NULLDevice::setSyncCallback(syncFunction function, void* data)
|
||||
{
|
||||
}
|
||||
|
||||
int NULLDevice::isSynchronizerPlaying()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
class NULLDeviceFactory : public IDeviceFactory
|
||||
|
||||
45
extern/audaspace/src/devices/SoftwareDevice.cpp
vendored
45
extern/audaspace/src/devices/SoftwareDevice.cpp
vendored
@@ -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<ILockable> 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<ILockable> lock(*this);
|
||||
|
||||
m_synchronizerState = 1;
|
||||
}
|
||||
|
||||
void SoftwareDevice::stopSynchronizer()
|
||||
{
|
||||
std::lock_guard<ILockable> lock(*this);
|
||||
|
||||
m_synchronizerState = 0;
|
||||
}
|
||||
|
||||
void SoftwareDevice::setSyncCallback(syncFunction function, void* data)
|
||||
{
|
||||
}
|
||||
|
||||
int SoftwareDevice::isSynchronizerPlaying()
|
||||
{
|
||||
return m_synchronizerState;
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user