Commit 7d528cb1 authored by henryjandrew's avatar henryjandrew

added a VDL to Outdoorsource, and an initial implementation of IIR filtering,...

added a VDL to Outdoorsource, and an initial implementation of IIR filtering, interpolation, and ITD correction to the WaveFront
parent fc0bc2d2
......@@ -117,9 +117,11 @@ void CVABinauralOutdoorNoiseRenderer::Init( const CVAStruct& oArgs )
m_oDefaultWaveFrontConf.blockLength = GetBlocklength();
m_oDefaultWaveFrontConf.sampleRate = GetSampleRate();
m_oDefaultWaveFrontConf.iFilterOrder = 4; //TODO: get these from the ini file as above
m_oDefaultWaveFrontConf.IR_length = 1024;
m_oDefaultWaveFrontConf.FilterDesignAlgorithm = CVABinauralOutdoorWaveFront::BURG;
conf.OptInteger("FilterOrder", m_oDefaultWaveFrontConf.iFilterOrder, 4);
conf.OptInteger("ImpulseResponseLength", m_oDefaultWaveFrontConf.IR_length, 1024);
conf.OptInteger("BufferSize", m_oDefaultWaveFrontConf.BufferSize, 1124);
m_oDefaultWaveFrontConf.FilterDesignAlgorithm = CVABinauralOutdoorWaveFront::BURG; //no other algorthms currently implemented
VAConfig::blockLength = GetBlocklength();
VAConfig::hrirLength = m_iHRIRFilterLength;
......@@ -148,10 +150,11 @@ void CVABinauralOutdoorNoiseRenderer::SetParameters(const CVAStruct& oInArgs)
const CVAStruct path = path_value;
//This is the unique string which identifies the current path for which data is being given (stored in "path")
std::string path_ID = path["path"]; //get path identifier from simulation engine. In future iterations, this will be generated within VA
int path_ID = path["ID"]; //get path identifier from simulation engine. In future iterations, this will be generated within VA
bool delete_path = path["delete"];
if (delete_path ) {
m_pClusterEngine->RemoveWaveFront(path_ID); //remove wavefront from the clustering engine
m_mPaths.erase(path_ID); //delete the sound path from the map
}
else {
......@@ -159,8 +162,18 @@ void CVABinauralOutdoorNoiseRenderer::SetParameters(const CVAStruct& oInArgs)
if (path_pos == m_mPaths.end()) { //if the path ID is not found, create a new path with this ID
CVABinauralOutdoorWaveFront* wave_front = dynamic_cast<CVABinauralOutdoorWaveFront*>(m_pWaveFrontPool->RequestObject()); //request a new path from the pool
m_mPaths.insert(std::pair<std::string, CVABinauralOutdoorWaveFront*>(path_ID, wave_front)); //put that path into the map with the label path_ID
m_mPaths.insert(std::pair<int, CVABinauralOutdoorWaveFront*>(path_ID, wave_front)); //put that path into the map with the label path_ID
//TODO: might need to add a reference to the new path here? *******************************************
m_mPaths[path_ID]->hasValidTrajectory = true; //TODO: check if this should be checked in some fn rather than just alway set
int path_source_ID = path["source"]; //get source ID from incoming data
m_mPaths[path_ID]->setSource(m_mSources[path_source_ID]); //set the source for this path
int path_receiver_ID = path["receiver"];
m_mPaths[path_ID]->setReceiver( m_mBinauralReceivers[path_receiver_ID] ); //set the sound receiver for this path
//m_pClusterEngine->AddWaveFront(path_ID, m_mPaths[path_ID]); //add new wave front to the clustering engine
}
CVAStructValue sMags = path["frequency_magnitudes"]; //
......@@ -175,16 +188,28 @@ void CVABinauralOutdoorNoiseRenderer::SetParameters(const CVAStruct& oInArgs)
Mags.SetMagnitude(i, pfMags[i]);
}
m_mPaths[path_ID]->setFilterCoefficients(Mags); //update the filter coefficients internal to the path based on the target frequency magnitudes
double delay = path["delay"];
m_mPaths[path_ID]->setDelay(delay); //update the delay for this path ********METHOD NOT YET IMPLEMENTED
m_mPaths[path_ID]->setDelay(delay);
m_mPaths[path_ID]->setMotion(); //update the motion of the path **********METHOD NOT YET IMPLEMENTED
//TODO: add extra "setters" for wave front delay, position/motion e.t.c
CVAStructValue position = path["position"]; //
num_values = position.GetDataSize() / sizeof(float); //currently only accepts third octave values, this is a safety check
if (num_values != 3)
VA_EXCEPT1("CVABinauralOutdoorNoiseRenderer: Expected a position vector with 3 components.");
void *pvposition = position.GetData();
const float* pfposition = (const float*)pvposition; //convert to float values
m_mPaths[path_ID]->v3PredictedPos.Set(pfposition[0], pfposition[1], pfposition[2]);
//m_mPaths[path_ID]->setMotion(); //update the motion of the path **********METHOD NOT YET IMPLEMENTED (dont think needed)
//TODO: add extra "setters" for wave front delay, position/motion e.t.c
if (path_pos == m_mPaths.end()) { //if the path ID is not found, create a new path with this ID
m_pClusterEngine->AddWaveFront(path_ID, m_mPaths[path_ID]); //add new wave front to the clustering engine
}
}
it++; //increment iterator for the next path
}
......
......@@ -68,7 +68,7 @@ private:
std::map< int, CVABinauralOutdoorSource* > m_mSources;
std::map< int, CVABinauralClusteringDirectionReceiver* > m_mBinauralReceivers;
std::map< std::string, CVABinauralOutdoorWaveFront* > m_mPaths; // All path of entire scene for all sources and receivers
std::map< int, CVABinauralOutdoorWaveFront* > m_mPaths; // All path of entire scene for all sources and receivers
//map containing all the paths, each accessed with a unique string key
CVABinauralClusteringEngine* m_pClusterEngine;
......
......@@ -2,8 +2,12 @@
CVABinauralOutdoorSource::CVABinauralOutdoorSource()
{
const int max_delay_samples = 1024; //TODO: take this number from ini file instead
pVDL = new CITASIMOVariableDelayLineBase(max_delay_samples);
}
CVABinauralOutdoorSource::~CVABinauralOutdoorSource()
{
delete pVDL;
}
#ifndef IW_VACORE_BINAURAL_OUTDOOR_SOURCE
#define IW_VACORE_BINAURAL_OUTDOOR_SOURCE
#include <ITASIMOVariableDelayLine.h>
#include <ITASIMOVariableDelayLineBase.h>
#include "../../../Motion/VASharedMotionModel.h"
#include "../../../Scene/VASoundSourceState.h"
......@@ -21,7 +21,7 @@ public:
CVASoundSourceDesc* pData;
CVASharedMotionModel* pMotionModel;
CITASIMOVariableDelayLine* pVDL;
CITASIMOVariableDelayLineBase* pVDL;
private:
};
......
......@@ -9,30 +9,35 @@
#include "../../../Scene/VAScene.h"
#include "../../../ITACoreLibs/ITADSP/include/ITAIIRFilterGenerator.h"
#include <ITAVariableDelayLine.h>
#include <ITASIMOVariableDelayLineBase.h>
#include <ITASampleBuffer.h>
#include <ITAThirdOctaveFIRFilterGenerator.h>
#define MIN_RESAMPLING_FACTOR 0.0f // [0, inf) ... aber sinnvoll z.B. 0, 0.1, 0.5 (< 1)
#define MAX_RESAMPLING_FACTOR 2.5f // [0, inf) ... aber sinnvoll z.B. 1, 1.5, 2, 3 (> 1)
CVABinauralOutdoorWaveFront::CVABinauralOutdoorWaveFront( const CVABinauralOutdoorWaveFront::Config& conf )
: oConf( conf )
{
vdlChL = new CITAVariableDelayLine( conf.sampleRate, conf.blockLength, (const float)( 3. * conf.sampleRate ), CITAVariableDelayLine::CUBIC_SPLINE_INTERPOLATION );
vdlChR = new CITAVariableDelayLine( conf.sampleRate, conf.blockLength, (const float)( 3. * conf.sampleRate ), CITAVariableDelayLine::CUBIC_SPLINE_INTERPOLATION );
if (conf.FilterDesignAlgorithm == BURG)
IIRFilterCoeffs.Initialise(conf.iFilterOrder, false);
else
VA_EXCEPT1("Flter coefficient design algorithm not recognised/ not yet implemented");
oIIRFilterEngine.Initialise(conf.iFilterOrder); //initialise the interal IIR filter
tempBuffer.Init(oConf.BufferSize); //initialise the buffers to a size which will not be exceeded
interpolatedSignal.Init(oConf.BufferSize);
}
CVABinauralOutdoorWaveFront::~CVABinauralOutdoorWaveFront()
{
delete vdlChR;
delete vdlChL;
}
void CVABinauralOutdoorWaveFront::PreRequest()
{
CVABasicMotionModel::Config sourceMotionConf;
......@@ -65,33 +70,136 @@ void CVABinauralOutdoorWaveFront::PreRelease()
void CVABinauralOutdoorWaveFront::GetOutput( ITASampleBuffer* pfLeftChannel, ITASampleBuffer* pfRightChannel )
{
//This fn is called from the clustering direction, and outputs the left and right audio channels for a path.
//First get the data from the VDL
//Then apply the IIR filter to the single channel
//Then interpolate delay
//Then split into two channels and adjust for ITD to nearest sample
// Determine ToA difference
VARelationMetrics sourceMetrics;
sourceMetrics.calc( m_v3PredictedClusteringPos, m_v3PredictedClusteringView, m_v3PredictedClusteringUp, v3PredictedPos );
VARelationMetrics clusterMetrics; //get metrics about the clustering direction
clusterMetrics.calc( m_v3PredictedClusteringPos, m_v3PredictedClusteringView, m_v3PredictedClusteringUp, v3ReceiverPos );
int toaClusterLeft = (int)round( sound_receiver->pTimeOfArrivalEstimator->getTOALeft(clusterMetrics.phi, clusterMetrics.theta) ); //the TOA difference for a signal coming from the specified angles for the left ear
int toaClusterRight = (int)round(sound_receiver->pTimeOfArrivalEstimator->getTOARight(clusterMetrics.phi, clusterMetrics.theta)); //TODO: test if this works/ how accurate
VARelationMetrics pathMetrics; //get metrics about the current path
pathMetrics.calc(v3PredictedPos, m_v3PredictedClusteringView, m_v3PredictedClusteringUp, v3ReceiverPos); //TODO: add these variables in setMotion NOTE: uses same view and up vectors as for cluster, I THINK this is ok
int toaPathLeft = (int)round(sound_receiver->pTimeOfArrivalEstimator->getTOALeft(pathMetrics.phi, pathMetrics.theta)); //the TOA difference for a signal coming from the specified angles for the left ear
int toaPathRight = (int)round(sound_receiver->pTimeOfArrivalEstimator->getTOARight(pathMetrics.phi, pathMetrics.theta)); //TODO: test if this works/ how accurate
int ITD_diff_margin_left, ITD_diff_margin_right; //calculate the difference in the TOA from cluster to path
ITD_diff_margin_left = toaPathLeft - toaClusterLeft;
ITD_diff_margin_right = toaPathRight - toaClusterRight;
int iCurrentDelay = VDL->GetCurrentDelaySamples(iCursorID); //get current and new delay in samples
int iNewDelay = VDL->GetNewDelaySamples(iCursorID);
if (iNewDelay == iCurrentDelay) {
//Directly read from VDL -> IIR to L/R channels, no interp needed, as no change in delay, so no Doppler
VDL->SetOverlapSamples(iCursorID, ITD_diff_margin_left, ITD_diff_margin_right); //no interp
int buffer_size = VDL->GetEffectiveReadLength(iCursorID, oConf.blockLength);
VDL->Read(iCursorID, oConf.blockLength, tempBuffer.GetData()); //read VDL data to tempary buffer
oIIRFilterEngine.Process(tempBuffer.GetData(), tempBuffer.GetData(), buffer_size); //apply the IIR filter to the data taken from the VDL
pfLeftChannel->write(tempBuffer, oConf.blockLength);
pfRightChannel->write(tempBuffer, oConf.blockLength, ITD_diff_margin_left + ITD_diff_margin_right);
return;
}
int interpolate_left, interpolate_right;
m_pInterpolationRoutine->GetOverlapSamples(interpolate_left, interpolate_right); //get number of overlap samples needed for the interpolation
VDL->SetOverlapSamples(iCursorID, interpolate_left + ITD_diff_margin_left, interpolate_right + ITD_diff_margin_right); //set the overlap to be read from the VDL
int buffer_size = VDL->GetEffectiveReadLength(iCursorID,oConf.blockLength);
int interpolated_signal_size = oConf.blockLength + ITD_diff_margin_left + ITD_diff_margin_right;
//***************************************************Read data from VDL******************************************************
VDL->Read(iCursorID,oConf.blockLength,tempBuffer.GetData());
//*****************************************************Apply IIR Filter******************************************************
oIIRFilterEngine.Process(tempBuffer.GetData(), tempBuffer.GetData(), buffer_size); //apply the IIR filter to the data taken from the VDL
//*****************************************************Interpolate***********************************************************
int iAlgorithmLocalCopy = oConf.iSwitchingAlgorithm; //make internal varibles
int iDeltaDelay = iNewDelay - iCurrentDelay;
if ((iAlgorithmLocalCopy == LINEAR_INTERPOLATION) ||
(iAlgorithmLocalCopy == WINDOWED_SINC_INTERPOLATION) ||
(iAlgorithmLocalCopy == CUBIC_SPLINE_INTERPOLATION))
{
float fResamplingFactor = 1 - iDeltaDelay / (float)oConf.blockLength; //work out re-sampling factor
if ((fResamplingFactor <= MIN_RESAMPLING_FACTOR) || (fResamplingFactor > MAX_RESAMPLING_FACTOR))
{
iAlgorithmLocalCopy = CROSSFADE;
}
}
switch (iAlgorithmLocalCopy)
{
case SWITCH:
{
//Directly switch from new to old delay: no interpolation skip to end
interpolatedSignal.write(tempBuffer.GetData(), interpolated_signal_size, interpolate_left+ITD_diff_margin_left);
break;
}
case CROSSFADE:
{
//Think for this that we need to store the previous buffer (or atleast the read cursor) either here or in the VDL base (probably better in base)
/*
assert(m_iFadeLength <= m_psbTemp->length()); //fade between old buffer and new one
m_psbTemp->cyclic_write(m_psbVDLBuffer, m_iFadeLength, iReadCursorCurrent, 0);
psbOutput->cyclic_write(m_psbVDLBuffer, m_iBlockLength, iReadCursorNew, 0);
psbOutput->Crossfade(m_psbTemp, 0, m_iFadeLength, ITABase::CrossfadeDirection::FROM_SOURCE, ITABase::FadingFunction::COSINE_SQUARE);
*/
VA_EXCEPT1("CROSSFADE not yet implememnted in VABinauralOutdoorWaveFront GetOutput");
break;
}
default: //Interpolate
{
//assert(iLeft < oConf.blockLength);
//assert(iRight < oConf.blockLength);
//assert(m_iVDLBufferSize > iNewIntDelay + iRight);
//assert(iLeft + iSize + iRight <= m_psbTemp->length());
m_pInterpolationRoutine->Interpolate(&tempBuffer, buffer_size, interpolate_left, &interpolatedSignal, interpolated_signal_size);
//output size increased to be blocklength + left/right margin for ITD difference offset
break;
}
} // end case switch
//m_lUserCursors[iCursorID].iOldReadCursorSamples = iNewDelayLocalCopy; //This should be done in VDL base?
//double toaDistance = sourceMetrics.dist / 343; // TODO: get sound speed from core homogeneous medium
//***************************************Apply ITD correction*************************************************
//write from the interpolated buffer to left/right channels with different offset depending on the ITD correction needed
pfLeftChannel->write(interpolatedSignal, oConf.blockLength);
pfRightChannel->write(interpolatedSignal, oConf.blockLength, ITD_diff_margin_left + ITD_diff_margin_right);
//double toaDistance = sourceMetrics.dist / 343; // TODO: get sound speed from core homogeneous medium
//double toaSourceChL = _listener->toaEstimator->getTOALeft(sourceMetrics.phi, sourceMetrics.theta);
//double toSourceaChR = _listener->toaEstimator->getTOARight(sourceMetrics.phi, sourceMetrics.theta);
// @todo finalize and use TOA estimation
// vdlChL->SetDelayTime(std::max(0., toaDistance + toaSourceChL - toaHRTFChL));
// vdlChR->SetDelayTime(std::max(0., toaDistance + toaSourceChR - toaHRTFChR));
//vdlChL->SetDelayTime(std::max(0., toaDistance + toaSourceChL - toaHRTFChL));
//vdlChR->SetDelayTime(std::max(0., toaDistance + toaSourceChR - toaHRTFChR)); //
// @todo jst: there is something wrong with the following two lines ... toaDistance seems to jump
//@todo jst: there is something wrong with the following two lines ... toaDistance seems to jump
//vdlChL->SetDelayTime( toaDistance );
//vdlChR->SetDelayTime( toaDistance );
iCursorID;
int iNumSamples;
//pSource->pVDL->Read( iCursorID, iNumSamples); // take data from VDL to be forwarded to IIR
vdlChL->Process( pData->pSignalSourceInputBuf, pfLeftChannel );
vdlChL->Process( pData->pSignalSourceInputBuf, pfRightChannel );
//vdlChR->SetDelayTime( toaDistance ); //taken from simulation instead
float gain = float( ( 1 / sourceMetrics.dist ) * pState->GetVolume( VAConfig::amplitudeCalibration ) );
pfLeftChannel->mul_scalar( gain );
pfRightChannel->mul_scalar( gain );
//float gain = float( ( 1 / sourceMetrics.dist ) * pState->GetVolume( VAConfig::amplitudeCalibration ) );
//pfLeftChannel->mul_scalar( gain ); //gain should be taken care of in simulation and IIR?
//pfRightChannel->mul_scalar( gain );
}
void CVABinauralOutdoorWaveFront::SetClusteringPose( const VAVec3& v3Pos, const VAVec3& v3View, const VAVec3& v3Up )
......@@ -121,11 +229,14 @@ void CVABinauralOutdoorWaveFront::setFilterCoefficients(const ITABase::CThirdOct
VA_EXCEPT1("Filter design algorithm not recognised/ not yet implemented");
ITADSP::IIRFilterGenerator::Burg(oIR, IIRFilterCoeffs); //using the impulse response as input, calculate the IIR filter coefficients.
}
oIIRFilterEngine.SetCoefficients(IIRFilterCoeffs); //set coefficients of internal IIR filter
}
void CVABinauralOutdoorWaveFront::setDelay(const float delay) {
void CVABinauralOutdoorWaveFront::setDelay(const int delay) {
VDL->SetDelaySamples(iCursorID, delay);
}
......@@ -133,4 +244,24 @@ void CVABinauralOutdoorWaveFront::setDelay(const float delay) {
void CVABinauralOutdoorWaveFront::setMotion() {
}
\ No newline at end of file
}
void CVABinauralOutdoorWaveFront::setSource(CVABinauralOutdoorSource* source) {
VDL = source->pVDL; //The variable delay line from the source this path originated from
iCursorID = VDL->AddCursor();
VDL->SetDelaySamples(iCursorID, 0);
VDL->SetOverlapSamples(iCursorID, 0, 0); //probably change this to something else more appropriate
sound_source = source;
}
void CVABinauralOutdoorWaveFront::setReceiver(CVABinauralClusteringDirectionReceiver* receiver) {
sound_receiver = receiver;
v3ReceiverPos = receiver->v3PredictedPosition; //set the position of the sound receiver
}
......@@ -7,10 +7,16 @@
#include "../Clustering/WaveFront/VABinauralWaveFront.h"
#include <ITAThirdOctaveMagnitudeSpectrum.h>
#include "../Clustering/Receiver/VABinauralClusteringDirectionReceiver.h"
#include <ITAIIRFilterEngine.h>
#include "ITASIMOVariableDelayLineBase.h"
#include <ITAIIRCoefficients.h>
#include <ITAIIRUtils.h>
//#include <ITASIMOVariableDelayLine.h>
#include <ITAInterpolation.h>
......@@ -30,6 +36,16 @@ class CVABinauralOutdoorSource;
class CVABinauralOutdoorWaveFront : public IVABinauralWaveFront, public CVAPoolObject
{
public:
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 Config
{
bool motionModelLogInput;
......@@ -46,6 +62,9 @@ public:
int iFilterOrder;
int IR_length;
int FilterDesignAlgorithm;
int BufferSize;
int iSwitchingAlgorithm;
};
......@@ -66,7 +85,7 @@ public:
CVASoundSourceDesc* pData;
CVASharedMotionModel* pMotionModel;
VAVec3 m_v3PredictedClusteringPos;
VAVec3 m_v3PredictedClusteringPos; //data of the position of the cluster this path belongs to
VAVec3 m_v3PredictedClusteringView;
VAVec3 m_v3PredictedClusteringUp;
......@@ -74,6 +93,11 @@ public:
VAVec3 predView; // @todo jst: appears to be useless in the context of clustering, confirm
VAVec3 predUp; // @todo jst: appears to be useless in the context of clustering, confirm
VAVec3 m_v3WaveFrontPos; //stores position data for the wave front - can be any point on the line the wave front travels to the source, as a normalisation happens later
VAVec3 v3ReceiverPos;
//VAVec3 m_v3WaveFrontView;
//VAVec3 m_v3WaveFrontUp;
CVABinauralOutdoorWaveFront( const CVABinauralOutdoorWaveFront::Config& conf );
~CVABinauralOutdoorWaveFront();
......@@ -87,9 +111,14 @@ public:
void setFilterCoefficients(const ITABase::CThirdOctaveGainMagnitudeSpectrum &oMags );
void setDelay( const float delay );
void setDelay( const int delay );
void setMotion();
void setSource(CVABinauralOutdoorSource* source);
void setReceiver(CVABinauralClusteringDirectionReceiver* sound_receiver);
virtual bool GetValidTrajectory() const
{
......@@ -114,13 +143,23 @@ public:
private:
double m_dCreationTimeStamp;
CITAVariableDelayLine* vdlChL; // Substitute with SIMO VDL (must be extended with a non-interpolating output)
CITAVariableDelayLine* vdlChR; // Remove, use separate SIMO VDL cursor for L/R channels
ITABase::CThirdOctaveGainMagnitudeSpectrum fMags;
ITADSP::CIIRCoefficients IIRFilterCoeffs;
CVABinauralOutdoorSource* sound_source;
CVABinauralClusteringDirectionReceiver* sound_receiver;
CITASIMOVariableDelayLineBase *VDL;
CITAIIRFilterEngine oIIRFilterEngine;
IITASampleInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine
ITASampleBuffer tempBuffer;
ITASampleBuffer interpolatedSignal;
};
#endif // IW_VACORE_BINAURAL_OUTDOOR_WAVE_FRONT
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