clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit 5a2806b26c16710aeda17359b572804f9f934daf
parent 60d4e1206803b32a229d5fc99ea3d1e2fba89a58
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Fri, 21 Nov 2014 01:33:06 +0100

Synthesizer + jack host works!!!

Diffstat:
Mexamples/thyns/dlist.h | 16++++++++++++----
Mexamples/thyns/env.h | 20++++++++++++--------
Mexamples/thyns/osc.h | 11++++++-----
Mexamples/thyns/thyns.h | 33+++++++++++++++++++++------------
Mexamples/thyns/voice.h | 23++++++++++++++++-------
Minclude/clap/clap-midi-parser.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Minclude/clap/clap.h | 2++
Mtools/clap-jack-host/CMakeLists.txt | 2+-
Mtools/clap-jack-host/clap-jack-host.c | 1+
9 files changed, 223 insertions(+), 44 deletions(-)

diff --git a/examples/thyns/dlist.h b/examples/thyns/dlist.h @@ -3,6 +3,8 @@ # define thyns_dlist_push_back(Head, Item) \ do { \ + assert((Item)->prev == NULL); \ + assert((Item)->next == NULL); \ if (!(Head)) { \ (Head) = (Item); \ (Item)->next = (Item); \ @@ -17,12 +19,18 @@ # define thyns_dlist_remove(Head, Item) \ do { \ - (Item)->next->prev = (Item)->prev; \ - (Item)->prev->next = (Item)->next; \ + assert((Head)); \ + assert((Item)->prev); \ + assert((Item)->next); \ + if ((Item)->next == (Item)) \ + (Head) = NULL; \ + else { \ + (Head) = (Item)->next; \ + (Item)->next->prev = (Item)->prev; \ + (Item)->prev->next = (Item)->next; \ + } \ (Item)->next = NULL; \ (Item)->prev = NULL; \ - if ((Head) == (Item)) \ - (Head) = NULL; \ } while (0) #endif /* !DLIST_H */ diff --git a/examples/thyns/env.h b/examples/thyns/env.h @@ -14,16 +14,11 @@ enum thyns_env_state struct thyns_env { - // params - uint32_t a; // duration in ms - uint32_t d; // duration in ms - double s; // sustain level - uint32_t r; // duration in ms - // state enum thyns_env_state state; double ar; // attack ramp double dr; // decay ramp + double s; // sustain level double rr; // release ramp double v; }; @@ -32,6 +27,10 @@ static inline void thyns_env_init(struct thyns_env * restrict env) { env->state = THYNS_ENV_IDLE; env->v = 0; + env->ar = 0.0001; + env->dr = 0.0001; + env->s = 0.7; + env->rr = 0.00001; } static inline void thyns_env_restart(struct thyns_env * restrict env) @@ -39,6 +38,11 @@ static inline void thyns_env_restart(struct thyns_env * restrict env) env->state = THYNS_ENV_ATTACK; } +static inline void thyns_env_release(struct thyns_env * restrict env) +{ + env->state = THYNS_ENV_RELEASE; +} + static inline double thyns_env_step(struct thyns_env * restrict env) { switch (env->state) { @@ -51,7 +55,7 @@ static inline double thyns_env_step(struct thyns_env * restrict env) break; case THYNS_ENV_DECAY: - env->v += env->dr; + env->v -= env->dr; if (env->v <= env->s) { env->v = env->s; env->state = THYNS_ENV_SUSTAIN; @@ -62,7 +66,7 @@ static inline double thyns_env_step(struct thyns_env * restrict env) break; case THYNS_ENV_RELEASE: - env->v += env->rr; + env->v -= env->rr; if (env->v <= 0) { env->v = 0; env->state = THYNS_ENV_IDLE; diff --git a/examples/thyns/osc.h b/examples/thyns/osc.h @@ -9,7 +9,7 @@ enum thyns_osc_type THYNS_OSC_SQUARE = 1, THYNS_OSC_SAW = 2, THYNS_OSC_TRI = 3, - THYNS_OSC_SIN = 4, + THYNS_OSC_SINE = 4, }; struct thyns_osc @@ -31,12 +31,13 @@ static inline void thyns_osc_init(struct thyns_osc *osc, uint32_t sr) { osc->sr = sr; - osc->pi_sr = M_PI / sr; - osc->type = THYNS_OSC_NONE; + osc->pi_sr = M_PI / ((float)sr); + osc->type = THYNS_OSC_SQUARE; osc->pwm = 0.5; osc->freq = 0; osc->angle = 0; osc->phase = 0; + osc->tune = 0; } static inline void @@ -49,7 +50,7 @@ thyns_osc_set_freq(struct thyns_osc *osc, double freq) static inline double thyns_osc_step(struct thyns_osc *osc) { - osc->angle += fmod(osc->angle + osc->angle_ramp, 2 * M_PI); + osc->angle = fmod(osc->angle + osc->angle_ramp, 2 * M_PI); double angle = fmod(osc->angle + osc->phase, 2 * M_PI); switch (osc->type) { @@ -67,7 +68,7 @@ thyns_osc_step(struct thyns_osc *osc) return angle * 2.0 / M_PI - 1; return (2 * M_PI - angle) * 2.0 / M_PI - 1; - case THYNS_OSC_SIN: + case THYNS_OSC_SINE: return sin(angle); default: diff --git a/examples/thyns/thyns.h b/examples/thyns/thyns.h @@ -38,30 +38,30 @@ static inline void thyns_init(struct thyns *thyns, uint32_t sr) static double thyns_step(struct thyns *thyns, struct clap_process *process) { + if (!thyns->singing) + return 0; + double out = 0; struct thyns_voice *prev = NULL; struct thyns_voice *v = thyns->singing; + struct thyns_voice *end = v->prev; - while (v) { + do { out += thyns_voice_step(v); // can we release the voice? if (v->amp_env.state == THYNS_ENV_IDLE) { - if (prev) { - prev->next = v->next; - v->next = thyns->idle; - thyns->idle = v; - v = prev->next; - } else { - v->next = thyns->idle; - thyns->idle = v; - v = thyns->singing; - } + printf("releasing %d\n", v->key); + assert(v->key != 0); + thyns->keys[v->key] = NULL; + thyns_dlist_remove(thyns->singing, v); + thyns_dlist_push_back(thyns->idle, v); + v = prev ? prev->next : thyns->singing; } else { prev = v; v = v->next; } - } + } while (v && prev != end && v != prev); return out; } @@ -73,6 +73,7 @@ thyns_note_on(struct thyns *thyns, { struct thyns_voice *voice = NULL; + printf("note_on(%d, %f)\n", key, pitch); assert(key < 0x80); if (thyns->keys[key]) { voice = thyns->keys[key]; @@ -87,6 +88,7 @@ thyns_note_on(struct thyns *thyns, } thyns_dlist_push_back(thyns->singing, voice); thyns->keys[key] = voice; + voice->key = key; } thyns_voice_start_note(thyns->keys[key], key, pitch); @@ -96,6 +98,10 @@ static inline void thyns_note_off(struct thyns *thyns, uint8_t key) { + printf("note_off(%d)\n", key); + assert(key < 0x80); + if (thyns->keys[key]) + thyns_voice_stop_note(thyns->keys[key], key); } static inline void @@ -138,6 +144,9 @@ thyns_process(struct thyns *thyns, struct clap_process *process) process->output[1][i] = process->output[0][i]; } + // ensure no more events are left + assert(!ev); + if (thyns->singing) return CLAP_PROCESS_CONTINUE; return CLAP_PROCESS_STOP; diff --git a/examples/thyns/voice.h b/examples/thyns/voice.h @@ -13,7 +13,7 @@ struct thyns_voice uint32_t sr; // sample rate double pi_sr; // M_PI / sample_rate - uint32_t key; + uint8_t key; float freq; // osc part @@ -47,13 +47,14 @@ thyns_voice_init(struct thyns_voice *voice, uint32_t sr) // filter thyns_filt_init(&voice->filt, sr); - thyns_filt_set_cutoff(&voice->filt, 1000); + voice->filt.cutoff = 4000; + voice->filt.resonance = 1.5; thyns_env_init(&voice->filt_env); - voice->filt_env_depth = 0; + voice->filt_env_depth = 0.2; // amp thyns_env_init(&voice->amp_env); - voice->amp = 0.7; + voice->amp = 0.2; } static inline void @@ -68,19 +69,27 @@ thyns_voice_start_note(struct thyns_voice *voice, thyns_env_restart(&voice->amp_env); } +static inline void +thyns_voice_stop_note(struct thyns_voice *voice, + uint32_t key) +{ + thyns_env_release(&voice->filt_env); + thyns_env_release(&voice->amp_env); +} + static inline double thyns_voice_step(struct thyns_voice *voice) { double osc1 = thyns_osc_step(&voice->osc1); double osc2 = thyns_osc_step(&voice->osc2); double oscm = osc1 * (1 - voice->osc_mix) + osc2 * voice->osc_mix; - - double fenv = thyns_env_step(&voice->filt_env); + double fenv = thyns_env_step(&voice->filt_env) * voice->filt_env_depth; double cutoff = exp(log(voice->filt.cutoff) + fenv); thyns_filt_set_cutoff(&voice->filt, cutoff); double filtered = thyns_filt_step(&voice->filt, oscm); + double amp = voice->amp * thyns_env_step(&voice->amp_env); - return filtered * voice->amp * thyns_env_step(&voice->amp_env); + return filtered * amp; } #endif /* !VOICE_H */ diff --git a/include/clap/clap-midi-parser.c b/include/clap/clap-midi-parser.c @@ -1,4 +1,146 @@ #include <assert.h> +#include <math.h> + +static inline float +clap_midi_pitch(uint8_t key) +{ + return 440.0f * powf(2, (key - 57.f) / 12.f); +} + +// for i in $(seq 0 127); +// do +// calc "440.0 * 2.0 ^ ((${i}.0 - 57.0) / 12.0)" +// done | sed -r 's/\t(.*)$/ \1,/g' +static const float clap_midi_pitches[128] = { + 16.3515978312874146696, + 17.323914436054506016, + 18.354047994837972516, + 19.4454364826300569232, + 20.6017223070543706096, + 21.8267644645627427796, + 23.1246514194771499336, + 24.499714748859330882, + 25.9565435987465711576, + 27.5, + 29.1352350948806197776, + 30.8677063285077569896, + 32.7031956625748293348, + 34.647828872109012032, + 36.708095989675945032, + 38.890872965260113842, + 41.2034446141087412192, + 43.6535289291254855548, + 46.2493028389542998672, + 48.9994294977186617596, + 51.9130871974931423152, + 55, + 58.2704701897612395508, + 61.7354126570155139792, + 65.4063913251496586696, + 69.295657744218024064, + 73.416191979351890064, + 77.781745930520227684, + 82.406889228217482434, + 87.3070578582509711096, + 92.4986056779085997344, + 97.9988589954373235236, + 103.8261743949862846304, + 110, + 116.5409403795224791016, + 123.4708253140310279584, + 130.8127826502993173392, + 138.5913154884360481236, + 146.8323839587037801324, + 155.563491861040455368, + 164.813778456434964868, + 174.6141157165019422236, + 184.9972113558171994688, + 195.9977179908746470428, + 207.6523487899725692608, + 220, + 233.0818807590449582032, + 246.9416506280620559168, + 261.6255653005986346784, + 277.1826309768720962472, + 293.6647679174075602648, + 311.126983722080910736, + 329.627556912869929736, + 349.2282314330038844472, + 369.9944227116343989332, + 391.9954359817492940856, + 415.3046975799451385216, + 440, + 466.1637615180899164064, + 493.8833012561241118292, + 523.2511306011972693568, + 554.3652619537441924988, + 587.3295358348151205252, + 622.253967444161821472, + 659.255113825739859472, + 698.45646286600776889, + 739.9888454232687978664, + 783.9908719634985881712, + 830.6093951598902770432, + 880, + 932.3275230361798328128, + 987.7666025122482236628, + 1046.5022612023945387092, + 1108.7305239074883849932, + 1174.6590716696302410504, + 1244.507934888323642944, + 1318.510227651479718944, + 1396.91292573201553778, + 1479.9776908465375957328, + 1567.9817439269971763424, + 1661.2187903197805540908, + 1760, + 1864.65504607235966563, + 1975.5332050244964473212, + 2093.0045224047890774228, + 2217.4610478149767699908, + 2349.3181433392604821008, + 2489.0158697766472858924, + 2637.020455302959437888, + 2793.8258514640310755644, + 2959.95538169307519147, + 3135.9634878539943526848, + 3322.4375806395611081816, + 3520, + 3729.3100921447193312556, + 3951.0664100489928946468, + 4186.0090448095781548456, + 4434.9220956299535399816, + 4698.636286678520964206, + 4978.0317395532945717804, + 5274.0409106059188757716, + 5587.6517029280621511244, + 5919.91076338615038294, + 6271.9269757079887053696, + 6644.8751612791222163588, + 7040, + 7458.6201842894386625156, + 7902.1328200979857892936, + 8372.0180896191563096912, + 8869.8441912599070799632, + 9397.2725733570419284076, + 9956.0634791065891435652, + 10548.0818212118377515476, + 11175.3034058561243022532, + 11839.82152677230076588, + 12543.8539514159774107436, + 13289.7503225582444327176, + 14080, + 14917.2403685788773250312, + 15804.2656401959715785828, + 16744.0361792383126193824, + 17739.688382519814159922, + 18794.5451467140838568196, + 19912.126958213178287126, + 21096.1636424236755030952, + 22350.606811712248604502, + 23679.6430535446015317556, + 25087.7079028319548214828, +}; static inline uint16_t clap_midi_parse_be16(const uint8_t *in) @@ -66,8 +208,8 @@ clap_midi_parse_channel_event(struct clap_midi_parser *parser) if (parser->size < 3) return CLAP_MIDI_PARSER_EOB; - parser->channel.event_type = parser->in[0] & 0xf; - parser->channel.channel = parser->in[0] >> 4; + parser->channel.event_type = parser->in[0] >> 4; + parser->channel.channel = parser->in[0] & 0xf; parser->channel.param1 = parser->in[1]; parser->channel.param2 = parser->in[2]; @@ -104,7 +246,7 @@ clap_midi_parse_meta_event(struct clap_midi_parser *parser) static inline enum clap_midi_parser_status clap_midi_parse_event(struct clap_midi_parser *parser) { - if ((parser->in[0] & 0xf) <= 0xe) + if ((parser->in[0] >> 4) <= 0xe) return clap_midi_parse_channel_event(parser); if (parser->in[0] == 0xff) return clap_midi_parse_meta_event(parser); @@ -114,7 +256,7 @@ clap_midi_parse_event(struct clap_midi_parser *parser) static inline enum clap_midi_parser_status clap_midi_parse(struct clap_midi_parser *parser) { - if (!parser->in || parser->size < 4) + if (!parser->in || parser->size < 1) return CLAP_MIDI_PARSER_EOB; switch (parser->state) { @@ -157,20 +299,23 @@ clap_midi_convert(const uint8_t *in, event->note.key = parser.channel.param1; event->note.velocity = ((float)parser.channel.param2) / 127.0f; event->note.events = NULL; - break; + event->note.pitch = clap_midi_pitches[event->note.key]; + return; case CLAP_MIDI_CHANNEL_NOTE_ON: event->type = CLAP_EVENT_NOTE_ON; event->note.key = parser.channel.param1; event->note.velocity = ((float)parser.channel.param2) / 127.0f; event->note.events = NULL; - break; + event->note.pitch = clap_midi_pitches[event->note.key]; + return; } default: + printf("midi\n"); event->type = CLAP_EVENT_MIDI; event->midi.buffer = in; event->midi.size = size; - break; + return; } } diff --git a/include/clap/clap.h b/include/clap/clap.h @@ -137,6 +137,8 @@ struct clap_event_note struct clap_event_param { + bool is_global; // is this event global? + uint8_t key; // if !is_global, target key uint32_t index; union clap_param_value value; float increment; // for param ramp diff --git a/tools/clap-jack-host/CMakeLists.txt b/tools/clap-jack-host/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable(clap-jack-host clap-jack-host.c) -target_link_libraries(clap-jack-host jack dl) +target_link_libraries(clap-jack-host jack dl m) diff --git a/tools/clap-jack-host/clap-jack-host.c b/tools/clap-jack-host/clap-jack-host.c @@ -87,6 +87,7 @@ int process(jack_nframes_t nframes, void *arg) p.output = out; p.samples_count = nframes; p.is_offline = false; + p.steady_time = app->steady_time; // XXX add time info /* convert midi events */