/* * ---------------------------------------------------------------- * * ITA core libs * (c) Copyright Institute of Technical Acoustics (ITA) * RWTH Aachen University, Germany, 2015-2024 * * ---------------------------------------------------------------- * ____ __________ _______ * // / //__ ___/ // _ | * // / // / // /_| | * // / // / // ___ | * //__/ //__/ //__/ |__| * * ---------------------------------------------------------------- * */ #ifndef INCLUDE_WATCHER_ITA_UP_CONVOLVER #define INCLUDE_WATCHER_ITA_UP_CONVOLVER #include <ITABaseDefinitions.h> #include <ITAConvolutionDefinitions.h> #include <ITACriticalSection.h> #include <ITAUPTrigger.h> #include <ITAUncopyable.h> #include <atomic> #include <tbb/concurrent_queue.h> #include <vector> // 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: //! 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 GetFilterExchangeFadingFunction( ); //! Filteraustausch-Modus setzen void SetFilterExchangeFadingFunction( 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 = ITABase::FadingFunction::COSINE_SQUARE, const int iCrossfadeLength = -1 ); //! 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 = ITABase::MixingMethod::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 = ITABase::MixingMethod::OVERWRITE ); private: typedef struct { ITAUPFilter* pFilter; int iExchangeMode; int iCrossfadeLength; } FilterUpdate; int m_iBlocklength; int m_iMaxFilterlength; std::atomic<int> m_iExchangeFadingFunction; // 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::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_ITA_UP_CONVOLVER