1 /*
2     bbcut.c:
3 
4     Copyright (C) 2001 Nick Collins
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 "csoundCore.h"
24 #include "interlocks.h"
25 #include "bbcut.h"
26 #include <math.h>
27 
28 /* my auxilliary functions */
29 
roundoffint(MYFLT x)30 static inline int32_t roundoffint(MYFLT x)
31 {
32     if (x > 0)
33       return((int32_t)(x + 0.500001)); /* in case of a close rounding
34                                       error when x= 0.5 +- integer */
35     else
36       return((int32_t)(x - 0.5));
37 }
38 
random_number(CSOUND * csound,int32_t a,int32_t b)39 static int32_t random_number(CSOUND *csound, int32_t a, int32_t b)
40 {
41     MYFLT x;
42     x = (MYFLT) (csound->Rand31(&(csound->randSeed1)) - 1) / FL(2147483645.0);
43     return roundoffint((MYFLT) a + x * (MYFLT) (b - a));
44 }
45 
myfltrandom(CSOUND * csound,MYFLT a,MYFLT b)46 static MYFLT myfltrandom(CSOUND *csound, MYFLT a, MYFLT b)
47 {
48     MYFLT x;
49     x = (MYFLT) (csound->Rand31(&(csound->randSeed1)) - 1) / FL(2147483645.0);
50     return (a + x * (b - a));
51 }
52 
BBCutMonoInit(CSOUND * csound,BBCUTMONO * p)53 static int32_t BBCutMonoInit(CSOUND *csound, BBCUTMONO *p)
54 {
55     /* call seed random at time now? */
56     /* later for efficiency- lookup table for grain envelope */
57     /* int32_t i; */
58     /* MYFLT t; */
59 
60     /* allocate space for a 256 point quarter sine/ exponential wavetable  */
61 /*     if (p->envbuffer.auxp == NULL) { */
62 /*       csound->AuxAlloc(csound, 256*sizeof(MYFLT),&p->envbuffer); */
63 
64 /*       for (i=0;i<256;++i) { */
65 /*         t= (PI*0.5*(MYFLT)i)/255.0; */
66 /*         ((MYFLT*) (p->envbuffer.auxp))[i]=t; */
67 /*       } */
68 /*     } */
69     size_t M;                      /* A temporary */
70 
71     /* have no knowledge of source length */
72     p->numbarsnow  = 0;
73     p->unitsdone   = 0;
74     p->totalunits  = 0;
75     p->unitblock   = 0;
76     p->repeats     = 0;
77     p->repeatsdone = 0;
78     p->stutteron   = 0;
79 
80     /* allocate space- need no more than a half bar at current
81        tempo and barlength */
82     M = ((size_t)(CS_ESR*(*p->barlength)/(*p->bps)))*sizeof(MYFLT);
83     if (p->repeatbuffer.auxp == NULL || p->repeatbuffer.size<M) {
84       csound->AuxAlloc(csound, M, &p->repeatbuffer);
85     }
86 
87     p->repeatsampdone = 0;
88 
89     p->Subdiv       = roundoffint(*p->subdiv);
90     p->Phrasebars   = roundoffint(*p->phrasebars);
91     p->Numrepeats   = roundoffint(*p->numrepeats);
92 
93     p->Stutterspeed = roundoffint(*p->stutterspeed);
94 
95     /* samp per unit= samp per bar/ subdiv */
96     /* = samp per beat * beats per bar /subdiv */
97     /* =(samp per sec / beats per sec)* (beats per bar/subdiv)  */
98     p->samplesperunit = roundoffint(((MYFLT)CS_ESR*(FL(1.0)/(*p->bps)))*
99                                     (*p->barlength/(MYFLT)p->Subdiv));
100 
101     /* enveloping */
102     p->Envelopingon = roundoffint(*p->envelopingon);
103     p->envsize = (p->Envelopingon) ? 64 : 0;
104 
105     return OK;
106 }
107 
108 /* rounding errors will accumulate slowly with respect to bps,  */
109 /* true tempo is determined by samplesperunit (which has been rounded off) */
110 /* only make floating point corrections for stutters with stutterspeed>1 */
111 
BBCutMono(CSOUND * csound,BBCUTMONO * p)112 static int32_t BBCutMono(CSOUND *csound, BBCUTMONO *p)
113 {
114     uint32_t offset = p->h.insdshead->ksmps_offset;
115     uint32_t early  = p->h.insdshead->ksmps_no_end;
116     uint32_t i, nsmps = CS_KSMPS;
117     int32_t oddmax,unitproj;
118     int32_t unitb,unitl,unitd;      /* temp for integer unitblock calculations */
119     MYFLT envmult,out;          /* intermedaites for enveloping grains */
120 
121     if (UNLIKELY(offset)) memset(p->aout, '\0', offset*sizeof(MYFLT));
122     if (UNLIKELY(early)) {
123       nsmps -= early;
124       memset(&p->aout[nsmps], '\0', early*sizeof(MYFLT));
125     }
126     for (i=offset;i<nsmps;i++) {
127       if (UNLIKELY((p->unitsdone+FL(0.000001))>=p->totalunits)) {
128         /* a new phrase of cuts */
129         p->numbarsnow  = random_number(csound, 1, p->Phrasebars);
130         p->totalunits  = p->numbarsnow*p->Subdiv;
131 
132         p->unitsdone   = 0;
133         p->unitsleft   = (MYFLT)p->totalunits;    /* must reset here */
134         p->repeats     = 0;
135         p->repeatsdone = 0;
136         p->stutteron   = 0;
137       }
138 
139       if (p->repeatsdone>=p->repeats) {
140         /* a new subphrase- a cut + some repeats of it */
141         p->repeatsdone = 0;
142 
143         /* STUTTER- only within half a bar of the end */
144         if (UNLIKELY((*p->stutterchance > myfltrandom(csound, FL(0.0), FL(1.0))) &&
145                      (p->unitsleft<(p->Subdiv/2)))) {
146           /* stutterspeed must be an integer greater than zero */
147 
148           p->repeats = roundoffint(p->unitsleft*p->Stutterspeed);
149           p->unitblock = FL(1.0)/(p->Stutterspeed);
150           p->stutteron = 1;
151         }
152         else {  /* NO STUTTER */
153           /* finding set of valid odd numbers for syncopated cuts */
154           /* integer division */
155           oddmax = p->Subdiv/2;
156           if ((oddmax&1)==0)
157             oddmax =(oddmax-2)/2;
158           else
159             oddmax = (oddmax-1)/2;
160 
161           unitb = random_number(csound, 0, oddmax);
162           unitb = (2*unitb)+1;
163 
164           unitl = roundoffint(p->unitsleft);
165 
166           while (unitb> unitl)
167             unitb = unitb-2;
168 
169           unitd = roundoffint(p->unitsdone);
170 
171           /* for debug testing           */
172           /* if (unitblock<=0, {"this should never happen".postln}); */
173           /* p->repeats is the total number of repeats, including
174              initial statement */
175           /* usually 1 or 2  */
176           p->repeats = random_number(csound, 1, p->Numrepeats + 1);
177           /* take right arg as p->Numrepeats+1 if make param more sensible */
178           unitproj = (p->repeats*unitb)+ unitd;
179           /* should be same logic as old algorithm for numrepeats=2 */
180           while (unitproj> p->totalunits) {
181             p->repeats = p->repeats-1;
182 
183             if (p->repeats<=1) {
184               p->repeats = 1;
185               unitb = unitl;    /* at this point is an integer */
186             }
187             unitproj =(p->repeats*unitb)+ unitd;
188           }
189 
190           /* convert integer to float */
191           p->unitblock = (MYFLT) unitb;
192         }       /* end of stutter/no stutter */
193 
194         /* determine offset - this part is very different for the csound
195            version */
196         /* there is no random access possible any more, only have now
197            (and past) */
198 
199         /* we must determine how long in samples a repeat is */
200         /* must persist between calls to this function */
201 
202         p->repeatlengthsamp = roundoffint(p->unitblock*p->samplesperunit);
203 
204         p->repeatsampdone = 0;
205 
206         /* determine envelope size - default is always 0 if enveloping off,
207            128 if on; */
208 
209         /* envsize must be at most a quarter of repeatsamplelength */
210         if ((p->Envelopingon) ==1) {
211           if (p->repeatlengthsamp<256) {
212             p->envsize = p->repeatlengthsamp/4;
213           }
214         }
215 
216       }
217 
218       /* AUDIO OUT */
219       if (p->repeatsdone==0) {
220         out = p->ain[i];        /* pass in directly to out */
221 
222         /* ENVELOPING */
223         envmult = FL(1.0);
224 
225         /* envelope in */
226         if (p->repeatsampdone<p->envsize) {
227           /* used sinusoid- prefer exponential */
228         /* envmult= sin(PI*0.5*(((MYFLT)(p->repeatsampdone))/(MYFLT)p->envsize)); */
229           envmult = (EXP((p->repeatsampdone)/(p->envsize))-FL(1.0))/
230             FL(1.7182818284590);
231         }
232 
233         /* envelope out if necessary */
234         if (p->repeatsampdone>=(p->repeatlengthsamp-p->envsize)) {
235           MYFLT xx = p->envsize; /* JPff patch 2019 Apr 28 */
236           if (xx==0.0) xx = 00.1;
237           /* envmult = sin(PI*0.5*
238              (((MYFLT)(p->repeatlengthsamp-p->repeatsampdone))/
239              (MYFLT)p->envsize)); */
240           envmult = (EXP(((p->repeatlengthsamp-p->repeatsampdone))/
241                          (xx))-FL(1.0))/
242             FL(1.7182818284590);
243         }
244 
245         out *= envmult;
246         /* /ENVELOPING DONE */
247 
248         p->aout[i] = out;
249 
250         if (p->repeats>1) {     /* if recording a repeat */
251           ((MYFLT*)(p->repeatbuffer.auxp))[p->repeatsampdone] = out;
252         }
253       }
254       else {    /* reading repeatbuffer for repeats */
255         p->aout[i] = ((MYFLT*)(p->repeatbuffer.auxp))[p->repeatsampdone];
256       }
257 
258       /* per sample accounting */
259       ++(p->repeatsampdone);
260 
261       /* if finished a cut, do accounting  */
262       if (UNLIKELY(p->repeatsampdone>=p->repeatlengthsamp)) {
263         ++(p->repeatsdone);
264         p->repeatsampdone = 0;
265 
266         p->unitsdone = p->unitsdone + p->unitblock;
267 
268         p->unitsleft = p->totalunits - p->unitsdone;
269 
270         /* to account for floating point errors in unitblock when  */
271         /* stuttering with stutterspeed > 1 */
272         if (UNLIKELY(p->stutteron && (p->repeatsdone==(p->repeats-1)))) {
273           p->unitblock = p->unitsleft;
274         }
275       }
276     }
277 
278     return OK;
279 }
280 
281 /* Stereo versions */
282 /* This following code is excatly the same as above, but */
283 /* uses BBCUTSTEREO- changes are doubling of buffer size
284    for interleaved stereo save and code in audio output part
285    to cope with that, variables out1,out2 for 2 channels */
286 
BBCutStereoInit(CSOUND * csound,BBCUTSTEREO * p)287 static int32_t BBCutStereoInit(CSOUND *csound, BBCUTSTEREO * p)
288 {
289     /* call seed random at time now? */
290 
291     /* later for efficiency- lookup table for grain envelope */
292     /* int32_t i; */
293     /* MYFLT t; */
294 
295        /* allocate space for a 256 point quarter sine/ exponential wavetable  */
296 /*     if (p->envbuffer.auxp == NULL) { */
297 /*       csound->AuxAlloc(csound, ((int32_t)(256*sizeof(MYFLT),&p->envbuffer); */
298 
299 /*                 for (i=0;i<256;++i) */
300 /*       { */
301 /*         t= (PI*0.5*(MYFLT)i)/255.0; */
302 /*         ((MYFLT*) (p->envbuffer.auxp))[i]=t; */
303 /*       } */
304 /*                 } */
305 
306     size_t M;                      /* temporary */
307 
308     /* have no knowledge of source length */
309 
310     p->numbarsnow = 0;
311     p->unitsdone = 0;
312     p->totalunits = 0;
313     p->unitblock = 0;
314     p->repeats = 0;
315     p->repeatsdone = 0;
316     p->stutteron = 0;
317 
318     /* allocate space- need no more than a half bar at current tempo
319        and barlength */
320     M = 2*((size_t)(CS_ESR*(*p->barlength)/(*p->bps)))*sizeof(MYFLT);
321     if (p->repeatbuffer.auxp == NULL || p->repeatbuffer.size<M) {
322       /* multiply by 2 for stereo buffer */
323       csound->AuxAlloc(csound, M, &p->repeatbuffer);
324     }
325 
326     p->repeatsampdone = 0;
327     p->Subdiv = roundoffint(*p->subdiv);
328     p->Phrasebars = roundoffint(*p->phrasebars);
329     p->Numrepeats = roundoffint(*p->numrepeats);
330 
331     p->Stutterspeed = roundoffint(*p->stutterspeed);
332 
333     /* samp per unit= samp per bar/ subdiv */
334     /* = samp per beat * beats per bar /subdiv */
335     /* =(samp per sec / beats per sec)* (beats per bar/subdiv)  */
336     p->samplesperunit = roundoffint(((MYFLT)CS_ESR/
337                                      (*p->bps))*(*p->barlength/
338                                                  (MYFLT)p->Subdiv));
339 
340     /* enveloping */
341     p->Envelopingon = roundoffint(*p->envelopingon);
342     p->envsize = (p->Envelopingon) ? 64 : 0;
343 
344     return OK;
345 }
346 
347 /* rounding errors will accumulate slowly with respect to bps,  */
348 /* true tempo is determined by samplesperunit (which has been rounded off) */
349 /* only make floating point corrections for stutters with stutterspeed>1 */
BBCutStereo(CSOUND * csound,BBCUTSTEREO * p)350 static int32_t BBCutStereo(CSOUND *csound, BBCUTSTEREO *p)
351 {
352     uint32_t offset = p->h.insdshead->ksmps_offset;
353     uint32_t early  = p->h.insdshead->ksmps_no_end;
354     uint32_t i, nsmps = CS_KSMPS;
355     int32_t oddmax,unitproj;
356     int32_t unitb,unitl,unitd;      /* temp for integer unitblock calculations */
357     MYFLT envmult,out1,out2;/* intermediates for enveloping grains */
358 
359     if (UNLIKELY(offset)) {
360       memset(p->aout1, '\0', offset*sizeof(MYFLT));
361       memset(p->aout2, '\0', offset*sizeof(MYFLT));
362     }
363     if (UNLIKELY(early)) {
364       nsmps -= early;
365       memset(&p->aout1[nsmps], '\0', early*sizeof(MYFLT));
366       memset(&p->aout2[nsmps], '\0', early*sizeof(MYFLT));
367     }
368     for (i=offset;i<nsmps;i++) {
369       /* a new phrase of cuts */
370       if (UNLIKELY((p->unitsdone+FL(0.000001))>=p->totalunits)) {
371         p->numbarsnow  = random_number(csound, 1, p->Phrasebars);
372         p->totalunits  = p->numbarsnow*p->Subdiv;
373 
374         p->unitsdone   = 0;
375         p->unitsleft   = (MYFLT)p->totalunits;    /* must reset here */
376         p->repeats     = 0;
377         p->repeatsdone = 0;
378         p->stutteron   = 0;
379       }
380 
381       if (UNLIKELY(p->repeatsdone>=p->repeats)) {
382         /* a new subphrase- a cut + some repeats of it */
383         p->repeatsdone = 0;
384 
385         /* STUTTER- only within half a bar of the end */
386         if (UNLIKELY((*p->stutterchance > myfltrandom(csound, FL(0.0), FL(1.0))) &&
387                      (p->unitsleft<(p->Subdiv/2)))) {
388           /* stutterspeed must be an integer greater than zero */
389 
390           p->repeats   = roundoffint(p->unitsleft*p->Stutterspeed);
391           p->unitblock = FL(1.0)/p->Stutterspeed;
392           p->stutteron = 1;
393         }
394         else {  /* NO STUTTER */
395           /* finding set of valid odd numbers for syncopated cuts */
396           /* integer division */
397           oddmax = p->Subdiv/2;
398           if ((oddmax & 1)==0)
399             oddmax = (oddmax-2)/2;
400           else
401             oddmax = (oddmax-1)/2;
402 
403           unitb = random_number(csound, 0, oddmax);
404           unitb = (2*unitb)+1;
405 
406           unitl = roundoffint(p->unitsleft);
407 
408           while (unitb> unitl)
409             unitb = unitb-2;
410 
411           unitd = roundoffint(p->unitsdone);
412 
413           /* for debug testing           */
414           /* if (unitblock<=0, {"this should never happen".postln}); */
415 
416           /* p->repeats is the total number of repeats, including
417              initial statement */
418           /* usually 1 or 2  */
419           p->repeats = random_number(csound, 1, p->Numrepeats + 1);
420           /* take right arg as p->Numrepeats+1 if make param more sensible */
421 
422           unitproj = (p->repeats*unitb)+ unitd;
423 
424           /* should be same logic as old algorithm for numrepeats=2 */
425           while (unitproj> p->totalunits) {
426             p->repeats = p->repeats-1;
427 
428             if (p->repeats<=1) {
429               p->repeats = 1;
430               unitb = unitl;    /* at this point is an integer */
431             }
432 
433             unitproj = (p->repeats*unitb)+ unitd;
434           }
435 
436           /* convert integer to float */
437           p->unitblock = (MYFLT) unitb;
438 
439         }       /* end of stutter/no stutter */
440 
441         /* determine offset- this part is very different for the
442            csound version */
443         /* there is no random access possible any more, only have now
444            (and past) */
445 
446         /* we must determine how long in samples a repeat is */
447         /* must persist between calls to this function */
448 
449         p->repeatlengthsamp = roundoffint(p->unitblock*p->samplesperunit);
450 
451         p->repeatsampdone = 0;
452 
453         /* determine envelope size- default is always 0 if enveloping off,
454            128 if on; */
455 
456         /* envsize must be at most a quarter of repeatsamplelength */
457         if (p->Envelopingon ==1) {
458           if (p->repeatlengthsamp<256) {
459             p->envsize = p->repeatlengthsamp/4;
460           }
461         }
462       }
463 
464       /* AUDIO OUT- some changes for buffer access */
465       if (p->repeatsdone == 0) {
466 
467         out1 = p->ain1[i];      /* pass in directly to out */
468         out2 = p->ain2[i];
469 
470         /* ENVELOPING */
471         envmult = FL(1.0);
472 
473         /* envelope in */
474         if (p->repeatsampdone<p->envsize) {
475           /* used sinusoid- prefer exponential */
476           /* envmult = sin(PI*0.5*(((MYFLT)(p->repeatsampdone))/
477              (MYFLT)p->envsize)); */
478           envmult = (EXP((p->repeatsampdone)/
479                                 (p->envsize))-
480                      FL(1.0))/FL(1.7182818284590);
481         }
482 
483         /* envelope out if necessary */
484         if (p->repeatsampdone>=(p->repeatlengthsamp-p->envsize)) {
485    /* envmult = sin(PI*0.5*(((MYFLT)(p->repeatlengthsamp-p->repeatsampdone))/
486       (MYFLT)p->envsize)); */
487           MYFLT xx = p->envsize;
488           if (xx==FL(0.0)) xx = 0.001; /* JPff patch 2019 Apr 28 */
489           envmult = (EXP(((p->repeatlengthsamp-
490                                           p->repeatsampdone))/
491                                 (xx))-FL(1.0))/
492             FL(1.7182818284590);
493         }
494 
495         out1 *= envmult;
496         out2 *= envmult;
497         /* /ENVELOPING DONE */
498 
499         p->aout1[i] = out1;
500         p->aout2[i] = out2;
501 
502         if (p->repeats>1) {     /* if recording a repeat */
503           /* STEREO INTERLEAVED */
504           ((MYFLT*)(p->repeatbuffer.auxp))[2*p->repeatsampdone] = out1;
505           ((MYFLT*)(p->repeatbuffer.auxp))[2*p->repeatsampdone+1] = out2;
506         }
507 
508       }
509       else {    /* reading repeatbuffer for repeats */
510         p->aout1[i] = ((MYFLT*)(p->repeatbuffer.auxp))[2*p->repeatsampdone];
511         p->aout2[i] = ((MYFLT*)(p->repeatbuffer.auxp))[2*p->repeatsampdone+1];
512       }
513 
514       /* per sample accounting */
515       ++(p->repeatsampdone);
516 
517       /* if finished a cut, do accounting  */
518       if (p->repeatsampdone>=p->repeatlengthsamp) {
519         ++(p->repeatsdone);
520         p->repeatsampdone = 0;
521 
522         p->unitsdone = p->unitsdone+ p->unitblock;
523 
524         p->unitsleft = p->totalunits- p->unitsdone;
525 
526         /* to account for floating point errors in unitblock when  */
527         /* stuttering with stutterspeed > 1 */
528         if (p->stutteron && (p->repeatsdone== (p->repeats-1))) {
529           p->unitblock = p->unitsleft;
530         }
531       }
532     }
533     return OK;
534 }
535 
536 #define S(x)    sizeof(x)
537 
538 static OENTRY localops[] = {
539   { "bbcutm",S(BBCUTMONO), 0, 3, "a","aiiiiipop",
540                                  (SUBR)BBCutMonoInit, (SUBR)BBCutMono  },
541   { "bbcuts",S(BBCUTSTEREO), 0, 3, "aa","aaiiiiipop",
542                                (SUBR)BBCutStereoInit, (SUBR)BBCutStereo}
543 };
544 
bbcut_init_(CSOUND * csound)545 int32_t bbcut_init_(CSOUND *csound)
546 {
547     return csound->AppendOpcodes(csound, &(localops[0]),
548                                  (int32_t
549                                   ) (sizeof(localops) / sizeof(OENTRY)));
550 }
551 
552