aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/DelayReverb.cpp
blob: 89eebf0d79d7b61d0c19b91329d505c90f69fe5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
73
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
138
139
140
141
142
143
144
145
146
147
148
149
150
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
 * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 2.1 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//#include <cmath>
//#include <cstring>
#include "mt32emu.h"
#include "DelayReverb.h"

using namespace MT32Emu;


// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations.
// Obviously:
// rightDelay = (leftDelay - 2) * 2 + 2
// echoDelay = rightDelay - 1
// Leaving these separate in case it's useful for work on other reverb modes...
const Bit32u REVERB_TIMINGS[8][3]= {
	// {leftDelay, rightDelay, feedbackDelay}
	{402, 802, 801},
	{626, 1250, 1249},
	{962, 1922, 1921},
	{1490, 2978, 2977},
	{2258, 4514, 4513},
	{3474, 6946, 6945},
	{5282, 10562, 10561},
	{8002, 16002, 16001}
};

const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f};
const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3)
const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96)
const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f)

DelayReverb::DelayReverb() {
	buf = NULL;
	sampleRate = 0;
	setParameters(0, 0);
}

DelayReverb::~DelayReverb() {
	delete[] buf;
}

void DelayReverb::open(unsigned int newSampleRate) {
	if (newSampleRate != sampleRate || buf == NULL) {
		sampleRate = newSampleRate;

		delete[] buf;

		// If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes
		bufSize = 16384 * sampleRate / 32000;
		buf = new float[bufSize];

		recalcParameters();

		// mute buffer
		bufIx = 0;
		if (buf != NULL) {
			for (unsigned int i = 0; i < bufSize; i++) {
				buf[i] = 0.0f;
			}
		}
	}
	// FIXME: IIR filter value depends on sample rate as well
}

void DelayReverb::close() {
	delete[] buf;
	buf = NULL;
}

// This method will always trigger a flush of the buffer
void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) {
	time = newTime;
	level = newLevel;
	recalcParameters();
}

void DelayReverb::recalcParameters() {
	// Number of samples between impulse and eventual appearance on the left channel
	delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000;
	// Number of samples between impulse and eventual appearance on the right channel
	delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000;
	// Number of samples between a response and that response feeding back/echoing
	delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000;

	if (time < 6) {
		feedback = REVERB_FEEDBACK;
	} else {
		feedback = REVERB_FEEDBACK67;
	}

	// Fading speed, i.e. amplitude ratio of neighbor responses
	fade = REVERB_FADE[level];
}

void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
	if (buf == NULL) {
		return;
	}

	for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) {
		// The ring buffer write index moves backwards; reads are all done with positive offsets.
		Bit32u bufIxPrev = (bufIx + 1) % bufSize;
		Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize;
		Bit32u bufIxRight = (bufIx + delayRight) % bufSize;
		Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize;

		// Attenuated input samples and feedback response are directly added to the current ring buffer location
		float sample = fade * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback];

		// Single-pole IIR filter found on real devices
		buf[bufIx] = buf[bufIxPrev] + (sample - buf[bufIxPrev]) * LPF_VALUE;

		outLeft[sampleIx] = buf[bufIxLeft];
		outRight[sampleIx] = buf[bufIxRight];

		bufIx = (bufSize + bufIx - 1) % bufSize;
	}
}

bool DelayReverb::isActive() const {
	// Quick hack: Return true iff all samples in the left buffer are the same and
	// all samples in the right buffers are the same (within the sample output threshold).
	if (buf == NULL) {
		return false;
	}
	float last = buf[0] * 8192.0f;
	for (unsigned int i = 1; i < bufSize; i++) {
		float s = (buf[i] * 8192.0f);
		if (fabs(s - last) > 1.0f) {
			return true;
		}
	}
	return false;
}