2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
31 //==============================================================================
33 A collection of structs to pass as the template argument when setting the
34 interpolation type for the DelayLine class.
36 namespace DelayLineInterpolationTypes
39 No interpolation between successive samples in the delay line will be
40 performed. This is useful when the delay is a constant integer or to
41 create lo-fi audio effects.
48 Successive samples in the delay line will be linearly interpolated. This
49 type of interpolation has a low computational cost where the delay can be
50 modulated in real time, but it also introduces a low-pass filtering effect
51 into your audio signal.
58 Successive samples in the delay line will be interpolated using a 3rd order
59 Lagrange interpolator. This method incurs more computational overhead than
60 linear interpolation but reduces the low-pass filtering effect whilst
61 remaining amenable to real time delay modulation.
65 struct Lagrange3rd
{};
68 Successive samples in the delay line will be interpolated using 1st order
69 Thiran interpolation. This method is very efficient, and features a flat
70 amplitude frequency response in exchange for less accuracy in the phase
71 response. This interpolation method is stateful so is unsuitable for
72 applications requiring fast delay modulation.
79 //==============================================================================
81 A delay line processor featuring several algorithms for the fractional delay
82 calculation, block processing, and sample-by-sample processing useful when
83 modulating the delay in real time or creating a standard delay effect with
86 Note: If you intend to change the delay in real time, you may want to smooth
87 changes to the delay systematically using either a ramp or a low-pass filter.
89 @see SmoothedValue, FirstOrderTPTFilter
93 template <typename SampleType
, typename InterpolationType
= DelayLineInterpolationTypes::Linear
>
97 //==============================================================================
98 /** Default constructor. */
102 explicit DelayLine (int maximumDelayInSamples
);
104 //==============================================================================
105 /** Sets the delay in samples. */
106 void setDelay (SampleType newDelayInSamples
);
108 /** Returns the current delay in samples. */
109 SampleType
getDelay() const;
111 //==============================================================================
112 /** Initialises the processor. */
113 void prepare (const ProcessSpec
& spec
);
115 /** Sets a new maximum delay in samples.
117 Also clears the delay line.
119 This may allocate internally, so you should never call it from the audio thread.
121 void setMaximumDelayInSamples (int maxDelayInSamples
);
123 /** Gets the maximum possible delay in samples.
125 For very short delay times, the result of getMaximumDelayInSamples() may
126 differ from the last value passed to setMaximumDelayInSamples().
128 int getMaximumDelayInSamples() const noexcept
{ return totalSize
- 1; }
130 /** Resets the internal state variables of the processor. */
133 //==============================================================================
134 /** Pushes a single sample into one channel of the delay line.
136 Use this function and popSample instead of process if you need to modulate
137 the delay in real time instead of using a fixed delay value, or if you want
138 to code a delay effect with a feedback loop.
140 @see setDelay, popSample, process
142 void pushSample (int channel
, SampleType sample
);
144 /** Pops a single sample from one channel of the delay line.
146 Use this function to modulate the delay in real time or implement standard
147 delay effects with feedback.
149 @param channel the target channel for the delay line.
151 @param delayInSamples sets the wanted fractional delay in samples, or -1
152 to use the value being used before or set with
155 @param updateReadPointer should be set to true if you use the function
156 once for each sample, or false if you need
157 multi-tap delay capabilities.
159 @see setDelay, pushSample, process
161 SampleType
popSample (int channel
, SampleType delayInSamples
= -1, bool updateReadPointer
= true);
163 //==============================================================================
164 /** Processes the input and output samples supplied in the processing context.
166 Can be used for block processing when the delay is not going to change
167 during processing. The delay must first be set by calling setDelay.
171 template <typename ProcessContext
>
172 void process (const ProcessContext
& context
) noexcept
174 const auto& inputBlock
= context
.getInputBlock();
175 auto& outputBlock
= context
.getOutputBlock();
176 const auto numChannels
= outputBlock
.getNumChannels();
177 const auto numSamples
= outputBlock
.getNumSamples();
179 jassert (inputBlock
.getNumChannels() == numChannels
);
180 jassert (inputBlock
.getNumChannels() == writePos
.size());
181 jassert (inputBlock
.getNumSamples() == numSamples
);
183 if (context
.isBypassed
)
185 outputBlock
.copyFrom (inputBlock
);
189 for (size_t channel
= 0; channel
< numChannels
; ++channel
)
191 auto* inputSamples
= inputBlock
.getChannelPointer (channel
);
192 auto* outputSamples
= outputBlock
.getChannelPointer (channel
);
194 for (size_t i
= 0; i
< numSamples
; ++i
)
196 pushSample ((int) channel
, inputSamples
[i
]);
197 outputSamples
[i
] = popSample ((int) channel
);
203 //==============================================================================
204 template <typename T
= InterpolationType
>
205 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::None
>::value
, SampleType
>::type
206 interpolateSample (int channel
) const
208 auto index
= (readPos
[(size_t) channel
] + delayInt
) % totalSize
;
209 return bufferData
.getSample (channel
, index
);
212 template <typename T
= InterpolationType
>
213 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Linear
>::value
, SampleType
>::type
214 interpolateSample (int channel
) const
216 auto index1
= readPos
[(size_t) channel
] + delayInt
;
217 auto index2
= index1
+ 1;
219 if (index2
>= totalSize
)
225 auto value1
= bufferData
.getSample (channel
, index1
);
226 auto value2
= bufferData
.getSample (channel
, index2
);
228 return value1
+ delayFrac
* (value2
- value1
);
231 template <typename T
= InterpolationType
>
232 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Lagrange3rd
>::value
, SampleType
>::type
233 interpolateSample (int channel
) const
235 auto index1
= readPos
[(size_t) channel
] + delayInt
;
236 auto index2
= index1
+ 1;
237 auto index3
= index2
+ 1;
238 auto index4
= index3
+ 1;
240 if (index4
>= totalSize
)
248 auto* samples
= bufferData
.getReadPointer (channel
);
250 auto value1
= samples
[index1
];
251 auto value2
= samples
[index2
];
252 auto value3
= samples
[index3
];
253 auto value4
= samples
[index4
];
255 auto d1
= delayFrac
- 1.f
;
256 auto d2
= delayFrac
- 2.f
;
257 auto d3
= delayFrac
- 3.f
;
259 auto c1
= -d1
* d2
* d3
/ 6.f
;
260 auto c2
= d2
* d3
* 0.5f
;
261 auto c3
= -d1
* d3
* 0.5f
;
262 auto c4
= d1
* d2
/ 6.f
;
264 return value1
* c1
+ delayFrac
* (value2
* c2
+ value3
* c3
+ value4
* c4
);
267 template <typename T
= InterpolationType
>
268 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Thiran
>::value
, SampleType
>::type
269 interpolateSample (int channel
)
271 auto index1
= readPos
[(size_t) channel
] + delayInt
;
272 auto index2
= index1
+ 1;
274 if (index2
>= totalSize
)
280 auto value1
= bufferData
.getSample (channel
, index1
);
281 auto value2
= bufferData
.getSample (channel
, index2
);
283 auto output
= delayFrac
== 0 ? value1
: value2
+ alpha
* (value1
- v
[(size_t) channel
]);
284 v
[(size_t) channel
] = output
;
289 //==============================================================================
290 template <typename T
= InterpolationType
>
291 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::None
>::value
, void>::type
292 updateInternalVariables()
296 template <typename T
= InterpolationType
>
297 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Linear
>::value
, void>::type
298 updateInternalVariables()
302 template <typename T
= InterpolationType
>
303 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Lagrange3rd
>::value
, void>::type
304 updateInternalVariables()
313 template <typename T
= InterpolationType
>
314 typename
std::enable_if
<std::is_same
<T
, DelayLineInterpolationTypes::Thiran
>::value
, void>::type
315 updateInternalVariables()
317 if (delayFrac
< (SampleType
) 0.618 && delayInt
>= 1)
323 alpha
= (1 - delayFrac
) / (1 + delayFrac
);
326 //==============================================================================
329 //==============================================================================
330 AudioBuffer
<SampleType
> bufferData
;
331 std::vector
<SampleType
> v
;
332 std::vector
<int> writePos
, readPos
;
333 SampleType delay
= 0.0, delayFrac
= 0.0;
334 int delayInt
= 0, totalSize
= 4;
335 SampleType alpha
= 0.0;