Refactoring DSMBC to ITAUP (uniform-partitioned ...)

parent 297ebd0d
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_ITA_CONVOLUTION
#define INCLUDE_WATCHER_ITA_CONVOLUTION
#include <ITAConvolutionDefinitions.h>
#include <ITASampleBuffer.h>
#include <ITASampleFrame.h>
#include <ITAUncopyable.h>
// Vorwärtsdeklarationen
class ITADirectConvolutionImpl;
/**
* Diese Klasse definiert die Schnittstelle für schnelle (offline) Faltungen.
* Die Funktionalität besteht darin zwei Signale möglichst schnell und effizient
* miteinander zu falten. Die Eingaben und Ausgaben werden im Zeitbereich übergeben.
* Instanzen der Klasse repräsentieren Faltungen eines bestimmten Typs
* (bestimmte Eingabeparameter). Damit die maximale Leistung erreicht werden kann,
* müssen vorher die Faltungsoperationen geplant werden.
*/
class ITA_CONVOLUTION_API ITADirectConvolution : public ITAUncopyable
{
public:
// TODO: Flags static const int
static const int DEFAULT_FLAGS = 0;
//! Standardkonstruktor
ITADirectConvolution();
//! Initialisierungskonstruktor (Arrays)
ITADirectConvolution( const int src1length, const int src2length, const int flags = DEFAULT_FLAGS );
//! Destruktor
virtual ~ITADirectConvolution();
//! Planungsmethode (Arrays)
void Plan( const int src1length, const int src2length, const int flags = DEFAULT_FLAGS );
//! Gibt zurück ob die Faltung schon geplant ist
bool IsPlanned() const;
//! Faltungsmethode (Arrays)
virtual void Convolve( const float* src1, const int src1length, const float* src2, const int src2length, float* dest, const int destlength );
private:
ITADirectConvolutionImpl* m_pImpl;
};
#endif // INCLUDE_WATCHER_ITA_CONVOLUTION
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_DSMBC_CONVOLVER
#define INCLUDE_WATCHER_DSMBC_CONVOLVER
#include <ITAConvolutionDefinitions.h>
#include <ITACriticalSection.h>
#include <ITAUncopyable.h>
#include <vector>
#include <ITAUPTrigger.h>
#include <tbb/concurrent_queue.h>
#include <atomic>
// Vorwürtsdeklarationen
class ITAUPFilter;
class ITAUPFilterPool;
class ITAFFT;
/**
* Diese Klasse realisiert einen schnellen (einkanaligen) Falter (dynamic single-channel multi-block convolver),
* welcher die effiziente Overlap-Save Blockfaltung zusammen mit einer frequency-domain delay-line (FDL)
* benutzt um eine Impulsantwortet zu falten, welche in mehrere gleichlange Teile zerlegt wird.
* Dieser Ansatz ermüglicht eine wesentlich grüüere Leistung als ein einfacher Blockfaltungsansatz,
* bei welchem die komplette Impulsantwort mit einem Block gefaltet werden. Der Falter ist dynamisch,
* d.h. er erlaubt den Austausch der Filter zur Laufzeit bzw. Streamingzeit. Der Austausch der Filter
* geschieht entweder durch hartes Umschalten oder überblendung im Zeitbereich.
*
* Nebenlüufigkeit & Synchronisation:
*
* Folgende Methoden sind synchronisiert:
*
* - getFilterPool, setFilterPool, requestFilter, releaseFilter => Blocking aber leichtgewichtig, reentrant
* - getFilterExchangeMode, setFilterExchangeMode, getFilterCrossfadeLength, setFilterCrossfadeLength,
* getActiveFilter, exchangeFilter => Non-blocking und wait-free, reentrant
* - process => Non-blocking, wait-free aber NICHT REENTRANT (!!)
*
* Alle anderen Methoden sind nicht synchronisiert.
*
* Müchtest Du mehr zu den Details wissen? Frag mich! -> Frank.Wefers@akustik.rwth-aachen.de
*/
class ITA_CONVOLUTION_API ITAUPConvolution : public ITAUncopyable
{
public:
//! Austausch-Modi
enum
{
SWITCH = 0, //!< Hartes Umschalten
CROSSFADE_LINEAR, //!< Lineare Kreuzblende
CROSSFADE_COSINE_SQUARE //!< Cosinus-Quadrat Kreuzblende
};
//! Ausgabe-Modi
enum
{
OUTPUT_OVERWRITE = 0, //!< Daten im Ausgabepuffer mit neuen Daten überschreiben
OUTPUT_MIX, //!< Neue Daten in den Ausgabepuffer einmischen
};
static const int AUTO = -1;
//! Standard-Konstruktor
/**
* Erzeugt einen Falter der seinen eigenen Filterpool betreibt
*/
ITAUPConvolution();
//! Initialierungs-Konstruktor
/**
* Erzeugt einen Falter.
*
* \param iBlocklength Blocklänge [in Samples]
* \param iMaxFilterlength Maximale Filterlänge [Anzahl Filterkoeffizienten]
*/
ITAUPConvolution( const int iBlocklength, const int iMaxFilterlength, ITAUPFilterPool* pFilterPool = NULL );
//! Destruktor
virtual ~ITAUPConvolution();
//! Initialisieren
/**
* Initialisiert einen Falter.
*
* \param iBlocklength Blocklänge [in Samples]
* \param iMaxFilterlength Maximale Filterlänge [Anzahl Filterkoeffizienten]
*/
void init( const int iBlocklength, const int iFilterLength );
//! Blocklänge zurückgeben
int getBlocklength() const;
//! Maximale Filterlänge [Anzahl Filterkoeffizienten] zurückgeben
int getMaxFilterlength() const;
//! Trigger für den Filteraustausch zurückgeben (NULL falls keiner zugeordnet)
const ITAUPTrigger* getFilterExchangeTrigger() const;
//! Trigger für den Filteraustausch setzen
void setFilterExchangeTrigger( const ITAUPTrigger* pTrigger );
//! Filteraustausch-Modus zurückgeben
int getFilterExchangeMode();
//! Filteraustausch-Modus setzen
void setFilterExchangeMode( const int iMode );
//! überblendlänge [Samples] des Filteraustauschs zurückgeben
int getFilterCrossfadeLength();
//! überblendlänge [Samples] für den Filteraustausch setzen
void setFilterCrossfadeLength( const int iLength );
//! Verstärkung zurückgeben
float getGain() const;
//! Verstärkung setzen
// Hinweis: Falls bSetImmediately==true gesetzt, wird die Verstärkung nicht
// dynamisch angepasst, sondern hart auf den angegebenen Wert gesetzt.
void setGain( const float fGain, const bool bSetImmediately = false );
//! Filterpool zurückgeben
ITAUPFilterPool* getFilterPool() const;
//! Filterpool setzen
/**
* NULL => Falter-eigenen Pool benutzen
*/
void setFilterPool( ITAUPFilterPool* pFilterPool );
//! Freies Filter anfordern
ITAUPFilter* requestFilter();
//! Filter wieder zur anderweitigen Benutzung freigeben
void releaseFilter( ITAUPFilter* pFilter );
//! Aktives Filter zurückgeben
/**
* Das aktive Filter ist jenes, welches der Falter momentan benutzt.
*/
ITAUPFilter* getActiveFilter();
//! Filter austauschen
/**
* Hinweis: Nullzeiger => Aktives Filter entfernen
*/
void exchangeFilter( ITAUPFilter* pNewFilter, const int iExchangeMode = AUTO, const int iCrossfadeLength = AUTO );
//! Löscht alle internen Samplepuffer
void clear();
//! Faltungsmethode
/**
* Nimmt einen neuen Block Eingangsdaten entgegen und faltet ihn mit der Impulsantwortet.
* Das (partielle) Ausgangssignal wird als Block im Zielarray gespeichert.
*
* \param pfInputData Array der Eingangsdaten (Exakt soviele Elemente wie die Blocklänge ist)
* \param pfOutputData Array der Ausgangsdaten (Exakt soviele Elemente wie die Blocklänge ist)
* \param iOutputMode Ausgabemodus (überschreiben oder Einmischen)
*/
// TODO: Hier wird Austausch durchgeführt!
void process( const float* pfInputData, float* pfOutputData, const int iOutputMode = OUTPUT_OVERWRITE );
//! Faltungsmethode (Erweitert)
/**
* Nimmt einen neuen Block Eingangsdaten entgegen und faltet ihn mit der Impulsantwortet.
* Das (partielle) Ausgangssignal wird als Block im Zielarray gespeichert.
*
* \param pfInputData Array der Eingangsdaten
* \param iInputLength Anzahl der Samples in den Eingangsdaten
* \param pfOutputData Array der Ausgangsdaten (Exakt soviele Elemente wie die Blocklänge ist)
* \param iOutputLength Anzahl der Samples in den Ausgabedaten
* \param iOutputMode Ausgabemodus (überschreiben oder Einmischen)
*/
void process( const float* pfInputData, const int iInputLength, float* pfOutputData, const int iOutputLength, const int iOutputMode = OUTPUT_OVERWRITE );
private:
typedef struct
{
ITAUPFilter* pFilter;
int iExchangeMode;
int iCrossfadeLength;
} FilterUpdate;
int m_iBlocklength;
int m_iMaxFilterlength;
std::atomic< int > m_iExchangeMode; // Austauschmodus
std::atomic< int > m_iCrossfadeLength; // überblendlänge
std::atomic< float > m_fCurrentGain; // Aktuelle Verstärkung
std::atomic< float > m_fNewGain; // Gewünschte Verstärkung
std::atomic< ITAUPFilter* > m_pCurrentFilter; // Aktives Filter
tbb::strict_ppl::concurrent_queue<FilterUpdate> m_qExchangeFilters; // Queue: Nächste Filter zum Austausch
float *m_pfTimeInputBuffer; // Eingangspuffer (Zeitbereich)
float *m_pfTimeOutputBuffer1; // Ausgangspuffer (Zeitbereich)
float *m_pfTimeOutputBuffer2; // Ausgangspuffer (Zeitbereich)
float *m_pfFreqAuxBuffer; // Hilfspuffer (Frequenz-bereich)
float *m_pfFreqMixdownBuffer; // Mischpuffer (Frequenz-bereich)
int m_iFreqCoeffs; // Anzahl DFT-Koeffizienten (Symetrien eingerechnet)
std::vector< float* > m_vpfFreqDelayLine; // Frequency-domain delay line (FDL)
ITAFFT *m_pFFT, *m_pIFFT; // FFT, IFFT der Daten
ITACriticalSection m_csPool; // Exklusiver Zugriff auf den Filterpool
ITAUPFilterPool* m_pCurrentPool; // Gesetzer Filterpool
ITAUPFilterPool* m_pOwnPool; // Falter-eigener Filterpool
ITAUPTriggerWatch m_oTriggerWatch; // TriggerWatch für den Filteraustausch
void copyOutputApplyGain1( float* pfDest, const float* pfSrc, const int iNumSamples, const int iOutputMode );
void copyOutputApplyGain2( float* pfDest, const float* pfSrc1, const float* pfSrc2, const int iOutputLength, const int iCrossfadeLength, const int iOutputMode );
};
#endif // INCLUDE_WATCHER_DSMBC_CONVOLVER
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_DSMBC_FILTER
#define INCLUDE_WATCHER_DSMBC_FILTER
#include <ITAConvolutionDefinitions.h>
#include <ITACriticalSection.h>
#include <ITATypes.h>
#include <ITAUncopyable.h>
#include <atomic>
#include <vector>
// Vorwärtsdeklarationen
class ITAUPFilterPool;
class ITAFFT;
/**
* Diese Klasse realisiert Filter (d.h. Representationen von Impulsantworten im Frequenzbereich)
* für DSMBCConvolver. Solche Filter haben einen Zustand: 1) unbenutzt oder 2) momentan in einem
* Falter in Benutzung. Generell kann 1 Filter in beliebig vielen Faltern verwendet werden.
* Seine Daten dürfen aber nur modifiziert werden, wenn es nicht in Benutzung ist.
*
* Eine neue Impulsantwort wird mittels der Methode load geladen und intern transformiert.
*
* Nebenläufigkeit & Synchronisation:
*
* Instanzen der Klasse sollen nur von einem Thread zu einer Zeit benutzt werden
* (üblicherweise der Thread der die Filterdaten generiert hat). Im Sinne der Anwendung
* macht es keinen Sinn das mehrere Threads um beispielsweise die load-Methode konkurrieren.
* Deshalb unternimmt die Klasse keine Maßnahmen zur Synchronisation.
*/
class ITA_CONVOLUTION_API ITAUPFilter : public ITAUncopyable
{
public:
//! Konstruktor
ITAUPFilter( const int iBlocklength, const int iMaxFilterLength );
//! Destruktor
/**
* Hinweis: Das Objekt kann nur freigegeben werden, wenn es nicht mehr benutzt wird!
*/
virtual ~ITAUPFilter();
//! Gibt zurück ob der Filter aktuell in Benutzung ist
bool isInUse() const;
//! übergeordneten Filterpool zurückgeben (falls nicht vorhanden NULL)
ITAUPFilterPool* getParentFilterPool() const;
//! Filter übergeordneten Filterpool freigeben (falls Teil eines Filterpools)
void release();
//! Filterdaten laden
/**
* \param pfFilterData Array mit den Filterkoeffizienten
* \param iFilterLength Anzahl Filterkoeffizienten in pfFilterData (maximal iMaxFilterLength)
*
* \note Falls das Filter in Benutzung ist wird eine ITAException geworfen!
*/
void load( const float* pfFilterData, const int iFilterLength );
//! Nullen setzen
void zeros();
//! Einheitsimpuls (Dirac) setzen
void identity();
// Interne Zustandklasse, welche Referenzzähler enthält.
// Es werden immer zwei Zähler benötigt: Prep und Use.
class State
{
public:
State( ITAUPFilter* pParent );
bool isInUse() const;
void addPrep();
void removePrep();
void xchangePrep2Use();
void removeUse();
// Damit auf älteren Windows-System, welche keine DCAS-Operationen bereistellen,
// dieser zwei-elementige Zustand atomar verändert werden kann,
// muss eine Datenstruktur von 32-Bit realisiert werden.
// 16-Bit für die Zähler ist immer noch ausreichend
typedef struct
{
std::atomic< int16_t > iPrepRefCount;
std::atomic< int16_t > iUseRefCount;
} StateStruct;
ITAUPFilter* m_pParent;
StateStruct m_oState;
void modifyState( const int16_t iPrepDelta, const int16_t iUseDelta );
};
ITAUPFilterPool* m_pParent; // Übergeordneter Filterpool
int m_iBlocklength; // Blocklänge (Zeitbereich)
int m_iNumFilterParts; // Anzahl Filterteile
int m_iNumEffectiveFilterParts; // Anzahl effektiver Filterteile mit Filterkoeff. != 0
std::vector<float*> m_vpfFreqData; // Filterdaten im Frequenzbereich (DFT-Spektren der Teile)
ITAFFT* m_pFFT;
ITACriticalSection m_csReentrance; // Reentrance-Lock für die Load-Methode
State m_oState; // Zustand
friend class ITAUPConvolution;
friend class ITAUPFilterPool;
};
#endif // INCLUDE_WATCHER_DSMBC_FILTER
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_DSMBC_FILTER_POOL
#define INCLUDE_WATCHER_DSMBC_FILTER_POOL
#include <ITAConvolutionDefinitions.h>
#include <ITACriticalSection.h>
#include <ITAFFT.h>
#include <list>
// Vorwärtsdeklarationen
class ITAUPFilter;
/**
* Diese Klasse realisiert eine Verwalter für Filterobjekte und
* ermöglicht ein effizientes Filter-Management mit geringem Speicherverbrauch,
* durch die automatische Wiederverwendung von Filtern.
*
* Nebenläufigkeit & Synchronisation:
*
* Instanzen der Klasse können von beliebig vielen Threads gleichzeitig benutzt werden.
* Alle Methoden mittels synchronisiert und dürfen auch mehrfach parallel aufgerufen
* werden (reentrant). Die Synchronisation ist blocking, aber die Wartezeiten sind
* gering, da innerhalb des exklusiven Zugriffs nur kurze Operationen durchgeführt werden.
*
*/
class ITA_CONVOLUTION_API ITAUPFilterPool
{
public:
//! Konstruktor
ITAUPFilterPool( const int iBlocklength, const int iMaxFilterLength, const int iInitialSize );
//! Destruktor
/**
* Hinweis: Das Objekt kann nur freigegeben werden, wenn es nicht mehr benutzt wird!
*/
virtual ~ITAUPFilterPool();
//! Blocklänge zurückgeben
int getBlocklength() const;
//! Maximale Filterlänge [Anzahl Filterkoeffizienten] zurückgeben
int getMaxFilterlength() const;
//! Anzahl freier Filter zurückgeben
int getNumFreeFilters() const;
//! Anzahl freier Filter zurückgeben
int getNumUsedFilters() const;
//! Anzahl freier Filter zurückgeben
int getNumTotalFilters() const;
//! Freies Filter anfordern
ITAUPFilter* requestFilter();
//! Filter wieder zur anderweitigen Benutzung freigeben
/**
* Ist das Filter nicht in Benutzung, so kann es sofort weiterbenutzt werden.
* Ist das Filter in Benutzung, so wird es für die Weiterbenutzung freigebenen,
* sobald es in einem Falter (DSMBCConvolver) nicht mehr in Benutzung ist
*/
void releaseFilter( ITAUPFilter* pFilter );
private:
int m_iBlocklength;
int m_iMaxFilterLength;
std::list<ITAUPFilter*> m_lpFreeFilters; // Unbenutzte (freie) Filter im Pool
std::list<ITAUPFilter*> m_lpUsedFilters; // Benutzte Filter im Pool
std::list<ITAUPFilter*> m_lpAutoFilters; // Filter welche nicht mehr seitens den Benutzers gebraucht werden, aber noch in einem Falter benutzt werden
ITACriticalSection m_csFilters; // Exklusiver Zugriff auf die Listen
};
#endif // INCLUDE_WATCHER_DSMBC_FILTER_POOL
\ No newline at end of file
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_DSMBC_TRIGGER
#define INCLUDE_WATCHER_DSMBC_TRIGGER
#include <ITAConvolutionDefinitions.h>
#include <ITAUncopyable.h>
// Lightweight atomic trigger for synchronized filter exchanges
class ITAUPTrigger : public ITAUncopyable
{
public:
inline ITAUPTrigger() : m_iState( 0 )
{
};
// Returns the current state
inline int getState() const
{
return m_iState;
};
// Pull the trigger. Update/alter the state.
void trigger()
{
++m_iState;
};
private:
volatile int m_iState;
};
// Simple watcher class, realization update checks on a trigger
class ITAUPTriggerWatch
{
public:
inline ITAUPTriggerWatch()
: m_pTrigger( 0 )
, bFirst( true )
, m_iPrevTriggerState( 0 )
{
};
inline const ITAUPTrigger* getWatchedTrigger() const
{
return m_pTrigger;
};
inline void setWatchedTrigger( const ITAUPTrigger* pNewTrigger )
{
m_pTrigger = pNewTrigger;
bFirst = true;
};
// Logic: We always fire, if there is no trigger associated. This is needed in the convolver.
inline bool fire()
{
if( !m_pTrigger )
return true;
int iNewTriggerState = m_pTrigger->getState();
if( ( iNewTriggerState != m_iPrevTriggerState ) || bFirst )
{
m_iPrevTriggerState = iNewTriggerState;
bFirst = false;
return true;
}
return false;
};
private:
const ITAUPTrigger* m_pTrigger;
bool bFirst;
int m_iPrevTriggerState;
};
#endif // INCLUDE_WATCHER_DSMBC_TRIGGER
#include <ITADirectConvolution.h>
#include "ITADirectConvolutionImpl.h"
ITADirectConvolution::ITADirectConvolution() : m_pImpl( NULL )
{
}
ITADirectConvolution::ITADirectConvolution( const int src1length, const int src2length, const int flags )
: m_pImpl( NULL )
{
Plan( src1length, src2length, flags );
}
ITADirectConvolution::~ITADirectConvolution()
{
delete m_pImpl;
}
void ITADirectConvolution::Plan( const int src1length, const int src2length, const int flags )
{
if( m_pImpl ) {
delete m_pImpl;
m_pImpl = NULL;
}
m_pImpl = new ITADirectConvolutionImpl( src1length, src2length, flags );
}
bool ITADirectConvolution::IsPlanned() const
{
return ( m_pImpl != NULL );
}
void ITADirectConvolution::Convolve( const float* src1, const int src1length, const float* src2, const int src2length, float* dest, const int destlength )
{
if( m_pImpl )
m_pImpl->Convolve( src1, src1length, src2, src2length, dest, destlength );
}
#include "ITADirectConvolutionImpl.h"
#include <ITAUPConvolution.h>
#include <ITAUPFilter.h>
#include <ITANumericUtils.h>
ITADirectConvolutionImpl::ITADirectConvolutionImpl( const int src1length, const int src2length, const int )
: m_pConvolver( NULL )
, m_pFilter( NULL )
{
int iSignallength;
int iFilterlength;
// Krzere Eingabe = Signal, die andere das Filter
if( src1length < src2length )
{
iSignallength = src2length;
iFilterlength = src1length;
m_iFilter = 1;
}
else
{
iSignallength = src1length;
iFilterlength = src2length;
m_iFilter = 2;
}
// Blocklnge = Nchste Zweierpotenz grer der Filterlnge
m_iBlockLength = ( int ) nextPow2( ( unsigned int ) iFilterlength );
m_pConvolver = new ITAUPConvolution( m_iBlockLength, iFilterlength );
m_pConvolver->setFilterExchangeMode( ITAUPConvolution::SWITCH );
// Filter holen
m_pFilter = m_pConvolver->requestFilter();
}
ITADirectConvolutionImpl::~ITADirectConvolutionImpl()
{
delete m_pConvolver;
}
void ITADirectConvolutionImpl::Convolve( const float* src1, const int src1length, const float* src2, const int src2length, float* dest, const int destlength )
{
if( !m_pConvolver )
return;
const float* pfInput;
int iInputLength;
// Filter laden
if( m_iFilter == 1 )
{
m_pFilter->load( src1, src1length );
pfInput = src2;
iInputLength = src2length;
}
else
{
m_pFilter->load( src2, src2length );
pfInput = src1;
iInputLength = src1length;
}
m_pConvolver->exchangeFilter( m_pFilter );
// Iterativ durchfalten
int k = 0;
while( k < destlength )
{
// Restliche Samples in der Eingabe
int ri = ( std::min )( ( std::max )( iInputLength - k, 0 ), m_iBlockLength );
// Restliche Samples in der Ausgabe
int ro = ( std::min )( ( std::max )( destlength - k, 0 ), m_iBlockLength );
m_pConvolver->process( pfInput + k, ri, dest + k, ro );
k += ro;
}
}
#ifndef IW_ITA_CONVOLUTION_IMPL
#define IW_ITA_CONVOLUTION_IMPL
#include <ITADirectConvolution.h>
// Vorwrtsdeklarationen
class ITAUPConvolution;
class ITAUPFilter;
// Implementierungsklasse der Faltungsklasse ITAConvolution
class ITADi