VAPTGenericPathAudioRenderer.cpp 32.1 KB
Newer Older
1 2 3 4 5 6
/*
 *  --------------------------------------------------------------------------------------------
 *
 *    VVV        VVV A           Virtual Acoustics (VA) | http://www.virtualacoustics.org
 *     VVV      VVV AAA          Licensed under the Apache License, Version 2.0
 *      VVV    VVV   AAA
7
 *       VVV  VVV     AAA        Copyright 2015-2018
8 9 10 11 12 13
 *        VVVVVV       AAA       Institute of Technical Acoustics (ITA)
 *         VVVV         AAA      RWTH Aachen University
 *
 *  --------------------------------------------------------------------------------------------
 */

Jonas Stienen's avatar
Jonas Stienen committed
14 15
#include "VAPTGenericPathAudioRenderer.h"

16
#ifdef VACORE_WITH_RENDERER_PROTOTYPE_GENERIC_PATH
Jonas Stienen's avatar
Jonas Stienen committed
17 18

// VA includes
19 20 21 22 23
#include "../../../Scene/VAScene.h"
#include "../../../Utils/VAUtils.h"
#include "../../../VALog.h"
#include "../../../VACoreImpl.h"
#include "../../../VACoreConfig.h"
Jonas Stienen's avatar
Jonas Stienen committed
24 25 26
#include <VAReferenceableObject.h>

// ITA includes
27 28 29
#include <ITAUPConvolution.h>
#include <ITAUPFilter.h>
#include <ITAUPFilterPool.h>
Jonas Stienen's avatar
Jonas Stienen committed
30
#include <ITAAtomicPrimitives.h>
31
#include <ITADataSourceRealization.h>
32
#include <ITAVariableDelayLine.h>
Jonas Stienen's avatar
Jonas Stienen committed
33 34 35 36 37

// Vista includes
#include <VistaInterProcComm/Concurrency/VistaThreadEvent.h>

// STL includes
38
#include <cassert>
Jonas Stienen's avatar
Jonas Stienen committed
39
#include <vector>
40
#include <cmath>
Jonas Stienen's avatar
Jonas Stienen committed
41 42 43 44

// 3rdParty includes
#include <tbb/concurrent_queue.h>

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
45
//! Generic sound path
Jonas Stienen's avatar
Jonas Stienen committed
46 47 48 49 50 51 52 53
class CVAPTGenericSoundPath : public CVAPoolObject
{
public:
	virtual ~CVAPTGenericSoundPath();
	CVAPTGenericPathAudioRenderer::CVAPTGPSource* pSource;
	CVAPTGenericPathAudioRenderer::CVAPTGPListener* pListener;
	ITAAtomicBool bDelete;

54
	CITAVariableDelayLine* pVariableDelayLine;
55
	std::vector< ITAUPConvolution* > vpFIRConvolver; // N-channel convolver
56

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
57
	inline void PreRequest()
58
	{
Jonas Stienen's avatar
Jonas Stienen committed
59 60
		pSource = nullptr;
		pListener = nullptr;
61 62 63 64 65

		pVariableDelayLine->Clear();

		for( size_t n = 0; n < vpFIRConvolver.size(); n++ )
			vpFIRConvolver[ n ]->clear();
Jonas Stienen's avatar
Jonas Stienen committed
66 67 68 69 70
	};

private:
	CVAPTGenericSoundPath();
	CVAPTGenericSoundPath( double dSamplerate, int iBlocklength, int iNumChannels, int iIRFilterLength );
71

Jonas Stienen's avatar
Jonas Stienen committed
72 73 74 75 76
	friend class CVAPTGenericSoundPathFactory;
};

class CVAPTGenericSoundPathFactory : public IVAPoolObjectFactory
{
77
public:
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
78 79 80 81 82 83 84 85
	inline CVAPTGenericSoundPathFactory( double dSampleRate, int iBlockLength, int iNumChannels, int iIRFilterLength )
		: m_dSampleRate( dSampleRate )
		, m_iBlockLength( iBlockLength )
		, m_iNumChannels( iNumChannels )
		, m_iIRFilterLengthSamples( iIRFilterLength )
	{};

	inline CVAPoolObject* CreatePoolObject()
Jonas Stienen's avatar
Jonas Stienen committed
86
	{
87
		return new CVAPTGenericSoundPath( m_dSampleRate, m_iBlockLength, m_iNumChannels, m_iIRFilterLengthSamples );
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
88
	};
Jonas Stienen's avatar
Jonas Stienen committed
89 90 91 92

private:
	double m_dSampleRate;
	int m_iBlockLength;
93
	int m_iIRFilterLengthSamples;
Jonas Stienen's avatar
Jonas Stienen committed
94 95 96 97 98 99
	int m_iNumChannels;
};

class CVAPTGPListenerPoolFactory : public IVAPoolObjectFactory
{
public:
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
100
	inline CVAPTGPListenerPoolFactory( CVACoreImpl* pCore )
101
		: m_pCore( pCore )
Jonas Stienen's avatar
Jonas Stienen committed
102 103 104
	{
	};

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
105
	inline CVAPoolObject* CreatePoolObject()
Jonas Stienen's avatar
Jonas Stienen committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119
	{
		CVAPTGenericPathAudioRenderer::CVAPTGPListener* pListener;
		pListener = new CVAPTGenericPathAudioRenderer::CVAPTGPListener();
		pListener->pCore = m_pCore;
		return pListener;
	};

private:
	CVACoreImpl* m_pCore;
};

class CVAPTGPSourcePoolFactory : public IVAPoolObjectFactory
{
public:
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
120
	inline CVAPTGPSourcePoolFactory()
Jonas Stienen's avatar
Jonas Stienen committed
121 122 123
	{
	};

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
124
	inline CVAPoolObject* CreatePoolObject()
Jonas Stienen's avatar
Jonas Stienen committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	{
		CVAPTGenericPathAudioRenderer::CVAPTGPSource* pSource;
		pSource = new CVAPTGenericPathAudioRenderer::CVAPTGPSource();
		return pSource;
	};
};


// Class CVAPTGenericSoundPath

CVAPTGenericSoundPath::CVAPTGenericSoundPath( double dSamplerate, int iBlocklength, int iNumChannels, int iIRFilterLength )
{
	if( iNumChannels < 1 )
		ITA_EXCEPT1( INVALID_PARAMETER, "Number of channels must be positive" );

140 141 142 143
	const int iAlgorithm = CITAVariableDelayLine::CUBIC_SPLINE_INTERPOLATION;
	pVariableDelayLine = new CITAVariableDelayLine( dSamplerate, iBlocklength, 6 * dSamplerate, iAlgorithm );

	for( int n = 0; n < iNumChannels; n++ )
Jonas Stienen's avatar
Jonas Stienen committed
144
	{
145
		ITAUPConvolution* pFIRConvolver = new ITAUPConvolution( iBlocklength, iIRFilterLength );
146
		pFIRConvolver->SetFilterExchangeMode( ITAUPConvolution::CROSSFADE_COSINE_SQUARE );
147
		pFIRConvolver->SetFilterCrossfadeLength( ( std::min )( iBlocklength, 32 ) );
148 149 150 151 152
		pFIRConvolver->SetGain( 1.0f, true );
		ITAUPFilter* pHRIRFilterChL = pFIRConvolver->RequestFilter();
		pHRIRFilterChL->Zeros();
		pFIRConvolver->ExchangeFilter( pHRIRFilterChL );
		pFIRConvolver->ReleaseFilter( pHRIRFilterChL );
Jonas Stienen's avatar
Jonas Stienen committed
153

154 155
		vpFIRConvolver.push_back( pFIRConvolver );
	}
Jonas Stienen's avatar
Jonas Stienen committed
156 157
}

158
CVAPTGenericSoundPath::~CVAPTGenericSoundPath()
Jonas Stienen's avatar
Jonas Stienen committed
159
{
160 161 162 163
	delete pVariableDelayLine;

	for( size_t n = 0; n < vpFIRConvolver.size(); n++ )
		delete vpFIRConvolver[ n ];
Jonas Stienen's avatar
Jonas Stienen committed
164 165 166 167 168 169
}


// Renderer

CVAPTGenericPathAudioRenderer::CVAPTGenericPathAudioRenderer( const CVAAudioRendererInitParams& oParams )
170 171 172 173 174
	: m_pCore( oParams.pCore )
	, m_pCurSceneState( nullptr )
	, m_iIRFilterLengthSamples( -1 )
	, m_iNumChannels( -1 )
	, m_oParams( oParams )
Jonas Stienen's avatar
Jonas Stienen committed
175 176 177
{
	// read config
	Init( *oParams.pConfig );
178

179
	m_pOutput = new ITADatasourceRealization( m_iNumChannels, oParams.pCore->GetCoreConfig()->oAudioDriverConfig.dSampleRate, oParams.pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize );
Jonas Stienen's avatar
Jonas Stienen committed
180 181 182 183 184 185 186 187
	m_pOutput->SetStreamEventHandler( this );

	IVAPoolObjectFactory* pListenerFactory = new CVAPTGPListenerPoolFactory( m_pCore );
	m_pListenerPool = IVAObjectPool::Create( 16, 2, pListenerFactory, true );

	IVAPoolObjectFactory* pSourceFactory = new CVAPTGPSourcePoolFactory();
	m_pSourcePool = IVAObjectPool::Create( 16, 2, pSourceFactory, true );

188
	m_pSoundPathFactory = new CVAPTGenericSoundPathFactory( m_pOutput->GetSampleRate(), m_pOutput->GetBlocklength(), m_iNumChannels, m_iIRFilterLengthSamples );
Jonas Stienen's avatar
Jonas Stienen committed
189 190 191 192 193 194 195 196

	m_pSoundPathPool = IVAObjectPool::Create( 64, 8, m_pSoundPathFactory, true );

	m_pUpdateMessagePool = IVAObjectPool::Create( 2, 1, new CVAPoolObjectDefaultFactory< CVAPTGPUpdateMessage >, true );

	ctxAudio.m_iResetFlag = 0; // Normal operation mode
	ctxAudio.m_iStatus = 0; // Stopped

197
	m_iCurGlobalAuralizationMode = IVAInterface::VA_AURAMODE_DEFAULT;
198 199

	m_sfTempBuffer.Init( oParams.pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize, true );
Jonas Stienen's avatar
Jonas Stienen committed
200 201
}

202
CVAPTGenericPathAudioRenderer::~CVAPTGenericPathAudioRenderer()
Jonas Stienen's avatar
Jonas Stienen committed
203 204 205 206 207 208 209 210 211 212 213
{
	delete m_pSoundPathPool;
	delete m_pUpdateMessagePool;
}

void CVAPTGenericPathAudioRenderer::Init( const CVAStruct& oArgs )
{
	CVAConfigInterpreter conf( oArgs );

	conf.ReqInteger( "NumChannels", m_iNumChannels );

214
	conf.OptInteger( "IRFilterLengthSamples", m_iIRFilterLengthSamples, 1024 );
Jonas Stienen's avatar
Jonas Stienen committed
215

216
	if( m_iIRFilterLengthSamples < 0 )
Jonas Stienen's avatar
Jonas Stienen committed
217 218
		ITA_EXCEPT1( INVALID_PARAMETER, "IR filter size must be positive" );

219 220
	conf.OptBool( "OutputMonitoring", m_bOutputMonitoring, false );

Jonas Stienen's avatar
Jonas Stienen committed
221 222 223
	return;
}

224
void CVAPTGenericPathAudioRenderer::Reset()
Jonas Stienen's avatar
Jonas Stienen committed
225 226 227
{
	ctxAudio.m_iResetFlag = 1; // Request reset

228
	if( ctxAudio.m_iStatus == 0 || m_oParams.bOfflineRendering )
Jonas Stienen's avatar
Jonas Stienen committed
229 230 231 232 233 234 235 236 237 238 239
	{
		// if no streaming active, reset manually
		//SyncInternalData();
		ResetInternalData();
	}

	// Wait for last streaming block before internal reset
	while( ctxAudio.m_iResetFlag != 2 )
	{
		VASleep( 100 ); // Wait for acknowledge
	}
240

Jonas Stienen's avatar
Jonas Stienen committed
241 242 243 244 245
	// Iterate over sound pathes and free items
	std::list< CVAPTGenericSoundPath* >::iterator it = m_lSoundPaths.begin();
	while( it != m_lSoundPaths.end() )
	{
		CVAPTGenericSoundPath* pPath = *it;
246

Jonas Stienen's avatar
Jonas Stienen committed
247 248 249 250 251 252 253 254
		int iNumRefs = pPath->GetNumReferences();
		assert( iNumRefs == 1 );
		pPath->RemoveReference();

		++it;
	}
	m_lSoundPaths.clear();

255
	// Iterate over sound receiver and free items
Jonas Stienen's avatar
Jonas Stienen committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
	std::map< int, CVAPTGPListener* >::const_iterator lcit = m_mListeners.begin();
	while( lcit != m_mListeners.end() )
	{
		CVAPTGPListener* pListener( lcit->second );
		pListener->pData->RemoveReference();
		assert( pListener->GetNumReferences() == 1 );
		pListener->RemoveReference();
		lcit++;
	}
	m_mListeners.clear();

	// Iterate over sources and free items
	std::map< int, CVAPTGPSource* >::const_iterator scit = m_mSources.begin();
	while( scit != m_mSources.end() )
	{
		CVAPTGPSource* pSource( scit->second );
		pSource->pData->RemoveReference();
		assert( pSource->GetNumReferences() == 1 );
		pSource->RemoveReference();
		scit++;
	}
	m_mSources.clear();

	// Scene frei geben
	if( m_pCurSceneState )
	{
		m_pCurSceneState->RemoveReference();
		m_pCurSceneState = nullptr;
	}

	ctxAudio.m_iResetFlag = 0; // Enter normal mode
}

void CVAPTGenericPathAudioRenderer::UpdateScene( CVASceneState* pNewSceneState )
{
	assert( pNewSceneState );

	m_pNewSceneState = pNewSceneState;
	if( m_pNewSceneState == m_pCurSceneState )
		return;

	// Neue Szene referenzieren (gegen Freigabe sperren)
	m_pNewSceneState->AddReference();

	// Unterschiede ermitteln: Neue Szene vs. alte Szene
	CVASceneStateDiff oDiff;
	pNewSceneState->Diff( m_pCurSceneState, &oDiff );

	// Leere Update-Nachricht zusammenstellen
	m_pUpdateMessage = dynamic_cast< CVAPTGPUpdateMessage* >( m_pUpdateMessagePool->RequestObject() );

	ManageSoundPaths( m_pCurSceneState, pNewSceneState, &oDiff );

	// Update-Nachricht an den Audiokontext schicken
	ctxAudio.m_qpUpdateMessages.push( m_pUpdateMessage );

	// Alte Szene freigeben (dereferenzieren)
	if( m_pCurSceneState )
		m_pCurSceneState->RemoveReference();

	m_pCurSceneState = m_pNewSceneState;
	m_pNewSceneState = nullptr;
}

ITADatasource* CVAPTGenericPathAudioRenderer::GetOutputDatasource()
{
	return m_pOutput;
}

void CVAPTGenericPathAudioRenderer::ManageSoundPaths( const CVASceneState* pCurScene,
326 327
	const CVASceneState* pNewScene,
	const CVASceneStateDiff* pDiff )
Jonas Stienen's avatar
Jonas Stienen committed
328 329 330
{
	// 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();
331
	while( itp != m_lSoundPaths.end() )
Jonas Stienen's avatar
Jonas Stienen committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	{
		CVAPTGenericSoundPath* pPath( *itp );
		int iSourceID = pPath->pSource->pData->iID;
		int iListenerID = pPath->pListener->pData->iID;
		bool bDeletetionRequired = false;

		// Source deleted?
		std::vector< int >::const_iterator cits = pDiff->viDelSoundSourceIDs.begin();
		while( cits != pDiff->viDelSoundSourceIDs.end() )
		{
			const int& iIDDeletedSource( *cits++ );
			if( iSourceID == iIDDeletedSource )
			{
				bDeletetionRequired = true; // Source removed, deletion required
				break;
			}
		}

		if( bDeletetionRequired == false )
		{
			// Receiver deleted?
353 354
			std::vector< int >::const_iterator citr = pDiff->viDelReceiverIDs.begin();
			while( citr != pDiff->viDelReceiverIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
			{
				const int& iIDListenerDeleted( *citr++ );
				if( iListenerID == iIDListenerDeleted )
				{
					bDeletetionRequired = true; // Listener removed, deletion required
					break;
				}
			}
		}

		if( bDeletetionRequired )
		{
			DeleteSoundPath( pPath );
			itp = m_lSoundPaths.erase( itp ); // Increment via erase on path list
		}
		else
		{
			++itp; // no deletion detected, continue
		}
	}

	// Deleted sources
	std::vector< int >::const_iterator cits = pDiff->viDelSoundSourceIDs.begin();
	while( cits != pDiff->viDelSoundSourceIDs.end() )
	{
		const int& iID( *cits++ );
		DeleteSource( iID );
	}

	// Deleted receivers
385 386
	std::vector< int >::const_iterator citr = pDiff->viDelReceiverIDs.begin();
	while( citr != pDiff->viDelReceiverIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
387 388 389 390 391 392 393 394 395 396 397 398 399 400
	{
		const int& iID( *citr++ );
		DeleteListener( iID );
	}

	// New sources
	cits = pDiff->viNewSoundSourceIDs.begin();
	while( cits != pDiff->viNewSoundSourceIDs.end() )
	{
		const int& iID( *cits++ );
		CVAPTGPSource* pSource = CreateSource( iID, pNewScene->GetSoundSourceState( iID ) );
	}

	// New receivers
401 402
	citr = pDiff->viNewReceiverIDs.begin();
	while( citr != pDiff->viNewReceiverIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
403 404
	{
		const int& iID( *citr++ );
405
		CVAPTGPListener* pListener = CreateListener( iID, pNewScene->GetReceiverState( iID ) );
Jonas Stienen's avatar
Jonas Stienen committed
406 407 408
	}

	// New paths: (1) new receivers, current sources
409 410
	citr = pDiff->viNewReceiverIDs.begin();
	while( citr != pDiff->viNewReceiverIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
411 412
	{
		int iListenerID = ( *citr++ );
413
		CVAPTGPListener* pListener = m_mListeners[ iListenerID ];
Jonas Stienen's avatar
Jonas Stienen committed
414

415
		for( size_t i = 0; i < pDiff->viComSoundSourceIDs.size(); i++ )
Jonas Stienen's avatar
Jonas Stienen committed
416
		{
417 418
			int iSourceID = pDiff->viComSoundSourceIDs[ i ];
			CVAPTGPSource* pSource = m_mSources[ iSourceID ];
Jonas Stienen's avatar
Jonas Stienen committed
419 420 421 422 423 424 425
			if( !pSource->bDeleted ) // only, if not marked for deletion
				CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener );
		}
	}

	// New paths: (2) new sources, current receivers
	cits = pDiff->viNewSoundSourceIDs.begin();
426
	while( cits != pDiff->viNewSoundSourceIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
427 428
	{
		const int& iSourceID( *cits++ );
429
		CVAPTGPSource* pSource = m_mSources[ iSourceID ];
Jonas Stienen's avatar
Jonas Stienen committed
430

431
		for( size_t i = 0; i < pDiff->viComReceiverIDs.size(); i++ )
Jonas Stienen's avatar
Jonas Stienen committed
432
		{
433 434
			int iListenerID = pDiff->viComReceiverIDs[ i ];
			CVAPTGPListener* pListener = m_mListeners[ iListenerID ];
Jonas Stienen's avatar
Jonas Stienen committed
435 436 437 438 439 440 441 442 443 444
			if( !pListener->bDeleted )
				CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener );
		}
	}

	// New paths: (3) new sources, new receivers
	cits = pDiff->viNewSoundSourceIDs.begin();
	while( cits != pDiff->viNewSoundSourceIDs.end() )
	{
		const int& iSourceID( *cits++ );
445
		CVAPTGPSource* pSource = m_mSources[ iSourceID ];
Jonas Stienen's avatar
Jonas Stienen committed
446

447 448
		citr = pDiff->viNewReceiverIDs.begin();
		while( citr != pDiff->viNewReceiverIDs.end() )
Jonas Stienen's avatar
Jonas Stienen committed
449 450
		{
			const int& iListenerID( *citr++ );
451
			CVAPTGPListener* pListener = m_mListeners[ iListenerID ];
Jonas Stienen's avatar
Jonas Stienen committed
452 453 454
			CVAPTGenericSoundPath* pPath = CreateSoundPath( pSource, pListener );
		}
	}
455

Jonas Stienen's avatar
Jonas Stienen committed
456 457 458
	return;
}

459
CVAPTGenericSoundPath* CVAPTGenericPathAudioRenderer::CreateSoundPath( CVAPTGPSource* pSource, CVAPTGPListener* pListener )
Jonas Stienen's avatar
Jonas Stienen committed
460 461 462 463 464 465
{
	int iSourceID = pSource->pData->iID;
	int iListenerID = pListener->pData->iID;

	assert( !pSource->bDeleted && !pListener->bDeleted );

466 467 468
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating sound path from source " << iSourceID << " -> sound receiver " << iListenerID );

	CVAPTGenericSoundPath* pPath = dynamic_cast< CVAPTGenericSoundPath* >( m_pSoundPathPool->RequestObject() );
Jonas Stienen's avatar
Jonas Stienen committed
469 470 471 472 473 474 475 476 477 478 479 480 481 482

	pPath->pSource = pSource;
	pPath->pListener = pListener;

	pPath->bDelete = false;

	m_lSoundPaths.push_back( pPath );
	m_pUpdateMessage->vNewPaths.push_back( pPath );

	return pPath;
}

void CVAPTGenericPathAudioRenderer::DeleteSoundPath( CVAPTGenericSoundPath* pPath )
{
483
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Marking sound path from source " << pPath->pSource->pData->iID << " -> sound receiver " << pPath->pListener->pData->iID << " for deletion" );
Jonas Stienen's avatar
Jonas Stienen committed
484 485 486 487 488 489

	pPath->bDelete = true;
	pPath->RemoveReference();
	m_pUpdateMessage->vDelPaths.push_back( pPath );
}

490
CVAPTGenericPathAudioRenderer::CVAPTGPListener* CVAPTGenericPathAudioRenderer::CreateListener( const int iID, const CVAReceiverState* pListenerState )
Jonas Stienen's avatar
Jonas Stienen committed
491
{
492
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating sound receiver with ID " << iID );
Jonas Stienen's avatar
Jonas Stienen committed
493 494 495 496 497 498 499

	CVAPTGPListener* pListener = dynamic_cast< CVAPTGPListener* >( m_pListenerPool->RequestObject() ); // Reference = 1

	pListener->pData = m_pCore->GetSceneManager()->GetListenerDesc( iID );
	pListener->pData->AddReference();

	// Move to prerequest of pool?
500
	pListener->psfOutput = new ITASampleFrame( m_iNumChannels,
501 502
		m_pCore->GetCoreConfig()->oAudioDriverConfig.iBuffersize,
		true );
Jonas Stienen's avatar
Jonas Stienen committed
503 504 505 506 507 508 509 510 511 512 513 514
	assert( pListener->pData );
	pListener->bDeleted = false;

	m_mListeners.insert( std::pair< int, CVAPTGenericPathAudioRenderer::CVAPTGPListener* >( iID, pListener ) );

	m_pUpdateMessage->vNewListeners.push_back( pListener );

	return pListener;
}

void CVAPTGenericPathAudioRenderer::DeleteListener( int iListenerID )
{
515
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Marking sound receiver with ID " << iListenerID << " for removal" );
Jonas Stienen's avatar
Jonas Stienen committed
516 517 518 519 520 521 522 523 524 525 526 527 528 529
	std::map< int, CVAPTGPListener* >::iterator it = m_mListeners.find( iListenerID );
	CVAPTGPListener* pListener = it->second;
	m_mListeners.erase( it );
	pListener->bDeleted = true;
	pListener->pData->RemoveReference();
	pListener->RemoveReference();

	m_pUpdateMessage->vDelListeners.push_back( pListener );

	return;
}

CVAPTGenericPathAudioRenderer::CVAPTGPSource* CVAPTGenericPathAudioRenderer::CreateSource( int iID, const CVASoundSourceState* pSourceState )
{
530
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Creating source with ID " << iID );
Jonas Stienen's avatar
Jonas Stienen committed
531
	CVAPTGPSource* pSource = dynamic_cast< CVAPTGPSource* >( m_pSourcePool->RequestObject() );
532 533

	pSource->pData = m_pCore->GetSceneManager()->GetSoundSourceDesc( iID );
Jonas Stienen's avatar
Jonas Stienen committed
534 535 536 537 538 539 540 541 542 543 544 545 546
	pSource->pData->AddReference();

	pSource->bDeleted = false;

	m_mSources.insert( std::pair< int, CVAPTGPSource* >( iID, pSource ) );

	m_pUpdateMessage->vNewSources.push_back( pSource );

	return pSource;
}

void CVAPTGenericPathAudioRenderer::DeleteSource( int iSourceID )
{
547
	VA_VERBOSE( "PTGenericPathAudioRenderer", "Marking source with ID " << iSourceID << " for removal" );
Jonas Stienen's avatar
Jonas Stienen committed
548 549 550 551 552 553 554 555
	std::map< int, CVAPTGPSource* >::iterator it = m_mSources.find( iSourceID );
	CVAPTGPSource* pSource = it->second;
	m_mSources.erase( it );
	pSource->bDeleted = true;
	pSource->pData->RemoveReference();
	pSource->RemoveReference();

	m_pUpdateMessage->vDelSources.push_back( pSource );
556

Jonas Stienen's avatar
Jonas Stienen committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
	return;
}

void CVAPTGenericPathAudioRenderer::SyncInternalData()
{
	CVAPTGPUpdateMessage* pUpdate;
	while( ctxAudio.m_qpUpdateMessages.try_pop( pUpdate ) )
	{
		std::list< CVAPTGenericSoundPath* >::const_iterator citp = pUpdate->vDelPaths.begin();
		while( citp != pUpdate->vDelPaths.end() )
		{
			CVAPTGenericSoundPath* pPath( *citp++ );
			ctxAudio.m_lSoundPaths.remove( pPath );
			pPath->RemoveReference();
		}

		citp = pUpdate->vNewPaths.begin();
		while( citp != pUpdate->vNewPaths.end() )
		{
			CVAPTGenericSoundPath* pPath( *citp++ );
			pPath->AddReference();
			ctxAudio.m_lSoundPaths.push_back( pPath );
		}

		std::list< CVAPTGPSource* >::const_iterator cits = pUpdate->vDelSources.begin();
		while( cits != pUpdate->vDelSources.end() )
		{
			CVAPTGPSource* pSource( *cits++ );
			ctxAudio.m_lSources.remove( pSource );
			pSource->pData->RemoveReference();
			pSource->RemoveReference();
		}

		cits = pUpdate->vNewSources.begin();
		while( cits != pUpdate->vNewSources.end() )
		{
			CVAPTGPSource* pSource( *cits++ );
			pSource->AddReference();
			pSource->pData->AddReference();
			ctxAudio.m_lSources.push_back( pSource );
		}

		std::list< CVAPTGPListener* >::const_iterator citl = pUpdate->vDelListeners.begin();
		while( citl != pUpdate->vDelListeners.end() )
		{
			CVAPTGPListener* pListener( *citl++ );
			ctxAudio.m_lListener.remove( pListener );
			pListener->pData->RemoveReference();
			pListener->RemoveReference();
		}

		citl = pUpdate->vNewListeners.begin();
		while( citl != pUpdate->vNewListeners.end() )
		{
			CVAPTGPListener* pListener( *citl++ );
			pListener->AddReference();
			pListener->pData->AddReference();
			ctxAudio.m_lListener.push_back( pListener );
		}

		pUpdate->RemoveReference();
	}

	return;
}

void CVAPTGenericPathAudioRenderer::ResetInternalData()
{
	std::list< CVAPTGenericSoundPath* >::const_iterator citp = ctxAudio.m_lSoundPaths.begin();
626
	while( citp != ctxAudio.m_lSoundPaths.end() )
Jonas Stienen's avatar
Jonas Stienen committed
627 628 629 630 631 632
	{
		CVAPTGenericSoundPath* pPath( *citp++ );
		pPath->RemoveReference();
	}
	ctxAudio.m_lSoundPaths.clear();

633 634
	std::list< CVAPTGPListener* >::const_iterator itl = ctxAudio.m_lListener.begin();
	while( itl != ctxAudio.m_lListener.end() )
Jonas Stienen's avatar
Jonas Stienen committed
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
	{
		CVAPTGPListener* pListener( *itl++ );
		pListener->pData->RemoveReference();
		pListener->RemoveReference();
	}
	ctxAudio.m_lListener.clear();

	std::list< CVAPTGPSource* >::const_iterator cits = ctxAudio.m_lSources.begin();
	while( cits != ctxAudio.m_lSources.end() )
	{
		CVAPTGPSource* pSource( *cits++ );
		pSource->pData->RemoveReference();
		pSource->RemoveReference();
	}
	ctxAudio.m_lSources.clear();

	ctxAudio.m_iResetFlag = 2; // set ack
}

void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, const std::string& sIRFilePath )
{
	ITASampleFrame sfIR( sIRFilePath );
	if( sfIR.channels() != m_pOutput->GetNumberOfChannels() )
		ITA_EXCEPT1( INVALID_PARAMETER, "Filter has mismatching channels" );
	UpdateGenericSoundPath( iListenerID, iSourceID, sfIR );
}

662 663 664 665 666 667 668 669 670 671
void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, int iChannelIndex, const std::string& sIRFilePath )
{
	if( iChannelIndex >= m_iNumChannels || iChannelIndex < 0 )
		ITA_EXCEPT1( INVALID_PARAMETER, "Requested filter channel greater than output channels or smaller than 1, can not update." );

	ITASampleFrame sfIR( sIRFilePath );
	if( sfIR.channels() != 1 )
	{
		if( sfIR.channels() != m_iNumChannels )
		{
672
			VA_ERROR( "PTGenericPathAudioRenderer", "Filter has mismatching and more than one channel. Don't know what to do - refusing this update." );
673 674 675
		}
		else
		{
676
			VA_WARN( "PTGenericPathAudioRenderer", "Filter has more than one channel, updating only requested channel index." );
677 678 679 680 681 682 683 684 685
			UpdateGenericSoundPath( iListenerID, iSourceID, iChannelIndex, sfIR[ iChannelIndex ] );
		}
	}
	else
	{
		UpdateGenericSoundPath( iListenerID, iSourceID, iChannelIndex, sfIR[ 0 ] );
	}

}
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
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 );
}
704

Jonas Stienen's avatar
Jonas Stienen committed
705 706
void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, ITASampleFrame& sfIR )
{
707
	if( sfIR.length() > m_iIRFilterLengthSamples )
708
		VA_WARN( "PTGenericPathAudioRenderer", "Filter length for generic sound path too long, cropping." );
Jonas Stienen's avatar
Jonas Stienen committed
709 710 711 712 713 714 715

	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 )
		{
716
			for( int n = 0; n < m_iNumChannels; n++ )
Jonas Stienen's avatar
Jonas Stienen committed
717
			{
718
				ITAUPConvolution* pConvolver( pPath->vpFIRConvolver[ n ] );
719 720
				ITAUPFilter* pFilter = pConvolver->RequestFilter();
				pFilter->Zeros();
721 722 723
				int iLength = ( std::min )( m_iIRFilterLengthSamples, sfIR.length() );
				pFilter->Load( sfIR[ n ].data(), iLength );

724 725
				pConvolver->ExchangeFilter( pFilter );
				pFilter->Release();
Jonas Stienen's avatar
Jonas Stienen committed
726 727 728 729 730
			}
			return;
		}
	}

731
	VA_ERROR( "PTGenericPathAudioRenderer", "Could not find sound path for sound receiver " << iListenerID << " and source " << iSourceID );
Jonas Stienen's avatar
Jonas Stienen committed
732 733 734
}


735
void CVAPTGenericPathAudioRenderer::UpdateGenericSoundPath( int iListenerID, int iSourceID, int iChannelIndex, ITASampleBuffer& sbIR )
Jonas Stienen's avatar
Jonas Stienen committed
736
{
737
	if( iChannelIndex >= m_iNumChannels || iChannelIndex < 0 )
Jonas Stienen's avatar
Jonas Stienen committed
738 739
		VA_EXCEPT2( INVALID_PARAMETER, "Requested filter channel index of generic sound path out of bounds" );

740
	if( sbIR.length() > m_iIRFilterLengthSamples )
741
		VA_WARN( "PTGenericPathAudioRenderer", "Filter length for generic sound path channel too long, cropping." );
Jonas Stienen's avatar
Jonas Stienen committed
742 743 744 745 746 747 748
	
	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 )
		{
749
			ITAUPConvolution* pConvolver( pPath->vpFIRConvolver[ iChannelIndex ] );
750 751
			ITAUPFilter* pFilter = pConvolver->RequestFilter();
			pFilter->Zeros();
752
			int iLength = ( std::min )( m_iIRFilterLengthSamples, sbIR.length() );
753 754 755
			pFilter->Load( sbIR.data(), iLength );
			pConvolver->ExchangeFilter( pFilter );
			pFilter->Release();
Jonas Stienen's avatar
Jonas Stienen committed
756 757 758 759 760

			return;
		}
	}

761
	VA_ERROR( "PTGenericPathAudioRenderer", "Could not find sound path for sound receiver " << iListenerID << " and source " << iSourceID );
Jonas Stienen's avatar
Jonas Stienen committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775
}

void CVAPTGenericPathAudioRenderer::UpdateGlobalAuralizationMode( int )
{
	return;
}

void CVAPTGenericPathAudioRenderer::HandleProcessStream( ITADatasourceRealization*, const ITAStreamInfo* pStreamInfo )
{
	// If streaming is active, set to 1
	ctxAudio.m_iStatus = 1;

	// Sync pathes
	SyncInternalData();
776

Jonas Stienen's avatar
Jonas Stienen committed
777 778
	const CVAAudiostreamState* pStreamState = dynamic_cast< const CVAAudiostreamState* >( pStreamInfo );
	double dListenerTime = pStreamState->dSysTime;
779

Jonas Stienen's avatar
Jonas Stienen committed
780 781 782 783 784 785 786 787 788 789 790 791
	// Check for reset request
	if( ctxAudio.m_iResetFlag == 1 )
	{
		ResetInternalData();

		return;
	}
	else if( ctxAudio.m_iResetFlag == 2 )
	{
		// Reset active, skip until finished
		return;
	}
792

Jonas Stienen's avatar
Jonas Stienen committed
793 794 795 796 797 798 799 800 801 802 803 804 805
	std::map< int, CVAPTGPListener* >::iterator lit = m_mListeners.begin();
	while( lit != m_mListeners.end() )
	{
		CVAPTGPListener* pListener( lit->second );
		pListener->psfOutput->zero();
		lit++;
	}

	// Update sound pathes
	std::list< CVAPTGenericSoundPath* >::iterator spit = ctxAudio.m_lSoundPaths.begin();
	while( spit != ctxAudio.m_lSoundPaths.end() )
	{
		CVAPTGenericSoundPath* pPath( *spit );
806
		CVAReceiverState* pListenerState = ( m_pCurSceneState ? m_pCurSceneState->GetReceiverState( pPath->pListener->pData->iID ) : NULL );
807
		CVASoundSourceState* pSourceState = ( m_pCurSceneState ? m_pCurSceneState->GetSoundSourceState( pPath->pSource->pData->iID ) : NULL );
Jonas Stienen's avatar
Jonas Stienen committed
808 809 810 811 812 813 814 815 816 817 818

		if( pListenerState == nullptr || pSourceState == nullptr )
		{
			// Skip if no data is present
			spit++;
			continue;
		}

		CVASoundSourceDesc* pSourceData = pPath->pSource->pData;
		ITASampleBuffer* psbInput = pSourceData->pSignalSourceInputBuf;

819 820
		pPath->pVariableDelayLine->Process( psbInput, &m_sfTempBuffer );

821
		float fSoundSourceGain = float( pSourceState->GetVolume( m_oParams.pCore->GetCoreConfig()->dDefaultAmplitudeCalibration ) );
822 823

		for( int n = 0; n < m_iNumChannels; n++ )
Jonas Stienen's avatar
Jonas Stienen committed
824
		{
825
			ITAUPConvolution* pConvolver = pPath->vpFIRConvolver[ n ];
826
			pConvolver->SetGain( fSoundSourceGain );
827
			pConvolver->Process( m_sfTempBuffer.GetData(), ( *pPath->pListener->psfOutput )[ n ].data(), ITAUPConvolution::OUTPUT_MIX );
Jonas Stienen's avatar
Jonas Stienen committed
828 829 830 831 832
		}

		spit++;
	}

833
	// TODO: Select active sound receiver
Jonas Stienen's avatar
Jonas Stienen committed
834 835 836
	if( !m_mListeners.empty() )
	{
		CVAPTGPListener* pActiveListener = m_mListeners.begin()->second;
837 838
		for( int n = 0; n< int( m_pOutput->GetNumberOfChannels() ); n++ )
			for( int i = 0; i<int( m_pOutput->GetBlocklength() ); i++ )
839 840 841 842 843 844 845 846 847
				m_pOutput->GetWritePointer( n )[ i ] = ( *pActiveListener->psfOutput )[ n ][ i ];

		bool bDataPresent = false;
		std::vector< float > vfRMS( m_iNumChannels );
		for( int n = 0; n< int( m_pOutput->GetNumberOfChannels() ); n++ )
		{
			vfRMS[ n ] = 0.0f;
			for( int i = 0; i<int( m_pOutput->GetBlocklength() ); i++ )
				vfRMS[ n ] += pow( std::abs( m_pOutput->GetWritePointer( n )[ i ] ), 2 );
848
			vfRMS[ n ] = std::sqrt( vfRMS[ n ] ) / m_pOutput->GetBlocklength();
849 850 851 852
			if( vfRMS[ n ] != 0.0f )
				bDataPresent = true;
		}

853
		if( m_bOutputMonitoring && int( pStreamInfo->nSamples / m_pOutput->GetBlocklength() ) % int( m_pOutput->GetSampleRate() / m_pOutput->GetBlocklength() * 2.0f ) == 0 )
854 855 856
		{
			if( bDataPresent )
			{
857
				VA_TRACE( m_oParams.sID, "RMS at active sound receiver: <Ch1," << vfRMS[ 0 ] << "> <Ch2," << vfRMS[ 1 ] << "> " );
858 859 860
			}
			else
			{
861
				VA_WARN( m_oParams.sID, "No data on output channel for active sound receiver present (empty path filter?)" );
862 863
			}
		}
Jonas Stienen's avatar
Jonas Stienen committed
864 865 866
	}
	else
	{
867 868 869
		for( int n = 0; n< int( m_pOutput->GetNumberOfChannels() ); n++ )
			for( int i = 0; i<int( m_pOutput->GetBlocklength() ); i++ )
				m_pOutput->GetWritePointer( n )[ i ] = 0.0f;
Jonas Stienen's avatar
Jonas Stienen committed
870 871 872 873 874 875 876 877 878 879 880
	}

	m_pOutput->IncrementWritePointer();

	return;
}

std::string CVAPTGenericPathAudioRenderer::HelpText() const
{
	std::stringstream ss;
	ss << std::endl;
881
	ss << " --- GenericPath renderer instance '" << m_oParams.sID << "' ---" << std::endl;
Jonas Stienen's avatar
Jonas Stienen committed
882 883 884 885 886 887 888 889
	ss << std::endl;
	ss << "[help]" << std::endl;
	ss << "If the call module struct contains a key with the name 'help', this help text will be shown and the return struct will be returned with the key name 'help'." << std::endl;
	ss << std::endl;
	ss << "[info]" << std::endl;
	ss << "If the call module struct contains a key with the name 'info', information on the static configuration of the renderer will be returned." << std::endl;
	ss << std::endl;
	ss << "[update]" << std::endl;
890 891
	ss << "For every successful path update, the VA source and sound receiver ID has to be passed like this:" << std::endl;
	ss << " receiver: <int>, the number of the sound receiver identifier" << std::endl;
Jonas Stienen's avatar
Jonas Stienen committed
892 893
	ss << " source: <int>, the number of the source identifier" << std::endl;
	ss << std::endl;
894
	ss << "Updating the path filter (impulse response in time domain) for a sound receiver and a source can be performed in two ways:" << std::endl;
Jonas Stienen's avatar
Jonas Stienen committed
895 896 897 898
	ss << " a) using a path to a multi-channel WAV file:" << std::endl;
	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). " <<
899
		"The call parameter struct does not necessarily have to contain all channels, also single channels will be updated if key is given." << std::endl;
Jonas Stienen's avatar
Jonas Stienen committed
900 901 902 903 904
	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();
}

905
CVAStruct CVAPTGenericPathAudioRenderer::GetParameters( const CVAStruct& oArgs ) const
Jonas Stienen's avatar
Jonas Stienen committed
906
{
907
	CVAStruct oReturn;
Jonas Stienen's avatar
Jonas Stienen committed
908

909
	if( oArgs.HasKey( "help" ) || oArgs.IsEmpty() )
Jonas Stienen's avatar
Jonas Stienen committed
910 911 912
	{
		// Print and return help text
		VA_PRINT( HelpText() );
913
		oReturn[ "help" ] = HelpText();
914
		return oReturn;
Jonas Stienen's avatar
Jonas Stienen committed
915 916
	}

917 918 919 920 921
	oReturn[ "numchannels" ] = m_iNumChannels;
	oReturn[ "irfilterlengthsamples" ] = m_iIRFilterLengthSamples;
	oReturn[ "numpaths" ] = int( m_lSoundPaths.size() );
	return oReturn;
}
Jonas Stienen's avatar
Jonas Stienen committed
922

923 924 925 926 927
void CVAPTGenericPathAudioRenderer::SetParameters( const CVAStruct& oArgs )
{
	// Update
	if( oArgs.HasKey( "receiver" ) == false || oArgs.HasKey( "source" ) == false )
		VA_EXCEPT2( INVALID_PARAMETER, "PrototypeGenericPath filter update requires a receiver and a source identifier" );
Jonas Stienen's avatar
Jonas Stienen committed
928

929 930
	int iReceiverID = oArgs[ "receiver" ];
	int iSourceID = oArgs[ "source" ];
931

932 933 934
	bool bVerbose = false;
	if( oArgs.HasKey( "verbose" ) )
		bVerbose = true;
Jonas Stienen's avatar
Jonas Stienen committed
935

936 937 938 939 940 941
	if( oArgs.HasKey( "delay" ) )
	{
		const double dDelaySeconds = oArgs[ "delay" ];
		UpdateGenericSoundPath( iReceiverID, iSourceID, dDelaySeconds );
	}

942 943 944 945
	if( oArgs.HasKey( "filepath" ) )
	{
		const std::string& sFilePathRaw( oArgs[ "filepath" ] );
		std::string sFilePath = m_pCore->FindFilePath( sFilePathRaw );
Jonas Stienen's avatar
Jonas Stienen committed
946

947 948 949 950
		if( oArgs.HasKey( "channel" ) )
		{
			int iChannelNumber = oArgs[ "channel" ];
			UpdateGenericSoundPath( iReceiverID, iSourceID, iChannelNumber - 1, sFilePath );
Jonas Stienen's avatar
Jonas Stienen committed
951 952 953
		}
		else
		{
954 955 956 957 958 959 960 961 962 963 964 965 966
			UpdateGenericSoundPath( iReceiverID, iSourceID, sFilePath );
		}

		std::stringstream ssVerboseText;
		if( bVerbose )
		{
			ssVerboseText << "Updated sound path <L" << iReceiverID << ", S" << iSourceID << "> using (unrolled) file path '" << sFilePath << "'";
			VA_PRINT( ssVerboseText.str() );
		}
	}
	else
	{
		ITASampleBuffer sbIR( m_iIRFilterLengthSamples );
967
		for( int n = 0; n < int( m_pOutput->GetNumberOfChannels() ); n++ )
968 969 970 971
		{
			std::string sChKey = "ch" + IntToString( n + 1 );
			if( !oArgs.HasKey( sChKey ) )
				continue;
972 973

			const CVAStructValue& oValue( oArgs[ sChKey ] );
974 975 976 977 978 979
			int iNumSamples = 0;

			if( oValue.GetDatatype() == CVAStructValue::DATA )
			{
				int iBytes = oValue.GetDataSize();
				iNumSamples = iBytes / int( sizeof( float ) ); // crop overlapping bytes for safety (by integer division)
Jonas Stienen's avatar
Jonas Stienen committed
980

981
				if( iNumSamples > m_iIRFilterLengthSamples )
Jonas Stienen's avatar
Jonas Stienen committed
982
				{
983 984
					VA_WARN( "PTGenericPathAudioRenderer", "Given IR filter too long, cropping to fit internal filter length." );
					iNumSamples = m_iIRFilterLengthSamples;
Jonas Stienen's avatar
Jonas Stienen committed
985 986
				}

987
				const float* pfData = ( const float* ) ( oValue.GetData() );
988 989 990 991 992 993
				sbIR.Zero();
				sbIR.write( pfData, iNumSamples );
			}
			else if( oValue.GetDatatype() == CVAStructValue::SAMPLEBUFFER )
			{
				const CVASampleBuffer& oSampleBuffer = oValue;
Jonas Stienen's avatar
Jonas Stienen committed
994

995
				iNumSamples = oSampleBuffer.GetNumSamples(); // crop overlapping bytes for safety (by integer division)
Jonas Stienen's avatar
Jonas Stienen committed
996

997
				if( iNumSamples > m_iIRFilterLengthSamples )
Jonas Stienen's avatar
Jonas Stienen committed
998
				{
999 1000
					VA_WARN( "PTGenericPathAudioRenderer", "Given IR filter too long, cropping to fit internal filter length." );
					iNumSamples = m_iIRFilterLengthSamples;
Jonas Stienen's avatar
Jonas Stienen committed
1001
				}
1002 1003 1004

				sbIR.Zero();
				sbIR.write( oSampleBuffer.GetDataReadOnly(), iNumSamples );
Jonas Stienen's avatar
Jonas Stienen committed
1005 1006
			}

1007
			UpdateGenericSoundPath( iReceiverID, iSourceID, n, sbIR );
Jonas Stienen's avatar
Jonas Stienen committed
1008

1009 1010 1011
			std::stringstream ssVerboseText;
			if( bVerbose )
			{
1012
				ssVerboseText << "Updated sound path <L" << iReceiverID << ", S" << iSourceID << ", C" << n + 1 << "> with " << iNumSamples << " new samples";
1013 1014
				VA_PRINT( ssVerboseText.str() );
			}
Jonas Stienen's avatar
Jonas Stienen committed
1015 1016 1017 1018
		}
	}
}

1019
#endif // VACORE_WITH_RENDERER_PROTOTYPE_GENERIC_PATH