aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/Analog.cpp
blob: 8ac28e401a2f1218807c0c683bc3926882d114b1 (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
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
 * Copyright (C) 2011, 2012, 2013, 2014 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 <cstring>
#include "Analog.h"

namespace MT32Emu {

#if MT32EMU_USE_FLOAT_SAMPLES

/* FIR approximation of the overall impulse response of the cascade composed of the sample & hold circuit and the low pass filter
 * of the MT-32 first generation.
 * The coefficients below are found by windowing the inverse DFT of the 1024 pin frequency response converted to the minimum phase.
 * The frequency response of the LPF is computed directly, the effect of the S&H is approximated by multiplying the LPF frequency
 * response by the corresponding sinc. Although, the LPF has DC gain of 3.2, we ignore this in the emulation and use normalised model.
 * The peak gain of the normalised cascade appears about 1.7 near 11.8 kHz. Relative error doesn't exceed 1% for the frequencies
 * below 12.5 kHz. In the higher frequency range, the relative error is below 8%. Peak error value is at 16 kHz.
 */
static const float COARSE_LPF_TAPS_MT32[] = {
	1.272473681f, -0.220267785f, -0.158039905f, 0.179603785f, -0.111484097f, 0.054137498f, -0.023518029f, 0.010997169f, -0.006935698f
};

// Similar approximation for new MT-32 and CM-32L/LAPC-I LPF. As the voltage controlled amplifier was introduced, LPF has unity DC gain.
// The peak gain value shifted towards higher frequencies and a bit higher about 1.83 near 13 kHz.
static const float COARSE_LPF_TAPS_CM32L[] = {
	1.340615635f, -0.403331694f, 0.036005517f, 0.066156844f, -0.069672532f, 0.049563806f, -0.031113416f, 0.019169774f, -0.012421368f
};

#else

static const unsigned int COARSE_LPF_FRACTION_BITS = 14;

// Integer versions of the FIRs above multiplied by (1 << 14) and rounded.
static const SampleEx COARSE_LPF_TAPS_MT32[] = {
	20848, -3609, -2589, 2943, -1827, 887, -385, 180, -114
};

static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
	21965, -6608, 590, 1084, -1142, 812, -510, 314, -204
};

#endif

/* Combined FIR that both approximates the impulse response of the analogue circuits of sample & hold and the low pass filter
 * in the audible frequency range (below 20 kHz) and attenuates unwanted mirror spectra above 28 kHz as well. It is a polyphase
 * filter intended for resampling the signal to 48 kHz yet for applying high frequency boost.
 * As with the filter above, the analogue LPF frequency response is obtained for 1536 pin grid for range up to 96 kHz and multiplied
 * by the corresponding sinc. The result is further squared, windowed and passed to generalised Parks-McClellan routine as a desired response.
 * Finally, the minimum phase factor is found that's essentially the coefficients below.
 * Relative error in the audible frequency range doesn't exceed 0.0006%, attenuation in the stopband is better than 100 dB.
 * This level of performance makes it nearly bit-accurate for standard 16-bit sample resolution.
 */

// FIR version for MT-32 first generation.
static const float ACCURATE_LPF_TAPS_MT32[] = {
	0.003429281f, 0.025929869f, 0.096587777f, 0.228884848f, 0.372413431f, 0.412386503f, 0.263980018f,
	-0.014504962f, -0.237394528f, -0.257043496f, -0.103436603f, 0.063996095f, 0.124562333f, 0.083703206f,
	0.013921662f, -0.033475018f, -0.046239712f, -0.029310921f, 0.00126585f, 0.021060961f, 0.017925605f,
	0.003559874f, -0.005105248f, -0.005647917f, -0.004157918f, -0.002065664f, 0.00158747f, 0.003762585f,
	0.001867137f, -0.001090028f, -0.001433979f, -0.00022367f, 4.34308E-05f, -0.000247827f, 0.000157087f,
	0.000605823f, 0.000197317f, -0.000370511f, -0.000261202f, 9.96069E-05f, 9.85073E-05f, -5.28754E-05f,
	-1.00912E-05f, 7.69943E-05f, 2.03162E-05f, -5.67967E-05f, -3.30637E-05f, 1.61958E-05f, 1.73041E-05f
};

// FIR version for new MT-32 and CM-32L/LAPC-I.
static const float ACCURATE_LPF_TAPS_CM32L[] = {
	0.003917452f, 0.030693861f, 0.116424199f, 0.275101674f, 0.43217361f, 0.431247894f, 0.183255659f,
	-0.174955671f, -0.354240244f, -0.212401714f, 0.072259178f, 0.204655344f, 0.108336211f, -0.039099027f,
	-0.075138174f, -0.026261906f, 0.00582663f, 0.003052193f, 0.00613657f, 0.017017951f, 0.008732535f,
	-0.011027427f, -0.012933664f, 0.001158097f, 0.006765958f, 0.00046778f, -0.002191106f, 0.001561017f,
	0.001842871f, -0.001996876f, -0.002315836f, 0.000980965f, 0.001817454f, -0.000243272f, -0.000972848f,
	0.000149941f, 0.000498886f, -0.000204436f, -0.000347415f, 0.000142386f, 0.000249137f, -4.32946E-05f,
	-0.000131231f, 3.88575E-07f, 4.48813E-05f, -1.31906E-06f, -1.03499E-05f, 7.71971E-06f, 2.86721E-06f
};

// According to the CM-64 PCB schematic, there is a difference in the values of the LPF entrance resistors for the reverb and non-reverb channels.
// This effectively results in non-unity LPF DC gain for the reverb channel of 0.68 while the LPF has unity DC gain for the LA32 output channels.
// In emulation, the reverb output gain is multiplied by this factor to compensate for the LPF gain difference.
static const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;

static const unsigned int OUTPUT_GAIN_FRACTION_BITS = 8;
static const float OUTPUT_GAIN_MULTIPLIER = float(1 << OUTPUT_GAIN_FRACTION_BITS);

static const unsigned int COARSE_LPF_DELAY_LINE_LENGTH = 8; // Must be a power of 2
static const unsigned int ACCURATE_LPF_DELAY_LINE_LENGTH = 16; // Must be a power of 2
static const unsigned int ACCURATE_LPF_NUMBER_OF_PHASES = 3; // Upsampling factor
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_REGULAR = 2; // Downsampling factor
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED = 1; // No downsampling
static const Bit32u ACCURATE_LPF_DELTAS_REGULAR[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 2, 1 } };
static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 0, 1 } };

class AbstractLowPassFilter {
public:
	static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
	static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);

	virtual ~AbstractLowPassFilter() {}
	virtual SampleEx process(SampleEx sample) = 0;
	virtual bool hasNextSample() const;
	virtual unsigned int getOutputSampleRate() const;
	virtual unsigned int estimateInSampleCount(unsigned int outSamples) const;
	virtual void addPositionIncrement(unsigned int) {}
};

class NullLowPassFilter : public AbstractLowPassFilter {
public:
	SampleEx process(SampleEx sample);
};

class CoarseLowPassFilter : public AbstractLowPassFilter {
private:
	const SampleEx * const LPF_TAPS;
	SampleEx ringBuffer[COARSE_LPF_DELAY_LINE_LENGTH];
	unsigned int ringBufferPosition;

public:
	CoarseLowPassFilter(bool oldMT32AnalogLPF);
	SampleEx process(SampleEx sample);
};

class AccurateLowPassFilter : public AbstractLowPassFilter {
private:
	const float * const LPF_TAPS;
	const Bit32u (* const deltas)[ACCURATE_LPF_NUMBER_OF_PHASES];
	const unsigned int phaseIncrement;
	const unsigned int outputSampleRate;

	SampleEx ringBuffer[ACCURATE_LPF_DELAY_LINE_LENGTH];
	unsigned int ringBufferPosition;
	unsigned int phase;

public:
	AccurateLowPassFilter(bool oldMT32AnalogLPF, bool oversample);
	SampleEx process(SampleEx sample);
	bool hasNextSample() const;
	unsigned int getOutputSampleRate() const;
	unsigned int estimateInSampleCount(unsigned int outSamples) const;
	void addPositionIncrement(unsigned int positionIncrement);
};

Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) :
	leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
	rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
	synthGain(0),
	reverbGain(0)
{}

Analog::~Analog() {
	delete &leftChannelLPF;
	delete &rightChannelLPF;
}

void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
	if (outStream == NULL) {
		leftChannelLPF.addPositionIncrement(outLength);
		rightChannelLPF.addPositionIncrement(outLength);
		return;
	}

	while (0 < (outLength--)) {
		SampleEx outSampleL;
		SampleEx outSampleR;

		if (leftChannelLPF.hasNextSample()) {
			outSampleL = leftChannelLPF.process(0);
			outSampleR = rightChannelLPF.process(0);
		} else {
			SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain;
			SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain;

#if !MT32EMU_USE_FLOAT_SAMPLES
			inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
			inSampleR >>= OUTPUT_GAIN_FRACTION_BITS;
#endif

			outSampleL = leftChannelLPF.process(inSampleL);
			outSampleR = rightChannelLPF.process(inSampleR);
		}

		*((*outStream)++) = Synth::clipSampleEx(outSampleL);
		*((*outStream)++) = Synth::clipSampleEx(outSampleR);
	}
}

unsigned int Analog::getOutputSampleRate() const {
	return leftChannelLPF.getOutputSampleRate();
}

Bit32u Analog::getDACStreamsLength(Bit32u outputLength) const {
	return leftChannelLPF.estimateInSampleCount(outputLength);
}

void Analog::setSynthOutputGain(float useSynthGain) {
#if MT32EMU_USE_FLOAT_SAMPLES
	synthGain = useSynthGain;
#else
	if (OUTPUT_GAIN_MULTIPLIER < useSynthGain) useSynthGain = OUTPUT_GAIN_MULTIPLIER;
	synthGain = SampleEx(useSynthGain * OUTPUT_GAIN_MULTIPLIER);
#endif
}

void Analog::setReverbOutputGain(float useReverbGain, bool mt32ReverbCompatibilityMode) {
	if (!mt32ReverbCompatibilityMode) useReverbGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
#if MT32EMU_USE_FLOAT_SAMPLES
	reverbGain = useReverbGain;
#else
	if (OUTPUT_GAIN_MULTIPLIER < useReverbGain) useReverbGain = OUTPUT_GAIN_MULTIPLIER;
	reverbGain = SampleEx(useReverbGain * OUTPUT_GAIN_MULTIPLIER);
#endif
}

AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
	switch (mode) {
		case AnalogOutputMode_COARSE:
			return *new CoarseLowPassFilter(oldMT32AnalogLPF);
		case AnalogOutputMode_ACCURATE:
			return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
		case AnalogOutputMode_OVERSAMPLED:
			return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
		default:
			return *new NullLowPassFilter;
	}
}

void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) {

#if MT32EMU_USE_FLOAT_SAMPLES

	SampleEx *p = ringBuffer;
	while (length--) {
		*(p++) = 0.0f;
	}

#else

	memset(ringBuffer, 0, length * sizeof(SampleEx));

#endif

}

bool AbstractLowPassFilter::hasNextSample() const {
	return false;
}

unsigned int AbstractLowPassFilter::getOutputSampleRate() const {
	return SAMPLE_RATE;
}

unsigned int AbstractLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
	return outSamples;
}

SampleEx NullLowPassFilter::process(const SampleEx inSample) {
	return inSample;
}

CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
	LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
	ringBufferPosition(0)
{
	muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
}

SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
	static const unsigned int DELAY_LINE_MASK = COARSE_LPF_DELAY_LINE_LENGTH - 1;

	SampleEx sample = LPF_TAPS[COARSE_LPF_DELAY_LINE_LENGTH] * ringBuffer[ringBufferPosition];
	ringBuffer[ringBufferPosition] = Synth::clipSampleEx(inSample);

	for (unsigned int i = 0; i < COARSE_LPF_DELAY_LINE_LENGTH; i++) {
		sample += LPF_TAPS[i] * ringBuffer[(i + ringBufferPosition) & DELAY_LINE_MASK];
	}

	ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;

#if !MT32EMU_USE_FLOAT_SAMPLES
	sample >>= COARSE_LPF_FRACTION_BITS;
#endif

	return sample;
}

AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const bool oversample) :
	LPF_TAPS(oldMT32AnalogLPF ? ACCURATE_LPF_TAPS_MT32 : ACCURATE_LPF_TAPS_CM32L),
	deltas(oversample ? ACCURATE_LPF_DELTAS_OVERSAMPLED : ACCURATE_LPF_DELTAS_REGULAR),
	phaseIncrement(oversample ? ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED : ACCURATE_LPF_PHASE_INCREMENT_REGULAR),
	outputSampleRate(SAMPLE_RATE * ACCURATE_LPF_NUMBER_OF_PHASES / phaseIncrement),
	ringBufferPosition(0),
	phase(0)
{
	muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
}

SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
	static const unsigned int DELAY_LINE_MASK = ACCURATE_LPF_DELAY_LINE_LENGTH - 1;

	float sample = (phase == 0) ? LPF_TAPS[ACCURATE_LPF_DELAY_LINE_LENGTH * ACCURATE_LPF_NUMBER_OF_PHASES] * ringBuffer[ringBufferPosition] : 0.0f;
	if (!hasNextSample()) {
		ringBuffer[ringBufferPosition] = inSample;
	}

	for (unsigned int tapIx = phase, delaySampleIx = 0; delaySampleIx < ACCURATE_LPF_DELAY_LINE_LENGTH; delaySampleIx++, tapIx += ACCURATE_LPF_NUMBER_OF_PHASES) {
		sample += LPF_TAPS[tapIx] * ringBuffer[(delaySampleIx + ringBufferPosition) & DELAY_LINE_MASK];
	}

	phase += phaseIncrement;
	if (ACCURATE_LPF_NUMBER_OF_PHASES <= phase) {
		phase -= ACCURATE_LPF_NUMBER_OF_PHASES;
		ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
	}

	return SampleEx(ACCURATE_LPF_NUMBER_OF_PHASES * sample);
}

bool AccurateLowPassFilter::hasNextSample() const {
	return phaseIncrement <= phase;
}

unsigned int AccurateLowPassFilter::getOutputSampleRate() const {
	return outputSampleRate;
}

unsigned int AccurateLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
	Bit32u cycleCount = outSamples / ACCURATE_LPF_NUMBER_OF_PHASES;
	Bit32u remainder = outSamples - cycleCount * ACCURATE_LPF_NUMBER_OF_PHASES;
	return cycleCount * phaseIncrement + deltas[remainder][phase];
}

void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncrement) {
	phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
}

}