#ifndef CONTROL_H #define CONTROL_H /** Control Component The midi implementation polls the midi device every whenever and gets to react to midi events. Any and all changes should not happen directly to the active `value`, but rather to the `mod`. This way the sound engine can smoothly interpolate the changed value while generating samples. The sound engine fills a buffer with samples. The buffer has BUFFER_SIZE. Each call of the sound callback will insert BUFFER_SIZE samples in the buffer. Before we get the samples, we poll the synth's ccs for any changes. This will set the target value to the `value + mod`, and the sound engine will interpolate the `value` from value to target . It should also reset the mod since other midi events might trigger until the next buffer generation. */ typedef struct cc_t { char name[64]; int midi_cc; float min, max; float step; float def; float value; /* active value (start for interpolation) */ float mod; /* stores the modified value before it is set as target */ float target; /* target value (end for interpolation) */ } cc_t; #ifndef CC #define CC(SYNTH, NAME, MIN, MAX, STEP, DEF) \ strcpy(SYNTH.name, NAME); \ SYNTH.midi_cc = -1; \ SYNTH.min = MIN; \ SYNTH.max = MAX; \ SYNTH.step = STEP; \ SYNTH.def = DEF; \ SYNTH.value = DEF; \ SYNTH.mod = 0; \ SYNTH.target = DEF; \ synth->ccs[synth->cci++] = &SYNTH; #endif /** Step the value of the target cc for `steps`. `steps` can be positive or negative. The change will occur in the mod value of the cc. returns 1 if the value changed, 0 otherwise */ int cc_step(cc_t *cc, int steps); /** Reset the cc to defaults */ void cc_reset(cc_t *cc); /** Get the interpolated value from 0 to `max` in position `i` */ float cc_iget(cc_t *cc, unsigned int i, unsigned int max); void cc_prep(cc_t *cc); void cc_fix(cc_t *cc); const char * cc_to_str(cc_t *cc); #endif /* CONTROL_H */