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

Initial checkin, starting with single-input multi-output variable delay line

parents
cmake_minimum_required( VERSION 3.0 )
project( ITADSP )
list( APPEND CMAKE_MODULE_PATH "$ENV{VISTA_CMAKE_COMMON}" )
include( VistaCommon )
# dependencies
vista_use_package( ITABase REQUIRED FIND_DEPENDENCIES )
vista_use_package( ITAFFT REQUIRED FIND_DEPENDENCIES )
# includes
include_directories( "include" )
# sources
set( ITADSPHeader
"include/ITADSPDefinitions.h"
"include/ITASIMOVariableDelayLine.h"
)
set( ITADSPSources
"src/ITASIMOVariableDelayLine.cpp"
)
# compiler settings
if( ITA_VISTA_BUILD_STATIC )
add_definitions( -DVISTABASE_STATIC -DVISTAMATH_STATIC -DVISTAASPECTS_STATIC -DVISTATOOLS_STATIC -DVISTAINTERPROCCOMM_STATIC )
endif( )
if( BUILD_SHARED_LIBS )
add_definitions( -DITA_DSP_EXPORT )
else( )
add_definitions( -DITA_DSP_STATIC -DITA_FFT_STATIC -DITA_BASE_STATIC )
endif( )
if( NOT WIN32 )
add_definitions( -std=gnu++11)
endif( )
# linker
add_library( ITADSP ${ITADSPHeader} ${ITADSPSources} )
target_link_libraries( ITADSP ${VISTA_USE_PACKAGE_LIBRARIES} )
# configure
vista_configure_lib( ITADSP )
vista_install( ITADSP )
set( ITADSP_INCLUDE_OUTDIR "${CMAKE_CURRENT_SOURCE_DIR}/include" )
vista_create_cmake_configs( ITADSP )
vista_create_default_info_file( ITADSP )
set_property( TARGET ITADSP PROPERTY FOLDER "ITACoreLibs" )
# apps
if( ITA_CORE_LIBS_WITH_APPS )
set( ITADSP_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" )
endif( )
Copyright 2015-2017 Institute of Technical Acoustics, RWTH Aachen University
Licensed under the Apache License, Version 2.0 (the "License");
you may not use files of this project except in compliance with the License.
You may obtain a copy of the License at
<http://www.apache.org/licenses/LICENSE-2.0>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
\ No newline at end of file
## ITADSP
ITADSP is a C++ digital signal processing library for acoustics. It is a component from [ITACoreLibs](https://git.rwth-aachen.de/ita/ITACoreLibs), a collection of C++ libraries for virtual acoustics.
### License
See [LICENSE](LICENSE.md) file.
### Quick build guide
Follow instructions from Wiki pages of [ITABase](https://git.rwth-aachen.de/ita/ITABase/wikis/home) project.
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_ITA_DSP_DEFINITIONS
#define INCLUDE_WATCHER_ITA_DSP_DEFINITIONS
#if ( defined WIN32 ) && !( defined ITA_BASE_STATIC )
#ifdef ITA_BASE_EXPORT
#define ITA_DSP_API __declspec( dllexport )
#else
#define ITA_DSP_API __declspec( dllimport )
#endif
#else
#define ITA_DSP_API
#endif
#endif // INCLUDE_WATCHER_ITA_DSP_DEFINITIONS
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef IW_ITA_SIMO_VARIABLE_DELAY_LINE
#define IW_ITA_SIMO_VARIABLE_DELAY_LINE
#include <ITAAtomicPrimitives.h>
#include <ITACriticalSection.h>
#include <ITADataLog.h>
#include <ITAStopWatch.h>
#include <atomic>
#include <map>
//! Vorwärtsdeklarationen
class ITASampleBuffer;
class IITAVDLInterpolationRoutine;
//! Single-input multiple-output block-based variable delay line
/**
* 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
*
* \see CVAFreefieldAudiostreamProcessor
* \see CVAFreefieldFilterGenerator
*
* TODO: Doku, Synchronität
* - Wächst automatisch mit Setzen der Verzögerung
*/
class CITASIMOVariableDelayLine
{
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
};
struct CITAVDLReadCursor
{
std::atomic< float > fNewReadCursorSamples; //!< Read cursor that will be forwarded to on next processing block
std::atomic< float > fOldReadCursorSamples; //!< Read cursor from last calculation
};
//! 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)
*/
CITASIMOVariableDelayLine( const double dSamplerate, const int iBlocklength, const float fReservedMaxDelaySamples, const int iAlgorithm );
//! Destruktor der variablen Verzögerungsleitung
~CITASIMOVariableDelayLine();
//! 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( const 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: CVAVariableDelayLine::ReserveMaximumDelaySamples(), CVAVariableDelayLine::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( const float fMaxDelaySamples );
//! Pufferspeicher reservieren für die angegebene maximale Verzögerung [Sekunden]
/**
* Wie ReserveMaximumDelaySamples(), nur für Zeit in Sekunden
*/
void ReserveMaximumDelayTime( const float fMaxDelaySecs );
//! Adds a new reading cursor
/**
* @return Cursor ID
*/
int AddCursor();
//! Remove cursor
/**
* Removes cursor of given ID
* @param[in] iCursorID Cursor ID
* @return False, if cursor not valid
*/
bool RemoveCursor( const int iCursorID );
//! Checks existance of a cursor
bool CursorExists( const int iCursorID ) const;
//! Returns the IDs of the cursors
std::vector< int > GetCursorIDs() const;
//! Overall current latency of a cursor in samples
/**
* \return Delay and fractional delay in samples (which is rounded during VDL processing)
*
* \note Raises ITAException if cursor not valid
*/
float GetCurrentDelaySamples( const int iCursorID ) const;
//! Overall new latency of a cursor in samples (not yet effective)
/**
* \return New delay and fractional delay in samples (which is rounded during VDL processing)
*
* \note Raises ITAException if cursor not valid
*/
float GetNewDelaySamples( const int iCursorID ) const;
//! Overall current latency of a cursor in seconds
/**
* \return Delay and fractional delay in seconds
*
* \note Raises ITAException if cursor not valid
*/
float GetCurrentDelayTime( const int iCursorID ) const;
//! Overall new latency of a cursor in seconds
/**
* Gibt die neu eingestellte (und möglicherweise noch nicht übernommene) Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
*
* \note Raises ITAException if cursor not valid
*/
float GetNewDelayTime( const int iCursorID ) 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( const int iID, const float fDelaySamples );
//! Verzögerung setzen [Sekunden]
/**
* Wie SetDelaySamples(), aber für Zeit in Sekunden.
*/
void SetDelayTime( const int iID, const float fDelaySecs );
//! Löscht alle internen gespeicherten Samples und setzt die Distanz auf 0
void Clear();
//! Pushes a block of samples at front of delay line
void WriteBlock( const ITASampleBuffer* psbInput );
//! Reads a block of samples for a read cursor and switches to new delay
void ReadBlock( const int iID, ITASampleBuffer* psbOutput );
//! Reads a block of samples at all cursors (switches to new delay)
/**
* The order of the channels in the sample frame will be linear over the
* internal cursor list, see GetCursorIDs()
*
* @param[in] psfOutput Initialized sample frame
*
*/
void ReadBlock( ITASampleFrame* psfOutput );
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
std::map< int, CITAVDLReadCursor > m_lUserCursors; //!< List of read cursors (managed by user)
std::map< int, CITAVDLReadCursor > m_lInternalCursors; //!< List of read cursors (synced for processing)
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_swProcess; //!< StopWatch zur Überwachung der Berechnungsschleife
IITAVDLInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
};
//! Abstract interface for interpolation algorithms
/**
* For equidistant sampling.
*
*/
class IITAVDLInterpolationRoutine
{
public:
//! Human-readable name
virtual std::string GetName() const = 0;
//! Anzahl an Stützwerten, die linksseitig (Anfang) bzw. rechtseitig (Ende) benötigt werden
/**
* Auf dem Überlappungsbereich wird die Interpolation angewendet, auch wenn sie
* auf diesen keine gültigen Daten liefern kann. Erst ab dem Offset iLeft und bis
* zum Ende abzüglich iRight werden in der Interpolationsroutine korrekte Daten
* abgelegt.
*
* \param iLeft Anzahl Samples zur linken Seite (call-by-reference)
* \param 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, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset = 0 ) const = 0;
};
//! Linear interpolation (fast)
class 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, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset = 0 ) const;
};
//! Cubic-Spline interpolation (efficient)
class 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, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset = 0 ) const;
};
// Window size (entire samples of window, forced to be uneven)
#define WINDOWED_SINC_INTERPOLATION_WINDOW_SIZE 13
//! Windowed Sinc-Interpolation (costly)
class CITAVDLWindowedSincInterpolation : public IITAVDLInterpolationRoutine
{
public:
inline CITAVDLWindowedSincInterpolation() : m_iWindowSize( WINDOWED_SINC_INTERPOLATION_WINDOW_SIZE ) { 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, int iInputLength, int iInputStartOffset, ITASampleBuffer* pOutput, int iOutputLength, int iOutputOffset = 0 ) const;
private:
int m_iWindowSize;
};
#endif // IW_ITA_SIMO_VARIABLE_DELAY_LINE
This diff is collapsed.
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