CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

Mixer.cpp
Go to the documentation of this file.
1 //
2 // Mixer.cpp -- The multi-channel panner and mixer classes.
3 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 //
5
6 #include "Mixer.h"
7 #include <stdlib.h>
8 #include <math.h>
9 #include <stdio.h>
10
11 using namespace csl;
12
13 //// Mixer class implementation
14
15 // Constructors
16
17 Mixer::Mixer() : UnitGenerator(), Scalable(1, 0) {
18 mNumChannels = 1;
19 allocateOpBuffer(1); // assume mono input
20 }
21
22 Mixer::Mixer(unsigned chans) : UnitGenerator(), Scalable(1, 0) {
23 mNumChannels = chans;
24 allocateOpBuffer(chans);
25 }
26
27 Mixer::Mixer(UnitGenerator & aa) : UnitGenerator(), Scalable(aa, 0) {
28 mNumChannels = 1;
29 allocateOpBuffer(1);
30 }
31
32 Mixer::Mixer(unsigned chans, UnitGenerator & aa) : UnitGenerator(), Scalable(aa, 0) {
33 mNumChannels = chans;
34 allocateOpBuffer(chans);
35 }
36
37 Mixer::~Mixer() {
38 mOpBuffer.freeBuffers();
39 }
40
41 // add a new input to the list
42
43 void Mixer::addInput(UnitGenerator & inp) {
44 mSources.push_back(&inp);
45 mScaleValues.push_back(1.0f);
46 inp.addOutput((UnitGenerator *) this); // be sure to add me as an output of the other guy
47 }
48
49 void Mixer::addInput(UnitGenerator * inp) {
50 mSources.push_back(inp);
51 mScaleValues.push_back(1.0f);
52 inp->addOutput((UnitGenerator *) this); // be sure to add me as an output of the other guy
53 }
54
55 void Mixer::addInput(UnitGenerator & inp, float ampl) {
56 mSources.push_back(&inp);
57 mScaleValues.push_back(ampl);
58 inp.addOutput((UnitGenerator *) this); // be sure to add me as an output of the other guy
59 }
60
61 void Mixer::addInput(UnitGenerator * inp, float ampl) {
62 mSources.push_back(inp);
63 mScaleValues.push_back(ampl);
64 inp->addOutput((UnitGenerator *) this); // be sure to add me as an output of the other guy
65 }
66
67 // delete from the list
68
69 void Mixer::removeInput(UnitGenerator & inp) {
70 // logMsg(" Mixer: rem %x", &inp);
71 for (unsigned i = 0; i < mSources.size(); i++) {
72 if (mSources[i] == &inp) {
73 mSources.erase(mSources.begin() + i);
74 mScaleValues.erase(mScaleValues.begin() + i);
75 return;
76 }
77 }
78 throw RunTimeError("Error removing mixer input: not found.");
79 }
80
81 void Mixer::removeInput(UnitGenerator * inp) {
82 // logMsg(" Mixer: rem %x", inp);
83 for (unsigned i = 0; i < mSources.size(); i++) {
84 if (mSources[i] == inp) {
85 mSources.erase(mSources.begin() + i);
86 mScaleValues.erase(mScaleValues.begin() + i);
87 return;
88 }
89 }
90 throw RunTimeError("Error removing mixer input: not found.");
91 }
92
93 ///< number of active inputs
94
95 unsigned Mixer::getNumInputs(void) {
96 return mSources.size();
97 }
98
99
100 // change the scale value
101
102 void Mixer::scaleInput(UnitGenerator & inp, float val) {
103 for (unsigned i = 0; i < mSources.size(); i++) {
104 if (mSources[i] == & inp) {
105 mScaleValues[i] = val;
106 return;
107 }
108 }
109 logMsg("Mixer scaleInput -- input not found");
110 }
111
112 // free all the inputs
113
114 void Mixer::deleteInputs() {
115 for (unsigned i = 0; i < mSources.size(); i++) {
116 delete mSources[i];
117 mSources.erase(mSources.begin() + i);
118 mScaleValues.erase(mScaleValues.begin() + i);
119 }
120 }
121
122 // Allocate an input operation buffer
123
124 void Mixer::allocateOpBuffer(unsigned chans) {
125 mNumChannels = chans;
126 mOpBuffer.setSize(chans, CGestalt::blockSize());
127 mOpBuffer.allocateBuffers();
128 }
129
130 // Count active sources
131
132 unsigned Mixer::activeSources() {
133 unsigned answer = 0;
134 for (unsigned i = 0; i < mSources.size(); i++)
135 if ((mSources[i]) && (mSources[i]->isActive()))
136 answer++;
137 return answer;
138 }
139
140 //bool Mixer::isActive() {
141 // for (UGenVector::iterator pos = mSources.begin(); pos != mSources.end(); ++pos) {
142 // if ((*pos)->isActive())
143 // return true;
144 // }
145 // return false;
146 //}
147
148 // Here's the mixing part -- fill the buffer with the next num_frames values
149
150 void Mixer::nextBuffer(Buffer & outputBuffer) throw (CException) {
151 SampleBuffer out1, out2, opp;
152 unsigned numIns = mSources.size();
153 unsigned numFrames = outputBuffer.mNumFrames;
154 unsigned j, k, ich;
155 // unsigned ins = mSources.size();
156
157 // logMsg("Mixer %x - nxt_b %d - %d in - S %d", this, numFrames, numIns, outputBuffer.mSequence);
158 mOpBuffer.copyHeaderFrom(outputBuffer);
159 outputBuffer.zeroBuffers(); // clear output buffer
160
161 for (int i = 0; i < numIns; i++) {
162 UnitGenerator * input = mSources[i];
163 // printf("\t\tmix in %d = %x ", i, input); fflush(stdout);
164 if (input->isActive()) { // if active input, get a buffer and sum it to output
165 ich = input->numChannels();
166 // printf("\tnext_b %d, inp %x active, %d ch\n", numFrames, input, ich);
167 float scal = mScaleValues[i];
168 mOpBuffer.mNumChannels = ich;
169 mOpBuffer.mSequence = outputBuffer.mSequence;
170 mOpBuffer.zeroBuffers(); // clear operation buffer
171
172 input->nextBuffer(mOpBuffer); // get the input's nextBuffer
173
174 if (ich == mNumChannels) { // if input and mixer have same # of channels
175 for (j = 0; j < ich; j++) { // j loops through channels
176 out1 = outputBuffer.buffer(j);
177 opp = mOpBuffer.buffer(j);
178 // if ((out1 == 0) || (opp == 0))
179 // return;
180 if (scal == 1.0f) {
181 for (k = 0; k < numFrames; k++) // k loops through sample buffers
182 *out1++ += *opp++; // summing samples into the output buffer
183 } else {
184 for (k = 0; k < numFrames; k++) // k loops through sample buffers
185 *out1++ += (*opp++ * scal); // summing samples into the output buffer
186 }
187 }
188 } // special case: mix mono to stereo
189 else if ((ich == 1) && (mNumChannels == 2)) {
190 out1 = outputBuffer.buffer(0);
191 out2 = outputBuffer.buffer(1);
192 opp = mOpBuffer.buffer(0);
193 if (scal == 1.0f)
194 for (k = 0; k < numFrames; k++) { // k loops through sample buffers
195 *out1++ += *opp; // sum mono-to-stereo
196 *out2++ += *opp++;
197 }
198 else
199 for (k = 0; k < numFrames; k++) { // k loops through sample buffers
200 *out1++ += *opp * scal; // sum mono-to-stereo
201 *out2++ += *opp++ * scal;
202 }
203 } else { // ??? -- channel # mismatch
204 logMsg(kLogError, "Error in mix: in = %d ch, out = %d ch\n", ich, mNumChannels);
205 }
206 } // end of isActive()
207 } // end of input loop, process scale/offset
208 #ifdef DO_MIX_AS_SCALABLE // not defined at present
209 DECLARE_SCALABLE_CONTROLS;
210 LOAD_SCALABLE_CONTROLS;
211 sample samp; // loop through output buffer per-channel applying scale/offset
212 for (j = 0; j < mNumChannels; j++) {
213 out1 = outputBuffer.buffer(j];
214 for (k = 0; k < numFrames; k++) {
215 samp = *out1;
216 samp = (samp * scaleValue) + offsetValue;
217 *out1++ = samp;
218 UPDATE_SCALABLE_CONTROLS;
219 }
220 scalePort->resetPtr();
221 offsetPort->resetPtr();
222 scaleValue = scalePort->nextValue();
223 offsetValue = offsetPort->nextValue();
224 }
225 #endif
226 mOpBuffer.mAreBuffersZero = false;
227
228 }
229
230 // print info about this instance
231
232 void Mixer::dump() {
233 fprintf(stderr, "Mixer -- %d inputs: ", (int) mSources.size());
234 for (unsigned i = 0; i < mSources.size(); i++) {
235 mSources[i]->dump();
236 fprintf(stderr, "\n");
237 }
238 }
239
240 // Panner methods
241
242 Panner::Panner() : Effect(), Scalable() {
243 mNumChannels = 2;
244 };
245
246 Panner::Panner(UnitGenerator &input) : Effect(input), Scalable(1.0, 0.0) {
247 mNumChannels = 2;
248 this->addInput(CSL_POSITION, 0.0);
249 }
250
251 Panner::Panner(UnitGenerator &input, UnitGenerator &position) : Effect(input), Scalable(1.0, 0.0) {
252 mNumChannels = 2;
253 this->addInput(CSL_POSITION, position);
254 }
255
256 Panner::Panner(UnitGenerator &input, UnitGenerator &position, UnitGenerator & amp) : Effect(input), Scalable(amp, 0.0) {
257 mNumChannels = 2;
258 this->addInput(CSL_POSITION, position);
259 }
260
261 Panner::Panner(UnitGenerator &input, UnitGenerator &position, float amp) : Effect(input), Scalable(amp, 0.0) {
262 mNumChannels = 2;
263 this->addInput(CSL_POSITION, position);
264 }
265
266 Panner::Panner(UnitGenerator &input, float position) : Effect(input), Scalable(1.0, 0.0) {
267 mNumChannels = 2;
268 this->addInput(CSL_POSITION, position);
269 }
270
271 Panner::Panner(UnitGenerator &input, float position, float amp) : Effect(input), Scalable(amp, 0.0) {
272 mNumChannels = 2;
273 this->addInput(CSL_POSITION, position);
274 }
275
276 Panner::~Panner(void) {
277 }
278
279 void Panner::setPosition(UnitGenerator &pan) {
280 this->addInput(CSL_POSITION, pan);
281 }
282
283 void Panner::setPosition(float pan) {
284 this->addInput(CSL_POSITION, pan);
285 }
286
287 // To get my next buffer, get a buffer from the input, and then "process" it...
288
289 void Panner::nextBuffer(Buffer &outputBuffer) throw (CException) {
290 if (outputBuffer.mNumChannels != 2)
291 logMsg(kLogError, "Panner output ch = %d", outputBuffer.mNumChannels);
292 SampleBuffer out1 = outputBuffer.buffer(0);
293 SampleBuffer out2 = outputBuffer.buffer(1);
294 unsigned numFrames = outputBuffer.mNumFrames;
295
296 // logMsg(" Panner: checking (%d - %d %d)", mNumOutputs, mSequence, outputBuffer.mSequence);
297 if (checkFanOut(outputBuffer)) return; // check if we're doing fan-out
298
299 // logMsg(" Panner: nxt_b %d", numFrames);
300 DECLARE_SCALABLE_CONTROLS; // declare the scale/offset buffers and values
301 LOAD_SCALABLE_CONTROLS;
302
303 Effect::pullInput(numFrames); // get the input samples via Effect
304 SampleBuffer inpp = mInputPtr;
305
306 Port * posPort = mInputs[CSL_POSITION];
307 Controllable::pullInput(posPort, numFrames); // get the position UGen's data
308 float posValue = posPort->nextValue() * 0.5; // get and scale the first position value
309
310 for (unsigned i = 0; i < numFrames; i++) {
311 *out1++ = (*inpp) * (0.5 - posValue) * scaleValue; // L sample
312 *out2++ = (*inpp++) * (posValue + 0.5) * scaleValue; // R sample
313 UPDATE_SCALABLE_CONTROLS; // update the dynamic scale/offset
314 posValue = (posPort->nextValue()) * 0.5;
315 }
316 handleFanOut(outputBuffer); // process possible fan-out
317 }
318
319 // NtoMPanner -- Many variations on the constructor
320
321 NtoMPanner::NtoMPanner(UnitGenerator & i, float a, unsigned in_c, unsigned out_c) : Panner(i, a) {
322 this->addInput(CSL_POSITIONX, 0.0);
323 this->addInput(CSL_POSITIONY, 0.0);
324 mInCh = in_c;
325 mOutCh = out_c;
326 mSpread = CSL_PI / 2.0;
327 initSpeakers();
328 }
329
330 NtoMPanner::NtoMPanner(UnitGenerator & i, UnitGenerator & pX, UnitGenerator & pY, UnitGenerator & a,
331 unsigned in_c, unsigned out_c)
332 : Panner(i, pX, a) {
333 mInCh = in_c;
334 mOutCh = out_c;
335 this->addInput(CSL_POSITIONX, pX);
336 this->addInput(CSL_POSITIONY, pY);
337 mSpread = CSL_PI / 2.0;
338 initSpeakers();
339 }
340
341 NtoMPanner::NtoMPanner(UnitGenerator & i, UnitGenerator & pX, UnitGenerator & pY, UnitGenerator & a,
342 unsigned in_c, unsigned out_c, float spr)
343 : Panner(i, pX, a) {
344 this->addInput(CSL_POSITIONX, pX);
345 this->addInput(CSL_POSITIONY, pY);
346 mInCh = in_c;
347 mOutCh = out_c;
348 mSpread = spr;
349 initSpeakers();
350 }
351
352 NtoMPanner::NtoMPanner(UnitGenerator & i, UnitGenerator & pX, UnitGenerator & pY, float a,
353 unsigned in_c, unsigned out_c, float spr)
354 : Panner(i, pX, a) {
355 this->addInput(CSL_POSITIONX, pX);
356 this->addInput(CSL_POSITIONY, pY);
357 mInCh = in_c;
358 mOutCh = out_c;
359 mSpread = spr;
360 initSpeakers();
361 }
362
363 NtoMPanner::~NtoMPanner() { /* _op_buffer.freeBuffers(); */ }
364
365 void NtoMPanner::setX(float pan) {
366 this->addInput(CSL_POSITIONX, 0.0);
367 }
368
369 void NtoMPanner::setY(float pan) {
370 this->addInput(CSL_POSITIONY, 0.0);
371 }
372
373 // To get my next buffer, get a bufffer from the input, and then mix it to each of the outputs
374
375 void NtoMPanner::nextBuffer(Buffer &outputBuffer) throw (CException) {
376 unsigned numFrames = outputBuffer.mNumFrames;
377 float a_scale, dist, l_samp, r_samp, o_samp;
378 CPoint l_pos, r_pos;
379 #ifdef CSL_WINDOWS
380 float l_weights[MAX_OUTPUTS], r_weights[MAX_OUTPUTS];
381 #else
382 float l_weights[mOutCh], r_weights[mOutCh];
383 #endif
384 // Get an n-channel buffer from the input signal
385 DECLARE_SCALABLE_CONTROLS; // declare the scale/offset buffers and values
386 LOAD_SCALABLE_CONTROLS;
387
388 Effect::pullInput(numFrames); // get the input samples via Effect
389
390 Port * posXPort = mInputs[CSL_POSITIONX];
391 Controllable::pullInput(posXPort, numFrames); // get the position UGen's data
392 float posXValue = posXPort->nextValue(); // get and scale the first position value
393 Port * posYPort = mInputs[CSL_POSITIONY];
394 Controllable::pullInput(posYPort, numFrames); // get the position UGen's data
395 float posYValue = posYPort->nextValue(); // get and scale the first position value
396
397 l_pos.set(posXValue, posYValue); // set current source positions
398 r_pos.set(posXValue, posYValue);
399 r_pos.rotateBy(mSpread);
400
401 for (unsigned j = 0; j < mOutCh; j++) { // calculate the weights per-speaker
402 dist = mSpeakers[j]->distance(& l_pos); // distance between j'th speaker and source position
403 if (dist < 1.0) dist = 1.0; // limit to 1.0 (ft)
404 l_weights[j] = 1.0 / dist; // (dist * dist); // inverse-square law
405 if (mInCh > 1) {
406 dist = mSpeakers[j]->distance(& r_pos);
407 if (dist < 1.0) dist = 1.0;
408 r_weights[j] = 1.0 / dist; //(dist * dist);
409 }
410 }
411 // printf("L: %6.4f %6.4f %6.4f %6.4f\n", l_weights[0], l_weights[1], l_weights[2], l_weights[3]);
412 // printf("R: %6.4f %6.4f %6.4f %6.4f\n", r_weights[0], r_weights[1], r_weights[2], r_weights[3]);
413 // printf("L: %6.4f %6.4f\n", l_weights[0], l_weights[1]);
414 // printf("R: %6.4f %6.4f\n", r_weights[0], r_weights[1]);
415
416 // for (unsigned i = 0; i < numFrames; i++) {
417 // *out1++ = (*inpp) * (0.5 - posValue) * scaleValue; // L sample
418 // *out2++ = (*inpp++) * (posValue + 0.5) * scaleValue; // R sample
419 // UPDATE_SCALABLE_CONTROLS; // update the dynamic scale/offset
420 // posValue = (posPort->nextValue()) * 0.5;
421 // }
422
423 SampleBuffer inL = mInputPtr;
424 SampleBuffer inR = NULL;
425 if (mInCh > 1)
426 inR = mInputs[CSL_INPUT]->mBuffer->buffer(1);
427 // outputBuffer.zeroBuffers();
428 for (unsigned i = 0; i < numFrames; i++) { // now do the output loop -- per frame
429 l_samp = *inL++; // get input sample(s)
430 if (mInCh > 1)
431 r_samp = *inR++;
432 for (unsigned j = 0; j < mOutCh; j++) { // generate output per channel
433 o_samp = l_samp * a_scale * l_weights[j]; // scale input sample
434 if (mInCh > 1) // right channel if stereo
435 o_samp += r_samp * a_scale * r_weights[j];
436 outputBuffer.setBuffer(j, i, o_samp); // write output sample
437 }
438 }
439 }
440
441 // Speaker array init methods
442
443 void NtoMPanner::initSpeakers() {
444 switch(mOutCh) {
445 case 2:
446 init_stereo(10.0); break;
447 case 4:
448 init_quad(10.0); break;
449 case 5:
450 init_5point1(10.0); break;
451 case 6:
452 init_6ch(15.0, 20.0); break;
453 default:
454 printf("ERROR: unknown # of channels in NtoM_Panner: %d\n", mOutCh);
455 }
456 }
457
458 // Stereo -- assume 45 degree separation
459
460 void NtoMPanner::init_stereo(float dist) {
461 float coord = dist * 0.7071;
462 mSpeakers = (CPoint **) malloc(2 * sizeof(void *));
463 mSpeakers[0] = new CPoint((0.0f - coord), coord);
464 mSpeakers[1] = new CPoint(coord, coord);
465 }
466
467 // Square quad
468
469 void NtoMPanner::init_quad(float dist) {
470 float coord = dist * 0.7071;
471 mSpeakers = (CPoint **) malloc(4 * sizeof(void *));
472 mSpeakers[0] = new CPoint((0.0f - coord), coord);
473 mSpeakers[1] = new CPoint(coord, coord);
474 mSpeakers[2] = new CPoint(coord, (0.0f - coord));
475 mSpeakers[3] = new CPoint((0.0f - coord), (0.0f - coord));
476 }
477
478 // ITU standard 5.1-channel layout
479
480 void NtoMPanner::init_5point1(float dist) {
481 mSpeakers = (CPoint **) malloc(5 * sizeof(void *));
482 mSpeakers[0] = new CPoint(kPolar, dist, (float)(2.0 / 3.0 / CSL_PI));
483 mSpeakers[1] = new CPoint(kPolar, dist, (float)(1.0 / 2.0 / CSL_PI));
484 mSpeakers[2] = new CPoint(kPolar, dist, (float)(1.0 / 3.0 / CSL_PI));
485 mSpeakers[3] = new CPoint(kPolar, dist, (float)(17.0 / 9.0 / CSL_PI));
486 mSpeakers[4] = new CPoint(kPolar, dist, (float)(10.0 / 9.0 / CSL_PI));
487 }
488
489 // X-by-Y 6-channels along the sides (0 = LF, 2 = RF, 3 = RC, ...)
490
491 void NtoMPanner::init_6ch(float x, float y) {
492 mSpeakers = (CPoint **) malloc(6 * sizeof(void *));
493 mSpeakers[0] = new CPoint((0.0f - x), y);
494 mSpeakers[1] = new CPoint(x, y);
495 mSpeakers[2] = new CPoint(x, 0.0f);
496 mSpeakers[3] = new CPoint(x, (0.0f - y));
497 mSpeakers[4] = new CPoint((0.0f - x), (0.0f - y));
498 mSpeakers[5] = new CPoint((0.0f - x), 0.0f);
499 }
500
501 // StereoWidth implementation
502
503 StereoWidth::StereoWidth() : Effect(), mWidth(0.0f), mGain(1.0f), mPan(0.5f) { }
504
505 StereoWidth::~StereoWidth() { }
506
507 void StereoWidth::nextBuffer(Buffer & outputBuffer) throw (CException) {
508 Effect::pullInput(outputBuffer);
509
510 // we'll process in place in the output buffer
511 SampleBuffer outL = outputBuffer.buffer(0);
512 SampleBuffer outR = outputBuffer.buffer(1);
513
514 float gain = mGain;
515 // equal power panning -- of sorts
516 float panL = sqrt(2*(1.0 - mPan));
517 float panR = sqrt(2*(mPan));
518
519 float widthFactor = mWidth;
520 if ((fabs(1.0 - panL) < 0.0001) && (fabs(1.0 - panR) < 0.0001) && (fabs(widthFactor) < 0.0001) && (fabs(1.0 - gain) < 0.0001)) {
521 // nothing to do
522 return;
523 }
524 #ifdef DEBUG
525 static int count = 0;
526 if (count < 1) {
527 printf("Pan L/R %f/%f \tWidth Factor/Gain %f\n", panL, panR, widthFactor);
528 count = 100;
529 } else count--;
530 #endif
531 unsigned numFrames = outputBuffer.mNumFrames;
532 float newL, newR;
533 for (unsigned i = 0; i < numFrames; i++) {
534 newL = *outL + (*outR * widthFactor);
535 newR = *outR + (*outL * widthFactor);
536 *outL++ = newL * panL * gain;
537 *outR++ = newR * panR * gain;
538 }
539 return;
540 }