1 // fourKlives.c
2 // weed plugin to generate parametric audio
3 // (c) G. Finch (salsaman) 2012
4 //
5 // released under the GNU GPL 3 or later
6 // see file COPYING or www.gnu.org for details
7 
8 //#define DEBUG
9 
10 ///////////////////////////////////////////////////////////////////
11 
12 static int package_version = 1; // version of this package
13 
14 //////////////////////////////////////////////////////////////////
15 
16 #define NEED_AUDIO
17 
18 #ifndef NEED_LOCAL_WEED_PLUGIN
19 #include <weed/weed-plugin.h>
20 #include <weed/weed-utils.h> // optional
21 #include <weed/weed-plugin-utils.h> // optional
22 #else
23 #include "../../libweed/weed-plugin.h"
24 #include "../../libweed/weed-utils.h" // optional
25 #include "../../libweed/weed-plugin-utils.h" // optional
26 #endif
27 
28 #include "weed-plugin-utils.c"
29 
30 /////////////////////////////////////////////////////////////
31 
32 /*
33     The implementation of Syna functions
34 
35     - Marq + Antti
36 */
37 
38 // ported to LiVES by salsaman@gmail.com
39 
40 //#define MONO
41 
42 #include <math.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/types.h>
47 #include <dirent.h>
48 #include "syna.h"
49 
50 #define MAX_TUNES 1024
51 #define MAX_TUNELEN 1024
52 #define TUNE_DIR "data/fourKlives/songs/"
53 
54 static char *tunes[MAX_TUNES];
55 
set_tempo(_sdata * sdata,int tempo)56 static void set_tempo(_sdata *sdata, int tempo) {
57   sdata->update = sdata->new_update = 6 * sdata->global / (tempo * 10 / 25);
58   //        ekolen=update*3;
59 }
60 
61 
set_base_freq(_sdata * sdata,int freq)62 static void set_base_freq(_sdata *sdata, int freq) {
63   //base_freq=(freq<<13)/BFREQ;
64   sdata->base_freq = BFREQ + freq;
65   if (sdata->base_freq <= 0) sdata->base_freq = 1;
66 }
67 
68 
syna_init(_sdata * sdata,int freq)69 static int syna_init(_sdata *sdata, int freq) {
70   register int n, i;
71   sdata->module = NULL;
72   sdata->base_freq = BFREQ;
73   sdata->maxtracks = 0;
74 
75   for (n = 0; n < WAVES; n++) {
76     sdata->aalto[n] = NULL;
77   }
78 
79   for (n = 0; n < INSTR; n++) {
80     sdata->echo[n] = NULL;
81     sdata->instr[n] = NULL;
82     sdata->pi[n] = 0;
83   }
84 
85   sdata->slen = freq / BFREQ;
86   sdata->global = freq;
87   sdata->counter = 0;
88 
89   /* Generate lower octaves from 6 */
90   for (n = 4; n >= 0; n--)
91     for (i = 0; i < 12; i++)
92       notei[n * 12 + i] = notei[(n + 1) * 12 + i] / 2;
93 
94   /* Generate the waveforms */
95   for (n = 0; n < WAVES; n++) {
96     if (n != KOHINA) {
97       sdata->aalto[n] = weed_malloc(sdata->slen * sizeof(float));
98       if (sdata->aalto[n] == NULL) {
99         return WEED_ERROR_MEMORY_ALLOCATION;
100       }
101       weed_memset(sdata->aalto[n], 0, sdata->slen * sizeof(float));
102     }
103   }
104 
105   for (n = 0; n < sdata->slen; n++) {
106     sdata->aalto[KANTTI][n] = (n < sdata->slen / 2) ? -1.0 : 1.0;
107     sdata->aalto[SINI][n]  = sin(n * 2.0 * M_PI / (float)sdata->slen);
108     sdata->aalto[SAHA][n]  = -1.0 + fmod(1.0 + 2.0 * n / (float)sdata->slen, 2.0);
109   }
110 
111   /* Noise needs to be longer */
112   sdata->aalto[KOHINA] = weed_malloc(sdata->global * sizeof(float));
113   if (sdata->aalto[KOHINA] == NULL) {
114     return WEED_ERROR_MEMORY_ALLOCATION;
115   }
116 
117   for (n = 0; n < sdata->global; n++)
118     sdata->aalto[KOHINA][n] = (rand() % 2000 - 1000) / (float)1000.0;
119 
120   /* Set starting values */
121   for (n = 0; n < INSTR; n++) {
122     sdata->live_row[n] = 0;
123     sdata->new_live_row[n] = 0;
124 
125     sdata->off[n] = -1;
126     sdata->plus[n] = 0;
127     sdata->trak[n][0] = END;
128     sdata->ti[n] = -1;
129     sdata->eko[n] = 0;
130     sdata->pan[n] = (n & 1) ? 128 - 64 : 128 + 64;
131 #ifdef MONO
132     sdata->pan[n] = 128;
133 #endif
134     sdata->pola[n] = 0;
135     sdata->vol[n] = 255;
136     sdata->prev[n] = 0;
137     sdata->slide[n] = 0;
138   }
139   return WEED_SUCCESS;
140 }
141 
142 
143 #ifndef TINY
syna_load(_sdata * sdata,const char * tune)144 static int syna_load(_sdata *sdata, const char *tune) {
145   FILE    *f;
146   long    length;
147 
148   int retval;
149 
150   /* Read the file and call syna_get */
151   f = fopen(tune, "rb");
152   if (f == NULL) return WEED_ERROR_REINIT_NEEDED;
153   fseek(f, 0, SEEK_END);
154   length = ftell(f);
155   fseek(f, 0, SEEK_SET);
156   sdata->module = weed_malloc(length + 1);
157   retval = fread(sdata->module, 1, length, f);
158   sdata->module[length] = 0; /* String ends in zero */
159   fclose(f);
160 
161   if (retval < length) return WEED_ERROR_REINIT_NEEDED;
162 
163   return (syna_get(sdata));
164 }
165 #endif
166 
167 
syna_get(_sdata * sdata)168 static int syna_get(_sdata *sdata) {
169   static char *rows[MAXR], *key, *tnp;
170   int tmp, a, d, s, r, mod, sweep, wave, patt, track, note, wave_mod;
171 
172   register int i, j, n;
173 
174 #ifndef TINY
175   cleanup(sdata->module);  /* You better have a clean module in intros... */
176 #endif
177 
178   /* Extract rows */
179   for (n = 0; n < MAXR; n++) {
180     rows[n] = strtok((n) ? NULL : sdata->module, "\n");
181     if (rows[n] == NULL)
182       break;
183   }
184 
185   /* Extract data from rows */
186   for (i = 0; i < n; i++) {
187     if (rows[i][0] == '#')
188       continue;
189 
190     key = strtok(rows[i], ":");
191 
192     if (key) {
193       if (!strcmp(key, "bpm")) {
194         tmp = atoi(strtok(NULL, ":")) * 10 / 25;
195         sdata->song_bpm = tmp;
196         sdata->new_update = sdata->update = 6 * sdata->global / tmp;
197         sdata->ekolen = sdata->update * 3;
198       }
199       if (key[0] == 'i') {
200         /* Get instrument number and wave form */
201         tmp = atoi(&key[1]);
202         if (tmp < INSTR) {
203           if (sdata->echo[tmp] != NULL) weed_free(sdata->echo[tmp]);
204           sdata->echo[tmp] = weed_malloc(sdata->ekolen * sizeof(int));
205           if (sdata->echo[tmp] == NULL) {
206             return WEED_ERROR_MEMORY_ALLOCATION;
207           }
208           weed_memset(sdata->echo[tmp], 0, sdata->ekolen * sizeof(int));
209         }
210 
211         tnp = strtok(NULL, ",");
212 
213         wave = 0;
214 
215         if (!strcmp(tnp, "kantti") || !strcmp(tnp, "square"))
216           wave = 0;
217         else if (!strcmp(tnp, "sini") || !strcmp(tnp, "sin"))
218           wave = 1;
219         else if (!strcmp(tnp, "saha") || !strcmp(tnp, "saw"))
220           wave = 2;
221         else if (!strcmp(tnp, "kohina") || !strcmp(tnp, "noise"))
222           wave = 3;
223 
224         /* Get ADSR */
225         a = atoi(strtok(NULL, ","));
226         d = atoi(strtok(NULL, ","));
227         s = atoi(strtok(NULL, ","));
228         r = atoi(strtok(NULL, ","));
229 
230         if (tmp < INSTR) {
231           sdata->len[tmp] = a + d + s + r + 1;
232           sdata->instr[tmp] = weed_malloc(sdata->len[tmp] * sizeof(int));
233           if (sdata->echo[tmp] == NULL) {
234             return WEED_ERROR_MEMORY_ALLOCATION;
235           }
236           weed_memset(sdata->instr[tmp], 0, sdata->len[tmp]*sizeof(int));
237         }
238 
239         mod = atoi(strtok(NULL, ","));
240         if ((tnp = strtok(NULL, ",")))
241           sweep = atoi(tnp);
242         else
243           sweep = 0;
244         if ((tnp = strtok(NULL, ",")))
245           if (tmp < INSTR) sdata->pan[tmp] = (float)atoi(tnp) * 255 / 100;
246         if ((tnp = strtok(NULL, ",")))
247           if (tmp < INSTR) sdata->pola[tmp] = atoi(tnp) * 255 / 100;
248 
249         wave_mod = 1;
250 
251         if ((tnp = strtok(NULL, ","))) {
252           if (!strcmp(tnp, "kantti") || !strcmp(tnp, "square"))
253             wave_mod = 0;
254           else if (!strcmp(tnp, "sini") || !strcmp(tnp, "sin"))
255             wave_mod = 1;
256           else if (!strcmp(tnp, "saha") || !strcmp(tnp, "saw"))
257             wave_mod = 2;
258           else if (!strcmp(tnp, "kohina") || !strcmp(tnp, "noise"))
259             wave_mod = 3;
260         }
261 
262         if (tmp < INSTR) adsr(sdata, a, d, s, r, mod, sweep, tmp, wave, wave_mod);
263       }
264 
265       if (key[0] == 'p') { /* Handle pattern */
266         patt = atoi(&key[1]);
267         j = 0;
268         while (1) {
269           tnp = strtok(NULL, ",");
270           if (tnp != NULL) {
271             note = 0;
272             for (tmp = 0; notes[tmp][0] != '0'; tmp++)
273               if (!strcmp(notes[tmp], tnp))
274                 note = notei[tmp];
275             sdata->ptn[patt][j] = note;
276             if (note == VOL || note == SLIDE) {
277               j++;
278               sdata->ptn[patt][j] = atoi(strtok(NULL, ","));
279             }
280 
281             j++;
282           } else {
283             sdata->ptn[patt][j] = END;
284             break;
285           }
286         }
287       }
288 
289       if (key[0] == 't') { /* Handle track */
290         track = atoi(&key[1]);
291         j = 0;
292         while (1) {
293           tnp = strtok(NULL, ",");
294           if (tnp != NULL) {
295             if (!strcmp(tnp, "loop"))
296               sdata->trak[track][j] = LOOP;
297             else {
298               patt = atoi(&tnp[1]);
299               sdata->trak[track][j] = patt;
300             }
301             if (j > sdata->maxtracks) sdata->maxtracks = j;
302             j++;
303           } else {
304             sdata->trak[track][j] = END;
305             break;
306           }
307         }
308       }
309     }
310   }
311 
312   return WEED_SUCCESS;
313 }
314 
315 #if 0
316 // added by Antti Silvast for setting all live the rows
317 static void set_live_rows(_sdata *sdata, int *the_rows) {
318   register int i;
319 
320   for (i = 0; i < INSTR; i++) {
321     sdata->new_live_row[i] = the_rows[i];
322     //pi[i]=0;
323   }
324 }
325 #endif
326 
327 // added by Antti Silvast for setting just one live row
set_live_row(_sdata * sdata,int channel,int the_row)328 static void set_live_row(_sdata *sdata, int channel, int the_row) {
329   sdata->new_live_row[channel] = the_row;
330 }
331 
332 
syna_play(_sdata * sdata,float ** dest,int length,int channels,int interleave)333 static void syna_play(_sdata *sdata, float **dest, int length, int channels, int interleave) {
334   int note, li; // li is the "live i"
335   int left, right, smp;
336   int ceko, c1eko;
337 
338   register int i, n;
339 
340   ceko = sdata->counter % sdata->ekolen;
341 
342   for (n = 0; n < length; n++, sdata->counter++) {
343     c1eko = ceko + 1;
344     if (c1eko == sdata->ekolen)
345       c1eko = 0;
346 
347     /* New row */
348     if (sdata->counter > sdata->update) {
349       sdata->counter = 0;
350       sdata->update = sdata->new_update;
351       for (i = 1; sdata->trak[i][0] != END; i++) {
352         li = sdata->live_row[i];
353         //li=i;
354         if (li == END)
355           continue;
356         sdata->pi[i]++;
357 
358         if (li == -1 || sdata->ptn[sdata->trak[i][li]][sdata->pi[i]] == END) {
359           li = sdata->live_row[i] = sdata->new_live_row[i];
360           //ti[i]++;
361           /*
362             if(trak[i][li]==LOOP)
363             li=0;
364             if(trak[i][li]==END)
365             li=END;
366           */
367           sdata->pi[i] = 0;
368         }
369         //printf("%d=%d ",i,trak[i][li]);
370 
371         if (li != END) {
372           if ((note = sdata->ptn[sdata->trak[i][li]][sdata->pi[i]])) {
373             switch (note) {
374             case STOP:
375               sdata->off[i] = -1;
376               break;
377             case ECHON:
378               sdata->eko[i] = 1;
379               break;
380             case ECHOFF:
381               sdata->eko[i] = 0;
382               break;
383             case VOL:
384               sdata->pi[i]++;
385               sdata->vol[i] = sdata->ptn[sdata->trak[i][li]][sdata->pi[i]] * 255 / 100;
386               break;
387             case SLIDE:
388               sdata->pi[i]++;
389               sdata->slide[i] = sdata->ptn[sdata->trak[i][li]][sdata->pi[i]] * 164 / 1000;
390               break;
391             default:
392               sdata->plus[i] = note << 13;
393               //plus[i]/=BFREQ;
394               sdata->off[i] = 0;
395             }
396           }
397         }
398       }
399 
400       //printf("\n");
401     }
402 
403 #define MARQ_ARM_OPT
404 
405 #ifndef MARQ_ARM_OPT
406     /* Sum the instruments */
407     left = right = 0;
408     for (i = 1; sdata->trak[i][0] != END; i++) {
409       smp = sdata->echo[i][(sdata->counter + 1) % sdata->ekolen];
410 
411       sdata->echo[i][sdata->counter % sdata->ekolen] = sdata->echo[i][(sdata->counter + 1) % sdata->ekolen] * 6 / 10;
412       if (sdata->off[i] >= 0) {
413         smp += sdata->instr[i][sdata->off[i] >> 13];
414 
415         if (sdata->eko[i])
416           sdata->echo[i][sdata->counter % sdata->ekolen] = smp * 2 / 10;
417 
418         sdata->off[i] += (sdata->plus[i] + sdata->base_speed);
419         sdata->plus[i] += sdata->slide[i];
420         if ((sdata->off[i] >> 13) >= sdata->len[i] || sdata->off[i] < 0)
421           sdata->off[i] = -1;
422       }
423 
424       if (sdata->pola[i])
425         smp = (smp * (255 ^ sdata->pola[i]) >> 8) + (sdata->prev[i] * sdata->pola[i] >> 8);
426       sdata->prev[i] = smp;
427 
428       smp = smp * sdata->vol[i] >> 8;
429       left += (255 ^ sdata->pan[i]) * smp >> 8;
430       right += sdata->pan[i] * smp >> 8;
431     }
432 
433     if (left < -98301)
434       left = -98301;
435     if (left > 98301)
436       left = 98301;
437     dest[0][n] = (float)left / 98301.;
438 
439     if (channels == 2) {
440       if (right < -98301)
441         right = -98301;
442       if (right > 98301)
443         right = 98301;
444       dest[1][n] = (float)right / 98301.;
445     }
446 #else
447     // sum the instruments
448     left = right = 0;
449     for (i = 1; sdata->trak[i][0] != END; i++) {
450       smp = sdata->echo[i][c1eko];
451 
452       //fprintf(stderr,"ok %d\n",smp);
453 
454       sdata->echo[i][ceko] = smp * 19 >> 5;
455 
456       if (sdata->off[i] >= 0) {
457         smp += sdata->instr[i][sdata->off[i] >> 13];
458         //fprintf(stderr,"ok2 %d\n",smp);
459 
460         if (sdata->eko[i])
461           sdata->echo[i][ceko] = smp * 13 >> 6;
462 
463         sdata->off[i] += (sdata->plus[i] / sdata->base_freq);
464         sdata->plus[i] += sdata->slide[i] * sdata->base_freq;
465         if ((sdata->off[i] >> 13) >= sdata->len[i] || sdata->off[i] < 0)
466           sdata->off[i] = -1;
467       }
468 
469       if (sdata->pola[i])
470         smp = (smp * (255 ^ sdata->pola[i]) >> 8) + (sdata->prev[i] * sdata->pola[i] >> 8);
471       sdata->prev[i] = smp;
472       //fprintf(stderr,"ok23 %d\n",smp);
473 
474       smp = smp * sdata->vol[i] >> 8;
475       //fprintf(stderr,"ok24 %d\n",smp);
476       left += (255 ^ sdata->pan[i]) * smp >> 8;
477       if (channels == 2)
478         right += sdata->pan[i] * smp >> 8;
479     }
480 
481     if (left < -98301)
482       left = -98301;
483     else if (left > 98301)
484       left = 98301;
485     dest[0][n] = (float)(left * 21 >> 6) / 32767.;
486 
487 
488     if (channels == 2) {
489       if (right < -98301)
490         right = -98301;
491       else if (right > 98301)
492         right = 98301;
493       dest[1][n] = (float)(right * 21 >> 6) / 32767.;
494     }
495 
496     //fprintf(stderr,"vals %d and %d, %f and %f\n",left,right,(float)(left*21>>6)/32767.,(float)(right*21>>6)/32767.);
497 
498     ceko++;
499     if (ceko == sdata->ekolen)
500       ceko = 0;
501   }
502 #endif
503   }
504 
505 
506 
507   /* Make ADSR to instruments */
adsr(_sdata * sdata,int a,int d,int s,int r,int mod,int swp,int ins,int wave,int wave_mod)508   static void adsr(_sdata *sdata, int a, int d, int s, int r, int mod, int swp, int ins, int wave, int wave_mod) {
509     int n, modulo = sdata->slen, id = 0;
510     float  i = 0, vol = 0.0, dv, oh = 0.0, op, ip = 1, sweep;
511 
512     if (!a) a = 1;
513     if (!r) r = 1;
514 
515     if (wave == KOHINA)
516       modulo = sdata->global;
517 
518     if (mod) /* We modulate! */
519       if (wave_mod == 1)
520         op = mod / 100.0 * 2.0 * M_PI / (float)sdata->slen;
521       else
522         op = (float)mod / 100.0;
523     else
524       op = 0;
525 
526     sweep = (float)swp / 1000.0 / (float)sdata->slen;
527 
528     dv = 32767.0 / (float)a;
529     for (n = 0; n < a; n++, i += ip, ip += sweep, vol += dv, oh += op)
530       if (wave_mod != 1)
531         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sdata->aalto[wave_mod][((
532                                     int)oh) % modulo] : 1.0);
533       else
534         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sin(oh) : 1.0);
535 
536     for (n = 0; n < d; n++, i += ip, ip += sweep, vol -= dv, oh += op)
537       if (wave_mod != 1)
538         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sdata->aalto[wave_mod][((
539                                     int)oh) % modulo] : 1.0);
540       else
541         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sin(oh) : 1.0);
542 
543 
544     for (n = 0; n < s; n++, i += ip, ip += sweep, oh += op)
545       if (wave_mod != 1)
546         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sdata->aalto[wave_mod][((
547                                     int)oh) % modulo] : 1.0);
548       else
549         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sin(oh) : 1.0);
550 
551     dv = vol / (float)r;
552 
553     for (n = 0; n < r; n++, i += ip, ip += sweep, vol -= dv, oh += op)
554       if (wave_mod != 1)
555         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sdata->aalto[wave_mod][((
556                                     int)oh) % modulo] : 1.0);
557       else
558         sdata->instr[ins][id++] = vol * sdata->aalto[wave][((int)i) % modulo] * ((mod) ? sin(oh) : 1.0);
559   }
560 
561 
562   /* Fix the fscking Windoze/DOS newlines */
563 #ifndef TINY
cleanup(char * s)564   void cleanup(char *s) {
565     char *d = strdup(s);
566     for (; *d; d++)
567       if (*d != '\r' && *d != ' ') *s++ = *d;
568     *s = 0;
569   }
570 #endif
571 
572 
syna_deinit(_sdata * sdata)573   static void syna_deinit(_sdata *sdata) {
574     int n;
575 
576     if (!sdata) return;
577 
578     for (n = 0; n < WAVES; n++) {
579       if (sdata->aalto[n]) weed_free(sdata->aalto[n]);
580     }
581     for (n = 0; n < INSTR; n++) {
582       if (sdata->echo[n]) weed_free(sdata->echo[n]);
583     }
584     if (sdata->module) weed_free(sdata->module);
585   }
586 
587 
588   /////////////////////////////////////////////
589 
fourk_deinit(weed_plant_t * inst)590   static weed_error_t fourk_deinit(weed_plant_t *inst) {
591     _sdata *sdata = weed_get_voidptr_value(inst, "plugin_internal", NULL);
592     if (sdata) {
593       syna_deinit(sdata);
594       weed_free(sdata);
595       weed_set_voidptr_value(inst, "plugin_internal", NULL);
596     }
597     return WEED_SUCCESS;
598   }
599 
600 
fourk_init(weed_plant_t * inst)601   static weed_error_t fourk_init(weed_plant_t *inst) {
602     int retval;
603     int rate;
604 
605     _sdata *sdata;
606 
607     weed_plant_t *out_channel = weed_get_plantptr_value(inst, WEED_LEAF_OUT_CHANNELS, NULL);
608     weed_plant_t **in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
609 
610     char tune[MAX_TUNELEN];
611 
612     snprintf(tune, MAX_TUNELEN - 4, "%s%s", TUNE_DIR, tunes[weed_get_int_value(in_params[0], WEED_LEAF_VALUE, NULL)]);
613 
614     weed_free(in_params);
615 
616     sdata = (_sdata *)weed_malloc(sizeof(_sdata));
617 
618     if (sdata == NULL) return WEED_ERROR_MEMORY_ALLOCATION;
619 
620     weed_set_voidptr_value(inst, "plugin_internal", sdata);
621 
622     rate = weed_get_int_value(out_channel, WEED_LEAF_AUDIO_RATE, NULL);
623 
624     if ((retval = syna_init(sdata, rate)) != WEED_SUCCESS) {
625 #ifdef DEBUG
626       fprintf(stderr, "4k init failed\n");
627 #endif
628       fourk_deinit(inst);
629       return retval;
630     }
631 
632 #ifdef DEBUG
633     fprintf(stderr, "4k: loading tune %s\n", tune);
634 #endif
635 
636     if ((retval = syna_load(sdata, tune)) != WEED_SUCCESS) {
637 
638       sprintf(tune + strlen(tune), "%s", ".txt");
639 
640 #ifdef DEBUG
641       fprintf(stderr, "4k: loading tune %s\n", tune);
642 #endif
643 
644       if ((retval = syna_load(sdata, tune)) != WEED_SUCCESS) {
645 
646         fourk_deinit(inst);
647 #ifdef DEBUG
648         fprintf(stderr, "4k load failed\n");
649 #endif
650         return retval;
651       }
652     }
653 
654     //set_tempo(sdata,136.); // bpm: maybe 8. to 263. ?
655     //set_base_freq(sdata,272.); // 145. to 400.
656 
657     return WEED_SUCCESS;
658   }
659 
660 
fourk_process(weed_plant_t * inst,weed_timecode_t timestamp)661   static weed_error_t fourk_process(weed_plant_t *inst, weed_timecode_t timestamp) {
662     int chans, nsamps, inter;
663 
664     weed_plant_t **in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
665     weed_plant_t *out_channel = weed_get_plantptr_value(inst, WEED_LEAF_OUT_CHANNELS, NULL);
666 
667     float **dst = (float **)weed_get_voidptr_array(out_channel, WEED_LEAF_AUDIO_DATA, NULL);
668 
669     double tempo = weed_get_double_value(in_params[1], WEED_LEAF_VALUE, NULL);
670     double bfreq = weed_get_double_value(in_params[2], WEED_LEAF_VALUE, NULL);
671 
672     _sdata *sdata = weed_get_voidptr_value(inst, "plugin_internal", NULL);
673 
674     register int i;
675 
676     weed_free(in_params);
677 
678     if (!dst) return WEED_SUCCESS;
679 
680     chans = weed_get_int_value(out_channel, WEED_LEAF_AUDIO_CHANNELS, NULL);
681     nsamps = weed_get_int_value(out_channel, WEED_LEAF_AUDIO_DATA_LENGTH, NULL);
682     //rate=weed_get_int_value(out_channel,WEED_LEAF_AUDIO_RATE,NULL);
683 
684     for (i = 0; i < NCHANNELS; i++) {
685       set_live_row(sdata, i, (rand() % (sdata->maxtracks * 1000 - 1)) / 1000.f + 1.); // 2nd val can be 2 (?) to npat (?)
686     }
687 
688     set_tempo(sdata, tempo * 255. + 8.); // bpm: maybe 8. to 263. ?
689     set_base_freq(sdata, bfreq * 255. - 128.); // 145. to 400.
690 
691     syna_play(sdata, dst, nsamps, chans, inter); // dlen is number of samps....does interleaved
692     weed_free(dst);
693     return WEED_SUCCESS;
694   }
695 
696 
697   WEED_SETUP_START(200, 200) {
698     DIR *dir = NULL;
699     struct dirent *dirent;
700     size_t dlen;
701     int tcount = 0;
702     char desc[512];
703 
704     weed_plant_t *out_chantmpls[2];
705     weed_plant_t *in_params[NCHANNELS + 4]; // tune name + channel rows + tempo + base_freq + NULL
706     weed_plant_t *filter_class;
707 
708     register int i;
709 
710     // make list of tunes
711     // scan entries in the ./data/fourklives/songs directory
712     dir = opendir(TUNE_DIR);
713     if (dir == NULL) return NULL;
714 
715     while (1) {
716       if (tcount == MAX_TUNES - 1) break;
717       dirent = readdir(dir);
718       if (dirent == NULL) break;
719       dlen = strlen(dirent->d_name);
720       if (!strncmp(dirent->d_name, "..", dlen)) continue;
721       if (dlen > 4 && !strcmp(dirent->d_name + dlen - 4, ".txt")) dlen -= 4;
722       tunes[tcount++] = strndup(dirent->d_name, dlen);
723     }
724 
725     closedir(dir);
726 
727     tunes[tcount] = NULL;
728 
729     in_params[0] = weed_string_list_init("tune_name", "_Tune", 0, (const char **const)tunes);
730     weed_set_int_value(in_params[0], WEED_LEAF_FLAGS, WEED_PARAMETER_REINIT_ON_VALUE_CHANGE);
731 
732     in_params[1] = weed_float_init("tempo", "_Tempo", .5, 0., 1.);
733     in_params[2] = weed_float_init("bfreq", "Base _Frequency", .5, 0., 1.);
734 
735     for (i = 3; i < NCHANNELS + 3; i++) {
736       // TODO - unique name
737       in_params[i] = weed_float_init("cparam", "cparam", .5, 0., 1.);
738     }
739 
740     in_params[i] = NULL;
741 
742     out_chantmpls[0] = weed_audio_channel_template_init("out channel 0", 0);
743     out_chantmpls[1] = NULL;
744 
745     filter_class = weed_filter_class_init("fourKlives", "salsaman, anti and marq", 1, 0, NULL,
746                                           fourk_init, fourk_process, fourk_deinit, NULL, out_chantmpls, in_params, NULL);
747 
748     weed_plugin_info_add_filter_class(plugin_info, filter_class);
749 
750     snprintf(desc, 512, "fourK is a mini tracker player, which plays tunes from\ntext files\n");
751 
752     weed_set_string_value(filter_class, WEED_LEAF_DESCRIPTION, desc);
753     weed_plugin_set_package_version(plugin_info, package_version);
754   }
755   WEED_SETUP_END;
756 
757 
758   WEED_DESETUP_START {
759     register int i;
760     for (i = 0; tunes[i] != NULL; i++) weed_free(tunes[i]);
761   }
762   WEED_DESETUP_END;
763