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