1 /*
2     grain4.c:
3 
4     Copyright (C) 1994, 1995 Allan S C Lee, 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 /* Verson 4.0 -    Mar 95                               */
25 /* Verson 4.1 - 10 Mar 95                               */
26 /*        Lifted restriction on pitch, now accept       */
27 /*        anything greater than zero                    */
28 /*        Improved code in handling the warp-round      */
29 /*        pointer.                                      */
30 /* Verson 4.2 - 20 Apr 95                               */
31 /*        Add optional parameter ifnenv                 */
32 /*        Function table to be used for the shape of the*/
33 /*        envelop rise and decade curve                 */
34 /* Minor changes by John Fitch Dec 1995                 */
35 
36 // #include "csdl.h"
37 #include "csoundCore.h"
38 #include "interlocks.h"
39 #include "grain4.h"
40 #include <math.h>
41 
42 #define        RNDMUL  15625L
43 
44 static MYFLT grand(GRAINV4 *);
45 
grainsetv4(CSOUND * csound,GRAINV4 * p)46 static int32_t grainsetv4(CSOUND *csound, GRAINV4 *p)
47 {
48     FUNC        *ftp, *ftp_env;
49     int32_t         nvoice, cnt;
50     int32_t    tmplong1, tmplong2;
51     MYFLT       tmpfloat1;
52     MYFLT       pitch[4];
53 
54     /* call ftfind() to get the function table...*/
55     if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) {
56       p->ftp = ftp;
57     }
58     else {
59       return csound->InitError(csound, Str("granule_set: "
60                                            "Unable to find function table"));
61     }
62 
63     /* call ftfind() to get the function table for the envelop...*/
64     if (*p->ifnenv > 0) {
65       if (LIKELY((ftp_env = csound->FTnp2Find(csound, p->ifnenv)) != NULL)) {
66         p->ftp_env = ftp_env;
67       }
68       else {
69         return csound->InitError(csound, Str("granule_set: Unable to find "
70                                              "function table for envelope"));
71       }
72     }
73 
74     if (UNLIKELY(*p->ivoice > MAXVOICE)) {
75       return csound->InitError(csound, Str("granule_set: Too many voices"));
76     }
77     if (UNLIKELY(*p->iratio <= 0)) {
78       return csound->InitError(csound, Str("granule_set: "
79                                            "iratio must be greater then 0"));
80     }
81     if (UNLIKELY((*p->imode != 0) && ((*p->imode != -1) && (*p->imode != 1)))) {
82       return csound->InitError(csound, Str("granule_set: "
83                                            "imode must be -1, 0 or +1"));
84     }
85     if (UNLIKELY(*p->ithd < 0)) {
86       return csound->InitError(csound, Str("granule_set: Illegal ithd, "
87                                            "must be greater than zero"));
88     }
89     if (UNLIKELY((*p->ipshift != 1) && (*p->ipshift!=2) && (*p->ipshift!=3) &&
90                  (*p->ipshift!=4) && (*p->ipshift!=0) )) {
91       return csound->InitError(csound, Str("granule_set: ipshift must be "
92                                            "integer between 0 and 4"));
93     }
94     if (UNLIKELY(((*p->ipshift >=1) && (*p->ipshift <=4)) &&
95                  (*p->ivoice < *p->ipshift))) {
96       return csound->InitError(csound, Str("granule_set: Not enough voices "
97                                            "for the number of pitches"));
98     }
99     if ( *p->ipshift !=FL(0.0) ) {
100       if (UNLIKELY(*p->ipitch1 < FL(0.0) )) {
101         return
102           csound->InitError(csound,
103                             Str("granule_set: ipitch1 must be greater then zero"));
104       }
105       if (UNLIKELY(*p->ipitch2 < FL(0.0) )) {
106         return
107           csound->InitError(csound,
108                             Str("granule_set: ipitch2 must be greater then zero"));
109       }
110       if (UNLIKELY(*p->ipitch3 < FL(0.0) )) {
111         return
112           csound->InitError(csound,
113                             Str("granule_set: ipitch3 must be greater then zero"));
114       }
115       if (UNLIKELY(*p->ipitch4 < FL(0.0) )) {
116         return
117           csound->InitError(csound,
118                             Str("granule_set: ipitch4 must be greater then zero"));
119       }
120     }
121 
122     if (UNLIKELY((*p->igskip < 0) || (*p->igskip * CS_ESR > ftp->flen) )) {
123       return csound->InitError(csound, Str("granule_set: must be positive and "
124                                            "less than function table length"));
125     }
126     if (UNLIKELY(*p->igskip_os < 0)) {
127       return csound->InitError(csound, Str("granule_set: "
128                                            "igskip_os must be greater then 0"));
129     }
130 
131     p->gstart = (int32)(*p->igskip * CS_ESR);
132     p->glength = (int32)(*p->ilength * CS_ESR);
133     p->gend = p->gstart + p->glength;
134 
135     if (UNLIKELY(*p->kgap < 0)) {
136       return csound->InitError(csound, Str("granule_set: "
137                                            "kgap must be greater then 0"));
138     }
139     if (UNLIKELY((*p->igap_os < 0) || (*p->igap_os > 100))) {
140       return csound->InitError(csound, Str("granule_set: "
141                                            "igap_os must be 0%% to 100%%"));
142     }
143     if (UNLIKELY(*p->kgsize < 0)) {
144       return csound->InitError(csound, Str("granule_set: "
145                                            "kgsize must be greater then 0"));
146     }
147     if (UNLIKELY((*p->igsize_os < 0) || (*p->igsize_os >100))) {
148       return csound->InitError(csound, Str("granule_set: "
149                                            "igsize_os must be 0%% to 100%%"));
150     }
151     if (UNLIKELY((*p->iatt < FL(0.0)) || (*p->idec < 0.0) ||
152                  ((*p->iatt + *p->idec) > FL(100.0)))) {
153       return
154         csound->InitError(csound,
155                           Str("granule_set: Illegal value of iatt and/or idec"));
156     } /* end if */
157 
158     /* Initialize random number generator */
159     if (*p->iseed >=0) {
160       p->grnd = (int16)(*p->iseed * FL(32768.0));       /* IV - Jul 11 2002 */
161     }
162 
163                                 /* Initialize variables....*/
164     p->gskip_os = (int32)(*p->igskip_os * CS_ESR);/* in number of samples */
165     p->gap_os = *p->igap_os / FL(100.0);
166     p->gsize_os = *p->igsize_os / FL(100.0);
167 
168     for (nvoice = 0; nvoice < *p->ivoice; nvoice++) {
169       p->fpnt[nvoice] = 0;
170       p->cnt[nvoice]  = 0;
171       p->phs[nvoice]  = FL(0.0);
172       p->gskip[nvoice] = (int32)(*p->igskip * CS_ESR);
173       p->gap[nvoice] = (int32)(*p->kgap * CS_ESR);
174     }
175 
176     if (*p->igap_os != 0) {
177       for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
178         p->gap[nvoice] += (int32)((MYFLT)p->gap[nvoice] * p->gap_os * grand(p));
179     }
180 
181     if (*p->imode == 0) {
182       for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
183         p->mode[nvoice] = (grand(p) < 0) ? -1 : 1;
184     }
185     else {
186       for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
187         p->mode[nvoice] = (int32)*p->imode;
188     }
189 
190     if ((*p->ipshift >=1) && (*p->ipshift <=4)) {
191       pitch[0] = *p->ipitch1;
192       pitch[1] = *p->ipitch2;
193       pitch[2] = *p->ipitch3;
194       pitch[3] = *p->ipitch4;
195       cnt = 0;
196       for (nvoice = 0; nvoice < *p->ivoice; nvoice++) {
197         p->pshift[nvoice] = pitch[cnt++];
198         cnt = (cnt < *p->ipshift) ? cnt : 0;
199       }
200     }
201     if (*p->ipshift == 0) {
202       for (nvoice = 0; nvoice < *p->ivoice; nvoice++) {
203         tmpfloat1 = grand(p);
204         p->pshift[nvoice] =
205           (tmpfloat1 <FL(0.0)) ? (tmpfloat1*FL(0.5))+FL(1.0) : tmpfloat1+1.0f;
206       }
207     }
208 
209     for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
210       p->gsize[nvoice] = (int32)(*p->kgsize * CS_ESR * p->pshift[nvoice]);
211 
212     if (*p->igsize_os != 0) {
213       for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
214         p->gsize[nvoice] += (int32)(p->gsize[nvoice] * p->gsize_os * grand(p));
215     }
216 
217     for (nvoice = 0; nvoice < *p->ivoice; nvoice++)
218       p->stretch[nvoice] = p->gsize[nvoice] + p->gap[nvoice];
219 
220     if (*p->igskip_os != 0)
221       for (nvoice = 0; nvoice < *p->ivoice; nvoice++) {
222         tmplong1 = ((p->gskip_os * grand(p)) + (MYFLT)p->gskip[nvoice]);
223         p->gskip[nvoice] =
224           (tmplong1 < p->gstart) ? p->gstart : tmplong1;
225         p->gskip[nvoice]=
226           ((p->gskip[nvoice]+p->stretch[nvoice])>(int32)p->gend) ?
227           (int32)p->gstart :
228           p->gskip[nvoice];
229       }
230 
231     if (*p->ithd != 0) {        /* Do thresholding.... */
232       tmplong2 = 0;
233       for (tmplong1=0; tmplong1< (int32_t) ftp->flen; tmplong1++)
234         if (fabs(ftp->ftable[tmplong1]) >= *p->ithd )
235           ftp->ftable[tmplong2++] = ftp->ftable[tmplong1];
236       ftp->flen = tmplong2;
237     }
238 
239     if (UNLIKELY(p->gend > (int32_t) ftp->flen)) {
240       return csound->InitError(csound, Str("granule_set: Illegal combination "
241                                            "of igskip and ilength"));
242     }
243 
244     nvoice = (int32_t)*p->ivoice;
245 
246     if (UNLIKELY(*p->ilength < (20 * *p->kgsize)))
247       csound->Warning(csound, Str("granule_set: "
248                                   "WARNING * ilength may be too short *\n"
249                                   "            ilength should be "
250                                   "greater than kgsize * max up\n"
251                                   "            pitch shift. Also, igsize_os "
252                                   "and igskip_os should\n"
253                                   "            be taken into consideration.\n"
254                                   "ilength is "
255                                   "%f Sec, kgsize is %f Sec\n"),
256                       *p->ilength, *p->kgsize);
257 
258     //p->clock = 0;               /* init clock */
259     return OK;
260 } /* end grainsetv4(p) */
261 
graingenv4(CSOUND * csound,GRAINV4 * p)262 static int32_t graingenv4(CSOUND *csound, GRAINV4 *p)
263 {
264     FUNC        *ftp, *ftp_env;
265     MYFLT       *ar, *ftbl, *ftbl_env=NULL;
266     uint32_t offset = p->h.insdshead->ksmps_offset;
267     uint32_t early  = p->h.insdshead->ksmps_no_end;
268     uint32_t n, nsmps = CS_KSMPS;
269     int32_t         nvoice;
270     int32       tmplong1, tmplong2, tmplong3, tmpfpnt, flen_env=0;
271     MYFLT       fract, v1, tmpfloat1;
272     int32       att_len, dec_len, att_sus;
273     MYFLT       envlop;
274 
275     /* Optimisations */
276     int32       gstart  = p->gstart;
277     int32       gend    = p->gend;
278     int32       glength = p->glength;
279     MYFLT       iratio  = *p->iratio;
280 
281  /* Recover parameters from previous call.... */
282    ftp = p->ftp;
283    if (UNLIKELY(p->ftp==NULL)) goto err1;          /* RWD fix */
284    ftbl = ftp->ftable;
285 
286    if (*p->ifnenv > 0) {
287      ftp_env = p->ftp_env;
288      flen_env = ftp_env->flen;
289      ftbl_env = ftp_env->ftable;
290    }
291 
292    /* Recover audio output pointer... */
293    ar   = p->ar;
294    if (UNLIKELY(offset)) memset(ar, '\0', offset*sizeof(MYFLT));
295    if (UNLIKELY(early)) {
296      nsmps -= early;
297      memset(&ar[nsmps], '\0', early*sizeof(MYFLT));
298    }
299    /* *** Start the loop .... *** */
300    for (n=offset; n<nsmps; n++) {
301                                 /* Optimisations */
302      int32      *fpnt = p->fpnt, *cnt = p->cnt, *gskip = p->gskip;
303      int32      *gap = p->gap, *gsize = p->gsize;
304      int32      *stretch = p->stretch, *mode = p->mode;
305      MYFLT      *pshift = p->pshift, *phs = p->phs;
306      ar[n] = FL(0.0);
307 
308      for (nvoice = 0; nvoice <  *p->ivoice ; nvoice++) {
309        if (*fpnt >= (*gsize -1)) {
310          ar[n] += 0;            /* Is this necessary?? */
311          *cnt +=1L;
312        }
313        else {
314          fract = *phs - *fpnt;
315 
316          if (*mode < 0) {
317            tmplong1 = *gskip - gstart;
318            if (*fpnt >= tmplong1) {
319              tmplong1= *fpnt - tmplong1;
320              tmplong2= tmplong1/glength;
321              tmplong1 -= tmplong2 * glength;
322              tmpfpnt = gend - tmplong1;
323            }
324            else
325              tmpfpnt = *gskip - *fpnt;
326          }
327          else {
328            tmplong1 = gend - *gskip;
329            if (*fpnt >= tmplong1) {
330              tmplong1= *fpnt - tmplong1;
331              tmplong2= tmplong1/glength;
332              tmplong1 -= tmplong2 * glength;
333              tmpfpnt = gstart + tmplong1;
334            }
335            else
336              tmpfpnt = *gskip + *fpnt;
337          }
338 
339          att_len = (int32)(*gsize * *p->iatt * FL(0.01));
340          dec_len = (int32)(*gsize * *p->idec * FL(0.01));
341          att_sus =  *gsize -  dec_len;
342 
343          if (*fpnt < att_sus) {
344            tmpfloat1 = (FL(1.0) * *fpnt) / att_len;
345            envlop = ((tmpfloat1 >=FL(1.0)) ? FL(1.0) : tmpfloat1);
346          }
347          else
348            envlop =
349              ((MYFLT)(dec_len - (MYFLT)(*fpnt - att_sus)))/((MYFLT)dec_len);
350 
351          v1 = *(ftbl + tmpfpnt);
352 
353          tmpfpnt = tmpfpnt + *mode;
354          if (tmpfpnt < gstart)
355            tmpfpnt = gend - (gstart - tmpfpnt) + 1;
356          if (tmpfpnt > gend)
357            tmpfpnt = gstart + (tmpfpnt - gend) - 1;
358 
359          if (*p->ifnenv > 0) {
360            tmplong3 = (int32)(envlop * flen_env) -1L;
361            envlop = *(ftbl_env + tmplong3);
362          }
363 
364          ar[n] +=(v1 + ( *(ftbl + tmpfpnt)   - v1) * fract ) * envlop ;
365 
366          *phs += *pshift;
367          *fpnt = (int32)*phs;
368          *cnt  = (int32)*phs;
369        } /* end if (*fpnt >= (*gsize -1)) */
370 
371        if (*cnt >= *stretch) {
372          *cnt = 0;
373          *fpnt= 0;
374          *phs = FL(0.0);
375 
376          /* pick up new values... */
377 
378          /* Use the old value of the pshift, gsize and gap */
379          /*           to determine the time advanced */
380          /*           *gskip+=
381                       ((*gsize / *pshift) +
382                       *gap) * iratio;
383                       */
384          *gskip += (int32)((*gsize / *pshift) * iratio);
385 
386          if (*p->igskip_os != 0)
387            *gskip  += (int32)(p->gskip_os * grand(p));
388 
389          if (*gskip >= gend) {
390            tmplong1 = *gskip - gend;
391            tmplong2 = tmplong1 /glength;
392            tmplong1 -= tmplong2 * glength;
393            *gskip = gstart + tmplong1;
394          }
395 
396          if (*gskip < gstart) *gskip = gstart;
397 
398          if (*p->imode == 0) {
399            *mode = (grand(p) < 0) ? -1 : 1;
400          }
401 
402          if (*p->ipshift == 0) {
403            tmpfloat1 = grand(p);
404            *pshift = (tmpfloat1 < FL(0.0)) ?
405              (tmpfloat1*FL(0.5))+FL(1.0) : tmpfloat1+FL(1.0);
406          }
407 
408          *gap = (int32)(*p->kgap * CS_ESR);
409          if (*p->igap_os != 0) {
410            *gap += (int32)((*gap * p->gap_os) * grand(p));
411          }
412 
413          *gsize = (int32)(*p->kgsize * CS_ESR * *pshift);
414          if (*p->igsize_os != 0)
415            *gsize += (int32)((*gsize * p->gsize_os) * grand(p));
416 
417          *stretch = *gsize + *gap;
418 
419        }
420        fpnt++; cnt++; gskip++; gap++; gsize++;
421        stretch++; mode++; pshift++; phs++;
422      }
423   /* p->clock++; */
424      ar[n] *= *p->xamp;     /* increment audio pointer and multiply the xamp */
425    }
426    return OK;
427  err1:
428    return csound->PerfError(csound, &(p->h),
429                             Str("grain4: not initialised"));
430 
431 } /* end graingenv4(p) */
432 
433 /* Function return a float random number between -1 to +1 */
grand(GRAINV4 * p)434 static MYFLT grand( GRAINV4 *p)
435 {
436    p->grnd *= (int32_t
437                )RNDMUL;
438    p->grnd += 1;
439    return ((MYFLT) p->grnd * DV32768);  /* IV - Jul 11 2002 */
440 } /* end grand(p) */
441 
442 #define S(x)    sizeof(x)
443 
444 
445 
446 
447 
448 
449 static OENTRY grain4_localops[] = {
450   { "granule", S(GRAINV4), TR, 3, "a", "xiiiiiiiiikikiiivppppo",
451              (SUBR)grainsetv4, (SUBR)graingenv4},
452 };
453 
454 LINKAGE_BUILTIN(grain4_localops)
455