Adding code for non-uniform partitioned convolution

parent 5cd20b7c
......@@ -49,10 +49,55 @@ set( ITAConvolutionSources
)
if( ITA_CONVOLUTION_WITH_NUPCONV )
list( APPEND ITAConvolutionHeader "include/LLC.h" "include/LLCDefinitions.h" "include/LLCFilter.h" )
include( "src/LLC/_SourceFiles.cmake" )
list( APPEND ITAConvolutionHeader "include/ITANUPConvolution.h" "include/ITANUPFilter.h" )
set( ITANUPSources
"src/ITADirectConvolution.cpp"
"src/ITADirectConvolutionImpl.cpp"
"src/ITADirectConvolutionImpl.h"
"src/ITANUPCEventLog.cpp"
"src/ITANUPCEventLog.h"
"src/ITANUPCFade.cpp"
"src/ITANUPCFade.h"
"src/ITANUPCFilterSegmentation.cpp"
"src/ITANUPCFilterSegmentation.h"
"src/ITANUPCHelpers.cpp"
"src/ITANUPCHelpers.h"
"src/ITANUPCInputBuffer1.cpp"
"src/ITANUPCInputBuffer1.h"
"src/ITANUPCOutputBuffer.cpp"
"src/ITANUPCOutputBuffer.h"
"src/ITANUPCPerformanceProfile.cpp"
"src/ITANUPCPerformanceProfile.h"
"src/ITANUPCStage.cpp"
"src/ITANUPCStage.h"
"src/ITANUPCStageInfo.h"
"src/ITANUPCStageStatistic.h"
"src/ITANUPCTask.cpp"
"src/ITANUPCTask.h"
"src/ITANUPCTaskQueue.cpp"
"src/ITANUPCTaskQueue.h"
"src/ITANUPCUFilter.cpp"
"src/ITANUPCUFilter.h"
"src/ITANUPCUFilterPool.cpp"
"src/ITANUPCUFilterPool.h"
"src/ITANUPCUtils.cpp"
"src/ITANUPCUtils.h"
"src/ITANUPConvolution.cpp"
"src/ITANUPConvolutionImpl.cpp"
"src/ITANUPConvolutionImpl.h"
"src/ITANUPFilterComponent.h"
"src/ITANUPFilterImpl.h"
"src/ITANUPartitioningScheme.cpp"
"src/ITANUPartitioningScheme.h"
"src/ITAUPConvolution.cpp"
"src/ITAUPFilter.cpp"
"src/ITAUPFilterPool.cpp"
)
list( APPEND ITAConvolutionSources ${ITANUPSources} )
endif( )
# compiler settings
if( ITA_VISTA_BUILD_STATIC )
add_definitions( -DVISTABASE_STATIC -DVISTAMATH_STATIC -DVISTAASPECTS_STATIC -DVISTATOOLS_STATIC -DVISTAINTERPROCCOMM_STATIC )
......@@ -91,6 +136,20 @@ vista_create_default_info_file( ITAConvolution )
set_property( TARGET ITAConvolution PROPERTY FOLDER "ITACoreLibs" )
# benchmarks
if( ITA_CORE_LIBS_WITH_BENCHMARKS )
set( ITACONVOLUTION_COMMON_BUILD TRUE )
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks" )
endif( )
# profiler
if( ITA_CORE_LIBS_WITH_PROFILERS )
set( ITACONVOLUTION_COMMON_BUILD TRUE )
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/profilers" )
endif( )
# apps
if( ITA_CORE_LIBS_WITH_APPS )
set( ITACONVOLUTION_COMMON_BUILD TRUE )
......
#include "ITANUPCFilterComponentBenchmark.h"
#include <cmath>
#include <ITAFastMath.h>
#include <ITANUPConvolution.h>
#include <stdio.h>
#include <cstdlib>
#include <time.h>
#include <vector>
#include <ITAHPT.h>
#include <ITAStopWatch.h>
using namespace std;
// Puffergren welche von Interesse sind (0 signalisiert das Ende der Liste}
unsigned int buffersizes[] = { 64, 128, 256, 512, 0 };
//unsigned int buffersizes[] = {256, 0};
// IR-Lngen in Sekunden welche von Interesse sind (0 signalisiert das Ende der Liste}
//double ir_durations[] = {1.0, 0};
//double ir_durations[] = {0.1, 0.25, 0.5, 1.0, 1.5, 2.0, 0};
double ir_durations[] = { 0.1, 0.25, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 0 };
// IR-Lngen fr segmentierten Test
double segload_ir_durations[] = { 0.5, 1.0, 1.5, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 0 };
// Lnge des ersten Segments (Samples)
unsigned int first_part_length = 7000; // Samples
const double samplerate = 44100.0;
void benchmarkFilterComponentCreation()
{
vector<double> vIRDurations;
unsigned int i = 0;
while( ir_durations[ i ] != 0 ) vIRDurations.push_back( ir_durations[ i++ ] );
vector<unsigned int> vBuffersizes;
i = 0;
while( buffersizes[ i ] != 0 ) vBuffersizes.push_back( buffersizes[ i++ ] );
unsigned int n = 30;
ITAHPT_init();
unsigned int j;
unsigned int k; // Schleifenzhler
unsigned int b; // Puffergre
double d; // Dauer der Impulsantwort
ITAStopWatch sw;
vector< vector<double> > results;
// Zufallsgenerator initialisieren
srand( ( unsigned int ) time( NULL ) );
for( i = 0; i < vBuffersizes.size(); i++ )
{
b = vBuffersizes[ i ];
vector<double> v;
for( j = 0; j < vIRDurations.size(); j++ )
{
d = vIRDurations[ j ];
// Anzahl der Samples der IR bestimmen
unsigned int l = ( unsigned int ) ceil( d*samplerate );
// Falter erzeugen
ITANUPC::IConvolution* pConv = ITANUPC::IConvolution::create( samplerate, 1, b, l );
float* pfLeft = fm_falloc( l, false );
float* pfRight = fm_falloc( l, false );
// Speicher mit Zufallszahlen initialisieren
for( k = 0; k < l; k++ )
pfLeft[ k ] = pfRight[ k ] = ( float ) rand() / ( float ) RAND_MAX;
// Filterkomponente erzeugen
ITANUPC::CFilterComponent* pFC = pConv->createFilterComponent( 0, l, 0, 0, 0, 0 );
// Mess-Vorlauf zur Einspielung des Systems
for( k = 0; k < 3; k++ );
{
sw.start();
ITANUPC::CFilterComponent* pFC = pConv->createFilterComponent( 0, l, pfLeft, l, pfRight, l );
sw.stop();
pFC->destroy();
}
// Eigentliche Messschleife:
sw.reset();
for( k = 0; k < n; k++ )
{
sw.start();
ITANUPC::CFilterComponent* pFC = pConv->createFilterComponent( 0, l, pfLeft, l, pfRight, l );
sw.stop();
pFC->destroy();
}
// Speicher freigeben und Falter lschen
fm_free( pfLeft );
fm_free( pfRight );
delete pConv;
// Infos ausgeben
printf( "\nbs = %d, dur = %0.2f s = %d samples\nmin = %0.12f s\navg = %0.12f s\nmax = %0.12f s\ncyc = %d\n\n",
b, d, l, sw.minimum(), sw.mean(), sw.maximum(), sw.cycles() );
v.push_back( sw.mean() );
}
results.push_back( v );
}
// Ergebnisse ausgeben
printf( "\n\n\nErzeugen von FCs:\n\n" );
printf( "IRL / BS \t" );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%6d \t", vBuffersizes[ j ] );
printf( "\n" );
for( i = 0; i < vIRDurations.size(); i++ )
{
printf( "%0.3f s \t", vIRDurations[ i ] );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%0.6f s \t", results[ j ][ i ] );
printf( "\n" );
}
}
void benchmarkFilterComponentLoad() {
vector<double> vIRDurations;
unsigned int i = 0;
while( ir_durations[ i ] != 0 )
vIRDurations.push_back( ir_durations[ i++ ] );
vector<unsigned int> vBuffersizes;
i = 0;
while( buffersizes[ i ] != 0 )
vBuffersizes.push_back( buffersizes[ i++ ] );
unsigned int n = 30;
ITAHPT_init();
unsigned int j;
unsigned int k; // Schleifenzhler
unsigned int b; // Puffergre
double d; // Dauer der Impulsantwort
ITAStopWatch sw;
vector< vector<double> > results;
// Zufallsgenerator initialisieren
srand( time( NULL ) );
for( i = 0; i < vBuffersizes.size(); i++ )
{
b = vBuffersizes[ i ];
vector<double> v;
for( j = 0; j < vIRDurations.size(); j++ )
{
d = vIRDurations[ j ];
// Anzahl der Samples der IR bestimmen
unsigned int l = ( unsigned int ) ceil( d*samplerate );
// Falter erzeugen
ITANUPC::IConvolution* pConv = ITANUPC::IConvolution::create( NULL, samplerate, b, l );
float* pfLeft = fm_falloc( l, false );
float* pfRight = fm_falloc( l, false );
// Speicher mit Zufallszahlen initialisieren
for( k = 0; k < l; k++ )
pfLeft[ k ] = pfRight[ k ] = ( float ) rand() / ( float ) RAND_MAX;
// Filterkomponente erzeugen
ITANUPC::CFilterComponent* pFC = pConv->createFilterComponent( 0, l, 0, 0, 0, 0 );
// Mess-Vorlauf zur Einspielung des Systems
for( k = 0; k < 3; k++ )
{
sw.start();
pFC->load( pfLeft, l, pfRight, l );
sw.stop();
}
// Eigentliche Messschleife:
sw.reset();
for( k = 0; k < n; k++ )
{
sw.start();
pFC->load( pfLeft, l, pfRight, l );
sw.stop();
}
// Speicher freigeben und Falter lschen
fm_free( pfLeft );
fm_free( pfRight );
delete pConv;
// Infos ausgeben
printf( "\nbs = %d, dur = %0.2f s = %d samples\nmin = %0.12f s\navg = %0.12f s\nmax = %0.12f s\ncyc = %d\n\n",
b, d, l, sw.minimum(), sw.mean(), sw.maximum(), sw.cycles() );
v.push_back( sw.mean() );
}
results.push_back( v );
}
// Ergebnisse ausgeben
printf( "\n\n\nLaden von FCs:\n\n" );
printf( "IRL / BS \t" );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%6d \t", vBuffersizes[ j ] );
printf( "\n" );
for( i = 0; i < vIRDurations.size(); i++ )
{
printf( "%0.3f s \t", vIRDurations[ i ] );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%0.6f s \t", results[ j ][ i ] );
printf( "\n" );
}
}
void benchmarkFilterComponentSegLoad()
{
vector<double> vIRDurations;
unsigned int i = 0;
while( segload_ir_durations[ i ] != 0 ) vIRDurations.push_back( segload_ir_durations[ i++ ] );
vector<unsigned int> vBuffersizes;
i = 0;
while( buffersizes[ i ] != 0 ) vBuffersizes.push_back( buffersizes[ i++ ] );
unsigned int n = 30;
ITAHPT_init();
unsigned int j;
unsigned int k; // Schleifenzhler
unsigned int b; // Puffergre
double d; // Dauer der Impulsantwort
ITAStopWatch sw;
vector< vector<double> > results;
// Zufallsgenerator initialisieren
srand( time( NULL ) );
for( i = 0; i < vBuffersizes.size(); i++ )
{
b = vBuffersizes[ i ];
vector<double> v;
for( j = 0; j < vIRDurations.size(); j++ )
{
d = vIRDurations[ j ];
// Anzahl der Samples der IR bestimmen
unsigned int l = ( unsigned int ) ceil( d*samplerate );
// Falter erzeugen
ITANUPC::IConvolution* pConv = ITANUPC::IConvolution::create( NULL, samplerate, b, l );
float* pfLeft = fm_falloc( l, false );
float* pfRight = fm_falloc( l, false );
// Speicher mit Zufallszahlen initialisieren
for( k = 0; k < l; k++ )
pfLeft[ k ] = pfRight[ k ] = ( float ) rand() / ( float ) RAND_MAX;
unsigned int L1 = pConv->getUpperSegmentOffset( first_part_length );
unsigned int L2 = l - L1;
printf( "\nErstes Segment: %d Samples\n", L1 );
printf( "Zweites Segment: %d Samples\n", L2 );
printf( "Anpasste Laeng.: %d Samples\n\n", L1 + L2 );
// Filterkomponente erzeugen
ITANUPC::CFilterComponent* pFC1 = pConv->createFilterComponent( 0, L1, 0, 0, 0, 0 );
ITANUPC::CFilterComponent* pFC2 = pConv->createFilterComponent( 0, L2, 0, 0, 0, 0 );
// Mess-Vorlauf zur Einspielung des Systems
for( k = 0; k < 3; k++ )
{
sw.start();
pFC1->load( pfLeft, L1, pfRight, L1 );
pFC2->load( pfLeft, L2, pfRight, L2 );
sw.stop();
}
// Eigentliche Messschleife:
sw.reset();
for( k = 0; k < n; k++ )
{
sw.start();
pFC1->load( pfLeft, L1, pfRight, L1 );
pFC2->load( pfLeft, L2, pfRight, L2 );
sw.stop();
}
// Speicher freigeben und Falter lschen
fm_free( pfLeft );
fm_free( pfRight );
delete pConv;
// Infos ausgeben
printf( "\nbs = %d, dur = %0.2f s = %d samples\nmin = %0.12f s\navg = %0.12f s\nmax = %0.12f s\ncyc = %d\n\n",
b, d, l, sw.minimum(), sw.mean(), sw.maximum(), sw.cycles() );
v.push_back( sw.mean() );
}
results.push_back( v );
}
// Ergebnisse ausgeben
printf( "\n\n\nLaden von FCs:\n\n" );
printf( "IRL / BS \t" );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%6d \t", vBuffersizes[ j ] );
printf( "\n" );
for( i = 0; i < vIRDurations.size(); i++ )
{
printf( "%0.3f s \t", vIRDurations[ i ] );
for( j = 0; j < vBuffersizes.size(); j++ ) printf( "%0.6f s \t", results[ j ][ i ] );
printf( "\n" );
}
}
\ No newline at end of file
#ifndef __FILTER_COMPONENT_BENCHMARK__
#define __FILTER_COMPONENT_BENCHMARK__
void benchmarkFilterComponentCreation();
void benchmarkFilterComponentLoad();
void benchmarkFilterComponentSegLoad();
#endif
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_NUP_CONVOLUTION
#define IW_ITA_NUP_CONVOLUTION
#include <ITAConvolutionDefinitions.h>
#include <ITANUPFilter.h>
// ITA includes
#include <ITAUncopyable.h>
#include <ITATimeSeriesAnalyzer.h>
// STL includes
#include <cstdint>
#include <string>
#include <vector>
class ITADatasource;
//! Non-uniform partitioned convolution (NUPC) engine namespace
/**
* Uses block convolution with arbitrary segmentation for efficient impulse response convolution
* of long FIR filters, like those in room acoustics.
*
*/
namespace ITANUPC
{
//! Datentyp welcher fr Zyklen (Zeitpunkte) verwendet wird
typedef uint64_t CYCLE;
// Literals
const unsigned int NUPC_AUTO = 0;
//! Fading function
typedef enum
{
LINEAR = 0, // Linear
COSINE_SQUARE // Squared cosine function
} FadingFunction;
//! Segmentation schemes
typedef enum
{
AUTO = 0,
UNIFORM,
GARDNER,
FROM_FILE,
} SegmentationScene;
//! Runtime infos on stages
typedef struct
{
int iPartLength;
int iNumDropouts;
ITATimeseriesAnalyzerData< double > tadExecutionDeadtime;
ITATimeseriesAnalyzerData< double > tadExecutionTime;
ITATimeseriesAnalyzerData< int > tadTaskInterruptions;
} StageRuntimeInfo;
//! General runtime info
typedef struct
{
ITANUPC::CYCLE tCycles; //!< Number of processed blocks
bool bError;
double dOverallRuntime;
double dRuntimeCyclesMinimum;
double dRuntimeCyclesAverage;
double dRuntimeCyclesMaximum;
double dRuntimeCyclesVariance;
double dRuntimeDirectAnswerAverage;
double dRuntimeDirectAnswerMaximum;
double dRuntimeDirectAnswerStdDeviation;
double dDirectAnswerLoadAverage; //!< load average in buffer switch callback
double dLoadAverage;
int iNumDropouts;
std::vector< ITANUPC::StageRuntimeInfo > vStageInfos;
int iBaseExp2; //!< Smallest base-two stage
} RuntimeInfo;
//! Parameter (purely inline)
class CConvolutionParameter
{
public:
double dSampleRate;
int iBlockLength;
int iImpulseResponseFilterLength; //!< (Maximum) filter length
SegmentationScene eSegmentationScheme;
std::string sSegmentationFile;
std::string sParam;
int nThreads;
bool bSynchronous; // Forces to process all tasks in callback function
inline CConvolutionParameter()
: dSampleRate( 0 )
, iBlockLength( 0 )
, iImpulseResponseFilterLength( 0 ),
eSegmentationScheme( AUTO )
, nThreads( NUPC_AUTO ),
bSynchronous( false )
{};
//! Convolution parameter constructor
/**
* \param[in] dSampleRate Abtastrate [Hz] (>0)
* \param[in] iBlockLength Blocklnge (Potenz n=k^2 zur Basis 2 und kleinstmglicher Wert 4)
* \param[in] iImpulseResponseFilterLength Maximale Filterlnge [Anzahl Filterkoeffizienten]
*/
inline CConvolutionParameter( const double dSampleRate, const int iBlockLength, const int iImpulseResponseFilterLength )
: dSampleRate( dSampleRate )
, iBlockLength( iBlockLength )
, iImpulseResponseFilterLength( iImpulseResponseFilterLength )
, eSegmentationScheme( AUTO )
, nThreads( NUPC_AUTO )
, bSynchronous( false )
{};
};
//! Input description class
class IInput
{
public:
virtual int GetID() const = 0;
// Returns the buffer of the input
// Input data must be put in here (size = blocklength)
virtual float* GetBuffer() const = 0;
// Puts one block of samples into the input
// Note: Nullptr may be passed and is treated as nullsamples
virtual void Put( const float* pfData ) = 0;
//virtual float GetGain() const = 0;
//virtual void SetGain(float fGain) = 0;
protected:
inline virtual ~IInput() {};
};
//! Output description class
class IOutput
{
public:
virtual int GetID() const = 0;
virtual const float* GetBuffer() const = 0;
// Copies the latest block of samples into the given destination buffer
virtual void Get( float* pfData ) = 0;
protected:
virtual inline ~IOutput() {};
};
//! Interface for non-uniform partitioned realtime convolution
class ITA_CONVOLUTION_API IConvolution : public ITAUncopyable
{
public:
//! Convolution instance factory
/**
* Create non-uniform partitioned convolution engine with thie factory method.
* sampling rate and block length can not be changed during runtime. Convolves
* two channels.
* Throws ITANUPC::Exception on failure.
*/
static ITANUPC::IConvolution* Create( const CConvolutionParameter& oParams );
virtual ~IConvolution();
virtual ITANUPC::CConvolutionParameter GetParams() const = 0;
virtual void Reset() = 0;
//! Audio stream callback function, processes a block of samples
virtual void Process() = 0;
//! Property bit flag getter (debug, SSE, etc) [deprecated]
static unsigned int GetFlags();
//! Get runtime info
/**
* @param[out] pRuntimeInfo pointer to runtime info instance
*/
virtual void GetRuntimeInfo( ITANUPC::RuntimeInfo* pRuntimeInfo ) = 0;
virtual void ResetRuntimeInfo() = 0;
virtual void GetInputs( std::vector< ITANUPC::IInput* >& vpInputs ) const = 0;
virtual ITANUPC::IInput* AddInput() = 0;
virtual void RemoveInput( ITANUPC::IInput* pInput, const bool bRemoveImmediately ) = 0;
virtual void GetOutputs( std::vector< ITANUPC::IOutput* >& vpOutputs ) const = 0;
//! Request empty filter
virtual ITANUPC::IFilter* RequestFilter() = 0;
//! Exchange filter