1 /*
2  * Delay.h - Delay effect objects to use as building blocks in DSP
3  *
4  * Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
5  * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6  *
7  * This file is part of LMMS - https://lmms.io
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public
20  * License along with this program (see COPYING); if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301 USA.
23  *
24  */
25 
26 
27 #ifndef DELAY_H
28 #define DELAY_H
29 
30 #include "lmms_basics.h"
31 #include "lmms_math.h"
32 #include "interpolation.h"
33 #include "MemoryManager.h"
34 
35 // brief usage
36 
37 // Classes:
38 
39 // CombFeedback: a feedback comb filter - basically a simple delay line, makes a comb shape in the freq response
40 // CombFeedfwd: a feed-forward comb filter - an "inverted" comb filter, can be combined with CombFeedback to create a net allpass if negative gain is used
41 // CombFeedbackDualtap: same as CombFeedback but takes two delay values
42 // AllpassDelay: an allpass delay - combines feedback and feed-forward - has flat frequency response
43 
44 // all classes are templated with channel count, any arbitrary channel count can be used for each fx
45 
46 // Methods (for all classes):
47 
48 // setDelay sets delay amount in frames. It's up to you to make this samplerate-agnostic.
49 // Fractions are allowed - linear interpolation is used to deal with them
50 // CombFeedbackDualTap is a special case: it requires 2 delay times
51 
52 // setMaxDelay (re)sets the maximum allowed delay, in frames
53 // NOTE: for performance reasons, there's no bounds checking at setDelay, so make sure you set maxDelay >= delay!
54 
55 // clearHistory clears the delay buffer
56 
57 // setGain sets the feedback/feed-forward gain, in linear amplitude, negative values are allowed
58 // 1.0 is full feedback/feed-forward, -1.0 is full negative feedback/feed-forward
59 
60 // update runs the fx for one frame - takes as arguments input and number of channel to run, returns output
61 
62 template<ch_cnt_t CHANNELS>
63 class CombFeedback
64 {
65 public:
66 	typedef double frame[CHANNELS];
67 
CombFeedback(int maxDelay)68 	CombFeedback( int maxDelay ) :
69 		m_size( maxDelay ),
70 		m_position( 0 ),
71 		m_feedBack( 0.0 ),
72 		m_delay( 0 ),
73 		m_fraction( 0.0 )
74 	{
75 		m_buffer = MM_ALLOC( frame, maxDelay );
76 		memset( m_buffer, 0, sizeof( frame ) * maxDelay );
77 	}
~CombFeedback()78 	virtual ~CombFeedback()
79 	{
80 		MM_FREE( m_buffer );
81 	}
82 
setMaxDelay(int maxDelay)83 	inline void setMaxDelay( int maxDelay )
84 	{
85 		if( maxDelay > m_size )
86 		{
87 			MM_FREE( m_buffer );
88 			m_buffer = MM_ALLOC( frame, maxDelay );
89 			memset( m_buffer, 0, sizeof( frame ) * maxDelay );
90 		}
91 		m_size = maxDelay;
92 		m_position %= m_size;
93 	}
94 
clearHistory()95 	inline void clearHistory()
96 	{
97 		memset( m_buffer, 0, sizeof( frame ) * m_size );
98 	}
99 
setDelay(double delay)100 	inline void setDelay( double delay )
101 	{
102 		m_delay = static_cast<int>( ceil( delay ) );
103 		m_fraction = 1.0 - ( delay - floor( delay ) );
104 	}
105 
setGain(double gain)106 	inline void setGain( double gain )
107 	{
108 		m_gain = gain;
109 	}
110 
update(double in,ch_cnt_t ch)111 	inline double update( double in, ch_cnt_t ch )
112 	{
113 		int readPos = m_position - m_delay;
114 		if( readPos < 0 ) { readPos += m_size; }
115 
116 		const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction );
117 
118 		++m_position %= m_size;
119 
120 		m_buffer[m_position][ch] = in + m_gain * y;
121 		return y;
122 	}
123 
124 private:
125 	frame * m_buffer;
126 	int m_size;
127 	int m_position;
128 	double m_gain;
129 	int m_delay;
130 	double m_fraction;
131 };
132 
133 
134 template<ch_cnt_t CHANNELS>
135 class CombFeedfwd
136 {
137 	typedef double frame[CHANNELS];
138 
CombFeedfwd(int maxDelay)139 	CombFeedfwd( int maxDelay ) :
140 		m_size( maxDelay ),
141 		m_position( 0 ),
142 		m_feedBack( 0.0 ),
143 		m_delay( 0 ),
144 		m_fraction( 0.0 )
145 	{
146 		m_buffer = MM_ALLOC( frame, maxDelay );
147 		memset( m_buffer, 0, sizeof( frame ) * maxDelay );
148 	}
~CombFeedfwd()149 	virtual ~CombFeedfwd()
150 	{
151 		MM_FREE( m_buffer );
152 	}
153 
setMaxDelay(int maxDelay)154 	inline void setMaxDelay( int maxDelay )
155 	{
156 		if( maxDelay > m_size )
157 		{
158 			MM_FREE( m_buffer );
159 			m_buffer = MM_ALLOC( frame, maxDelay );
160 			memset( m_buffer, 0, sizeof( frame ) * maxDelay );
161 		}
162 		m_size = maxDelay;
163 		m_position %= m_size;
164 	}
165 
clearHistory()166 	inline void clearHistory()
167 	{
168 		memset( m_buffer, 0, sizeof( frame ) * m_size );
169 	}
170 
setDelay(double delay)171 	inline void setDelay( double delay )
172 	{
173 		m_delay = static_cast<int>( ceil( delay ) );
174 		m_fraction = 1.0 - ( delay - floor( delay ) );
175 	}
176 
setGain(double gain)177 	inline void setGain( double gain )
178 	{
179 		m_gain = gain;
180 	}
181 
update(double in,ch_cnt_t ch)182 	inline double update( double in, ch_cnt_t ch )
183 	{
184 		int readPos = m_position - m_delay;
185 		if( readPos < 0 ) { readPos += m_size; }
186 
187 		const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * m_gain;
188 
189 		++m_position %= m_size;
190 
191 		m_buffer[m_position][ch] = in;
192 		return y;
193 	}
194 
195 private:
196 	frame * m_buffer;
197 	int m_size;
198 	int m_position;
199 	double m_gain;
200 	int m_delay;
201 	double m_fraction;
202 };
203 
204 
205 template<ch_cnt_t CHANNELS>
206 class CombFeedbackDualtap
207 {
208 	typedef double frame[CHANNELS];
209 
CombFeedbackDualtap(int maxDelay)210 	CombFeedbackDualtap( int maxDelay ) :
211 		m_size( maxDelay ),
212 		m_position( 0 ),
213 		m_feedBack( 0.0 ),
214 		m_delay( 0 ),
215 		m_fraction( 0.0 )
216 	{
217 		m_buffer = MM_ALLOC( frame, maxDelay );
218 		memset( m_buffer, 0, sizeof( frame ) * maxDelay );
219 	}
~CombFeedbackDualtap()220 	virtual ~CombFeedbackDualtap()
221 	{
222 		MM_FREE( m_buffer );
223 	}
224 
setMaxDelay(int maxDelay)225 	inline void setMaxDelay( int maxDelay )
226 	{
227 		if( maxDelay > m_size )
228 		{
229 			MM_FREE( m_buffer );
230 			m_buffer = MM_ALLOC( frame, maxDelay );
231 			memset( m_buffer, 0, sizeof( frame ) * maxDelay );
232 		}
233 		m_size = maxDelay;
234 		m_position %= m_size;
235 	}
236 
clearHistory()237 	inline void clearHistory()
238 	{
239 		memset( m_buffer, 0, sizeof( frame ) * m_size );
240 	}
241 
setDelays(double delay1,double delay2)242 	inline void setDelays( double delay1, double delay2 )
243 	{
244 		m_delay1 = static_cast<int>( ceil( delay1 ) );
245 		m_fraction1 = 1.0 - ( delay1 - floor( delay1 ) );
246 
247 		m_delay2 = static_cast<int>( ceil( delay2 ) );
248 		m_fraction2 = 1.0 - ( delay2 - floor( delay2 ) );
249 	}
250 
setGain(double gain)251 	inline void setGain( double gain )
252 	{
253 		m_gain = gain;
254 	}
255 
update(double in,ch_cnt_t ch)256 	inline double update( double in, ch_cnt_t ch )
257 	{
258 		int readPos1 = m_position - m_delay1;
259 		if( readPos1 < 0 ) { readPos1 += m_size; }
260 
261 		int readPos2 = m_position - m_delay2;
262 		if( readPos2 < 0 ) { readPos2 += m_size; }
263 
264 		const double y = linearInterpolate( m_buffer[readPos1][ch], m_buffer[( readPos1 + 1 ) % m_size][ch], m_fraction1 ) +
265 			linearInterpolate( m_buffer[readPos2][ch], m_buffer[( readPos2 + 1 ) % m_size][ch], m_fraction2 );
266 
267 		++m_position %= m_size;
268 
269 		m_buffer[m_position][ch] = in + m_gain * y;
270 		return y;
271 	}
272 
273 private:
274 	frame * m_buffer;
275 	int m_size;
276 	int m_position;
277 	double m_gain;
278 	int m_delay1;
279 	int m_delay2;
280 	double m_fraction1;
281 	double m_fraction2;
282 };
283 
284 
285 template<ch_cnt_t CHANNELS>
286 class AllpassDelay
287 {
288 public:
289 	typedef double frame[CHANNELS];
290 
AllpassDelay(int maxDelay)291 	AllpassDelay( int maxDelay ) :
292 		m_size( maxDelay ),
293 		m_position( 0 ),
294 		m_feedBack( 0.0 ),
295 		m_delay( 0 ),
296 		m_fraction( 0.0 )
297 	{
298 		m_buffer = MM_ALLOC( frame, maxDelay );
299 		memset( m_buffer, 0, sizeof( frame ) * maxDelay );
300 	}
~AllpassDelay()301 	virtual ~AllpassDelay()
302 	{
303 		MM_FREE( m_buffer );
304 	}
305 
setMaxDelay(int maxDelay)306 	inline void setMaxDelay( int maxDelay )
307 	{
308 		if( maxDelay > m_size )
309 		{
310 			MM_FREE( m_buffer );
311 			m_buffer = MM_ALLOC( frame, maxDelay );
312 			memset( m_buffer, 0, sizeof( frame ) * maxDelay );
313 		}
314 		m_size = maxDelay;
315 		m_position %= m_size;
316 	}
317 
clearHistory()318 	inline void clearHistory()
319 	{
320 		memset( m_buffer, 0, sizeof( frame ) * m_size );
321 	}
322 
setDelay(double delay)323 	inline void setDelay( double delay )
324 	{
325 		m_delay = static_cast<int>( ceil( delay ) );
326 		m_fraction = 1.0 - ( delay - floor( delay ) );
327 	}
328 
setGain(double gain)329 	inline void setGain( double gain )
330 	{
331 		m_gain = gain;
332 	}
333 
update(double in,ch_cnt_t ch)334 	inline double update( double in, ch_cnt_t ch )
335 	{
336 		int readPos = m_position - m_delay;
337 		if( readPos < 0 ) { readPos += m_size; }
338 
339 		const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * -m_gain;
340 		const double x = in + m_gain * y;
341 
342 		++m_position %= m_size;
343 
344 		m_buffer[m_position][ch] = x;
345 		return y;
346 	}
347 
348 private:
349 	frame * m_buffer;
350 	int m_size;
351 	int m_position;
352 	double m_gain;
353 	int m_delay;
354 	double m_fraction;
355 };
356 
357 // convenience typedefs for stereo effects
358 typedef CombFeedback<2> StereoCombFeedback;
359 typedef CombFeedfwd<2> StereoCombFeedfwd;
360 typedef CombFeedbackDualtap<2> StereoCombFeedbackDualtap;
361 typedef AllpassDelay<2> StereoAllpassDelay;
362 
363 #endif
364