Commits (16)
......@@ -8,6 +8,9 @@ include( VistaCommon )
# dependencies
vista_use_package( ITABase REQUIRED FIND_DEPENDENCIES )
vista_use_package( ITAFFT REQUIRED FIND_DEPENDENCIES )
vista_use_package( ITAConvolution REQUIRED FIND_DEPENDENCIES ) # required for FIR filtering (uses block convolver)
vista_use_package( TBB REQUIRED FIND_DEPENDENCIES )
vista_use_package( SPLINE REQUIRED FIND_DEPENDENCIES )
# includes
include_directories( "include" )
......@@ -17,9 +20,21 @@ include_directories( "include" )
set( ITADSPHeader
"include/ITADSPDefinitions.h"
"include/ITASIMOVariableDelayLine.h"
"include/ITAVariableDelayLine.h"
"include/ITABiquad.h"
"include/ITAThirdOctaveFilterbank.h"
"include/ITAThirdOctaveFilterbankFIR.h"
"include/ITAThirdOctaveFilterbankIIR.h"
"include/ITAThirdOctaveFIRFilterGenerator.h"
)
set( ITADSPSources
"src/ITASIMOVariableDelayLine.cpp"
"src/ITABiquad.cpp"
"src/ITAThirdOctaveFilterbank.cpp"
"src/ITAThirdOctaveFilterbankIIR.cpp"
"src/ITAThirdOctaveFIRFilterGenerator.cpp"
"src/ITAThirdOctaveFilterbankCoefficients.h"
"src/ITAVariableDelayLine.cpp"
)
......@@ -55,13 +70,13 @@ set_property( TARGET ITADSP PROPERTY FOLDER "ITACoreLibs" )
# apps
if( ITA_CORE_LIBS_WITH_APPS )
set( ITADSP_COMMON_BUILD TRUE )
set( ITA_DSP_COMMON_BUILD TRUE )
#add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/apps" )
endif( )
# tests
if( ITA_CORE_LIBS_WITH_TESTS )
set( ITADSP_COMMON_BUILD TRUE )
#add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/tests" )
set( ITA_DSP_COMMON_BUILD TRUE )
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/tests" )
endif( )
......@@ -24,3 +24,4 @@ limitations under the License.
### Quick build guide
It is recommended to clone and follow the build guide of the parent project [ITACoreLibs](https://git.rwth-aachen.de/ita/ITACoreLibs/wikis/home), which includes this project as a submodule.
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_BIQUAD
#define IW_ITA_BIQUAD
#include <ITADSPDefinitions.h>
//! Implements digital biquad component for IIR filtering
/**
* @todo jst: translate comments to English.
*
* Diese Klasse realisiert ein Biquad IIR Filter. Es hat einen globalen Verstärkungsfaktor (Gain),
* damit eine Kaskadierung vieler Biquads zur Bandfilterung benutzt werden kann.
* Die Umsetzung erfolgt in Kanonischer Form, um möglichst wenig Multiplizierer/Addierer und
* Verzögerer zu benutzen.
*/
class ITA_DSP_API CITABiquad
{
public:
// [stienen] Atomic? Lock? ... cpp Datei?
class ITA_DSP_API CParams
{
public:
float g; //!< Verstärkungsfaktor (Gain)
float a1; //!< Nenner-Koeffizient 1
float a2; //!< Nenner-Koeffizient 2
float b0; //!< Zähler-Koeffizient 0
float b1; //!< Zähler-Koeffizient 1
float b2; //!< Zähler-Koeffizient 2
CParams();
//! Parameter setzen
/**
* \param params Filterparameter/Koeffizienten, Reihenfolge: g, b0, b1, b2, a1, a2
*
* \note Reihenfolge der Parameter beachten
*/
void SetParameters( const float params[] );
} oParams;
//! Output write modes
enum
{
OUTPUT_OVERWRITE = 0, //!< Overwrites output
OUTPUT_ADD = 1 //!< Adds to the output
};
CITABiquad();
virtual inline ~CITABiquad() {};
//! Clears all accumulators
void Clear();
//! Filter samples, simple variant without any gain (factor)
/**
* Direct second canonical form.
*
* @param[in] pfInputData Input samples
* @param[out] pfOutputData Output samples
* @param[in] iNumSamples Number of samples to be processed
*
* @note Input and output samples must have same length
*/
void Process( const float* pfInputData, float* pfOutputData, const int iNumSamples );
//! Filter samples, simple variant with given gain (factor)
/**
* Direct second canonical form.
*
* @param[in] pfInputData Input samples
* @param[out] pfOutputData Output samples
* @param[in] iNumSamples Number of samples to be processed
* @param[in] fOutputGain Gain (factor) for output
* @param[in] iOutputMode Add to or overwrite output buffer
*
* @note Input and output samples must have same length
*/
void Process( const float* pfInputData, float* pfOutputData, const int iNumSamples, const float fOutputGain, const int iOutputMode );
//! Filter samples, simple variant with given gain (factor)
/**
* Direct second canonical form.
*
* @param[in] pfInputData Input samples
* @param[out] pfOutputData Output samples
* @param[in] iNumSamples Number of samples to be processed
* @param[in] fOutputGain1 Initial gain (factor) for output
* @param[in] fOutputGain2 Target gain (factor) for output
* @param[in] iOutputMode Add to or overwrite output buffer
*
* @note Input and output samples must have same length
*/
void Process( const float* pfInputData, float* pfOutputData, const int iNumSamples, const float fOutputGain1, const float fOutputGain2, const int iOutputMode );
private:
float vfAccumulators[ 2 ]; //!< Accumulators
};
#endif // IW_ITA_BIQUAD
......@@ -20,7 +20,7 @@
#define INCLUDE_WATCHER_ITA_DSP_DEFINITIONS
#if ( defined WIN32 ) && !( defined ITA_BASE_STATIC )
#ifdef ITA_BASE_EXPORT
#ifdef ITA_DSP_EXPORT
#define ITA_DSP_API __declspec( dllexport )
#else
#define ITA_DSP_API __declspec( dllimport )
......
......@@ -30,7 +30,7 @@
//! Vorwärtsdeklarationen
class ITASampleBuffer;
class ITASampleFrame;
class IITAVDLInterpolationRoutine;
class IITASampleInterpolationRoutine;
//! Single-input multiple-output block-based variable delay line
/**
......@@ -64,10 +64,7 @@ class IITAVDLInterpolationRoutine;
* Beispiel:
* - lineare Interpolation: 1 Sample Latenz, d.h. VDL Verzögerung 0 = t < 1 Sample => 1 - t = 1 Sample Latenz
* - sinc-Interpolation: z.B. 12 Sample Latenz, d.h. VDL Verzögerung t < 12 Sample => 12 - t Samples Latenz
*
* \see CVAFreefieldAudiostreamProcessor
* \see CVAFreefieldFilterGenerator
*
* *
* TODO: Doku, Synchronität
* - Wächst automatisch mit Setzen der Verzögerung
*/
......@@ -143,7 +140,7 @@ public:
* Maximale mögliche Verzögerung auf dem momentan
* reservierten Pufferspeicher in Sekunden zurückgeben
*
* Siehe auch: CVAVariableDelayLine::ReserveMaximumDelaySamples(), CVAVariableDelayLine::SetDelaySamples()
* Siehe auch: CITAVariableDelayLine::ReserveMaximumDelaySamples(), CITAVariableDelayLine::SetDelaySamples()
*/
float GetReservedMaximumDelayTime() const;
......@@ -272,7 +269,7 @@ private:
ITAStopWatch m_swProcess; //!< StopWatch zur Überwachung der Berechnungsschleife
IITAVDLInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
IITASampleInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
};
......
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_THIRD_OCTAVE_FILTER_GENERATOR
#define IW_ITA_THIRD_OCTAVE_FILTER_GENERATOR
#include <ITADSPDefinitions.h>
#include <ITAThirdOctaveMagnitudeSpectrum.h>
#include <ITAFFT.h>
#include <ITAStopWatch.h>
#include <string.h>
#include <vector>
/**
* Diese Klasse erzeugt FIR-Filter (Impulsantworten) aus Terzband-Betragsspektren.
* Hierzu wird ein linear-phasiges FIR-Filter mittels Spline-Interpolation erzeugt.
* Die Filter erzeugen eine Latenz der halben Filterlnge.
*/
class ITA_DSP_API CITAThirdOctaveFIRFilterGenerator
{
public:
// Design methods
enum
{
LINEAR_PHASE = 0,
MINIMUM_PHASE
};
// Konstruktor
CITAThirdOctaveFIRFilterGenerator( const double dSampleRate, const int iFilterLength );
// Destruktor
~CITAThirdOctaveFIRFilterGenerator();
// Filterlnge zurckgeben [Samples]
int GetFilterLength() const;
// Latenz in den Filtern zurckgeben [Samples]
int GetLatency() const;
// Mittlere Laufzeit der Erzeuger-Methode zurckgeben
double GetAverageRuntime() const;
// Dateiname fr Debug-Ausgaben setzen
void SetDumpFilename( const std::string& sFilename );
// Filter erzeugen
void GenerateFilter( const CITAThirdOctaveMagnitudeSpectrum& oTOMagnitudes, float* pfFilterCoeffs );
private:
double m_dSamplerate; // Abtastrate der Ausgabefilter [Hz]
int m_iFilterLength; // Lnge der Ausgabefilter
int m_iDFTCoeffs; // Anzahl symetrischer DFT-Koeffizienten
float m_fDeltaF; // DFT-Frequenzauflsung [Hz]
int m_iInputFreqs; // Anzahl Frequenzsttzstellen fr Interpolation (31 Terzen + 2 Rnder)
float* m_pfInputFreqs; // Frequenzsttzstellen der Eingabedaten (Terzen) @todo jst: std::vector
float* m_pfInputData; // Zwischenspeicher fr die Interpolation @todo jst: std::vector
float* m_ypp; // Interpolationsdaten
float* m_pfBuf1;
float* m_pfBuf2;
bool m_bWindow; // Apply window?
float* m_pfWindow;
ITAFFT m_ifft;
std::string m_sDumpFilename;
mutable ITAStopWatch m_sw;
};
#endif // IW_ITA_THIRD_OCTAVE_FILTER_GENERATOR
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_THIRD_OCTAVE_FILTERBANK
#define IW_ITA_THIRD_OCTAVE_FILTERBANK
#include <ITADSPDefinitions.h>
#include <ITAThirdOctaveMagnitudeSpectrum.h>
//! Terzbandspektrum-Filterbank zur digitalen Filterung
/**
* Diese Oberklasse für Filterbänke, die zur Realisierung der Filterung von Terzbandspektren dient,
* definiert die Schnittstellen zur Nutzung einer solchen Filterbank.
*
* Sie wird durch die Realisierungsmethoden (#FilterbankRealisationMethods) mittels Factory Method
* erstellt und kann dann Eingabesamples im Process()-Schritt filtern.
*
* Die Datensätze der Terzbandspektren werden durch die Klasse \CITAThirdOctaveMagnitudeSpectrum verwaltet.
*
*/
class ITA_DSP_API CITAThirdOctaveFilterbank
{
public:
//! Realisierungsmethoden
enum
{
FIR_SPLINE_LINEAR_PHASE = 0, //!< Linearphasiges FIR Filter mit Spline-Interpolation
IIR_BIQUADS_ORDER10 //!< Rekursives IIR Filter umgesetzt durch kaskadierte Biquads
} FilterbankRealisationMethods;
//! Factory method zum erzeugen einer Filterbank
/**
* \param dSamplerate Samplingrate
* \param iBlocklength Blockgröße
* \param iMethod Realisierungsmethode, eines von #FilterbankRealisationMethods (default: linearphasiges FIR Filter)
*
* \return Zeiger auf die erzeugte Terzfilterbank
*/
static CITAThirdOctaveFilterbank* Create( const double dSampleRate, const int iBlockLength, const int iMethod = FIR_SPLINE_LINEAR_PHASE );
//! Destruktor
virtual inline ~CITAThirdOctaveFilterbank() {};
//! Idealer Übertrager setzen
/**
* \param bSmoothChangeover Überbenden (default, true) oder direktes Umschalten (false)
*/
virtual inline void SetIdentity( const bool bSmoothChangeover = true )
{
CITAThirdOctaveGainMagnitudeSpectrum oIdentity;
oIdentity.SetIdentity();
SetMagnitudes( oIdentity, bSmoothChangeover );
};
//! Verstärkungsfaktoren setzen
/**
* \param oGains Neue Verstärkungsfaktoren
* \param bSmoothChangeover Überbenden (default, true) oder direktes Umschalten (false)
*/
virtual void SetMagnitudes( const CITAThirdOctaveGainMagnitudeSpectrum& oGains, const bool bSmoothChangeover = true ) = 0;
//! Latenz (Verzögerung) der Filterbank zurückgeben
/**
* \return Latenz (Verzögerung) in ganzzahligen Sample
*/
int GetLatency() const;
//! Löscht alle internen Puffer
virtual void Clear() = 0;
//! Filtert einen Block Samples (muss die angegebene Blocklänge haben, s.o.)
/**
* \pfInputSamples Eingabesamples (Anzahl = Blocklänge)
* \pfOutputSamples Ausgabesamples (Anzahl = Blocklänge)
*
* @todo jst: check if this couln't be decoupled from block-bound processing (like ITABiquad)
*/
virtual void Process( const float* pfInputSamples, float* pfOutputSamples ) = 0;
protected:
//! Standardkonstruktor (Deaktiviert, FactoryMethod Create() benutzen)
inline CITAThirdOctaveFilterbank() {};
};
#endif // IW_ITA_THIRD_OCTAVE_FILTERBANK
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_THIRD_OCTAVE_FILTERBANK_FIR
#define IW_ITA_THIRD_OCTAVE_FILTERBANK_FIR
#include <ITAThirdOctaveFilterbank.h>
#include <ITAThirdOctaveFIRFilterGenerator.h>
#include <ITAUPConvolution.h>
#include <ITAUPFilter.h>
#include <ITAFastMath.h>
#include <cassert>
//! Terzfilterbank mittels Dynamic Single-Channel Blockfalter (FIR Filter)
class ITA_DSP_API CITAThirdOctaveFilterbankFIR : public CITAThirdOctaveFilterbank
{
public:
inline CITAThirdOctaveFilterbankFIR( const double dSampleRate, const int iBlockLength )
: m_pfFilter( nullptr )
, m_pGenerator( nullptr )
, m_pConvolver( nullptr )
{
m_iBlocklength = iBlockLength;
// [fwe] Hier wird die Filterlänge für Directivities festgelegt
m_iFilterLength = 128;
m_pfFilter = fm_falloc( m_iFilterLength, true );
m_pGenerator = new CITAThirdOctaveFIRFilterGenerator( dSampleRate, m_iFilterLength );
m_pConvolver = new ITAUPConvolution( iBlockLength, m_iFilterLength );
m_pConvolver->SetFilterExchangeMode( ITAUPConvolution::CROSSFADE_COSINE_SQUARE );
m_pConvolver->SetFilterCrossfadeLength( 32 );
SetIdentity( false );
}
inline virtual ~CITAThirdOctaveFilterbankFIR()
{
fm_free( m_pfFilter );
delete m_pGenerator;
delete m_pConvolver;
}
inline virtual void SetIdentity( const bool bSmoothChangeover )
{
ITAUPFilter* pFilter = m_pConvolver->RequestFilter();
int iLatency = m_pGenerator->GetLatency();
assert( iLatency < m_iFilterLength );
fm_zero( m_pfFilter, m_iFilterLength );
m_pfFilter[ iLatency ] = 1;
pFilter->Load( m_pfFilter, m_iFilterLength );
m_pConvolver->ExchangeFilter( pFilter, ( bSmoothChangeover ? ITAUPConvolution::AUTO : ITAUPConvolution::SWITCH ) );
pFilter->Release(); // Auto-release
}
inline virtual void SetMagnitudes( const CITAThirdOctaveGainMagnitudeSpectrum& oMags, const bool bSmoothChangeover = true )
{
m_pGenerator->GenerateFilter( oMags, m_pfFilter );
ITAUPFilter* pFilter = m_pConvolver->RequestFilter();
pFilter->Load( m_pfFilter, m_iFilterLength );
m_pConvolver->ExchangeFilter( pFilter, ( bSmoothChangeover ? ITAUPConvolution::AUTO : ITAUPConvolution::SWITCH ) );
pFilter->Release(); // Auto-release
}
inline int GetLatency() const
{
return m_pGenerator->GetLatency();
}
inline virtual void Clear()
{
m_pConvolver->clear();
SetIdentity( true );
}
inline virtual void Process( const float* pfInputSamples, float* pfOutputSamples )
{
m_pConvolver->Process( pfInputSamples, m_iBlocklength, pfOutputSamples, m_iBlocklength );
}
private:
int m_iBlocklength;
int m_iFilterLength;
float* m_pfFilter;
CITAThirdOctaveFIRFilterGenerator* m_pGenerator;
ITAUPConvolution* m_pConvolver;
};
#endif // IW_ITA_THIRD_OCTAVE_FILTERBANK_FIR
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_THIRD_OCTAVE_FILTERBANK_IIR
#define IW_ITA_THIRD_OCTAVE_FILTERBANK_IIR
#include <ITADSPDefinitions.h>
#include <ITAThirdOctaveFilterbank.h>
#include <ITAThirdOctaveMagnitudeSpectrum.h>
#include <ITABiquad.h>
#include <ITASampleBuffer.h>
#include <vector>
#include <tbb/concurrent_queue.h>
//! Third octave magnitude filtering using Biquads (IIR filter)
/**
*
*/
class ITA_DSP_API CITAThirdOctaveFilterbankIIR : public CITAThirdOctaveFilterbank
{
public:
//! Constructor
/**
* @param[in] dSampleRate Samplingrate
* @param[in] iBlockLength Block length
*/
CITAThirdOctaveFilterbankIIR( const double dSampleRate, const int iBlockLength );
virtual inline ~CITAThirdOctaveFilterbankIIR() {};
//! Filterlatenz in Samples zurückgeben
int GetLatency() const;
//! Set magnitudes (in decibel) of filter bank
/**
* @param[in] oMagnitudes Filter magnitudes (dB)
* @param[in] bSmoothChangeover If true, switching is smoothed
*/
void SetMagnitudes( const CITAThirdOctaveGainMagnitudeSpectrum& oMagnitudes, const bool bSmoothChangeover = true );
//! Clear all internal accumulators
void Clear();
//! Verarbeite Samples (Filtern)
/**
* \param pfInputSamples Eingabesamples (Anzahl = Blocklänge)
* \param pfOutputSamples Ausgabesamples (Anzahl = Blocklänge)
*/
void Process( const float* pfInputSamples, float* pfOutputSamples );
private:
//! Interne Datenklasse für das Verarbeiten eines neuen Gain Datensatzes
class MagnitudeUpdate
{
public:
CITAThirdOctaveGainMagnitudeSpectrum oMags; //! New magnitudes
int iBlendSamples; //!< Anzahl Samples zum Überblenden
};
double m_dSampleRate; //!< Samplingrate
int m_iBlockLength; //!< Blocklänge
int m_nBandsInternal; //!< Anzahl der internen Bänder
int m_nBiquadsPerBand; //!< Anzahl von Biqads pro Band
std::vector< CITABiquad > m_vBiquads; //!< Biquads, access: [Band][BiquadNummer]
tbb::strict_ppl::concurrent_queue< CITAThirdOctaveFilterbankIIR::MagnitudeUpdate > m_vMagnitudesQueue; //!< Liste von neuen Verstärkungsfaktoren
CITAThirdOctaveGainMagnitudeSpectrum m_oMagnitudesInternal; //!< Interne Verstärkungsfaktoren
ITASampleBuffer m_sfTempFilterBuf; //!< Intermediate buffer for filter
ITASampleBuffer m_pfTempOutputBuf; //!< Intermediate buffer for output assembly
};
#endif // IW_ITA_THIRD_OCTAVE_FILTERBANK_IIR
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_VARIABLE_DELAY_LINE
#define IW_ITA_VARIABLE_DELAY_LINE
#include <ITADSPDefinitions.h>
#include <ITAAtomicPrimitives.h>
#include <ITACriticalSection.h>
#include <ITADataLog.h>
#include <ITAStopWatch.h>
//! Vorwärtsdeklarationen
class ITASampleBuffer;
class IITASampleInterpolationRoutine;
//! Daten-Logger der VDL aktivieren
#define ITA_DSP_VDL_DATA_LOG 0
//! Klasse für variable Verzögerungsglieder (variable delay-lines, VDLs)
/**
* Diese Klasse realisiert Verzögerungsglieder (variable delay-lines, VDLs)
* mit frei einstellbarer und zur Laufzeit veränderbarer Verzögerung.
* Hierbei wird nur ein Kanal betrachtet.
*
* Die maximale Verzögerung wird durch den internen Pufferspeicher voralloziert.
* Da das Reservieren von Speicherplatz zur Laufzeit teuer sein kann, ist es ratsam,
* eine grobe Schätzung vor Ausführung einer Szene zu vollziehen und dem entsprechend
* eine Verzögerung zu setzen.
*
* Berechnungsvorschrift: Samples = ( Distance / SpeedOfSound ) * SampleRate
*
* Beispiel:
* - Raumakustik, Ausbreitungspfade maximal rund 100 m: 13000 = 13e3 Samples
* - Fluglärm, Ausbreitungspfade bis zu 10 km: 1300000 = 13e5 Samples
*
* Mittels der Methode ReserveMaximumDelaySamples() oder
* ReserveMaximumDelayTime() kann dieser Speicher den
* benötigten Verzögerungen zur Laufzeit angepasst werden.
*
* Dieses Modul erzeugt nur dann Latenz, wenn für den aktuellen Switching-Algorithmus
* nicht genügend Stützstellen zur Verfügung stehen. Diese Latenz tritt nur dann auf,
* wenn die Gesamtverzögerung der VDL unter die Latenz der Interpolationsroutine
* fällt. Die VDL Implementierung erzwingt dann diese Latenz, um auf weitere Stützwerte
* zu warten. Anders interpretiert funktioniert die Verzögerung durch die VDL nur bis
* zu einem Minimalabstand, welcher durch die Interpolationsroutine begrenzt wird. Unterhalb
* dieser Grenze kommt es zu keiner zeitlich korrekten Wiedergabe der Samples.
*
* Beispiel:
* - lineare Interpolation: 1 Sample Latenz, d.h. VDL Verzögerung 0 = t < 1 Sample => 1 - t = 1 Sample Latenz
* - sinc-Interpolation: z.B. 12 Sample Latenz, d.h. VDL Verzögerung t < 12 Sample => 12 - t Samples Latenz
*
* TODO: Doku, Synchronität
* - Wächst automatisch mit Setzen der Verzögerung
*/
class ITA_DSP_API CITAVariableDelayLine
{
public:
//! Umsetzung der Verzögerungsänderung
/**
* Auflistung der Algorithmen, die zur Umsetzung einer Verzögerungsänderung
* zur Verfügung stehen.
*/
enum SwitchingAlgorithm
{
SWITCH = 0, //!< Hartes umschalten
CROSSFADE, //!< Überblenden im Zeitbereich mittels Kreuzblende (Kosinus-Quadrat)
LINEAR_INTERPOLATION, //!< Stauchen und Strecken im Zeitbereich durch lineare Interpolation (Polynominterpolation der Ordnung 1)
WINDOWED_SINC_INTERPOLATION, //!< Stauchen und Strecken im Zeitbereich durch Interpolation mittels gefensterter si-Funktion
CUBIC_SPLINE_INTERPOLATION, //!< Stauchen und Strecken im Zeitbereich durch kubische Spline-Interpolation
};
//! Konstruktor der variablen Verzögerungsleitung
/**
* \param dSamplerate Abtastrate [Hz]
* \param iBlocklength Streaming-Blocklänge [Anzahl Samples]
* \param fReservedMaxDelaySamples Initiale maximale Verzögerung [Anzahl Samples]
* \param iAlgorithm Algorithmus (siehe #SwitchingAlgorithm)
*/
CITAVariableDelayLine( double dSamplerate, int iBlocklength, float fReservedMaxDelaySamples, int iAlgorithm );
//! Destruktor der variablen Verzögerungsleitung
~CITAVariableDelayLine();
//! Verfahren zur Änderung der Verzögerung zurückgeben
/**
* Gibt das momentan benutzte Verfahren zur Umsetzung der Verzögerungsänderung zurück
*
* \return Eine Nummer aus der Auflistung #SwitchingAlgorithm
*
*/
int GetAlgorithm() const;
//! Verfahren zur Änderung der Verzögerung setzen
/**
* Setzt das momentan zu benutzende Verfahren zur Umsetzung der Verzögerungsänderung
*
* \param iAlgorithm Eine Nummer aus der Auflistung #SwitchingAlgorithm
*
*/
void SetAlgorithm( int iAlgorithm );
//! Minimal mögliche Verzögerung in Samples zurückgeben
int GetMinimumDelaySamples() const;
//! Minimal mögliche Verzögerung in Sekunden zurückgeben
float GetMinimumDelayTime() const;
//! Maximal Verzögerung zurückgeben [Samples]
/**
* Maximale mögliche Verzögerung auf dem momentan
* reservierten Pufferspeicher in Samples zurückgeben
*/
float GetReservedMaximumDelaySamples() const;
//! Maximal Verzögerung zurückgeben [Sekunden]
/**
* Maximale mögliche Verzögerung auf dem momentan
* reservierten Pufferspeicher in Sekunden zurückgeben
*
* Siehe auch: CITAVariableDelayLine::ReserveMaximumDelaySamples(), CITAVariableDelayLine::SetDelaySamples()
*/
float GetReservedMaximumDelayTime() const;
//! Pufferspeicher reservieren für die angegebene maximale Verzögerung [Samples]
/**
* \note Die vorhandenen Daten bleiben erhalten
* \note Nicht vor parallelem Einstieg sicher
*/
void ReserveMaximumDelaySamples( float fMaxDelaySamples );
//! Pufferspeicher reservieren für die angegebene maximale Verzögerung [Sekunden]
/**
* Wie ReserveMaximumDelaySamples(), nur für Zeit in Sekunden
*/
void ReserveMaximumDelayTime( float fMaxDelaySecs );
//! Sub-Sample-Verzögerung aktiviert
/**
* \return Gibt zurück, ob Sub-Sample-Verzögerungen aktiviert (true) oder deaktiviert (false) ist
*/
bool GetFractionalDelaysEnabled() const;
//! Sub-Sample-Verzögerung setzen
/**
* \param bEnabled Aktiviert (true) oder deaktiviert (false) Sub-Sample-Verzögerungen
*/
void SetFractionalDelaysEnabled( bool bEnabled );
//! Gesamtverzögerung zurückgeben [Samples]
/**
* Gibt die Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
*/
float GetDelaySamples() const;
//! Gesamtverzögerung zurückgeben
/**
* Gibt die Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
*/
float GetDelayTime() const;
//! Gesamtverzögerung zurückgeben [Zeit]
/**
* Gibt die neu eingestellte (und möglicherweise noch nicht übernommene) Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
*/
float GetNewDelayTime() const;
//! Gesamtverzögerung zurückgeben [Samples]
/**
* Gibt die Gesamtverzögerung der VDL als Ganzzahl und als Sub-Sample zurück
*
* \return iIntegerDelay Ganzzahlwert der Verzögerung (kleiner oder gleich der Gesamtverzögerung)
* \return fFractionalDelay Bruch der Sub-Sample-Verzögerung aus dem Wertebereich [0, 1)
*
*/
float GetDelaySamples( int& iIntegerDelay, float& fFractionalDelay ) const;
//! Verzögerung setzen [Samples]
/**
* Setzt die Verzögerung der VDL. Die Verzögerungsanpassung wird
* sofort auf den aktuellen Leseblock angewendet.
*
* \note Vergrößert gegebenenfalls den internen Puffer auf das Doppelte der aktuellen Größe.
* Dies kann unter Umständen zu einem Blockausfall führen, da die Operation teuer ist.
* Es empfiehlt sich bereits bei der Initialisierung für ausreichend Speicher zu sorgen,
* siehe ReserveMaximumDelaySamples().
*
* \note Die Funktion darf nicht parallel betreten werden (non-reentrant)
*/
void SetDelaySamples( float fDelaySamples );
//! Verzögerung setzen [Sekunden]
/**
* Wie SetDelaySamples(), aber für Zeit in Sekunden.
*/
void SetDelayTime( float fDelaySecs );
//! Löscht alle internen gespeicherten Samples und setzt die Distanz auf 0
void Clear();
//! Daten verarbeiten
/**
* Diese Funktion wird immer dann aufgerufen, wenn ein neuer Block für die Audiohardware
* verarbeitet werden soll (1 Block eingeben, 1 Block entnehmen).
*
* \param psbInput Eingabepuffer (Block) der VDL
* \param psbOutput Ausgabepuffer (Block) der VDL
*
*/
void Process( const ITASampleBuffer* psbInput, ITASampleBuffer* psbOutput );
private:
double m_dSampleRate; //!< Audio-Abtastrate
int m_iBlockLength; //!< Audio-Blockgröße
int m_iVDLBufferSize; //!< Größe des Puffers zum Speichern verzögerter Samples
ITASampleBuffer* m_psbVDLBuffer; //!< Puffer zum Speichern verzögerter Samples (variable Größe, mindestens 2xBlocklänge)
ITASampleBuffer* m_psbTemp; //!< Temporärer Puffer zum Arbeiten mit Samples (Größe: 2xBlocklänge) (das könnte evtl. knapp sein)
ITACriticalSection m_csBuffer; //!< Zugriff auf Puffer schützen
int m_iWriteCursor; //!< Der Schreibzeiger ist immer Vielfaches der Blocklänge
int m_iMaxDelay; //!< Maximal einstellbare Verzögerung (hängt von Puffergröße ab)
int m_iSwitchingAlgorithm; //!< Eingestellter Algorithmus zum Umschalten der Verzögerung
ITAAtomicFloat m_fCurrentDelay; //!< Aktuelle Verzögerung in Samples
ITAAtomicFloat m_fNewDelay; //!< Neue Verzögerung in Samples
bool m_bFracDelays; //!< Fractional Delay Filterung an/aus
int m_iFadeLength; //!< Überblendlänge für das Umschaltverfahren mittels Kreuzblende (Minimum von Blocklänge oder 32 Samples)
bool m_bStarted; //!< Statusvariable zur Initialisierung
ITAStopWatch m_swBufferSizeInc; //!< StopWatch zur Überwachung von Speicherallozierungszeiten
ITAStopWatch m_swProcess; //!< StopWatch zur Überwachung der Berechnungsschleife
int m_iNumberOfDropouts; //!< Zählt die Anzahl durch die VDL verursachten Ausfälle
IITASampleInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
#if (ITA_DSP_VDL_DATA_LOG == 1)
//! Implementierungsklasse für Logger-Datum
class VDLLogData : ITALogDataBase
{
public:
static std::ostream& outputDesc(std::ostream& os);
std::ostream& outputData(std::ostream& os) const;
float fCurrentDelay;
float fNewDelay;
float fResamplingFactor;
int iTargetBlockSize;
float fProcessingTime; //!< Zeit der Process-Routine in Millisekunden
};
ITABufferedDataLogger< VDLLogData > m_oDataLog; //!< Logger Datum für VDL spezifische Prozess-Information
#endif
};
#endif // IW_ITA_VARIABLE_DELAY_LINE
#include <ITABiquad.h>
#include <ITAException.h>
CITABiquad::CITABiquad()
{
Clear();
}
void CITABiquad::Clear()
{
vfAccumulators[ 0 ] = vfAccumulators[ 1 ] = 0;
}
void CITABiquad::Process( const float* pfInputData, float* pfOutputData, const int iNumSamples )
{
// Local accumulators
float z0, z1, z2;
z1 = vfAccumulators[ 0 ];
z2 = vfAccumulators[ 1 ];
for( int i = 0; i < iNumSamples; i++ )
{
// w[n] = x[n] - a_1*w[n-1] - a_2*w[n-2]
z0 = oParams.g * pfInputData[ i ] - oParams.a1 * z1 - oParams.a2 * z2;
// y[n] = b_0*w[n] + b_1*w[n-1] + b_2*w[n-2]
pfOutputData[ i ] = oParams.b0 * z0 + oParams.b1 * z1 + oParams.b2 * z2;
// Shift accumulators
z2 = z1;
z1 = z0;
}
// Store accumulators
vfAccumulators[ 0 ] = z1;
vfAccumulators[ 1 ] = z2;
return;
}
void CITABiquad::Process( const float* pfInputData, float* pfOutputData, const int iNumSamples, const float fOutputGain, const int iOutputMode )
{
// Local accumulators
float z0, z1, z2;
z1 = vfAccumulators[ 0 ];
z2 = vfAccumulators[ 1 ];
if( iOutputMode == CITABiquad::OUTPUT_ADD )
{
for( int i = 0; i < iNumSamples; i++ )
{
z0 = oParams.g * pfInputData[ i ] - oParams.a1*z1 - oParams.a2*z2;
pfOutputData[ i ] += ( oParams.b0*z0 + oParams.b1*z1 + oParams.b2*z2 ) * fOutputGain;
// Shift accumulators
z2 = z1;
z1 = z0;
}
}
else if( iOutputMode == CITABiquad::OUTPUT_OVERWRITE )
{
for( int i = 0; i < iNumSamples; i++ )
{
z0 = oParams.g*pfInputData[ i ] - oParams.a1*z1 - oParams.a2*z2;
pfOutputData[ i ] = ( oParams.b0 * z0 + oParams.b1 * z1 + oParams.b2*z2 ) * fOutputGain;
// Shift accumulators
z2 = z1;
z1 = z0;
}
}
else
{
ITA_EXCEPT1( INVALID_PARAMETER, "Unrecognized output write mode in CITABiquad" );
}
// Store accumulators
vfAccumulators[ 0 ] = z1;
vfAccumulators[ 1 ] = z2;
return;
}
void CITABiquad::Process( const float* pfInputData, float* out, const int iNumSamples, const float fOutputGain1, const float fOutputGain2, const int iOutputWriteMode )
{
if( iNumSamples == 0 )
return;
// Local accumulators
float z0, z1, z2;
z1 = vfAccumulators[ 0 ];
z2 = vfAccumulators[ 1 ];
// Factor for linear gain
const float fLinearGainFactor = ( fOutputGain2 - fOutputGain1 ) / float( iNumSamples );
if( iOutputWriteMode == OUTPUT_ADD )
{
for( int i = 0; i < iNumSamples; i++ )
{
const float fSampleGain = fOutputGain1 + i * fLinearGainFactor;
z0 = oParams.g*pfInputData[ i ] - oParams.a1*z1 - oParams.a2*z2;
out[ i ] += ( oParams.b0*z0 + oParams.b1*z1 + oParams.b2*z2 ) * fSampleGain;
// Shift accumulators
z2 = z1;
z1 = z0;
}
}
else if( iOutputWriteMode == CITABiquad::OUTPUT_OVERWRITE )
{
for( int i = 0; i < iNumSamples; i++ )
{
const float fSampleGain = fOutputGain1 + i * fLinearGainFactor;
z0 = oParams.g*pfInputData[ i ] - oParams.a1*z1 - oParams.a2*z2;
out[ i ] = ( oParams.b0*z0 + oParams.b1*z1 + oParams.b2*z2 ) * fSampleGain;
// Shift accumulators
z2 = z1;
z1 = z0;
}
}
else
{
ITA_EXCEPT1( INVALID_PARAMETER, "Unrecognized output write mode in CITABiquad" );
}
// Store accumulators
vfAccumulators[ 0 ] = z1;
vfAccumulators[ 1 ] = z2;
}
CITABiquad::CParams::CParams()
: g( 1 )
, a1( 0 )
, a2( 0 )
, b0( 1 )
, b1( 0 )
, b2( 0 )
{
}
void CITABiquad::CParams::SetParameters( const float params[] )
{
g = params[ 0 ];
b0 = params[ 1 ];
b1 = params[ 2 ];
b2 = params[ 3 ];
a1 = params[ 4 ];
a2 = params[ 5 ];
}
......@@ -35,13 +35,13 @@ CITASIMOVariableDelayLine::CITASIMOVariableDelayLine( const double dSamplerate,
m_iFadeLength = std::min( m_iBlockLength, 32 );
if( m_iSwitchingAlgorithm == LINEAR_INTERPOLATION )
m_pInterpolationRoutine = new CITAVDLLinearInterpolation();
m_pInterpolationRoutine = new CITASampleLinearInterpolation();
if( m_iSwitchingAlgorithm == CUBIC_SPLINE_INTERPOLATION )
m_pInterpolationRoutine = new CITAVDLCubicSplineInterpolation();
m_pInterpolationRoutine = new CITASampleCubicSplineInterpolation();
if( m_iSwitchingAlgorithm == WINDOWED_SINC_INTERPOLATION )
m_pInterpolationRoutine = new CITAVDLWindowedSincInterpolation();
m_pInterpolationRoutine = new CITASampleWindowedSincInterpolation();
Clear();
}
......
#include <ITAThirdOctaveFIRFilterGenerator.h>
#include <ITAConstants.h>
#include <ITAFastMath.h>
#include <ITANumericUtils.h>
#include <ITAStringUtils.h>
#include <ITAThirdOctaveMagnitudeSpectrum.h>
#include <spline.h>
CITAThirdOctaveFIRFilterGenerator::CITAThirdOctaveFIRFilterGenerator( const double dSampleRate, const int iFilterLength )
: m_dSamplerate( dSampleRate )
, m_iFilterLength( iFilterLength )
, m_ypp( nullptr )
, m_pfInputFreqs( nullptr )
, m_pfInputData( nullptr )
, m_pfBuf1( nullptr )
, m_pfBuf2( nullptr )
, m_bWindow( false )
{
m_iInputFreqs = CITAThirdOctaveMagnitudeSpectrum::GetNumBands() + 2;
m_pfInputFreqs = fm_falloc( m_iInputFreqs, true );
m_pfInputFreqs[ 0 ] = 0; // Left margin
for( int i = 0; i < CITAThirdOctaveMagnitudeSpectrum::GetNumBands(); i++ )
m_pfInputFreqs[ i + 1 ] = CITAThirdOctaveMagnitudeSpectrum::GetCenterFrequencies()[ i ];
m_pfInputFreqs[ m_iInputFreqs - 1 ] = ( float ) dSampleRate / 2; // Right margin: Nyquist frequency
m_pfInputData = fm_falloc( m_iInputFreqs, true );
// DFT frequency bandwidth
m_fDeltaF = ( float ) dSampleRate / ( float ) iFilterLength;
// Number of symetric DFT coefficients;
m_iDFTCoeffs = iFilterLength / 2 + 1;
m_pfBuf1 = fm_falloc( 2 * m_iDFTCoeffs, false );
m_pfBuf2 = fm_falloc( iFilterLength, false );
m_pfWindow = fm_falloc( iFilterLength, false );
// Windowing function (Hann window)
float c = 2 * ITAConstants::PI_F / ( float ) ( m_iFilterLength - 1 );
for( int i = 0; i < m_iFilterLength; i++ )
{
m_pfWindow[ i ] = 0.5F * ( 1 - cos( c * i ) );
}
m_ifft.plan( ITAFFT::IFFT_C2R, iFilterLength, m_pfBuf1, m_pfBuf2 );
m_sDumpFilename = "interpolated_magnitudes.csv";
}
CITAThirdOctaveFIRFilterGenerator::~CITAThirdOctaveFIRFilterGenerator()
{
fm_free( m_pfInputFreqs );
fm_free( m_pfInputData );
fm_free( m_pfBuf1 );
fm_free( m_pfBuf2 );
fm_free( m_pfWindow );
}
int CITAThirdOctaveFIRFilterGenerator::GetFilterLength() const
{
return m_iFilterLength;
}
int CITAThirdOctaveFIRFilterGenerator::GetLatency() const
{
// Latency = Half DFT period (ceil)
return uprdiv( m_iFilterLength, 2 );
}
double CITAThirdOctaveFIRFilterGenerator::GetAverageRuntime() const
{
return m_sw.mean();
}
void CITAThirdOctaveFIRFilterGenerator::SetDumpFilename( const std::string& sFilename )
{
m_sDumpFilename = sFilename;
}
void CITAThirdOctaveFIRFilterGenerator::GenerateFilter( const CITAThirdOctaveMagnitudeSpectrum& oTOMagnitudes, float* pfFilterCoeffs )
{
m_sw.start();
if( oTOMagnitudes.IsZero() )
{
for( int i = 0; i < m_iFilterLength; i++ )
pfFilterCoeffs[ i ] = 0.0f;
return;
}
if( oTOMagnitudes.IsIdentity() )
{
for( int i = 0; i < m_iFilterLength; i++ )
pfFilterCoeffs[ i ] = 0.0f;
pfFilterCoeffs[ int( m_iFilterLength / 2 ) ] = 1.0f;
return;
}
// 1st step: Interpolate the magnitudes
m_pfInputData[ 0 ] = 1.0f;
for( int i = 0; i < CITAThirdOctaveMagnitudeSpectrum::GetNumBands(); i++ )
m_pfInputData[ 1 + i ] = float( db10_to_ratio( oTOMagnitudes[ i ] ) );
m_pfInputData[ m_iInputFreqs - 1 ] = 0.0f; // @todo jst: check if this is good
// Initialize cubic spline interpolation
m_ypp = spline_cubic_set( m_iInputFreqs,
m_pfInputFreqs,
m_pfInputData,
1, // Left boundary condition => 1st derivative m=0
0,
1, // Right boundary condition => 1st derivative m=0
0 );
float fDummy;
const float fScale = 1 / ( float ) m_iFilterLength;
// No DC offset, ever!
m_pfBuf1[ 0 ] = 0;
m_pfBuf1[ 1 ] = 0;
for( int i = 1; i < m_iDFTCoeffs; i++ )
{
float x = spline_cubic_val( m_iInputFreqs,
m_pfInputFreqs,
i*m_fDeltaF,
m_pfInputData,
m_ypp,
&fDummy,
&fDummy );
// Phase-shift by half the FFT-period: Negate all odd DFT coefficients
m_pfBuf1[ 2 * i ] = ( ( i % 2 ) == 0 ) ? x*fScale : -x*fScale;
m_pfBuf1[ 2 * i + 1 ] = 0;
}
// 2nd step: Convert into time-domain (out-of-place C2R-IFFT)
m_ifft.execute();
// 3rd (optional) step: Hann window in the time-domain (optional)
if( m_bWindow )
{
for( int i = 0; i < m_iFilterLength; i++ )
pfFilterCoeffs[ i ] = m_pfBuf2[ i ] * m_pfWindow[ i ];
}
else
{
for( int i = 0; i < m_iFilterLength; i++ )
pfFilterCoeffs[ i ] = m_pfBuf2[ i ];
}
// @todo: Minimum-phase?
m_sw.stop();
}
#include <ITAThirdOctaveFilterbank.h>
#include <ITAThirdOctaveFilterbankFIR.h>
#include <ITAThirdOctaveFilterbankIIR.h>
#include <cassert>
CITAThirdOctaveFilterbank* CITAThirdOctaveFilterbank::Create( const double dSampleRate, const int iBlockLength, const int iMethod )
{
switch( iMethod )
{
case FIR_SPLINE_LINEAR_PHASE:
return new CITAThirdOctaveFilterbankFIR( dSampleRate, iBlockLength );
case IIR_BIQUADS_ORDER10:
return new CITAThirdOctaveFilterbankIIR( dSampleRate, iBlockLength );
default:
assert( false );
return nullptr;
}
}
#ifndef IW_ITA_THIRD_OCTAVE_FILTERBANK_COEFFICIENTS
#define IW_ITA_THIRD_OCTAVE_FILTERBANK_COEFFICIENTS
const double ITA_BIQUAD_FILTER_SAMPLINGRATE = 44100;
const int ITA_BIQUAD_FILTER_NUM_BANDS = 30;
const int ITA_BIQUAD_FILTER_NUM_BIQUADS_PER_BAND = 5;
//!< Parameters (g, b0, b1, b2, a0, a1) for biquad bandpasses, 10th order Buttworth design, access: [Band][Biquad][Param]
const float ITA_BIQUAD_FILTER_PARAMS[ 30 ][ 5 ][ 6 ] =
{
{ // Band 1, center frequency 25.1 Hz
{ 0.000412886359F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999701032925F, 0.999716977654F },
{ 0.000412886359F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999762381947F, 0.999772667553F },
{ 0.000412801159F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999272014143F, 0.999286684169F },
{ 0.000412801159F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999366290299F, 0.999377465320F },
{ 0.000412768629F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999161660004F, 0.999174462741F }
},
{ // Band 2, center frequency 31.6 Hz
{ 0.000519775953F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999618435430F, 0.999643704564F },
{ 0.000519775953F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999697516620F, 0.999713818333F },
{ 0.000519640943F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999078838244F, 0.999102087287F },
{ 0.000519640943F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999198613647F, 0.999216322640F },
{ 0.000519589402F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998940532405F, 0.998960821195F }
},
{ // Band 3, center frequency 39.8 Hz
{ 0.000654331930F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999511423741F, 0.999551470536F },
{ 0.000654331930F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999613897140F, 0.999639732824F },
{ 0.000654118003F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998832888172F, 0.998869731376F },
{ 0.000654118003F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998985439915F, 0.999013503629F },
{ 0.000654036346F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998659776098F, 0.998691927308F }
},
{ // Band 4, center frequency 50.1 Hz
{ 0.000823711945F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999371902235F, 0.999435368417F },
{ 0.000823711945F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999505526285F, 0.999546471025F },
{ 0.000823372990F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998518896023F, 0.998577279493F },
{ 0.000823372990F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998713765369F, 0.998758238050F },
{ 0.000823243631F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998302565207F, 0.998353512737F }
},
{ // Band 5, center frequency 63.1 Hz
{ 0.001036923510F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999188644752F, 0.999289224342F },
{ 0.001036923510F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999364184260F, 0.999429073228F },
{ 0.001036386495F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998116721755F, 0.998209236485F },
{ 0.001036386495F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998366487465F, 0.998436959976F },
{ 0.001036181594F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997846907818F, 0.997927636813F }
},
{ // Band 6, center frequency 79.4 Hz
{ 0.001305300963F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998945877968F, 0.999105270391F },
{ 0.001305300963F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.999178463685F, 0.999281297884F },
{ 0.001304450235F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997599494703F, 0.997746085894F },
{ 0.001304450235F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997920977703F, 0.998032646290F },
{ 0.001304125724F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997263836512F, 0.997391748552F }
},
{ // Band 7, center frequency 100.0 Hz
{ 0.001643104752F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998621147285F, 0.998873736026F },
{ 0.001643104752F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998932324314F, 0.999095289573F },
{ 0.001641757202F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996931059562F, 0.997163321401F },
{ 0.001641757202F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997346938807F, 0.997523875758F },
{ 0.001641243358F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996514855992F, 0.996717513284F }
},
{ // Band 8, center frequency 125.9 Hz
{ 0.002068274026F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998182068301F, 0.998582331224F },
{ 0.002068274026F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998602915674F, 0.998861165898F },
{ 0.002066139826F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996062182555F, 0.996430153684F },
{ 0.002066139826F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996603400062F, 0.996883733940F },
{ 0.002065326377F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.995548296528F, 0.995869347247F }
},
{ // Band 9, center frequency 158.5 Hz
{ 0.002603371133F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997581356527F, 0.998215601780F },
{ 0.002603371133F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.998157261685F, 0.998566495240F },
{ 0.002599991690F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.994925008366F, 0.995507923797F },
{ 0.002599991690F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.995634303731F, 0.996078417928F },
{ 0.002598704327F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.994294039757F, 0.994802591345F }
},
{ // Band 10, center frequency 199.5 Hz
{ 0.003276765981F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996749173689F, 0.997754121590F },
{ 0.003276765981F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.997547183221F, 0.998195641453F },
{ 0.003271415977F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.993424837314F, 0.994348133672F },
{ 0.003271415977F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.994361989205F, 0.995065490856F },
{ 0.003269379362F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.992655802250F, 0.993461241276F }
},
{ // Band 11, center frequency 251.2 Hz
{ 0.004124119630F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.995581287874F, 0.997173482335F },
{ 0.004124119630F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.996701477447F, 0.997728942098F },
{ 0.004115652499F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.991427802936F, 0.992889995427F },
{ 0.004115652499F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.992677491060F, 0.993791717185F },
{ 0.004112432080F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.990499728292F, 0.991775135840F }
},
{ // Band 12, center frequency 316.2 Hz
{ 0.005190240228F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.993920694024F, 0.996443034155F },
{ 0.005190240228F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.995513815132F, 0.997141676934F },
{ 0.005176844736F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.988742279809F, 0.991057407815F },
{ 0.005176844736F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.990425954314F, 0.992190372535F },
{ 0.005171755419F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.987637373907F, 0.989656489161F }
},
{ // Band 13, center frequency 398.1 Hz
{ 0.006531400804F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.991529010344F, 0.995524320626F },
{ 0.006531400804F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.993823945486F, 0.996402774083F },
{ 0.006510218163F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.985090657285F, 0.988755244154F },
{ 0.006510218163F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.987384557979F, 0.990177914593F },
{ 0.006502181340F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.983800134263F, 0.986995637319F }
},
{ // Band 14, center frequency 501.2 Hz
{ 0.008218227544F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.988041898375F, 0.994369143399F },
{ 0.008218227544F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.991388434096F, 0.995473186859F },
{ 0.008184750376F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.980066299255F, 0.985864830656F },
{ 0.008184750376F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.983228914765F, 0.987649878662F },
{ 0.008172070787F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.978600590565F, 0.983655858425F }
},
{ // Band 15, center frequency 631.0 Hz
{ 0.010339288897F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.982899564803F, 0.992917186273F },
{ 0.010339288897F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.987835037480F, 0.994303855474F },
{ 0.010286420099F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.973067748267F, 0.982238497138F },
{ 0.010286420099F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.977481738173F, 0.984475839526F },
{ 0.010266439012F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.971473862048F, 0.979467121977F }
},
{ // Band 16, center frequency 794.3 Hz
{ 0.013005539410F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.975238542083F, 0.991093129863F },
{ 0.013005539410F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.982591542460F, 0.992833142129F },
{ 0.012922123194F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.963198099912F, 0.977693108891F },
{ 0.012922123194F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.969434270888F, 0.980493256099F },
{ 0.012890682045F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.961588474455F, 0.974218635910F }
},
{ // Band 17, center frequency 1000.0 Hz
{ 0.016355795943F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.963723689222F, 0.988803206729F },
{ 0.016355795943F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.974774903849F, 0.990983589344F },
{ 0.016224334245F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.949111440426F, 0.972002557792F },
{ 0.016224334245F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.958026064350F, 0.975499985956F },
{ 0.016174950514F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.947711035158F, 0.967650098973F }
},
{ // Band 18, center frequency 1258.9 Hz
{ 0.020563444373F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.946288895400F, 0.985931201087F },
{ 0.020563444373F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.963019054182F, 0.988657790197F },
{ 0.020356565082F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.928779799666F, 0.964889337866F },
{ 0.020356565082F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.941661620753F, 0.969245215095F },
{ 0.020279176270F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.928001671308F, 0.959441647460F }
},
{ // Band 19, center frequency 1584.9 Hz
{ 0.025844585941F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.919740838190F, 0.982334024432F },
{ 0.025844585941F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.945209007877F, 0.985733053955F },
{ 0.025519616408F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.899143158447F, 0.956015616827F },
{ 0.025519616408F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.917932720989F, 0.961418473039F },
{ 0.025398685497F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.899707599022F, 0.949202629006F }
},
{ // Band 20, center frequency 1995.3 Hz
{ 0.032467823220F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.879163513424F, 0.977837272169F },
{ 0.032467823220F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.918074385233F, 0.982054357605F },
{ 0.031958522281F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.855593221788F, 0.944974746791F },
{ 0.031958522281F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.883203336797F, 0.951636244650F },
{ 0.031770217479F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.858711448828F, 0.936459565042F }
},
{ // Band 21, center frequency 2511.9 Hz
{ 0.040765838588F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.817048441089F, 0.972231734924F },
{ 0.040765838588F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.876578564099F, 0.977424697925F },
{ 0.039969934531F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.791235089356F, 0.931285127153F },
{ 0.039969934531F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.832002866030F, 0.939425327915F },
{ 0.039678001166F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.798883752261F, 0.920643997668F }
},
{ // Band 22, center frequency 3162.3 Hz
{ 0.051148804773F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.722086739688F, 0.965273009840F },
{ 0.051148804773F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.813027276104F, 0.971591186941F },
{ 0.049909472910F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.695887428498F, 0.914390107276F },
{ 0.049909472910F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.756173255338F, 0.924201241426F },
{ 0.049459320559F, 1.000000000000F, 0.000000000000F, -1.000000000000F, -1.711199114420F, 0.901081358883F }