1 /*
2     mixer.cXF
3 
4     Copyright (C) 1995 John ffitch
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 
24 /*******************************************************\
25 *   mixer.c                                             *
26 *   mix a set of sound files with arbitary starts       *
27 *   jpff 23 Sep 1994                                    *
28 *   including lifting from Csound itself                *
29 \*******************************************************/
30 
31 
32 /* Notes:
33  *     This makes a mess of multichannel inputs.
34  *     Needs to take much more care
35  */
36 
37 #include "std_util.h"
38 #include "soundio.h"
39 #include <ctype.h>
40 #include <inttypes.h>
41 
42 /* Constants */
43 
44 #define NUMBER_OF_SAMPLES (65536)
45 #define NUMBER_OF_FILES   (32)
46 
47 #define FIND(MSG)   if (*s == '\0')  \
48     if (UNLIKELY(!(--argc) || ((s = *++argv) && *s == '-')))            \
49       csound->Die(csound, Str("mixer: error: %s"), MSG);
50 
51 typedef struct scalepoint {
52     MYFLT y0;
53     MYFLT y1;
54     MYFLT yr;
55     int32_t x0;
56     int32_t x1;
57     struct scalepoint *next;
58 } scalepoint;
59 
60 typedef struct inputs {
61     long        start;          /* Time this file starts in samples */
62     MYFLT       time;           /* Time this file starts in secs */
63     char *      name;           /* Name of file */
64     int32_t         use_table;      /* Should we use multiplier or table */
65     MYFLT       factor;         /* Gain factor */
66     char *      fname;          /* Name of scale table file */
67     scalepoint *fulltable;      /* Scaling table */
68     scalepoint *table;          /* current position in table */
69     SNDFILE    *fd;             /* File descriptor handle */
70     int16       channels[5];    /* destinations of channels */
71     int32_t         non_clear;      /* Boolean to say if fiddled mixing */
72     SOUNDIN *   p;              /* Csound structure */
73 } inputs;
74 
75 typedef struct mixer_globals_ {
76     CSOUND    *csound;
77     inputs    mixin[NUMBER_OF_FILES];
78     int32_t   outputs;
79     int32_t   debug;
80     uint32_t  outbufsiz;
81     MYFLT     *out_buf;
82     int32_t   outrange;                 /* Count samples out of range */
83 } MIXER_GLOBALS;
84 
85 /* Static function prototypes */
86 
87 static  void    InitScaleTable(MIXER_GLOBALS *, int32_t);
88 static  MYFLT   gain(MIXER_GLOBALS *, int32_t, int32_t);
89 static  SNDFILE *MXsndgetset(CSOUND*,inputs *);
90 static  void    MixSound(MIXER_GLOBALS *, int32_t, SNDFILE *, OPARMS *);
91 
92 static const char *usage_txt[] = {
93   Str_noop("Usage:\tmixer [-flags] soundfile [-flags] soundfile ..."),
94   Str_noop("Legal flags are:"),
95   Str_noop("-o fnam\tsound output filename"),
96   Str_noop("-A\tcreate an AIFF format output soundfile"),
97   Str_noop("-W\tcreate a WAV format output soundfile"),
98   Str_noop("-h\tno header on output soundfile"),
99   Str_noop("-8\t8-bit unsigned_char sound samples"),
100   Str_noop("-c\t8-bit signed_char sound samples"),
101   Str_noop("-8\t8-bit unsigned_char sound samples"),
102   Str_noop("-a\talaw sound samples"),
103   Str_noop("-u\tulaw sound samples"),
104   Str_noop("-s\tshort_int sound samples"),
105   Str_noop("-l\tlong_int sound samples"),
106   Str_noop("-f\tfloat sound samples"),
107   Str_noop("-R\tcontinually rewrite header while writing soundfile (WAV/AIFF)"),
108   Str_noop("-H#\tprint a heartbeat style 1, 2 or 3 at each soundfile write"),
109   Str_noop("-N\tnotify (ring the bell) when score or miditrack is done"),
110   Str_noop("-F fpnum\tamount to scale amplitude for next input"),
111   Str_noop("-F fname\tfile of a scale table for next input"),
112   Str_noop("-S integer\tsample number at which to insert file"),
113   Str_noop("-T fpnum\ttime at which to insert file"),
114   Str_noop("-1 -2 -3 -4\tinclude named channel"),
115   Str_noop("-^ n m\tinclude channel n and output as channel m"),
116   Str_noop("-v\tverbose mode for debugging"),
117   Str_noop("-- fname\tLog output to file"),
118   Str_noop("flag defaults: mixer -s -otest -F 1.0 -S 0"),
119     NULL
120 };
121 
usage(CSOUND * csound,const char * mesg,...)122 static void usage(CSOUND *csound, const char *mesg, ...)
123 {
124     const char  **sp;
125     va_list     args;
126 
127     for (sp = &(usage_txt[0]); *sp != NULL; sp++)
128       csound->Message(csound, "%s\n", Str(*sp));
129 
130     va_start(args, mesg);
131     csound->ErrMsgV(csound, Str("mixer: error: "), mesg, args);
132     va_end(args);
133     csound->LongJmp(csound, 1);
134 }
135 
set_output_format(CSOUND * csound,char c,char outformch,OPARMS * O)136 static char set_output_format(CSOUND *csound, char c, char outformch, OPARMS *O)
137 {
138   (void) csound;
139     switch (c) {
140       case 'a': O->outformat = AE_ALAW;   /* a-law soundfile */
141                 break;
142       case 'c': O->outformat = AE_CHAR;   /* signed 8-bit soundfile */
143                 break;
144       case '8': O->outformat = AE_UNCH;   /* unsigned 8-bit soundfile */
145                 break;
146       case 'f': O->outformat = AE_FLOAT;  /* float soundfile */
147                 break;
148       case 's': O->outformat = AE_SHORT;  /* short_int soundfile*/
149                 break;
150       case 'l': O->outformat = AE_LONG;   /* long_int soundfile */
151                 break;
152       case 'u': O->outformat = AE_ULAW;   /* mu-law soundfile */
153                 break;
154       case '3': O->outformat = AE_24INT;  /* 24bit packed soundfile*/
155                 break;
156       case 'e': O->outformat = AE_FLOAT;  /* float soundfile (for rescaling) */
157                 break;
158       default:  return outformch;         /* do nothing */
159     }
160     return c;
161 }
162 
mixer_main(CSOUND * csound,int argc,char ** argv)163 static int mixer_main(CSOUND *csound, int argc, char **argv)
164 {
165     OPARMS      O;
166     char        *inputfile = NULL;
167     SNDFILE     *outfd;
168     int32_t         i;
169     char        outformch='s', c, *s;
170     const char  *envoutyp;
171     int32_t         n = 0;
172     SF_INFO     sfinfo;
173     MIXER_GLOBALS *pp = (MIXER_GLOBALS*) csound->Calloc(csound,
174                                                         sizeof(MIXER_GLOBALS));
175     inputs      *mixin = &(pp->mixin[0]);
176 
177     csound->GetOParms(csound, &O);
178 
179     pp->csound = csound;
180     /*csound->dbfs_to_float = csound->e0dbfs = FL(1.0);*/
181     /* Check arguments */
182     if ((envoutyp = csound->GetEnv(csound, "SFOUTYP")) != NULL) {
183       if (strcmp(envoutyp, "AIFF") == 0)
184         O.filetyp = TYP_AIFF;
185       else if (strcmp(envoutyp, "WAV") == 0)
186         O.filetyp = TYP_WAV;
187       else if (strcmp(envoutyp, "IRCAM") == 0)
188         O.filetyp = TYP_IRCAM;
189       else {
190         csound->ErrorMsg(csound, Str("%s not a recognized SFOUTYP env setting"),
191                                  envoutyp);
192         return -1;
193       }
194     }
195     mixin[n].start = -1; mixin[n].time = -FL(1.0);
196     mixin[n].factor = FL(1.0); mixin[n].non_clear = 0;
197     mixin[n].fulltable = NULL; mixin[n].use_table = 0;
198     for (i=1; i<5; i++) mixin[n].channels[i] = 0;
199     if (UNLIKELY(!(--argc)))
200       usage(csound,"%s", Str("Insufficient arguments"));
201     do {
202       s = *++argv;
203       if (*s++ == '-')                  /* read all flags:  */
204         while ((c = *s++) != '\0')
205           switch(c) {
206           case 'o':
207             FIND(Str("no outfilename"))
208             O.outfilename = s;         /* soundout name */
209             for ( ; *s != '\0'; s++) ;
210             if (UNLIKELY(strcmp(O.outfilename, "stdin") == 0))
211               csound->Die(csound, "%s", Str("mixer: -o cannot be stdin"));
212 #if defined(WIN32)
213             if (UNLIKELY(strcmp(O.outfilename,"stdout") == 0)) {
214               csound->Die(csound, "%s", Str("mixer: stdout audio not supported"));
215             }
216 #endif
217             break;
218           case 'A':
219             O.filetyp = TYP_AIFF;      /* AIFF output request  */
220             break;
221           case 'J':
222             O.filetyp = TYP_IRCAM;     /* IRCAM output request */
223             break;
224           case 'W':
225             O.filetyp = TYP_WAV;       /* WAV output request  */
226             break;
227           case 'F':
228             FIND(Str("no scale factor"));
229             if (isdigit(*s) || *s == '-' || *s == '+')
230               mixin[n].factor = (MYFLT) atof(s);
231             else {
232               mixin[n].fname = (char*) csound->Malloc(csound, strlen(s) + 1);
233               strcpy(mixin[n].fname, s);
234               mixin[n].use_table = 1;
235            }
236             while (*++s);
237             break;
238           case 'S':
239             FIND(Str("no start sample"));
240             mixin[n].start = atoi(s);
241             while (*++s);
242             if (UNLIKELY(mixin[n].time >= FL(0.0))) {
243               csound->Warning(csound, "%s", Str("-S overriding -T"));
244               mixin[n].time = -FL(1.0);
245             }
246             break;
247           case 'T':
248             FIND(Str("no start time"));
249             mixin[n].time = (MYFLT) atof(s);
250             while (*++s);
251             if (UNLIKELY(mixin[n].start >= 0)) {
252               csound->Warning(csound, "%s", Str("-T overriding -S"));
253               mixin[n].start = -1;
254             }
255             break;
256           case '1':
257           case '2':
258           case '3':
259           case '4':
260             {
261               int32_t src = c - '0';
262               if (src > pp->outputs)
263                 pp->outputs = src;
264               mixin[n].channels[src] = src;
265               mixin[n].non_clear = 1;
266               break;
267             }
268           case '^':
269             {
270               int32_t src = c, dst;
271               FIND(Str("no source channel number"));
272               src = atoi(s);
273               while (*++s);
274               FIND(Str("no destination channel number"));
275               dst = atoi(s);
276               while (*++s);
277               if (UNLIKELY(src > 4 || src < 1 || dst > 4 || dst < 1)) {
278                 csound->Warning(csound, "%s",
279                                 Str("illegal channel number ignored"));
280                 break;
281               }
282               if (dst > pp->outputs)
283                 pp->outputs = dst;
284               mixin[n].channels[dst] = src;
285               mixin[n].non_clear = 1;
286               break;
287             }
288           case 'h':
289             O.filetyp = TYP_RAW;       /* skip sfheader  */
290             break;
291           case 'c':
292           case 'a':
293           case 'u':
294           case '8':
295           case 's':
296           case 'l':
297           case 'f':
298             outformch = set_output_format(csound, c, outformch, &O);
299             break;
300           case 'R':
301             O.rewrt_hdr = 1;
302             break;
303           case 'H':
304             if (isdigit(*s)) {
305               int32_t n;
306               sscanf(s, "%d%n", &O.heartbeat, &n);
307               s += n;
308             }
309             else O.heartbeat = 1;
310             break;
311           case 'N':
312             O.ringbell = 1;             /* notify on completion */
313             break;
314           case 'v':                       /* Verbose mode */
315             pp->debug = 1;
316             break;
317           default:
318             usage(csound, Str("unknown flag -%c"), c);
319           }
320       else {
321         int32_t i;
322         mixin[n].name = --s;
323         if (!mixin[n].non_clear)
324           for (i=1; i<5; i++) mixin[n].channels[i] = i;
325         if (UNLIKELY(n++ >= NUMBER_OF_FILES-1)) {
326           usage(csound,Str("Too many mixin"));
327         }
328         mixin[n].start = -1;
329         mixin[n].time = -1;
330         mixin[n].factor = FL(1.0);
331         mixin[n].non_clear = 0;
332       }
333     } while (--argc);
334 
335     /* Read sound files */
336     if (UNLIKELY(n == 0)) {
337       csound->ErrorMsg(csound, "%s", Str("No mixin"));
338       return -1;
339     }
340     for (i = 0; i < n; i++) {
341       if (UNLIKELY(!MXsndgetset(csound, &mixin[i]))) {
342         csound->ErrorMsg(csound, Str("%s: error while opening %s"),
343                                  argv[0], inputfile);
344         return -1;
345       }
346       mixin[i].p->channel = ALLCHNLS;
347       if (i>0) {
348         if (UNLIKELY(mixin[0].p->sr != mixin[i].p->sr)) {
349           csound->ErrorMsg(csound, "%s", Str("Input formats not the same"));
350           return -1;
351         }
352       }
353       if (mixin[i].non_clear) {
354         int32_t j;
355         for (j = 1; j<5; j++)
356           if (pp->outputs < mixin[i].channels[j]) {
357             pp->outputs = mixin[i].channels[j];
358           }
359       }
360       else if (pp->outputs < mixin[i].p->nchanls)
361         pp->outputs = mixin[i].p->nchanls;
362       if (mixin[i].time >= FL(0.0)) {
363         MYFLT sval = (MYFLT) mixin[i].time * (MYFLT) mixin[i].p->sr;
364         mixin[i].start = (long) MYFLT2LRND(sval);
365       }
366       else if (mixin[i].start < 0L)
367         mixin[i].start = 0L;
368       if (mixin[i].use_table) InitScaleTable(pp, i);
369     }
370 
371     if (!O.outformat)                      /* if no audioformat yet  */
372       O.outformat = mixin[0].p->format;    /* Copy from first input file */
373     O.sfsampsize = csound->sfsampsize(FORMAT2SF(O.outformat));
374     if (!O.filetyp)
375       O.filetyp = mixin[0].p->filetyp;     /* Copy from input file */
376     O.sfheader = (O.filetyp == TYP_RAW ? 0 : 1);
377     if (!O.sfheader)       /* can't rewrite header if no header requested */
378       O.rewrt_hdr = 0;
379 #ifdef NeXT
380     if (O.outfilename == NULL && !O.filetyp) O.outfilename = "test.snd";
381     else if (O.outfilename == NULL) O.outfilename = "test";
382 #else
383     if (O.outfilename == NULL) {
384       if (O.filetyp == TYP_WAV) O.outfilename = "test.wav";
385       else if (O.filetyp == TYP_AIFF) O.outfilename = "test.aif";
386       else O.outfilename = "test";
387     }
388 #endif
389     csound->SetUtilSr(csound, (MYFLT)mixin[0].p->sr);
390     memset(&sfinfo, 0, sizeof(SF_INFO));
391     //sfinfo.frames = 0/*was -1*/;
392     sfinfo.samplerate = mixin[0].p->sr;
393     sfinfo.channels /*= csound->nchnls*/ = (int32_t) mixin[0].p->nchanls;
394     sfinfo.format = TYPE2SF(O.filetyp) | FORMAT2SF(O.outformat);
395     if (strcmp(O.outfilename, "stdout") == 0) {
396       outfd = sf_open_fd(1, SFM_WRITE, &sfinfo, 0);
397       if (outfd != NULL) {
398         if (UNLIKELY(csound->CreateFileHandle(csound,
399                                               &outfd, CSFILE_SND_W,
400                                               "stdout") == NULL)) {
401           sf_close(outfd);
402           return -1;
403         }
404       }
405     }
406     else if (csound->FileOpen2(csound, &outfd, CSFILE_SND_W, O.outfilename,
407                        &sfinfo, "SFDIR", csound->type2csfiletype(O.filetyp,
408                        O.outformat), 0) == NULL)
409       outfd = NULL;
410     if (UNLIKELY(outfd == NULL)) {
411       csound->ErrorMsg(csound, Str("mixer: error opening output file '%s': %s"),
412                        O.outfilename, Str(sf_strerror(NULL)));
413       return -1;
414     }
415     if (UNLIKELY(O.rewrt_hdr))
416       sf_command(outfd, SFC_SET_UPDATE_HEADER_AUTO, NULL, 0);
417     /* calc outbuf size & alloc bufspace */
418     pp->outbufsiz = NUMBER_OF_SAMPLES * pp->outputs;
419     pp->out_buf = csound->Malloc(csound, pp->outbufsiz * sizeof(MYFLT));
420     pp->outbufsiz *= O.sfsampsize;
421     csound->Message(csound, Str("writing %d-byte blks of %s to %s (%s)\n"),
422                             pp->outbufsiz,
423                             csound->getstrformat(O.outformat), O.outfilename,
424                             csound->type2string(O.filetyp));
425     MixSound(pp, n, outfd, &O);
426     if (O.ringbell)
427       csound->MessageS(csound, CSOUNDMSG_REALTIME, "\007");
428     return 0;
429 }
430 
431 static void
InitScaleTable(MIXER_GLOBALS * pp,int32_t i)432 InitScaleTable(MIXER_GLOBALS *pp, int32_t i)
433 {
434     CSOUND *csound = pp->csound;
435     FILE    *f;
436     inputs  *mixin = &(pp->mixin[0]);
437     MYFLT   samplepert = (MYFLT) mixin[i].p->sr;
438     MYFLT   x, y;
439     scalepoint *tt = (scalepoint*) csound->Malloc(csound, sizeof(scalepoint));
440 
441     if (UNLIKELY(csound->FileOpen2(csound, &f, CSFILE_STD, mixin[i].fname,
442                                    "r", NULL, CSFTYPE_FLOATS_TEXT, 0) == NULL)) {
443       csound->Die(csound, Str("Cannot open scale table file %s"),
444                           mixin[i].fname);
445       return;   /* not reached */
446     }
447     mixin[i].fulltable = mixin[i].table = tt;
448     tt->x0 = 0; tt->y0 = FL(0.0); tt->x1 = 0; tt->y1 = FL(0.0);
449     tt->yr = FL(0.0); tt->next = NULL;
450 #ifdef USE_DOUBLE
451     while (fscanf(f, "%lf %lf\n", &x, &y) == 2) {
452 #else
453     while (fscanf(f, "%f %f\n", &x, &y) == 2) {
454 #endif
455       scalepoint *newpoint;
456       newpoint = (scalepoint*) csound->Malloc(csound, sizeof(scalepoint));
457       newpoint->x0 = tt->x1;
458       newpoint->y0 = tt->y1;
459       newpoint->x1 = (int32_t) (x*samplepert);
460       newpoint->y1 = y;
461       if (newpoint->x1 == newpoint->x0) {
462         MYFLT div = (MYFLT)(tt->x1 - tt->x0);
463         tt->y1 = y;
464         if (LIKELY(div))
465           tt->yr = (y - tt->y0)/div;
466         else  tt->yr = y;
467         csound->Free(csound, newpoint);
468       }
469       else {
470         newpoint->yr =
471           (y - newpoint->y0)/((MYFLT)(newpoint->x1 - newpoint->x0));
472         tt->next = newpoint;
473         newpoint->next = NULL;
474         tt = newpoint;
475       }
476     }
477     {
478       scalepoint *newpoint =
479           (scalepoint*) csound->Malloc(csound, sizeof(scalepoint));
480       tt->next = newpoint;
481       newpoint->x0 = tt->x1;
482       newpoint->y0 = tt->y1;
483       newpoint->x1 = 0x7fffffff;
484       newpoint->y1 = FL(0.0);
485       newpoint->next = NULL;
486       newpoint->yr = (x == newpoint->x0 ?
487                       -newpoint->y0 :
488                       -newpoint->y0/((MYFLT)(0x7fffffff-newpoint->x0)));
489     }
490     if (pp->debug) {
491       scalepoint *tt = mixin[i].table;
492       csound->Message(csound, "Scale table is\n");
493       while (tt != NULL) {
494         csound->Message(csound,  "(%d %f) -> %d %f [%f]\n",
495                     tt->x0, tt->y0, tt->x1, tt->y1, tt->yr);
496         tt = tt->next;
497       }
498       csound->Message(csound,  "END of Table\n");
499     }
500     mixin[i].use_table = 1;
501 }
502 
503 static MYFLT gain(MIXER_GLOBALS *pp, int32_t n, int32_t i)
504 {
505     CSOUND *csound = pp->csound;
506     inputs  *mixin = &(pp->mixin[0]);
507 
508     if (!mixin[n].use_table) return mixin[n].factor;
509     if (i<mixin[n].table->x0) mixin[n].table = mixin[n].fulltable;
510     while (i<mixin[n].table->x0 ||
511            i>=mixin[n].table->x1) {/* Get correct segment */
512       if (UNLIKELY(pp->debug))
513         csound->Message(csound, "Table %d: %d (%d %f) -> %d %f [%f]\n",
514                         n, i, mixin[n].table->x0, mixin[n].table->y0,
515                         mixin[n].table->x1, mixin[n].table->y1,
516                         mixin[n].table->yr);
517       mixin[n].table = mixin[n].table->next;
518     }
519     return mixin[n].factor*(mixin[n].table->y0 +
520                             mixin[n].table->yr*(MYFLT)(i - mixin[n].table->x0));
521 }
522 
523 static SNDFILE *MXsndgetset(CSOUND *csound, inputs *ddd)
524 {
525     SNDFILE *infd;
526     MYFLT   dur;
527     SOUNDIN *p;
528 
529     csound->SetUtilSr(csound, FL(0.0));         /* set esr 0. with no orchestra   */
530     ddd->p = p = (SOUNDIN *) csound->Calloc(csound, sizeof(SOUNDIN));
531     p->analonly = 1;
532     p->channel = ALLCHNLS;
533     p->skiptime = FL(0.0);
534     strNcpy(p->sfname, ddd->name, MAXSNDNAME-1);
535     /* open sndfil, do skiptime */
536     if (UNLIKELY((infd = csound->sndgetset(csound, p)) == NULL))
537       return NULL;
538     p->getframes = p->framesrem;
539     dur = (MYFLT) p->getframes / p->sr;
540     csound->Message(csound, "%s %" PRId64 " %s (%3.1f secs)\n",
541                     Str("mixing"), p->getframes, Str("sample frames"), dur);
542     ddd->fd = infd;
543     return infd;
544 }
545 
546  static void MixSound(MIXER_GLOBALS *pp, int32_t n, SNDFILE *outfd, OPARMS *O)
547 {
548     CSOUND *csound = pp->csound;
549     inputs  *mixin = &(pp->mixin[0]);
550     MYFLT   *buffer = (MYFLT*) csound->Calloc(csound, sizeof(MYFLT)
551                                                       * 6 * NUMBER_OF_SAMPLES);
552     MYFLT   *ibuffer = (MYFLT*) csound->Calloc(csound, sizeof(MYFLT)
553                                                        * 6 * NUMBER_OF_SAMPLES);
554     long    read_in;
555     MYFLT   tpersample;
556     MYFLT   max, min;
557     long    lmaxpos, lminpos;
558     int32_t     maxtimes, mintimes;
559     long    sample = 0;
560     int32_t     i, j, k;
561     long    bytes = 0;
562     int32_t     block = 0;
563     int32_t     more_to_read = 1;
564     int32_t     size;
565     int32_t     this_block;
566     int32_t     outputs = pp->outputs;
567 
568     tpersample = FL(1.0)/(MYFLT)mixin[0].p->sr;
569     max = FL(0.0);  lmaxpos = 0; maxtimes = 0;
570     min = FL(0.0);  lminpos = 0; mintimes = 0;
571     while (more_to_read) {
572       more_to_read = 0;
573       size = NUMBER_OF_SAMPLES;
574       for (i = 0; i < n; i++)
575         if (mixin[i].start > sample && mixin[i].start - sample < size)
576           size = mixin[i].start - sample;
577       /* for (j=0; j<size*outputs; j++) buffer[j] = FL(0.0); */
578       memset(buffer, 0, sizeof(MYFLT)*size*outputs);
579       this_block = 0;
580       for (i = 0; i<n; i++) {
581         if (sample >= mixin[i].start) {
582           read_in = csound->getsndin(csound, mixin[i].fd, ibuffer,
583                                      size*mixin[i].p->nchanls, mixin[i].p);
584           if (csound->Get0dBFS(csound)!=FL(1.0)) { /* Optimisation? */
585             MYFLT xx = 1.0/csound->Get0dBFS(csound);
586             for(j=0; j < read_in; j++)
587               ibuffer[j] *= xx;
588           }
589           read_in /= mixin[i].p->nchanls;
590           if (read_in > this_block) this_block = read_in;
591           if (mixin[i].non_clear) {
592             for (k = 1; k<=mixin[i].p->nchanls; k++)
593               if (mixin[i].channels[k]) {
594                 for (j=0; j<read_in; j++) {
595                   buffer[j*outputs+mixin[i].channels[k]-1] +=
596                     ibuffer[j*outputs+k-1] *
597                     gain(pp, i, sample + j + mixin[i].channels[k] - 1);
598                 }
599               }
600             mixin[i].fulltable = mixin[i].table;
601           }
602           else {
603             for (k = 1; k<=mixin[i].p->nchanls; k++) {
604               for (j=0; j<read_in; j++) {
605                 buffer[j*outputs+k-1] +=
606                   ibuffer[j*outputs + k - 1] * gain(pp, i, sample + j + k - 1);
607               }
608             }
609             mixin[i].fulltable = mixin[i].table;
610           }
611           if (read_in < size) {
612             mixin[i].start = 0x7ffffff;
613           }
614           else more_to_read++;
615         }
616         else if (mixin[i].start > sample && mixin[i].start != 0x7ffffff)
617           more_to_read++;
618       }
619       for (j = 0; j < this_block * outputs; j++) {
620         if (UNLIKELY(buffer[j] > 1.0 || buffer[j] < -(1.0)))
621           pp->outrange++;
622         if (buffer[j] == max) maxtimes++;
623         if (buffer[j] == min) mintimes++;
624         if (buffer[j] > max) max = buffer[j], lmaxpos = sample+j, maxtimes=1;
625         if (buffer[j] < min) min = buffer[j], lminpos = sample+j, mintimes=1;
626       }
627       sf_write_MYFLT(outfd, buffer, this_block * outputs);
628       block++;
629       bytes += O->sfsampsize * this_block * outputs;
630       switch (O->heartbeat) {
631       case 1:
632         csound->MessageS(csound, CSOUNDMSG_REALTIME, "%c\b", "|/-\\"[block&3]);
633         break;
634       case 2:
635         csound->MessageS(csound, CSOUNDMSG_REALTIME, ".");
636         break;
637       case 3:
638         {
639           int32_t n;
640           csound->MessageS(csound, CSOUNDMSG_REALTIME, "%d%n", block, &n);
641           while (n--) csound->MessageS(csound, CSOUNDMSG_REALTIME, "\b");
642         }
643         break;
644       case 4:
645         csound->MessageS(csound, CSOUNDMSG_REALTIME, "\007");
646         break;
647       }
648       sample += size;
649     }
650     csound->rewriteheader((struct SNFDILE*)outfd);
651     min *= (DFLT_DBFS);
652     max *= (DFLT_DBFS);
653     csound->Message(csound, Str("Max val %d at index %ld (time %.4f, chan %d) "
654                                 "%d times\n"),
655                             (int32_t) max, lmaxpos, tpersample * (lmaxpos/outputs),
656                             (int32_t) lmaxpos % outputs, maxtimes);
657     csound->Message(csound, Str("Min val %d at index %ld (time %.4f, chan %d) "
658                                 "%d times\n"),
659                             (int32_t) min, lminpos, tpersample * (lminpos/outputs),
660                             (int32_t) lminpos % outputs, mintimes);
661     if (UNLIKELY(pp->outrange))
662       csound->Message(csound, Str("%d sample%s out of range\n"),
663                               pp->outrange, (pp->outrange == 1 ? "" : "s"));
664     else
665       csound->Message(csound, Str("Max scale factor = %.3f\n"),
666                               DFLT_DBFS / ((max > -min) ? max : -min));
667 }
668 
669 /* module interface */
670 
671 int32_t mixer_init_(CSOUND *csound)
672 {
673     char    buf[128];
674     int32_t     retval = csound->AddUtility(csound, "mixer", mixer_main);
675 
676     snprintf(buf, 128, Str("Mixes sound files (max. %d)"),
677              (int32_t) NUMBER_OF_FILES);
678     if (!retval) {
679       retval = csound->SetUtilityDescription(csound, "mixer", buf);
680     }
681     return retval;
682 }
683