ITAVariableDelayLine.h 10.2 KB
Newer Older
1
2
3
4
5
/*
 * ----------------------------------------------------------------
 *
 *		ITA core libs
 *		(c) Copyright Institute of Technical Acoustics (ITA)
6
 *		RWTH Aachen University, Germany, 2015-2018
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * ----------------------------------------------------------------
 *				    ____  __________  _______
 *				   //  / //__   ___/ //  _   |
 *				  //  /    //  /    //  /_|  |
 *				 //  /    //  /    //  ___   |
 *				//__/    //__/    //__/   |__|
 *
 * ----------------------------------------------------------------
 *
 */

#ifndef IW_ITA_VARIABLE_DELAY_LINE
#define IW_ITA_VARIABLE_DELAY_LINE

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
22
23
#include <ITADSPDefinitions.h>

24
25
26
27
28
29
30
31
32
33
#include <ITAAtomicPrimitives.h>
#include <ITACriticalSection.h>
#include <ITADataLog.h>
#include <ITAStopWatch.h>

//! Vorwärtsdeklarationen
class ITASampleBuffer;
class IITASampleInterpolationRoutine;

//! Daten-Logger der VDL aktivieren
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
34
#define ITA_DSP_VDL_DATA_LOG 0
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

//! Klasse für variable Verzögerungsglieder (variable delay-lines, VDLs)
/**
 * 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
 *
 * TODO: Doku, Synchronität
 * - Wächst automatisch mit Setzen der Verzögerung
 */

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
73
class ITA_DSP_API CITAVariableDelayLine
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{
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
	};

	//! 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)
	 */
	CITAVariableDelayLine( double dSamplerate, int iBlocklength, float fReservedMaxDelaySamples, int iAlgorithm );

	//! Destruktor der variablen Verzögerungsleitung
	~CITAVariableDelayLine();

	//! 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( 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
	  *
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
138
	  * Siehe auch: CITAVariableDelayLine::ReserveMaximumDelaySamples(), CITAVariableDelayLine::SetDelaySamples()
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
	  */
	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( float fMaxDelaySamples );

	//! Pufferspeicher reservieren für die angegebene maximale Verzögerung [Sekunden]
	/**
	  * Wie ReserveMaximumDelaySamples(), nur für Zeit in Sekunden
	  */
	void ReserveMaximumDelayTime( float fMaxDelaySecs );

	//! Sub-Sample-Verzögerung aktiviert
	/**
	  * \return Gibt zurück, ob Sub-Sample-Verzögerungen aktiviert (true) oder deaktiviert (false) ist
	  */
	bool GetFractionalDelaysEnabled() const;

	//! Sub-Sample-Verzögerung setzen
	/**
	  * \param bEnabled Aktiviert (true) oder deaktiviert (false) Sub-Sample-Verzögerungen
	  */
	void SetFractionalDelaysEnabled( bool bEnabled );

	//! Gesamtverzögerung zurückgeben [Samples]
	/**
	  * Gibt die Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
	  */
	float GetDelaySamples() const;

	//! Gesamtverzögerung zurückgeben 
	/**
	  * Gibt die Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
	  */

	float GetDelayTime() const;

	//! Gesamtverzögerung zurückgeben [Zeit]
	/**
	  * Gibt die neu eingestellte (und möglicherweise noch nicht übernommene) Gesamtverzögerung der VDL als Zusammensetzung der Ganzzahl und des Sub-Sample zurück
	  */
	float GetNewDelayTime() const;

	//! Gesamtverzögerung zurückgeben [Samples]
	/**
	  * Gibt die Gesamtverzögerung der VDL als Ganzzahl und als Sub-Sample zurück
	  *
	  * \return iIntegerDelay Ganzzahlwert der Verzögerung (kleiner oder gleich der Gesamtverzögerung)
	  * \return fFractionalDelay Bruch der Sub-Sample-Verzögerung aus dem Wertebereich [0, 1)
	  *
	  */
	float GetDelaySamples( int& iIntegerDelay, float& fFractionalDelay ) 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( float fDelaySamples );

	//! Verzögerung setzen [Sekunden]
	/**
	  * Wie SetDelaySamples(), aber für Zeit in Sekunden.
	  */
	void SetDelayTime( float fDelaySecs );

	//! Löscht alle internen gespeicherten Samples und setzt die Distanz auf 0
	void Clear();

	//! Daten verarbeiten
	/**
	  * Diese Funktion wird immer dann aufgerufen, wenn ein neuer Block für die Audiohardware
	  * verarbeitet werden soll (1 Block eingeben, 1 Block entnehmen).
	  *
	  * \param psbInput Eingabepuffer (Block) der VDL
	  * \param psbOutput Ausgabepuffer (Block) der VDL
	  *
	  */
	void Process( const ITASampleBuffer* psbInput, ITASampleBuffer* psbOutput );

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
	ITAAtomicFloat m_fCurrentDelay;		//!< Aktuelle Verzögerung in Samples
	ITAAtomicFloat m_fNewDelay;			//!< Neue Verzögerung in Samples
	bool m_bFracDelays;					//!< Fractional Delay Filterung an/aus

	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_swBufferSizeInc;		//!< StopWatch zur Überwachung von Speicherallozierungszeiten
	ITAStopWatch m_swProcess;			//!< StopWatch zur Überwachung der Berechnungsschleife
	int m_iNumberOfDropouts;			//!< Zählt die Anzahl durch die VDL verursachten Ausfälle

	IITASampleInterpolationRoutine* m_pInterpolationRoutine; //!< Zeiger auf Interpolationsroutine

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
255
#if (ITA_DSP_VDL_DATA_LOG == 1)
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

	//! Implementierungsklasse für Logger-Datum
	class VDLLogData : ITALogDataBase
	{
	public:
		static std::ostream& outputDesc(std::ostream& os);
		std::ostream& outputData(std::ostream& os) const;

		float fCurrentDelay;
		float fNewDelay;
		float fResamplingFactor;
		int iTargetBlockSize;
		float fProcessingTime; //!< Zeit der Process-Routine in Millisekunden
	};

	ITABufferedDataLogger< VDLLogData > m_oDataLog; //!< Logger Datum für VDL spezifische Prozess-Information
#endif
};

#endif // IW_ITA_VARIABLE_DELAY_LINE