aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/DelayReverb.cpp
blob: d80c98acbc2c631e4bb5b0bae8fee2414abcfaa3 (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
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
 * Copyright (C) 2011, 2012, 2013 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"

namespace MT32Emu {

// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. 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...
static 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}
};

// Reverb amp is found as dryAmp * wetAmp
static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8};
static const Bit32u REVERB_FEEDBACK67 = 0x60;
static const Bit32u REVERB_FEEDBACK = 0x68;
static const float LPF_VALUE = 0x68 / 256.0f;

static const Bit32u BUFFER_SIZE = 16384;

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

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

void DelayReverb::open() {
	if (buf == NULL) {
		delete[] buf;

		buf = new float[BUFFER_SIZE];

		recalcParameters();

		// mute buffer
		bufIx = 0;
		if (buf != NULL) {
			for (unsigned int i = 0; i < BUFFER_SIZE; i++) {
				buf[i] = 0.0f;
			}
		}
	}
}

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];
	// Number of samples between impulse and eventual appearance on the right channel
	delayRight = REVERB_TIMINGS[time][1];
	// Number of samples between a response and that response feeding back/echoing
	delayFeedback = REVERB_TIMINGS[time][2];

	if (level < 3 || time < 6) {
		feedback = REVERB_FEEDBACK / 256.0f;
	} else {
		feedback = REVERB_FEEDBACK67 / 256.0f;
	}

	// Overall output amp
	amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f;
}

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) % BUFFER_SIZE;
		Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE;
		Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE;
		Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE;

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

		// Single-pole IIR filter found on real devices
		buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn;

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

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

bool DelayReverb::isActive() const {
	if (buf == NULL) return false;

	float *b = buf;
	float max = 0.001f;
	for (Bit32u i = 0; i < BUFFER_SIZE; i++) {
		if ((*b < -max) || (*b > max)) return true;
		b++;
	}
	return false;
}

}