Implemented and tested artificial reverb with 1, 3 or 10 reverberation time values

parent af276202
......@@ -65,6 +65,10 @@
#include <tbb/atomic.h>
#include <tbb/concurrent_queue.h>
#include <DspFilters/Dsp.h>
using namespace ITAConstants;
const double g_dMinReverberationTime = 0.25f; // Minimum allowed reverberation time [s]
const double g_dMinRoomVolume = 3 * 2 * 1; // Minimum allowed room volume [m^3]
const double g_dMinRoomSurfaceArea = 2 * ( 3 * 2 + 3 + 2 ); // Minimum allowed room surface area [m^2]
......@@ -210,6 +214,67 @@ private:
std::atomic< int > m_iState;
};
const int iOrder = 10;
const int iChannels = 1;
int iFilterLength = ( int ) pow( 2, 12 );
class CLowPassFilter : public CVABinauralArtificialReverbAudioRenderer::CFilterBase
{
public:
inline CLowPassFilter( float fLowerFrequencyCuttoff, float fSampleRate )
{
oFilterEngine.setup( iOrder, fSampleRate, fLowerFrequencyCuttoff );
};
inline void Process( int iNumSamples, float* pfInOut, bool bAutoReset = true )
{
if( bAutoReset )
oFilterEngine.reset();
oFilterEngine.process( iNumSamples, &pfInOut );
};
Dsp::SimpleFilter< Dsp::Butterworth::LowPass< iOrder >, iChannels > oFilterEngine;
};
class CHighPassFilter : public CVABinauralArtificialReverbAudioRenderer::CFilterBase
{
public:
inline CHighPassFilter( float fLowerFrequencyCuttoff, float fSampleRate )
{
oFilterEngine.setup( iOrder, fSampleRate, fLowerFrequencyCuttoff );
};
inline void Process( int iNumSamples, float* pfInOut, bool bAutoReset = true )
{
if( bAutoReset )
oFilterEngine.reset();
oFilterEngine.process( iNumSamples, &pfInOut );
};
Dsp::SimpleFilter< Dsp::Butterworth::HighPass< iOrder >, iChannels > oFilterEngine;
};
class CBandPassFilter : public CVABinauralArtificialReverbAudioRenderer::CFilterBase
{
public:
inline CBandPassFilter( float fCenterFrequency, float fBandwidth, float fSampleRate )
{
oFilterEngine.setup( iOrder, fSampleRate, fCenterFrequency, fBandwidth );
};
inline void Process( int iNumSamples, float* pfInOut, bool bAutoReset = true )
{
if( bAutoReset )
oFilterEngine.reset();
oFilterEngine.process( iNumSamples, &pfInOut );
};
Dsp::SimpleFilter< Dsp::Butterworth::BandPass< iOrder >, iChannels > oFilterEngine;
};
// --= Renderer =--
......@@ -251,7 +316,24 @@ void CVABinauralArtificialReverbAudioRenderer::Init( const CVAStruct& oArgs )
{
CVAConfigInterpreter conf( oArgs );
conf.OptNumber( "ReverberationTime", m_dReverberationTime, 0.71f );
std::string sReverberationTimes;
conf.OptString( "ReverberationTimes", sReverberationTimes );
m_vdReverberationTimes = StringToDoubleVec( sReverberationTimes );
std::string sReverberationTime;
conf.OptString( "ReverberationTime", sReverberationTime );
if( sReverberationTimes.empty() && !sReverberationTime.empty() )
{
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Deprecated ReverberationTime used in configuration, please switch to ReverberationTimes and provide a list of e.g. 3 values (low, mid, high). Approximating reverb." );
double dReverberationTime = StringToDouble( sReverberationTime );
m_vdReverberationTimes = { dReverberationTime * sqrt( 2 ), dReverberationTime, dReverberationTime / sqrt( 2 ) };
}
DesignEQFilters();
conf.OptNumber( "RoomVolume", m_dRoomVolume, 10.0f *10.0f * 2.0f );
conf.OptNumber( "RoomSurfaceArea", m_dRoomSurfaceArea, 4 * ( 10.0f * 2.0f ) + 2 * ( 2.0f * 2.0f ) );
conf.OptNumber( "SoundPowerCorrectionFactor", m_dSoundPowerCorrectionFactor, 0.05 );
......@@ -282,10 +364,14 @@ void CVABinauralArtificialReverbAudioRenderer::Init( const CVAStruct& oArgs )
else
ITA_EXCEPT1( INVALID_PARAMETER, "Unrecognized interpolation algorithm '" + sVLDInterpolationAlgorithm + "' in BinauralFreefieldAudioRendererConfig" );
if( m_dReverberationTime < g_dMinReverberationTime )
for( size_t i = 0; i < m_vdReverberationTimes.size(); i++ )
{
auto dReverberationTime = m_vdReverberationTimes[ i ];
if( dReverberationTime < g_dMinReverberationTime )
{
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << m_dReverberationTime << " s too small, minimum is " << g_dMinReverberationTime << " s." );
m_dReverberationTime = g_dMinReverberationTime;
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << dReverberationTime << " s too small, minimum is " << g_dMinReverberationTime << " s." );
m_vdReverberationTimes[ i ] = g_dMinReverberationTime;
}
}
if( m_dRoomSurfaceArea < g_dMinRoomSurfaceArea )
......@@ -303,6 +389,37 @@ void CVABinauralArtificialReverbAudioRenderer::Init( const CVAStruct& oArgs )
return;
}
void CVABinauralArtificialReverbAudioRenderer::DesignEQFilters()
{
if( m_vdReverberationTimes.size() == 3 )
{
m_vpFilter.clear();
m_vpFilter.push_back( std::make_shared< CLowPassFilter >( 330.0f, GetSampleRate() ) );
m_vpFilter.push_back( std::make_shared< CBandPassFilter >( 2300.0f, 4000.0f, GetSampleRate() ) );
m_vpFilter.push_back( std::make_shared< CHighPassFilter >( 3900.0f, GetSampleRate() ) );
}
else if( m_vdReverberationTimes.size() == 10 )
{
m_vpFilter.clear();
float fLowPassFrequencyCuttoff = OCTAVE_CENTER_FREQUENCIES_ISO_F[ 0 ] * sqrt( 2.0f );
m_vpFilter.push_back( std::make_shared< CLowPassFilter >( fLowPassFrequencyCuttoff, GetSampleRate() ) );
for( size_t i = 1; i < OCTAVE_CENTER_FREQUENCIES_ISO_F.size() - 1; i++ )
{
float fMidCenterFrequency = OCTAVE_CENTER_FREQUENCIES_ISO_F[ i ];
float fMidBandWidth = ( OCTAVE_CENTER_FREQUENCIES_ISO_F[ i + 1 ] - OCTAVE_CENTER_FREQUENCIES_ISO_F[ i - 1 ] ) / 2.15f;
m_vpFilter.push_back( std::make_shared< CBandPassFilter >( fMidCenterFrequency, fMidBandWidth, GetSampleRate() ) );
}
float fHighPassFrequencyCuttoff = OCTAVE_CENTER_FREQUENCIES_ISO_F[ 0 ] / sqrt( 2.0f );
m_vpFilter.push_back( std::make_shared< CHighPassFilter >( fHighPassFrequencyCuttoff, GetSampleRate() ) );
}
else
{
VA_ERROR( "BinauralArtificialReverbAudioRenderer", "Current implementation only accepts 3 reverb times for low, mid & high frequencies or octave resolution (8 values)" );
}
}
CVAObjectInfo CVABinauralArtificialReverbAudioRenderer::GetObjectInfo() const
{
CVAObjectInfo oInfo;
......@@ -332,7 +449,7 @@ CVAStruct CVABinauralArtificialReverbAudioRenderer::CallObject( const CVAStruct&
if( sValue == "STATUS" )
{
VA_PRINT( "RoomReverberationTime: " << m_dReverberationTime << " seconds" );
VA_PRINT( "RoomReverberationTimes: " << DoubleVecToString( m_vdReverberationTimes ) << " seconds" );
VA_PRINT( "MaxReverbFilterLengthSamples: " << m_iMaxReverbFilterLengthSamples << " samples (" << m_iMaxReverbFilterLengthSamples / GetSampleRate() << " seconds)" );
VA_PRINT( "RoomVolume: " << m_dRoomVolume << " m^3" );
VA_PRINT( "RoomSurfaceArea: " << m_dRoomSurfaceArea << " m^2" );
......@@ -356,25 +473,28 @@ CVAStruct CVABinauralArtificialReverbAudioRenderer::CallObject( const CVAStruct&
( pStruct->GetDatatype() == CVAStructValue::BOOL ) )
{
double dRoomReverberationTime = *pStruct;
for( size_t i = 0; i < m_vdReverberationTimes.size(); i++ )
if( dRoomReverberationTime < g_dMinReverberationTime )
{
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << m_dReverberationTime << "s too small, minimum is " << g_dMinReverberationTime << "s." );
m_dReverberationTime = g_dMinReverberationTime;
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << dRoomReverberationTime << "s too small, minimum is " << g_dMinReverberationTime << "s." );
m_vdReverberationTimes[ i ] = g_dMinReverberationTime;
}
else
{
m_dReverberationTime = dRoomReverberationTime;
}
VA_INFO( "BinauralArtificialReverbAudioRenderer", "Set reverberation time to " << m_dReverberationTime );
m_vdReverberationTimes[ i ] = dRoomReverberationTime;
}
VA_INFO( "BinauralArtificialReverbAudioRenderer", "Set reverberation time to " << dRoomReverberationTime );
if( m_dReverberationTime * GetSampleRate() > m_iMaxReverbFilterLengthSamples )
if( dRoomReverberationTime * GetSampleRate() > m_iMaxReverbFilterLengthSamples )
VA_WARN( "BinauralArtificialReverbAudioRenderer",
"Reverberation time greater than target filter length, reverb will be truncated at "
<< m_iMaxReverbFilterLengthSamples / GetSampleRate() << " seconds"
);
}
m_bForceARUpdateOnce = true;
DesignEQFilters();
UpdateArtificialReverbPaths();
return oReturn;
......@@ -462,7 +582,9 @@ CVAStruct CVABinauralArtificialReverbAudioRenderer::CallObject( const CVAStruct&
std::string sValue = toUppercase( *pStruct );
if( sValue == "ROOMREVERBERATIONTIME" )
oReturn[ "Return" ] = m_dReverberationTime;
oReturn[ "Return" ] = m_vdReverberationTimes[ 0 ];
else if( sValue == "ROOMREVERBERATIONTIMES" )
oReturn[ "Return" ] = DoubleVecToString( m_vdReverberationTimes );
else if( sValue == "ROOMVOLUME" )
oReturn[ "Return" ] = m_dRoomVolume;
else if( sValue == "ROOMSURFACEAREA" )
......@@ -581,6 +703,62 @@ ITADatasource* CVABinauralArtificialReverbAudioRenderer::GetOutputDatasource()
return this;
}
CVAStruct CVABinauralArtificialReverbAudioRenderer::GetParameters( const CVAStruct& oInArgs ) const
{
CVAStruct oRet;
std::vector< float > vf;
for( auto d : m_vdReverberationTimes )
vf.push_back( ( float ) d );
oRet[ "room_reverberation_times" ] = CVAStructValue( ( void* ) &vf[ 0 ], vf.size() * sizeof( float ) );
return oRet;
}
void CVABinauralArtificialReverbAudioRenderer::SetParameters( const CVAStruct& oInArgs )
{
if( oInArgs.HasKey( "room_reverberation_times" ) )
{
const auto& oReverbTimes( oInArgs[ "room_reverberation_times" ] );
if( oReverbTimes.IsDouble() )
{
double dReverbTime = oReverbTimes;
if( dReverbTime < g_dMinReverberationTime )
{
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << dReverbTime << " s too small, minimum is " << g_dMinReverberationTime << " s." );
dReverbTime = g_dMinReverberationTime;
}
m_vdReverberationTimes = { dReverbTime * sqrt( 2 ), dReverbTime, dReverbTime / sqrt( 2 ) };
DesignEQFilters();
UpdateArtificialReverbPaths( true );
}
else if( oReverbTimes.IsData() )
{
int iNumReverbTimes = int( oReverbTimes.GetDataSize() / sizeof( float ) );
if( iNumReverbTimes == 3 || iNumReverbTimes == 10 )
{
m_vdReverberationTimes.resize( iNumReverbTimes );
const auto pfReverbTimes = ( const float* ) oReverbTimes.GetData();
for( size_t i = 0; i < iNumReverbTimes; i++ )
if( pfReverbTimes[ i ] < g_dMinReverberationTime )
{
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Requested reverberation time of " << pfReverbTimes[ i ] << " s too small, minimum is " << g_dMinReverberationTime << " s." );
m_vdReverberationTimes[ i ] = g_dMinReverberationTime;
}
else
m_vdReverberationTimes[ i ] = pfReverbTimes[ i ];
}
else
VA_EXCEPT2( INVALID_PARAMETER, "Reverberation times must containt 1, 3 (low, mid, high) or 8 (octave) values" );
DesignEQFilters();
UpdateArtificialReverbPaths( true );
}
}
}
void CVABinauralArtificialReverbAudioRenderer::ManageArtificialReverbPaths( const CVASceneStateDiff* pDiff )
{
// ber aktuelle Pfade iterieren und gelschte markieren
......@@ -1226,14 +1404,15 @@ void CVABinauralArtificialReverbAudioRenderer::UpdateArtificialReverbFilter( Lis
// local copies
const double dRoomSurfaceArea = m_dRoomSurfaceArea;
const double dRoomVolume = m_dRoomVolume;
const double dReverberationTime = m_dReverberationTime;
assert( dReverberationTime > 0.0f );
const double dSoundPowerCorrectionFactor = m_dSoundPowerCorrectionFactor;
for( auto dReverberationTime : m_vdReverberationTimes )
{
assert( dReverberationTime > 0.0f );
if( ceil( dReverberationTime * GetSampleRate() ) > m_iMaxReverbFilterLengthSamples )
VA_WARN( "BinauralArtificialReverbAudioRenderer", "Target filter length smaller than requested reverberation time, truncating." )
ITASampleFrame sfBRIR( 2, m_iMaxReverbFilterLengthSamples, true ); // 0=L, 1=R, TODO member var?
}
if( pListener->pNewHRIR != nullptr )
{
......@@ -1243,28 +1422,48 @@ void CVABinauralArtificialReverbAudioRenderer::UpdateArtificialReverbFilter( Lis
}
CVADirectivityDAFFHRIR* pHRIR = ( CVADirectivityDAFFHRIR* ) pListener->pCurHRIR;
if( !pHRIR )
{
VA_ERROR( "BinauralArtificialReverbAudioRenderer", "Could not cast receiver HRIR to type CVADirectivityDAFFHRIR, please use a DAFF file with IR content." );
}
int iHRIRLength = pHRIR->GetProperties()->iFilterLength;
ITASampleFrame sfTempHRIR( 2, iHRIRLength, true ); // TODO member?
// Some prior assertions
assert( dRoomSurfaceArea > 0.0f );
assert( dRoomVolume > 0.0f );
assert( dReverberationTime > 0.0f );
assert( m_dTimeSlotResolution > 0.0f );
if( m_vdReverberationTimes.size() != m_vpFilter.size() )
{
VA_ERROR( "BinauralArtificialReverbAudioRenderer", "EQ filters not equalized, aborting reverb calculation" );
}
// START LAS -----------------------------------------------------------------------------------------
ITASampleFrame sfBRIR_temp( 2, m_iMaxReverbFilterLengthSamples, true ); // 0=L, 1=R, TODO member var? Temporal buffer per frequency
ITASampleFrame sfBRIR( 2, m_iMaxReverbFilterLengthSamples, true ); // Accumulated reverbs
for( size_t r = 0; r < m_vdReverberationTimes.size(); r++ )
{
const double& dReverberationTime( m_vdReverberationTimes[ r ] );
assert( dReverberationTime > 0.0f );
srand( 667 ); // fixed poisson sequence
sfBRIR_temp.zero();
const double dAverageLengthRoom = dRoomVolume / dRoomSurfaceArea;
const double dMeanfreePath = 4 * dAverageLengthRoom;
const double dMeanFreePath = 4 * dAverageLengthRoom;
const double dScatterReflectionFactor = 0.75f + ( m_dScatteringCoefficient / 4.0f );
const double dSamplesPerTimeSlot = GetSampleRate() * m_dTimeSlotResolution;
// get time of last image source
const double dTimeLastImageSource = dMeanfreePath / m_pCore->oHomogeneousMedium.dSoundSpeed;
const double dTimeLastImageSource = dMeanFreePath / m_pCore->oHomogeneousMedium.dSoundSpeed;
// calc critical distances and initial reverberation energies
const double dTimeConst = -13.816f / dReverberationTime;
......@@ -1374,11 +1573,11 @@ void CVABinauralArtificialReverbAudioRenderer::UpdateArtificialReverbFilter( Lis
fCurrentScaleFactorConstantPoissonEnergy;
int iDestOffset = iCurrentDiracPosition + iScatterDelayInSamples;
if( iDestOffset + iHRIRLength > sfBRIR.length() )
if( iDestOffset + iHRIRLength > sfBRIR_temp.length() )
continue;
//float* pfDestCh1 = sfBRIR[ 0 ].data();
sfBRIR.muladd_frame( sfTempHRIR, float( dCurrentScatterFactor ), 0, iDestOffset, iHRIRLength );
sfBRIR_temp.muladd_frame( sfTempHRIR, float( dCurrentScatterFactor ), 0, iDestOffset, iHRIRLength );
}
}
else if( bLateReflectionPhase )
......@@ -1399,13 +1598,17 @@ void CVABinauralArtificialReverbAudioRenderer::UpdateArtificialReverbFilter( Lis
break; // PROBLEM, kann man das als Prozess nicht klarer programmieren?
}
assert( int( iCurrentDiracPosition ) + iHRIRLength <= sfBRIR.length() );
assert( int( iCurrentDiracPosition ) + iHRIRLength <= sfBRIR_temp.length() );
//float* pfDestCh1 = sfBRIR[ 0 ].data();
sfBRIR.muladd_frame( sfTempHRIR, float( dCurrentScaleFactor ), 0, iCurrentDiracPosition, iHRIRLength );
sfBRIR_temp.muladd_frame( sfTempHRIR, float( dCurrentScaleFactor ), 0, iCurrentDiracPosition, iHRIRLength );
} // END PLACING DIRACS IN CURRENT TIMESLOT LOOP
} // END TIMESLOT LOOP
m_vpFilter[ r ]->Process( sfBRIR_temp[ 0 ].GetLength(), sfBRIR_temp[ 0 ].GetData() ); // Auto-resets filter engine
m_vpFilter[ r ]->Process( sfBRIR_temp[ 1 ].GetLength(), sfBRIR_temp[ 1 ].GetData() ); // Auto-resets filter engine
sfBRIR.add_frame( sfBRIR_temp );
}
// END LAS -----------------------------------------------------------------------------------------
......
......@@ -48,6 +48,7 @@
// STL includes
#include <list>
#include <set>
#include <memory>
// External VA forward declarations
class CVACoreImpl;
......@@ -88,6 +89,16 @@ public:
void ProcessStream( const ITAStreamInfo* pStreamInfo );
ITADatasource* GetOutputDatasource();
void SetParameters( const CVAStruct& );
CVAStruct GetParameters( const CVAStruct& ) const;
//! Filter base class for reverb equalization
class CFilterBase
{
public:
virtual void Process( int iNumSamples, float* pfOut, bool bAutoReset = true ) = 0;
};
protected:
//! Internal representation of a listener
......@@ -186,7 +197,9 @@ private:
CVACoreImpl* m_pCore; //!< Pointer to VACore
double m_dReverberationTime; //!< Reverberation time [s]
std::vector< double > m_vdReverberationTimes; //!< Reverberation times [s]
std::vector< std::shared_ptr< CFilterBase > > m_vpFilter; //!< Reverb EQ filters
double m_dRoomVolume; //!< Room volume [m^3]
double m_dRoomSurfaceArea; //!< Room surface area [m^2]
double m_dSoundPowerCorrectionFactor; //!< Sound power correction factor (BRIR vs. Direct Sound level)
......@@ -247,6 +260,9 @@ private:
*/
void Init( const CVAStruct& oArgs );
//! Design equalization filters for different reverberation times & base frequencies
void DesignEQFilters();
// --= Update internal lists (sources, listener, artificial reverb paths) =--
......
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