aboutsummaryrefslogtreecommitdiff
path: root/transverb/transverb.h
blob: 28c7a4b0b8fdba26d68f7f0f7f64463ab94968ca (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
#ifndef __TOM7_TRANSVERB_H
#define __TOM7_TRANSVERB_H

//#define	TRANSVERB_STEREO	1
#define	USING_HERMITE		1

/* DFX Transverb transverb by Tom 7 and Marc 3 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>

#include "flext.h"

#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 202)
#error You need at least flext version 0.2.2 
#endif

#include "dfxmisc.h"
#include "IIRfilter.h"



//----------------------------------------------------------------------------- 
// these are the transverb parameters:

#define fsign(f) ((f<0.0f)?-1.0f:1.0f)

#ifdef TRANSVERB_STEREO
  #define NUM_CHANNELS 2
#else
  #define NUM_CHANNELS 1
#endif

#define BUFFER_MIN 1.0f
#define BUFFER_MAX 3000.0f
#define bufferMsScaled(A)   ( paramRangeScaled((A), BUFFER_MIN, BUFFER_MAX) )
#define bufferScaled(A)   ( ((int)(bufferMsScaled(A)*SAMPLERATE*0.001f) > MAXBUF) ? MAXBUF : (int)(bufferMsScaled(A)*SAMPLERATE*0.001f) )

#define gainScaled(A)   ((A)*(A))

#define SPEED_MIN (-3.0f)
#define SPEED_MAX 6.0f
#define speedScaled(A)   ( paramRangeScaled((A), SPEED_MIN, SPEED_MAX) )
#define speedUnscaled(A)   ( paramRangeUnscaled((A), SPEED_MIN, SPEED_MAX) )
// for backwards compatibility with versions 1.0 & 1.0.1
#define OLD_SPEED_MIN 0.03f
#define OLD_SPEED_MAX 10.0f
#define oldSpeedScaled(A)   ( paramRangeSquaredScaled((A), OLD_SPEED_MIN, OLD_SPEED_MAX) )

#define qualityScaled(A)   ( paramSteppedScaled((A), numQualities) )
#define qualityUnscaled(A)   ( paramSteppedUnscaled((A), numQualities) )

#define SMOOTH_DUR 42

// this is for converting version 1.0 speed parameter valuess to the current format
//#define newSpeed(A)   ((log2f(oldSpeedScaled((A)))-SPEED_MIN) / (SPEED_MAX-SPEED_MIN))
#define newSpeed(A)   (((logf(oldSpeedScaled((A)))/logf(2.0f))-SPEED_MIN) / (SPEED_MAX-SPEED_MIN))

// this stuff is for the speed parameter adjustment mode switch on the GUI
enum { kFineMode, kSemitoneMode, kOctaveMode, numSpeedModes };
#define speedModeScaled(A)   ( paramSteppedScaled((A), numSpeedModes) )

#define numFIRtaps 23

const float RAND_MAX_FLOAT = (float) RAND_MAX;	// reduces wasteful casting

enum { dirtfi, hifi, ultrahifi, numQualities };

enum { useNothing, useHighpass, useLowpassIIR, useLowpassFIR, numFilterModes };


class transverb: 
	public flext_dsp {

	FLEXT_HEADER(transverb, flext_dsp)
  
public:
	transverb(int argc, t_atom *argv);
	~transverb();


protected:

	void initPresets();
	void createAudioBuffers();
	void clearBuffers();

	virtual void m_signal(int n, float *const *in, float *const *out);
	virtual void m_help();

	float drymix;
	int bsize, ireplace;
	float mix1, speed1, feed1, dist1;
	float mix2, speed2, feed2, dist2;
	float fQuality, fTomsound;
	long quality;
	bool tomsound;

	int writer;
	double read1, read2;
  
	int sr; int blocksize;

	float * buf1[2];
	float * buf2[2];
	int MAXBUF;	// the size of the audio buffer (dependant on sampling rate)

	IIRfilter *filter1, *filter2;
	bool speed1hasChanged, speed2hasChanged;
	float fSpeed1mode, fSpeed2mode;

	int smoothcount1[2], smoothcount2[2], smoothdur1[2], smoothdur2[2];
	float smoothstep1[2], smoothstep2[2], lastr1val[2], lastr2val[2];

	float SAMPLERATE;

	float fBsize;

	float *firCoefficients1, *firCoefficients2;

	FLEXT_CALLBACK_F(setBsize)
	void setBsize(float f) {
		f = (f < 2999) ? f : 2999;
		f = (f > 0) ? f : 0;
		bsize = bufferScaled((int)f);
		writer %= bsize;
		read1 = fmod(fabs(read1), (double)bsize);
		read2 = fmod(fabs(read2), (double)bsize);
	}

	FLEXT_CALLBACK_F(setMix1)
	void setMix1(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		mix1 = gainScaled(f);
	}

	FLEXT_CALLBACK_F(setSpeed1)
	void setSpeed1(float f) {
		f = (f < 6) ? f : 6;
		f = (f > -3) ? f : -3;
		speed1 = powf(2.0f, speedScaled(f));
		speed1hasChanged = true;
	}

	FLEXT_CALLBACK_F(setFeed1)
	void setFeed1(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		feed1 = f;
	}

	FLEXT_CALLBACK_F(setDist1)
	void setDist1(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		dist1 = f;
		read1 = fmod(fabs((double)writer + (double)dist1 *
				  (double)MAXBUF), (double)bsize);
	}

	FLEXT_CALLBACK_F(setMix2)
	void setMix2(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		mix2 = gainScaled(f);
	}

	FLEXT_CALLBACK_F(setSpeed2)
	void setSpeed2(float f) {
		f = (f < 6) ? f : 6;
		f = (f > -3) ? f : -3;
		speed2 = powf(2.0f, speedScaled(f));
		speed2hasChanged = true;
	}

	FLEXT_CALLBACK_F(setFeed2)
	void setFeed2(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		feed2 = f;
	}

	FLEXT_CALLBACK_F(setDist2)
	void setDist2(float f) {
		f = (f < 1) ? f : 1;
		f = (f > 0) ? f : 0;
		dist2 = f;
		read2 = fmod(fabs((double)writer + (double)dist2 *
				  (double)MAXBUF), (double)bsize);
	}

private:
	// this one does the work
	virtual void processX(float *in, float *out, long n, int replacing);

	// init would be better name
	virtual void suspend();
};


inline float interpolateHermite (float *data, double address, 
				 int arraysize, int danger) {
  int pos, posMinus1, posPlus1, posPlus2;
  float posFract, a, b, c;

  pos = (long)address;
  posFract = (float) (address - (double)pos);

  // because the readers & writer are not necessarilly aligned, 
  // upcoming or previous samples could be discontiguous, in which case 
  // just "interpolate" with repeated samples
  switch (danger) {
    case 0:		// the previous sample is bogus
      posMinus1 = pos;
      posPlus1 = (pos+1) % arraysize;
      posPlus2 = (pos+2) % arraysize;
      break;
    case 1:		// the next 2 samples are bogus
      posMinus1 = (pos == 0) ? arraysize-1 : pos-1;
      posPlus1 = posPlus2 = pos;
      break;
    case 2:		// the sample 2 steps ahead is bogus
      posMinus1 = (pos == 0) ? arraysize-1 : pos-1;
      posPlus1 = posPlus2 = (pos+1) % arraysize;
      break;
    default:	// everything's cool
      posMinus1 = (pos == 0) ? arraysize-1 : pos-1;
      posPlus1 = (pos+1) % arraysize;
      posPlus2 = (pos+2) % arraysize;
      break;
    }

  a = ( (3.0f*(data[pos]-data[posPlus1])) - 
	 data[posMinus1] + data[posPlus2] ) * 0.5f;
  b = (2.0f*data[posPlus1]) + data[posMinus1] - 
         (2.5f*data[pos]) - (data[posPlus2]*0.5f);
  c = (data[posPlus1] - data[posMinus1]) * 0.5f;

  return ( ((a*posFract)+b) * posFract + c ) * posFract + data[pos];
}

inline float interpolateLinear(float *data, double address, 
				int arraysize, int danger) {
	int posPlus1, pos = (long)address;
	float posFract = (float) (address - (double)pos);

	if (danger == 1) {
	  /* the upcoming sample is not contiguous because 
	     the write head is about to write to it */
	  posPlus1 = pos;
	} else {
	  // it's all right
	  posPlus1 = (pos + 1) % arraysize;
	}
	return (data[pos] * (1.0f-posFract)) + 
	       (data[posPlus1] * posFract);
}



#endif