1 //Spencer Jackson
2 //stuck.c
3 #include<lv2.h>
4 #include<stdlib.h>
5 #include<stdio.h>
6 #include<string.h>
7 #include<math.h>
8 #include"rms_calc.h"
9 #include"stuck.h"
10 
11 //#define CV_PORTS
12 #define AUTOCORR
13 //#define COMP
14 
15 #ifdef AUTOCORR
16 #define START_SCORE 0
17 #else //least squared error
18 #define START_SCORE 200
19 #endif
20 
21 enum states
22 {
23     INACTIVE = 0,
24     LOADING,
25     MATCHING,
26     LOADING_XFADE,
27     XFADE_ONLY,
28     PLAYING,
29     RELEASING,
30     QUICK_RELEASING,
31 	DEBUGGING,
32 };
33 
34 typedef struct _STUCK
35 {
36     uint16_t indx;//current write point in buffer
37     uint16_t indx2;//working/read point in buffer
38     uint16_t bufsize;//size of buffer
39     uint16_t wavesize;//size of waveform
40     uint16_t acorr_size;//size of autocorrelation
41     uint16_t xfade_size;
42     uint16_t wave_min;//int16_test allowed wavesize
43     uint16_t wave_max;//int32_test allowed wavesize
44     uint8_t state;
45     uint8_t stack;
46     uint8_t dbg;//used for whatever, delete it
47     double sample_freq;
48 
49     float *buf;
50     float gain;
51     float env;//envelope gain to normalize compression to
52     float score;
53     float shortscore;
54 
55     RMS_CALC rms_calc;
56 
57     float *input_p;
58     float *output_p;
59     float *trigger_p;
60     float *stick_it_p;
61     float *drone_gain_p;
62     float *release_p;
63     float *dbg_p;
64     float *output2_p;
65 
66     float *xf_func;
67 } STUCK;
68 
run_stuck(LV2_Handle handle,uint32_t nframes)69 void run_stuck(LV2_Handle handle, uint32_t nframes)
70 {
71     STUCK* plug = (STUCK*)handle;
72     uint32_t i,j,k,t,chunk=0;
73     double slope = 0;
74     double interp;
75 
76     if(plug->stack)
77         for(i=0;i<nframes;i++)
78             plug->output_p[i] = 0;
79     else
80         memcpy(plug->output_p,plug->input_p,nframes*sizeof(float));
81 
82     interp = nframes>64?nframes:64;
83 
84     //Evaluate port values and see if it requires any state changes
85     if(plug->state == INACTIVE)
86     {
87         //decide if triggered
88 #ifdef CV_PORTS
89         if(*plug->stick_it_p >= 1 || plug->trigger_p[nframes-1] >= 1)
90 #else
91         if(*plug->stick_it_p >= 1)
92 #endif
93         {
94             plug->state = LOADING;
95 #ifdef COMP
96             plug->env = plug->rms_calc.rms;
97         }
98         else
99         {
100             rms_block_fill(&plug->rms_calc, plug->input_p,nframes);
101 #else
102         }
103         else{
104 #endif
105             return;
106         }
107     }
108     else if(plug->state < LOADING_XFADE)
109     {
110         //decide if need to abort
111 #ifdef CV_PORTS
112         if(*plug->stick_it_p < 1 && plug->trigger_p[nframes-1] < 1)
113 #else
114         if(*plug->stick_it_p < 1)
115 #endif
116         {
117             //reinit
118             plug->indx = 0;
119             plug->indx2 = plug->wave_min;
120             plug->state = INACTIVE;
121             plug->gain = 0;
122             plug->wavesize = plug->wave_max;
123             plug->score = START_SCORE;
124             plug->shortscore = .25*START_SCORE;
125 #ifdef COMP
126             rms_block_fill(&plug->rms_calc, plug->input_p,nframes);
127 #endif
128             return;
129         }
130     }
131     else if(plug->state < RELEASING)
132     {
133         //decide if released
134 #ifdef CV_PORTS
135         if(*plug->stick_it_p < 1 && plug->trigger_p[nframes-1] < 1)
136 #else
137         if(*plug->stick_it_p < 1)
138 #endif
139         {
140             plug->state = RELEASING;
141 
142         }
143     }
144     else if(plug->state == RELEASING)
145     {
146         //decide if new trigger has been sent before release is complete
147 #ifdef CV_PORTS
148         if(*plug->stick_it_p >= 1 || plug->trigger_p[nframes-1] >= 1)
149 #else
150         if(*plug->stick_it_p >= 1)
151 #endif
152         {
153             plug->state = QUICK_RELEASING;
154         }
155 #ifdef COMP
156         else
157         {
158             rms_block_fill(&plug->rms_calc, plug->input_p,nframes);
159         }
160 #endif
161     }
162 
163     //now run the state machine
164     for(i=0; i<nframes;)
165     {
166         chunk = nframes - i;
167         if(plug->state == LOADING)//load enough frames to start calculating the autocorrelation
168         {
169             //decide if reaching minimum length in this period
170             if(plug->indx+chunk >= plug->wave_min+plug->acorr_size)
171             {
172                 chunk = plug->wave_min + plug->acorr_size - plug->indx;
173                 plug->state = MATCHING;
174             }
175             //load buffer with compressed signal
176             for(j=0; j<chunk; j++)
177             {
178 #ifdef COMP
179                 plug->buf[plug->indx++] = plug->input_p[i]*plug->env/rms_shift(&plug->rms_calc,plug->input_p[i]);
180 #else
181                 plug->buf[plug->indx++] = plug->input_p[i];
182 #endif
183                 i++;
184             }
185         }
186         else if(plug->state == MATCHING)//find autocorrelation
187         {
188             if(plug->indx2+chunk >= plug->wave_max)
189             {
190                 chunk = plug->wave_max - plug->indx2;
191                 plug->state = LOADING_XFADE;
192             }
193             // calculate autocorrelation of sample in buffer, save the minimum
194             float tmp,score;
195             for(j=0; j<chunk; j++)
196             {
197 #ifdef COMP
198                 plug->buf[plug->indx++] = plug->input_p[i]*plug->env/rms_shift(&plug->rms_calc,plug->input_p[i]);
199 #else
200                 plug->buf[plug->indx++] = plug->input_p[i];
201 #endif
202                 i++;
203 
204                 score = 0;
205                 t=0;
206 #ifdef AUTOCORR
207                 for(k=plug->indx2; k<plug->indx2+(plug->acorr_size>>2); k++)
208                 {
209                     score += plug->buf[k]*plug->buf[t++];
210                 }
211                 if(score >= plug->shortscore)
212                 {
213                     //full calc
214                     plug->shortscore = .9*score;
215                     for( ; k<plug->indx2+plug->acorr_size; k++)
216                         score += plug->buf[k]*plug->buf[t++];
217                 }
218                 //save place if score is higher than last highest
219                 if(score>=plug->score)
220 #else
221                 for(k=plug->indx2; k<plug->indx2+plug->acorr_size && score<=plug->score; k++)
222                 {
223                     tmp = plug->buf[k] - plug->buf[t++];//jsyk this isn't the strict definition of an autocorrelation, a variation on the principle
224                     score += tmp*tmp;
225                 }
226                 //save place if score is lower than last minimum
227                 if(score<=plug->score)
228 #endif
229                 {
230                     plug->wavesize = plug->indx2;
231                     plug->score = score;
232                 }
233                 plug->indx2++;
234             }
235             if(plug->indx2>=plug->wave_max)
236             {
237                 plug->indx2 = 0;//reset indx2
238                 //xfade in beginning of sample, these will be halved in the next state
239                 for(k=0; k<plug->xfade_size; k++)
240                     plug->buf[k] *= k/plug->xfade_size;
241             }
242         }
243         else if(plug->state == LOADING_XFADE)//xfade end of buffer with start (loop it) over an entire wave and fade in drone
244         {
245             slope = (*plug->drone_gain_p-plug->gain)/interp;
246             //decide if xfade ends in this period
247             if(plug->indx2+chunk >= plug->wavesize)
248             {
249             	//this means it has already played through the loop once and finished the xfade (layering really)
250                 chunk = plug->wavesize - plug->indx2;
251                 plug->state = PLAYING;
252             }
253             //decide if going to overflow
254             if(plug->indx+chunk >= plug->bufsize)
255             {
256             	//this means we've already filled the buffer, but haven't finished the xfade
257                 chunk = plug->bufsize - plug->indx;
258                 plug->state = XFADE_ONLY;
259             }
260             //load buffer with xfade
261             for(j=0; j<chunk; j++)
262             {
263                 //still loading end of buffer
264 #ifdef COMP
265                 plug->buf[plug->indx++] = plug->input_p[i]*plug->env/rms_shift(&plug->rms_calc,plug->input_p[i]);
266 #else
267                 plug->buf[plug->indx++] = plug->input_p[i];
268 #endif
269 
270             	//layer 2 full cycles on top of each other
271                 plug->buf[plug->indx2] = .5*plug->buf[plug->indx2+plug->wavesize] + .5*plug->buf[plug->indx2];
272 
273                 //but now also playing back start of buffer
274                 plug->output_p[i++] += plug->gain*plug->buf[plug->indx2++];
275                 plug->gain += slope;
276 
277             }
278             //xfade out end if we're there else we'll do it in the next state
279             if(plug->indx2>=plug->wavesize)//TODO: this must actually be ==
280             {
281                 for(k=0; k<plug->xfade_size; k++)
282                     plug->buf[k] += .5*(1-k/plug->xfade_size)*plug->buf[plug->indx2+plug->wavesize+k];
283                 plug->indx2 = 0;
284             }
285         }
286         else if(plug->state == XFADE_ONLY)//xfade after buffer is full, in practice we never get here, but we might change our smoothing strategy again
287         {
288             slope = (*plug->drone_gain_p-plug->gain)/interp;
289             //decide if xfade ends in this period
290             if(plug->indx2+chunk >= plug->wavesize)
291             {
292                 chunk = plug->wavesize - plug->indx2;
293                 plug->state = PLAYING;
294             }
295             //xfade buffer
296             for(j=0; j<chunk; j++)
297             {
298             	//continue layering the 2 cycles
299                 plug->buf[plug->indx2] = .5*plug->buf[plug->indx2+plug->wavesize] + .5*plug->buf[plug->indx2];
300                 plug->output_p[i++] += plug->gain*plug->buf[plug->indx2++];
301                 plug->gain += slope;
302             }
303             //xfade out end
304             if(plug->indx2>=plug->wavesize)//TODO: this must acutally be ==
305             {
306                 for(k=0; k<plug->xfade_size; k++)
307                     plug->buf[k] += .5*(1-k/plug->xfade_size)*plug->buf[plug->indx2+plug->wavesize+k];
308                 plug->indx2 = 0;
309             }
310         }
311         else if(plug->state == PLAYING)//just loop buffer and track gain changes
312         {
313             slope = (*plug->drone_gain_p-plug->gain)/interp;
314             for(j=0; j<chunk; j++)
315             {
316                 plug->output_p[i++] += plug->gain*plug->buf[plug->indx2++];
317                 plug->gain += slope;
318                 plug->indx2 = plug->indx2<plug->wavesize?plug->indx2:0;
319             }
320         }
321         else if(plug->state == RELEASING)
322         {
323             slope = -*plug->drone_gain_p/(*plug->release_p*plug->sample_freq);
324             //decide if released in this period
325             if(plug->gain + chunk*slope < slope)
326             {
327                 chunk = -plug->gain/slope;
328                 plug->state = INACTIVE;
329             }
330             for(j=0; j<chunk; j++)
331             {
332                 plug->output_p[i++] += plug->gain*plug->buf[plug->indx2++];
333                 plug->gain += slope;
334                 plug->indx2 = plug->indx2<plug->wavesize?plug->indx2:0;
335             }
336             if(plug->gain <= -slope)
337             {
338                 plug->indx = 0;
339                 plug->indx2 = plug->wave_min;
340                 plug->state = INACTIVE;
341                 plug->gain = 0;
342                 plug->wavesize = plug->wave_max;
343                 plug->score = START_SCORE;
344                 plug->shortscore = .25*START_SCORE;
345                 return;
346             }
347         }
348         else if(plug->state == QUICK_RELEASING)
349         {
350             slope = -*plug->drone_gain_p/(double)plug->wave_min;
351             //decide if released in this period
352             if(plug->gain + chunk*slope < slope)
353             {
354                 chunk = -plug->gain/slope;
355                 plug->state = LOADING;
356             }
357             for(j=0; j<chunk; j++)
358             {
359                 plug->output_p[i++] += plug->gain*plug->buf[plug->indx2++];
360                 plug->gain += slope;
361                 plug->indx2 = plug->indx2<plug->wavesize?plug->indx2:0;
362             }
363 #ifdef COMP
364             rms_block_fill(&plug->rms_calc, plug->input_p,chunk);
365 #endif
366             if(plug->gain <= -slope)
367             {
368                 plug->indx = 0;
369                 plug->indx2 = plug->wave_min;
370                 plug->state = LOADING;
371                 plug->wavesize = plug->wave_max;
372                 plug->score = START_SCORE;
373                 plug->shortscore = .25*START_SCORE;
374 #ifdef COMP
375                 plug->env = plug->rms_calc.rms;
376 #endif
377             }
378         }
379     }
380     return;
381 }
382 
init_stuck(const LV2_Descriptor * descriptor,double sample_freq,const char * bundle_path,const LV2_Feature * const * host_features)383 LV2_Handle init_stuck(const LV2_Descriptor *descriptor,double sample_freq, const char *bundle_path,const LV2_Feature * const* host_features)
384 {
385     STUCK* plug = malloc(sizeof(STUCK));
386 
387     uint16_t tmp;
388     uint8_t i;
389     plug->sample_freq = sample_freq;
390     tmp = 0x8000;//15 bits
391     if(sample_freq<100000)//88.1 or 96.1kHz
392         tmp = tmp>>1;//14 bits
393     if(sample_freq<50000)//44.1 or 48kHz
394         tmp = tmp>>1;//13 bits //8192
395     plug->buf = (float*)malloc(tmp*sizeof(float));
396     plug->bufsize = tmp;
397     plug->acorr_size = tmp>>3;//1024 if you mess with this, keep in mind it may need change in the default score value
398     plug->xfade_size = tmp>>6;//128
399     plug->wave_max = (tmp - plug->xfade_size)>>1;//4064
400     plug->wave_min = tmp>>6;//128
401     plug->wavesize = plug->wave_max;
402     plug->indx = 0;
403     plug->indx2 = plug->wave_min;
404     plug->state = INACTIVE;
405     plug->gain = 0;
406     plug->score = START_SCORE;
407     plug->shortscore = .25*START_SCORE;
408     plug->env = 0;
409     plug->stack = 0;
410     plug->dbg = 0;
411 
412     //half rasied cosine for equal power xfade
413     plug->xf_func = (float*)malloc(plug->xfade_size*sizeof(float));
414 	for (i = 0; i < plug->xfade_size; i++) plug->xf_func[i] = 0.5 * (1 - cos((M_PI * i / plug->xfade_size)));
415 
416     rms_init(&plug->rms_calc,tmp>>3);
417 
418     return plug;
419 }
420 
init_stuckstacker(const LV2_Descriptor * descriptor,double sample_freq,const char * bundle_path,const LV2_Feature * const * host_features)421 LV2_Handle init_stuckstacker(const LV2_Descriptor *descriptor,double sample_freq, const char *bundle_path,const LV2_Feature * const* host_features)
422 {
423     STUCK* plug = (STUCK*)init_stuck(descriptor, sample_freq, bundle_path, host_features);
424     plug->stack = 1;
425     return plug;
426 }
427 
connect_stuck_ports(LV2_Handle handle,uint32_t port,void * data)428 void connect_stuck_ports(LV2_Handle handle, uint32_t port, void *data)
429 {
430     STUCK* plug = (STUCK*)handle;
431     switch(port)
432     {
433     case IN:
434         plug->input_p = (float*)data;
435         break;
436     case OUT:
437         plug->output_p = (float*)data;
438         break;
439     case TRIGGER:
440         plug->trigger_p = (float*)data;
441         break;
442     case STICKIT:
443         plug->stick_it_p = (float*)data;
444         break;
445     case DRONEGAIN:
446         plug->drone_gain_p = (float*)data;
447         break;
448     case RELEASE:
449         plug->release_p = (float*)data;
450         break;
451     case DBG:
452         plug->dbg_p = (float*)data;
453         break;
454     case OUT2:
455         plug->output2_p = (float*)data;
456         break;
457     default:
458         puts("UNKNOWN PORT YO!!");
459     }
460 }
461 
cleanup_stuck(LV2_Handle handle)462 void cleanup_stuck(LV2_Handle handle)
463 {
464     STUCK* plug = (STUCK*)handle;
465     rms_deinit(&plug->rms_calc);
466     free(plug->buf);
467     free(plug->xf_func);
468     free(plug);
469 }
470 
471 static const LV2_Descriptor stuck_descriptor=
472 {
473     STUCK_URI,
474     init_stuck,
475     connect_stuck_ports,
476     0,//activate
477     run_stuck,
478     0,//deactivate
479     cleanup_stuck,
480     0//extension
481 };
482 static const LV2_Descriptor stuckstacker_descriptor=
483 {
484     STUCKSTACKER_URI,
485     init_stuckstacker,
486     connect_stuck_ports,
487     0,//activate
488     run_stuck,
489     0,//deactivate
490     cleanup_stuck,
491     0//extension
492 };
493 
494 LV2_SYMBOL_EXPORT
lv2_descriptor(uint32_t index)495 const LV2_Descriptor* lv2_descriptor(uint32_t index)
496 {
497     switch (index)
498     {
499     case 0:
500         return &stuck_descriptor;
501     case 1:
502         return &stuckstacker_descriptor;
503     default:
504         return 0;
505     }
506 }
507