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

Starting directivity handling refactoring

parent c3987b7a
......@@ -14,9 +14,41 @@
#ifndef IW_VACORE_DIRECTIVITY
#define IW_VACORE_DIRECTIVITY
class DAFFContentMS;
#include <string>
// Alias fr Directivity-Content
typedef DAFFContentMS CVADirectivity;
//! Abstract directivity base class
/**
* This class pointer is stored in the directivity manager. Use casting to convert
* to the required specialized class you want to employ.
*/
class IVADirectivity
{
public:
enum DirectivityType
{
UNSPECIFIED = -1,
DAFF_HRIR = 1,
DAFF_ENERGETIC,
MULTIPOLE,
SPHERICAL_HARMONICS
};
inline IVADirectivity()
: m_iType( UNSPECIFIED )
{};
virtual inline ~IVADirectivity() {};
virtual std::string GetName() const = 0;
virtual std::string GetDesc() const = 0;
//! Returns type of class for casting
virtual inline DirectivityType GetType() const
{
return m_iType;
};
protected:
DirectivityType m_iType;
};
#endif // IW_VACORE_DIRECTIVITY
/*
* --------------------------------------------------------------------------------------------
*
* VVV VVV A Virtual Acoustics (VA) | http://www.virtualacoustics.org
* VVV VVV AAA Licensed under the Apache License, Version 2.0
* VVV VVV AAA
* VVV VVV AAA Copyright 2015-2017
* VVVVVV AAA Institute of Technical Acoustics (ITA)
* VVVV AAA RWTH Aachen University
*
* --------------------------------------------------------------------------------------------
*/
#include "VADirectivityDAFFHRIR.h"
#include <VAException.h>
#include "../VALog.h"
#include <ITANumericUtils.h>
#include <ITASampleFrame.h>
#include <ITAStringUtils.h>
#include <cassert>
#include <DAFF.h>
#include <sstream>
CVADirectivityDAFFHRIR::CVADirectivityDAFFHRIR( const std::string& sFilename, const std::string& sName, const double dDesiredSamplerate )
: m_sName( sName )
, m_pReader( nullptr )
{
m_iType = IVADirectivity::DAFF_HRIR;
// Der DAFF-Reader wirft std::exceptions
m_pReader = DAFFReader::create();
int iError = m_pReader->openFile( sFilename );
if( iError != DAFF_NO_ERROR )
{
delete m_pReader;
std::string sErrorStr = DAFFUtils::StrError( iError );
VA_EXCEPT1( std::string( "Could not load HRIR dataset from file \"" ) +
sFilename + std::string( "\". " ) + sErrorStr + std::string( "." ) );
}
if( m_pReader->getContentType() != DAFF_IMPULSE_RESPONSE )
{
delete m_pReader;
VA_EXCEPT1( std::string( "The file \"" ) + sFilename +
std::string( "\" does not contain impulse response content." ) );
}
if( m_pReader->getProperties()->getNumberOfChannels() != 2 )
{
//delete m_pReader;
//VA_EXCEPT1( std::string("The file \"") + sFilename +
// std::string("\" does not have two channels.") );
VA_WARN( "HRIRDataset2D", std::string( "The file \"" ) + sFilename + // LAS: modification for HAA auralization. HRIRs with more than 2 channels are supported!
std::string( "\" does not have two channels." ) );
}
m_pContent = dynamic_cast< DAFFContentIR* >( m_pReader->getContent() );
if( m_pContent->getSamplerate() != dDesiredSamplerate )
{
delete m_pReader;
VA_EXCEPT1( std::string( "The file \"" ) + sFilename +
std::string( "\" does not have the required sampling rate of " ) + DoubleToString( dDesiredSamplerate ) +
std::string( " Hz." ) );
}
// Sicherheitshalber, auch wenn was paranoid ...
if( m_pContent->getMaxEffectiveFilterLength() == 0 )
{
delete m_pReader;
VA_EXCEPT1( std::string( "The file \"" ) + sFilename +
std::string( "\" contains empty filters." ) );
}
m_pMetadata = m_pReader->getMetadata();
// Filter-Latenz
if( !m_pMetadata->hasKey( "DELAY_SAMPLES" ) )
{
delete m_pReader;
VA_EXCEPT1( std::string( "The file \"" ) + sFilename +
std::string( "\" is missing the meta tag DELAY_SAMPLES." ) );
}
if( ( m_pMetadata->getKeyType( "DELAY_SAMPLES" ) != DAFFMetadata::DAFF_INT ) && ( m_pMetadata->getKeyType( "DELAY_SAMPLES" ) != DAFFMetadata::DAFF_FLOAT ) )
{
delete m_pReader;
VA_EXCEPT1( std::string( "In file \"" ) + sFilename + std::string( "\": " ) +
std::string( " The meta tag DELAY_SAMPLES must be numeric." ) );
}
m_fLatency = ( float ) m_pMetadata->getKeyFloat( "DELAY_SAMPLES" );
if( m_fLatency < 0 )
{
delete m_pReader;
VA_EXCEPT1( std::string( "In file \"" ) + sFilename + std::string( "\": " ) + std::string( " The meta tag DELAY_SAMPLES must be positive." ) );
}
/*
// Messdistanz
if (!m_pMetadata->hasKey("MEASUREMENT_DISTANCE_METERS")) {
delete m_pReader;
VA_EXCEPT1( std::string("The file \"") + sFilename +
std::string("\" is missing the meta tag MEASUREMENT_DISTANCE_METERS.") );
}
if ((m_pMetadata->getKeyType("MEASUREMENT_DISTANCE_METERS") != DAFFMetadata::DAFF_INT) &&
(m_pMetadata->getKeyType("MEASUREMENT_DISTANCE_METERS") != DAFFMetadata::DAFF_FLOAT)) {
delete m_pReader;
VA_EXCEPT1( std::string("In file \"") + sFilename + std::string("\": ") +
std::string(" The meta tag MEASUREMENT_DISTANCE_METERS must be numeric.") );
}
float fMeasurementDistance = (float) m_pMetadata->getKeyFloat("MEASUREMENT_DISTANCE_METERS");
if (fMeasurementDistance < 0) {
delete m_pReader;
VA_EXCEPT1( std::string("In file \"") + sFilename + std::string("\": ") +
std::string(" The meta tag MEASUREMENT_DISTANCE_METERS must be positive.") );
}
*/
m_iMinOffset = m_pContent->getMinEffectiveFilterOffset();
//m_iFilterLength = m_pContent->getMaxEffectiveFilterLength();
m_iFilterLength = m_pContent->getFilterLength();
// Warnung, falls keine Vollkugel
if( !m_pReader->getProperties()->coversFullSphere() )
VA_WARN( "HRIRDataset2D", "The HRIR dataset file " << sFilename << " does not cover all directions" );
// Eigenschaften definieren
m_oProps.sFilename = sFilename;
m_oProps.sName = sName;
m_oProps.iFilterLength = m_iFilterLength;
m_oProps.fFilterLatency = m_fLatency;
m_oProps.bFullSphere = m_pReader->getProperties()->coversFullSphere();
m_oProps.bSpaceDiscrete = true;
m_oProps.bDistanceDependent = false;
}
CVADirectivityDAFFHRIR::~CVADirectivityDAFFHRIR()
{
if( m_pReader ) m_pReader->closeFile();
delete m_pReader;
}
std::string CVADirectivityDAFFHRIR::GetFilename() const
{
return m_pReader->getFilename();
}
std::string CVADirectivityDAFFHRIR::GetName() const {
return m_sName;
}
std::string CVADirectivityDAFFHRIR::GetDesc() const
{
char buf[ 1024 ];
sprintf( buf, "DAFF, 2D, %s, discrete %0.0fx%0.0f, %d taps",
( m_oProps.bFullSphere ? "full sphere" : "partial sphere" ),
m_pContent->getProperties()->getAlphaResolution(),
m_pContent->getProperties()->getBetaResolution(),
m_oProps.iFilterLength );
return buf;
}
const CVAHRIRProperties* CVADirectivityDAFFHRIR::GetProperties() const
{
return &m_oProps;
}
void CVADirectivityDAFFHRIR::GetNearestNeighbour( const float fAzimuthDeg, const float fElevationDeg, int* piIndex, bool* pbOutOfBounds ) const
{
assert( m_pContent );
if( !piIndex )
return;
bool bOutOfBounds;
m_pContent->getNearestNeighbour( DAFF_OBJECT_VIEW, fAzimuthDeg, fElevationDeg, *piIndex, bOutOfBounds );
if( pbOutOfBounds ) *pbOutOfBounds = bOutOfBounds;
}
void CVADirectivityDAFFHRIR::GetHRIRByIndex( ITASampleFrame* psfDest, const int iIndex, const float ) const
{
assert( m_pContent );
if( psfDest->channels() > m_pReader->getProperties()->getNumberOfChannels() )
VA_EXCEPT1( std::string( "HRIRDatasetDAFF2D::GetHRIRByIndex - Target SampleFrame contains more channels than HRIR database" ) );
int iResult;
for( int iChan = 0; iChan < psfDest->channels(); iChan++ )
{
iResult = m_pContent->getFilterCoeffs( iIndex, iChan, ( *psfDest )[ iChan ].data() );
if( iResult != DAFF_NO_ERROR )
VA_EXCEPT1( DAFFUtils::StrError( iResult ) );
}
}
void CVADirectivityDAFFHRIR::GetHRIR( ITASampleFrame* psfDest, const float fAzimuthDeg, const float fElevationDeg, const float fDistanceMeters, int* piIndex, bool* pbOutOfBounds ) const
{
int i;
GetNearestNeighbour( fAzimuthDeg, fElevationDeg, &i, pbOutOfBounds );
if( piIndex )
*piIndex = i;
GetHRIRByIndex( psfDest, i, fDistanceMeters );
}
/*
float CVAHRIRDataset2D::GetAlphaResolution() const {
return m_pReader->getProperties()->getAlphaResolution();
}
float CVAHRIRDataset2D::GetBetaResolution() const {
return m_pReader->getProperties()->getBetaResolution();
}
int CVAHRIRDataset2D::GetFilterLength() const {
return m_iFilterLength;
}
float CVAHRIRDataset2D::GetFilterLatency() const {
return m_fLatency;
}
void CVAHRIRDataset2D::GetHRIR(ITASampleFrame* psfDest, const float fAzimuth, const float fElevation) const {
// Hier muss nun die entsprechende HRIR erzeugt werden
// Direktionale Datenbank
if (m_pContent != nullptr) {
int iRecordIndex;
m_pContent->getNearestNeighbour(DAFF_OBJECT_VIEW, fAzimuth, fElevation, iRecordIndex);
m_pContent->getFilterCoeffs(iRecordIndex, 0, (*psfDest)[0].data());
m_pContent->getFilterCoeffs(iRecordIndex, 1, (*psfDest)[1].data());
return;
}
// Kugelflchenfunktion Datenbank
/*
if (m_pSHContent) {
...
}
*/
/*
void CVAHRIRDataset2D::GetHRIR(int iRecord, ITASampleFrame* psfDest) const {
assert( psfDest );
assert( psfDest->length() == m_iFilterLength );
m_pContent->getFilterCoeffs(iRecord, 0, (*psfDest)[0].data());
m_pContent->getFilterCoeffs(iRecord, 1, (*psfDest)[1].data());
/*
* HRIR laden (effektiv)
*
* Die Daten beider Kanle knnen unterschiedliche Offsets und Lngen haben.
* Sie werden beide mssen beide entsprechend ausgerichtet
* so dass sie gemeinsame effektive Grenzen haben. Ferner drfen
* keine eine unterschiedlichen Latenzen zwischen verschiedenen Records auftreten,
* da sonst eine (knstliche) Laufzeit wieder ausserhalb der VDL auftritt.
*
* Der gemeinsame Nenner fr die Anordnung ist durch den minimalen Offset
* und maximale effektive Lnge des HRIR-Datensatzes gegeben. Deshalb wird alles
* mit effektiver Lnge gespeichert und die Nullen davor (minimaler Offset)
* werden entfernt und als Latenz realisiert.
*
/*
int iOffsetCh0, iOffsetCh1;
int iLengthCh0, iLengthCh1;
m_pContent->getEffectiveFilterBounds(iRecord, 0, iOffsetCh0, iLengthCh0);
m_pContent->getEffectiveFilterBounds(iRecord, 1, iOffsetCh1, iLengthCh1);
int iOffsetMin = std::min(iOffsetCh0, iLengthCh0);
int iLengthMax = std::max(iLengthCh0, iLengthCh1);
float* pfDataCh0 = (*psfDest)[0].data();
float* pfDataCh1 = (*psfDest)[1].data();
// HRIR (ohne Laufzeit) korrekt ausgerichtetet (links, rechts) zusammenstellen
psfDest->zero();
m_pContent->getEffectiveFilterCoeffs(iRecord, 0, pfDataCh0 + (iOffsetCh0 - m_iMinOffset));
m_pContent->getEffectiveFilterCoeffs(iRecord, 1, pfDataCh1 + (iOffsetCh1 - m_iMinOffset));
*
}
*/
DAFFContentIR* CVADirectivityDAFFHRIR::GetDAFFContent() const
{
return m_pContent;
}
......@@ -11,15 +11,18 @@
* --------------------------------------------------------------------------------------------
*/
#ifndef IW_VACORE_HRIRDATASET
#define IW_VACORE_HRIRDATASET
#ifndef IW_VACORE_HRIRDATASET_DAFF2D
#define IW_VACORE_HRIRDATASET_DAFF2D
#include "VADirectivity.h"
#include <string>
class ITASampleFrame;
class DAFFReader;
class DAFFContentIR;
class DAFFMetadata;
//! Data class with properties of an HRIR data set
class CVAHRIRDatasetProperties
class CVAHRIRProperties
{
public:
std::string sFilename; // Dateiname
......@@ -45,71 +48,89 @@ public:
} oAnthroParams;
};
/**
* Diese Klasse ist eine Facade fr zwei-dimensionale
* HRIR-Datenstze und stellt eine (mehr) applikationsbezogene
* Schnittstelle zu solchen Daten bereit.
* Zwei-dimensional bedeutet, dass nur Datenstze fr eine
* einzige Entfernung enthalten sind (Kugelschale).
*
* Die HRIRs enthalten NICHT die Laufzeit, die bei
* der Messung vorhanden war. Sie werden alle auf die minimale
* Gre gekrzt (nur falls tatschlich Nullen vorhanden sind).
* Was brig bleibt, ist der Rest an Verzgerung im Filter,
* welcher bis zur ursprnglichen Entfernung fehlt.
* Diese Verzgerung kann abgefragt werden.
* Alle Lautstrken werden automatisch auf 1m nomiert.
*
* Beispiel:
*
* - Messdistanz 2.0m
* - Impulseantworten enthalten nur Koeffizienten von 200 bis 400
* - Ursprnglich liegt Laufzeit bei 256 Samples. ( 2.0m/(344m/s) ) *(44.1k Samples/s) = 256 Samples )
* - Dann ist die Latenz 256-200 = 56 Samples.
* - Also: Beim 56ten Samples ist man quasi beim Mittelpunkt des Filters.
*/
class IVADirectivity
class CVADirectivityDAFFHRIR : public IVADirectivity
{
public:
//! Lade-Konstruktor. Wirft CVAException im Fehlerfall.
CVADirectivityDAFFHRIR( const std::string& sFilePath, const std::string& sName, const double dDesiredSamplerate );
//! Destruktor
virtual inline ~IVADirectivity() {};
// --= Eigenschaften =--
//! Dateiname zurckgeben
virtual std::string GetFilename() const = 0;
//! Anzeigename zurckgeben
virtual std::string GetName() const = 0;
//! Anzeigename setzen
// TODO: virtual void SetName(const std::string& sName)=0;
//! Beschreibung zurckgeben (Bsp. Typ, Auflsung, usw.)
virtual std::string GetDesc() const = 0;
//! Eigenschaften zurckgeben
virtual const CVAHRIRDatasetProperties* GetProperties() const = 0;
// --= Datenzugriff =--
//! Nchsten Nachbardatensatz suchen
/**
* - Winkel in Grad []
* - Fr ortsabgetastete Gitter whlt die Funktion den nchsten Nachbar und speichert
* gibt den Index des Gitterpunkt zurck (falls gewnscht, nutzbar fr schnelle Vergleiche).
* - Zustzlich kann ermittelt werden, ob die Richtung von den Daten abgedeckt wurde
*/
virtual void GetNearestNeighbour( float fAzimuthDeg, float fElevationDeg, int* piIndex, bool* pbOutOfBounds = nullptr ) const = 0;
//! HRIR-Datensatz abrufen (Winkel in Grad []) [copy-to-buffer]
/**
* - Der Samplebuffer muss genau zwei Kanle und exakt die Filterlnge aufweisen
* (siehe auch: GetFilterLength())
* - Winkel in Grad []
* - Distanz in Metern [m]
* - Distanz wird bei 2D-Datenstzen ignoriert
* - Die Funktion wendet NICHT das 1/r-Gesetz an. Alles muss auf 1m normiert sein.
* - Fr ortsabgetastete Gitter whlt die Funktion den nchsten Nachbar und speichert
* gibt den Index des Gitterpunkt zurck (falls gewnscht, nutzbar fr schnelle Vergleiche).
* - Zustzlich kann ermittelt werden, ob die Richtung von den Daten abgedeckt wurde
*/
virtual void GetHRIR( ITASampleFrame* psfDest, const float fAzimuthDeg, const float fElevationDeg, const float fDistanceMeters, int* piIndex = nullptr, bool* pbOutOfBounds = nullptr ) const = 0;
//! HRIR-Datensatz abrufen (durch gegebenen Index) [copy-to-buffer]
/**
* - Der Samplebuffer muss genau zwei Kanle und exakt die Filterlnge aufweisen
* (siehe auch: GetFilterLength())
* - Distanz in Metern [m]
* - Distanz wird bei 2D-Datenstzen ignoriert
* - Die Funktion wendet NICHT das 1/r-Gesetz an. Alles muss auf 1m normiert sein.
* - Fr ortsabgetastete Gitter whlt die Funktion den nchsten Nachbar und speichert
* gibt den Index des Gitterpunkt zurck (falls gewnscht, nutzbar fr schnelle Vergleiche).
*
* - Diese Funktion kann man effizient zusammen mit GetNearestNeighbourIndex benutzen
*/
virtual void GetHRIRByIndex( ITASampleFrame* psfDest, const int iIndex, const float fDistanceMeters ) const = 0;
virtual ~CVADirectivityDAFFHRIR();
// --= Schnittstelle "IVAHRIRDataset" =-----------------------
std::string GetFilename() const;
std::string GetName() const;
std::string GetDesc() const;
const CVAHRIRProperties* GetProperties() const;
void GetNearestNeighbour( const float fAzimuthDeg, const float fElevationDeg, int* piIndex, bool* pbOutOfBounds = nullptr ) const;
void GetHRIRByIndex( ITASampleFrame* psfDest, const int iIndex, const float fDistanceMeters ) const;
void GetHRIR( ITASampleFrame* psfDest, const float fAzimuthDeg, const float fElevationDeg, const float fDistanceMeters, int* piIndex = nullptr, bool* pbOutOfBounds = nullptr ) const;
/*
//! Auflsung zurckgeben (Alpha|Beta)
float GetAlphaResolution() const;
float GetBetaResolution() const;
//! Filterlnge [Samples] zurckgeben
int GetFilterLength() const;
// Latenz [Samples] zurckgeben
float GetFilterLatency() const;
////! Nchsten Nachbardatensatz suchen
//int GetNearestNeighbour(float fAzimuthDeg, float fElevationDeg);
////! HRIR abrufen (Winkel in Grad [])
///** \note Der Samplebuffer muss genau zwei Kanle und die exakte Lnge (siehe oben) aufweisen
// */
////void GetHRIR(int iRecord, ITASampleFrame* psfDest) const;
////! HRIR abrufen (Winkel in Grad [])
///**
// * \note Der Samplebuffer muss genau zwei Kanle und die exakte Lnge (siehe oben) aufweisen
// * Siehe auch: GetFilterLength()
// */
//void GetHRIR(ITASampleFrame* psfDest, const float fAzimuth, const float fElevation) const;
// Zugriff auf den DAFF-Content
DAFFContentIR* GetDAFFContent() const;
private:
CVAHRIRProperties m_oProps;
DAFFReader* m_pReader;
mutable DAFFContentIR* m_pContent;
const DAFFMetadata* m_pMetadata;
std::string m_sName;
float m_fLatency;
int m_iMinOffset;
int m_iFilterLength;
};
#endif // IW_VACORE_HRIRDATASET
#endif // IW_VACORE_HRIRDATASET_DAFF2D
......@@ -13,9 +13,13 @@
#include "VADirectivityManager.h"
#include "VADirectivityDAFFEnergetic.h"
#include "VADirectivityDAFFHRIR.h"
//#include "VAHRIRDatasetSH.h"
#include "../VALog.h"
#include <VAException.h>
#include <ITAASCIITable.h>
#include <ITAFileSystemUtils.h>
#include <ITAException.h>
......@@ -26,9 +30,11 @@
#include <algorithm>
CVADirectivityManager::CVADirectivityManager()
CVADirectivityManager::CVADirectivityManager( IVAInterface* pAssociatedCore, const double dDesiredSamplerate )
: m_pAssociatedCore( pAssociatedCore )
, m_dSampleRate( dDesiredSamplerate )
{
assert( m_pAssociatedCore );
}
CVADirectivityManager::~CVADirectivityManager()
......@@ -43,154 +49,168 @@ void CVADirectivityManager::Initialize()
void CVADirectivityManager::Finalize()
{
for( CVAObjectContainer<CVADirectivity>::iterator it = m_ocDir.begin(); it != m_ocDir.end(); ++it )
{
CVADirectivity* pDir( it->second );
DAFFReader* pReader = pDir->getParent();
delete pReader;
}
Reset();
}
void CVADirectivityManager::Reset() {
// Alle Richtcharakteristiken freigeben
for( CVAObjectContainer<CVADirectivity>::iterator it = m_ocDir.begin(); it != m_ocDir.end(); ++it )
void CVADirectivityManager::Reset()
{
for( CVAObjectContainer< IVADirectivity >::iterator it = m_oDirectivities.begin(); it != m_oDirectivities.end(); ++it )
{
delete it->second->getParent();
delete it->second;
}
m_ocDir.Clear();
m_oDirectivities.Clear();
}
int CVADirectivityManager::LoadDirectivity( const std::string& sFilename, const std::string& sName )
int CVADirectivityManager::CreateDirectivity( const CVAStruct& oParams, const std::string& sName )
{
std::string s = correctPath( sFilename );
int iDirID = 0;
// TODO: Log-Level
VA_VERBOSE( "DirMan", "LoadDirectivity(\"" << s << "\", \"" << sName << "\")" );
// Zunchst prfen ob diese Datenbank schon geladen wurde
for( CVAObjectContainer<CVADirectivity>::const_iterator cit = m_ocDir.begin(); cit != m_ocDir.end(); ++cit )
{
if( cit->second->getParent()->getFilename() == s ) {
iDirID = cit->first;
m_csDir.leave();
VA_VERBOSE( "DirMan", "LoadDirectivity successful, directivity already loaded ID " << iDirID );
return iDirID;
}
}
DAFFReader* pReader = DAFFReader::create();
DAFFContentMS* pDir = nullptr;
VA_VERBOSE( "DirectivityManager", "Creating directivity with name '" << sName << "'" );
try
// Try to identify the directivity type by parsing parameter arguments
if( oParams.HasKey( "filepath" ) )
{
int ec = pReader->openFile( s );
if( ec != DAFF_NO_ERROR )
const std::string sFilePath = oParams[ "filepath" ];
const std::string sDestFilePath = m_pAssociatedCore->FindFilePath( sFilePath );
if( sDestFilePath.empty() )
VA_EXCEPT2( INVALID_PARAMETER, "Looked everywhere, but could not find file '" + sFilePath + "'" );
// Format entscheiden und Implementierung whlen
IVADirectivity* pDirectivity = nullptr;
std::string sSuffix = toLowercase( getFilenameSuffix( sDestFilePath ) );