Commit 7a4adcbb authored by Dipl.-Ing. Jonas Stienen's avatar Dipl.-Ing. Jonas Stienen
Browse files

Moving interpolation routines (from VDL implementation) to ITABase and making...

Moving interpolation routines (from VDL implementation) to ITABase and making them available for public use
parent a46b357b
......@@ -65,6 +65,7 @@ set( ITABaseHeader
"include/ITAHDFTSpectra.h"
"include/ITAHDFTSpectrum.h"
"include/ITAFunctors.h"
"include/ITAInterpolation.h"
"include/ITALog.h"
"include/ITAMagnitudeSpectrum.h"
"include/ITANumericUtils.h"
......@@ -98,6 +99,7 @@ set( ITABaseSources
"src/ITAFileSystemUtils.cpp"
"src/ITAHDFTSpectra.cpp"
"src/ITAHDFTSpectrum.cpp"
"src/ITAInterpolation.cpp"
"src/ITALog.cpp"
"src/ITAMagnitudeSpectrum.cpp"
"src/ITANumericUtils.cpp"
......
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_INTERPOLATION
#define IW_ITA_INTERPOLATION
#include <ITABaseDefinitions.h>
#include <string>
class ITASampleBuffer;
//! Abstract interface for interpolation algorithms
/**
* For equidistant sampling.
*
*/
class IITAVDLInterpolationRoutine
{
public:
//! Human-readable name
virtual std::string GetName() const = 0;
//! Anzahl an Sttzwerten, die linksseitig (Anfang) bzw. rechtseitig (Ende) bentigt werden
/**
* Auf dem berlappungsbereich wird die Interpolation angewendet, auch wenn sie
* auf diesen keine gltigen Daten liefern kann. Erst ab dem Offset iLeft und bis
* zum Ende abzglich iRight werden in der Interpolationsroutine korrekte Daten
* abgelegt.
*
* @param[out] iLeft Anzahl Samples zur linken Seite (call-by-reference)
* @param[out] iRight Anzahl Samples zur rechten Seite (call-by-reference)
*/
virtual void GetOverlapSamples( int& iLeft, int& iRight ) const = 0;
//! Interpolation/Resampling
/**
* An input buffer with a given length will be interpolated starting at a given input offset.
* As many samples will be generated, as defined for the output buffer sample length. In- and
* output bufers are samples equidistant. An overlapping area is required in the source buffer
* to ensure a sufficient covererage of base samples (depending on algorithm). Those left-side
* and right-side samples can be retrieved via the function GetOverlapSamples().
*
* \param pInput In buffer pointer
* \param iInputLength Length of input samples
* \param iInputStartOffset Offset on input buffer (usually = left-side overlap samples)
* \param pOutput Out buffer pointer
* \param iOutputLength Length of requested output sample
* \param iOutputOffset Offset in output buffer
*
* \return True, if interpolation algorithm could be executed
*
*/
virtual bool Interpolate( const ITASampleBuffer* pInput, const int iInputLength, const int iInputStartOffset, ITASampleBuffer* pOutput, const int iOutputLength, const int iOutputOffset = 0 ) const = 0;
};
//! Linear interpolation (fast)
class ITA_BASE_API CITAVDLLinearInterpolation : public IITAVDLInterpolationRoutine
{
public:
inline CITAVDLLinearInterpolation() {};
inline ~CITAVDLLinearInterpolation() {};
inline std::string GetName() const { return "Linear Interpolation"; };
inline void GetOverlapSamples( int& iLeft, int& iRight ) const { iLeft = 1; iRight = 0; };
bool Interpolate( const ITASampleBuffer* pInput, const int iInputLength, const int iInputStartOffset, ITASampleBuffer* pOutput, const int iOutputLength, const int iOutputOffset = 0 ) const;
};
//! Cubic-Spline interpolation (efficient)
class ITA_BASE_API CITAVDLCubicSplineInterpolation : public IITAVDLInterpolationRoutine
{
public:
inline CITAVDLCubicSplineInterpolation() {};
inline ~CITAVDLCubicSplineInterpolation() {};
inline std::string GetName() const { return "Cubic Spline Interpolation"; };
inline void GetOverlapSamples( int& iLeft, int& iRight ) const { iLeft = 2; iRight = 2; };
bool Interpolate( const ITASampleBuffer* pInput, const int iInputLength, const int iInputStartOffset, ITASampleBuffer* pOutput, const int iOutputLength, const int iOutputOffset = 0 ) const;
};
//! Windowed Sinc-Interpolation (costly)
class ITA_BASE_API CITAVDLWindowedSincInterpolation : public IITAVDLInterpolationRoutine
{
public:
inline CITAVDLWindowedSincInterpolation( const int iWindowSize = 13 ) : m_iWindowSize( iWindowSize )
{
m_iWindowSize = m_iWindowSize + m_iWindowSize % 1;
};
inline ~CITAVDLWindowedSincInterpolation() {};
inline std::string GetName() const { return "Windowed Sinc-Interpolation"; };
inline void GetOverlapSamples( int& iLeft, int& iRight ) const { iLeft = m_iWindowSize / 2; iRight = m_iWindowSize / 2; };
bool Interpolate( const ITASampleBuffer* pInput, const int iInputLength, const int iInputStartOffset, ITASampleBuffer* pOutput, const int iOutputLength, const int iOutputOffset = 0 ) const;
private:
int m_iWindowSize;
};
#endif // IW_ITA_INTERPOLATION
#include <ITAInterpolation.h>
#include <ITAConstants.h>
#include <ITASampleBuffer.h>
#include <cassert>
// Calculate cubic spline set (second derivatives, ypp) for equidistant data
void spline_cubic_set_equidistant( const int n, const float* y, float* ypp );
// Evaluate cubic spline interpolation point using only base values (y) and second derivatives (ypp) for equidistant data
float spline_cubic_val_equidistant( const int n, const float fX, const float* y, const float* ypp );
bool CITAVDLLinearInterpolation::Interpolate( const ITASampleBuffer* pInput, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset /*=0*/ ) const
{
assert( pOutput->length() >= iOutputLength + iOutputOffset );
// Interpolation ist nur mglich, wenn der Eingabe-Offset auf dem Eingabepuffer
// grer oder gleich 1 ist, da sonst keine Sttzwerte vorhanden sind.
assert( iInputStartOffset > 0 );
if( iInputStartOffset < 1 )
return false;
// Samples, die aufgeholt (>0) bzw. abgebremst (<0) werden mssen
int iDeltaDelay = ( iInputLength - iInputStartOffset ) - iOutputLength;
// Resamplingfaktor
float r = 1 + iDeltaDelay / ( float ) iOutputLength;
// Grenzen prfen, in denen Resampling betrieben werden kann.
// Dies kann schon mal auftreten, wenn eine Quelle manuell versetzt wird
// und sich dadurch eine zu hohe Relativgeschwindigkeit ergibt.
if( ( r > 1.5 ) || ( r < 0.5 ) )
return false;
// TODO klren ob diese resampling grenzen berhaupt gelten, theor. gibt es keine einschrnkung hier!
// --= Resampling =--
if( r < 1 ) // Oversampling
{
// ber Ausgabesamples (Laufvariable i ber den Ausgangspuffer)
// laufen und interpolierte Werte eintragen, dabei evtl.
// Samples des berlappungspuffers am Anfang einbeziehen
for( int i = 0; i < iOutputLength; i++ )
{
// Position in der Eingabe berechnen
float x_input = ( i + 1 )*r;
// Die Position des letzten Samples muss bereinstimmen mit dem letzten
// Eingabesample, dann wurde die Zeit erfolgreich eingeholt bzw.
// abgebremst.
if( i == iOutputLength - 1 )
{
assert( iOutputLength + iDeltaDelay == iInputLength - iInputStartOffset );
}
// Linkes/rechtes Nachbarsample in der Eingabe
int a = ( int ) floor( x_input );
int b = ( int ) ceil( x_input );
// Relative Position zwischen den Nachbarsamples in der Eingabe
float frac = x_input - ( float ) a;
// Die Linke Seite liegt immer innerhalb des Ausgangs
assert( ( a >= 0 ) && ( a < iInputLength ) );
// Sample genau getroffen (z.B. a=b=0)
if( a == b )
{
( *pOutput )[ i + iOutputOffset ] = ( *pInput )[ a ];
}
else
{
// Zwischenwert anhand linkem und rechtem Sttzwert ermitteln
assert( ( b >= 1 ) && ( b < iInputLength ) );
// Steigung (leicht lesbare Implementierung, wird vom Compiler wegoptimiert)
float left_val = ( *pInput )[ a ];
float right_val = ( *pInput )[ b ];
float m = right_val - left_val;
float val = left_val + m*frac;
( *pOutput )[ i + iOutputOffset ] = val;
}
}
}
else if( r > 1 ) // Undersampling
{
// ber Ausgabesamples (Laufvariable i ber den Ausgangspuffer)
// laufen und interpolierte Werte eintragen, dabei evtl.
// Samples des berlappungspuffers am Anfang einbeziehen
for( int i = 0; i < iOutputLength; i++ )
{
// Position in der Eingabe berechnen
float x_input = ( i + 1 )*r;
if( i == iOutputLength - 1 )
{
assert( x_input == ( float ) iInputLength - iInputStartOffset );
}
// Linkes/rechtes Nachbarsample in der Eingabe
int a = ( int ) floor( x_input );
int b = ( int ) ceil( x_input );
// Relative Position zwischen den Nachbarsamples in der Eingabe
float frac = x_input - ( float ) a;
// Die Linke Seite liegt immer innerhalb des Ausgangs
assert( ( a >= 0 ) && ( a < ( iInputLength ) ) );
// Sample genau getroffen (z.B. a=b=0)
if( a == b )
{
( *pOutput )[ i + iOutputOffset ] = ( *pInput )[ a ];
}
else
{
// Zwischenwert anhand linkem und rechtem Sttzwert ermitteln
assert( ( b >= 1 ) && ( b < iInputLength ) );
// Steigung
float m = ( *pInput )[ b ] - ( *pInput )[ a ];
( *pOutput )[ i + iOutputOffset ] = ( *pInput )[ a ] + m*frac;
}
}
}
else // Kein Resampling; TODO evtl immer resamplen, dieser Fall sollte vor Aufruf der Interpolation bereits abgefangen werden
{
// Nur Kopieren
pOutput->write( pInput, iOutputLength, iInputStartOffset );
}
return false;
}
bool CITAVDLCubicSplineInterpolation::Interpolate( const ITASampleBuffer* pInput, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset /*=0*/ ) const
{
// Eingabe validieren
assert( iInputStartOffset >= 2 );
assert( iOutputLength > 0 );
assert( pOutput->length() >= iOutputLength + iOutputOffset );
// Samples, die aufgeholt (>0) bzw. abgebremst (<0) werden mssen
int iDeltaDelay = ( iInputLength - iInputStartOffset ) - iOutputLength;
// Resamplingfaktor
float r = 1 + iDeltaDelay / ( float ) iOutputLength;
// --= Resampling =--
const float* pInputData = pInput->data();
float* pOutputData = pOutput->data();
// Return value
float* pInputSecondDerivatives = new float[ iInputLength ];
spline_cubic_set_equidistant( iInputLength, pInputData, pInputSecondDerivatives ); // Natural spline interpolation
for( int i = 0; i < iOutputLength; i++ )
{
// Position in der Eingabe berechnen (relativ zum Anfang mit Offset)
float fXInput = ( i + 1 )*r - 1;
if( i == iOutputLength - 1 )
assert( iOutputLength + iDeltaDelay == iInputLength - iInputStartOffset );
// Spline-Segment auswerten
float fYOutput = spline_cubic_val_equidistant( iInputLength, fXInput + iInputStartOffset, pInputData, pInputSecondDerivatives );
pOutputData[ i + iOutputOffset ] = fYOutput;
}
delete[] pInputSecondDerivatives;
return true;
}
bool CITAVDLWindowedSincInterpolation::Interpolate( const ITASampleBuffer* pInput, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset /*=0*/ ) const
{
// Eingabe validieren
assert( iInputStartOffset >= m_iWindowSize / 2 );
assert( iOutputLength > 0 );
assert( pOutput->length() >= iOutputLength + iOutputOffset );
// Samples, die aufgeholt (>0) bzw. abgebremst (<0) werden mssen
int iDeltaDelay = ( iInputLength - iInputStartOffset ) - iOutputLength;
// Resamplingfaktor
float r = 1 + iDeltaDelay / ( float ) iOutputLength;
// --= Resampling =--
const float* pInputData = pInput->data();
float* pOutputData = pOutput->data();
for( int i = 0; i < iOutputLength; i++ )
{
// Position in der Eingabe berechnen (relativ zum Anfang mit Offset)
float fXInput = ( i + 1 )*r - 1;
if( i == iOutputLength - 1 )
assert( iOutputLength + iDeltaDelay == iInputLength - iInputStartOffset );
// Fenster anwenden & Si-Rekonstruktion
pOutputData[ i + iOutputOffset ] = 0.0f;
for( int j = ( int ) ceil( fXInput - m_iWindowSize / 2 ); j < ( int ) floor( fXInput + m_iWindowSize / 2 ); j++ )
{
int iCenterIndex = j + iInputStartOffset;
assert( iCenterIndex >= 0 && iCenterIndex < iInputLength + m_iWindowSize / 2 ); // berlauf verhindern
float fAmplitude = pInputData[ iCenterIndex ];
// Relativer Abstand zum aktuellen Lesecursor in der Eingabe
float fSincArg = fXInput - iCenterIndex + iInputStartOffset;
assert( std::fabs( fSincArg ) <= ( float ) m_iWindowSize / 2 ); // Fensterberlauf verhindern
float fSincValue = ( fSincArg != 0.0f ) ? ( sin( ITAConstants::PI_F * fSincArg ) / ( ITAConstants::PI_F * fSincArg ) ) : 1.0f;
// Rect
//float fWindowValue = 1.0f;
// Hanning
//float fWindowValue = 0.5f * (1-cos(2*PI_F*fSincArg/(float) (m_iWindowSize/2)));
// Hamming
//float fWindowValue = 0.54f - 0.46f * cos(2*PI_F*fSincArg/(float) (m_iWindowSize/2));
// Lanczos
float a = m_iWindowSize / 2.0f;
float fWindowValue = ( fSincArg != 0.0f ) ? ( sin( ITAConstants::PI_F * fSincArg / a ) / ( ITAConstants::PI_F * fSincArg / a ) ) : 1.0f;
float fContribution = fWindowValue * fAmplitude * fSincValue;
pOutputData[ i + iOutputOffset ] += fContribution;
}
}
return true;
}
void spline_cubic_set_equidistant( const int n, const float* y, float* ypp )
{
assert( n > 2 );
// Temporrer Puffer
float* a = new float[ 3 * n - 1 ];
// Setup equations, save intermediate values to b
ypp[ 0 ] = 0.0f;
a[ 1 ] = 1.0f; // 1+0*3
a[ 3 ] = -1.0f; // 0+1*3
for( int i = 1; i < n - 1; i++ )
{
ypp[ i ] = y[ i + 1 ] - 2 * y[ i ] + y[ i - 1 ];
a[ 2 + ( i - 1 ) * 3 ] = 1.0f / 6.0f;
a[ 1 + i * 3 ] = 2.0f / 3.0f;
a[ 0 + ( i + 1 ) * 3 ] = 1.0f / 6.0f;
}
ypp[ n - 1 ] = 0.0f;
a[ 2 + ( n - 2 ) * 3 ] = -1.0f;
a[ 1 + ( n - 1 ) * 3 ] = 1.0f;
// Lineares Gleichungssystem lsen
a[ 1 + 1 * 3 ] = 5.0f / 6.0f;
ypp[ 1 ] = ypp[ 1 ] - ypp[ 0 ] / 6.0f;
float xmult;
for( int i = 2; i < n; i++ )
{
xmult = a[ 2 + ( i - 1 ) * 3 ] / a[ 1 + ( i - 1 ) * 3 ];
a[ 1 + i * 3 ] = a[ 1 + i * 3 ] - xmult * a[ 0 + i * 3 ];
ypp[ i ] = ypp[ i ] - xmult * ypp[ i - 1 ];
}
// Rckwrts einsetzen
for( int i = n - 2; 0 <= i; i-- )
ypp[ i ] = ( ypp[ i ] - a[ 0 + ( i + 1 ) * 3 ] * ypp[ i + 1 ] ) / a[ 1 + i * 3 ];
delete[] a;
}
float spline_cubic_val_equidistant( const int n, const float fX, const float* y, const float* ypp )
{
assert( n > fX );
int iXInt = ( int ) std::floor( fX );
float fFrac = fX - iXInt;
float y_interp = y[ iXInt ]
+ fFrac*( ( y[ iXInt + 1 ] - y[ iXInt ] ) - ( ypp[ iXInt + 1 ] / 6.0f + ypp[ iXInt ] / 3.0f )
+ fFrac * ( 0.5f * ypp[ iXInt ] + fFrac * ( ypp[ iXInt + 1 ] - ypp[ iXInt ] ) / 6.0f ) );
return y_interp;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment