...
 
Commits (3)
......@@ -19,7 +19,8 @@
#ifndef IW_ITA_SIMO_VARIABLE_DELAY_LINE
#define IW_ITA_SIMO_VARIABLE_DELAY_LINE
#include <ITAAtomicPrimitives.h>
#include <ITADSPDefinitions.h>
#include <ITACriticalSection.h>
#include <ITADataLog.h>
#include <ITAStopWatch.h>
......@@ -69,7 +70,7 @@ class IITASampleInterpolationRoutine;
* - Wchst automatisch mit Setzen der Verzgerung
*/
class CITASIMOVariableDelayLine
class ITA_DSP_API CITASIMOVariableDelayLine
{
public:
//! Umsetzung der Verzgerungsnderung
......@@ -90,6 +91,27 @@ public:
{
std::atomic< float > fNewReadCursorSamples; //!< Read cursor that will be forwarded to on next processing block
std::atomic< float > fOldReadCursorSamples; //!< Read cursor from last calculation
//! Default constructor for read cursor with zero delay
inline CITAVDLReadCursor()
{
fNewReadCursorSamples = 0.0f;
fOldReadCursorSamples = 0.0f;
};
inline CITAVDLReadCursor( CITAVDLReadCursor& rhs )
{
fNewReadCursorSamples.store( rhs.fNewReadCursorSamples );
fOldReadCursorSamples.store( rhs.fOldReadCursorSamples );
};
inline CITAVDLReadCursor& operator =( const CITAVDLReadCursor& rhs )
{
fNewReadCursorSamples.store( rhs.fNewReadCursorSamples );
fOldReadCursorSamples.store( rhs.fOldReadCursorSamples );
return *this;
}
};
//! Konstruktor der variablen Verzgerungsleitung
......@@ -158,13 +180,13 @@ public:
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
......@@ -172,11 +194,24 @@ public:
bool RemoveCursor( const int iCursorID );
//! Checks existance of a cursor
/**
* @param[in] iCursorID Cursor identifier
* @return True, if cursor exists, false otherwise
*/
bool CursorExists( const int iCursorID ) const;
//! Returns the IDs of the cursors
//! Returns the IDs of the current cursors
/**
* @return Cursor identifier
*/
std::vector< int > GetCursorIDs() const;
//! Returns number of current cursors
/**
* @return Number of user cursors
*/
int GetNumCursors() const;
//! Overall current latency of a cursor in samples
/**
* \return Delay and fractional delay in samples (which is rounded during VDL processing)
......@@ -233,26 +268,47 @@ public:
void Clear();
//! Pushes a block of samples at front of delay line
/**
* Writes a block of samples to the input of the VDL.
* Does not increment block, has to be done manually (i.e. after read)
*
* @note block-oriented VDL usage is usually a three-step process: write-read-increment.
*
* @param[in] psbInput Buffer source for incoming samples
*/
void WriteBlock( const ITASampleBuffer* psbInput );
//! Reads a block of samples for a read cursor and switches to new delay
/**
* @note Incremet block processing after all cursors have benn processed / read.
*
* @param[in] iID Cursor identifier
* @param[out] psbOutput Buffer target for processed samples (must be initialized)
*/
void ReadBlock( const int iID, ITASampleBuffer* psbOutput );
//! Increments processing to next block
/**
* @note Make sure that all cursors have been processed, i.e. have read a block from
* the delay line. Otherwise, dropouts occur.
*/
void Increment();
//! 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 );
void ReadBlockAndIncrement( ITASampleFrame* psfOutput );
private:
double m_dSampleRate; //!< Audio-Abtastrate
int m_iBlockLength; //!< Audio-Blockgre
int m_iVDLBufferSize; //!< Gre des Puffers zum Speichern verzgerter Samples
ITASampleBuffer* m_psbVDLBuffer; //!< Puffer zum Speichern verzögerter Samples (variable Größe, mindestens 2xBlocklänge)
ITASampleBuffer* m_psbVDLBuffer; //!< Buffer for samples (variable size at multiples of block leng, but minimum is 2 blocks)
ITASampleBuffer* m_psbTemp; //!< Temporrer Puffer zum Arbeiten mit Samples (Gre: 2xBlocklnge) (das knnte evtl. knapp sein)
ITACriticalSection m_csBuffer; //!< Zugriff auf Puffer schtzen
......@@ -260,13 +316,14 @@ private:
int m_iMaxDelay; //!< Maximal einstellbare Verzgerung (hngt von Puffergre ab)
int m_iSwitchingAlgorithm; //!< Eingestellter Algorithmus zum Umschalten der Verzgerung
std::map< int, CITAVDLReadCursor > m_lUserCursors; //!< List of read cursors (managed by user)
std::map< int, CITAVDLReadCursor > m_lUserCursors; //!< List of read cursors (managed by user) @todo maybe enforced thread-safe access required
std::map< int, CITAVDLReadCursor > m_lInternalCursors; //!< List of read cursors (synced for processing)
int m_iFadeLength; //!< berblendlnge fr das Umschaltverfahren mittels Kreuzblende (Minimum von Blocklnge oder 32 Samples)
bool m_bStarted; //!< Statusvariable zur Initialisierung
bool m_bBenchmark;
ITAStopWatch m_swProcess; //!< StopWatch zur berwachung der Berechnungsschleife
IITASampleInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
......
#include "ITASIMOVariableDelayLine.h"
#include <ITASIMOVariableDelayLine.h>
#include <ITAFastMath.h>
#include <ITANumericUtils.h>
#include <ITASampleBuffer.h>
#include <ITASampleFrame.h>
#include <ITAInterpolation.h>
#include <spline.h>
......@@ -21,7 +22,8 @@ CITASIMOVariableDelayLine::CITASIMOVariableDelayLine( const double dSamplerate,
m_iSwitchingAlgorithm( iAlgorithm ),
m_psbVDLBuffer( nullptr ),
m_psbTemp( nullptr ),
m_pInterpolationRoutine( nullptr )
m_pInterpolationRoutine( nullptr ),
m_bBenchmark( false )
{
assert( dSamplerate > 0 );
assert( iBlocklength > 0 );
......@@ -48,7 +50,7 @@ CITASIMOVariableDelayLine::CITASIMOVariableDelayLine( const double dSamplerate,
void CITASIMOVariableDelayLine::Clear()
{
m_psbVDLBuffer->Zero();
m_psbVDLBuffer->Zero(); // Very important, because on small delays the potentially unititialized end of VDL is also read
m_psbTemp->Zero();
m_iWriteCursor = 0;
......@@ -58,16 +60,6 @@ void CITASIMOVariableDelayLine::Clear()
m_swProcess.reset();
}
void CITASIMOVariableDelayLine::WriteBlock( const ITASampleBuffer* psbInput )
{
if( !m_bStarted )
m_bStarted = true;
assert( m_iWriteCursor % m_iBlockLength == 0 );
m_psbVDLBuffer->write( psbInput, m_iBlockLength, 0, m_iWriteCursor );
m_iWriteCursor = ( m_iWriteCursor + m_iBlockLength ) % m_iVDLBufferSize;
}
CITASIMOVariableDelayLine::~CITASIMOVariableDelayLine()
{
delete m_psbVDLBuffer;
......@@ -131,7 +123,7 @@ void CITASIMOVariableDelayLine::ReserveMaximumDelaySamples( float fMaxDelaySampl
* nur durch den Konstruktor aufgerufen wird
*/
m_psbVDLBuffer = new ITASampleBuffer( iNewBufferSize, true );
m_psbVDLBuffer = new ITASampleBuffer( iNewBufferSize, true ); // Important! set all samples to zero.
m_iVDLBufferSize = iNewBufferSize;
m_iWriteCursor = 0;
......@@ -141,7 +133,7 @@ void CITASIMOVariableDelayLine::ReserveMaximumDelaySamples( float fMaxDelaySampl
// Puffer schon gross genug => Nichts tun...
if( m_psbVDLBuffer->length() >= iNewBufferSize ) return;
assert( m_psbVDLBuffer->length() >= 0 ); // VDL-Pufferlnge muss eine Gre haben
m_csBuffer.enter();
......@@ -151,7 +143,7 @@ void CITASIMOVariableDelayLine::ReserveMaximumDelaySamples( float fMaxDelaySampl
int iOldBufferSize = m_psbVDLBuffer->length();
// Vorhandene Daten zyklisch Kopieren (aktuelle Schreibposition => Anfang abrollen)
ITASampleBuffer* psbNewBuffer = new ITASampleBuffer( iNewBufferSize, true );
ITASampleBuffer* psbNewBuffer = new ITASampleBuffer( iNewBufferSize, true ); // Important! set all samples to zero.
psbNewBuffer->cyclic_write( m_psbVDLBuffer, iNewBufferSize, m_iWriteCursor, 0 );
// Alten Puffer freigeben, neuen zuweisen
......@@ -169,12 +161,41 @@ void CITASIMOVariableDelayLine::ReserveMaximumDelayTime( const float fMaxDelaySe
ReserveMaximumDelaySamples( fMaxDelaySecs * ( float ) m_dSampleRate );
}
int CITASIMOVariableDelayLine::AddCursor()
{
int iMaxNumber = -1;
std::vector< int > vrIDs = GetCursorIDs();
for( size_t i = 0; i < vrIDs.size(); i++ )
if( iMaxNumber < vrIDs[ i ] )
iMaxNumber = vrIDs[ i ];
const int iID = iMaxNumber + 1;
m_lUserCursors[ iID ] = CITAVDLReadCursor();
return iID;
}
bool CITASIMOVariableDelayLine::CursorExists( const int iCursorID ) const
{
std::map< int, CITAVDLReadCursor >::const_iterator cit = m_lUserCursors.find( iCursorID );
return ( cit != m_lUserCursors.end() );
}
std::vector< int > CITASIMOVariableDelayLine::GetCursorIDs() const
{
std::vector< int > viCursorIDs;
std::map< int, CITAVDLReadCursor >::const_iterator cit = m_lUserCursors.begin();
while( cit != m_lUserCursors.end() )
viCursorIDs.push_back( cit++->first );
return viCursorIDs;
}
int CITASIMOVariableDelayLine::GetNumCursors() const
{
return int( m_lUserCursors.size() );
}
float CITASIMOVariableDelayLine::GetCurrentDelaySamples( const int iCursorID ) const
{
if( !CursorExists( iCursorID ) )
......@@ -211,7 +232,7 @@ void CITASIMOVariableDelayLine::SetDelaySamples( const int iID, const float fDel
CITAVDLReadCursor& oReadCursor( m_lUserCursors[ iID ] );
oReadCursor.fNewReadCursorSamples = fDelaySamples;
// If not started, set both
// If not started, set both to avoid artifact on first block
if( !m_bStarted )
oReadCursor.fOldReadCursorSamples = fDelaySamples;
}
......@@ -221,6 +242,29 @@ void CITASIMOVariableDelayLine::SetDelayTime( const int iCursorID, const float f
SetDelaySamples( iCursorID, fDelaySeconds * ( float ) m_dSampleRate );
}
void CITASIMOVariableDelayLine::WriteBlock( const ITASampleBuffer* psbInput )
{
if( !m_bStarted )
m_bStarted = true;
assert( m_iWriteCursor % m_iBlockLength == 0 );
m_psbVDLBuffer->write( psbInput, m_iBlockLength, 0, m_iWriteCursor );
}
void CITASIMOVariableDelayLine::ReadBlockAndIncrement( ITASampleFrame* psfOutput )
{
assert( psfOutput );
const std::vector< int > viIDs = GetCursorIDs();
if( psfOutput->GetNumChannels() != GetNumCursors() )
ITA_EXCEPT1( INVALID_PARAMETER, "Mismatching channel number, provide a sample frame with same number of channels as cursors available" );
for( int i = 0; i < GetNumCursors(); i++ )
ReadBlock( viIDs[ i ], &( *psfOutput )[ i ] );
Increment();
}
void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer* psbOutput )
{
if( !CursorExists( iCursorID ) )
......@@ -228,23 +272,18 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
m_swProcess.start();
// Lokale Kopie des gewnschten Algorithmus (Atomare Membervariable)
int iAlgorithm = m_iSwitchingAlgorithm;
// Lokale Kopie der neuen Verzgerung
float fCurrentDelay = m_lUserCursors[ iCursorID ].fOldReadCursorSamples;
float fNewDelay = m_lUserCursors[ iCursorID ].fNewReadCursorSamples;
// --= Keine nderung der Verzgerung (fr rasant schnelle statische Szenen) =--
int iAlgorithmLocalCopy = m_iSwitchingAlgorithm;
if( fNewDelay == fCurrentDelay ) {
// Keine nderung der Verzgerung. Einfach Anfang der VDL in den Ausgang kopieren.
int iReadCursor = ( m_iWriteCursor + m_iVDLBufferSize - ( int ) ceil( fCurrentDelay ) ) % m_iVDLBufferSize;
float fCurrentDelayLocalCopy = m_lUserCursors[ iCursorID ].fOldReadCursorSamples;
float fNewDelayLocalCopy = m_lUserCursors[ iCursorID ].fNewReadCursorSamples;
// Use a fast-forward copy in case of no new delay (static situation)
if( fNewDelayLocalCopy == fCurrentDelayLocalCopy )
{
// No change of delay, simply copy samples from read cursor
int iReadCursor = ( m_iWriteCursor - ( int ) ceil( fCurrentDelayLocalCopy ) + m_iVDLBufferSize ) % m_iVDLBufferSize;
psbOutput->cyclic_write( m_psbVDLBuffer, m_iBlockLength, iReadCursor, 0 );
// Schreibzeiger um Blocklnge vergrern (BlockPointerIncrement)
m_iWriteCursor = ( m_iWriteCursor + m_iBlockLength ) % m_iVDLBufferSize;
return;
}
......@@ -252,18 +291,18 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
// --= nderung der Verzgerung =--
// Zerlegen in Ganzzahl und Kommazahl
int iCurrentIntDelay = ( int ) ceil( fCurrentDelay );
float fCurrentFracDelay = ( float ) iCurrentIntDelay - fCurrentDelay;
int iCurrentIntDelay = ( int ) ceil( fCurrentDelayLocalCopy );
float fCurrentFracDelay = ( float ) iCurrentIntDelay - fCurrentDelayLocalCopy;
assert( fCurrentFracDelay >= 0.0f ); // Subsample darf nicht negativ sein
int iNewIntDelay = ( int ) ceil( fNewDelay );
float fNewFracDelay = ( float ) iNewIntDelay - fNewDelay;
int iNewIntDelay = ( int ) ceil( fNewDelayLocalCopy );
float fNewFracDelay = ( float ) iNewIntDelay - fNewDelayLocalCopy;
assert( fNewFracDelay >= 0.0f ); // Subsample darf nicht negativ sein
int iDeltaDelay = iNewIntDelay - iCurrentIntDelay;
// Falls Interpolation gewnscht ist, Grenzen prfen
if( ( iAlgorithm == LINEAR_INTERPOLATION ) ||
( iAlgorithm == WINDOWED_SINC_INTERPOLATION ) ||
( iAlgorithm == CUBIC_SPLINE_INTERPOLATION ) )
if( ( iAlgorithmLocalCopy == LINEAR_INTERPOLATION ) ||
( iAlgorithmLocalCopy == WINDOWED_SINC_INTERPOLATION ) ||
( iAlgorithmLocalCopy == CUBIC_SPLINE_INTERPOLATION ) )
{
/*
......@@ -289,11 +328,11 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
// Resamplingfaktor auf dem Eingangsstream bezogen auf eine Blocklnge berechnen
float fResamplingFactor = 1 - iDeltaDelay / ( float ) m_iBlockLength;
// Wenn Voraussetzungen verletzt werden, fr diesen Bearbeitungsschritt auf weiches Umschalten wechseln
if ((fResamplingFactor <= MIN_RESAMPLING_FACTOR) || (fResamplingFactor > MAX_RESAMPLING_FACTOR))
if( ( fResamplingFactor <= MIN_RESAMPLING_FACTOR ) || ( fResamplingFactor > MAX_RESAMPLING_FACTOR ) )
{
iAlgorithm = CROSSFADE;
iAlgorithmLocalCopy = CROSSFADE;
}
}
......@@ -308,24 +347,24 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
// --= Umschaltverfahren =--
// TODO: - vermutlich sehen die Rmpfe fr jede andere Interpolation hnlich aus, sodass man mit getOverlap() vereinheitlichen kann.
// (LinInterp schon fertig, aber erst Erfahrungen mit den anderen Verfahren sammeln...)
int iSize;
int iLeft, iRight; // berlappung an den Grenzen
switch( iAlgorithm )
switch( iAlgorithmLocalCopy )
{
// o Hartes Umschalten
case SWITCH:
{
// Direkt neue Verzgerung nehmen. Einfach kopieren. Keine Rcksicht nehmen.
psbOutput->cyclic_write( m_psbVDLBuffer, m_iBlockLength, iReadCursorNew, 0 );
break;
}
// o Umschalten mittels Kreuzblende
case CROSSFADE:
{
// Kreuzblende mittels temporrem Puffer
assert( m_iFadeLength <= m_psbTemp->length() ); // Zu groe Blende fr diesen Puffer
m_psbTemp->cyclic_write( m_psbVDLBuffer, m_iFadeLength, iReadCursorCurrent, 0 );
......@@ -334,11 +373,11 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
psbOutput->Crossfade( m_psbTemp, 0, m_iFadeLength, ITASampleBuffer::CROSSFADE_FROM_SOURCE, ITASampleBuffer::COSINE_SQUARE );
break;
}
// o Umschalten durch Stauchen oder Strecken: Lineare Interpolation, Kubische Spline-Interpolation oder gefensterte Sinc-Interpolation
default:
{
/* Zur Erklrung:
Verringerung der Verzgerung => Samples stauchen
......@@ -358,7 +397,7 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
iSize = m_iBlockLength; // Kausalitt erzwingen, wenn der Abstand kleiner als linker berlappbereich ist
else
iSize = iCurrentIntDelay - iNewIntDelay + m_iBlockLength;
// Sicherstellen, dass Daten des rechten berlappungsbereichs vorhanden sind
// (VDL Puffer sollte immer mindestens eine Blocklnge grer sein als die
// aktuelle Verzgerung)
......@@ -381,18 +420,24 @@ void CITASIMOVariableDelayLine::ReadBlock( const int iCursorID, ITASampleBuffer*
m_pInterpolationRoutine->Interpolate( m_psbTemp, iInputLength, iInputStartOffset, psbOutput, m_iBlockLength );
break;
}
} // end case switch
// Neue Verzgerung speichern
m_lUserCursors[ iCursorID ].fOldReadCursorSamples = fNewDelay;
m_lUserCursors[ iCursorID ].fOldReadCursorSamples = fNewDelayLocalCopy;
// Zeitnahme
double t = m_swProcess.stop();
// Schreibzeiger inkrementieren
// (Hinweis: Der Schreibzeiger ist immer Vielfaches der Blocklnge)
m_iWriteCursor = ( m_iWriteCursor + m_iBlockLength ) % m_iVDLBufferSize;
if( m_bBenchmark && t > double( m_iBlockLength ) / m_dSampleRate )
std::cerr << "[ SIMOVDL ] Stream processing panic, reading block for cursor " << iCursorID << " took too long: " << t << std::endl;
// Zeitnahme
double t = m_swProcess.stop();
return;
}
void CITASIMOVariableDelayLine::Increment()
{
// Increment write cursor by one block (BlockPointerIncrement)
m_iWriteCursor = ( m_iWriteCursor + m_iBlockLength ) % m_iVDLBufferSize;
}
......@@ -39,6 +39,16 @@ vista_create_default_info_file( ITADSPVariableDelayLineTest )
set_property( TARGET ITADSPVariableDelayLineTest PROPERTY FOLDER "ITACoreLibs/Tests/ITADSP" )
add_executable( ITADSPSIMOVDLTest ITADSPSIMOVDLTest.cpp )
target_link_libraries( ITADSPSIMOVDLTest ${VISTA_USE_PACKAGE_LIBRARIES} )
vista_configure_app( ITADSPSIMOVDLTest )
vista_install( ITADSPSIMOVDLTest )
vista_create_default_info_file( ITADSPSIMOVDLTest )
set_property( TARGET ITADSPSIMOVDLTest PROPERTY FOLDER "ITACoreLibs/Tests/ITADSP" )
add_executable( ITADSPThirdOctaveFilterGeneratorTest ITADSPThirdOctaveFilterGeneratorTest.cpp )
target_link_libraries( ITADSPThirdOctaveFilterGeneratorTest ${VISTA_USE_PACKAGE_LIBRARIES} )
......@@ -49,6 +59,16 @@ vista_create_default_info_file( ITADSPThirdOctaveFilterGeneratorTest )
set_property( TARGET ITADSPThirdOctaveFilterGeneratorTest PROPERTY FOLDER "ITACoreLibs/Tests/ITADSP" )
add_executable( ITADSPSIMOVDLSourceInShoebox ITADSPSIMOVDLSourceInShoebox.cpp )
target_link_libraries( ITADSPSIMOVDLSourceInShoebox ${VISTA_USE_PACKAGE_LIBRARIES} )
vista_configure_app( ITADSPSIMOVDLSourceInShoebox )
vista_install( ITADSPSIMOVDLSourceInShoebox )
vista_create_default_info_file( ITADSPSIMOVDLSourceInShoebox )
set_property( TARGET ITADSPSIMOVDLSourceInShoebox PROPERTY FOLDER "ITACoreLibs/Tests/ITADSP" )
add_executable( ITADSPThirdOctaveFilterbankTest ITADSPThirdOctaveFilterbankTest.cpp )
target_link_libraries( ITADSPThirdOctaveFilterbankTest ${VISTA_USE_PACKAGE_LIBRARIES} )
......
#include <ITAThirdOctaveFIRFilterGenerator.h>
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#include <ITABiquad.h>
#include <ITAThirdOctaveFilterbank.h>
#include <ITAThirdOctaveFIRFilterGenerator.h>
#include <ITAAudiofileWriter.h>
#include <ITAStringUtils.h>
......
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
* Circulates a sound source in a shoebox room including specular
* reflections off walls (perfectly hard).
*
*/
#include <ITASIMOVariableDelayLine.h>
#include <ITAStringUtils.h>
#include <ITAAudiofileWriter.h>
#include <ITASampleBuffer.h>
#include <ITASampleFrame.h>
#include <ITAStreamFunctionGenerator.h>
#include <ITAFileDataSource.h>
#include <ITAStreamInfo.h>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const float fSimulateSeconds = 20; // s
const float fShoeboxLength = 10.0f; // m
const float fShoeboxWidth = 7.0f; // m
const float fShoeboxHeight = 3.0f; // m
const float fCircleRadiusHorizontal = 3.0f; // m
const float fCircleDuration = 3.0f; // s
const float fSpeedOfSound = 343.0f; // m/s
const string sInFilePath = "CirculatingSource_Signal.wav";
const string sOutFilePath = "CirculatingSource_ShoeboxRoom.wav";
const unsigned int iBlockLength = 128;
const double dSampleRate = 44.1e3;
float DistanceToPropagationTime( const float fDistanceMeter )
{
return fDistanceMeter / fSpeedOfSound;
}
int main( int, char** )
{
assert( fCircleRadiusHorizontal * 2 < fShoeboxLength );
assert( fCircleRadiusHorizontal * 2 < fShoeboxWidth );
assert( 1.7f < fShoeboxHeight );
ITAStreamFunctionGenerator sinesignal( 1, dSampleRate, iBlockLength, ITAStreamFunctionGenerator::SINE, 500.0f, 0.5f, true );
ITAFileDatasource filesignal( "cl-mod-bb-piece-32.wav", iBlockLength, true );
ITADatasource* pIntputStream = &filesignal;
assert( fShoeboxLength > fShoeboxWidth );
const float fMaxReservedDelaySamples = float( fShoeboxLength * pow( 2, 1 ) / double( fSpeedOfSound ) * dSampleRate );
CITASIMOVariableDelayLine* pSIMOVDL = new CITASIMOVariableDelayLine( dSampleRate, iBlockLength, fMaxReservedDelaySamples, CITASIMOVariableDelayLine::CUBIC_SPLINE_INTERPOLATION );
unsigned int uiNumberOfFrames = ( unsigned int ) ceil( dSampleRate * fSimulateSeconds / ( float ) iBlockLength );
// OpenGL coordinates
const int iCursorDirect = pSIMOVDL->AddCursor();
const int iCursorPositiveX = pSIMOVDL->AddCursor();
const int iCursorNegativeX = pSIMOVDL->AddCursor();
const int iCursorPositiveY = pSIMOVDL->AddCursor();
const int iCursorNegativeY = pSIMOVDL->AddCursor();
const int iCursorPositiveZ = pSIMOVDL->AddCursor();
const int iCursorNegativeZ = pSIMOVDL->AddCursor();
ITAAudiofileProperties props_in;
props_in.iChannels = 1;
props_in.dSampleRate = dSampleRate;
props_in.eQuantization = ITAQuantization::ITA_FLOAT;
props_in.eDomain = ITADomain::ITA_TIME_DOMAIN;
props_in.iLength = ( unsigned int ) uiNumberOfFrames * iBlockLength;
ITAAudiofileWriter* writer_in = ITAAudiofileWriter::create( sInFilePath, props_in );
ITAAudiofileProperties props_out( props_in );
props_out.iChannels = pSIMOVDL->GetNumCursors();
ITAAudiofileWriter* writer_out = ITAAudiofileWriter::create( sOutFilePath, props_out );
ITAStreamInfo oState;
ITASampleBuffer* psbInput = new ITASampleBuffer( iBlockLength, true );
ITASampleFrame* psfOutput = new ITASampleFrame( pSIMOVDL->GetNumCursors(), iBlockLength, true );
cout << "Input file: " << sInFilePath << endl;
cout << "Processing ";
unsigned int n = 0;
while( n < uiNumberOfFrames )
{
// Set new delays
const double dT = double( n ) * double( iBlockLength ) / dSampleRate;
const float fX = fCircleRadiusHorizontal * float( sin( dT / fCircleDuration ) );
const float fY = 1.7f;
const float fZ = -fCircleRadiusHorizontal * float( cos( dT / fCircleDuration ) );
// Direct
const float fDelay = DistanceToPropagationTime( sqrt( fX * fX + fZ * fZ ) );
pSIMOVDL->SetDelayTime( iCursorDirect, fDelay );
// Reflection positive X (right wall)
const float fX_ReflectionPositiveX = fShoeboxWidth - fX;
pSIMOVDL->SetDelayTime( iCursorPositiveX, DistanceToPropagationTime( sqrt( fX_ReflectionPositiveX * fX_ReflectionPositiveX + fZ * fZ ) ) );
// Reflection negative X (left wall)
const float fX_ReflectionNegativeX = -fShoeboxWidth + fX;
pSIMOVDL->SetDelayTime( iCursorNegativeX, DistanceToPropagationTime( sqrt( fX_ReflectionNegativeX * fX_ReflectionNegativeX + fZ * fZ ) ) );
// Reflection positive Y (ceiling)
const float fY_ReflectionPositiveY = fShoeboxHeight - fY;
pSIMOVDL->SetDelayTime( iCursorPositiveY, DistanceToPropagationTime( sqrt( fX * fX + fY_ReflectionPositiveY * fY_ReflectionPositiveY + fZ * fZ ) ) );
// Reflection negative Y (floor)
const float fY_ReflectionNegativeY = -fShoeboxHeight + fY;
pSIMOVDL->SetDelayTime( iCursorNegativeY, DistanceToPropagationTime( sqrt( fX * fX + fY_ReflectionNegativeY * fY_ReflectionNegativeY + fZ * fZ ) ) );
// Reflection positive Z (rear wall)
const float fZ_ReflectionPositiveZ = fShoeboxLength - fZ;
pSIMOVDL->SetDelayTime( iCursorPositiveZ, DistanceToPropagationTime( sqrt( fX * fX + fZ_ReflectionPositiveZ * fZ_ReflectionPositiveZ ) ) );
// Reflection negative Z (front wall)
const float fZ_ReflectionNegativeZ = -fShoeboxLength + fZ;
pSIMOVDL->SetDelayTime( iCursorNegativeZ, DistanceToPropagationTime( sqrt( fX * fX + fZ_ReflectionNegativeZ * fZ_ReflectionNegativeZ ) ) );
// Process
psbInput->write( pIntputStream->GetBlockPointer( 0, &oState ), iBlockLength );
pSIMOVDL->WriteBlock( psbInput );
pSIMOVDL->ReadBlockAndIncrement( psfOutput );
std::vector< float* > pIn;
pIn.push_back( psbInput->data() );
writer_in->write( iBlockLength, pIn );
writer_out->write( psfOutput, iBlockLength );
n++;
pIntputStream->IncrementBlockPointer();
if( n % ( uiNumberOfFrames / 40 ) == 0 )
cout << ".";
}
cout << " done." << endl;
cout << "Output file: " << sOutFilePath << endl;
delete writer_in;
delete writer_out;
delete psbInput;
delete psfOutput;
return 255;
}
%% load
simo_i = ita_read( 'CirculatingSource_Signal.wav' );
simo_o = ita_read( 'CirculatingSource_ShoeboxRoom.wav' );
simo_io = ita_merge( simo_i, simo_o );
%% prepare
simo_io_snipped = ita_time_crop( simo_io, [ 2 2.1 ], 'time' );
simo_io_snipped.comment = 'Circulating source in a shoebox room (10x7x3)';
simo_io_snipped.channelNames = { 'Input signal', ...
'Direct sound', ...
'Reflection right wall', ...
'Reflection left wall', ...
'Reflection ceiling', ...
'Reflection floor', ...
'Reflection rear wall', ...
'Reflection front wall', ...
};
%% plot
simo_io_snipped.pt
%% merge
source_shoebox_auralization_mono = ita_sum( simo_o );
ita_write( ita_normalize_dat( source_shoebox_auralization_mono ), 'CirculatingSource_ShoeboxRoom_Mono.wav', 'overwrite' );
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
* Processes a sine signal through single-input multiple-output
* variable delay line with two cursors at different delays and
* exports the i/o streams to hard drive.
*
*/
#include <ITASIMOVariableDelayLine.h>
#include <ITAStringUtils.h>
#include <ITAAudiofileWriter.h>
#include <ITASampleBuffer.h>
#include <ITASampleFrame.h>
#include <ITAStreamFunctionGenerator.h>
#include <ITAFileDataSource.h>
#include <ITAStreamInfo.h>
#include <iostream>
#include <math.h>
#include <vector>
using namespace std;
const unsigned int iBlockLength = 128;
const double dSampleRate = 44.1e3;
const float fMaxReservedDelaySamples = 5 * iBlockLength;
const float fSimulateSeconds = 10;
const float fInitialDelaySamples = 10 * 2 * iBlockLength - 1;
const string sInFilePath = "SIMOVDL_in.wav";
const string sOutFilePath = "SIMOVDL_out.wav";
int main( int, char** )
{
ITAStreamFunctionGenerator sinesignal( 1, dSampleRate, iBlockLength, ITAStreamFunctionGenerator::SINE, 500.0f, 0.9f, true );
//ITAFileDatasource filesignal( "cl-mod-bb-piece-32.wav", iBlockLength, true );
ITADatasource* pIntputStream = &sinesignal;
CITASIMOVariableDelayLine* pSIMOVDL = new CITASIMOVariableDelayLine( dSampleRate, iBlockLength, fMaxReservedDelaySamples, CITASIMOVariableDelayLine::CUBIC_SPLINE_INTERPOLATION );
double dSamplerate = dSampleRate;
unsigned int uiBlocklength = iBlockLength;
unsigned int uiNumberOfFrames = ( unsigned int ) std::ceil( dSamplerate * fSimulateSeconds / ( float ) uiBlocklength );
int iCursor0 = pSIMOVDL->AddCursor();
pSIMOVDL->SetDelaySamples( iCursor0, .0f );
int iCursor1 = pSIMOVDL->AddCursor();
pSIMOVDL->SetDelaySamples( iCursor1, 11.0f );
int iCursor2 = pSIMOVDL->AddCursor();
pSIMOVDL->SetDelaySamples( iCursor2, float( uiBlocklength * 2 ) );
int iCursor3 = pSIMOVDL->AddCursor();
pSIMOVDL->SetDelaySamples( iCursor3, float( uiBlocklength * 3 ) );
ITAAudiofileProperties props_in;
props_in.iChannels = 1;
props_in.dSampleRate = dSamplerate;
props_in.eQuantization = ITAQuantization::ITA_FLOAT;
props_in.eDomain = ITADomain::ITA_TIME_DOMAIN;
props_in.iLength = uiNumberOfFrames * uiBlocklength;
ITAAudiofileWriter* writer_in = ITAAudiofileWriter::create( sInFilePath, props_in );
ITAAudiofileProperties props_out( props_in );
props_out.iChannels = pSIMOVDL->GetNumCursors();
ITAAudiofileWriter* writer_out = ITAAudiofileWriter::create( sOutFilePath, props_out );
ITAStreamInfo oState;
ITASampleBuffer* psbInput = new ITASampleBuffer( uiBlocklength, true );
ITASampleFrame* psfOutput = new ITASampleFrame( pSIMOVDL->GetNumCursors(), uiBlocklength, true );
cout << "Input file: " << sInFilePath << endl;
cout << "Processing ";
unsigned int n = 0;
while( n < uiNumberOfFrames )
{
// Add new samples
psbInput->write( pIntputStream->GetBlockPointer( 0, &oState ), uiBlocklength );
pSIMOVDL->WriteBlock( psbInput );
pSIMOVDL->ReadBlockAndIncrement( psfOutput );
std::vector< float* > pIn;
pIn.push_back( psbInput->data() );
writer_in->write( uiBlocklength, pIn );
writer_out->write( psfOutput, uiBlocklength );
n++;
pIntputStream->IncrementBlockPointer();
if( n % ( uiNumberOfFrames / 40 ) == 0 )
cout << ".";
}
cout << " done." << endl;
cout << "Output file: " << sOutFilePath << endl;
delete writer_in;
delete writer_out;
delete psbInput;
delete psfOutput;
return 255;
}
%% load
simo_io = ita_merge( ita_read( 'SIMOVDL_in.wav' ), ita_read( 'SIMOVDL_out.wav' ) );
%% prepare
simo_io_snipped = ita_time_crop( simo_io, [ 1 512 ], 'samples' );
simo_io_snipped.comment = 'Single-Input Multiple-Output Variable Delay Line';
simo_io_snipped.channelNames = { 'Sine signal in', ...
'Read cursor 1 out (no delay)', ...
'Read cursor 2 out (11 samples delay)', ...
'Read cursor 3 out (2 blocks delay)', ...
'Read cursor 4 out (3 blocks delay)' };
%% plot
simo_io_snipped.pt
\ No newline at end of file
......@@ -26,7 +26,7 @@ void TestThirdOctaveFilterbankIIR()
const int iSampleLength = ( 1 << 17 );
CITAThirdOctaveFilterbank* pIIRFilterbank = CITAThirdOctaveFilterbank::Create( g_dSampleRate, iSampleLength, CITAThirdOctaveFilterbank::IIR_BIQUADS_ORDER10 );
ITASampleBuffer x( iSampleLength );
ITASampleBuffer x( iSampleLength, true );
x[ 0 ] = 1.0f;
CITAThirdOctaveGainMagnitudeSpectrum oMags;
......
......@@ -20,15 +20,31 @@ const float fMaxReservedDelaySamples = 5 * iBlockLength;
const float fSimulateSeconds = 10;
const float fInitialDelaySamples = 10 * 2 * iBlockLength - 1;
int iSign = 0;
void WriteFromDatasourceToFile( vector< CITAVariableDelayLine* >, ITADatasource*, float );
void TestVDLProcessing();
void preProcessVDL( std::vector<CITAVariableDelayLine*> vpVDLs, unsigned int iCount );
void test7_lininterp();
void WriteFromDatasourceToFile( std::vector< CITAVariableDelayLine* >, ITADatasource*, float );
int main( int, char** )
{
TestVDLProcessing();
//test1_crossfade();
//test2_lininterp();
//test3_lininterp();
//test4_lininterp();
//test5_lininterp();
//test6_lininterp();
//test7_lininterp();
return 255;
}
void TestVDLProcessing()
{
std::cout << " * VDL test" << std::endl;
cout << " * VDL test" << endl;
//ITAStreamFunctionGenerator sinesignal(1, dSampleRate, iBlockLength, ITAStreamFunctionGenerator::SINE, 550, 1, true);
ITAStreamFunctionGenerator sinesignal( 1, dSampleRate, iBlockLength, ITAStreamFunctionGenerator::SINE, 500.0f, 0.9f, true );
CITAVariableDelayLine* pVDLSwitch = new CITAVariableDelayLine( dSampleRate, iBlockLength, fMaxReservedDelaySamples, CITAVariableDelayLine::SWITCH );
......@@ -43,7 +59,7 @@ void TestVDLProcessing()
pVDLSpline->SetDelaySamples( fInitialDelaySamples );
pVDLSinc->SetDelaySamples( fInitialDelaySamples );
std::vector<CITAVariableDelayLine*> vpVDLs;
vector< CITAVariableDelayLine* > vpVDLs;
vpVDLs.push_back( pVDLSwitch );
vpVDLs.push_back( pVDLCross );
vpVDLs.push_back( pVDLLin );
......@@ -52,32 +68,9 @@ void TestVDLProcessing()
WriteFromDatasourceToFile( vpVDLs, &sinesignal, fSimulateSeconds );
/* --= Known bugs =--
-- Beschreibung
-- Vermutungen
-- Sonstiges
o SWITCH klingt richtig schlecht bereits bei einem Sample Delay pro Block, uerst gutes Beispiel wie man es nicht machen sollte :)
o Wow VA_DEBUG_PRINTF bentigt pro Aufruf etwa 500 us ...
--= TODOs =--
o Grenzen testen: was passiert, wenn man unterhalb der Blocklnge verzgern will?
o Evtl. auf den einen Block Latenz verzichten? Dadurch etwas mehr Rechenaufwand wg. Abfrage Buffer-Underflow
*/
//int iCubicSplineInterpIndex; // to be skipped in loop
for( int i = 0; i < ( int ) vpVDLs.size(); i++ )
//if (vpVDLs[i]->GetAlgorithm() != CITAVariableDelayLine::CUBIC_SPLINE_INTERPOLATION)
delete vpVDLs[ i ];
//else
//iCubicSplineInterpIndex = i;
// Delete Cubic Spline VDL at last to override VDL.log with this specific algorithm
//delete vpVDLs[iCubicSplineInterpIndex];
vpVDLs.clear();
}
......@@ -133,7 +126,8 @@ void preProcessVDL( std::vector<CITAVariableDelayLine*> vpVDLs, unsigned int iCo
//VA_DEBUG_PRINTF(" * [VDL] New delay = %f\n", fNewDelaySamples);
}
void WriteFromDatasourceToFile( std::vector<CITAVariableDelayLine*> vpVDL, ITADatasource* pSource, float fSeconds ) {
void WriteFromDatasourceToFile( std::vector<CITAVariableDelayLine*> vpVDL, ITADatasource* pSource, float fSeconds )
{
double dSamplerate = dSampleRate;
unsigned int uiBlocklength = iBlockLength;
unsigned int uiNumberOfFrames = ( unsigned int ) std::ceil( dSamplerate * fSeconds / ( float ) uiBlocklength );
......@@ -383,18 +377,3 @@ void test1_crossfade() {
delete vdl;
}
int main( int, char** )
{
//test1_crossfade();
//test2_lininterp();
//test3_lininterp();
//test4_lininterp();
//test5_lininterp();
//test6_lininterp();
//test7_lininterp();
TestVDLProcessing();
return 255;
}