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