1 /* VOSIM.C: VOice SIMulation implementation
2 
3    Copyright 2008 rasmus ekman
4 
5    rasmus ekman March 13, 2008, for Csound.
6 
7     This file is part of Csound.
8 
9     The Csound Library is free software; you can redistribute it
10     and/or modify it under the terms of the GNU Lesser General Public
11     License as published by the Free Software Foundation; either
12     version 2.1 of the License, or (at your option) any later version.
13 
14     Csound is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public
20     License along with Csound; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22     02110-1301 USA
23 */
24 
25 
26 /* Programmer note:
27  * Pre- and post-conditions are noted at function level.
28  * These are essential for the code to work at all.
29  * There are some complications because we accept weird user input:
30  * (a) kfund < 0: This is forced to positive - no point in "backward" events.
31  * (b) kform == 0: This leads to infinite length pulse, ie silence.
32  * (c) kform < 0: Table is read backward.
33  *     If table is symmetric, kform and -kform should give bit-identical outputs.
34  *
35  * Don't fiddle with the code unless you understand how the table read
36  * params pulsephs and pulseinc are used both to read table and to indicate that
37  * a new pulse should start, and that pulseinc can be negative.
38  */
39 
40 //#include "csdl.h"
41 #include "csoundCore.h"
42 #include "interlocks.h"
43 #include <math.h>
44 #include <limits.h>
45 
46 typedef struct {
47         OPDS h;
48         MYFLT *ar, *amp, *kfund, *kform, *kdamp, *knofpulse, *kpulsemul,
49               *iftab, *iskip;
50         FUNC *ftable;
51         int32 timrem;    /* samples left of event */
52         int32 pulstogo;  /* count of pulses to produce in burst */
53         int32 pulsephs;  /* index into table of this pulse (= MAXLEN / kform) */
54         int32 pulseinc;  /* increment in table of pulse */
55         MYFLT pulseamp;  /* amp of current pulse */
56         MYFLT ampdecay;  /* subtract from amp on new pulse */
57         MYFLT lenfact;   /* increase length of next pulse */
58 } VOSIM;
59 
60 
61 /* Post: unless skipping init, timrem == 0 */
vosimset(CSOUND * csound,VOSIM * p)62 int32_t vosimset(CSOUND* csound, VOSIM *p)
63 {
64     if (*p->iskip)
65       return OK;
66 
67     p->ftable = csound->FTFind(csound, p->iftab);
68     if (UNLIKELY(p->ftable == NULL)) {
69       return csound->InitError(csound, Str("vosim: pulse table not found"));
70     }
71 
72      p->timrem = p->pulstogo = p->pulsephs = p->pulseinc = 0;
73      p->pulseamp = p->ampdecay = p->lenfact = FL(0.0);
74      return OK;
75 }
76 
77 
78 /* Pre: timrem == 0.
79   * Post:
80   *    IF kform >= 0, pulsephs >= FMAXLEN.
81   *    ELSE pulsephs < 0.
82   *    timrem > 0.
83   */
vosim_event(CSOUND * csound,VOSIM * p)84 void vosim_event(CSOUND* csound, VOSIM *p)
85 {
86     MYFLT fundabs = FABS(*p->kfund);
87     /* count of pulses, (+1 since decr at start of pulse) */
88     p->pulstogo = 1+(int32)*p->knofpulse;
89     if (UNLIKELY(fundabs == FL(0.0))) {                /* infinitely long event */
90       p->timrem = INT_MAX;
91       csound->Warning(csound,
92                       Str("vosim: zero kfund. 'Infinite' length event generated."));
93     }
94     else {
95         p->timrem = (int32)(CS_ESR / fundabs);
96         if (UNLIKELY(p->timrem == 0)) {
97           p->timrem = CS_KSMPS;
98           p->pulstogo = 0;
99           csound->Warning(csound,
100                           Str("vosim: kfund (%f) > sr. Generating ksmps silence."),
101                           *p->kfund);
102         }
103     }
104     p->pulseinc = (int32)(*p->kform * csound->sicvt);
105     p->pulsephs = (p->pulseinc >= 0)? MAXLEN : -1;   /* starts a new pulse */
106     p->ampdecay = *p->kdamp;
107     /* increase initial amp, since it's reduced at pulse start */
108     p->pulseamp = *p->amp + p->ampdecay;
109     /* if negative, table is read alternately back-/forward */
110     p->lenfact  = *p->kpulsemul;
111     /* reduce table rate, since it's increased at pulse start */
112     if (p->lenfact != FL(0.0))
113       p->pulseinc /= p->lenfact;
114 }
115 
116 
117 /* Pre: pulsephs >= FMAXLEN OR pulsephs < 0.
118  * Post:
119  *    pulstogo is decremented or zero.
120  *    0 <= pulsephs < FMAXLEN.
121  */
vosim_pulse(CSOUND * csound,VOSIM * p)122 void vosim_pulse(CSOUND* csound, VOSIM *p)
123 {
124     IGN(csound);
125     int32 pulselen;
126     p->pulsephs &= PHMASK;
127     p->pulseinc *= p->lenfact;
128     /* If pulse can't fit in remaining event time, skip and generate silence */
129     pulselen = (p->pulseinc != FL(0.0))?
130                 (int32)FABS(FMAXLEN / p->pulseinc) : INT_MAX;
131     if (p->pulstogo-- <= 0 || pulselen > p->timrem) {
132       p->pulstogo = 0;
133     }
134     p->pulseamp -= p->ampdecay;
135 }
136 
137 
vosim(CSOUND * csound,VOSIM * p)138 int32_t vosim(CSOUND* csound, VOSIM *p)
139 {
140     uint32_t offset = p->h.insdshead->ksmps_offset;
141     uint32_t early  = p->h.insdshead->ksmps_no_end;
142     uint32_t n, nsmps = CS_KSMPS;
143     MYFLT *ar = p->ar;
144     MYFLT *ftdata;
145     int32  lobits;
146 
147     FUNC *ftp = p->ftable;
148     if (UNLIKELY(ftp == NULL)) goto err1;
149     ftdata = ftp->ftable;
150     lobits = ftp->lobits;
151 
152     if (UNLIKELY(offset)) memset(ar, '\0', offset*sizeof(MYFLT));
153     if (UNLIKELY(early)) {
154       nsmps -= early;
155       memset(&ar[nsmps], '\0', early*sizeof(MYFLT));
156     }
157     for (n=offset; n<nsmps; n++) {
158       /* new event? */
159       if (p->timrem == 0)
160         vosim_event(csound, p);
161 
162       /* new pulse? */
163       if (p->pulsephs >= MAXLEN || p->pulsephs < 0)
164         vosim_pulse(csound, p);
165 
166       if (p->pulstogo > 0) {
167         /* produce one sample */
168         p->pulsephs &= PHMASK;
169         ar[n] = *(ftdata + (p->pulsephs >> lobits)) * p->pulseamp;
170         --p->timrem;
171         p->pulsephs += p->pulseinc;
172       }
173       else {
174         /* silence after last pulse in burst: */
175         /* bypass regular synthesis and fill output with zeros */
176         while (p->timrem && n<nsmps) {
177           ar[n] = FL(0.0);
178           --p->timrem;
179           n++;
180         }
181         n--;
182       }
183     }
184     return OK;
185  err1:
186     return csound->PerfError(csound, &(p->h),
187                              Str("vosim: not initialised"));
188 }
189 
190 
191 /* ar   vosim   kamp, kFund, kForm, kDamp, kPulseCount, kPulseFactor,
192                 ifn [, iskip] */
193 
194 #define S(x)    sizeof(x)
195 
196 static OENTRY vosim_localops[] = {
197   { "vosim", S(VOSIM), TR, 3, "a", "kkkkkkio", (SUBR)vosimset, (SUBR)vosim }
198 };
199 
200 
201 LINKAGE_BUILTIN(vosim_localops)
202