Jamoma API  0.6.0.a19
Core/DSP/extensions/Crossfade/Crossfade.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup dspCrossfadeLib
4  *
5  * @brief #TTCrossfade - signal cross fading
6  *
7  * @details
8  *
9  * @authors Tim Place, Trond Lossius
10  *
11  * @copyright Copyright © 2008, Tim Place @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 #include "TTDSP.h"
17 
18 #define thisTTClass TTCrossfade
19 #define thisTTClassName "crossfade"
20 #define thisTTClassDescription "Multichannel crossfader"
21 #define thisTTClassTags "audio, processor, mixing"
22 
23 
24 static bool zeroed = false;
25 static TTSampleValue zeroVector1[2048]; //TODO: make this dynamically sized
26 static TTSampleValue zeroVector2[2048]; //TODO: make this dynamically sized
27 static TTSampleValue zeroVector3[2048]; //TODO: make this dynamically sized
28 
29 
30 /** Crossfade between two input signals.
31 
32  @details In fact, this processor can work on a number of channels, provided that the number of input
33  channels is twice the number of output channels. In this case the first N/2 input channels are
34  considered as the A source and the last N/2 input channels are considered the B source.
35  */
38 
39  TTFloat64 mPosition; ///< Use a range of 0.0 to 1.0 to specify a ratio of the B source to the A source.
40  TTSymbol mShape; ///< The shape attribute is set with a TTSymbol that is either "equalPower" (the default) or "linear"
41  TTSymbol mMode; ///< The mode attribute is set with a TTSymbol that is either "lookup" (the default) or "calculate"
42 
43  /** Utility used by the setters for setting up the process routine. */
44  TTErr setProcessPointers();
45 
46  /** The process method used when the shape attribute is set to "linear"
47  * This method will return an error if the input and output channels are not matched properly. */
48  TTErr processLinear(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs);
49 
50  /** The process method used when the shape attribute is set to "equalPower" and the mode is set to "lookup"
51  * This method will return an error if the input and output channels are not matched properly. */
52  TTErr processEqualPowerLookup(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs);
53  TTErr processEqualPowerCalc(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs); // calculate
54 
55  /** The process method used when the shape attribute is set to "squareRoot""
56  * This method will return an error if the input and output channels are not matched properly. */
57  TTErr processSquareRootCalc(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs);
58  TTErr processSquareRootLookup(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs);
59 
60  /** Setter for the shape attribute. */
61  TTErr setShape(const TTValue& value);
62 
63  /** Setter for the mode attribute. */
64  TTErr setMode(const TTValue& value);
65 };
66 
67 
68 TT_AUDIO_CONSTRUCTOR_EXPORT(Crossfade)
69 {
70  addAttribute( Position, kTypeFloat64);
71  addAttributeProperty( Position, range, TTValue(0.0, 1.0));
72  addAttributeProperty( Position, rangeChecking, TT("clip"));
73  addMessageProperty( Position, description, TT("Sets the crossfader position."));
74 
75 
77  addMessageProperty( Shape, description, TT("Sets the crossfade function."));
79  addMessageProperty( Mode, description, TT("Wether the crossfader is based on a 512-point lookup table or on a real-time weight computation."));
80 
81  if (!zeroed) {
82  memset(zeroVector1, 0, sizeof(TTSampleValue) * 2048);
83  memset(zeroVector2, 0, sizeof(TTSampleValue) * 2048);
84  memset(zeroVector3, 0, sizeof(TTSampleValue) * 2048);
85  }
86 
87  // Set Defaults (the attribute setters will set the process method for us)...
88  setAttributeValue(TT("position"), 0.5);
89  setAttributeValue(TT("shape"), TT("equalPower"));
90  setAttributeValue(TT("mode"), TT("lookup"));
91 }
92 
93 
94 TTCrossfade::~TTCrossfade()
95 {;}
96 
97 
98 TTErr TTCrossfade::setShape(const TTValue& newValue)
99 {
100  mShape = newValue;
101  return setProcessPointers();
102 }
103 
104 
105 TTErr TTCrossfade::setMode(const TTValue& newValue)
106 {
107  mMode = newValue;
108  return setProcessPointers();
109 }
110 
111 
112 TTErr TTCrossfade::setProcessPointers()
113 {
114  TTErr err = kTTErrNone;
115 
116  if (mShape == TT("equalPower") && mMode == TT("lookup")) {
117  err = setProcess((TTProcessMethod)&TTCrossfade::processEqualPowerLookup);
118  }
119  else if (mShape == TT("equalPower") && mMode == TT("calculate")) {
120  err = setProcess((TTProcessMethod)&TTCrossfade::processEqualPowerCalc);
121  }
122  else if (mShape == TT("squareRoot") && mMode == TT("calculate")) {
123  err = setProcess((TTProcessMethod)&TTCrossfade::processSquareRootCalc);
124  }
125  else if (mShape == TT("squareRoot") && mMode == TT("lookup")) {
126  err = setProcess((TTProcessMethod)&TTCrossfade::processSquareRootLookup);
127  }
128  else {
129  err = setProcess((TTProcessMethod)&TTCrossfade::processLinear);
130  }
131  return err;
132 }
133 
134 
135 #if 0
136 #pragma mark -
137 #pragma mark Interleaved Process Methods
138 #endif
139 
140 TTErr TTCrossfade::processLinear(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
141 {
142  TTAudioSignal& in = inputs->getSignal(0);
143  TTAudioSignal& out = outputs->getSignal(0);
144  TTUInt16 vs;
145  TTSampleValue *inSampleA,
146  *inSampleB,
147  *outSample;
148  TTChannelCount numchannels = out.getNumChannelsAsInt();
149  TTChannelCount channel;
150 
151  if (inputs->numAudioSignals > 1) {
152  TTAudioSignal& in2 = inputs->getSignal(1);
153 
154  numchannels = TTAudioSignal::getMaxChannelCount(in, in2, out);;
155  for (channel=0; channel<numchannels; channel++) {
156  // inSampleA = in1.mSampleVectors[channel];
157  // inSampleB = in2.mSampleVectors[channel];
158  // outSample = out.mSampleVectors[channel];
159  if (channel < in.getNumChannelsAsInt())
160  inSampleA = in.mSampleVectors[channel];
161  else
162  inSampleA = zeroVector1;
163 
164  if (channel < in2.getNumChannelsAsInt())
165  inSampleB = in2.mSampleVectors[channel];
166  else
167  inSampleB = zeroVector2;
168 
169  if (channel < out.getNumChannelsAsInt())
170  outSample = out.mSampleVectors[channel];
171  else
172  outSample = zeroVector3;
173 
174  vs = in.getVectorSizeAsInt();
175 
176  while (vs--)
177  *outSample++ = (*inSampleB++ * mPosition) + (*inSampleA++ * (1.0 - mPosition));
178  }
179 
180  }
181  else {
182  if (in.getNumChannelsAsInt() != out.getNumChannelsAsInt()*2)
183  return kTTErrBadChannelConfig;
184 
185  for (channel=0; channel<numchannels; channel++) {
186  inSampleA = in.mSampleVectors[channel];
187  inSampleB = in.mSampleVectors[numchannels+channel];
188  outSample = out.mSampleVectors[channel];
189  vs = in.getVectorSizeAsInt();
190 
191  while (vs--)
192  *outSample++ = (*inSampleB++ * mPosition) + (*inSampleA++ * (1.0 - mPosition));
193  }
194  }
195  return kTTErrNone;
196 }
197 
198 
199 TTErr TTCrossfade::processEqualPowerLookup(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
200 {
201  TTAudioSignal& in = inputs->getSignal(0);
202  TTAudioSignal& out = outputs->getSignal(0);
203  TTUInt16 vs;
204  TTSampleValue *inSampleA,
205  *inSampleB,
206  *outSample;
207  TTChannelCount numchannels = out.getNumChannelsAsInt();
208  TTChannelCount channel;
209  int index;
210 
211  if (inputs->numAudioSignals > 1) {
212  TTAudioSignal& in2 = inputs->getSignal(1);
213 
214  numchannels = TTAudioSignal::getMaxChannelCount(in, in2, out);;
215  for (channel=0; channel<numchannels; channel++) {
216  if (channel < in.getNumChannelsAsInt())
217  inSampleA = in.mSampleVectors[channel];
218  else
219  inSampleA = zeroVector1;
220 
221  if (channel < in2.getNumChannelsAsInt())
222  inSampleB = in2.mSampleVectors[channel];
223  else
224  inSampleB = zeroVector2;
225 
226  if (channel < out.getNumChannelsAsInt())
227  outSample = out.mSampleVectors[channel];
228  else
229  outSample = zeroVector3;
230 
231  vs = in.getVectorSizeAsInt();
232 
233  while (vs--) {
234  index = (int)(mPosition * 511.0);
235  *outSample++ = (*inSampleB++ * kTTLookupEqualPower[511 - index]) + (*inSampleA++ * kTTLookupEqualPower[index]);
236  }
237  }
238  return kTTErrNone;
239  }
240  else {
241  if (in.getNumChannelsAsInt() != out.getNumChannelsAsInt()*2)
242  return kTTErrBadChannelConfig;
243 
244  for (channel=0; channel<numchannels; channel++) {
245  inSampleA = in.mSampleVectors[channel];
246  inSampleB = in.mSampleVectors[numchannels+channel];
247  outSample = out.mSampleVectors[channel];
248  vs = in.getVectorSizeAsInt();
249 
250  while (vs--) {
251  index = (int)(mPosition * 511.0);
252  *outSample++ = (*inSampleB++ * kTTLookupEqualPower[511 - index]) + (*inSampleA++ * kTTLookupEqualPower[index]);
253  }
254  }
255  }
256  return kTTErrNone;
257 }
258 
259 TTErr TTCrossfade::processSquareRootLookup(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
260 {
261  TTAudioSignal& in = inputs->getSignal(0);
262  TTAudioSignal& out = outputs->getSignal(0);
263  TTUInt16 vs;
264  TTSampleValue *inSampleA,
265  *inSampleB,
266  *outSample;
267  TTChannelCount numchannels = out.getNumChannelsAsInt();
268  TTChannelCount channel;
269  int index;
270 
271  if (inputs->numAudioSignals > 1) {
272  TTAudioSignal& in2 = inputs->getSignal(1);
273 
274  numchannels = TTAudioSignal::getMaxChannelCount(in, in2, out);;
275  for (channel=0; channel<numchannels; channel++) {
276  if (channel < in.getNumChannelsAsInt())
277  inSampleA = in.mSampleVectors[channel];
278  else
279  inSampleA = zeroVector1;
280 
281  if (channel < in2.getNumChannelsAsInt())
282  inSampleB = in2.mSampleVectors[channel];
283  else
284  inSampleB = zeroVector2;
285 
286  if (channel < out.getNumChannelsAsInt())
287  outSample = out.mSampleVectors[channel];
288  else
289  outSample = zeroVector3;
290 
291  vs = in.getVectorSizeAsInt();
292 
293  while (vs--) {
294  index = (int)(mPosition * 511.0);
295  *outSample++ = (*inSampleB++ * kTTLookupSquareRoot[511 - index]) + (*inSampleA++ * kTTLookupSquareRoot[index]);
296  }
297  }
298  return kTTErrNone;
299  }
300  else {
301  if (in.getNumChannelsAsInt() != out.getNumChannelsAsInt()*2)
302  return kTTErrBadChannelConfig;
303 
304  for (channel=0; channel<numchannels; channel++) {
305  inSampleA = in.mSampleVectors[channel];
306  inSampleB = in.mSampleVectors[numchannels+channel];
307  outSample = out.mSampleVectors[channel];
308  vs = in.getVectorSizeAsInt();
309 
310  while (vs--) {
311  index = (int)(mPosition * 511.0);
312  *outSample++ = (*inSampleB++ * kTTLookupSquareRoot[511 - index]) + (*inSampleA++ * kTTLookupSquareRoot[index]);
313  }
314  }
315  }
316  return kTTErrNone;
317 }
318 
319 // TODO: Nils says that we should experiment here with (sin*sin) here so that we can sum to 1.0 in the center???
320 // It's worth experimenting with it....
321 
322 TTErr TTCrossfade::processEqualPowerCalc(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
323 {
324  TTAudioSignal& in = inputs->getSignal(0);
325  TTAudioSignal& out = outputs->getSignal(0);
326  TTUInt16 vs;
327  TTSampleValue *inSampleA,
328  *inSampleB,
329  *outSample;
330  TTChannelCount numchannels = out.getNumChannelsAsInt();
331  TTChannelCount channel;
332  TTFloat64 radPosition;
333 
334  if (inputs->numAudioSignals > 1) {
335  TTAudioSignal& in2 = inputs->getSignal(1);
336 
337  numchannels = TTAudioSignal::getMinChannelCount(in, in2, out);
338  for (channel=0; channel<numchannels; channel++) {
339  inSampleA = in.mSampleVectors[channel];
340  inSampleB = in2.mSampleVectors[channel];
341  outSample = out.mSampleVectors[channel];
342  vs = in.getVectorSizeAsInt();
343 
344  while (vs--)
345  {
346  radPosition = mPosition * kTTHalfPi;
347  *outSample++ = (*inSampleB++ * (sin(radPosition))) + (*inSampleA++ * (cos(radPosition)));
348  }
349  }
350 
351  }
352  else {
353  if (in.getNumChannelsAsInt() != out.getNumChannelsAsInt()*2)
354  return kTTErrBadChannelConfig;
355 
356  for (channel=0; channel<numchannels; channel++) {
357  inSampleA = in.mSampleVectors[channel];
358  inSampleB = in.mSampleVectors[numchannels+channel];
359  outSample = out.mSampleVectors[channel];
360  vs = in.getVectorSizeAsInt();
361 
362  while (vs--)
363  {
364  radPosition = mPosition * kTTHalfPi;
365  *outSample++ = (*inSampleB++ * (sin(radPosition))) + (*inSampleA++ * (cos(radPosition)));
366  }
367  }
368  }
369  return kTTErrNone;
370 }
371 
372 TTErr TTCrossfade::processSquareRootCalc(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
373 {
374  TTAudioSignal& in = inputs->getSignal(0);
375  TTAudioSignal& out = outputs->getSignal(0);
376  TTUInt16 vs;
377  TTSampleValue *inSampleA,
378  *inSampleB,
379  *outSample;
380  TTChannelCount numchannels = out.getNumChannelsAsInt();
381  TTChannelCount channel;
382 
383  if (inputs->numAudioSignals > 1) {
384  TTAudioSignal& in2 = inputs->getSignal(1);
385 
386  numchannels = TTAudioSignal::getMinChannelCount(in, in2, out);
387  for (channel=0; channel<numchannels; channel++) {
388  inSampleA = in.mSampleVectors[channel];
389  inSampleB = in2.mSampleVectors[channel];
390  outSample = out.mSampleVectors[channel];
391  vs = in.getVectorSizeAsInt();
392 
393  while (vs--)
394  *outSample++ = (*inSampleB++ * (sqrt(mPosition))) + (*inSampleA++ * (sqrt(1 - mPosition)));
395  }
396 
397  }
398  else {
399  if (in.getNumChannelsAsInt() != out.getNumChannelsAsInt()*2)
400  return kTTErrBadChannelConfig;
401 
402  for (channel=0; channel<numchannels; channel++) {
403  inSampleA = in.mSampleVectors[channel];
404  inSampleB = in.mSampleVectors[numchannels+channel];
405  outSample = out.mSampleVectors[channel];
406  vs = in.getVectorSizeAsInt();
407 
408  while (vs--)
409  *outSample++ = (*inSampleB++ * (sqrt(mPosition))) + (*inSampleA++ * (sqrt(1 - mPosition)));
410  }
411  }
412  return kTTErrNone;
413 }
414 
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
#define addAttribute(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom getter...
Definition: TTAttribute.h:29
TTAudioObjectBase is the base class for all audio generating and processing objects in Jamoma DSP...
Crossfade between two input signals.
An invalid number of audio channels for a given context was encountered.
Definition: TTBase.h:353
Jamoma DSP Library.
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
#define TTCLASS_SETUP(className)
TODO Doxygen: need more comments here.
Definition: TTFoundation.h:54
64-bit floating point
Definition: TTBase.h:272
#define addAttributeProperty(attributeName, propertyName, initialValue)
A convenience macro to be used for registering properties of attributes.
Definition: TTAttribute.h:68
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
TTErr(TTAudioObjectBase::* TTProcessMethod)(TTAudioSignalArrayPtr in, TTAudioSignalArrayPtr out)
A type that can be used to store a pointer to a process method (which calculates a vector of samples)...
static TTChannelCount getMinChannelCount(const TTAudioSignal &signal1, const TTAudioSignal &signal2)
Use this class method to determine the least number of channels the two signals have in common...
The TTAudioSignal class represents N vectors of audio samples for M channels.
Definition: TTAudioSignal.h:57
TTSampleValue ** mSampleVectors
An array of pointers to the first sample in each vector. Declared Public for fast access...
Definition: TTAudioSignal.h:74
TTErr setProcess(TTProcessMethod processMethod)
Set the audio processing routine to point to a method that is defined as an arg to this function...
TTFOUNDATION_EXPORT const TTFloat32 kTTLookupEqualPower[]
Equal Power lookup table, 512 elements.
Definition: TTBase.cpp:38
TTUInt16 TTChannelCount
Data type used when counting the number of channels in multi-channel audio signals and processes...
Definition: TTAudioSignal.h:31
A simple container for an array of TTAudioSignal pointers.
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
TTFOUNDATION_EXPORT const TTFloat64 kTTHalfPi
Pre-calculated value of pi/2.
Definition: TTBase.cpp:28
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
No Error.
Definition: TTBase.h:343
TTFOUNDATION_EXPORT const TTFloat32 kTTLookupSquareRoot[]
Square Root lookup table, 512 elements.
Definition: TTBase.cpp:205
TTFloat64 TTSampleValue
A value representing a single audio sample.
Definition: TTBase.h:230
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTChannelCount numAudioSignals
The number of audio signal pointers which are actually valid.
#define addMessageProperty(messageName, propertyName, initialValue)
A convenience macro to be used for registering properties of messages.
Definition: TTMessage.h:37