1 /*
2  * I3DL2Reverb.cpp
3  * ---------------
4  * Purpose: Implementation of the DMO I3DL2Reverb DSP (for non-Windows platforms)
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #include "stdafx.h"
12 
13 #ifndef NO_PLUGINS
14 #include "../../Sndfile.h"
15 #include "I3DL2Reverb.h"
16 #ifdef MODPLUG_TRACKER
17 #include "../../../sounddsp/Reverb.h"
18 #endif // MODPLUG_TRACKER
19 #include "mpt/base/numbers.hpp"
20 #endif // !NO_PLUGINS
21 
22 OPENMPT_NAMESPACE_BEGIN
23 
24 #ifndef NO_PLUGINS
25 
26 namespace DMO
27 {
28 
Init(int32 ms,int32 padding,uint32 sampleRate,int32 delayTap)29 void I3DL2Reverb::DelayLine::Init(int32 ms, int32 padding, uint32 sampleRate, int32 delayTap)
30 {
31 	m_length = Util::muldiv(sampleRate, ms, 1000) + padding;
32 	m_position = 0;
33 	SetDelayTap(delayTap);
34 	assign(m_length, 0.0f);
35 }
36 
37 
SetDelayTap(int32 delayTap)38 void I3DL2Reverb::DelayLine::SetDelayTap(int32 delayTap)
39 {
40 	if(m_length > 0)
41 		m_delayPosition = (delayTap + m_position + m_length) % m_length;
42 }
43 
44 
Advance()45 void I3DL2Reverb::DelayLine::Advance()
46 {
47 	if(--m_position < 0)
48 		m_position += m_length;
49 	if(--m_delayPosition < 0)
50 		m_delayPosition += m_length;
51 }
52 
53 
Set(float value)54 MPT_FORCEINLINE void I3DL2Reverb::DelayLine::Set(float value)
55 {
56 	at(m_position) = value;
57 }
58 
59 
Get(int32 offset) const60 float I3DL2Reverb::DelayLine::Get(int32 offset) const
61 {
62 	offset = (offset + m_position) % m_length;
63 	if(offset < 0)
64 		offset += m_length;
65 	return at(offset);
66 }
67 
68 
Get() const69 MPT_FORCEINLINE float I3DL2Reverb::DelayLine::Get() const
70 {
71 	return at(m_delayPosition);
72 }
73 
74 
Create(VSTPluginLib & factory,CSoundFile & sndFile,SNDMIXPLUGIN * mixStruct)75 IMixPlugin* I3DL2Reverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
76 {
77 	return new (std::nothrow) I3DL2Reverb(factory, sndFile, mixStruct);
78 }
79 
80 
I3DL2Reverb(VSTPluginLib & factory,CSoundFile & sndFile,SNDMIXPLUGIN * mixStruct)81 I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
82 	: IMixPlugin(factory, sndFile, mixStruct)
83 {
84 	m_param[kI3DL2ReverbRoom] = 0.9f;
85 	m_param[kI3DL2ReverbRoomHF] = 0.99f;
86 	m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f;
87 	m_param[kI3DL2ReverbDecayTime] = 0.07f;
88 	m_param[kI3DL2ReverbDecayHFRatio] = 0.3842105f;
89 	m_param[kI3DL2ReverbReflections] = 0.672545433f;
90 	m_param[kI3DL2ReverbReflectionsDelay] = 0.233333333f;
91 	m_param[kI3DL2ReverbReverb] = 0.85f;
92 	m_param[kI3DL2ReverbReverbDelay] = 0.11f;
93 	m_param[kI3DL2ReverbDiffusion] = 1.0f;
94 	m_param[kI3DL2ReverbDensity] = 1.0f;
95 	m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f;
96 	m_param[kI3DL2ReverbQuality] = 2.0f / 3.0f;
97 
98 	SetCurrentProgram(m_program);
99 
100 	m_mixBuffer.Initialize(2, 2);
101 	InsertIntoFactoryList();
102 }
103 
104 
Process(float * pOutL,float * pOutR,uint32 numFrames)105 void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames)
106 {
107 	if(m_recalcParams)
108 	{
109 		auto sampleRate = m_effectiveSampleRate;
110 		RecalculateI3DL2ReverbParams();
111 		// Resize and clear delay lines if quality has changed
112 		if(sampleRate != m_effectiveSampleRate)
113 			PositionChanged();
114 	}
115 
116 	if(!m_ok || !m_mixBuffer.Ok())
117 		return;
118 
119 	const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) };
120 	float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) };
121 
122 	uint32 frames = numFrames;
123 	if(!(m_quality & kFullSampleRate) && m_remain && frames > 0)
124 	{
125 		// Remaining frame from previous render call
126 		frames--;
127 		*(out[0]++) = m_prevL;
128 		*(out[1]++) = m_prevR;
129 		in[0]++;
130 		in[1]++;
131 		m_remain = false;
132 	}
133 
134 	while(frames > 0)
135 	{
136 		// Apply room filter and insert into early reflection delay lines
137 		const float inL = *(in[0]++);
138 		const float inRoomL = (m_filterHist[12] - inL) * m_roomFilter + inL;
139 		m_filterHist[12] = inRoomL;
140 		m_delayLines[15].Set(inRoomL);
141 
142 		const float inR = *(in[1]++);
143 		const float inRoomR = (m_filterHist[13] - inR) * m_roomFilter + inR;
144 		m_filterHist[13] = inRoomR;
145 		m_delayLines[16].Set(inRoomR);
146 
147 		// Early reflections (left)
148 		float earlyL = m_delayLines[15].Get(m_earlyTaps[0][1]) * 0.68f
149 			- m_delayLines[15].Get(m_earlyTaps[0][2]) * 0.5f
150 			- m_delayLines[15].Get(m_earlyTaps[0][3]) * 0.62f
151 			- m_delayLines[15].Get(m_earlyTaps[0][4]) * 0.5f
152 			- m_delayLines[15].Get(m_earlyTaps[0][5]) * 0.62f;
153 		if(m_quality & kMoreDelayLines)
154 		{
155 			float earlyL2 = earlyL;
156 			earlyL = m_delayLines[13].Get() + earlyL * 0.618034f;
157 			m_delayLines[13].Set(earlyL2 - earlyL * 0.618034f);
158 		}
159 		const float earlyRefOutL = earlyL * m_ERLevel;
160 		m_filterHist[15] = m_delayLines[15].Get(m_earlyTaps[0][0]) + m_filterHist[15];
161 		m_filterHist[16] = m_delayLines[16].Get(m_earlyTaps[1][0]) + m_filterHist[16];
162 
163 		// Lots of slightly different copy-pasta ahead
164 		float reverbL1, reverbL2, reverbL3, reverbR1, reverbR2, reverbR3;
165 
166 		reverbL1 = -m_filterHist[15] * 0.707f;
167 		reverbL2 = m_filterHist[16] * 0.707f + reverbL1;
168 		reverbR2 = reverbL1 - m_filterHist[16] * 0.707f;
169 
170 		m_filterHist[5] = (m_filterHist[5] - m_delayLines[5].Get()) * m_delayCoeffs[5][1] + m_delayLines[5].Get();
171 		reverbL1 = m_filterHist[5] * m_delayCoeffs[5][0] + reverbL2 * m_diffusion;
172 		m_delayLines[5].Set(reverbL2 - reverbL1 * m_diffusion);
173 		reverbL2 = reverbL1;
174 		reverbL3 = -0.15f * reverbL1;
175 
176 		m_filterHist[4] = (m_filterHist[4] - m_delayLines[4].Get()) * m_delayCoeffs[4][1] + m_delayLines[4].Get();
177 		reverbL1 = m_filterHist[4] * m_delayCoeffs[4][0] + reverbL2 * m_diffusion;
178 		m_delayLines[4].Set(reverbL2 - reverbL1 * m_diffusion);
179 		reverbL2 = reverbL1;
180 		reverbL3 -= reverbL1 * 0.2f;
181 
182 		if(m_quality & kMoreDelayLines)
183 		{
184 			m_filterHist[3] = (m_filterHist[3] - m_delayLines[3].Get()) * m_delayCoeffs[3][1] + m_delayLines[3].Get();
185 			reverbL1 = m_filterHist[3] * m_delayCoeffs[3][0] + reverbL2 * m_diffusion;
186 			m_delayLines[3].Set(reverbL2 - reverbL1 * m_diffusion);
187 			reverbL2 = reverbL1;
188 			reverbL3 += 0.35f * reverbL1;
189 
190 			m_filterHist[2] = (m_filterHist[2] - m_delayLines[2].Get()) * m_delayCoeffs[2][1] + m_delayLines[2].Get();
191 			reverbL1 = m_filterHist[2] * m_delayCoeffs[2][0] + reverbL2 * m_diffusion;
192 			m_delayLines[2].Set(reverbL2 - reverbL1 * m_diffusion);
193 			reverbL2 = reverbL1;
194 			reverbL3 -= reverbL1 * 0.38f;
195 		}
196 		m_delayLines[17].Set(reverbL2);
197 
198 		reverbL1 = m_delayLines[17].Get() * m_delayCoeffs[12][0];
199 		m_filterHist[17] = (m_filterHist[17] - reverbL1) * m_delayCoeffs[12][1] + reverbL1;
200 
201 		m_filterHist[1] = (m_filterHist[1] - m_delayLines[1].Get()) * m_delayCoeffs[1][1] + m_delayLines[1].Get();
202 		reverbL1 = m_filterHist[17] * m_diffusion + m_filterHist[1] * m_delayCoeffs[1][0];
203 		m_delayLines[1].Set(m_filterHist[17] - reverbL1 * m_diffusion);
204 		reverbL2 = reverbL1;
205 		float reverbL4 = reverbL1 * 0.38f;
206 
207 		m_filterHist[0] = (m_filterHist[0] - m_delayLines[0].Get()) * m_delayCoeffs[0][1] + m_delayLines[0].Get();
208 		reverbL1 = m_filterHist[0] * m_delayCoeffs[0][0] + reverbL2 * m_diffusion;
209 		m_delayLines[0].Set(reverbL2 - reverbL1 * m_diffusion);
210 		reverbL3 -= reverbL1 * 0.38f;
211 		m_filterHist[15] = reverbL1;
212 
213 		// Early reflections (right)
214 		float earlyR = m_delayLines[16].Get(m_earlyTaps[1][1]) * 0.707f
215 			- m_delayLines[16].Get(m_earlyTaps[1][2]) * 0.6f
216 			- m_delayLines[16].Get(m_earlyTaps[1][3]) * 0.5f
217 			- m_delayLines[16].Get(m_earlyTaps[1][4]) * 0.6f
218 			- m_delayLines[16].Get(m_earlyTaps[1][5]) * 0.5f;
219 		if(m_quality & kMoreDelayLines)
220 		{
221 			float earlyR2 = earlyR;
222 			earlyR = m_delayLines[14].Get() + earlyR * 0.618034f;
223 			m_delayLines[14].Set(earlyR2 - earlyR * 0.618034f);
224 		}
225 		const float earlyRefOutR = earlyR * m_ERLevel;
226 
227 		m_filterHist[11] = (m_filterHist[11] - m_delayLines[11].Get()) * m_delayCoeffs[11][1] + m_delayLines[11].Get();
228 		reverbR1 = m_filterHist[11] * m_delayCoeffs[11][0] + reverbR2 * m_diffusion;
229 		m_delayLines[11].Set(reverbR2 - reverbR1 * m_diffusion);
230 		reverbR2 = reverbR1;
231 
232 		m_filterHist[10] = (m_filterHist[10] - m_delayLines[10].Get()) * m_delayCoeffs[10][1] + m_delayLines[10].Get();
233 		reverbR1 = m_filterHist[10] * m_delayCoeffs[10][0] + reverbR2 * m_diffusion;
234 		m_delayLines[10].Set(reverbR2 - reverbR1 * m_diffusion);
235 		reverbR3 = reverbL4 - reverbR2 * 0.15f - reverbR1 * 0.2f;
236 		reverbR2 = reverbR1;
237 
238 		if(m_quality & kMoreDelayLines)
239 		{
240 			m_filterHist[9] = (m_filterHist[9] - m_delayLines[9].Get()) * m_delayCoeffs[9][1] + m_delayLines[9].Get();
241 			reverbR1 = m_filterHist[9] * m_delayCoeffs[9][0] + reverbR2 * m_diffusion;
242 			m_delayLines[9].Set(reverbR2 - reverbR1 * m_diffusion);
243 			reverbR2 = reverbR1;
244 			reverbR3 += reverbR1 * 0.35f;
245 
246 			m_filterHist[8] = (m_filterHist[8] - m_delayLines[8].Get()) * m_delayCoeffs[8][1] + m_delayLines[8].Get();
247 			reverbR1 = m_filterHist[8] * m_delayCoeffs[8][0] + reverbR2 * m_diffusion;
248 			m_delayLines[8].Set(reverbR2 - reverbR1 * m_diffusion);
249 			reverbR2 = reverbR1;
250 			reverbR3 -= reverbR1 * 0.38f;
251 		}
252 		m_delayLines[18].Set(reverbR2);
253 
254 		reverbR1 = m_delayLines[18].Get() * m_delayCoeffs[12][0];
255 		m_filterHist[18] = (m_filterHist[18] - reverbR1) * m_delayCoeffs[12][1] + reverbR1;
256 
257 		m_filterHist[7] = (m_filterHist[7] - m_delayLines[7].Get()) * m_delayCoeffs[7][1] + m_delayLines[7].Get();
258 		reverbR1 = m_filterHist[18] * m_diffusion + m_filterHist[7] * m_delayCoeffs[7][0];
259 		m_delayLines[7].Set(m_filterHist[18] - reverbR1 * m_diffusion);
260 		reverbR2 = reverbR1;
261 
262 		float lateRevOutL = (reverbL3 + reverbR1 * 0.38f) * m_ReverbLevelL;
263 
264 		m_filterHist[6] = (m_filterHist[6] - m_delayLines[6].Get()) * m_delayCoeffs[6][1] + m_delayLines[6].Get();
265 		reverbR1 = m_filterHist[6] * m_delayCoeffs[6][0] + reverbR2 * m_diffusion;
266 		m_delayLines[6].Set(reverbR2 - reverbR1 * m_diffusion);
267 		m_filterHist[16] = reverbR1;
268 
269 		float lateRevOutR = (reverbR3 - reverbR1 * 0.38f) * m_ReverbLevelR;
270 
271 		float outL = earlyRefOutL + lateRevOutL;
272 		float outR = earlyRefOutR + lateRevOutR;
273 
274 		for(auto &line : m_delayLines)
275 			line.Advance();
276 
277 		if(!(m_quality & kFullSampleRate))
278 		{
279 			*(out[0]++) = (outL + m_prevL) * 0.5f;
280 			*(out[1]++) = (outR + m_prevR) * 0.5f;
281 			m_prevL = outL;
282 			m_prevR = outR;
283 			in[0]++;
284 			in[1]++;
285 			if(frames-- == 1)
286 			{
287 				m_remain = true;
288 				break;
289 			}
290 		}
291 		*(out[0]++) = outL;
292 		*(out[1]++) = outR;
293 		frames--;
294 	}
295 
296 	ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames);
297 }
298 
299 
GetNumPrograms() const300 int32 I3DL2Reverb::GetNumPrograms() const
301 {
302 #ifdef MODPLUG_TRACKER
303 	return NUM_REVERBTYPES;
304 #else
305 	return 0;
306 #endif
307 }
308 
SetCurrentProgram(int32 program)309 void I3DL2Reverb::SetCurrentProgram(int32 program)
310 {
311 #ifdef MODPLUG_TRACKER
312 	if(program < NUM_REVERBTYPES)
313 	{
314 		m_program = program;
315 		const auto &preset = *GetReverbPreset(m_program);
316 		m_param[kI3DL2ReverbRoom] = (preset.lRoom + 10000) / 10000.0f;
317 		m_param[kI3DL2ReverbRoomHF] = (preset.lRoomHF + 10000) / 10000.0f;
318 		m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f;
319 		m_param[kI3DL2ReverbDecayTime] = (preset.flDecayTime - 0.1f) / 19.9f;
320 		m_param[kI3DL2ReverbDecayHFRatio] = (preset.flDecayHFRatio - 0.1f) / 1.9f;
321 		m_param[kI3DL2ReverbReflections] = (preset.lReflections + 10000) / 11000.0f;
322 		m_param[kI3DL2ReverbReflectionsDelay] = preset.flReflectionsDelay / 0.3f;
323 		m_param[kI3DL2ReverbReverb] = (preset.lReverb + 10000) / 12000.0f;
324 		m_param[kI3DL2ReverbReverbDelay] = preset.flReverbDelay / 0.1f;
325 		m_param[kI3DL2ReverbDiffusion] = preset.flDiffusion / 100.0f;
326 		m_param[kI3DL2ReverbDensity] = preset.flDensity / 100.0f;
327 		m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f;
328 		RecalculateI3DL2ReverbParams();
329 	}
330 #else
331 	MPT_UNUSED_VARIABLE(program);
332 #endif
333 }
334 
335 
GetParameter(PlugParamIndex index)336 PlugParamValue I3DL2Reverb::GetParameter(PlugParamIndex index)
337 {
338 	if(index < kI3DL2ReverbNumParameters)
339 	{
340 		return m_param[index];
341 	}
342 	return 0;
343 }
344 
345 
SetParameter(PlugParamIndex index,PlugParamValue value)346 void I3DL2Reverb::SetParameter(PlugParamIndex index, PlugParamValue value)
347 {
348 	if(index < kI3DL2ReverbNumParameters)
349 	{
350 		value = mpt::safe_clamp(value, 0.0f, 1.0f);
351 		if(index == kI3DL2ReverbQuality)
352 			value = mpt::round(value * 3.0f) / 3.0f;
353 		m_param[index] = value;
354 		m_recalcParams = true;
355 	}
356 }
357 
358 
Resume()359 void I3DL2Reverb::Resume()
360 {
361 	RecalculateI3DL2ReverbParams();
362 	PositionChanged();
363 	m_isResumed = true;
364 }
365 
366 
PositionChanged()367 void I3DL2Reverb::PositionChanged()
368 {
369 	MemsetZero(m_filterHist);
370 	m_prevL = 0;
371 	m_prevR = 0;
372 	m_remain = false;
373 
374 	try
375 	{
376 		uint32 sampleRate = static_cast<uint32>(m_effectiveSampleRate);
377 		m_delayLines[0].Init(67, 5, sampleRate, m_delayTaps[0]);
378 		m_delayLines[1].Init(62, 5, sampleRate, m_delayTaps[1]);
379 		m_delayLines[2].Init(53, 5, sampleRate, m_delayTaps[2]);
380 		m_delayLines[3].Init(43, 5, sampleRate, m_delayTaps[3]);
381 		m_delayLines[4].Init(32, 5, sampleRate, m_delayTaps[4]);
382 		m_delayLines[5].Init(22, 5, sampleRate, m_delayTaps[5]);
383 		m_delayLines[6].Init(75, 5, sampleRate, m_delayTaps[6]);
384 		m_delayLines[7].Init(69, 5, sampleRate, m_delayTaps[7]);
385 		m_delayLines[8].Init(60, 5, sampleRate, m_delayTaps[8]);
386 		m_delayLines[9].Init(48, 5, sampleRate, m_delayTaps[9]);
387 		m_delayLines[10].Init(36, 5, sampleRate, m_delayTaps[10]);
388 		m_delayLines[11].Init(25, 5, sampleRate, m_delayTaps[11]);
389 		m_delayLines[12].Init(0, 0, 0);	// Dummy for array index consistency with both tap and coefficient arrays
390 		m_delayLines[13].Init(3, 0, sampleRate, m_delayTaps[13]);
391 		m_delayLines[14].Init(3, 0, sampleRate, m_delayTaps[14]);
392 		m_delayLines[15].Init(407, 1, sampleRate);
393 		m_delayLines[16].Init(400, 1, sampleRate);
394 		m_delayLines[17].Init(10, 0, sampleRate, -1);
395 		m_delayLines[18].Init(10, 0, sampleRate, -1);
396 		m_ok = true;
397 	} catch(mpt::out_of_memory e)
398 	{
399 		m_ok = false;
400 		mpt::delete_out_of_memory(e);
401 	}
402 }
403 
404 
405 #ifdef MODPLUG_TRACKER
406 
GetParamName(PlugParamIndex param)407 CString I3DL2Reverb::GetParamName(PlugParamIndex param)
408 {
409 	switch(param)
410 	{
411 	case kI3DL2ReverbRoom: return _T("Room");
412 	case kI3DL2ReverbRoomHF: return _T("RoomHF");
413 	case kI3DL2ReverbRoomRolloffFactor: return _T("RoomRolloffFactor");
414 	case kI3DL2ReverbDecayTime: return _T("DecayTime");
415 	case kI3DL2ReverbDecayHFRatio: return _T("DecayHFRatio");
416 	case kI3DL2ReverbReflections: return _T("Reflections");
417 	case kI3DL2ReverbReflectionsDelay: return _T("ReflectionsDelay");
418 	case kI3DL2ReverbReverb: return _T("Reverb");
419 	case kI3DL2ReverbReverbDelay: return _T("ReverbDelay");
420 	case kI3DL2ReverbDiffusion: return _T("Diffusion");
421 	case kI3DL2ReverbDensity: return _T("Density");
422 	case kI3DL2ReverbHFReference: return _T("HFRefrence");
423 	case kI3DL2ReverbQuality: return _T("Quality");
424 	}
425 	return CString();
426 }
427 
428 
GetParamLabel(PlugParamIndex param)429 CString I3DL2Reverb::GetParamLabel(PlugParamIndex param)
430 {
431 	switch(param)
432 	{
433 	case kI3DL2ReverbRoom:
434 	case kI3DL2ReverbRoomHF:
435 	case kI3DL2ReverbReflections:
436 	case kI3DL2ReverbReverb:
437 		return _T("dB");
438 	case kI3DL2ReverbDecayTime:
439 	case kI3DL2ReverbReflectionsDelay:
440 	case kI3DL2ReverbReverbDelay:
441 		return _T("s");
442 	case kI3DL2ReverbDiffusion:
443 	case kI3DL2ReverbDensity:
444 		return _T("%");
445 	case kI3DL2ReverbHFReference:
446 		return _T("Hz");
447 	}
448 	return CString();
449 }
450 
451 
GetParamDisplay(PlugParamIndex param)452 CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param)
453 {
454 	static constexpr const TCHAR * const modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") };
455 	float value = m_param[param];
456 	switch(param)
457 	{
458 	case kI3DL2ReverbRoom: value = Room() * 0.01f; break;
459 	case kI3DL2ReverbRoomHF: value = RoomHF() * 0.01f; break;
460 	case kI3DL2ReverbRoomRolloffFactor: value = RoomRolloffFactor(); break;
461 	case kI3DL2ReverbDecayTime: value = DecayTime(); break;
462 	case kI3DL2ReverbDecayHFRatio: value = DecayHFRatio(); break;
463 	case kI3DL2ReverbReflections: value = Reflections() * 0.01f; break;
464 	case kI3DL2ReverbReflectionsDelay: value = ReflectionsDelay(); break;
465 	case kI3DL2ReverbReverb: value = Reverb() * 0.01f; break;
466 	case kI3DL2ReverbReverbDelay: value = ReverbDelay(); break;
467 	case kI3DL2ReverbDiffusion: value = Diffusion(); break;
468 	case kI3DL2ReverbDensity: value = Density(); break;
469 	case kI3DL2ReverbHFReference: value = HFReference(); break;
470 	case kI3DL2ReverbQuality: return modes[Quality() % 4u];
471 	}
472 	CString s;
473 	s.Format(_T("%.2f"), value);
474 	return s;
475 }
476 
477 
GetCurrentProgramName()478 CString I3DL2Reverb::GetCurrentProgramName()
479 {
480 	return GetProgramName(m_program);
481 }
482 
483 
GetProgramName(int32 program)484 CString I3DL2Reverb::GetProgramName(int32 program)
485 {
486 	return mpt::ToCString(GetReverbPresetName(program));
487 }
488 
489 #endif // MODPLUG_TRACKER
490 
491 
RecalculateI3DL2ReverbParams()492 void I3DL2Reverb::RecalculateI3DL2ReverbParams()
493 {
494 	m_quality = Quality();
495 	m_effectiveSampleRate = static_cast<float>(m_SndFile.GetSampleRate() / ((m_quality & kFullSampleRate) ? 1u : 2u));
496 
497 	// Diffusion
498 	m_diffusion = Diffusion() * (0.618034f / 100.0f);
499 	// Early Reflection Level
500 	m_ERLevel = std::min(std::pow(10.0f, (Room() + Reflections()) / (100.0f * 20.0f)), 1.0f) * 0.761f;
501 
502 	// Room Filter
503 	float roomHF = std::pow(10.0f, RoomHF() / 100.0f / 10.0f);
504 	if(roomHF == 1.0f)
505 	{
506 		m_roomFilter = 0.0f;
507 	} else
508 	{
509 		float freq = std::cos(HFReference() * (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate);
510 		float roomFilter = (freq * (roomHF + roomHF) - 2.0f + std::sqrt(freq * (roomHF * roomHF * freq * 4.0f) + roomHF * 8.0f - roomHF * roomHF * 4.0f - roomHF * freq * 8.0f)) / (roomHF + roomHF - 2.0f);
511 		m_roomFilter = Clamp(roomFilter, 0.0f, 1.0f);
512 	}
513 
514 	SetDelayTaps();
515 	SetDecayCoeffs();
516 
517 	m_recalcParams = false;
518 }
519 
520 
SetDelayTaps()521 void I3DL2Reverb::SetDelayTaps()
522 {
523 	// Early reflections
524 	static constexpr float delays[] =
525 	{
526 		1.0000f, 1.0000f, 0.0000f, 0.1078f, 0.1768f, 0.2727f,
527 		0.3953f, 0.5386f, 0.6899f, 0.8306f, 0.9400f, 0.9800f,
528 	};
529 
530 	const float sampleRate = m_effectiveSampleRate;
531 	const float reflectionsDelay = ReflectionsDelay();
532 	const float reverbDelay = std::max(ReverbDelay(), 5.0f / 1000.0f);
533 	m_earlyTaps[0][0] = static_cast<int32>((reverbDelay + reflectionsDelay + 7.0f / 1000.0f) * sampleRate);
534 	for(uint32 i = 1; i < 12; i++)
535 	{
536 		m_earlyTaps[i % 2u][i / 2u] = static_cast<int32>((reverbDelay * delays[i] + reflectionsDelay) * sampleRate);
537 	}
538 
539 	// Late reflections
540 	float density = std::min((Density() / 100.0f + 0.1f) * 0.9091f, 1.0f);
541 	float delayL = density * 67.0f / 1000.0f * sampleRate;
542 	float delayR = density * 75.0f / 1000.0f * sampleRate;
543 	for(int i = 0, power = 0; i < 6; i++)
544 	{
545 		power += i;
546 		float factor = std::pow(0.93f, static_cast<float>(power));
547 		m_delayTaps[i + 0] = static_cast<int32>(delayL * factor);
548 		m_delayTaps[i + 6] = static_cast<int32>(delayR * factor);
549 	}
550 	m_delayTaps[12] = static_cast<int32>(10.0f / 1000.0f * sampleRate);
551 	// Early reflections (extra delay lines)
552 	m_delayTaps[13] = static_cast<int32>(3.25f / 1000.0f * sampleRate);
553 	m_delayTaps[14] = static_cast<int32>(3.53f / 1000.0f * sampleRate);
554 
555 	for(std::size_t d = 0; d < std::size(m_delayTaps); d++)
556 		m_delayLines[d].SetDelayTap(m_delayTaps[d]);
557 }
558 
559 
SetDecayCoeffs()560 void I3DL2Reverb::SetDecayCoeffs()
561 {
562 	float levelLtmp = 1.0f, levelRtmp = 1.0f;
563 	float levelL = 0.0f, levelR = 0.0f;
564 
565 	levelLtmp *= CalcDecayCoeffs(5);
566 	levelRtmp *= CalcDecayCoeffs(11);
567 	levelL += levelLtmp * 0.0225f;
568 	levelR += levelRtmp * 0.0225f;
569 
570 	levelLtmp *= CalcDecayCoeffs(4);
571 	levelRtmp *= CalcDecayCoeffs(10);
572 	levelL += levelLtmp * 0.04f;
573 	levelR += levelRtmp * 0.04f;
574 
575 	if(m_quality & kMoreDelayLines)
576 	{
577 		levelLtmp *= CalcDecayCoeffs(3);
578 		levelRtmp *= CalcDecayCoeffs(9);
579 		levelL += levelLtmp * 0.1225f;
580 		levelR += levelRtmp * 0.1225f;
581 
582 		levelLtmp *= CalcDecayCoeffs(2);
583 		levelRtmp *= CalcDecayCoeffs(8);
584 		levelL += levelLtmp * 0.1444f;
585 		levelR += levelRtmp * 0.1444f;
586 	}
587 	CalcDecayCoeffs(12);
588 	levelLtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0];
589 	levelRtmp *= m_delayCoeffs[12][0] * m_delayCoeffs[12][0];
590 
591 	levelLtmp *= CalcDecayCoeffs(1);
592 	levelRtmp *= CalcDecayCoeffs(7);
593 	levelL += levelRtmp * 0.1444f;
594 	levelR += levelLtmp * 0.1444f;
595 
596 	levelLtmp *= CalcDecayCoeffs(0);
597 	levelRtmp *= CalcDecayCoeffs(6);
598 	levelL += levelLtmp * 0.1444f;
599 	levelR += levelRtmp * 0.1444f;
600 
601 	// Final Reverb Level
602 	float level = std::min(std::pow(10.0f, (Room() + Reverb()) / (100.0f * 20.0f)), 1.0f);
603 	float monoInv = 1.0f - ((levelLtmp + levelRtmp) * 0.5f);
604 	m_ReverbLevelL = level * std::sqrt(monoInv / levelL);
605 	m_ReverbLevelR = level * std::sqrt(monoInv / levelR);
606 }
607 
608 
CalcDecayCoeffs(int32 index)609 float I3DL2Reverb::CalcDecayCoeffs(int32 index)
610 {
611 	float hfRef = (2.0f * mpt::numbers::pi_v<float>) / m_effectiveSampleRate * HFReference();
612 	float decayHFRatio = DecayHFRatio();
613 	if(decayHFRatio > 1.0f)
614 		hfRef = mpt::numbers::pi_v<float>;
615 
616 	float c1 = std::pow(10.0f, ((m_delayTaps[index] / m_effectiveSampleRate) * -60.0f / DecayTime()) / 20.0f);
617 	float c2 = 0.0f;
618 
619 	float c21 = (std::pow(c1, 2.0f - 2.0f / decayHFRatio) - 1.0f) / (1.0f - std::cos(hfRef));
620 	if(c21 != 0 && std::isfinite(c21))
621 	{
622 		float c22 = -2.0f * c21 - 2.0f;
623 		float c23sq = c22 * c22 - c21 * c21 * 4.0f;
624 		float c23 = c23sq > 0.0f ? std::sqrt(c23sq) : 0.0f;
625 		c2 = (c23 - c22) / (c21 + c21);
626 		if(std::abs(c2) > 1.0f)
627 			c2 = (-c22 - c23) / (c21 + c21);
628 	}
629 	m_delayCoeffs[index][0] = c1;
630 	m_delayCoeffs[index][1] = c2;
631 
632 	c1 *= c1;
633 	float diff2 = m_diffusion * m_diffusion;
634 	return diff2 + c1 / (1.0f - diff2 * c1) * (1.0f - diff2) * (1.0f - diff2);
635 }
636 
637 } // namespace DMO
638 
639 #else
640 MPT_MSVC_WORKAROUND_LNK4221(I3DL2Reverb)
641 
642 #endif // !NO_PLUGINS
643 
644 OPENMPT_NAMESPACE_END
645