1 /*
2     mixer.cpp:
3 
4     Copyright (C) 2005 by Michael Gogins
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22 */
23 #include "OpcodeBase.hpp"
24 #include <map>
25 #include <vector>
26 
27 using namespace csound;
28 
29 // Define ENABLE_MIXER_IDEBUG to enable i-rate debug messages.
30 //#define ENABLE_MIXER_IDEBUG
31 
32 // Define ENABLE_MIXER_KDEBUG to enable -rate and a-rate debug messages.
33 //#define ENABLE_MIXER_KDEBUG
34 
35 /**
36  * The mixer busses are laid out:
37  * busses[csound][bus][channel][frame].
38  * std::map<CSOUND *, std::map<size_t, std::vector< std::vector<MYFLT> > > >
39  * *busses = 0;
40  *
41  * The mixer send matrix is laid out:
42  * matrix[csound][send][bus].
43  * std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT> > > *matrix = 0;
44  */
45 
46 /**
47  * Creates the buss if it does not already exist.
48  */
createBuss(CSOUND * csound,size_t buss)49 static void createBuss(CSOUND *csound, size_t buss) {
50 #ifdef ENABLE_MIXER_IDEBUG
51   csound->Message(csound, "createBuss: csound %p buss %d...\n", csound, buss);
52 #endif
53   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>>
54       *busses = 0;
55   csound::QueryGlobalPointer(csound, "busses", busses);
56   if ((*busses)[csound].find(buss) == (*busses)[csound].end()) {
57     size_t channels = csound->GetNchnls(csound);
58     size_t frames = csound->GetKsmps(csound);
59     (*busses)[csound][buss].resize(channels);
60     for (size_t channel = 0; channel < channels; channel++) {
61       (*busses)[csound][buss][channel].resize(frames);
62     }
63 #ifdef ENABLE_MIXER_IDEBUG
64     csound->Message(csound, "createBuss: created buss.\n");
65 #endif
66   } else {
67 #ifdef ENABLE_MIXER_IDEBUG
68     csound->Message(csound, "createBuss: buss already exists.\n");
69 #endif
70   }
71 }
72 
73 /**
74  * MixerSetLevel isend, ibuss, kgain
75  *
76  * Controls the gain of any signal route from a send to a bus
77  */
78 struct MixerSetLevel : public OpcodeBase<MixerSetLevel> {
79   // No outputs.
80   // Inputs.
81   MYFLT *isend;
82   MYFLT *ibuss;
83   MYFLT *kgain;
84   // State.
85   size_t send;
86   size_t buss;
87   std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>> *matrix;
initMixerSetLevel88   int init(CSOUND *csound) {
89 #ifdef ENABLE_MIXER_IDEBUG
90     warn(csound, "MixerSetLevel::init...\n");
91 #endif
92     csound::QueryGlobalPointer(csound, "matrix", matrix);
93     send = static_cast<size_t>(*isend);
94     buss = static_cast<size_t>(*ibuss);
95     createBuss(csound, buss);
96     (*matrix)[csound][send][buss] = *kgain;
97 #ifdef ENABLE_MIXER_IDEBUG
98     warn(csound, "MixerSetLevel::init: csound %p send %d buss %d gain %f\n",
99          csound, send, buss, (*matrix)[csound][send][buss]);
100 #endif
101     return OK;
102   }
kontrolMixerSetLevel103   int kontrol(CSOUND *csound) {
104     (*matrix)[csound][send][buss] = *kgain;
105 #ifdef ENABLE_MIXER_KDEBUG
106     warn(csound, "MixerSetLevel::kontrol: csound %p send %d buss "
107                  "%d gain %f\n",
108          csound, send, buss, (*matrix)[csound][send][buss]);
109 #endif
110     return OK;
111   }
112 };
113 
114 /**
115  * kgain MixerGetLevel isend, ibuss
116  *
117  * Returns the gain of any signal route from a send to a bus.
118  */
119 struct MixerGetLevel : public OpcodeBase<MixerGetLevel> {
120   //.
121   MYFLT *kgain;
122   // Inputs.
123   MYFLT *isend;
124   MYFLT *ibuss;
125   // State.
126   size_t send;
127   size_t buss;
128   std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>> *matrix;
initMixerGetLevel129   int init(CSOUND *csound) {
130 #ifdef ENABLE_MIXER_IDEBUG
131     warn(csound, "MixerGetLevel::init...\n");
132 #endif
133     csound::QueryGlobalPointer(csound, "matrix", matrix);
134     send = static_cast<size_t>(*isend);
135     buss = static_cast<size_t>(*ibuss);
136     createBuss(csound, buss);
137     return OK;
138   }
noteoffMixerGetLevel139   int noteoff(CSOUND *) { return OK; }
kontrolMixerGetLevel140   int kontrol(CSOUND *csound) {
141 #ifdef ENABLE_MIXER_KDEBUG
142     warn(csound, "MixerGetLevel::kontrol...\n");
143 #endif
144     *kgain = (*matrix)[csound][send][buss];
145     return OK;
146   }
147 };
148 /**
149  * MixerSend asignal, isend, ibus, ichannel
150  *
151  * Routes a signal from a send to a channel of a mixer bus.
152  * The gain of the send is controlled by the previously set mixer level.
153  */
154 struct MixerSend : public OpcodeBase<MixerSend> {
155   // No outputs.
156   // Inputs.
157   MYFLT *ainput;
158   MYFLT *isend;
159   MYFLT *ibuss;
160   MYFLT *ichannel;
161   // State.
162   size_t send;
163   size_t buss;
164   size_t channel;
165   size_t frames;
166   MYFLT *busspointer;
167   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>> *busses;
168   std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>> *matrix;
initMixerSend169   int init(CSOUND *csound) {
170 #ifdef ENABLE_MIXER_IDEBUG
171     warn(csound, "MixerSend::init...\n");
172 #endif
173     csound::QueryGlobalPointer(csound, "busses", busses);
174     csound::QueryGlobalPointer(csound, "matrix", matrix);
175     send = static_cast<size_t>(*isend);
176     buss = static_cast<size_t>(*ibuss);
177     createBuss(csound, buss);
178     channel = static_cast<size_t>(*ichannel);
179     frames = opds.insdshead->ksmps;
180     busspointer = &(*busses)[csound][buss][channel].front();
181 #ifdef ENABLE_MIXER_IDEBUG
182     warn(csound, "MixerSend::init: instance %p send %d buss "
183                  "%d channel %d frames %d busspointer %p\n",
184          csound, send, buss, channel, frames, busspointer);
185 #endif
186     return OK;
187   }
noteoffMixerSend188   int noteoff(CSOUND *) { return OK; }
audioMixerSend189   int audio(CSOUND *csound) {
190 #ifdef ENABLE_MIXER_KDEBUG
191     warn(csound, "MixerSend::audio...\n");
192 #endif
193     MYFLT gain = (*matrix)[csound][send][buss];
194     for (size_t i = 0; i < frames; i++) {
195       busspointer[i] += (ainput[i] * gain);
196     }
197 #ifdef ENABLE_MIXER_KDEBUG
198     warn(csound, "MixerSend::audio: instance %d send %d buss "
199                  "%d gain %f busspointer %p\n",
200          csound, send, buss, gain, busspointer);
201 #endif
202     return OK;
203   }
204 };
205 
206 /**
207  * asignal MixerReceive ibuss, ichannel
208  *
209  * Receives a signal from a channel of a bus.
210  * Obviously, instruments receiving signals must be numbered higher
211  * than instruments sending those signals.
212  */
213 struct MixerReceive : public OpcodeBase<MixerReceive> {
214   // Output.
215   MYFLT *aoutput;
216   // Inputs.
217   MYFLT *ibuss;
218   MYFLT *ichannel;
219   // State.
220   size_t buss;
221   size_t channel;
222   size_t frames;
223   MYFLT *busspointer;
224   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>> *busses;
initMixerReceive225   int init(CSOUND *csound) {
226     csound::QueryGlobalPointer(csound, "busses", busses);
227     buss = static_cast<size_t>(*ibuss);
228     channel = static_cast<size_t>(*ichannel);
229     frames = opds.insdshead->ksmps;
230     createBuss(csound, buss);
231 #ifdef ENABLE_MIXER_IDEBUG
232     warn(csound, "MixerReceive::init...\n");
233 #endif
234     busspointer = &(*busses)[csound][buss][channel].front();
235 #ifdef ENABLE_MIXER_IDEBUG
236     warn(csound, "MixerReceive::init csound %p buss %d channel "
237                  "%d frames %d busspointer %p\n",
238          csound, buss, channel, frames, busspointer);
239 #endif
240     return OK;
241   }
noteoffMixerReceive242   int noteoff(CSOUND *) { return OK; }
audioMixerReceive243   int audio(CSOUND *csound) {
244 #ifdef ENABLE_MIXER_KDEBUG
245     warn(csound, "MixerReceive::audio...\n");
246 #else
247     IGN(csound);
248 #endif
249     for (size_t i = 0; i < frames; i++) {
250       aoutput[i] = busspointer[i];
251     }
252 #ifdef ENABLE_MIXER_KDEBUG
253     warn(csound, "MixerReceive::audio aoutput %p busspointer %p\n", aoutput,
254          buss);
255 #endif
256     return OK;
257   }
258 };
259 
260 /**
261  * MixerClear
262  *
263  * Clears all busses. Must be invoked after last MixerReceive.
264  * You should probably use a highest-numbered instrument
265  * with an indefinite duration that invokes only this opcode.
266  */
267 struct MixerClear : public OpcodeBase<MixerClear> {
268   // No output.
269   // No input.
270   // State.
271   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>> *busses;
initMixerClear272   int init(CSOUND *csound) {
273     csound::QueryGlobalPointer(csound, "busses", busses);
274     return OK;
275   }
audioMixerClear276   int audio(CSOUND *csound) {
277 #ifdef ENABLE_MIXER_KDEBUG
278     warn(csound, "MixerClear::audio...\n")
279 #endif
280         for (std::map<size_t, std::vector<std::vector<MYFLT>>>::iterator busi =
281                  (*busses)[csound].begin();
282              busi != (*busses)[csound].end(); ++busi) {
283       for (std::vector<std::vector<MYFLT>>::iterator channeli =
284                busi->second.begin();
285            channeli != busi->second.end(); ++channeli) {
286         for (std::vector<MYFLT>::iterator framei = (*channeli).begin();
287              framei != (*channeli).end(); ++framei) {
288           *framei = 0;
289         }
290       }
291     }
292 #ifdef ENABLE_MIXER_KDEBUG
293     warn(csound, "MixerClear::audio\n")
294 #endif
295         return OK;
296   }
297 };
298 
299 extern "C" {
300 
301 static OENTRY localops[] = {
302     {(char *)"MixerSetLevel", sizeof(MixerSetLevel), _CW, 3, (char *)"",
303      (char *)"iik", (SUBR)&MixerSetLevel::init_, (SUBR)&MixerSetLevel::kontrol_,
304      0},
305     {(char *)"MixerSetLevel_i", sizeof(MixerSetLevel), _CW, 1, (char *)"",
306      (char *)"iii", (SUBR)&MixerSetLevel::init_, 0, 0},
307     {(char *)"MixerGetLevel", sizeof(MixerGetLevel), _CR, 3, (char *)"k",
308      (char *)"ii", (SUBR)&MixerGetLevel::init_, (SUBR)&MixerGetLevel::kontrol_,
309      0},
310     {(char *)"MixerSend", sizeof(MixerSend), _CW, 3, (char *)"", (char *)"aiii",
311      (SUBR)&MixerSend::init_, (SUBR)&MixerSend::audio_},
312     {(char *)"MixerReceive", sizeof(MixerReceive), _CR, 3, (char *)"a",
313      (char *)"ii", (SUBR)&MixerReceive::init_, (SUBR)&MixerReceive::audio_},
314     {(char *)"MixerClear", sizeof(MixerClear), 0, 3, (char *)"", (char *)"",
315      (SUBR)&MixerClear::init_, (SUBR)&MixerClear::audio_},
316     {NULL, 0, 0, 0, NULL, NULL, (SUBR)NULL, (SUBR)NULL, (SUBR)NULL}};
317 
csoundModuleCreate_mixer(CSOUND * csound)318 PUBLIC int csoundModuleCreate_mixer(CSOUND *csound) {
319   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>>
320       *busses = 0;
321   busses =
322       new std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>>;
323   csound::CreateGlobalPointer(csound, "busses", busses);
324   std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>> *matrix = 0;
325   matrix = new std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>>;
326   csound::CreateGlobalPointer(csound, "matrix", matrix);
327   return OK;
328 }
329 
csoundModuleInit_mixer(CSOUND * csound)330 PUBLIC int csoundModuleInit_mixer(CSOUND *csound) {
331   OENTRY *ep = (OENTRY *)&(localops[0]);
332   int err = 0;
333 
334   while (ep->opname != NULL) {
335     err |= csound->AppendOpcode(csound, ep->opname, ep->dsblksiz, ep->flags,
336                                 ep->thread, ep->outypes, ep->intypes,
337                                 (int (*)(CSOUND *, void *))ep->iopadr,
338                                 (int (*)(CSOUND *, void *))ep->kopadr,
339                                 (int (*)(CSOUND *, void *))ep->aopadr);
340     ep++;
341   }
342   return err;
343 }
344 
345 /*
346  * The mixer busses are laid out:
347  * busses[csound][bus][channel][frame].
348  * std::map<CSOUND *, std::map<size_t,
349  *          std::vector< std::vector<MYFLT> > > > *busses = 0;
350  * The mixer send matrix is laid out:
351  * matrix[csound][send][bus].
352  * std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT> > > *matrix = 0;
353  */
csoundModuleDestroy_mixer(CSOUND * csound)354 PUBLIC int csoundModuleDestroy_mixer(CSOUND *csound) {
355   std::map<CSOUND *, std::map<size_t, std::vector<std::vector<MYFLT>>>>
356       *busses = 0;
357   csound::QueryGlobalPointer(csound, "busses", busses);
358   if (busses) {
359     for (std::map<size_t, std::vector<std::vector<MYFLT>>>::iterator busi =
360              (*busses)[csound].begin();
361          busi != (*busses)[csound].end(); ++busi) {
362       for (std::vector<std::vector<MYFLT>>::iterator channeli =
363                busi->second.begin();
364            channeli != busi->second.end(); ++channeli) {
365         channeli->resize(0);
366       }
367       busi->second.clear();
368     }
369     busses->clear();
370     csound->DestroyGlobalVariable(csound, "busses");
371     delete busses;
372     busses = nullptr;
373   }
374   std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT>>> *matrix = 0;
375   csound::QueryGlobalPointer(csound, "matrix", matrix);
376   if (matrix) {
377     // std::map<CSOUND *, std::map<size_t, std::map<size_t, MYFLT> > >
378     for (std::map<size_t, std::map<size_t, MYFLT>>::iterator matrixi =
379              (*matrix)[csound].begin();
380          matrixi != (*matrix)[csound].end(); ++matrixi) {
381       matrixi->second.clear();
382     }
383     matrix->clear();
384     csound->DestroyGlobalVariable(csound, "matrix");
385     delete matrix;
386     matrix = nullptr;
387   }
388   return OK;
389 }
390 
391 #ifndef INIT_STATIC_MODULES
csoundModuleCreate(CSOUND * csound)392 PUBLIC int csoundModuleCreate(CSOUND *csound) {
393   return csoundModuleCreate_mixer(csound);
394 }
395 
csoundModuleInit(CSOUND * csound)396 PUBLIC int csoundModuleInit(CSOUND *csound) {
397   return csoundModuleInit_mixer(csound);
398 }
399 
csoundModuleDestroy(CSOUND * csound)400 PUBLIC int csoundModuleDestroy(CSOUND *csound) {
401   return csoundModuleDestroy_mixer(csound);
402 }
403 #endif
404 } // END EXTERN C
405