1 /*
2     metro.c:
3 
4     Copyright (C) 2000 Gabriel Maldonado, (C) 2019 Gleb Rogozinsky
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 #include "stdopcod.h"
25 #include <math.h>
26 
27 typedef struct {
28         OPDS    h;
29         MYFLT   *sr, *xcps, *iphs;
30         double  curphs;
31         int32_t flag;
32 } METRO;
33 
34 // METRO2 ADDED BY GLEB ROGOZINSKY Oct 2019
35 typedef struct {
36         OPDS    h;
37         MYFLT   *sr, *xcps, *kswng, *iamp, *iphs;
38         double  amp2, curphs, curphs2, swng_init;
39         int32_t flag, flag2;
40 } METRO2;
41 //
42 
43 typedef struct  {
44         OPDS    h;
45         MYFLT   *trig, *ndx, *maxtics, *ifn, *outargs[VARGMAX];
46         int32_t             numouts, currtic, old_ndx;
47         MYFLT *table;
48 } SPLIT_TRIG;
49 
50 typedef struct  {
51         OPDS    h;
52         MYFLT   *ktrig, *kphs, *ifn, *args[VARGMAX];
53         MYFLT endSeq, *table, oldPhs;
54         int32_t numParm, endIndex, prevIndex, nextIndex ;
55         MYFLT prevActime, nextActime;
56         int32_t initFlag;
57 
58 } TIMEDSEQ;
59 
metro_set(CSOUND * csound,METRO * p)60 static int32_t metro_set(CSOUND *csound, METRO *p)
61 {
62     double phs = *p->iphs;
63     int32  longphs;
64 
65     if (phs >= 0.0) {
66       if (UNLIKELY((longphs = (int32)phs)))
67         csound->Warning(csound, Str("metro:init phase truncation"));
68       p->curphs = (MYFLT)phs - (MYFLT)longphs;
69     }
70     p->flag=1;
71     return OK;
72 }
73 
metro(CSOUND * csound,METRO * p)74 static int32_t metro(CSOUND *csound, METRO *p)
75 {
76     double      phs= p->curphs;
77     IGN(csound);
78     if (phs == 0.0 && p->flag) {
79       *p->sr = FL(1.0);
80       p->flag = 0;
81     }
82     else if ((phs += *p->xcps * CS_ONEDKR) >= 1.0) {
83       *p->sr = FL(1.0);
84       phs -= 1.0;
85       p->flag = 0;
86     }
87     else
88       *p->sr = FL(0.0);
89     p->curphs = phs;
90     return OK;
91 }
92 
93 /* GLEB ROGOZINSKY Oct 2019
94    Opcode metro2 in addition to 'classic' metro opcode,
95    allows swinging with possibiliy of setting its own amplitude value
96 */
metro2_set(CSOUND * csound,METRO2 * p)97 static int32_t metro2_set(CSOUND *csound, METRO2 *p)
98 {
99     double phs = *p->iphs;
100     double swng = *p->kswng;
101     int32  longphs;
102     p->amp2 = *p->iamp;
103 
104     if (phs >= 0.0) {
105       if (UNLIKELY((longphs = (int32)phs)))
106         csound->Warning(csound, Str("metro2:init phase truncation"));
107       p->curphs = (MYFLT)phs - (MYFLT)longphs;
108       p->curphs2 = (MYFLT)phs - (MYFLT)longphs + 1.0 - (MYFLT)swng;
109     }
110     p->flag = 1;
111     p->flag2 = 1;
112     p->swng_init = (MYFLT)swng;
113     return OK;
114 }
115 
metro2(CSOUND * csound,METRO2 * p)116 static int32_t metro2(CSOUND *csound, METRO2 *p)
117 {
118     double      phs= p->curphs;
119     double      phs2= p->curphs2;
120     double      phs2_init = p->swng_init;
121     double      amp2= p->amp2;
122     double      swng= *p->kswng;
123     IGN(csound);
124 // MAIN TICK
125     if (phs == 0.0 && p->flag) {
126       *p->sr = FL(1.0);
127       p->flag = 0;
128     }
129     else if ((phs += *p->xcps * CS_ONEDKR * 0.5) >= 1.0 ) {
130       *p->sr = FL(1.0);
131       phs -= 1.0;
132       p->flag = 0;
133     }
134     else
135       *p->sr = FL(0.0);
136     p->curphs = phs;
137 
138 // SWINGING TICK
139     if (phs2 == 0.0 && p->flag2) {
140       *p->sr = FL(amp2);
141       p->flag2 = 0;
142     }
143     else if ((phs2 += *p->xcps * CS_ONEDKR * 0.5) >= (1.0 + swng - phs2_init) ) {
144       *p->sr = FL(amp2);
145       phs2 -= 1.0;
146       p->flag2 = 0;
147     }
148     p->curphs2 = phs2;
149 
150     return OK;
151 }
152 //
153 
split_trig_set(CSOUND * csound,SPLIT_TRIG * p)154 static int32_t split_trig_set(CSOUND *csound,   SPLIT_TRIG *p)
155 {
156 
157     /* syntax of each table element:
158        numtics_elem1,
159        tic1_out1, tic1_out2, ... , tic1_outN,
160        tic2_out1, tic2_out2, ... , tic2_outN,
161        tic3_out1, tic3_out2, ... , tic3_outN,
162        .....
163        ticN_out1, ticN_out2, ... , ticN_outN,
164 
165        numtics_elem2,
166        tic1_out1, tic1_out2, ... , tic1_outN,
167        tic2_out1, tic2_out2, ... , tic2_outN,
168        tic3_out1, tic3_out2, ... , tic3_outN,
169        .....
170        ticN_out1, ticN_out2, ... , ticN_outN,
171 
172     */
173 
174     FUNC *ftp;
175     if (UNLIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) == NULL)) {
176       return csound->InitError(csound, Str("splitrig: incorrect table number"));
177     }
178     p->table = ftp->ftable;
179     p->numouts =  p->INOCOUNT-4;
180     p->currtic = 0;
181     return OK;
182 }
183 
split_trig(CSOUND * csound,SPLIT_TRIG * p)184 static int32_t split_trig(CSOUND *csound, SPLIT_TRIG *p)
185 {
186      IGN(csound);
187     int32_t j;
188     int32_t numouts =  p->numouts;
189     MYFLT **outargs = p->outargs;
190 
191     if (*p->trig) {
192       int32_t ndx = (int32_t) *p->ndx * (numouts * (int32_t) *p->maxtics + 1);
193       int32_t numtics =  (int32_t) p->table[ndx];
194       MYFLT *table = &(p->table[ndx+1]);
195       int32_t kndx = (int32_t) *p->ndx;
196       int32_t currtic;
197 
198       if (kndx != p->old_ndx) {
199         p->currtic = 0;
200         p->old_ndx = kndx;
201       }
202       currtic = p->currtic;
203 
204       for (j = 0; j < numouts; j++)
205         *outargs[j] = table[j +  currtic * numouts ];
206 
207       p->currtic = (currtic +1) % numtics;
208 
209     }
210 
211     else { // Maybe a memset?
212       for(j =0; j< numouts; j++)
213         *outargs[j] = FL(0.0);
214     }
215     return OK;
216 }
217 
timeseq_set(CSOUND * csound,TIMEDSEQ * p)218 static int32_t timeseq_set(CSOUND *csound, TIMEDSEQ *p)
219 {
220     FUNC *ftp;
221     MYFLT *table;
222     uint32_t j;
223     if (UNLIKELY((ftp = csound->FTnp2Finde(csound, p->ifn)) == NULL))  return NOTOK;
224     table = p->table = ftp->ftable;
225     p->numParm = p->INOCOUNT-2; /* ? */
226     for (j = 0; j < ftp->flen; j+= p->numParm) {
227       if (table[j] < 0) {
228         p->endSeq = table[j+1];
229         p->endIndex = j/p->numParm;
230         break;
231       }
232     }
233     p->initFlag = 1;
234     return OK;
235 }
236 
timeseq(CSOUND * csound,TIMEDSEQ * p)237 static int32_t timeseq(CSOUND *csound, TIMEDSEQ *p)
238 {
239      IGN(csound);
240     MYFLT *table = p->table, minDist = CS_ONEDKR;
241     MYFLT phs = *p->kphs, endseq = p->endSeq;
242     int32_t  j,k, numParm = p->numParm, endIndex = p->endIndex;
243     while (phs > endseq)
244       phs -=endseq;
245     while (phs < 0 )
246       phs +=endseq;
247 
248     if (p->initFlag) {
249     prev:
250       for (j=0,k=endIndex; j < endIndex; j++, k--) {
251         if (table[j*numParm + 1] > phs ) {
252           p->nextActime = table[j*numParm + 1];
253           p->nextIndex = j;
254           p->prevActime = table[(j-1)*numParm + 1];
255           p->prevIndex = j-1;
256           break;
257         }
258         if (table[k*numParm + 1] < phs ) {
259           p->nextActime = table[(k+1)*numParm + 1];
260           p->nextIndex = k+1;
261           p->prevActime = table[k*numParm + 1];
262           p->prevIndex = k;
263           break;
264         }
265       }
266       if (phs == p->prevActime&& p->prevIndex != -1 )  {
267         *p->ktrig = 1;
268         for (j=0; j < numParm; j++) {
269           *p->args[j]=table[p->prevIndex*numParm + j];
270         }
271       }
272       else if (phs == p->nextActime && p->nextIndex != -1 )  {
273         *p->ktrig = 1;
274         for (j=0; j < numParm; j++) {
275           *p->args[j]=table[p->nextIndex*numParm + j];
276         }
277       }
278       /*p->oldPhs = phs; */
279       p->initFlag=0;
280     }
281     else {
282       if (phs > p->nextActime || phs < p->prevActime) {
283         for (j=0; j < numParm; j++) {
284           *p->args[j]=table[p->nextIndex*numParm + j];
285         }
286         if (table[p->nextIndex*numParm] != -1) /* if it is not end locator */
287           /**p->ktrig = 1; */
288           *p->ktrig = table[p->nextIndex*numParm + 3];
289         if (phs > p->nextActime) {
290           if (p->prevIndex > p->nextIndex && p->oldPhs < phs) {
291             /* there is a phase jump */
292             *p->ktrig = 0;
293             goto fine;
294           }
295           if (fabs(phs-p->nextActime) > minDist)
296             goto prev;
297 
298           p->prevActime = table[p->nextIndex*numParm + 1];
299           p->prevIndex = p->nextIndex;
300           p->nextIndex = (p->nextIndex + 1) % endIndex;
301           p->nextActime = table[p->nextIndex*numParm + 1];
302         }
303         else {
304           if (fabs(phs-p->nextActime) > minDist)
305             goto prev;
306 
307           p->nextActime = table[p->prevIndex*numParm + 1]; /*p->nextActime+1; */
308           p->nextIndex = p->prevIndex;
309           p->prevIndex = (p->prevIndex - 1);
310           if (p->prevIndex < 0) {
311             p->prevIndex += p->endIndex;
312           }
313           p->prevActime = table[p->prevIndex*numParm + 1]; /*p->nextActime+1; */
314         }
315       }
316       else
317         *p->ktrig = 0;
318     fine:
319       p->oldPhs = phs;
320     }
321     return OK;
322 }
323 
324 #define S(x)    sizeof(x)
325 
326 static OENTRY localops[] = {
327   { "metro",  S(METRO),  0,  3,      "k", "ko",  (SUBR)metro_set, (SUBR)metro     },
328   { "metro2", S(METRO2), 0,  3,      "k", "kkpo", (SUBR)metro2_set, (SUBR)metro2  },
329   { "splitrig", S(SPLIT_TRIG), 0, 3, "",  "kkiiz",
330                                         (SUBR)split_trig_set, (SUBR)split_trig },
331   { "timedseq",S(TIMEDSEQ), TR, 3, "k", "kiz", (SUBR)timeseq_set, (SUBR)timeseq }
332 };
333 
metro_init_(CSOUND * csound)334 int32_t metro_init_(CSOUND *csound)
335 {
336     return csound->AppendOpcodes(csound, &(localops[0]),
337                                  (int32_t
338                                   ) (sizeof(localops) / sizeof(OENTRY)));
339 }
340