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