Jamoma API  0.6.0.a19
TTDelay.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup dspLibrary
4  *
5  * @brief Basic audio delay unit with 4 interpolation options.
6  *
7  * @authors Timothy Place & Nathan Wolek
8  *
9  * @copyright Copyright © 2003-2012, Timothy Place & Nathan Wolek @n
10  * This code is licensed under the terms of the "New BSD License" @n
11  * http://creativecommons.org/licenses/BSD/
12  */
13 
14 #include "TTDelay.h"
15 
16 #define thisTTClass TTDelay
17 #define thisTTClassName "delay"
18 #define thisTTClassTags "dspLibrary, audio, processor, delay"
19 
20 #ifndef TT_PLATFORM_MAC
21 #include <algorithm>
22 #endif
23 
24 TT_AUDIO_CONSTRUCTOR,
25  mDelay(0),
26  mDelayInSamples(0),
27  mDelayMax(0),
28  mDelayMaxInSamples(0)
29 {
30  // declare attributes
32  addAttributeWithSetter(DelayInSamples, kTypeFloat64);
34  addAttributeWithSetter(DelayMaxInSamples, kTypeInt64);
35  addAttributeWithSetter(Interpolation, kTypeSymbol);
36 
37  // declare messages
38  addMessage(clear);
39 
40  // updates from the parent class
41  addUpdates(SampleRate);
42  addUpdates(MaxNumChannels);
43 
44  // Set Defaults...
45  setAttributeValue(kTTSym_maxNumChannels, arguments);
46  setAttributeValue("delayMaxInSamples", 256);
47  setAttributeValue("delayInSamples", 100);
48  setAttributeValue("interpolation", "cubic");
49 }
50 
51 
52 TTDelay::~TTDelay()
53 {
54  ;
55 }
56 
57 
58 // This is called every time that:
59 // 1. sr changes
60 // 2. maxNumChannels changes
61 // 3. maxNumSamples change
62 TTErr TTDelay::init(TTUInt64 newDelayMaxInSamples)
63 {
64  if (newDelayMaxInSamples) {
65  mDelayMaxInSamples = newDelayMaxInSamples;
66  mDelayMax = mDelayMaxInSamples / srMill;
67 
68  for (TTDelayBufferIter buffer = mBuffers.begin(); buffer != mBuffers.end(); ++buffer) {
69  buffer->resize(size_t(mDelayMaxInSamples));
70  buffer->clear();
71  }
72  reset();
73  }
74  return kTTErrNone;
75 }
76 
77 
79 {
80  mBuffers.resize(mMaxNumChannels);
81  return init(mDelayMaxInSamples);
82 }
83 
84 
86 {
87  init(long(srMill * mDelayMax)); // allocate a larger delay buffer if neccessary
88  return setDelay(mDelay); // hold the delay time in ms constant, despite the change of sr
89 }
90 
91 
93 {
94  for_each(mBuffers.begin(), mBuffers.end(), std::mem_fun_ref(&TTDelayBuffer::clear));
95  return kTTErrNone;
96 }
97 
98 
99 void TTDelay::reset()
100 {
101  for (TTDelayBufferIter buffer = mBuffers.begin(); buffer != mBuffers.end(); ++buffer)
102  buffer->setDelay(TTUInt32(mIntegralDelay)); // TTDelayBuffer requires the integer portion of the delay in samples
103 }
104 
105 
107 {
108  mDelay = TTClip<TTFloat64>(newValue, 0.0, mDelayMax);
109  mDelayInSamples = mDelay * srMill;
111  mFractionalDelay = mDelayInSamples - mIntegralDelay;
112 
113  reset();
114  return kTTErrNone;
115 }
116 
117 
119 {
120  mDelayInSamples = TTClip<TTFloat64>(newValue, 0, mDelayMaxInSamples);
123 
124  mDelay = mDelayInSamples * 1000.0 * srInv;
125 
126  reset();
127  return kTTErrNone;
128 }
129 
130 
132 {
133  mDelayMax = newValue;
134  mDelayMaxInSamples = mDelayMax * srMill;
135  init(mDelayMaxInSamples);
136  return kTTErrNone;
137 }
138 
139 
141 {
142  mDelayMaxInSamples = newValue;
143  mDelayMax = mDelayMaxInSamples * 1000.0 * srInv;
144  init(mDelayMaxInSamples);
145  return kTTErrNone;
146 }
147 
148 
150 {
151  mInterpolation = newValue;
152 
153  if (mInterpolation == "none") {
154  setProcessMethod(processAudioNoInterpolation);
155  }
156  else if (mInterpolation == "linear") {
157  setProcessMethod(processAudioLinearInterpolation);
158  }
159  else if (mInterpolation == "cosine") {
160  setProcessMethod(processAudioCosineInterpolation);
161  }
162  else if (mInterpolation == "cubic") {
163  setProcessMethod(processAudioCubicInterpolation);
164  }
165  else {
166  setProcessMethod(processAudioLinearInterpolation);
167  return kTTErrInvalidValue;
168  }
169  return kTTErrNone;
170 }
171 
172 
173 #if 0
174 #pragma mark -
175 #pragma mark dsp routines
176 #endif
177 
178 
179 #define TTDELAY_WRAP_CALCULATE_METHOD(methodName) \
180  TTAudioSignal& in = inputs->getSignal(0); \
181  TTAudioSignal& out = outputs->getSignal(0); \
182  TTUInt16 vs; \
183  TTSampleValue* inSample; \
184  TTSampleValue* outSample; \
185  TTChannelCount numchannels = TTAudioSignal::getMinChannelCount(in, out); \
186  TTPtrSizedInt channel; \
187  TTDelayBufferPtr buffer; \
188  \
189  for (channel=0; channel<numchannels; channel++) { \
190  inSample = in.mSampleVectors[channel]; \
191  outSample = out.mSampleVectors[channel]; \
192  vs = in.getVectorSizeAsInt(); \
193  buffer = &mBuffers[channel]; \
194  \
195  while (vs--) { \
196  methodName (*inSample, *outSample, buffer); \
197  outSample++; \
198  inSample++; \
199  } \
200  }\
201  return kTTErrNone;
202 //////////////////////////////////////////////////////////////////////////////////////////
203 // No Interpolation
204 
205 inline TTErr TTDelay::calculateNoInterpolation(const TTFloat64& x, TTFloat64& y, TTDelayBufferPtr buffer)
206 {
207  *(buffer->mWritePointer) = x; // write the input into our buffer
208  y = *(buffer->mReadPointer); // fetch the output from our buffer
209 
210  // advance the pointers
211  buffer->mWritePointer++;
212  buffer->mReadPointer++;
213 
214  // wrap the pointers in the buffer, if needed
215  if (buffer->mWritePointer > buffer->tail())
216  buffer->mWritePointer = buffer->head();
217  if (buffer->mReadPointer > buffer->tail())
218  buffer->mReadPointer = buffer->head();
219 
220  return kTTErrNone;
221 }
222 
223 TTErr TTDelay::calculateNoInterpolation(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
224 {
225  TTDelayBufferPtr buffer = &mBuffers[channel];
226  return calculateNoInterpolation(x, y, buffer);
227 }
228 
229 
230 TTErr TTDelay::processAudioNoInterpolation(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
231 {
232  TTDELAY_WRAP_CALCULATE_METHOD(calculateNoInterpolation);
233 }
234 
235 //////////////////////////////////////////////////////////////////////////////////////////
236 // Linear Interpolation
237 
238 
239 inline TTErr TTDelay::calculateLinearInterpolation(const TTFloat64& x, TTFloat64& y, TTDelayBufferPtr buffer)
240 {
241  *(buffer->mWritePointer) = x; // write the input into our buffer
242 
243  // move the record head, since we are done with it
244  buffer->mWritePointer++;
245  if (buffer->mWritePointer > buffer->tail())
246  buffer->mWritePointer = buffer->head();
247 
248  // store the value of the integer part of delayInSamples
249  TTSampleValue delaySample0 = *(buffer->mReadPointer);
250 
251  // store the value of the sample *before* it for interpolation
252  TTSampleValuePtr delaySample1Ptr = buffer->mReadPointer - 1;
253  delaySample1Ptr = buffer->wrapPointer(delaySample1Ptr);
254  TTSampleValue delaySample1 = *(delaySample1Ptr);
255 
256  // now you are ready to interpolate
257  y = TTInterpolateLinear(delaySample0, delaySample1, mFractionalDelay);
258 
259  // then move the play head
260  buffer->mReadPointer++;
261  if (buffer->mReadPointer > buffer->tail())
262  buffer->mReadPointer = buffer->head();
263 
264  return kTTErrNone;
265 }
266 
267 TTErr TTDelay::calculateLinearInterpolation(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
268 {
269  TTDelayBufferPtr buffer = &mBuffers[channel];
270  return calculateLinearInterpolation(x, y, buffer);
271 }
272 
273 TTErr TTDelay::processAudioLinearInterpolation(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
274 {
275  TTDELAY_WRAP_CALCULATE_METHOD(calculateLinearInterpolation);
276 }
277 
278 //////////////////////////////////////////////////////////////////////////////////////////
279 // Cosine Interpolation
280 
281 
282 TTErr TTDelay::calculateCosineInterpolation(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
283 {
284  TTDelayBufferPtr buffer = &mBuffers[channel];
285  return calculateCosineInterpolation(x, y, buffer);
286 }
287 
288 inline TTErr TTDelay::calculateCosineInterpolation(const TTFloat64& x, TTFloat64& y, TTDelayBufferPtr buffer)
289 { //http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
290 
291  *buffer->mWritePointer = x; // write the input into our buffer
292 
293  // move the record head, since we are done with it
294  buffer->mWritePointer++;
295  if (buffer->mWritePointer > buffer->tail())
296  buffer->mWritePointer = buffer->head();
297 
298  // store the value of the integer part of delayInSamples
299  TTSampleValue delaySample0 = *(buffer->mReadPointer);
300 
301  // store the value of the sample *before* it for interpolation
302  TTSampleValuePtr delaySample1Ptr = buffer->mReadPointer - 1;
303  delaySample1Ptr = buffer->wrapPointer(delaySample1Ptr);
304  TTSampleValue delaySample1 = *(delaySample1Ptr);
305 
306  // now you are ready to interpolate
307  y = TTInterpolateCosine(delaySample0, delaySample1, mFractionalDelay);
308 
309  // then move the play head
310  buffer->mReadPointer++;
311  if (buffer->mReadPointer > buffer->tail())
312  buffer->mReadPointer = buffer->head();
313 
314  return kTTErrNone;
315 }
316 
317 
318 TTErr TTDelay::processAudioCosineInterpolation(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
319 {
320  TTDELAY_WRAP_CALCULATE_METHOD(calculateCosineInterpolation);
321 }
322 
323 
324 //////////////////////////////////////////////////////////////////////////////////////////
325 // f-point Interpolation
326 
327 // NW: not sure these references are valid anymore, now that TTInterpolate method is used
328 // Four-point interpolation as described @ http://crca.ucsd.edu/~msp/techniques/latest/book-html/node114.html
329 // and http://crca.ucsd.edu/~msp/techniques/latest/book-html/node31.html#tab02.1
330 // Additional reading on cubic interpolation can be found
331 // here: http://www.paulinternet.nl/?page=bicubic
332 // and here: http://paulbourke.net/miscellaneous/interpolation/
333 // similar to what is implemented in Pd's vd~ object
334 // note that in initial tests there appears to be slight signal boost
335 
336 inline TTErr TTDelay::calculateCubicInterpolation(const TTFloat64& x, TTFloat64& y, TTDelayBufferPtr buffer)
337 {
338 
339  *buffer->mWritePointer = x; // write the input into our buffer
340 
341  // move the record head, since we are done with it
342  buffer->mWritePointer++;
343  if (buffer->mWritePointer > buffer->tail())
344  buffer->mWritePointer = buffer->head();
345 
346  // sneak a zero into the buffer so that delayInSamples between 0 and 1 will work
347  *buffer->mWritePointer = 0;
348 
349  // store the value of the integer part of delayInSamples
350  TTSampleValue delaySample0 = *(buffer->mReadPointer);
351 
352  // store the value from 1 sample *before* mReadPointer for interpolation
353  TTSampleValuePtr delaySample1Ptr = buffer->mReadPointer - 1;
354  delaySample1Ptr = buffer->wrapPointer(delaySample1Ptr);
355  TTSampleValue delaySample1 = *(delaySample1Ptr);
356 
357  // store the value from 2 samples *before* mReadPointer for interpolation
358  TTSampleValuePtr delaySample2Ptr = buffer->mReadPointer - 2;
359  delaySample2Ptr = buffer->wrapPointer(delaySample2Ptr);
360  TTSampleValue delaySample2 = *(delaySample2Ptr);
361 
362  // store the value from 1 sample *after* mReadPointer for interpolation
363  TTSampleValuePtr delaySampleMinus1Ptr = buffer->mReadPointer + 1;
364  delaySampleMinus1Ptr = buffer->wrapPointer(delaySampleMinus1Ptr);
365  TTSampleValue delaySampleMinus1 = *(delaySampleMinus1Ptr);
366 
367  // now you are ready to interpolate
368  y = TTInterpolateCubic(delaySampleMinus1, delaySample0, delaySample1, delaySample2, mFractionalDelay);
369 
370  // then move the play head
371  buffer->mReadPointer++;
372  if (buffer->mReadPointer > buffer->tail())
373  buffer->mReadPointer = buffer->head();
374 
375  return kTTErrNone;
376 }
377 
378 TTErr TTDelay::calculateCubicInterpolation(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
379 {
380  TTDelayBufferPtr buffer = &mBuffers[channel];
381  return calculateCubicInterpolation(x, y, buffer);
382 }
383 
384 TTErr TTDelay::processAudioCubicInterpolation(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
385 {
386  TTDELAY_WRAP_CALCULATE_METHOD(calculateCubicInterpolation);
387 }
388 
389 
390 
391 
392 
393 
394 
395 // Process with a delay time set by a signal
396 //TTErr TTDelay::processAudioNoInterpolationWithDelaySignal(TTAudioSignal& in, TTAudioSignal& delayIn, TTAudioSignal& out, TTAudioSignal&)
397 //{
398  /* temp_vs = in->vectorsize;
399 
400  // CALCULATE THE DELAY TIME
401  delay_ms = *in2->vector; // Because this is the Lo-Fi loop, just do this at the vector...
402  fdelay_samples = delay_ms * m_sr;
403  delay_samples = (tt_attribute_value_discrete)fdelay_samples;
404  fractional_delay = fdelay_samples - delay_samples;
405 
406  while (temp_vs--) {
407  *in_ptr++ = *in->vector++; // Store Input
408  *out->vector++ = *out_ptr++; // Find Output
409 
410  if (in_ptr > end_ptr) // Buffer Managment...
411  in_ptr = buffer;
412  if (out_ptr > end_ptr)
413  out_ptr = buffer;
414  }
415  in->reset(); in2->reset(); out->reset();
416  */
417 // return kTTErrNone;
418 //}
419 
420 
421 //TTErr TTDelay::processAudioLinearInterpolationWithDelaySignal(TTAudioSignal& in, TTAudioSignal& delayIn, TTAudioSignal& out, TTAudioSignal&)
422 //{
423 /*
424  tt_sample_value temp;
425  tt_sample_value *next;
426 
427  if (buffer == NULL)
428  return;
429 
430  temp_vs = in1->vectorsize;
431  while (temp_vs--) {
432  *in_ptr = *in1->vector++; // Store the audio input @ the record head
433  delay_ms = *in2->vector++; // Store the delay time input
434  // delay_ms = clip(*in2->vector++, 0.f, delay_ms_max); // Store the delay time input
435 
436  // CALCULATE THE DELAY TIME
437  // delay_samples = clip(long(delay_ms * m_sr), long(0), delay_samples_max);
438  // delay_samples = delay_ms * m_sr;
439  fdelay_samples = delay_ms * m_sr;
440  delay_samples = (tt_attribute_value_discrete)fdelay_samples;
441  fractional_delay = fdelay_samples - delay_samples;
442 
443  // end_ptr = buffer + delay_samples; // set pointer to the end of the buffer
444 
445  // MOVE THE RECORD HEAD
446  in_ptr++;
447  if (in_ptr > end_ptr)
448  in_ptr = buffer;
449  // *in_ptr = *in1->vector++; // Store the audio input @ the record head
450 
451  // MOVE THE PLAY HEAD
452  out_ptr = in_ptr - delay_samples;
453  if (out_ptr > end_ptr)
454  out_ptr = buffer + (out_ptr - end_ptr);
455  else if (out_ptr < buffer)
456  out_ptr = end_ptr + (out_ptr - buffer) + 1;
457 
458  // STORE THE VALUE OF THE NEXT SAMPLE IN THE BUFFER FOR INTERPOLATION
459  next = out_ptr + 1;
460  if (next > end_ptr)
461  next = buffer + (next - end_ptr);
462  else if (next < buffer)
463  next = end_ptr + (next - buffer) + 1;
464  temp = *next;
465 
466  // Interpolate between the play head value (from above) and the next value in the buffer
467  *out->vector++ = (temp * (1.0 - fractional_delay)) + (*out_ptr * fractional_delay);
468  }
469  in1->reset(); in2->reset(); out->reset();
470  //log_post("delay_signal_ms: %f, samples: %f (%i)", delay_ms, fdelay_samples, delay_samples);
471  */
472 // return kTTErrNone;
473 //}
474 
475 
476 //TTErr TTDelay::processAudioCubicInterpolationWithDelaySignal(TTAudioSignal& in, TTAudioSignal& delayIn, TTAudioSignal& out, TTAudioSignal&)
477 //{
478 // return kTTErrNone;
479 //}
480 
481 
482 
TTErr updateMaxNumChannels(const TTValue &oldMaxNumChannels, TTValue &)
This method gets called when the inherited maxNumChannels attribute is changed.
Definition: TTDelay.cpp:78
T TTInterpolateLinear(const T &x0, const T &x1, const double &delta)
Isolate the fractional part from a double.
Definition: TTInterpolate.h:47
TTErr setDelay(const TTValue &newValue)
Attribute Accessor.
Definition: TTDelay.cpp:106
TTErr setDelayMaxInSamples(const TTValue &newValue)
Attribute Accessor.
Definition: TTDelay.cpp:140
An inappropriate value was specified for an attribute or variable.
Definition: TTBase.h:349
TTErr setDelayMax(const TTValue &newValue)
Attribute Accessor.
Definition: TTDelay.cpp:131
std::uint64_t TTUInt64
64 bit unsigned integer
Definition: TTBase.h:180
TTErr updateSampleRate(const TTValue &oldSampleRate, TTValue &)
Receives notifications when there are changes to the inherited sr attribute.
Definition: TTDelay.cpp:85
TTChannelCount mMaxNumChannels
This is the maximum number of channels that can be guaranteed to work.
TTSampleValuePtr mWritePointer
"record" pointer for buffer
Definition: TTDelayBuffer.h:32
#define setProcessMethod(methodName)
A convenience macro to be used by subclasses for setting the process method.
TTFloat64 mDelayInSamples
mIntegralDelay + mFractionalDelay
Definition: TTDelay.h:28
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
64-bit floating point
Definition: TTBase.h:272
Basic audio delay unit with 4 interpolation options.
TTSampleValuePtr mReadPointer
"playback" pointer
Definition: TTDelayBuffer.h:33
TTUInt64 mIntegralDelay
mDelayInSamples - mFractionalDelay. Used to set the TTDelayBuffer->mReadPointer.
Definition: TTDelay.h:33
TTErr setInterpolation(const TTValue &newValue)
Attribute Accessor.
Definition: TTDelay.cpp:149
64-bit signed integer, ragne is −9,223,372,036,854,775,808 through 9,223,372,036,854,775,807
Definition: TTBase.h:279
A simple container for an array of TTAudioSignal pointers.
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
T TTInterpolateCubic(const T &x0, const T &x1, const T &x2, const T &x3, const double &aDelta)
Cubic interpolation.
long TTPtrSizedInt
An integer that is the same size as a pointer.
Definition: TTBase.h:240
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
TTErr clear()
Zero out the delay's buffer.
Definition: TTDelay.cpp:92
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
TTFloat64 srMill
1/1000 of the current sample rate (samples per millisecond)
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
#define addMessage(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:19
TTFloat64 srInv
1.0 over the current sample rate (inverse)
No Error.
Definition: TTBase.h:343
TTErr setDelayInSamples(const TTValue &newValue)
Attribute Accessor.
Definition: TTDelay.cpp:118
T TTInterpolateCosine(const T &x, const T &y, const double &a)
Cosine interpolation.
Definition: TTInterpolate.h:65
void resize(size_type n)
Change the number of elements.
TTFloat64 mFractionalDelay
mDelayInSamples - mFractionalDelay. Used in interpolated dsp loops, to approximate the distance betwe...
Definition: TTDelay.h:32
TTFloat64 TTSampleValue
A value representing a single audio sample.
Definition: TTBase.h:230
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTDelayBuffer is a container object that holds some audio in a chunk of memory, with accessors for us...
Definition: TTDelayBuffer.h:27
#define addUpdates(updateName)
An 'update' is a message sent to a subclass instance from its parent class.
Definition: TTMessage.h:44