diff --git a/CMakeLists.txt b/CMakeLists.txt index 751e4e9ec26d914bbc6af335ff777ddf4aa43e50..466b6eb0a083c62292d69ff24efbbdb89c37a8b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,10 @@ vista_use_package( Eigen REQUIRED ) # Optional packages vista_find_package( RavenNet QUIET ) +if( ITA_VA_CORE_LIBS_FFT_USES_FFTW3 ) + set( ITA_VA_COPYING_GNU_GPL ON PARENT_SCOPE ) +endif( ) + if( NOT DEFINED ITA_VACORE_VERSION_MAJOR ) set( ITA_VACORE_VERSION_MAJOR "v2018" CACHE STRING "VACore version major (usually 'v' and year)" ) endif( ) @@ -116,8 +120,8 @@ endif( ) if( NOT DEFINED ITA_VACORE_WITH_AUDIO_BACKEND_PORTAUDIO ) set( ITA_VACORE_WITH_AUDIO_BACKEND_PORTAUDIO ON CACHE BOOL "Build VACore with Portaudio audio backend support" ) endif( ) -if( NOT DEFINED ITA_VACORE_WITH_AUDIO_BACKEND_DUMMY ) - set( ITA_VACORE_WITH_AUDIO_BACKEND_DUMMY OFF CACHE BOOL "Build VACore with dummy audio backend support" ) +if( NOT DEFINED ITA_VACORE_WITH_AUDIO_BACKEND_VIRTUAL ) + set( ITA_VACORE_WITH_AUDIO_BACKEND_VIRTUAL OFF CACHE BOOL "Build VACore with virtual audio backend support (for offline simulations)" ) endif( ) @@ -254,8 +258,8 @@ endif( ) if( ITA_VACORE_WITH_AUDIO_BACKEND_PORTAUDIO ) add_definitions( -DVACORE_WITH_AUDIO_BACKEND_PORTAUDIO ) endif( ) -if( ITA_VACORE_WITH_AUDIO_BACKEND_DUMMY ) - add_definitions( -DVACORE_WITH_AUDIO_BACKEND_DUMMY ) +if( ITA_VACORE_WITH_AUDIO_BACKEND_VIRTUAL ) + add_definitions( -DVACORE_WITH_AUDIO_BACKEND_VIRTUAL ) endif( ) # Rendering definitions diff --git a/src/Audiosignals/VAAudioSignalSourceManager.cpp b/src/Audiosignals/VAAudioSignalSourceManager.cpp index 7c78bac326cad6dab3cb0c2df54528001a99ead0..4726258b69c9fd99602352ac549160ad8d60e304 100644 --- a/src/Audiosignals/VAAudioSignalSourceManager.cpp +++ b/src/Audiosignals/VAAudioSignalSourceManager.cpp @@ -36,9 +36,7 @@ #include -CVAAudioSignalSourceManager::CVAAudioSignalSourceManager( CVACoreImpl* pParentCore, - const CVAAudioDriverConfig& oAudioDriverConfig, - ITADatasource* pDeviceInputSource ) +CVAAudioSignalSourceManager::CVAAudioSignalSourceManager( CVACoreImpl* pParentCore, const CVAAudioDriverConfig& oAudioDriverConfig, ITADatasource* pDeviceInputSource ) : m_pParentCore( pParentCore ), m_dSamplerate( oAudioDriverConfig.dSampleRate ), m_iBlocklength( oAudioDriverConfig.iBuffersize ), diff --git a/src/Drivers/Audio/VAAudioDriverConfig.cpp b/src/Drivers/Audio/VAAudioDriverConfig.cpp index 7061d6ba87bd813bf9dc89427323b361310d1155..40cf94e1f28f54aaf9feb8edad08b1c429264ceb 100644 --- a/src/Drivers/Audio/VAAudioDriverConfig.cpp +++ b/src/Drivers/Audio/VAAudioDriverConfig.cpp @@ -35,8 +35,8 @@ void CVAAudioDriverConfig::Init( const CVAStruct& oArgs ) CVAConfigInterpreter conf( oArgs ); conf.OptNonEmptyString( "Driver", sDriver ); conf.OptString( "Device", sDevice, "AUTO" ); - conf.OptNumber( "Samplerate", dSampleRate, DEFAULT_SAMPLERATE ); - conf.OptInteger( "Buffersize", iBuffersize, AUTO, &lits ); + conf.OptNumber( "SampleRate", dSampleRate, DEFAULT_SAMPLERATE ); + conf.OptInteger( "BufferSize", iBuffersize, AUTO, &lits ); conf.OptInteger( "InputChannels", iInputChannels, AUTO, &lits ); conf.OptInteger( "OutputChannels", iOutputChannels, AUTO, &lits ); @@ -48,9 +48,8 @@ void CVAAudioDriverConfig::Init( const CVAStruct& oArgs ) VA_EXCEPT1( "Invalid buffersize specified" ); if( ( iInputChannels < 0 ) && ( iInputChannels != AUTO ) ) - VA_EXCEPT1( "Invalid number of input channels specified" ); + VA_EXCEPT1( "Invalid number of input channels specified (autodetect = -1 or AUTO)" ); - // @todo: really?! I don't see a point why single channel output shouldn't be supported. - if( ( iOutputChannels < 2 ) && ( iOutputChannels != AUTO ) ) - VA_EXCEPT1( "Invalid number of output channels specified (at least two)" ); + if( ( iOutputChannels < 1 ) && ( iOutputChannels != AUTO ) ) + VA_EXCEPT1( "Invalid number of output channels specified (at least one required, if not using autodetect ... -1 or AUTO)" ); } diff --git a/src/Drivers/Audio/VADummyAudioDriverBackend.cpp b/src/Drivers/Audio/VADummyAudioDriverBackend.cpp deleted file mode 100644 index 7686fd71f589cceb64ebc3525044bab813ebd3a1..0000000000000000000000000000000000000000 --- a/src/Drivers/Audio/VADummyAudioDriverBackend.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "VADummyAudioDriverBackend.h" -#include "../../Utils/VADebug.h" - -#include -#include -#include -#include - -#include - -CVADummyAudioDriverBackend::CVADummyAudioDriverBackend( const CVAAudioDriverConfig* pConfig ) - : CVAObject( "VirtualAudioDriver" ) - , m_oConfig( *pConfig ) - , m_pDataSource( NULL ) - , m_bStarted( false ) -{ - m_oOutputStreamProps.dSamplerate = m_oConfig.dSampleRate; - m_oOutputStreamProps.uiChannels = ( unsigned int ) m_oConfig.iOutputChannels; - m_oOutputStreamProps.uiBlocklength = ( unsigned int ) m_oConfig.iBuffersize; -} - -CVADummyAudioDriverBackend::~CVADummyAudioDriverBackend() -{ - -} - -std::string CVADummyAudioDriverBackend::getDriverName() const -{ - return "Dummy"; -} - -std::string CVADummyAudioDriverBackend::getDeviceName() const -{ - return "Dummy"; -} - -int CVADummyAudioDriverBackend::getNumberOfInputs() const -{ - return m_oConfig.iInputChannels; -} - -const ITAStreamProperties* CVADummyAudioDriverBackend::getOutputStreamProperties() const -{ - return &m_oOutputStreamProps; -} - -void CVADummyAudioDriverBackend::setOutputStreamDatasource( ITADatasource* pDatasource ) -{ - m_pDataSource = pDatasource; -} - -ITADatasource* CVADummyAudioDriverBackend::getInputStreamDatasource() const -{ - return nullptr; -} - -void CVADummyAudioDriverBackend::initialize() -{ - -} - -void CVADummyAudioDriverBackend::finalize() -{ - -} - -void CVADummyAudioDriverBackend::startStreaming() -{ - m_bStarted = true; -} - -bool CVADummyAudioDriverBackend::isStreaming() -{ - return m_bStarted; -} - -void CVADummyAudioDriverBackend::stopStreaming() -{ - m_bStarted = false; -} - -CVAStruct CVADummyAudioDriverBackend::CallObject( const CVAStruct& oArgs ) -{ - CVAStruct oReturn; - - if( oArgs.HasKey( "trigger" ) && m_pDataSource ) - m_pDataSource->IncrementBlockPointer(); - - if( oArgs.HasKey( "help" ) || oArgs.HasKey( "info" ) ) - { - oReturn[ "usage" ] = "Call this virtual audio device with key 'trigger' to trigger another audio block increment"; - } - - return oReturn; -} diff --git a/src/Drivers/Audio/VAVirtualAudioDriverBackend.cpp b/src/Drivers/Audio/VAVirtualAudioDriverBackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a89aeb21ff27c55ef5b30ea1957efca9ded9d2e --- /dev/null +++ b/src/Drivers/Audio/VAVirtualAudioDriverBackend.cpp @@ -0,0 +1,146 @@ +#include "VAVirtualAudioDriverBackend.h" +#include "../../Utils/VADebug.h" +#include + +#include +#include +#include +#include + +#include + +CVAVirtualAudioDriverBackend::CVAVirtualAudioDriverBackend( const CVAAudioDriverConfig* pConfig ) + : CVAObject( "VirtualAudioDevice" ) + , m_oConfig( *pConfig ) + , m_pDataSource( NULL ) + , m_bStarted( false ) +{ + m_oOutputStreamProps.dSamplerate = m_oConfig.dSampleRate; + m_oOutputStreamProps.uiChannels = ( unsigned int ) m_oConfig.iOutputChannels; + m_oOutputStreamProps.uiBlocklength = ( unsigned int ) m_oConfig.iBuffersize; +} + +CVAVirtualAudioDriverBackend::~CVAVirtualAudioDriverBackend() +{ + +} + +std::string CVAVirtualAudioDriverBackend::getDriverName() const +{ + return "Virtual"; +} + +std::string CVAVirtualAudioDriverBackend::getDeviceName() const +{ + return "Trigger"; +} + +int CVAVirtualAudioDriverBackend::getNumberOfInputs() const +{ + return m_oConfig.iInputChannels; +} + +const ITAStreamProperties* CVAVirtualAudioDriverBackend::getOutputStreamProperties() const +{ + return &m_oOutputStreamProps; +} + +void CVAVirtualAudioDriverBackend::setOutputStreamDatasource( ITADatasource* pDatasource ) +{ + m_pDataSource = pDatasource; +} + +ITADatasource* CVAVirtualAudioDriverBackend::getInputStreamDatasource() const +{ + return nullptr; +} + +void CVAVirtualAudioDriverBackend::initialize() +{ + +} + +void CVAVirtualAudioDriverBackend::finalize() +{ + +} + +void CVAVirtualAudioDriverBackend::startStreaming() +{ + m_bStarted = true; +} + +bool CVAVirtualAudioDriverBackend::isStreaming() +{ + return m_bStarted; +} + +void CVAVirtualAudioDriverBackend::stopStreaming() +{ + m_bStarted = false; +} + +CVAStruct CVAVirtualAudioDriverBackend::CallObject( const CVAStruct& oArgs ) +{ + CVAStruct oReturn; + + if( oArgs.HasKey( "trigger" ) && m_pDataSource ) + { + // Trigger block pointer getter, then increment + for( int n = 0; n < (int) m_pDataSource->GetNumberOfChannels(); n++ ) + m_pDataSource->GetBlockPointer( n, &m_oStreamInfo ); + m_pDataSource->IncrementBlockPointer(); + } + + if( oArgs.HasKey( "help" ) || oArgs.HasKey( "info" ) ) + { + oReturn[ "usage" ] = "Call this virtual audio device with key 'trigger' to trigger another audio block increment"; + } + + return oReturn; +} + +CVAVirtualAudioDriverBackend::ManualClock::ManualClock() + : CVAObject( "ManualClock" ) + , m_dTime( 0.0f ) +{ +} + +CVAVirtualAudioDriverBackend::ManualClock::~ManualClock() +{ +} + +double CVAVirtualAudioDriverBackend::ManualClock::getTime() +{ + m_csTime.enter(); + double dTime = m_dTime; + m_csTime.leave(); + return dTime; +} + +void CVAVirtualAudioDriverBackend::ManualClock::SetTime( double dNow ) +{ + m_csTime.enter(); + assert( m_dTime < dNow ); + m_dTime = dNow; + m_csTime.leave(); +} + +CVAStruct CVAVirtualAudioDriverBackend::ManualClock::CallObject( const CVAStruct& oArgs ) +{ + if( oArgs.HasKey( "info" ) || oArgs.HasKey( "help" ) ) + { + CVAStruct oRet; + oRet[ "usage" ] = "Set the manual clock with the key 'time' and a floating point value. Time has to be strict monotonously increasing"; + return oRet; + } + else if( oArgs.HasKey( "time" ) ) + { + SetTime( oArgs[ "time" ] ); + return CVAStruct(); + } + else + { + VA_EXCEPT2( INVALID_PARAMETER, "Could npt understand call, 'time' key missing. use 'help' for more information" ); + } +} diff --git a/src/Drivers/Audio/VADummyAudioDriverBackend.h b/src/Drivers/Audio/VAVirtualAudioDriverBackend.h similarity index 60% rename from src/Drivers/Audio/VADummyAudioDriverBackend.h rename to src/Drivers/Audio/VAVirtualAudioDriverBackend.h index 3c77b2abf066342d1c1722d06c24319bfc2835f3..1293d12bc11566e238b950a608685dfa699905e9 100644 --- a/src/Drivers/Audio/VADummyAudioDriverBackend.h +++ b/src/Drivers/Audio/VAVirtualAudioDriverBackend.h @@ -11,8 +11,8 @@ * -------------------------------------------------------------------------------------------- */ -#ifndef IW_VACORE_DUMMY_AUDIO_DRIVER_BACKEND -#define IW_VACORE_DUMMY_AUDIO_DRIVER_BACKEND +#ifndef IW_VACORE_VIRTUAL_AUDIO_DRIVER_BACKEND +#define IW_VACORE_VIRTUAL_AUDIO_DRIVER_BACKEND #include "VAAudioDriverBackend.h" #include "VAAudioDriverConfig.h" @@ -21,14 +21,17 @@ #include #include +#include +#include +#include #pragma warning( disable : 4512 ) // yep no copy constructor -class CVADummyAudioDriverBackend : public IVAAudioDriverBackend, public CVAObject +class CVAVirtualAudioDriverBackend : public IVAAudioDriverBackend, public CVAObject { public: - CVADummyAudioDriverBackend( const CVAAudioDriverConfig* pConfig ); - ~CVADummyAudioDriverBackend(); + CVAVirtualAudioDriverBackend( const CVAAudioDriverConfig* pConfig ); + ~CVAVirtualAudioDriverBackend(); std::string getDriverName() const; std::string getDeviceName() const; @@ -45,11 +48,28 @@ public: CVAStruct CallObject( const CVAStruct& oArgs ); + class ManualClock : public ITAClock, public CVAObject + { + public: + ManualClock(); + ~ManualClock(); + inline std::string getName() const { return "ManualClock"; }; + inline double getResolution() const { return -1.0f; }; + double getFrequency() const { return -1.0f; }; + inline double getTime(); + inline void SetTime( const double dManualNow ); + CVAStruct CallObject( const CVAStruct& oArgs ); + private: + ITACriticalSection m_csTime; + double m_dTime; + }; + private: const CVAAudioDriverConfig m_oConfig; ITAStreamProperties m_oOutputStreamProps; + ITAStreamInfo m_oStreamInfo; ITADatasource* m_pDataSource; bool m_bStarted; }; -#endif // IW_VACORE_DUMMY_AUDIO_DRIVER_BACKEND +#endif // IW_VACORE_VIRTUAL_AUDIO_DRIVER_BACKEND diff --git a/src/Drivers/Audio/_SourceFiles.cmake b/src/Drivers/Audio/_SourceFiles.cmake index b999257d395536555fc5ab4483a5f3f748290303..660e25611b4872c41a953cf299f98ac05d62e6b2 100644 --- a/src/Drivers/Audio/_SourceFiles.cmake +++ b/src/Drivers/Audio/_SourceFiles.cmake @@ -17,8 +17,8 @@ endif( ) if( ITA_VACORE_WITH_AUDIO_BACKEND_PORTAUDIO ) list( APPEND DirFiles VAPortaudioBackend.cpp VAPortaudioBackend.h ) endif( ) -if( ITA_VACORE_WITH_AUDIO_BACKEND_DUMMY ) - list( APPEND DirFiles VADummyAudioDriverBackend.cpp VADummyAudioDriverBackend.h ) +if( ITA_VACORE_WITH_AUDIO_BACKEND_VIRTUAL ) + list( APPEND DirFiles VAVirtualAudioDriverBackend.cpp VAVirtualAudioDriverBackend.h ) endif( ) set( DirFiles_SourceGroup "${RelativeSourceGroup}" ) diff --git a/src/Rendering/Ambisonics/Freefield/VAAmbisonicsFreefieldAudioRenderer.cpp b/src/Rendering/Ambisonics/Freefield/VAAmbisonicsFreefieldAudioRenderer.cpp index d3e4614a82b28e8e1b00cde1741f77a575a46442..920a00ec7845f68f8007258a5a106fb9b9617c20 100644 --- a/src/Rendering/Ambisonics/Freefield/VAAmbisonicsFreefieldAudioRenderer.cpp +++ b/src/Rendering/Ambisonics/Freefield/VAAmbisonicsFreefieldAudioRenderer.cpp @@ -334,7 +334,7 @@ void CVAAmbisonicsFreeFieldAudioRenderer::Reset() VA_VERBOSE( "AmbisonicsFreeFieldAudioRenderer", "Received reset call, indicating reset now" ); ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { VA_VERBOSE( "AmbisonicsFreeFieldAudioRenderer", "Was not streaming, will reset manually" ); // if no streaming active, reset manually diff --git a/src/Rendering/Binaural/AirTrafficNoise/VAAirTrafficNoiseAudioRenderer.cpp b/src/Rendering/Binaural/AirTrafficNoise/VAAirTrafficNoiseAudioRenderer.cpp index d3b0c5c78e2c3bb4af13008572e41bc20b4aa719..6a85e0d0a2e0f0c1d2d05e55eedb384242a4a1ee 100644 --- a/src/Rendering/Binaural/AirTrafficNoise/VAAirTrafficNoiseAudioRenderer.cpp +++ b/src/Rendering/Binaural/AirTrafficNoise/VAAirTrafficNoiseAudioRenderer.cpp @@ -410,7 +410,7 @@ void CVABinauralAirTrafficNoiseAudioRenderer::Reset() { ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { // if no streaming active, reset manually //SyncInternalData(); diff --git a/src/Rendering/Binaural/ArtificialReverb/VABinauralArtificialReverb.cpp b/src/Rendering/Binaural/ArtificialReverb/VABinauralArtificialReverb.cpp index 7fee8eace6c22a4f10fc4e398f65c2957c201f60..5e4278cbd017dfff3d78c6be5a7b4574ecb7d2de 100644 --- a/src/Rendering/Binaural/ArtificialReverb/VABinauralArtificialReverb.cpp +++ b/src/Rendering/Binaural/ArtificialReverb/VABinauralArtificialReverb.cpp @@ -491,7 +491,7 @@ void CVABinauralArtificialReverbAudioRenderer::Reset() { ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { // if no streaming active, reset manually ResetInternalData(); diff --git a/src/Rendering/Binaural/FreeField/VABinauralFreeFieldAudioRenderer.cpp b/src/Rendering/Binaural/FreeField/VABinauralFreeFieldAudioRenderer.cpp index e9e2684a8b1776f977d4adc489e91eade4aaa22a..ab45d88720d3cafbb31815b3c2798f6b21a1f244 100644 --- a/src/Rendering/Binaural/FreeField/VABinauralFreeFieldAudioRenderer.cpp +++ b/src/Rendering/Binaural/FreeField/VABinauralFreeFieldAudioRenderer.cpp @@ -188,19 +188,20 @@ private: friend class CVABFFSoundPathFactory; }; -class CVABFFSoundPathFactory : public IVAPoolObjectFactory { +class CVABFFSoundPathFactory : public IVAPoolObjectFactory +{ public: CVABFFSoundPathFactory( double dSamplerate, int iBlocklength, int iHRIRFilterLength, int iDirFilterLength ) : m_dSamplerate( dSamplerate ) , m_iBlocklength( iBlocklength ) , m_iHRIRFilterLength( iHRIRFilterLength ) , m_iDirFilterLength( iDirFilterLength ) - {} + {}; CVAPoolObject* CreatePoolObject() { return new CVABFFSoundPath( m_dSamplerate, m_iBlocklength, m_iHRIRFilterLength, m_iDirFilterLength ); - } + }; private: double m_dSamplerate; //!< Abtastrate @@ -362,7 +363,7 @@ void CVABinauralFreeFieldAudioRenderer::Reset() VA_VERBOSE( "BinauralFreeFieldAudioRenderer", "Received reset call, indicating reset now" ); ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { VA_VERBOSE( "BinauralFreeFieldAudioRenderer", "Was not streaming, will reset manually" ); // if no streaming active, reset manually @@ -1391,6 +1392,17 @@ CVAStruct CVABinauralFreeFieldAudioRenderer::CallObject( const CVAStruct& oArgs VA_EXCEPT2( INVALID_PARAMETER, "Invalid command (\"" + sCommandOrg + "\")" ); } +void CVABinauralFreeFieldAudioRenderer::SetParameters( const CVAStruct& oParams ) +{ + // Only delegate + CallObject( oParams ); +} + +CVAStruct CVABinauralFreeFieldAudioRenderer::GetParameters( const CVAStruct& ) +{ + return CVAStruct( *m_oParams.pConfig ); +} + void CVABinauralFreeFieldAudioRenderer::onStartDumpListeners( const std::string& sFilenameFormat ) { if( m_bDumpListeners ) diff --git a/src/Rendering/Binaural/FreeField/VABinauralFreefieldAudioRenderer.h b/src/Rendering/Binaural/FreeField/VABinauralFreefieldAudioRenderer.h index ddd65e75292e1df602d83f4a03bce8c737d8ed5d..5e45e299c952ce5bb7595216629a7de591db6637 100644 --- a/src/Rendering/Binaural/FreeField/VABinauralFreefieldAudioRenderer.h +++ b/src/Rendering/Binaural/FreeField/VABinauralFreefieldAudioRenderer.h @@ -118,6 +118,8 @@ public: // --= Module interface =-- CVAStruct CallObject( const CVAStruct& oArgs ); + void SetParameters( const CVAStruct& oParams ); + CVAStruct GetParameters( const CVAStruct& ); void onStartDumpListeners( const std::string& sFilenameFormat ); void onStopDumpListeners(); diff --git a/src/Rendering/Prototyping/FreeField/VAPrototypeFreeFieldAudioRenderer.cpp b/src/Rendering/Prototyping/FreeField/VAPrototypeFreeFieldAudioRenderer.cpp index 8e6b6c3ccf8b9ffc87cc8c5d413d95e31c6fd90c..1d2160e7ab625420b9b60777675f3f78d5dc72ae 100644 --- a/src/Rendering/Prototyping/FreeField/VAPrototypeFreeFieldAudioRenderer.cpp +++ b/src/Rendering/Prototyping/FreeField/VAPrototypeFreeFieldAudioRenderer.cpp @@ -370,7 +370,7 @@ void CVAPrototypeFreeFieldAudioRenderer::Reset() VA_VERBOSE( "PrototypeFreeFieldAudioRenderer", "Received reset call, indicating reset now" ); ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { VA_VERBOSE( "PrototypeFreeFieldAudioRenderer", "Was not streaming, will reset manually" ); // if no streaming active, reset manually diff --git a/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.cpp b/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.cpp index 612869bad571d6051cbe5acf99e07b5aaab7a99f..f14d3092bd2f013f32ae7dafcf12634d4b3b170d 100644 --- a/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.cpp +++ b/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // Vista includes #include @@ -50,25 +51,30 @@ public: CVAPTGenericPathAudioRenderer::CVAPTGPListener* pListener; ITAAtomicBool bDelete; + CITAVariableDelayLine* pVariableDelayLine; std::vector< ITAUPConvolution* > vpFIRConvolver; // N-channel convolver + inline void PreRequest() - { + { pSource = nullptr; pListener = nullptr; - for( size_t n=0; n < vpFIRConvolver.size(); n++ ) - vpFIRConvolver[n]->clear(); + + pVariableDelayLine->Clear(); + + for( size_t n = 0; n < vpFIRConvolver.size(); n++ ) + vpFIRConvolver[ n ]->clear(); }; private: CVAPTGenericSoundPath(); CVAPTGenericSoundPath( double dSamplerate, int iBlocklength, int iNumChannels, int iIRFilterLength ); - + friend class CVAPTGenericSoundPathFactory; }; class CVAPTGenericSoundPathFactory : public IVAPoolObjectFactory { -public: +public: inline CVAPTGenericSoundPathFactory( double dSampleRate, int iBlockLength, int iNumChannels, int iIRFilterLength ) : m_dSampleRate( dSampleRate ) , m_iBlockLength( iBlockLength ) @@ -92,7 +98,7 @@ class CVAPTGPListenerPoolFactory : public IVAPoolObjectFactory { public: inline CVAPTGPListenerPoolFactory( CVACoreImpl* pCore ) - : m_pCore( pCore ) + : m_pCore( pCore ) { }; @@ -131,40 +137,45 @@ CVAPTGenericSoundPath::CVAPTGenericSoundPath( double dSamplerate, int iBlockleng if( iNumChannels < 1 ) ITA_EXCEPT1( INVALID_PARAMETER, "Number of channels must be positive" ); - for( int n=0; nSetFilterExchangeMode( ITAUPConvolution::CROSSFADE_COSINE_SQUARE ); - pFIRConvolver->SetFilterCrossfadeLength( (std::min)( iBlocklength, 32 ) ); + pFIRConvolver->SetFilterCrossfadeLength( ( std::min )( iBlocklength, 32 ) ); pFIRConvolver->SetGain( 1.0f, true ); ITAUPFilter* pHRIRFilterChL = pFIRConvolver->RequestFilter(); pHRIRFilterChL->Zeros(); pFIRConvolver->ExchangeFilter( pHRIRFilterChL ); pFIRConvolver->ReleaseFilter( pHRIRFilterChL ); - vpFIRConvolver.push_back( pFIRConvolver ); - } + vpFIRConvolver.push_back( pFIRConvolver ); + } } CVAPTGenericSoundPath::~CVAPTGenericSoundPath() { - for( size_t n=0; nGetCoreConfig()->oAudioDriverConfig.dSampleRate, oParams.pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize ); m_pOutput->SetStreamEventHandler( this ); @@ -174,7 +185,7 @@ CVAPTGenericPathAudioRenderer::CVAPTGenericPathAudioRenderer( const CVAAudioRend IVAPoolObjectFactory* pSourceFactory = new CVAPTGPSourcePoolFactory(); m_pSourcePool = IVAObjectPool::Create( 16, 2, pSourceFactory, true ); - m_pSoundPathFactory = new CVAPTGenericSoundPathFactory( m_pOutput->GetSampleRate(), m_pOutput->GetBlocklength(), m_iNumChannels, m_iIRFilterLengthSamples ); + m_pSoundPathFactory = new CVAPTGenericSoundPathFactory( m_pOutput->GetSampleRate(), m_pOutput->GetBlocklength(), m_iNumChannels, m_iIRFilterLengthSamples ); m_pSoundPathPool = IVAObjectPool::Create( 64, 8, m_pSoundPathFactory, true ); @@ -184,9 +195,11 @@ CVAPTGenericPathAudioRenderer::CVAPTGenericPathAudioRenderer( const CVAAudioRend ctxAudio.m_iStatus = 0; // Stopped m_iCurGlobalAuralizationMode = IVAInterface::VA_AURAMODE_DEFAULT; + + m_sfTempBuffer.Init( oParams.pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize, true ); } -CVAPTGenericPathAudioRenderer::~CVAPTGenericPathAudioRenderer() +CVAPTGenericPathAudioRenderer::~CVAPTGenericPathAudioRenderer() { delete m_pSoundPathPool; delete m_pUpdateMessagePool; @@ -200,25 +213,19 @@ void CVAPTGenericPathAudioRenderer::Init( const CVAStruct& oArgs ) conf.OptInteger( "IRFilterLengthSamples", m_iIRFilterLengthSamples, 1024 ); - conf.OptInteger( "FilterDelaySamples", m_iFilterDelaySamples, 0 ); - - if( m_iIRFilterLengthSamples < 0 ) ITA_EXCEPT1( INVALID_PARAMETER, "IR filter size must be positive" ); - if( m_iFilterDelaySamples >= m_iIRFilterLengthSamples ) - ITA_EXCEPT1( INVALID_PARAMETER, "IR filter delay samples must be smaller than IR filter length" ); - conf.OptBool( "OutputMonitoring", m_bOutputMonitoring, false ); return; } -void CVAPTGenericPathAudioRenderer::Reset() +void CVAPTGenericPathAudioRenderer::Reset() { ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { // if no streaming active, reset manually //SyncInternalData(); @@ -230,13 +237,13 @@ void CVAPTGenericPathAudioRenderer::Reset() { VASleep( 100 ); // Wait for acknowledge } - + // Iterate over sound pathes and free items std::list< CVAPTGenericSoundPath* >::iterator it = m_lSoundPaths.begin(); while( it != m_lSoundPaths.end() ) { CVAPTGenericSoundPath* pPath = *it; - + int iNumRefs = pPath->GetNumReferences(); assert( iNumRefs == 1 ); pPath->RemoveReference(); @@ -316,12 +323,12 @@ ITADatasource* CVAPTGenericPathAudioRenderer::GetOutputDatasource() } void CVAPTGenericPathAudioRenderer::ManageSoundPaths( const CVASceneState* pCurScene, - const CVASceneState* pNewScene, - const CVASceneStateDiff* pDiff ) + const CVASceneState* pNewScene, + const CVASceneStateDiff* pDiff ) { // Iterate over current paths and mark deleted (will be removed within internal sync of audio context thread) std::list< CVAPTGenericSoundPath* >::iterator itp = m_lSoundPaths.begin(); - while( itp != m_lSoundPaths.end() ) + while( itp != m_lSoundPaths.end() ) { CVAPTGenericSoundPath* pPath( *itp ); int iSourceID = pPath->pSource->pData->iID; @@ -403,12 +410,12 @@ void CVAPTGenericPathAudioRenderer::ManageSoundPaths( const CVASceneState* pCurS while( citr != pDiff->viNewReceiverIDs.end() ) { int iListenerID = ( *citr++ ); - CVAPTGPListener* pListener = m_mListeners[iListenerID]; + CVAPTGPListener* pListener = m_mListeners[ iListenerID ]; - for( size_t i=0; iviComSoundSourceIDs.size(); i++ ) + for( size_t i = 0; i < pDiff->viComSoundSourceIDs.size(); i++ ) { - int iSourceID = pDiff->viComSoundSourceIDs[i]; - CVAPTGPSource* pSource = m_mSources[iSourceID]; + int iSourceID = pDiff->viComSoundSourceIDs[ i ]; + CVAPTGPSource* pSource = m_mSources[ iSourceID ]; if( !pSource->bDeleted ) // only, if not marked for deletion CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener ); } @@ -416,15 +423,15 @@ void CVAPTGenericPathAudioRenderer::ManageSoundPaths( const CVASceneState* pCurS // New paths: (2) new sources, current receivers cits = pDiff->viNewSoundSourceIDs.begin(); - while( cits != pDiff->viNewSoundSourceIDs.end() ) + while( cits != pDiff->viNewSoundSourceIDs.end() ) { const int& iSourceID( *cits++ ); - CVAPTGPSource* pSource = m_mSources[iSourceID]; + CVAPTGPSource* pSource = m_mSources[ iSourceID ]; - for( size_t i=0; iviComReceiverIDs.size(); i++ ) + for( size_t i = 0; i < pDiff->viComReceiverIDs.size(); i++ ) { - int iListenerID = pDiff->viComReceiverIDs[i]; - CVAPTGPListener* pListener = m_mListeners[iListenerID]; + int iListenerID = pDiff->viComReceiverIDs[ i ]; + CVAPTGPListener* pListener = m_mListeners[ iListenerID ]; if( !pListener->bDeleted ) CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener ); } @@ -435,31 +442,31 @@ void CVAPTGenericPathAudioRenderer::ManageSoundPaths( const CVASceneState* pCurS while( cits != pDiff->viNewSoundSourceIDs.end() ) { const int& iSourceID( *cits++ ); - CVAPTGPSource* pSource = m_mSources[iSourceID]; + CVAPTGPSource* pSource = m_mSources[ iSourceID ]; citr = pDiff->viNewReceiverIDs.begin(); while( citr != pDiff->viNewReceiverIDs.end() ) { const int& iListenerID( *citr++ ); - CVAPTGPListener* pListener = m_mListeners[iListenerID]; + CVAPTGPListener* pListener = m_mListeners[ iListenerID ]; CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener ); } } - + return; } -CVAPTGenericSoundPath* CVAPTGenericPathAudioRenderer::CreateSoundPath( CVAPTGPSource* pSource, CVAPTGPListener* pListener ) +CVAPTGenericSoundPath* CVAPTGenericPathAudioRenderer::CreateSoundPath( CVAPTGPSource* pSource, CVAPTGPListener* pListener ) { int iSourceID = pSource->pData->iID; int iListenerID = pListener->pData->iID; assert( !pSource->bDeleted && !pListener->bDeleted ); - VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating sound path from source " << iSourceID << " -> sound receiver " << iListenerID); + VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating sound path from source " << iSourceID << " -> sound receiver " << iListenerID ); + + CVAPTGenericSoundPath* pPath = dynamic_cast< CVAPTGenericSoundPath* >( m_pSoundPathPool->RequestObject() ); - CVAPTGenericSoundPath* pPath = dynamic_cast( m_pSoundPathPool->RequestObject() ); - pPath->pSource = pSource; pPath->pListener = pListener; @@ -473,7 +480,7 @@ CVAPTGenericSoundPath* CVAPTGenericPathAudioRenderer::CreateSoundPath( CVAPTGPSo void CVAPTGenericPathAudioRenderer::DeleteSoundPath( CVAPTGenericSoundPath* pPath ) { - VA_VERBOSE( "PTGenericPathAudioRenderer", "Marking sound path from source " << pPath->pSource->pData->iID << " -> sound receiver " << pPath->pListener->pData->iID << " for deletion"); + VA_VERBOSE( "PTGenericPathAudioRenderer", "Marking sound path from source " << pPath->pSource->pData->iID << " -> sound receiver " << pPath->pListener->pData->iID << " for deletion" ); pPath->bDelete = true; pPath->RemoveReference(); @@ -491,8 +498,8 @@ CVAPTGenericPathAudioRenderer::CVAPTGPListener* CVAPTGenericPathAudioRenderer::C // Move to prerequest of pool? pListener->psfOutput = new ITASampleFrame( m_iNumChannels, - m_pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize, - true ); + m_pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize, + true ); assert( pListener->pData ); pListener->bDeleted = false; @@ -522,8 +529,8 @@ CVAPTGenericPathAudioRenderer::CVAPTGPSource* CVAPTGenericPathAudioRenderer::Cre { VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating source with ID " << iID ); CVAPTGPSource* pSource = dynamic_cast< CVAPTGPSource* >( m_pSourcePool->RequestObject() ); - - pSource->pData = m_pCore->GetSceneManager()->GetSoundSourceDesc(iID); + + pSource->pData = m_pCore->GetSceneManager()->GetSoundSourceDesc( iID ); pSource->pData->AddReference(); pSource->bDeleted = false; @@ -546,7 +553,7 @@ void CVAPTGenericPathAudioRenderer::DeleteSource( int iSourceID ) pSource->RemoveReference(); m_pUpdateMessage->vDelSources.push_back( pSource ); - + return; } @@ -616,15 +623,15 @@ void CVAPTGenericPathAudioRenderer::SyncInternalData() void CVAPTGenericPathAudioRenderer::ResetInternalData() { std::list< CVAPTGenericSoundPath* >::const_iterator citp = ctxAudio.m_lSoundPaths.begin(); - while( citp != ctxAudio.m_lSoundPaths.end() ) + while( citp != ctxAudio.m_lSoundPaths.end() ) { CVAPTGenericSoundPath* pPath( *citp++ ); pPath->RemoveReference(); } ctxAudio.m_lSoundPaths.clear(); - std::list< CVAPTGPListener* >::const_iterator itl=ctxAudio.m_lListener.begin(); - while( itl !=ctxAudio.m_lListener.end() ) + std::list< CVAPTGPListener* >::const_iterator itl = ctxAudio.m_lListener.begin(); + while( itl != ctxAudio.m_lListener.end() ) { CVAPTGPListener* pListener( *itl++ ); pListener->pData->RemoveReference(); @@ -676,29 +683,44 @@ void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int } } +void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( const int iListenerID, const int iSourceID, const double dDelaySeconds ) +{ + if( dDelaySeconds < 0.0f ) + VA_WARN( "PTGenericPathAudioRenderer", "Delay in variable delay line can not be anti-causal. Shift IR if necessary." ); + + std::list< CVAPTGenericSoundPath* >::const_iterator spcit = m_lSoundPaths.begin(); + while( spcit != m_lSoundPaths.end() ) + { + CVAPTGenericSoundPath* pPath( *spcit++ ); + if( pPath->pListener->pData->iID == iListenerID && pPath->pSource->pData->iID == iSourceID ) + { + pPath->pVariableDelayLine->SetDelayTime( dDelaySeconds ); + return; + } + } + + VA_ERROR( "PTGenericPathAudioRenderer", "Could not find sound path for sound receiver " << iListenerID << " and source " << iSourceID ); +} void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, ITASampleFrame& sfIR ) { if( sfIR.length() > m_iIRFilterLengthSamples ) VA_WARN( "PTGenericPathAudioRenderer", "Filter length for generic sound path too long, cropping." ); - if( m_iFilterDelaySamples != 0 ) - sfIR.CyclicShift( m_iFilterDelaySamples ); - std::list< CVAPTGenericSoundPath* >::const_iterator spcit = m_lSoundPaths.begin(); while( spcit != m_lSoundPaths.end() ) { CVAPTGenericSoundPath* pPath( *spcit++ ); if( pPath->pListener->pData->iID == iListenerID && pPath->pSource->pData->iID == iSourceID ) { - for( int n=0; nvpFIRConvolver[n] ); + ITAUPConvolution* pConvolver( pPath->vpFIRConvolver[ n ] ); ITAUPFilter* pFilter = pConvolver->RequestFilter(); pFilter->Zeros(); - int iLength = (std::min)( m_iIRFilterLengthSamples, sfIR.length() ); - pFilter->Load( sfIR[n].data(), iLength ); - + int iLength = ( std::min )( m_iIRFilterLengthSamples, sfIR.length() ); + pFilter->Load( sfIR[ n ].data(), iLength ); + pConvolver->ExchangeFilter( pFilter ); pFilter->Release(); } @@ -712,14 +734,11 @@ void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, int iChannelIndex, ITASampleBuffer& sbIR ) { - if( iChannelIndex >= m_iNumChannels || iChannelIndex < 0 ) + if( iChannelIndex >= m_iNumChannels || iChannelIndex < 0 ) VA_EXCEPT2( INVALID_PARAMETER, "Requested filter channel index of generic sound path out of bounds" ); if( sbIR.length() > m_iIRFilterLengthSamples ) VA_WARN( "PTGenericPathAudioRenderer", "Filter length for generic sound path channel too long, cropping." ); - - if( m_iFilterDelaySamples != 0 ) - sbIR.CyclicShift( m_iFilterDelaySamples ); std::list< CVAPTGenericSoundPath* >::const_iterator spcit = m_lSoundPaths.begin(); while( spcit != m_lSoundPaths.end() ) @@ -727,10 +746,10 @@ void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int CVAPTGenericSoundPath* pPath( *spcit++ ); if( pPath->pListener->pData->iID == iListenerID && pPath->pSource->pData->iID == iSourceID ) { - ITAUPConvolution* pConvolver( pPath->vpFIRConvolver[iChannelIndex] ); + ITAUPConvolution* pConvolver( pPath->vpFIRConvolver[ iChannelIndex ] ); ITAUPFilter* pFilter = pConvolver->RequestFilter(); pFilter->Zeros(); - int iLength = (std::min)( m_iIRFilterLengthSamples, sbIR.length() ); + int iLength = ( std::min )( m_iIRFilterLengthSamples, sbIR.length() ); pFilter->Load( sbIR.data(), iLength ); pConvolver->ExchangeFilter( pFilter ); pFilter->Release(); @@ -754,10 +773,10 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio // Sync pathes SyncInternalData(); - + const CVAAudiostreamState* pStreamState = dynamic_cast< const CVAAudiostreamState* >( pStreamInfo ); double dListenerTime = pStreamState->dSysTime; - + // Check for reset request if( ctxAudio.m_iResetFlag == 1 ) { @@ -770,7 +789,7 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio // Reset active, skip until finished return; } - + std::map< int, CVAPTGPListener* >::iterator lit = m_mListeners.begin(); while( lit != m_mListeners.end() ) { @@ -785,7 +804,7 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio { CVAPTGenericSoundPath* pPath( *spit ); CVAReceiverState* pListenerState = ( m_pCurSceneState ? m_pCurSceneState->GetReceiverState( pPath->pListener->pData->iID ) : NULL ); - CVASoundSourceState* pSourceState = ( m_pCurSceneState ? m_pCurSceneState->GetSoundSourceState( pPath->pSource->pData->iID ) : NULL ); + CVASoundSourceState* pSourceState = ( m_pCurSceneState ? m_pCurSceneState->GetSoundSourceState( pPath->pSource->pData->iID ) : NULL ); if( pListenerState == nullptr || pSourceState == nullptr ) { @@ -797,13 +816,15 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio CVASoundSourceDesc* pSourceData = pPath->pSource->pData; ITASampleBuffer* psbInput = pSourceData->pSignalSourceInputBuf; + pPath->pVariableDelayLine->Process( psbInput, &m_sfTempBuffer ); + float fSoundSourceGain = float( pSourceState->GetVolume( m_oParams.pCore->GetCoreConfig()->dDefaultAmplitudeCalibration ) ); - - for( int n=0; n < m_iNumChannels; n++ ) + + for( int n = 0; n < m_iNumChannels; n++ ) { - ITAUPConvolution* pConvolver = pPath->vpFIRConvolver[n]; + ITAUPConvolution* pConvolver = pPath->vpFIRConvolver[ n ]; pConvolver->SetGain( fSoundSourceGain ); - pConvolver->Process( psbInput->data(), ( *pPath->pListener->psfOutput )[n].data(), ITAUPConvolution::OUTPUT_MIX ); + pConvolver->Process( m_sfTempBuffer.GetData(), ( *pPath->pListener->psfOutput )[ n ].data(), ITAUPConvolution::OUTPUT_MIX ); } spit++; @@ -813,8 +834,8 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio if( !m_mListeners.empty() ) { CVAPTGPListener* pActiveListener = m_mListeners.begin()->second; - for( int n=0; n< int( m_pOutput->GetNumberOfChannels() ); n++ ) - for( int i=0; iGetBlocklength() ); i++ ) + for( int n = 0; n< int( m_pOutput->GetNumberOfChannels() ); n++ ) + for( int i = 0; iGetBlocklength() ); i++ ) m_pOutput->GetWritePointer( n )[ i ] = ( *pActiveListener->psfOutput )[ n ][ i ]; bool bDataPresent = false; @@ -843,9 +864,9 @@ void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealizatio } else { - for( int n=0; n< int( m_pOutput->GetNumberOfChannels() ); n++ ) - for( int i=0; iGetBlocklength() ); i++ ) - m_pOutput->GetWritePointer( n )[i] = 0.0f; + for( int n = 0; n< int( m_pOutput->GetNumberOfChannels() ); n++ ) + for( int i = 0; iGetBlocklength() ); i++ ) + m_pOutput->GetWritePointer( n )[ i ] = 0.0f; } m_pOutput->IncrementWritePointer(); @@ -875,7 +896,7 @@ std::string CVAPTGenericPathAudioRenderer::HelpText() const ss << " Provide a key with the name 'filepath' and the path to the WAV file (absolute or containing the macro '$(VADataDir)' or relative to the executable) [priority given to 'filepath' if b) also applies]" << std::endl; ss << " b) sending floating-point data for each channel" << std::endl; ss << " Provide a key for each channel with the generic name 'ch#', where the hash is substituted by the actual number of channel (starting at 1), and the value to this key will contain floating point data (or a sample buffer). " << - "The call parameter struct does not necessarily have to contain all channels, also single channels will be updated if key is given." << std::endl; + "The call parameter struct does not necessarily have to contain all channels, also single channels will be updated if key is given." << std::endl; ss << std::endl; ss << "Note: the existence of the key 'verbose' will print update information at server console and will provide the update info as an 'info' key in the returned struct." << std::endl; return ss.str(); @@ -896,7 +917,6 @@ CVAStruct CVAPTGenericPathAudioRenderer::GetParameters( const CVAStruct& oArgs ) oReturn[ "numchannels" ] = m_iNumChannels; oReturn[ "irfilterlengthsamples" ] = m_iIRFilterLengthSamples; oReturn[ "numpaths" ] = int( m_lSoundPaths.size() ); - oReturn[ "filterdelaysamples" ] = m_iFilterDelaySamples; return oReturn; } @@ -906,13 +926,19 @@ void CVAPTGenericPathAudioRenderer::SetParameters( const CVAStruct& oArgs ) if( oArgs.HasKey( "receiver" ) == false || oArgs.HasKey( "source" ) == false ) VA_EXCEPT2( INVALID_PARAMETER, "PrototypeGenericPath filter update requires a receiver and a source identifier" ); - int iReceiverID = oArgs["receiver"]; - int iSourceID = oArgs["source"]; + int iReceiverID = oArgs[ "receiver" ]; + int iSourceID = oArgs[ "source" ]; bool bVerbose = false; if( oArgs.HasKey( "verbose" ) ) bVerbose = true; + if( oArgs.HasKey( "delay" ) ) + { + const double dDelaySeconds = oArgs[ "delay" ]; + UpdateGenericSoundPath( iReceiverID, iSourceID, dDelaySeconds ); + } + if( oArgs.HasKey( "filepath" ) ) { const std::string& sFilePathRaw( oArgs[ "filepath" ] ); @@ -938,13 +964,13 @@ void CVAPTGenericPathAudioRenderer::SetParameters( const CVAStruct& oArgs ) else { ITASampleBuffer sbIR( m_iIRFilterLengthSamples ); - for( int n=0; n < int( m_pOutput->GetNumberOfChannels() ); n++ ) + for( int n = 0; n < int( m_pOutput->GetNumberOfChannels() ); n++ ) { std::string sChKey = "ch" + IntToString( n + 1 ); if( !oArgs.HasKey( sChKey ) ) continue; - - const CVAStructValue& oValue( oArgs[sChKey] ); + + const CVAStructValue& oValue( oArgs[ sChKey ] ); int iNumSamples = 0; if( oValue.GetDatatype() == CVAStructValue::DATA ) @@ -958,7 +984,7 @@ void CVAPTGenericPathAudioRenderer::SetParameters( const CVAStruct& oArgs ) iNumSamples = m_iIRFilterLengthSamples; } - const float* pfData = (const float*)( oValue.GetData() ); + const float* pfData = ( const float* ) ( oValue.GetData() ); sbIR.Zero(); sbIR.write( pfData, iNumSamples ); } @@ -983,7 +1009,7 @@ void CVAPTGenericPathAudioRenderer::SetParameters( const CVAStruct& oArgs ) std::stringstream ssVerboseText; if( bVerbose ) { - ssVerboseText << "Updated sound path with " << iNumSamples << " new samples"; + ssVerboseText << "Updated sound path with " << iNumSamples << " new samples"; VA_PRINT( ssVerboseText.str() ); } } diff --git a/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.h b/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.h index 85b4acd2dc431d2c9419b6e10f21d56e199acda1..e07fa31102209ed49324374eb84f746eb1b4e5d2 100644 --- a/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.h +++ b/src/Rendering/Prototyping/GenericPath/VAPTGenericPathAudioRenderer.h @@ -120,9 +120,9 @@ private: int m_iIRFilterLengthSamples; //!< Length of the HRIR filter DSP module int m_iNumChannels; //!< Number of channels per sound path - int m_iFilterDelaySamples; //!< Filter delay in samples bool m_bOutputMonitoring; //!< Shows output infos / warnings if the overall listener output is zero (no filter loaded) ITADatasourceRealization* m_pOutput; + ITASampleBuffer m_sfTempBuffer; class CVAPTGPUpdateMessage : public CVAPoolObject { @@ -179,6 +179,7 @@ private: void UpdateGenericSoundPath( int iListenerID, int iSourceID, int iChannel, const std::string& sIRFilePath ); void UpdateGenericSoundPath( int iListenerID, int iSourceID, ITASampleFrame& sfIR ); void UpdateGenericSoundPath( int iListenerID, int iSourceID, int iChannel, ITASampleBuffer& sbIR ); + void UpdateGenericSoundPath( const int iListenerID, const int iSourceID, const double dDelaySeconds ); friend class CVAPTGenericSoundPath; friend class CVAPTGPListenerPoolFactory; diff --git a/src/Rendering/Prototyping/HearingAid/VAPTHearingAidRenderer.cpp b/src/Rendering/Prototyping/HearingAid/VAPTHearingAidRenderer.cpp index 9a23ffc83c7fe0162b50940c3e654523fdbacded..af93432b71f0ef71fa9befc63aa10650b9130a7e 100644 --- a/src/Rendering/Prototyping/HearingAid/VAPTHearingAidRenderer.cpp +++ b/src/Rendering/Prototyping/HearingAid/VAPTHearingAidRenderer.cpp @@ -405,7 +405,7 @@ void CVAPTHearingAidRenderer::Reset() { ctxAudio.m_iResetFlag = 1; // Request reset - if( ctxAudio.m_iStatus == 0 ) + if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering ) { // if no streaming active, reset manually //SyncInternalData(); diff --git a/src/Rendering/VAAudioRenderer.h b/src/Rendering/VAAudioRenderer.h index f7851f0a3f5bdcddc8bb9a39ffc2fcaa54fab585..60dafdac1da2f682cf40685bcaf443ed19902621 100644 --- a/src/Rendering/VAAudioRenderer.h +++ b/src/Rendering/VAAudioRenderer.h @@ -39,6 +39,7 @@ public: bool bRecordOutputEnabled; //!< Renderer output recording and storing flag std::string sRecordOutputFilePath; //!< Renderer output recording and storing file path bool bOutputLevelMeterEnabled; //!< Renderer output level meter will be used (uses a little bit CPU resources) + bool bOfflineRendering; //!< Offline rendering indicator (using a virtual audio device and external trigger) }; //! Audio renderer interface diff --git a/src/VACoreImpl.cpp b/src/VACoreImpl.cpp index 4c48a63a0fe072da7e57cfc6a2df2c384f31a9c1..87aab9fa94bbc42ce96007483d2ace9a32866caf 100644 --- a/src/VACoreImpl.cpp +++ b/src/VACoreImpl.cpp @@ -49,8 +49,8 @@ #ifdef VACORE_WITH_AUDIO_BACKEND_PORTAUDIO #include "Drivers/Audio/VAPortaudioBackend.h" #endif -#ifdef VACORE_WITH_AUDIO_BACKEND_DUMMY -#include "Drivers/Audio/VADummyAudioDriverBackend.h" +#ifdef VACORE_WITH_AUDIO_BACKEND_VIRTUAL +#include "Drivers/Audio/VAVirtualAudioDriverBackend.h" #endif // ITA includes @@ -494,6 +494,7 @@ void CVACoreImpl::Initialize() { SetProgress( "Setting up resource managers", "", 2 ); + assert( m_oCoreConfig.oAudioDriverConfig.iInputChannels >= 0 ); m_pSignalSourceManager = new CVAAudioSignalSourceManager( this, m_oCoreConfig.oAudioDriverConfig, pInputTail ); m_pGlobalSamplePool = ITASoundSamplePool::Create( 1, m_oCoreConfig.oAudioDriverConfig.dSampleRate ); m_pGlobalSampler = ITASoundSampler::Create( 1, m_oCoreConfig.oAudioDriverConfig.dSampleRate, m_oCoreConfig.oAudioDriverConfig.iBuffersize, m_pGlobalSamplePool ); @@ -761,7 +762,8 @@ void CVACoreImpl::Finalize() VA_TRY { // Mehrfaches Finialisieren führt nicht zu Fehlern - if( m_iState == VA_CORESTATE_CREATED ) return; + if( m_iState == VA_CORESTATE_CREATED ) + return; if( m_iState == VA_CORESTATE_FAIL ) VA_EXCEPT2( MODAL_ERROR, "Core corrupted, finalization impossible" ); @@ -4327,17 +4329,32 @@ void CVACoreImpl::InitializeAudioDriver() if( m_oCoreConfig.oAudioDriverConfig.sDriver == "Portaudio" ) m_pAudioDriverBackend = new CVAPortaudioBackend( &m_oCoreConfig.oAudioDriverConfig ); #endif -#ifdef VACORE_WITH_AUDIO_BACKEND_DUMMY - if( m_oCoreConfig.oAudioDriverConfig.sDriver == "Dummy" ) +#ifdef VACORE_WITH_AUDIO_BACKEND_VIRTUAL + if( m_oCoreConfig.oAudioDriverConfig.sDriver == "Virtual" ) { - CVADummyAudioDriverBackend* pAudioDriverBackend = new CVADummyAudioDriverBackend( &m_oCoreConfig.oAudioDriverConfig ); + + if( m_oCoreConfig.oAudioDriverConfig.iBuffersize == -1 ) + VA_EXCEPT2( INVALID_PARAMETER, "For a virtual audio device, the buffer size has to be set (AUTO detect not possible)" ); + + if( m_oCoreConfig.oAudioDriverConfig.iOutputChannels == -1 ) + VA_EXCEPT2( INVALID_PARAMETER, "For a virtual audio device, the output channel number has to be set (AUTO detect not possible)" ); + + m_oCoreConfig.oAudioDriverConfig.iInputChannels = 0; // not allowed, override + + CVAVirtualAudioDriverBackend* pAudioDriverBackend = new CVAVirtualAudioDriverBackend( &m_oCoreConfig.oAudioDriverConfig ); RegisterModule( pAudioDriverBackend ); m_pAudioDriverBackend = pAudioDriverBackend; + + // Overwride default block pointer by manual clock + CVAVirtualAudioDriverBackend::ManualClock* pManualClock = new CVAVirtualAudioDriverBackend::ManualClock(); + RegisterModule( pManualClock ); + m_pClock = pManualClock; } +#else #endif if( m_pAudioDriverBackend == nullptr ) - VA_ERROR( "Core", "Unkown or unsupported audio driver backend '" << m_oCoreConfig.oAudioDriverConfig.sDriver << "'" ); + VA_EXCEPT2( INVALID_PARAMETER, "Unkown, uninitializable or unsupported audio driver backend '" + m_oCoreConfig.oAudioDriverConfig.sDriver + "'" ); try { @@ -4445,6 +4462,7 @@ void CVACoreImpl::InitializeAudioRenderers() oParams.sClass = sClass; oParams.pCore = this; oParams.pConfig = &cit->second.GetStruct(); + oParams.bOfflineRendering = ( m_oCoreConfig.oAudioDriverConfig.sDriver == "Virtual" ) ? true : false; conf.ReqStringListRegex( "Reproductions", oParams.vsReproductions, "\\s*,\\s*" ); std::unique( oParams.vsReproductions.begin(), oParams.vsReproductions.end() ); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 97016f65b9698baadf9d7d39aba51037fdab5d95..bb89a1c3810b1d414845c81f2ecbe5cc4c17bc0d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -61,4 +61,14 @@ vista_configure_app( NetworkStreamAudioSignalSourceTest ) vista_install( NetworkStreamAudioSignalSourceTest ) vista_create_default_info_file( NetworkStreamAudioSignalSourceTest ) -set_property( TARGET NetworkStreamAudioSignalSourceTest PROPERTY FOLDER "VA/Tests/VABase" ) +set_property( TARGET NetworkStreamAudioSignalSourceTest PROPERTY FOLDER "VA/Tests/VACore" ) + + +add_executable( OfflineSimulationVirtualDeviceTest OfflineSimulationVirtualDeviceTest.cpp ) +target_link_libraries( OfflineSimulationVirtualDeviceTest ${VISTA_USE_PACKAGE_LIBRARIES} ${VISTAINTERPROCCOMM_ADDITIONAL_DEPENDENCIES} ) + +vista_configure_app( OfflineSimulationVirtualDeviceTest ) +#vista_install( OfflineSimulationVirtualDeviceTest ) +#vista_create_default_info_file( OfflineSimulationVirtualDeviceTest ) + +set_property( TARGET OfflineSimulationVirtualDeviceTest PROPERTY FOLDER "VA/Tests/VACore" ) diff --git a/tests/CoreTest.cpp b/tests/CoreTest.cpp index fc0a5db36578d700fcf20a314e6061480b7542e5..7e01d9c0b0b161eae6c772550b4c6bbeae9dbd70 100644 --- a/tests/CoreTest.cpp +++ b/tests/CoreTest.cpp @@ -132,13 +132,13 @@ CVAStruct GetCoreConfig() CVAStruct oRenderer1; oRenderer1[ "class" ] = "BinauralFreeField"; - oRenderer1[ "outputs" ] = "MyTalkthroughHeadphones"; + oRenderer1[ "Reproductions" ] = "MyTalkthroughHeadphones"; oConfig[ "Renderer:BFF_CoreTest" ] = oRenderer1; CVAStruct oRenderer2; oRenderer2[ "class" ] = "PrototypeGenericPath"; oRenderer2[ "numchannels" ] = 2; - oRenderer2[ "outputs" ] = "MyTalkthroughHeadphones"; + oRenderer2[ "Reproductions" ] = "MyTalkthroughHeadphones"; oConfig[ "Renderer:PTGP_CoreTest" ] = oRenderer2; return oConfig; diff --git a/tests/OfflineSimulationVirtualDeviceTest.cpp b/tests/OfflineSimulationVirtualDeviceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b5c8b9d09559fcd223ea1be3999d71688f242e3 --- /dev/null +++ b/tests/OfflineSimulationVirtualDeviceTest.cpp @@ -0,0 +1,165 @@ +#include +#include + +#include + +#include + +using namespace std; + +IVAInterface* pCore = NULL; + +const int g_iBlockLength = 128; +const double g_dSampleRate = 44100.0f; + + +//! Trigger virtual audio device block increment in audio processing thread +/** + * A special prototype call to control the virtual audio device and increment a block + * of the audio processing by user. Produces one more block of audio samples and increments + * the pCore time by user-defined time steps. + */ +void TriggerVirtualAudioDeviceBlockIncrement(); + +//! Manually update the core clock +/** + * ... used for scene modification time stamps. This lets the user control the + * asynchronous update beaviour independent from the audio processing thread. + */ +void ManualCoreClockUpdate( const double ); + +void TestOfflineSimulationVirtualAudioDevice() +{ + // Load directivity + int iHRTFID = pCore->CreateDirectivityFromFile( "ITA_Artificial_Head_5x5_44kHz_128.v17.ir.daff" ); + + // Set up scene + int iListenerID = pCore->CreateSoundReceiver( "Listener with HRTF" ); + pCore->SetSoundReceiverPosition( iListenerID, VAVec3( 0, 1.7, 0 ) ); + pCore->SetActiveSoundReceiver( iListenerID ); + pCore->SetSoundReceiverDirectivity( iListenerID, iHRTFID ); + + int iSourceID = pCore->CreateSoundSource( "Source" ); + + // Load input data & start playback + string sFileSignalID = pCore->CreateSignalSourceBufferFromFile( "WelcomeToVA.wav" ); + pCore->SetSignalSourceBufferPlaybackAction( sFileSignalID, IVAInterface::VA_PLAYBACK_ACTION_PLAY ); + pCore->SetSignalSourceBufferLooping( sFileSignalID, true ); + pCore->SetSoundSourceSignalSource( iSourceID, sFileSignalID ); + + + // --- Simulation loop using time steps and synchronized scene & audio updates --- // + + const int iNumSteps = 3445; // will produce 10 seconds of scene updates & audio processing + const double dTimeStepIncrement = double( g_iBlockLength ) / g_dSampleRate; // Set to audio stream block length for synced example + double dManualCoreTime = 0.0f; + + // Motion + float fXPos = -100.0f; + const float fXPosIncrement = 0.05f; + + for( int n = 0; n < iNumSteps; n++ ) + { + pCore->SetSoundSourcePosition( iSourceID, VAVec3( fXPos, 1.7f, -2.0f ) ); + fXPos += fXPosIncrement; + + dManualCoreTime += dTimeStepIncrement; + ManualCoreClockUpdate( dManualCoreTime ); // Scene clock increment + + TriggerVirtualAudioDeviceBlockIncrement(); // Audio processing increment + } +} + +//! Creates an example configuration for two-channel offline simulation and recording +CVAStruct CreateOfflineSimulationVirtualDeviceConfig() +{ + CVAStruct oConfig, oAudioDevice; + + CVAStruct oSectionDebug; + oSectionDebug[ "loglevel" ] = IVAInterface::VA_LOG_LEVEL_VERBOSE; + oConfig[ "debug" ] = oSectionDebug; + + CVAStruct oSectionDriver; + oSectionDriver[ "driver" ] = "Virtual"; + + // Default user-triggered settings + oSectionDriver[ "Device" ] = "Trigger"; + oSectionDriver[ "BufferSize" ] = g_iBlockLength; + oSectionDriver[ "SampleRate" ] = g_dSampleRate; + oSectionDriver[ "OutputChannels" ] = 2; + + // Timeout settings + //oSectionDriver[ "Device" ] = "Timout"; + //oSectionDriver[ "UpdateRate" ] = 300; + + oConfig[ "audio driver" ] = oSectionDriver; + + CVAStruct oDevice1; + oDevice1[ "type" ] = "HP"; + oDevice1[ "channels" ] = "1,2"; + oConfig[ "OutputDevice:MyHP" ] = oDevice1; + + CVAStruct oOutput1; + oOutput1[ "devices" ] = "MyHP"; + oConfig[ "Output:MyDesktopHP" ] = oOutput1; + + CVAStruct oReproduction1; + oReproduction1[ "class" ] = "Talkthrough"; + oReproduction1[ "outputs" ] = "MyDesktopHP"; + oConfig[ "Reproduction:MyTalkthroughHeadphones" ] = oReproduction1; + + CVAStruct oRenderer1; + oRenderer1[ "class" ] = "BinauralFreeField"; + oRenderer1[ "Reproductions" ] = "MyTalkthroughHeadphones"; + oRenderer1[ "RecordOutputEnabled" ] = true; + oRenderer1[ "RecordOutputFilePath" ] = "OfflineSimulationVirtualDeviceTest.wav"; + oConfig[ "Renderer:BFF_OfflineSimulationTest" ] = oRenderer1; + + return oConfig; +} + +void TriggerVirtualAudioDeviceBlockIncrement() +{ + CVAStruct oTriggerMessage; + oTriggerMessage[ "trigger" ] = true; + CVAStruct oAnswer = pCore->CallModule( "VirtualAudioDevice", oTriggerMessage ); +} + +void ManualCoreClockUpdate( const double dNewCoreClockTimeSeconds ) +{ + CVAStruct oCoreClockIncrement; + oCoreClockIncrement[ "time" ] = dNewCoreClockTimeSeconds; + CVAStruct oAnswer = pCore->CallModule( "manualclock", oCoreClockIncrement ); +} + +int main() +{ + try + { + CVAStruct opCoreConfig = CreateOfflineSimulationVirtualDeviceConfig(); + pCore = VACore::CreateCoreInstance( opCoreConfig ); + pCore->Initialize(); + + pCore->AddSearchPath( "../data" ); + TestOfflineSimulationVirtualAudioDevice(); + + CVAStruct oManualReset; + oManualReset[ "prepare_manual_reset" ] = true; + pCore->SetRenderingModuleParameters( "BFF_OfflineSimulationTest", oManualReset ); + + pCore->Finalize(); // Executes reset, which results in deadlock of no audio streaming thread active + + } + catch( CVAException& e ) + { + cerr << "Error: " << e << endl; + int iErrorCode = e.GetErrorCode(); + delete pCore; + + return iErrorCode; + } + + delete pCore; + + return 0; +}