From eb3a05b119e5efd5ea130fc69aeab18cc8cc3b6a Mon Sep 17 00:00:00 2001 From: Nils <> Date: Tue, 7 Apr 2020 16:19:59 +0200 Subject: [PATCH] update template --- template/calfbox/appmenu.c | 4 +- template/calfbox/envelope.h | 2 + template/calfbox/experiments/interactive.py | 2 +- template/calfbox/experiments/meta.py | 2 +- .../experiments/playPatternsAsMeasures.py | 2 +- .../calfbox/experiments/printAllMidiEvents.py | 2 +- .../printAllMidiEventsSpecificPort.py | 2 +- .../calfbox/experiments/simplePlayback.py | 2 +- .../calfbox/experiments/simplerPlayback.py | 2 +- template/calfbox/experiments/testmetadata.py | 2 +- template/calfbox/fluid.c | 8 - template/calfbox/menu.c | 4 +- template/calfbox/sampler.c | 13 +- template/calfbox/sampler.h | 79 +- template/calfbox/sampler_api_test.py | 81 +- template/calfbox/sampler_channel.c | 66 +- template/calfbox/sampler_impl.h | 13 +- template/calfbox/sampler_layer.c | 1587 +++++++++-------- template/calfbox/sampler_layer.h | 181 +- template/calfbox/sampler_nif.c | 31 +- template/calfbox/sampler_prevoice.c | 9 +- template/calfbox/sampler_prg.c | 13 +- template/calfbox/sampler_rll.c | 11 +- template/calfbox/sampler_voice.c | 300 ++-- template/calfbox/sfzloader.c | 2 +- template/calfbox/tests.c | 66 + template/documentation/index.adoc.template | 1 + 27 files changed, 1415 insertions(+), 1072 deletions(-) diff --git a/template/calfbox/appmenu.c b/template/calfbox/appmenu.c index aa2053a..7fb7cce 100644 --- a/template/calfbox/appmenu.c +++ b/template/calfbox/appmenu.c @@ -35,6 +35,8 @@ along with this program. If not, see . #include "ui.h" #include "wavebank.h" +#if USE_NCURSES + #include #include #include @@ -46,8 +48,6 @@ along with this program. If not, see . #include #include -#if USE_NCURSES - int cmd_quit(struct cbox_menu_item_command *item, void *context) { return 1; diff --git a/template/calfbox/envelope.h b/template/calfbox/envelope.h index 58522ff..af26dae 100644 --- a/template/calfbox/envelope.h +++ b/template/calfbox/envelope.h @@ -117,6 +117,8 @@ static inline float cbox_envelope_get_value(struct cbox_envelope *env, const str static inline void cbox_envelope_update_shape_after_modify(struct cbox_envelope *env, struct cbox_envelope_shape *shape, double sr) { + if (env->cur_stage < 0) + return; struct cbox_envstage *es = &shape->stages[env->cur_stage]; if (es->time != env->orig_time) { diff --git a/template/calfbox/experiments/interactive.py b/template/calfbox/experiments/interactive.py index 88d3ed7..e2d23cc 100755 --- a/template/calfbox/experiments/interactive.py +++ b/template/calfbox/experiments/interactive.py @@ -4,7 +4,7 @@ This is a minimal calfbox python example. It is meant as a starting point to find bugs and test performance. -Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/meta.py b/template/calfbox/experiments/meta.py index cd98241..3651b53 100644 --- a/template/calfbox/experiments/meta.py +++ b/template/calfbox/experiments/meta.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/playPatternsAsMeasures.py b/template/calfbox/experiments/playPatternsAsMeasures.py index 6a2368a..8f3657f 100755 --- a/template/calfbox/experiments/playPatternsAsMeasures.py +++ b/template/calfbox/experiments/playPatternsAsMeasures.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/printAllMidiEvents.py b/template/calfbox/experiments/printAllMidiEvents.py index 408f795..394663d 100755 --- a/template/calfbox/experiments/printAllMidiEvents.py +++ b/template/calfbox/experiments/printAllMidiEvents.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/printAllMidiEventsSpecificPort.py b/template/calfbox/experiments/printAllMidiEventsSpecificPort.py index 8638c3d..0f9c218 100755 --- a/template/calfbox/experiments/printAllMidiEventsSpecificPort.py +++ b/template/calfbox/experiments/printAllMidiEventsSpecificPort.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/simplePlayback.py b/template/calfbox/experiments/simplePlayback.py index d36ef77..6312894 100755 --- a/template/calfbox/experiments/simplePlayback.py +++ b/template/calfbox/experiments/simplePlayback.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/simplerPlayback.py b/template/calfbox/experiments/simplerPlayback.py index 1bf8d14..43e8c7d 100755 --- a/template/calfbox/experiments/simplerPlayback.py +++ b/template/calfbox/experiments/simplerPlayback.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- """ -Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/experiments/testmetadata.py b/template/calfbox/experiments/testmetadata.py index 5954144..7f50180 100755 --- a/template/calfbox/experiments/testmetadata.py +++ b/template/calfbox/experiments/testmetadata.py @@ -4,7 +4,7 @@ This is a minimal calfbox python example. It is meant as a starting point to find bugs and test performance. -Copyright 2018, Nils Hilbricht, Germany ( https://www.hilbricht.net ) +Copyright, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/template/calfbox/fluid.c b/template/calfbox/fluid.c index 7ac37d8..20580e4 100644 --- a/template/calfbox/fluid.c +++ b/template/calfbox/fluid.c @@ -150,14 +150,6 @@ MODULE_CREATE_FUNCTION(fluidsynth) m->synth = new_fluid_synth(m->settings); fluid_synth_set_reverb_on(m->synth, cbox_config_get_int(cfg_section, "reverb", 1)); fluid_synth_set_chorus_on(m->synth, cbox_config_get_int(cfg_section, "chorus", 1)); - - //Log levels in Fluidsynth are handled as settable functions - fluid_set_log_function(FLUID_PANIC, NULL, NULL); - fluid_set_log_function(FLUID_ERR, NULL, NULL); - fluid_set_log_function(FLUID_WARN, NULL, NULL); - fluid_set_log_function(FLUID_DBG, NULL, NULL); - fluid_set_log_function(FLUID_INFO, NULL, NULL); - m->bank_name = NULL; m->sfid = -1; diff --git a/template/calfbox/menu.c b/template/calfbox/menu.c index 90860f9..e80ca0c 100644 --- a/template/calfbox/menu.c +++ b/template/calfbox/menu.c @@ -20,14 +20,14 @@ along with this program. If not, see . #include "menuitem.h" #include "ui.h" +#if USE_NCURSES + #include #include #include #include #include -#if USE_NCURSES - struct cbox_menu { GPtrArray *items; diff --git a/template/calfbox/sampler.c b/template/calfbox/sampler.c index f17950f..b3bdf92 100644 --- a/template/calfbox/sampler.c +++ b/template/calfbox/sampler.c @@ -90,10 +90,11 @@ void sampler_create_voice_from_prevoice(struct sampler_module *m, struct sampler { if (!m->voices_free) return; - int exgroups[MAX_RELEASED_GROUPS], exgroupcount = 0; - sampler_voice_start(m->voices_free, pv->channel, pv->layer_data, pv->note, pv->vel, exgroups, &exgroupcount); - if (exgroupcount) - sampler_channel_release_groups(pv->channel, pv->note, exgroups, exgroupcount); + struct sampler_released_groups exgroups; + sampler_released_groups_init(&exgroups); + sampler_voice_start(m->voices_free, pv->channel, pv->layer_data, pv->note, pv->vel, &exgroups); + if (exgroups.low_groups || exgroups.group_count) + sampler_channel_release_groups(pv->channel, pv->note, &exgroups); } void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs) @@ -142,7 +143,9 @@ void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, c } m->active_voices = vcount; m->active_prevoices = pvcount; - if (vcount - vrel > m->max_voices) + if(vcount - vrel > m->max_voices + 1) + sampler_steal_voice(m); + if(vcount - vrel > m->max_voices) sampler_steal_voice(m); m->serial_no++; m->current_time += CBOX_BLOCK_SIZE; diff --git a/template/calfbox/sampler.h b/template/calfbox/sampler.h index 9181093..6dba6e9 100644 --- a/template/calfbox/sampler.h +++ b/template/calfbox/sampler.h @@ -189,7 +189,46 @@ struct sampler_module struct cbox_sincos sincos[12800]; }; -#define MAX_RELEASED_GROUPS 4 +#define MAX_RELEASED_GROUPS 16 + +struct sampler_released_groups +{ + // Groups 1-32 use a bitmask + uint32_t low_groups; + int group_count; + int groups[MAX_RELEASED_GROUPS]; +}; + +static inline void sampler_released_groups_init(struct sampler_released_groups *groups) +{ + groups->low_groups = 0; + groups->group_count = 0; +} + +static inline gboolean sampler_released_groups_check(struct sampler_released_groups *groups, int group) +{ + if (group <= 32) + return (groups->low_groups >> (group - 1)) & 1; + for (int j = 0; j < groups->group_count; j++) + { + if (groups->groups[j] == group) + return TRUE; + } + return FALSE; +} + +static inline void sampler_released_groups_add(struct sampler_released_groups *groups, int group) +{ + if (group <= 32) + { + groups->low_groups |= (1 << (group - 1)); + return; + } + if (groups->group_count >= MAX_RELEASED_GROUPS) + return; + if (!sampler_released_groups_check(groups, group)) + groups->groups[groups->group_count++] = group; +} extern GQuark cbox_sampler_error_quark(void); @@ -209,12 +248,13 @@ extern void sampler_channel_program_change(struct sampler_channel *c, int progra extern void sampler_channel_stop_sustained(struct sampler_channel *c); extern void sampler_channel_stop_sostenuto(struct sampler_channel *c); extern void sampler_channel_capture_sostenuto(struct sampler_channel *c); -extern void sampler_channel_release_groups(struct sampler_channel *c, int note, int exgroups[MAX_RELEASED_GROUPS], int exgroupcount); +extern void sampler_channel_release_groups(struct sampler_channel *c, int note, struct sampler_released_groups *exgroups); extern void sampler_channel_stop_all(struct sampler_channel *c); extern void sampler_channel_process_cc(struct sampler_channel *c, int cc, int val); extern void sampler_channel_reset_keyswitches(struct sampler_channel *c); -extern void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, int *exgroups, int *pexgroupcount); +extern void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, struct sampler_released_groups *exgroups); +extern void sampler_voice_start_silent(struct sampler_layer_data *l, struct sampler_released_groups *exgroups); extern void sampler_voice_release(struct sampler_voice *v, gboolean is_polyaft); extern void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cbox_sample_t **outputs); extern void sampler_voice_link(struct sampler_voice **pv, struct sampler_voice *v); @@ -292,13 +332,13 @@ static inline float sampler_program_get_curve_value(struct sampler_program *prog return val; } -static inline float sampler_channel_getcc_mod(struct sampler_channel *c, struct sampler_voice *v, int cc_no, struct sampler_modulation *sm) +static inline float sampler_channel_getcc_mod(struct sampler_channel *c, struct sampler_voice *v, int cc_no, int curve_id, float step) { float val = (cc_no < 128) ? c->floatcc[cc_no] : sampler_channel_get_expensive_cc(c, v, NULL, cc_no); - if (sm->step) - val = floorf(0.9999f * val * (sm->step + 1)) / sm->step; - if (sm->curve_id || c->program->interpolated_curves[0]) - val = sampler_program_get_curve_value(c->program, sm->curve_id, val); + if (step) + val = floorf(0.9999f * val * (step + 1)) / step; + if (curve_id || c->program->interpolated_curves[0]) + val = sampler_program_get_curve_value(c->program, curve_id, val); return val; } @@ -309,11 +349,14 @@ static inline int sampler_channel_getintcc(struct sampler_channel *c, struct sam return (int)127 * (sampler_channel_get_expensive_cc(c, v, NULL, cc_no)); } -static inline float sampler_channel_getcc_prevoice(struct sampler_channel *c, struct sampler_prevoice *pv, int cc_no) +static inline float sampler_channel_getcc_prevoice(struct sampler_channel *c, struct sampler_prevoice *pv, int cc_no, int curve_id, float step) { - if (cc_no < 128) - return c->floatcc[cc_no]; - return sampler_channel_get_expensive_cc(c, NULL, pv, cc_no); + float val = (cc_no < 128) ? c->floatcc[cc_no] : sampler_channel_get_expensive_cc(c, NULL, pv, cc_no); + if (step) + val = floorf(0.9999f * val * (step + 1)) / step; + if (curve_id || c->program->interpolated_curves[0]) + val = sampler_program_get_curve_value(c->program, curve_id, val); + return val; } static inline float sampler_channel_get_poly_pressure(struct sampler_channel *c, uint8_t note) @@ -322,6 +365,18 @@ static inline float sampler_channel_get_poly_pressure(struct sampler_channel *c, return (c->poly_pressure_mask & (1 << (note >> 2))) ? c->poly_pressure[note] * (1.f / 127.f) : 0;; } +static inline gboolean sampler_cc_range_is_in(const struct sampler_cc_range *range, struct sampler_channel *c) +{ + while(range) + { + int ccval = sampler_channel_getintcc(c, NULL, range->key.cc_number); + if (ccval < range->value.locc || ccval > range->value.hicc) + return FALSE; + range = range->next; + } + return TRUE; +} + #define FOREACH_VOICE(var, p) \ for (struct sampler_voice *p = (var), *p##_next = NULL; p && (p##_next = p->next, TRUE); p = p##_next) #define FOREACH_PREVOICE(var, p) \ diff --git a/template/calfbox/sampler_api_test.py b/template/calfbox/sampler_api_test.py index aa22bae..8beca0c 100644 --- a/template/calfbox/sampler_api_test.py +++ b/template/calfbox/sampler_api_test.py @@ -61,11 +61,13 @@ g1.set_param("volume", "-12") g1.set_param("fileg_depthcc14", "-5400") def check_exception(param, value, substr): + error = False try: g1.set_param(param, value) - assert False, "Exception with substring %s expected when setting %s to %s, none caught" % (substr, param, value) except Exception as e: - assert substr in str(e), "Exception with substring %s expected when setting %s to %s, caught another one: %s" % (substr, param, value, str(e)) + error = True + assert substr in str(e), "Exception with substring '%s' expected when setting %s to %s, caught another one: %s" % (substr, param, value, str(e)) + assert error, "Exception with substring '%s' expected when setting %s to %s, none caught" % (substr, param, value) check_exception("cutoff", "bla", "correct numeric value") check_exception("key", "bla", "valid note name") @@ -74,9 +76,11 @@ check_exception("lochan", "10.5", "correct integer value") check_exception("offset", "bla", "correct unsigned integer value") check_exception("offset", "10.5", "correct unsigned integer value") check_exception("offset", "-1000", "correct unsigned integer value") + +# Make sure that multiple CC assignments work g1.set_param("locc5", "100") -check_exception("locc8", "110", "Conflicting controller") -check_exception("hicc8", "110", "Conflicting controller") +g1.set_param("locc8", "100") +g1.set_param("hicc8", "110") #g1.set_param("cutoff", "1000") #g1.set_param("fillfo_freq", "4") @@ -89,6 +93,16 @@ r1.set_param("transpose", "0") r1.set_param("tune", "5") r1.set_param("gain_cc17", "12") +verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5']) +verify_region(r1, ["locc5=100", "locc8=100", "hicc8=110"], [], full=True) + +r1.set_param("hicc5", "120") +r1.set_param("hicc8", "124") + +verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5']) +verify_region(g1, ["locc5=100", "hicc5=127", "locc8=100", "hicc8=110"], [], full=True) +verify_region(r1, ["locc5=100", "hicc5=120", "locc8=100", "hicc8=124"], [], full=True) + r2 = g1.new_child() r2.set_param("sample", "*sqr") r2.set_param("transpose", "12") @@ -103,12 +117,29 @@ verify_region(r2, ["sample=*sqr"], ["transpose"]) r2.unset_param("sample") verify_region(r2, [], ["transpose", "sample"]) +g1.unset_param("cutoff") +g1.unset_param("resonance") +g1.unset_param("fil_type") +g1.unset_param("fileg_sustain") +g1.unset_param("fileg_decay") +g1.unset_param("fileg_depth") +g1.unset_param("fileg_release") +g1.unset_param("ampeg_release") +g1.unset_param("amp_veltrack") +g1.unset_param("volume") +g1.unset_param("fileg_depthcc14") +g1.unset_param("locc5") +g1.unset_param("locc8") +g1.unset_param("hicc8") + params_to_test = [ 'lokey', 'hikey', 'lovel', 'hivel', 'key', 'cutoff', 'pan', 'offset', 'tune', 'position', 'width', 'amp_random', 'fil_random', 'pitch_random', 'delay_random', 'pitch_veltrack', 'reloffset_veltrack', 'offset_veltrack', 'delay_cc5', 'delay_cc10', 'reloffset_cc5', 'reloffset_cc10', 'offset_cc5', 'offset_cc10', + 'delay_curvecc8', 'reloffset_curvecc5', 'offset_curvecc10', + 'delay_stepcc8', 'reloffset_stepcc5', 'offset_stepcc10', 'cutoff_cc1', "resonance_cc1", 'pitch_cc1', 'tonectl_cc1', 'gain_cc1', 'amplitude_cc1', 'cutoff_oncc2', "resonance_oncc2", 'pitch_oncc2', 'tonectl_oncc2', 'gain_oncc2', 'amplitude_oncc2', 'cutoff_curvecc1', 'resonance_curvecc5', 'pitch_curvecc10', 'amplitude_curvecc10', @@ -141,6 +172,7 @@ params_to_test = [ 'amp_velcurve_5', 'amp_velcurve_127', 'locc5', 'hicc5', 'on_locc8', 'on_hicc8', + 'lfo5_freq', 'lfo1_wave', 'lfo3_fade', 'lfo4_delay', ] for i in range(len(params_to_test)): param = params_to_test[0] @@ -149,24 +181,57 @@ for i in range(len(params_to_test)): value1, value2 = "100", "80" if 'key' in param: value1, value2 = "e1", "g1" + # Verify that a setting is reported back + r2.set_param(param, value1) + verify_region(r2, ["%s=%s" % (param, value1)], rest) + + # Verify that setting the same value in parent doesn't change the local 'has' flag + g1.set_param(param, value1) + verify_region(r2, ["%s=%s" % (param, value1)], rest) + + # Verify that setting a different local value doesn't get overridden by parent + r2.set_param(param, value2) + verify_region(r2, ["%s=%s" % (param, value2)], rest) + # Write the original value r2.set_param(param, value1) verify_region(r2, ["%s=%s" % (param, value1)], rest) + + # Delete the parent value, confirm the deletion doesn't propagate to child + g1.unset_param(param) + verify_region(r2, ["%s=%s" % (param, value1)], rest) + # Set a different value in the parent, confirm it doesn't affect the child g1.set_param(param, value2) - verify_region(g1, ["%s=%s" % (param, value2)], []) + verify_region(g1, ["%s=%s" % (param, value2)], rest) + verify_region(r2, ["%s=%s" % (param, value1)], rest) + # Check that the newly created child inherits the setting from the parent r3 = g1.new_child() verify_region(r3, [], [param]) verify_region(r3, ["%s=%s" % (param, value2)], [], full=True) r3.delete() - verify_region(r2, ["%s=%s" % (param, value1)], rest) + # Verify that the original child still has the original value verify_region(r2, ["%s=%s" % (param, value1)], [], full=True) + # Delete the child value, make sure it disappears but the inherited + # value is still reported in the full listing r2.unset_param(param) verify_region(r2, [], params_to_test) - verify_region(r2, ["%s=%s" % (param, value2)], [], full=True) + # Delete the setting in the parent, make sure the inherited value in the + # child disappears too g1.unset_param(param) verify_region(r2, [], ["%s=%s" % (param, value2)], full=True) + + master.set_param(param, value1) + verify_region(master, ["%s=%s" % (param, value1)], []) + verify_region(g1, [], params_to_test) + verify_region(g1, ["%s=%s" % (param, value1)], [], full=True) + verify_region(r2, [], params_to_test) + verify_region(r2, ["%s=%s" % (param, value1)], [], full=True) + master.unset_param(param) + verify_region(master, [], params_to_test) + verify_region(g1, [], params_to_test) + verify_region(r2, [], params_to_test) params_to_test = params_to_test[1:] + params_to_test[0:1] @@ -187,7 +252,7 @@ for t in old_names: v1, v2 = "10", "20" else: old, new, v1, v2 = t - print ("Trying %s" % old) + print ("Trying alias: %s" % old) r1.set_param(old, v1) verify_region(r1, ["%s=%s" % (new, v1)], [old]) r1.set_param(old, v2) diff --git a/template/calfbox/sampler_channel.c b/template/calfbox/sampler_channel.c index e211a59..4af9396 100644 --- a/template/calfbox/sampler_channel.c +++ b/template/calfbox/sampler_channel.c @@ -126,16 +126,28 @@ void sampler_channel_process_cc(struct sampler_channel *c, int cc, int val) // transition between in-range and out-of-range. gboolean compatible_oncc_behaviour = TRUE; - if (layer->runtime->on_cc.cc_number == cc && - (val >= layer->runtime->on_cc.locc && val <= layer->runtime->on_cc.hicc) && - (compatible_oncc_behaviour || !(old_value >= layer->runtime->on_cc.locc && old_value <= layer->runtime->on_cc.hicc))) + struct sampler_cc_range *on_cc = layer->runtime->on_cc; + gboolean trigger = FALSE; + while(on_cc) + { + if (on_cc->key.cc_number == cc && + (val >= on_cc->value.locc && val <= on_cc->value.hicc) && + (compatible_oncc_behaviour || !(old_value >= on_cc->value.locc && old_value <= on_cc->value.hicc))) + { + trigger = TRUE; + break; + } + on_cc = on_cc->next; + } + if(trigger) { struct sampler_voice *v = m->voices_free; if (!v) break; - int exgroups[MAX_RELEASED_GROUPS], exgroupcount = 0; - sampler_voice_start(v, c, layer->runtime, layer->runtime->pitch_keycenter, 127, exgroups, &exgroupcount); - sampler_channel_release_groups(c, -1, exgroups, exgroupcount); + struct sampler_released_groups exgroups; + sampler_released_groups_init(&exgroups); + sampler_voice_start(v, c, layer->runtime, layer->runtime->pitch_keycenter, 127, &exgroups); + sampler_channel_release_groups(c, -1, &exgroups); } } } @@ -192,26 +204,19 @@ void sampler_channel_process_cc(struct sampler_channel *c, int cc, int val) set_cc_int(c, cc, val); } -void sampler_channel_release_groups(struct sampler_channel *c, int note, int exgroups[MAX_RELEASED_GROUPS], int exgroupcount) +void sampler_channel_release_groups(struct sampler_channel *c, int note, struct sampler_released_groups *exgroups) { - if (exgroupcount) + if (exgroups->group_count || exgroups->low_groups) { FOREACH_VOICE(c->voices_running, v) { - for (int j = 0; j < exgroupcount; j++) + if (v->off_by && v->note != note) { - if (v->off_by == exgroups[j] && v->note != note) + if (sampler_released_groups_check(exgroups, v->off_by)) { + v->released = 1; if (v->layer->off_mode == som_fast) - { - v->released = 1; cbox_envelope_go_to(&v->amp_env, 15); - } - else - { - v->released = 1; - } - break; } } } @@ -268,7 +273,7 @@ void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gb } struct sampler_layer_data *layers[MAX_SAMPLER_VOICES]; struct sampler_layer_data *delayed_layers[MAX_SAMPLER_PREVOICES]; - int lcount = 0, dlcount = 0; + int lcount = 0, dlcount = 0, slcount = 0; struct sampler_voice *free_voice = m->voices_free; struct sampler_prevoice *free_prevoice = m->prevoices_free; int fvcount = 0, fpcount = 0; @@ -286,16 +291,22 @@ void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gb fpcount++; } assert(layer->runtime); - if (layer->runtime->use_prevoice) + if (layer->runtime->computed.eff_use_prevoice) delayed_layers[dlcount++] = layer->runtime; else + { layers[lcount++] = layer->runtime; + if (layer->runtime->computed.eff_is_silent) + slcount++; + } layer = sampler_rll_iterator_next(&iter); } - int exgroups[MAX_RELEASED_GROUPS], exgroupcount = 0; + struct sampler_released_groups exgroups; + sampler_released_groups_init(&exgroups); // If running out of polyphony, do not start the note if all the regions cannot be played - if (lcount <= fvcount && dlcount <= fpcount) + // (silent notes don't count into polyphony) + if (lcount <= slcount + fvcount && dlcount <= fpcount) { // this might perhaps be optimized by mapping the group identifiers to flat-array indexes // but I'm not going to do that until someone gives me an SFZ worth doing that work ;) @@ -303,8 +314,13 @@ void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gb for (int i = 0; i < lcount; ++i) { struct sampler_layer_data *l = layers[i]; - int velc = (!is_first && l->vel_mode == svm_previous) ? c->first_note_vel : vel; - sampler_voice_start(m->voices_free, c, l, note, velc, exgroups, &exgroupcount); + if (l->computed.eff_is_silent) + sampler_voice_start_silent(l, &exgroups); + else + { + int velc = (!is_first && l->vel_mode == svm_previous) ? c->first_note_vel : vel; + sampler_voice_start(m->voices_free, c, l, note, velc, &exgroups); + } } for (int i = 0; i < dlcount; ++i) { @@ -317,7 +333,7 @@ void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gb c->previous_note = note; if (is_first) c->first_note_vel = vel; - sampler_channel_release_groups(c, note, exgroups, exgroupcount); + sampler_channel_release_groups(c, note, &exgroups); } void sampler_channel_start_release_triggered_voices(struct sampler_channel *c, int note) diff --git a/template/calfbox/sampler_impl.h b/template/calfbox/sampler_impl.h index aeab6d3..4e28748 100644 --- a/template/calfbox/sampler_impl.h +++ b/template/calfbox/sampler_impl.h @@ -30,10 +30,15 @@ static inline int sfz_note_from_string(const char *note) int pos; int nn = tolower(note[0]); int nv; - if (nn >= '0' && nn <= '9') - return atoi(note); + if ((nn >= '0' && nn <= '9') || nn == '-') + { + int nv = atoi(note); + if (nv >= -1 && nv <= 127) + return nv; + return -2; + } if (nn < 'a' || nn > 'g') - return -1; + return -2; nv = semis[nn - 'a']; for (pos = 1; tolower(note[pos]) == 'b' || note[pos] == '#'; pos++) @@ -44,7 +49,7 @@ static inline int sfz_note_from_string(const char *note) return nv + 12 * (1 + atoi(note + pos)); } - return -1; + return -2; } static inline gboolean atof_C_verify(const char *key, const char *value, double *result, GError **error) diff --git a/template/calfbox/sampler_layer.c b/template/calfbox/sampler_layer.c index 5f6f8d7..4637d3d 100644 --- a/template/calfbox/sampler_layer.c +++ b/template/calfbox/sampler_layer.c @@ -35,282 +35,221 @@ along with this program. If not, see . #include #include -void sampler_layer_data_dump_modulations(struct sampler_layer_data *l) +static inline gboolean sampler_modulation_key_equal(const struct sampler_modulation_key *k1, const struct sampler_modulation_key *k2) { - GSList *p = l->modulations; - while(p) - { - struct sampler_modulation *sm = p->data; - printf("%d x %d -> %d : %f : %d\n", sm->src, sm->src2, sm->dest, sm->amount, sm->curve_id); - p = g_slist_next(p); - } + return (k1->src == k2->src && k1->src2 == k2->src2 && k1->dest == k2->dest); } -static struct sampler_modulation *sampler_layer_data_find_modulation(struct sampler_layer_data *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, GSList **link_ptr) +static inline void sampler_modulation_dump_one(const struct sampler_modulation *sm) { - GSList *p = l->modulations; - while(p) - { - struct sampler_modulation *sm = p->data; - if (sm->src == src && sm->src2 == src2 && sm->dest == dest) - { - if (link_ptr) - *link_ptr = p; - return sm; - } - p = g_slist_next(p); - } - return NULL; + printf("%d x %d -> %d : %f : %d\n", sm->key.src, sm->key.src2, sm->key.dest, sm->value.amount, sm->value.curve_id); } -static struct sampler_modulation *sampler_layer_data_add_modulation(struct sampler_layer_data *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest) -{ - struct sampler_modulation *sm = sampler_layer_data_find_modulation(l, src, src2, dest, NULL); - if (sm) - return sm; - sm = g_malloc0(sizeof(struct sampler_modulation)); - sm->src = src; - sm->src2 = src2; - sm->dest = dest; - sm->amount = 0; - sm->curve_id = 0; - sm->smooth = 0; - sm->step = 0; - sm->has_amount = FALSE; - sm->has_curve = FALSE; - sm->has_smooth = FALSE; - sm->has_step = FALSE; - l->modulations = g_slist_prepend(l->modulations, sm); - return sm; -} +////////////////////////////////////////////////////////////////////////////////////////////////// -static inline gboolean is_null_modulation(const struct sampler_modulation *sm) +static inline gboolean sampler_noteinitfunc_key_equal(const struct sampler_noteinitfunc_key *k1, const struct sampler_noteinitfunc_key *k2) { - return !sm->amount && !sm->curve_id && !sm->smooth && !sm->step && - !sm->has_amount && !sm->has_curve && !sm->has_smooth && !sm->has_step; + return (k1->notefunc_voice == k2->notefunc_voice && k1->variant == k2->variant); } -static inline gboolean is_null_values_modulation(const struct sampler_modulation *sm) +static inline void sampler_noteinitfunc_dump_one(const struct sampler_noteinitfunc *nif) { - return !sm->amount && !sm->curve_id && !sm->smooth && !sm->step; + printf("%p(%d) = %f\n", nif->key.notefunc_voice, nif->key.variant, nif->value.value); } -void sampler_layer_propagate_modulation(struct sampler_layer *l, const struct sampler_modulation *srcm, gboolean starting) -{ - if (!starting) - { - struct sampler_modulation *dstm = sampler_layer_data_add_modulation(&l->data, srcm->src, srcm->src2, srcm->dest); - if (!dstm->has_amount) - dstm->amount = srcm->amount; - if (!dstm->has_curve) - dstm->curve_id = srcm->curve_id; - if (!dstm->has_smooth) - dstm->smooth = srcm->smooth; - if (!dstm->has_step) - dstm->step = srcm->step; - } - - if (l->child_layers) { - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *child = value; - sampler_layer_propagate_modulation(child, srcm, FALSE); - } - } -} +////////////////////////////////////////////////////////////////////////////////////////////////// -void sampler_layer_set_modulation_amount(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, float amount) +static inline gboolean sampler_cc_range_key_equal(const struct sampler_cc_range_key *k1, const struct sampler_cc_range_key *k2) { - struct sampler_modulation *dstm = sampler_layer_data_add_modulation(&l->data, src, src2, dest); - dstm->has_amount = TRUE; - dstm->amount = amount; - sampler_layer_propagate_modulation(l, dstm, TRUE); + return k1->cc_number == k2->cc_number; } -void sampler_layer_set_modulation_curve(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, int curve) +static inline void sampler_cc_range_dump_one(const struct sampler_cc_range *ccrange) { - struct sampler_modulation *dstm = sampler_layer_data_add_modulation(&l->data, src, src2, dest); - dstm->has_curve = TRUE; - dstm->curve_id = curve; - sampler_layer_propagate_modulation(l, dstm, TRUE); + printf("CC%d in [%c%d, %c%d]\n", (int)ccrange->key.cc_number, ccrange->value.has_locc ? '!' : '.', (int)ccrange->value.locc, ccrange->value.has_hicc ? '!' : '.', (int)ccrange->value.hicc); } -void sampler_layer_set_modulation_smooth(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, float amount) -{ - struct sampler_modulation *dstm = sampler_layer_data_add_modulation(&l->data, src, src2, dest); - dstm->has_smooth = TRUE; - dstm->smooth = amount; - sampler_layer_propagate_modulation(l, dstm, TRUE); -} +////////////////////////////////////////////////////////////////////////////////////////////////// -void sampler_layer_set_modulation_step(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, float amount) +static inline gboolean sampler_flex_lfo_key_equal(const struct sampler_flex_lfo_key *k1, const struct sampler_flex_lfo_key *k2) { - struct sampler_modulation *dstm = sampler_layer_data_add_modulation(&l->data, src, src2, dest); - dstm->has_step = TRUE; - dstm->step = amount; - sampler_layer_propagate_modulation(l, dstm, TRUE); + return k1->id == k2->id; } -static gboolean sampler_layer_data_unset_modulation(struct sampler_layer_data *l, struct sampler_layer_data *parent_data, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local, gboolean unset_amount, gboolean unset_curve, gboolean unset_smooth, gboolean unset_step) +static inline void sampler_flex_lfo_dump_one(const struct sampler_flex_lfo *lfo) { - GSList *link = NULL; - struct sampler_modulation *sm = sampler_layer_data_find_modulation(l, src, src2, dest, &link); - if (!sm) - return FALSE; - struct sampler_modulation *psm = remove_local && parent_data != NULL ? sampler_layer_data_find_modulation(parent_data, src, src2, dest, NULL) : NULL; - if (unset_amount && sm->has_amount == remove_local) - { - sm->amount = psm ? psm->amount : 0; - if (remove_local) - sm->has_amount = FALSE; - } - if (unset_curve && sm->has_curve == remove_local) - { - sm->curve_id = psm ? psm->curve_id : 0; - if (remove_local) - sm->has_curve = FALSE; - } - if (unset_smooth && sm->has_smooth == remove_local) - { - sm->smooth = psm ? psm->smooth : 0; - if (remove_local) - sm->has_smooth = FALSE; - } - if (unset_step && sm->has_step == remove_local) - { - sm->step = psm ? psm->step : 0; - if (remove_local) - sm->has_step = FALSE; - } - // Delete if it's all default values and it's not overriding anything - if (is_null_modulation(sm)) - l->modulations = g_slist_delete_link(l->modulations, link); - - return TRUE; + printf("LFO%d (freq %s %f, delay %s %f, fade %s %f, wave %s %d)\n", + (int)lfo->key.id, + lfo->value.has_freq ? "(local)" : "(inherited)", lfo->value.freq, + lfo->value.has_delay ? "(local)" : "(inherited)", lfo->value.delay, + lfo->value.has_fade ? "(local)" : "(inherited)", lfo->value.fade, + lfo->value.has_wave ? "(local)" : "(inherited)", lfo->value.wave + ); } -static void sampler_layer_unset_modulation(struct sampler_layer*l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local, gboolean unset_amount, gboolean unset_curve, gboolean unset_smooth, gboolean unset_step) -{ - if (!sampler_layer_data_unset_modulation(&l->data, l->parent ? &l->parent->data : NULL, src, src2, dest, remove_local, unset_amount, unset_curve, unset_smooth, unset_step)) - return; +////////////////////////////////////////////////////////////////////////////////////////////////// - if (l->child_layers) { - // Also recursively remove propagated copies from child layers, if any - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *child = value; - sampler_layer_unset_modulation(child, src, src2, dest, FALSE, unset_amount, unset_curve, unset_smooth, unset_step); - } +#define SAMPLER_COLL_FUNC_DUMP(sname) \ + void sname##_dump(const struct sname *p) \ + { \ + for(; p; p = p->next) \ + sname##_dump_one(p); \ } -} -void sampler_layer_unset_modulation_amount(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local) -{ - sampler_layer_unset_modulation(l, src, src2, dest, remove_local, TRUE, FALSE, FALSE, FALSE); -} +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_DUMP) + +#define SAMPLER_COLL_FUNC_FIND(sname) \ + static struct sname *sname##_find(struct sname *list, const struct sname##_key *key) \ + { \ + for(struct sname *p = list; p; p = p->next) \ + { \ + struct sname##_key *dkey = &p->key; \ + if (sname##_key_equal(dkey, key)) \ + return p; \ + } \ + return NULL; \ + } \ + static struct sname *sname##_find2(struct sname **list_ptr, const struct sname##_key *key, struct sname ***link_ptr) \ + { \ + for(struct sname **pp = list_ptr; *pp; pp = &(*pp)->next) \ + { \ + struct sname##_key *dkey = &(*pp)->key; \ + if (sname##_key_equal(dkey, key)) \ + { \ + if (link_ptr) \ + *link_ptr = pp; \ + return *pp; \ + } \ + } \ + return NULL; \ + } -void sampler_layer_unset_modulation_curve(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local) -{ - sampler_layer_unset_modulation(l, src, src2, dest, remove_local, FALSE, TRUE, FALSE, FALSE); +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_FIND) + +#define SAMPLER_COLL_FIELD_INIT(name, has_name, type, init_value) \ + d->value.name = init_value; \ + d->value.has_name = FALSE; + +#define SAMPLER_COLL_FUNC_ADD(sname) \ +static struct sname *sname##_add(struct sname **list_ptr, const struct sname##_key *key) \ +{ \ + struct sname *d = sname##_find(*list_ptr, key); \ + if (d) \ + return d; \ + d = g_malloc0(sizeof(struct sname)); \ + d->key = *key; \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_INIT)\ + d->next = *list_ptr; \ + *list_ptr = d; \ + return d; \ } -void sampler_layer_unset_modulation_smooth(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local) -{ - sampler_layer_unset_modulation(l, src, src2, dest, remove_local, FALSE, FALSE, TRUE, FALSE); +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_ADD) + +#define SAMPLER_COLL_FUNC_DESTROY(sname) \ +static void sname##s_destroy(struct sname *list_ptr) \ +{ \ + while(list_ptr) \ + { \ + struct sname *p = list_ptr->next; \ + g_free(list_ptr); \ + list_ptr = p; \ + } \ } -void sampler_layer_unset_modulation_step(struct sampler_layer *l, enum sampler_modsrc src, enum sampler_modsrc src2, enum sampler_moddest dest, gboolean remove_local) -{ - sampler_layer_unset_modulation(l, src, src2, dest, remove_local, FALSE, FALSE, FALSE, TRUE); -} +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_DESTROY) -void sampler_layer_data_add_nif(struct sampler_layer_data *l, SamplerNoteInitFunc notefunc_voice, SamplerNoteInitFunc2 notefunc_prevoice, int variant, float param, gboolean propagating_defaults) -{ - assert(!(notefunc_voice && notefunc_prevoice)); - GSList **list = notefunc_voice ? &l->voice_nifs : &l->prevoice_nifs; - GSList *p = *list; - while(p) - { - struct sampler_noteinitfunc *nif = p->data; - if (nif->notefunc_voice == notefunc_voice && nif->notefunc_prevoice == notefunc_prevoice && nif->variant == variant) - { - // do not overwrite locally set value with defaults - if (propagating_defaults && nif->has_value) - return; - nif->param = param; - nif->has_value = !propagating_defaults; - return; - } - p = g_slist_next(p); - } - struct sampler_noteinitfunc *nif = malloc(sizeof(struct sampler_noteinitfunc)); - nif->notefunc_voice = notefunc_voice; - nif->notefunc_prevoice = notefunc_prevoice; - nif->variant = variant; - nif->param = param; - nif->has_value = !propagating_defaults; - *list = g_slist_prepend(*list, nif); -} +#define SAMPLER_COLL_FIELD_ISNULL(name, has_name, type, init_value) \ + if (d->name != init_value || d->has_name) \ + return FALSE; -void sampler_layer_data_remove_nif(struct sampler_layer_data *l, struct sampler_layer_data *parent_data, SamplerNoteInitFunc notefunc_voice, SamplerNoteInitFunc2 notefunc_prevoice, int variant, gboolean remove_propagated) -{ - assert(!(notefunc_voice && notefunc_prevoice)); - GSList **list = notefunc_voice ? &l->voice_nifs : &l->prevoice_nifs; - GSList *p = *list; - while(p) - { - struct sampler_noteinitfunc *nif = p->data; - if (nif->notefunc_voice == notefunc_voice && nif->notefunc_prevoice == notefunc_prevoice && nif->variant == variant && nif->has_value == !remove_propagated) - { - if (!remove_propagated && parent_data) { - // Try to copy over from parent - GSList *q = notefunc_voice ? parent_data->voice_nifs : parent_data->prevoice_nifs; - while(q) - { - struct sampler_noteinitfunc *pnif = q->data; - if (pnif->notefunc_voice == notefunc_voice && pnif->notefunc_prevoice == notefunc_prevoice && pnif->variant == variant && pnif->has_value) - { - memcpy(nif, pnif, sizeof(*pnif)); - nif->has_value = FALSE; - return; - } - q = g_slist_next(q); - } - } - *list = g_slist_delete_link(*list, p); - return; - } - p = g_slist_next(p); +#define SAMPLER_COLL_FUNC_ISNULL(sname) \ + static inline gboolean sname##_is_null(const struct sname##_value *d) \ + { \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_ISNULL) \ + return TRUE; \ } -} -void sampler_layer_add_nif(struct sampler_layer *l, SamplerNoteInitFunc notefunc_voice, SamplerNoteInitFunc2 notefunc_prevoice, int variant, float param) -{ - sampler_layer_data_add_nif(&l->data, notefunc_voice, notefunc_prevoice, variant, param, FALSE); -} +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_ISNULL) + +// sampler_modulation_set_amount etc. +#define SAMPLER_COLL_FIELD_SETTER(name, has_name, type, init_value, sname) \ + struct sname *sname##_set_##name##_by_offset(struct sampler_layer *l, uint32_t offset, const struct sname##_key *key, gboolean set_local_value, type value) \ + { \ + void *vl = &l->data; \ + struct sname **list_ptr = vl + offset; \ + struct sname *dstm = sname##_add(list_ptr, key); \ + if (!set_local_value && dstm->value.has_name) \ + return dstm; \ + dstm->value.has_name = set_local_value; \ + dstm->value.name = value; \ + return dstm; \ + } -void sampler_layer_remove_nif(struct sampler_layer *l, SamplerNoteInitFunc notefunc_voice, SamplerNoteInitFunc2 notefunc_prevoice, int variant, gboolean remove_propagated) -{ - sampler_layer_data_remove_nif(&l->data, !remove_propagated && l->parent ? &l->parent->data : NULL, notefunc_voice, notefunc_prevoice, variant, remove_propagated); +#define SAMPLER_COLL_FUNC_SETTERS(sname) \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_SETTER, sname) + +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_SETTERS) + +#define SAMPLER_COLL_FIELD_UNSET(name, has_name, type, init_value, sname) \ + if ((unset_mask & (1 << sname##_value_field_##name)) && d->value.has_name == remove_local) \ + { \ + d->value.name = parent ? parent->value.name : init_value; \ + d->value.has_name = FALSE; \ + } \ + +#define SAMPLER_COLL_FIELD_KEY_ENUM_VALUE(name, has_name, type, init_value, sname) \ + sname##_value_field_##name, + +#define SAMPLER_COLL_FUNC_UNSET(sname) \ + enum { \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_KEY_ENUM_VALUE, sname) \ + }; \ + static gboolean sname##_unset_by_offset(struct sampler_layer *l, uint32_t offset, const struct sname##_key *key, gboolean remove_local, uint32_t unset_mask) \ + { \ + void *vl = &l->data, *vp = l->parent ? &l->parent->data : NULL; \ + struct sname **list_ptr = vl + offset; \ + struct sname **parent_list_ptr = vp ? vp + offset : NULL; \ + struct sname **link_ptr = NULL; \ + struct sname *d = sname##_find2(list_ptr, key, &link_ptr); \ + if (!d) \ + return FALSE; \ + struct sname *parent = remove_local && *parent_list_ptr != NULL ? sname##_find(*parent_list_ptr, key) : NULL; \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_UNSET, sname) \ + /* Delete if it's all default values and it's not overriding anything */ \ + if (sname##_is_null(&d->value)) {\ + *link_ptr = d->next; \ + g_free(d); \ + } \ + return TRUE; \ + } - if (l->child_layers) { - // Also recursively remove propagated copies from child layers, if any - GHashTableIter iter; - g_hash_table_iter_init(&iter, l->child_layers); - gpointer key, value; - while(g_hash_table_iter_next(&iter, &key, &value)) - { - struct sampler_layer *child = value; - sampler_layer_remove_nif(child, notefunc_voice, notefunc_prevoice, variant, TRUE); - } +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_UNSET) + +#define SAMPLER_COLL_FIELD_ZEROHASATTR(name, has_name, type, init_value) \ + dstv->value.has_name = FALSE; +#define SAMPLER_COLL_FUNC_CLONE(sname) \ + static struct sname *sname##_clone(struct sname *src, gboolean copy_hasattr) \ + { \ + struct sname *dst = NULL, **last = &dst;\ + for(const struct sname *srcv = src; srcv; srcv = srcv->next) \ + { \ + struct sname *dstv = g_malloc(sizeof(struct sname)); \ + memcpy(dstv, srcv, sizeof(struct sname)); \ + if (!copy_hasattr) \ + { \ + SAMPLER_COLL_FIELD_LIST_##sname(SAMPLER_COLL_FIELD_ZEROHASATTR) \ + } \ + *last = dstv; \ + dstv->next = NULL; \ + last = &dstv->next; \ + } \ + return dst; \ } -} + +SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_CLONE) + +////////////////////////////////////////////////////////////////////////////////////////////////// enum sampler_layer_param_type { @@ -326,22 +265,15 @@ enum sampler_layer_param_type slpt_midicurve, slpt_ccrange, // modulation matrix - slpt_depthcc, // src * CC -> dest - slpt_amountcc, // CC -> dest - slpt_amount, // src -> dest - slpt_modulation, // src1 * src2 -> dest + slpt_mod_amount, // src (or CC) * src2 (or CC) -> dest + slpt_mod_curveid, + slpt_mod_smooth, + slpt_mod_step, slpt_generic_modulation, - slpt_curvecc, - slpt_smoothcc, - slpt_stepcc, - slpt_depth_curvecc, - slpt_depth_smoothcc, - slpt_depth_stepcc, // note init functions slpt_voice_nif, slpt_prevoice_nif, - slpt_voice_cc_nif, - slpt_prevoice_cc_nif, + slpt_flex_lfo, slpt_reserved, }; @@ -351,103 +283,113 @@ struct sampler_layer_param_entry size_t offset; enum sampler_layer_param_type type; double def_value; - uint32_t extra_int; + uint64_t extra_int; void *extra_ptr; void (*set_has_value)(struct sampler_layer_data *, gboolean); + gboolean (*get_has_value)(struct sampler_layer_data *); }; +#define MODSRC_CC 0xFFF +#define smsrc_CC MODSRC_CC + +#define ENCODE_MOD(src, src2, dst) ((((uint32_t)(src) & 0xFFFU) | (((uint32_t)(src2) & 0xFFFU) << 12U) | ((uint32_t)(dst) << 24U))) + #define PROC_SUBSTRUCT_FIELD_SETHASFUNC(name, index, def_value, parent) \ - static void sampler_layer_data_##parent##_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##parent.name = value; } + static void sampler_layer_data_##parent##_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##parent.name = value; } \ + static gboolean sampler_layer_data_##parent##_get_has_##name(struct sampler_layer_data *l) { return l->has_##parent.name; } #define PROC_FIELD_SETHASFUNC(type, name, default_value) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } + static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } \ + static gboolean sampler_layer_data_get_has_##name(struct sampler_layer_data *l) { return l->has_##name; } #define PROC_FIELD_SETHASFUNC_string(name) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } + static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } \ + static gboolean sampler_layer_data_get_has_##name(struct sampler_layer_data *l) { return l->has_##name; } #define PROC_FIELD_SETHASFUNC_dBamp(type, name, default_value) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } + PROC_FIELD_SETHASFUNC(type, name, default_value) #define PROC_FIELD_SETHASFUNC_enum(type, name, default_value) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, gboolean value) { l->has_##name = value; } + PROC_FIELD_SETHASFUNC(type, name, default_value) #define PROC_FIELD_SETHASFUNC_dahdsr(field, name, default_value) \ DAHDSR_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) #define PROC_FIELD_SETHASFUNC_lfo(field, name, default_value) \ LFO_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) #define PROC_FIELD_SETHASFUNC_eq(field, name, default_value) \ EQ_FIELDS(PROC_SUBSTRUCT_FIELD_SETHASFUNC, field) -#define PROC_FIELD_SETHASFUNC_ccrange(name, parname) \ - static void sampler_layer_data_set_has_##name##_lo(struct sampler_layer_data *l, gboolean value) { l->name.has_locc = value; } \ - static void sampler_layer_data_set_has_##name##_hi(struct sampler_layer_data *l, gboolean value) { l->name.has_hicc = value; } +#define PROC_FIELD_SETHASFUNC_ccrange(name, parname) #define PROC_FIELD_SETHASFUNC_midicurve(name) \ - static void sampler_layer_data_set_has_##name(struct sampler_layer_data *l, uint32_t index, gboolean value) { l->name.has_values[index] = value; } + static gboolean sampler_layer_data_set_get_has_##name(struct sampler_layer_data *l, uint32_t index, int value) \ + { \ + if (value != -1) \ + l->name.has_values[index] = value; \ + return l->name.has_values[index]; \ + } SAMPLER_FIXED_FIELDS(PROC_FIELD_SETHASFUNC) +#define LOFS(field) offsetof(struct sampler_layer_data, field) + +#define FIELD_MOD(name, param, src, src2, dest) \ + { name, LOFS(modulations), slpt_mod_##param, 0, ENCODE_MOD(smsrc_##src, smsrc_##src2, smdest_##dest), NULL, NULL, NULL }, #define FIELD_AMOUNT(name, src, dest) \ - { name, -1, slpt_amount, 0, (smsrc_##src << 16) | smdest_##dest, NULL, NULL }, + FIELD_MOD(name, amount, src, none, dest) #define FIELD_AMOUNT_CC(name, dest) \ - { name "cc#", -1, slpt_amountcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_oncc#", -1, slpt_amountcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_curvecc#", -1, slpt_curvecc, 0, smdest_##dest, NULL, NULL }, \ - { name "_smoothcc#", -1, slpt_smoothcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_stepcc#", -1, slpt_stepcc, 0, smdest_##dest, NULL, NULL }, + FIELD_ALIAS(name "cc#", name "_oncc#") \ + FIELD_MOD(name "_oncc#", amount, CC, none, dest) \ + FIELD_MOD(name "_curvecc#", curveid, CC, none, dest) \ + FIELD_MOD(name "_smoothcc#", smooth, CC, none, dest) \ + FIELD_MOD(name "_stepcc#", step, CC, none, dest) #define FIELD_AMOUNT_CC_(name, dest) \ - { name "_cc#", -1, slpt_amountcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_oncc#", -1, slpt_amountcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_curvecc#", -1, slpt_curvecc, 0, smdest_##dest, NULL, NULL }, \ - { name "_smoothcc#", -1, slpt_smoothcc, 0, smdest_##dest, NULL, NULL }, \ - { name "_stepcc#", -1, slpt_stepcc, 0, smdest_##dest, NULL, NULL }, + FIELD_ALIAS(name "_cc#", name "_oncc#") \ + FIELD_MOD(name "_oncc#", amount, CC, none, dest) \ + FIELD_MOD(name "_curvecc#", curveid, CC, none, dest) \ + FIELD_MOD(name "_smoothcc#", smooth, CC, none, dest) \ + FIELD_MOD(name "_stepcc#", step, CC, none, dest) #define FIELD_VOICE_NIF(name, nif, variant) \ - { name, -1, slpt_voice_nif, 0, variant, nif, NULL }, + { name, LOFS(voice_nifs), slpt_voice_nif, 0, variant, nif, NULL, NULL }, #define FIELD_PREVOICE_NIF(name, nif, variant) \ - { name, -1, slpt_prevoice_nif, 0, variant, nif, NULL }, -#define FIELD_VOICE_CC_NIF(name, nif, variant) \ - { name, -1, slpt_voice_cc_nif, 0, variant, nif, NULL }, -#define FIELD_PREVOICE_CC_NIF(name, nif, variant) \ - { name, -1, slpt_prevoice_cc_nif, 0, variant, nif, NULL }, + { name, LOFS(prevoice_nifs), slpt_prevoice_nif, 0, variant, nif, NULL, NULL }, #define FIELD_ALIAS(alias, name) \ - { alias, -1, slpt_alias, 0, 0, name, NULL }, + { alias, -1, slpt_alias, 0, 0, name, NULL, NULL }, -#define LOFS(field) offsetof(struct sampler_layer_data, field) #define PROC_SUBSTRUCT_FIELD_DESCRIPTOR(name, index, def_value, parent, parent_name, parent_index, parent_struct) \ - { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name }, \ + { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name, sampler_layer_data_##parent##_get_has_##name }, \ #define PROC_SUBSTRUCT_FIELD_DESCRIPTOR_DAHDSR(name, index, def_value, parent, parent_name, parent_index, parent_struct) \ - { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name }, \ + { #parent_name "_" #name, offsetof(struct sampler_layer_data, parent) + offsetof(struct parent_struct, name), slpt_float, def_value, parent_index * 100 + index, NULL, sampler_layer_data_##parent##_set_has_##name, sampler_layer_data_##parent##_get_has_##name }, \ FIELD_VOICE_NIF(#parent_name "_vel2" #name, sampler_nif_vel2env, (parent_index << 4) + snif_env_##name) \ FIELD_AMOUNT_CC(#parent_name "_" #name, ampeg_stage + (parent_index << 4) + snif_env_##name) \ #define PROC_FIELD_DESCRIPTOR(type, name, default_value) \ - { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name }, + { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, #define PROC_FIELD_DESCRIPTOR_dBamp(type, name, default_value) \ - { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name }, + { #name, LOFS(name), slpt_##type, default_value, 0, NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, #define PROC_FIELD_DESCRIPTOR_string(name) \ - { #name, LOFS(name), slpt_string, 0, LOFS(name##_changed), NULL, sampler_layer_data_set_has_##name }, + { #name, LOFS(name), slpt_string, 0, LOFS(name##_changed), NULL, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, #define PROC_FIELD_DESCRIPTOR_enum(enumtype, name, default_value) \ - { #name, LOFS(name), slpt_enum, (double)(enum enumtype)default_value, 0, enumtype##_from_string, sampler_layer_data_set_has_##name }, + { #name, LOFS(name), slpt_enum, (double)(enum enumtype)default_value, 0, enumtype##_from_string, sampler_layer_data_set_has_##name, sampler_layer_data_get_has_##name }, #define PROC_FIELD_DESCRIPTOR_midicurve(name) \ - { #name "_#", LOFS(name), slpt_midicurve, 0, 0, (void *)sampler_layer_data_set_has_##name, NULL }, - -#define FIELD_DEPTHCC_SET(name, dest, attrib) \ - { #name attrib "cc#", -1, slpt_depthcc, 0, (smsrc_##name << 16) | (dest), NULL, NULL }, \ - { #name attrib "_oncc#", -1, slpt_depthcc, 0, (smsrc_##name << 16) | (dest), NULL, NULL }, \ - { #name attrib "_curvecc#", -1, slpt_depth_curvecc, 0, (smsrc_##name << 16) | (dest), NULL, NULL }, \ - { #name attrib "_smoothcc#", -1, slpt_depth_smoothcc, 0, (smsrc_##name << 16) | (dest), NULL, NULL }, \ - { #name attrib "_stepcc#", -1, slpt_depth_stepcc, 0, (smsrc_##name << 16) | (dest), NULL, NULL }, + { #name "_#", LOFS(name), slpt_midicurve, 0, 0, (void *)sampler_layer_data_set_get_has_##name, NULL, NULL }, + +#define FIELD_DEPTH_SET(name, dest, attrib) \ + FIELD_ALIAS(#name attrib "cc#", #name attrib "_oncc#") \ + FIELD_MOD(#name attrib "_oncc#", amount, name, CC, dest) \ + FIELD_MOD(#name attrib "_curvecc#", curveid, name, CC, dest) \ + FIELD_MOD(#name attrib "_smoothcc#", smooth, name, CC, dest) \ + FIELD_MOD(#name attrib "_stepcc#", step, name, CC, dest) \ + FIELD_MOD(#name attrib "polyaft", amount, name, polyaft, dest) \ + FIELD_MOD(#name attrib "chanaft", amount, name, chanaft, dest) \ + FIELD_MOD(#name attrib, amount, name, none, dest) \ #define PROC_FIELD_DESCRIPTOR_dahdsr(field, name, index) \ DAHDSR_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR_DAHDSR, field, name, index, cbox_dahdsr) \ - FIELD_AMOUNT(#name "_depth", name, from_##name) \ - FIELD_DEPTHCC_SET(name, smdest_from_##name, "_depth") \ - { #name "_vel2depth", -1, slpt_modulation, 0, (smsrc_##name << 8) | (smsrc_vel << 20) | (smdest_from_##name), NULL, NULL }, + FIELD_DEPTH_SET(name, from_##name, "_depth") \ + FIELD_MOD(#name "_vel2depth", amount, name, vel, from_##name) #define PROC_FIELD_DESCRIPTOR_lfo(field, name, index) \ LFO_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR, field, name, index, sampler_lfo_params) \ - FIELD_AMOUNT(#name "_depth", name, from_##name) \ FIELD_AMOUNT(#name "_freqpolyaft", polyaft, name##_freq) \ FIELD_AMOUNT(#name "_freqchanaft", chanaft, name##_freq) \ FIELD_AMOUNT_CC(#name "_freq", name##_freq) \ - { #name "_depthpolyaft", -1, slpt_modulation, 0, (smsrc_##name << 8) | (smsrc_polyaft << 20) | (smdest_from_##name), NULL, NULL }, \ - { #name "_depthchanaft", -1, slpt_modulation, 0, (smsrc_##name << 8) | (smsrc_chanaft << 20) | (smdest_from_##name), NULL, NULL }, \ - FIELD_DEPTHCC_SET(name, smdest_from_##name, "_depth") + FIELD_DEPTH_SET(name, from_##name, "_depth") #define PROC_FIELD_DESCRIPTOR_eq(field, name, index) \ EQ_FIELDS(PROC_SUBSTRUCT_FIELD_DESCRIPTOR, field, name, index, sampler_eq_params) \ @@ -456,8 +398,16 @@ SAMPLER_FIXED_FIELDS(PROC_FIELD_SETHASFUNC) FIELD_AMOUNT_CC(#name "_gain", name##_gain) #define PROC_FIELD_DESCRIPTOR_ccrange(field, parname) \ - { #parname "locc#", LOFS(field), slpt_ccrange, 0, 0, NULL, sampler_layer_data_set_has_##field##_lo }, \ - { #parname "hicc#", LOFS(field), slpt_ccrange, 127, 1, NULL, sampler_layer_data_set_has_##field##_hi }, + { #parname "locc#", LOFS(field), slpt_ccrange, 0, 0, NULL, NULL, NULL }, \ + { #parname "hicc#", LOFS(field), slpt_ccrange, 127, 1, NULL, NULL, NULL }, + +#define FIELD_FLEX_LFO(name, field) \ + { name, LOFS(flex_lfos), slpt_flex_lfo, 0, sampler_flex_lfo_value_field_##field, NULL, NULL, NULL }, + +#define NIF_VARIANT_CC 0x01000000 +#define NIF_VARIANT_CURVECC 0x02000000 +#define NIF_VARIANT_STEPCC 0x03000000 +#define NIF_VARIANT_MASK 0xFF000000 struct sampler_layer_param_entry sampler_layer_params[] = { SAMPLER_FIXED_FIELDS(PROC_FIELD_DESCRIPTOR) @@ -467,13 +417,10 @@ struct sampler_layer_param_entry sampler_layer_params[] = { FIELD_AMOUNT("cutoff_polyaft", polyaft, cutoff) FIELD_AMOUNT("resonance_polyaft", polyaft, resonance) - FIELD_AMOUNT("fileg_depth2", fileg, cutoff2) - FIELD_DEPTHCC_SET(fileg, smdest_cutoff2, "_depth2") - { "fileg_vel2depth2", -1, slpt_modulation, 0, (smsrc_fileg << 8) | (smsrc_vel << 20) | (smdest_cutoff2), NULL, NULL }, - FIELD_AMOUNT("fillfo_depth2", fillfo, cutoff2) - { "fillfo_depthpolyaft", -1, slpt_modulation, 0, (smsrc_fillfo << 8) | (smsrc_polyaft << 20) | (smdest_cutoff2), NULL, NULL }, \ - { "fillfo_depthchanaft", -1, slpt_modulation, 0, (smsrc_fillfo << 8) | (smsrc_chanaft << 20) | (smdest_cutoff2), NULL, NULL }, \ - FIELD_DEPTHCC_SET(fillfo, smdest_cutoff2, "_depth2") + FIELD_DEPTH_SET(fileg, cutoff2, "_depth2") + FIELD_MOD("fileg_vel2depth2", amount, fileg, vel, cutoff2) + + FIELD_DEPTH_SET(fillfo, cutoff2, "_depth2") FIELD_AMOUNT("cutoff2_chanaft", chanaft, cutoff2) FIELD_AMOUNT("resonance2_chanaft", chanaft, resonance2) @@ -499,9 +446,20 @@ struct sampler_layer_param_entry sampler_layer_params[] = { FIELD_VOICE_NIF("reloffset_veltrack", sampler_nif_vel2reloffset, 0) FIELD_PREVOICE_NIF("delay_random", sampler_nif_addrandomdelay, 0) FIELD_PREVOICE_NIF("sync_beats", sampler_nif_syncbeats, 0) - FIELD_PREVOICE_CC_NIF("delay_cc#", sampler_nif_cc2delay, 0) - FIELD_VOICE_CC_NIF("reloffset_cc#", sampler_nif_cc2reloffset, 0) - FIELD_VOICE_CC_NIF("offset_cc#", sampler_nif_cc2offset, 0) + FIELD_PREVOICE_NIF("delay_cc#", sampler_nif_cc2delay, NIF_VARIANT_CC) + FIELD_PREVOICE_NIF("delay_curvecc#", sampler_nif_cc2delay, NIF_VARIANT_CURVECC) + FIELD_PREVOICE_NIF("delay_stepcc#", sampler_nif_cc2delay, NIF_VARIANT_STEPCC) + FIELD_VOICE_NIF("reloffset_cc#", sampler_nif_cc2reloffset, NIF_VARIANT_CC) + FIELD_VOICE_NIF("reloffset_curvecc#", sampler_nif_cc2reloffset, NIF_VARIANT_CURVECC) + FIELD_VOICE_NIF("reloffset_stepcc#", sampler_nif_cc2reloffset, NIF_VARIANT_STEPCC) + FIELD_VOICE_NIF("offset_cc#", sampler_nif_cc2offset, NIF_VARIANT_CC) + FIELD_VOICE_NIF("offset_curvecc#", sampler_nif_cc2offset, NIF_VARIANT_CURVECC) + FIELD_VOICE_NIF("offset_stepcc#", sampler_nif_cc2offset, NIF_VARIANT_STEPCC) + + FIELD_FLEX_LFO("lfo#_freq", freq) + FIELD_FLEX_LFO("lfo#_delay", delay) + FIELD_FLEX_LFO("lfo#_fade", fade) + FIELD_FLEX_LFO("lfo#_wave", wave) FIELD_ALIAS("hilev", "hivel") FIELD_ALIAS("lolev", "lovel") @@ -515,7 +473,7 @@ struct sampler_layer_param_entry sampler_layer_params[] = { FIELD_ALIAS("reloffset_oncc#", "reloffset_cc#") FIELD_ALIAS("delay_oncc#", "delay_cc#") - { "genericmod_#_#_#_#", -1, slpt_generic_modulation, 0, 0, NULL, NULL }, + { "genericmod_#_#_#_#", -1, slpt_generic_modulation, 0, 0, NULL, NULL, NULL }, }; #define NPARAMS (sizeof(sampler_layer_params) / sizeof(sampler_layer_params[0])) @@ -541,17 +499,233 @@ void sampler_layer_prepare_params(void) assert(found); e->extra_ptr = found; } + if (i) + { + struct sampler_layer_param_entry *prev_e = &sampler_layer_params[i - 1]; + if (!strcmp(e->name, prev_e->name)) + { + printf("Duplicate parameter %s\n", e->name); + assert(FALSE); + } + } } } -#define VERIFY_FLOAT_VALUE do { if (!atof_C_verify(e->name, value, &fvalue, error)) return FALSE; } while(0) +// This only works for setting. Unsetting is slightly different. +static gboolean override_logic(gboolean is_equal, gboolean has_value, gboolean set_local_value) +{ + if (!set_local_value && has_value) // Do not override locally set values + return FALSE; + // Override if a value or a inherited value is replaced with a local setting. + return (!is_equal) || (set_local_value != has_value); +} + +static inline void mod_key_decode(uint64_t extra_int, const uint32_t *args, struct sampler_modulation_key *mod_key) +{ + uint32_t modsrc = (extra_int & 0xFFF); + uint32_t modsrc2 = ((extra_int >> 12) & 0xFFF); + if (modsrc == MODSRC_CC) + modsrc = args[0]; + if (modsrc2 == MODSRC_CC) + modsrc2 = args[0]; + mod_key->src = modsrc; + mod_key->src2 = modsrc2; + mod_key->dest = ((extra_int >> 24) & 0xFF); +} -gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_param_entry *e, struct sampler_layer *l, const char *value, const uint32_t *args, GError **error) +static inline void nif_key_decode(uint64_t extra_int, void *extra_ptr, const uint32_t *args, struct sampler_noteinitfunc_key *nif_key) +{ + uint32_t variant = extra_int &~ NIF_VARIANT_MASK; + nif_key->notefunc_voice = extra_ptr; + if (extra_int & NIF_VARIANT_MASK) + { + int cc = args[0] & 255; + variant = cc + (variant << 8); + } + nif_key->variant = variant; +} + +static inline void flex_lfo_key_decode(const uint32_t *args, struct sampler_flex_lfo_key *flex_lfo_key) +{ + flex_lfo_key->id = args[0]; +} + +#define OVERRIDE_LOGIC(type) override_logic(!memcmp(p, data_ptr, sizeof(type)), e->get_has_value(&l->data), set_local_value) + +#define CAST_FLOAT_VALUE fvalue = *(double *)data_ptr + +gboolean sampler_layer_param_entry_set_from_ptr(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean set_local_value, const void *data_ptr, const uint32_t *args, GError **error) { void *p = ((uint8_t *)&l->data) + e->offset; - uint32_t cc; - double fvalue; + uint32_t cc = 0; + double fvalue = 0; + struct sampler_modulation_key mod_key = {0, 0, 0}; + struct sampler_noteinitfunc_key nif_key = {{NULL}, 0}; + struct sampler_flex_lfo_key flex_lfo_key = {0}; + switch(e->type) + { + case slpt_midi_note_t: + if (!OVERRIDE_LOGIC(midi_note_t)) + return TRUE; + memcpy(p, data_ptr, sizeof(midi_note_t)); + break; + case slpt_int: + if (!OVERRIDE_LOGIC(int)) + return TRUE; + memcpy(p, data_ptr, sizeof(int)); + break; + case slpt_enum: + case slpt_uint32_t: + if (!OVERRIDE_LOGIC(uint32_t)) + return TRUE; + memcpy(p, data_ptr, sizeof(uint32_t)); + break; + case slpt_string: + { + char **pc = p; + gboolean str_differs = (!*pc != !data_ptr) || strcmp(*pc, data_ptr); + if (!override_logic(!str_differs, e->get_has_value(&l->data), set_local_value)) + return TRUE; + if (str_differs) + { + free(*pc); + *pc = g_strdup(data_ptr); + gboolean *changed_ptr = (gboolean *)(((uint8_t *)&l->data) + e->extra_int); + *changed_ptr = 1; + } + } + break; + case slpt_float: + case slpt_dBamp: + fvalue = *(double *)data_ptr; + if (!override_logic((float)fvalue == *(float *)p, e->get_has_value(&l->data), set_local_value)) + return TRUE; + *(float *)p = fvalue; + break; + case slpt_midicurve: + CAST_FLOAT_VALUE; + if (args[0] >= 0 && args[0] <= 127) + { + gboolean (*setgethasfunc)(struct sampler_layer_data *, uint32_t, int) = e->extra_ptr; + float *dst = &((struct sampler_midi_curve *)p)->values[args[0]]; + if (!override_logic(*dst == fvalue, setgethasfunc(&l->data, args[0], -1), set_local_value)) + return TRUE; + *dst = fvalue; + setgethasfunc(&l->data, args[0], set_local_value); + } + else + { + g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Curve entry index (%u) is out of range for %s", (unsigned)args[0], e->name); + return FALSE; + } + break; + case slpt_ccrange: + { + int number = *(int *)data_ptr; + cc = args[0]; + switch(e->extra_int) { + case 0: + sampler_cc_range_set_locc_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, set_local_value, number); + break; + case 1: + sampler_cc_range_set_hicc_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, set_local_value, number); + break; + default: assert(0); + } + break; + } + case slpt_mod_amount: + CAST_FLOAT_VALUE; + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_set_amount_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); + break; + case slpt_mod_curveid: + CAST_FLOAT_VALUE; + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_set_curve_id_by_offset(l, e->offset, &mod_key, set_local_value, (int)fvalue); + break; + case slpt_mod_smooth: + CAST_FLOAT_VALUE; + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_set_smooth_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); + break; + case slpt_mod_step: + CAST_FLOAT_VALUE; + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_set_step_by_offset(l, e->offset, &mod_key, set_local_value, fvalue); + break; + case slpt_generic_modulation: + CAST_FLOAT_VALUE; + sampler_modulation_set_amount_by_offset(l, e->offset, &(struct sampler_modulation_key){args[0], args[1], args[2]}, set_local_value, fvalue); + sampler_modulation_set_curve_id_by_offset(l, e->offset, &(struct sampler_modulation_key){args[0], args[1], args[2]}, set_local_value, (int)args[3]); + break; + case slpt_voice_nif: + case slpt_prevoice_nif: + CAST_FLOAT_VALUE; + nif_key_decode(e->extra_int, e->extra_ptr, args, &nif_key); + switch(e->extra_int & NIF_VARIANT_MASK) + { + case 0: + case NIF_VARIANT_CC: + sampler_noteinitfunc_set_value_by_offset(l, e->offset, &nif_key, set_local_value, fvalue); + break; + case NIF_VARIANT_CURVECC: + sampler_noteinitfunc_set_curve_id_by_offset(l, e->offset, &nif_key, set_local_value, (int)fvalue); + break; + case NIF_VARIANT_STEPCC: + sampler_noteinitfunc_set_step_by_offset(l, e->offset, &nif_key, set_local_value, fvalue); + break; + } + break; + case slpt_flex_lfo: + CAST_FLOAT_VALUE; + flex_lfo_key_decode(args, &flex_lfo_key); + switch(e->extra_int) + { + case sampler_flex_lfo_value_field_freq: + sampler_flex_lfo_set_freq_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); + break; + case sampler_flex_lfo_value_field_delay: + sampler_flex_lfo_set_delay_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); + break; + case sampler_flex_lfo_value_field_fade: + sampler_flex_lfo_set_fade_by_offset(l, e->offset, &flex_lfo_key, set_local_value, fvalue); + break; + case sampler_flex_lfo_value_field_wave: + sampler_flex_lfo_set_wave_by_offset(l, e->offset, &flex_lfo_key, set_local_value, (int)fvalue); + break; + } + break; + case slpt_reserved: + case slpt_invalid: + case slpt_alias: + printf("Unhandled parameter type of parameter %s\n", e->name); + assert(0); + return FALSE; + } + if (e->set_has_value) + e->set_has_value(&l->data, set_local_value); + if (l->child_layers) { + /* Propagate to children */ + GHashTableIter iter; + g_hash_table_iter_init(&iter, l->child_layers); + gpointer key, value; + while(g_hash_table_iter_next(&iter, &key, &value)) + { + struct sampler_layer *child = value; + if (!sampler_layer_param_entry_set_from_ptr(e, child, FALSE, data_ptr, args, error)) + return FALSE; + } + } + return TRUE; +} + +#define VERIFY_FLOAT_VALUE do { if (!atof_C_verify(e->name, value, &fvalue, error)) return FALSE; } while(0) + +gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean set_local_value, const char *value, const uint32_t *args, GError **error) +{ + double fvalue; switch(e->type) { case slpt_midi_note_t: @@ -562,9 +736,7 @@ gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_pa g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a valid note name for %s", value, e->name); return FALSE; } - *((midi_note_t *)p) = note; - e->set_has_value(&l->data, 1); - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, ¬e, args, error); } case slpt_int: { @@ -576,21 +748,19 @@ gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_pa g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct integer value for %s", value, e->name); return FALSE; } - *(int *)p = number; - e->set_has_value(&l->data, 1); - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); } case slpt_enum: { gboolean (*func)(const char *, uint32_t *value); func = e->extra_ptr; - if (!func(value, (uint32_t *)p)) + uint32_t data = 0; + if (!func(value, &data)) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct value for %s", value, e->name); return FALSE; } - e->set_has_value(&l->data, 1); - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &data, args, error); } case slpt_uint32_t: { @@ -602,45 +772,12 @@ gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_pa g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct unsigned integer value for %s", value, e->name); return FALSE; } - *(uint32_t *)p = number; - e->set_has_value(&l->data, 1); - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); } - case slpt_float: - case slpt_dBamp: - VERIFY_FLOAT_VALUE; - *((float *)p) = fvalue; - e->set_has_value(&l->data, 1); - return TRUE; case slpt_string: - { - char **pc = p; - if (*pc && !strcmp(*pc, value)) - return TRUE; // no change - free(*pc); - *pc = g_strdup(value); - e->set_has_value(&l->data, 1); - gboolean *changed_ptr = (gboolean *)(((uint8_t *)&l->data) + e->extra_int); - *changed_ptr = 1; - } - return TRUE; - case slpt_midicurve: - VERIFY_FLOAT_VALUE; - if (args[0] >= 0 && args[0] <= 127) - { - ((struct sampler_midi_curve *)p)->values[args[0]] = fvalue; - void (*sethasfunc)(struct sampler_layer_data *, uint32_t, gboolean value) = e->extra_ptr; - sethasfunc(&l->data, args[0], 1); - } - else - { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Curve entry index (%u) is out of range for %s", (unsigned)args[0], e->name); - return FALSE; - } - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, value, args, error); case slpt_ccrange: { - cc = args[0]; char *endptr; errno = 0; int number = strtol(value, &endptr, 10); @@ -649,113 +786,31 @@ gboolean sampler_layer_param_entry_set_from_string(const struct sampler_layer_pa g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "'%s' is not a correct control change value for %s", value, e->name); return FALSE; } - struct sampler_cc_range *range = p; - if (!range->has_locc && !range->has_hicc) - range->cc_number = cc; - else if (range->cc_number != cc) - { - //g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Conflicting controller number for %s (%d vs previously used %d)", e->name, cc, (int)range->cc_number); - //return FALSE; - } - switch(e->extra_int) { - case 0: range->locc = number; range->is_active = 1; break; - case 1: range->hicc = number; range->is_active = 1; break; - default: assert(0); - } - e->set_has_value(&l->data, 1); - return TRUE; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error); } - case slpt_depthcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_amount(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_depth_curvecc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_curve(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), (int)fvalue); - return TRUE; - case slpt_depth_smoothcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_smooth(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_depth_stepcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_step(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_amountcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_amount(l, cc, smsrc_none, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_curvecc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_curve(l, cc, smsrc_none, (e->extra_int & 0xFFFF), (int)fvalue); - return TRUE; - case slpt_smoothcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_smooth(l, cc, smsrc_none, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_stepcc: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_set_modulation_step(l, cc, smsrc_none, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_amount: - VERIFY_FLOAT_VALUE; - sampler_layer_set_modulation_amount(l, (e->extra_int >> 16), smsrc_none, (e->extra_int & 0xFFFF), fvalue); - return TRUE; - case slpt_modulation: - VERIFY_FLOAT_VALUE; - sampler_layer_set_modulation_amount(l, (e->extra_int >> 8) & 0xFFF, (e->extra_int >> 20), (e->extra_int & 0xFF), fvalue); - return TRUE; - case slpt_generic_modulation: - VERIFY_FLOAT_VALUE; - sampler_layer_set_modulation_amount(l, args[0], args[1], args[2], fvalue); - sampler_layer_set_modulation_curve(l, args[0], args[1], args[2], args[3]); - return TRUE; - case slpt_voice_nif: - VERIFY_FLOAT_VALUE; - sampler_layer_add_nif(l, e->extra_ptr, NULL, e->extra_int, fvalue); - return TRUE; - case slpt_prevoice_nif: - VERIFY_FLOAT_VALUE; - sampler_layer_add_nif(l, NULL, e->extra_ptr, e->extra_int, fvalue); - return TRUE; - case slpt_voice_cc_nif: - VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_add_nif(l, e->extra_ptr, NULL, cc + (e->extra_int << 8), fvalue); - return TRUE; - case slpt_prevoice_cc_nif: + case slpt_float: + case slpt_dBamp: + default: VERIFY_FLOAT_VALUE; - cc = args[0]; - sampler_layer_add_nif(l, NULL, e->extra_ptr, cc + (e->extra_int << 8), fvalue); - return TRUE; - case slpt_reserved: - case slpt_invalid: - case slpt_alias: - break; + return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &fvalue, args, error); } - printf("Unhandled parameter type of parameter %s\n", e->name); - assert(0); - return FALSE; } #define COPY_NUM_FROM_PARENT(case_value, type) \ case case_value: \ + if (!unset_local_value && e->get_has_value(&l->data)) \ + return TRUE; \ *(type *)p = pp ? *(type *)pp : (type)e->def_value; \ e->set_has_value(&l->data, 0); \ - return TRUE; -gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry *e, struct sampler_layer *l, const uint32_t *args, GError **error) + break; +gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry *e, struct sampler_layer *l, gboolean unset_local_value, const uint32_t *args, GError **error) { void *p = ((uint8_t *)&l->data) + e->offset; void *pp = l->parent ? ((uint8_t *)&l->parent->data) + e->offset : NULL; uint32_t cc; + struct sampler_modulation_key mod_key = {0, 0, 0}; + struct sampler_noteinitfunc_key nif_key = {{NULL}, 0}; + struct sampler_flex_lfo_key flex_lfo_key = {0}; switch(e->type) { @@ -767,6 +822,8 @@ gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry COPY_NUM_FROM_PARENT(slpt_dBamp, float) case slpt_string: { + if (!unset_local_value && e->get_has_value(&l->data)) + return TRUE; char **pc = p; free(*pc); *pc = pp ? g_strdup(*(const char **)pp) : NULL; @@ -779,10 +836,12 @@ gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry if (args[0] >= 0 && args[0] <= 127) { struct sampler_midi_curve *curve = p, *parent_curve = pp; - curve->values[args[0]] = parent_curve ? parent_curve->values[args[0]] : -1; - void (*sethasfunc)(struct sampler_layer_data *, uint32_t, gboolean value) = e->extra_ptr; - sethasfunc(&l->data, args[0], 0); - return TRUE; + gboolean (*setgethasfunc)(struct sampler_layer_data *, uint32_t, gboolean value) = e->extra_ptr; + if (setgethasfunc(&l->data, args[0], -1) && !unset_local_value) + return TRUE; + curve->values[args[0]] = parent_curve ? parent_curve->values[args[0]] : SAMPLER_CURVE_GAP; + setgethasfunc(&l->data, args[0], 0); + break; } else { @@ -790,86 +849,94 @@ gboolean sampler_layer_param_entry_unset(const struct sampler_layer_param_entry return FALSE; } case slpt_ccrange: - { - struct sampler_cc_range *range = p, *prange = pp; cc = args[0]; - if ((range->has_locc || range->has_hicc) && range->cc_number != cc) + if (!sampler_cc_range_unset_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, unset_local_value, 1 << e->extra_int)) { - g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Conflicting controller number for %s (%d vs previously used %d)", e->name, cc, (int)range->cc_number); + if (!unset_local_value) + return TRUE; + g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Controller number %d not used for %s", cc, e->name); return FALSE; } - - switch(e->extra_int) { - case 0: range->locc = prange ? prange->locc : (uint8_t)e->def_value; range->is_active = range->has_hicc || (prange ? prange->is_active : 0); break; - case 1: range->hicc = prange ? prange->hicc : (uint8_t)e->def_value; range->is_active = range->has_locc || (prange ? prange->is_active : 0); break; - default: assert(0); - } - e->set_has_value(&l->data, 0); - return TRUE; - } - case slpt_depthcc: - cc = args[0]; - sampler_layer_unset_modulation_amount(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_depth_curvecc: - cc = args[0]; - sampler_layer_unset_modulation_curve(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_depth_smoothcc: - cc = args[0]; - sampler_layer_unset_modulation_smooth(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_depth_stepcc: - cc = args[0]; - sampler_layer_unset_modulation_step(l, (e->extra_int >> 16), cc, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_amountcc: - cc = args[0]; - sampler_layer_unset_modulation_amount(l, cc, smsrc_none, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_curvecc: - cc = args[0]; - sampler_layer_unset_modulation_curve(l, cc, smsrc_none, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_smoothcc: - cc = args[0]; - sampler_layer_unset_modulation_smooth(l, cc, smsrc_none, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_stepcc: - cc = args[0]; - sampler_layer_unset_modulation_step(l, cc, smsrc_none, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_amount: - sampler_layer_unset_modulation_amount(l, (e->extra_int >> 16), smsrc_none, (e->extra_int & 0xFFFF), TRUE); - return TRUE; - case slpt_modulation: - sampler_layer_unset_modulation_amount(l, (e->extra_int >> 8) & 0xFFF, (e->extra_int >> 20), (e->extra_int & 0xFF), TRUE); - return TRUE; + break; + case slpt_mod_amount: + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_amount); + break; + case slpt_mod_curveid: + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_curve_id); + break; + case slpt_mod_smooth: + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_smooth); + break; + case slpt_mod_step: + mod_key_decode(e->extra_int, args, &mod_key); + sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, 1 << sampler_modulation_value_field_step); + break; + case slpt_generic_modulation: + mod_key = (struct sampler_modulation_key){args[0], args[1], args[2]}; + sampler_modulation_unset_by_offset(l, e->offset, &mod_key, unset_local_value, (1 << sampler_modulation_value_field_amount) | (1 << sampler_modulation_value_field_curve_id)); + break; case slpt_voice_nif: - sampler_layer_remove_nif(l, e->extra_ptr, NULL, e->extra_int, FALSE); - return TRUE; case slpt_prevoice_nif: - sampler_layer_remove_nif(l, NULL, e->extra_ptr, e->extra_int, FALSE); - return TRUE; - case slpt_voice_cc_nif: - cc = args[0]; - sampler_layer_remove_nif(l, e->extra_ptr, NULL, cc + (e->extra_int << 8), FALSE); - return TRUE; - case slpt_prevoice_cc_nif: - cc = args[0]; - sampler_layer_remove_nif(l, NULL, e->extra_ptr, cc + (e->extra_int << 8), FALSE); - return TRUE; - case slpt_generic_modulation: - sampler_layer_unset_modulation_amount(l, args[0], args[1], args[2], TRUE); - return TRUE; + { + nif_key_decode(e->extra_int, e->extra_ptr, args, &nif_key); + static const uint32_t value_fields[] = { + sampler_noteinitfunc_value_field_value, sampler_noteinitfunc_value_field_value, + sampler_noteinitfunc_value_field_curve_id, sampler_noteinitfunc_value_field_step, + }; + if (!sampler_noteinitfunc_unset_by_offset(l, e->offset, &nif_key, unset_local_value, 1 << value_fields[e->extra_int >> 24])) + { + if (!unset_local_value) + return TRUE; + if (e->extra_int & NIF_VARIANT_MASK) + g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Controller number %d not used for %s", args[0], e->name); + else + g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s not set", e->name); + return FALSE; + } + break; + } + case slpt_flex_lfo: + flex_lfo_key_decode(args, &flex_lfo_key); + switch(e->extra_int) + { + case sampler_flex_lfo_value_field_freq: + sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_freq); + break; + case sampler_flex_lfo_value_field_delay: + sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_delay); + break; + case sampler_flex_lfo_value_field_fade: + sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_fade); + break; + case sampler_flex_lfo_value_field_wave: + sampler_flex_lfo_unset_by_offset(l, e->offset, &flex_lfo_key, unset_local_value, 1 << sampler_flex_lfo_value_field_wave); + break; + } + break; case slpt_invalid: case slpt_reserved: case slpt_alias: - break; + default: + printf("Unhandled parameter type of parameter %s\n", e->name); + assert(0); + return FALSE; } - printf("Unhandled parameter type of parameter %s\n", e->name); - assert(0); - return FALSE; + if (l->child_layers) { + /* Propagate to children */ + GHashTableIter iter; + g_hash_table_iter_init(&iter, l->child_layers); + gpointer key, value; + while(g_hash_table_iter_next(&iter, &key, &value)) + { + struct sampler_layer *child = value; + if (!sampler_layer_param_entry_unset(e, child, FALSE, args, error)) + return FALSE; + } + } + return TRUE; } #undef COPY_NUM_FROM_PARENT @@ -944,7 +1011,7 @@ int sampler_layer_apply_fixed_param(struct sampler_layer *l, const char *key, co uint32_t args[10]; const struct sampler_layer_param_entry *e = sampler_layer_param_find(key, args); if (e) - return sampler_layer_param_entry_set_from_string(e, l, value, args, error); + return sampler_layer_param_entry_set_from_string(e, l, TRUE, value, args, error); else return -1; } @@ -954,7 +1021,7 @@ int sampler_layer_unapply_fixed_param(struct sampler_layer *l, const char *key, uint32_t args[10]; const struct sampler_layer_param_entry *e = sampler_layer_param_find(key, args); if (e) - return sampler_layer_param_entry_unset(e, l, args, error); + return sampler_layer_param_entry_unset(e, l, TRUE, args, error); else return -1; } @@ -1070,12 +1137,7 @@ static gboolean sampler_layer_process_cmd(struct cbox_command_target *ct, struct EQ_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \ EQ_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld) #define PROC_FIELDS_INITIALISER_ccrange(name, parname) \ - ld->name.locc = 0; \ - ld->name.hicc = 127; \ - ld->name.cc_number = 0; \ - ld->name.has_locc = 0; \ - ld->name.has_hicc = 0; \ - ld->name.is_active = 0; + ld->name = NULL; CBOX_CLASS_DEFINITION_ROOT(sampler_layer) @@ -1104,21 +1166,22 @@ struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler struct sampler_layer_data *ld = &l->data; SAMPLER_FIXED_FIELDS(PROC_FIELDS_INITIALISER) - ld->eff_waveform = NULL; - ld->eff_freq = 44100; + ld->computed.eff_waveform = NULL; + ld->computed.eff_freq = 44100; ld->modulations = NULL; ld->voice_nifs = NULL; ld->prevoice_nifs = NULL; - ld->eff_use_keyswitch = 0; + ld->computed.eff_use_keyswitch = 0; if (!parent) { // Systemwide default instead? - sampler_layer_set_modulation_amount(l, 74, smsrc_none, smdest_cutoff, 9600); - sampler_layer_set_modulation_curve(l, 74, smsrc_none, smdest_cutoff, 1); - sampler_layer_set_modulation_amount(l, 71, smsrc_none, smdest_resonance, 12); - sampler_layer_set_modulation_curve(l, 71, smsrc_none, smdest_resonance, 1); - sampler_layer_set_modulation_amount(l, smsrc_pitchlfo, 1, smdest_pitch, 100); + uint32_t mod_offset = LOFS(modulations); + sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){74, smsrc_none, smdest_cutoff}, TRUE, 9600); + sampler_modulation_set_curve_id_by_offset(l, mod_offset, &(struct sampler_modulation_key){74, smsrc_none, smdest_cutoff}, TRUE, 1); + sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){71, smsrc_none, smdest_resonance}, TRUE, 12); + sampler_modulation_set_curve_id_by_offset(l, mod_offset, &(struct sampler_modulation_key){71, smsrc_none, smdest_resonance}, TRUE, 1); + sampler_modulation_set_amount_by_offset(l, mod_offset, &(struct sampler_modulation_key){smsrc_pitchlfo, 1, smdest_pitch}, TRUE, 100); } l->runtime = NULL; l->unknown_keys = NULL; @@ -1152,99 +1215,18 @@ struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler if (!copy_hasattr) \ EQ_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst) #define PROC_FIELDS_CLONE_ccrange(name, parname) \ - dst->name.locc = src->name.locc; \ - dst->name.hicc = src->name.hicc; \ - dst->name.cc_number = src->name.cc_number; \ - dst->name.is_active = src->name.is_active; \ - dst->name.has_locc = copy_hasattr ? src->name.has_locc : FALSE; \ - dst->name.has_hicc = copy_hasattr ? src->name.has_hicc : FALSE; - -static GSList *clone_nifs(GSList *nifs, gboolean copy_hasattr) -{ - nifs = g_slist_copy(nifs); - for(GSList *nif = nifs; nif; nif = nif->next) - { - struct sampler_noteinitfunc *dstn = g_malloc(sizeof(struct sampler_noteinitfunc)); - struct sampler_noteinitfunc *srcn = nif->data; - memcpy(dstn, srcn, sizeof(struct sampler_noteinitfunc)); - dstn->has_value = copy_hasattr ? srcn->has_value : FALSE; - nif->data = dstn; - } - return nifs; -} + dst->name = sampler_cc_range_clone(src->name, copy_hasattr); void sampler_layer_data_clone(struct sampler_layer_data *dst, const struct sampler_layer_data *src, gboolean copy_hasattr) { SAMPLER_FIXED_FIELDS(PROC_FIELDS_CLONE) - dst->modulations = g_slist_copy(src->modulations); - for(GSList *mod = dst->modulations; mod; mod = mod->next) - { - struct sampler_modulation *srcm = mod->data; - struct sampler_modulation *dstm = g_malloc(sizeof(struct sampler_modulation)); - memcpy(dstm, srcm, sizeof(struct sampler_modulation)); - dstm->has_amount = copy_hasattr ? srcm->has_amount : FALSE; - dstm->has_curve = copy_hasattr ? srcm->has_curve : FALSE; - dstm->has_smooth = copy_hasattr ? srcm->has_smooth : FALSE; - dstm->has_step = copy_hasattr ? srcm->has_step : FALSE; - mod->data = dstm; - } - dst->voice_nifs = clone_nifs(src->voice_nifs, copy_hasattr); - dst->prevoice_nifs = clone_nifs(src->prevoice_nifs, copy_hasattr); - dst->eff_waveform = src->eff_waveform; - if (dst->eff_waveform) - cbox_waveform_ref(dst->eff_waveform); -} - -#define PROC_FIELDS_CLONEPARENT(type, name, def_value) \ - if (!l->has_##name) \ - l->name = parent ? parent->name : def_value; -#define PROC_FIELDS_CLONEPARENT_string(name) \ - if (!l->has_##name && (!l->name || !parent->name || strcmp(l->name, parent->name))) { \ - g_free(l->name); \ - l->name = parent && parent->name ? g_strdup(parent->name) : NULL; \ - l->name##_changed = parent && parent->name##_changed; \ - } -// XXXKF use a better default -#define PROC_FIELDS_CLONEPARENT_midicurve(name) \ - for (uint32_t i = 0; i < 128; ++i) \ - if (!l->name.has_values[i]) \ - l->name.values[i] = parent ? parent->name.values[i] : SAMPLER_CURVE_GAP; -#define PROC_FIELDS_CLONEPARENT_dBamp PROC_FIELDS_CLONEPARENT -#define PROC_FIELDS_CLONEPARENT_enum PROC_FIELDS_CLONEPARENT -#define PROC_FIELDS_CLONEPARENT_dahdsr(name, parname, index) \ - DAHDSR_FIELDS(PROC_SUBSTRUCT_CLONEPARENT, name, l) -#define PROC_FIELDS_CLONEPARENT_lfo(name, parname, index) \ - LFO_FIELDS(PROC_SUBSTRUCT_CLONEPARENT, name, l) -#define PROC_FIELDS_CLONEPARENT_eq(name, parname, index) \ - EQ_FIELDS(PROC_SUBSTRUCT_CLONEPARENT, name, l) -#define PROC_FIELDS_CLONEPARENT_ccrange(name, parname) \ - if (!l->name.has_locc) \ - l->name.locc = parent ? parent->name.locc : 0; \ - if (!l->name.has_hicc) \ - l->name.hicc = parent ? parent->name.hicc : 127; \ - if (!l->name.has_locc && !l->name.has_hicc) {\ - l->name.is_active = parent ? parent->name.is_active : 0; \ - l->name.cc_number = parent ? parent->name.cc_number : -1; \ - } - -static void sampler_layer_data_getdefaults(struct sampler_layer_data *l, struct sampler_layer_data *parent) -{ - SAMPLER_FIXED_FIELDS(PROC_FIELDS_CLONEPARENT) - // XXXKF: add handling for velcurve - if (parent) - { - // set NIFs used by parent - for(GSList *mod = parent->voice_nifs; mod; mod = mod->next) - { - struct sampler_noteinitfunc *nif = mod->data; - sampler_layer_data_add_nif(l, nif->notefunc_voice, nif->notefunc_prevoice, nif->variant, nif->param, TRUE); - } - for(GSList *mod = parent->prevoice_nifs; mod; mod = mod->next) - { - struct sampler_noteinitfunc *nif = mod->data; - sampler_layer_data_add_nif(l, nif->notefunc_voice, nif->notefunc_prevoice, nif->variant, nif->param, TRUE); - } - } + dst->modulations = sampler_modulation_clone(src->modulations, copy_hasattr); + dst->voice_nifs = sampler_noteinitfunc_clone(src->voice_nifs, copy_hasattr); + dst->prevoice_nifs = sampler_noteinitfunc_clone(src->prevoice_nifs, copy_hasattr); + dst->flex_lfos = sampler_flex_lfo_clone(src->flex_lfos, copy_hasattr); + dst->computed.eff_waveform = src->computed.eff_waveform; + if (dst->computed.eff_waveform) + cbox_waveform_ref(dst->computed.eff_waveform); } void sampler_midi_curve_init(struct sampler_midi_curve *curve) @@ -1312,7 +1294,7 @@ static inline int sampler_filter_num_stages(float cutoff, enum sampler_filter_ty #define PROC_FIELDS_FINALISER(type, name, def_value) #define PROC_FIELDS_FINALISER_string(name) #define PROC_FIELDS_FINALISER_midicurve(name) \ - sampler_midi_curve_interpolate(&l->name, l->eff_##name, START_VALUE_##name, END_VALUE_##name, IS_QUADRATIC_##name); + sampler_midi_curve_interpolate(&l->name, l->computed.eff_##name, START_VALUE_##name, END_VALUE_##name, IS_QUADRATIC_##name); #define PROC_FIELDS_FINALISER_enum(type, name, def_value) #define PROC_FIELDS_FINALISER_dBamp(type, name, def_value) \ l->name##_linearized = dB2gain(l->name); @@ -1327,31 +1309,30 @@ void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_la struct sampler_module *m = p->module; SAMPLER_FIXED_FIELDS(PROC_FIELDS_FINALISER) - sampler_layer_data_getdefaults(l, parent); - // Handle change of sample in the parent group without override on region level if (parent && (l->sample_changed || parent->sample_changed)) { - struct cbox_waveform *oldwf = l->eff_waveform; + struct cbox_waveform *oldwf = l->computed.eff_waveform; if (l->sample && *l->sample) { GError *error = NULL; - l->eff_waveform = cbox_wavebank_get_waveform(p->name, p->tarfile, p->sample_dir, l->sample, &error); - if (!l->eff_waveform) + l->computed.eff_waveform = cbox_wavebank_get_waveform(p->name, p->tarfile, p->sample_dir, l->sample, &error); + if (!l->computed.eff_waveform) { g_warning("Cannot load waveform %s: %s", l->sample, error ? error->message : "unknown error"); g_error_free(error); } } else - l->eff_waveform = NULL; + l->computed.eff_waveform = NULL; if (oldwf) cbox_waveform_unref(oldwf); + l->computed.eff_is_silent = !l->sample || !strcmp(l->sample, "*silence"); l->sample_changed = FALSE; } - l->eff_use_keyswitch = ((l->sw_down != -1) || (l->sw_up != -1) || (l->sw_last != -1) || (l->sw_previous != -1)); - l->eff_use_simple_trigger_logic = + l->computed.eff_use_keyswitch = ((l->sw_down != -1) || (l->sw_up != -1) || (l->sw_last != -1) || (l->sw_previous != -1)); + l->computed.eff_use_simple_trigger_logic = (l->seq_length == 1 && l->seq_position == 1) && (l->trigger != stm_first && l->trigger != stm_legato) && (l->lochan == 1 && l->hichan == 16) && @@ -1360,28 +1341,31 @@ void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_la (l->lochanaft == 0 && l->hichanaft == 127) && (l->lopolyaft == 0 && l->hipolyaft == 127) && (l->lobpm == 0 && l->hibpm == NO_HI_BPM_VALUE) && - !l->cc.is_active && !l->eff_use_keyswitch; - l->eff_use_xfcc = l->xfin_cc.is_active || l->xfout_cc.is_active; - l->eff_freq = (l->eff_waveform && l->eff_waveform->info.samplerate) ? l->eff_waveform->info.samplerate : 44100; - l->eff_loop_mode = l->loop_mode; + !l->cc && !l->computed.eff_use_keyswitch; + l->computed.eff_use_xfcc = l->xfin_cc || l->xfout_cc; + l->computed.eff_use_channel_mixer = l->position != 0 || l->width != 100; + l->computed.eff_freq = (l->computed.eff_waveform && l->computed.eff_waveform->info.samplerate) ? l->computed.eff_waveform->info.samplerate : 44100; + l->computed.eff_loop_mode = l->loop_mode; + l->computed.eff_use_filter_mods = l->cutoff != -1 || l->cutoff2 != -1; if (l->loop_mode == slm_unknown) { - if (l->eff_waveform && l->eff_waveform->has_loop) - l->eff_loop_mode = slm_loop_continuous; + if (l->computed.eff_waveform && l->computed.eff_waveform->has_loop) + l->computed.eff_loop_mode = slm_loop_continuous; else - if (l->eff_waveform) - l->eff_loop_mode = l->loop_end == 0 ? slm_no_loop : slm_loop_continuous; + if (l->computed.eff_waveform) + l->computed.eff_loop_mode = l->loop_end == 0 ? slm_no_loop : slm_loop_continuous; } - - if (l->eff_loop_mode == slm_one_shot || l->eff_loop_mode == slm_no_loop || l->eff_loop_mode == slm_one_shot_chokeable) - l->loop_start = SAMPLER_NO_LOOP; - if ((l->eff_loop_mode == slm_loop_continuous || l->eff_loop_mode == slm_loop_sustain) && l->loop_start == SAMPLER_NO_LOOP) - l->loop_start = 0; - if ((l->eff_loop_mode == slm_loop_continuous || l->eff_loop_mode == slm_loop_sustain) && l->loop_start == 0 && l->eff_waveform && l->eff_waveform->has_loop) - l->loop_start = l->eff_waveform->loop_start; - if (l->loop_end == 0 && l->eff_waveform != NULL) - l->loop_end = l->eff_waveform->has_loop ? l->eff_waveform->loop_end : l->eff_waveform->info.frames; + l->computed.eff_loop_start = l->loop_start; + l->computed.eff_loop_end = l->loop_end; + if (l->computed.eff_loop_mode == slm_one_shot || l->computed.eff_loop_mode == slm_no_loop || l->computed.eff_loop_mode == slm_one_shot_chokeable) + l->computed.eff_loop_start = SAMPLER_NO_LOOP; + if ((l->computed.eff_loop_mode == slm_loop_continuous || l->computed.eff_loop_mode == slm_loop_sustain) && l->computed.eff_loop_start == SAMPLER_NO_LOOP) + l->computed.eff_loop_start = 0; + if ((l->computed.eff_loop_mode == slm_loop_continuous || l->computed.eff_loop_mode == slm_loop_sustain) && l->computed.eff_loop_start == 0 && l->computed.eff_waveform && l->computed.eff_waveform->has_loop) + l->computed.eff_loop_start = l->computed.eff_waveform->loop_start; + if (l->loop_end == 0 && l->computed.eff_waveform != NULL) + l->computed.eff_loop_end = l->computed.eff_waveform->has_loop ? l->computed.eff_waveform->loop_end : l->computed.eff_waveform->info.frames; if (l->off_mode == som_unknown) l->off_mode = l->off_by != 0 ? som_fast : som_normal; @@ -1394,45 +1378,45 @@ void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_la // and 3 (N) frames at the start of the loop, and play it; in rare cases this will need to be // repeated twice if output write pointer is close to CBOX_BLOCK_SIZE or playback rate is very low, // but that's OK. - if (l->eff_waveform && l->eff_waveform->preloaded_frames == (size_t)l->eff_waveform->info.frames) + if (l->computed.eff_waveform && l->computed.eff_waveform->preloaded_frames == (size_t)l->computed.eff_waveform->info.frames) { - int shift = l->eff_waveform->info.channels == 2 ? 1 : 0; + int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0; uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift; - memcpy(&l->scratch_loop[0], &l->eff_waveform->data[(l->loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); - memcpy(&l->scratch_end[0], &l->eff_waveform->data[(l->loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); - memset(l->scratch_end + halfscratch, 0, halfscratch * sizeof(int16_t)); - if (l->loop_start != (uint32_t)-1) - memcpy(l->scratch_loop + halfscratch, &l->eff_waveform->data[l->loop_start << shift], halfscratch * sizeof(int16_t)); + memcpy(&l->computed.scratch_loop[0], &l->computed.eff_waveform->data[(l->computed.eff_loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); + memcpy(&l->computed.scratch_end[0], &l->computed.eff_waveform->data[(l->computed.eff_loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) ); + memset(l->computed.scratch_end + halfscratch, 0, halfscratch * sizeof(int16_t)); + if (l->computed.eff_loop_start != (uint32_t)-1) + memcpy(l->computed.scratch_loop + halfscratch, &l->computed.eff_waveform->data[l->computed.eff_loop_start << shift], halfscratch * sizeof(int16_t)); else - memset(l->scratch_loop + halfscratch, 0, halfscratch * sizeof(int16_t)); + memset(l->computed.scratch_loop + halfscratch, 0, halfscratch * sizeof(int16_t)); } if (l->cutoff < 20) - l->logcutoff = -1; + l->computed.logcutoff = -1; else - l->logcutoff = 1200.0 * log(l->cutoff / 440.0) / log(2) + 5700.0; + l->computed.logcutoff = 1200.0 * log(l->cutoff / 440.0) / log(2) + 5700.0; if (l->cutoff2 < 20) - l->logcutoff2 = -1; + l->computed.logcutoff2 = -1; else - l->logcutoff2 = 1200.0 * log(l->cutoff2 / 440.0) / log(2) + 5700.0; + l->computed.logcutoff2 = 1200.0 * log(l->cutoff2 / 440.0) / log(2) + 5700.0; - l->eq_bitmask = ((l->eq1.gain != 0 || l->eq1.vel2gain != 0) ? 1 : 0) + l->computed.eq_bitmask = ((l->eq1.gain != 0 || l->eq1.vel2gain != 0) ? 1 : 0) | ((l->eq2.gain != 0 || l->eq2.vel2gain != 0) ? 2 : 0) | ((l->eq3.gain != 0 || l->eq3.vel2gain != 0) ? 4 : 0); - l->mod_bitmask = 0; - for(GSList *mod = l->modulations; mod; mod = g_slist_next(mod)) + l->computed.mod_bitmask = 0; + for(struct sampler_modulation *mod = l->modulations; mod; mod = mod->next) { - const struct sampler_modulation *mval = (const struct sampler_modulation *)mod->data; - if (mval->dest >= smdest_eg_stage_start && mval->dest <= smdest_eg_stage_end) - l->mod_bitmask |= slmb_ampeg_cc << ((mval->dest >> 4) & 3); + const struct sampler_modulation_key *mk = &mod->key; + if (mk->dest >= smdest_eg_stage_start && mk->dest <= smdest_eg_stage_end) + l->computed.mod_bitmask |= slmb_ampeg_cc << ((mk->dest >> 4) & 3); } - l->use_prevoice = (l->delay || l->prevoice_nifs); - l->eff_num_stages = sampler_filter_num_stages(l->cutoff, l->fil_type); - l->eff_num_stages2 = sampler_filter_num_stages(l->cutoff2, l->fil2_type); + l->computed.eff_use_prevoice = (l->delay || l->prevoice_nifs); + l->computed.eff_num_stages = sampler_filter_num_stages(l->cutoff, l->fil_type); + l->computed.eff_num_stages2 = sampler_filter_num_stages(l->cutoff2, l->fil2_type); - l->resonance_scaled = pow(l->resonance_linearized, 1.f / l->eff_num_stages); - l->resonance2_scaled = pow(l->resonance2_linearized, 1.f / l->eff_num_stages2); + l->computed.resonance_scaled = pow(l->resonance_linearized, 1.f / l->computed.eff_num_stages); + l->computed.resonance2_scaled = pow(l->resonance2_linearized, 1.f / l->computed.eff_num_stages2); } void sampler_layer_reset_switches(struct sampler_layer *l, struct sampler_module *m) @@ -1565,10 +1549,16 @@ gboolean sampler_layer_unapply_param(struct sampler_layer *layer, const char *ke #define PROC_FIELDS_TO_FILEPTR_eq(name, parname, index) \ EQ_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname) #define PROC_FIELDS_TO_FILEPTR_ccrange(name, parname) \ - if (show_inherited || l->name.has_locc) \ - g_string_append_printf(outstr, " " #parname "locc%d=%d", l->name.cc_number, l->name.locc); \ - if (show_inherited || l->name.has_hicc) \ - g_string_append_printf(outstr, " " #parname "hicc%d=%d", l->name.cc_number, l->name.hicc); + { \ + struct sampler_cc_range *range = l->name; \ + while (range) { \ + if (show_inherited || range->value.has_locc) \ + g_string_append_printf(outstr, " " #parname "locc%d=%d", range->key.cc_number, range->value.locc); \ + if (show_inherited || range->value.has_hicc) \ + g_string_append_printf(outstr, " " #parname "hicc%d=%d", range->key.cc_number, range->value.hicc); \ + range = range->next; \ + } \ + } static const char *addrandom_variants[] = { "amp", "fil", "pitch" }; static const char *env_stages[] = { "delay", "attack", "hold", "decay", "sustain", "release", "start" }; @@ -1579,7 +1569,7 @@ static const char *moddest_names[] = { "gain", "pitch", "cutoff", "resonance", " "eq3_freq", "eq3_bw", "eq3_gain", }; -static void mod_cc_attrib_to_string(GString *outstr, const char *attrib, const struct sampler_modulation *md, const char *floatbuf) +static void mod_cc_attrib_to_string(GString *outstr, const char *attrib, const struct sampler_modulation_key *md, const char *floatbuf) { if (md->dest >= smdest_eg_stage_start && md->dest <= smdest_eg_stage_end) { @@ -1594,24 +1584,40 @@ static void mod_cc_attrib_to_string(GString *outstr, const char *attrib, const s (md->src == smsrc_fillfo && md->dest == smdest_cutoff) || (md->src == smsrc_pitchlfo && md->dest == smdest_pitch)) { - if (md->src2 < 120) + if (md->src2 < EXT_CC_COUNT) g_string_append_printf(outstr, " %s_depth%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); } else if ((md->src == smsrc_ampenv && md->dest == smdest_gain) || (md->src == smsrc_filenv && md->dest == smdest_cutoff) || (md->src == smsrc_pitchenv && md->dest == smdest_pitch)) { - if (md->src2 < 120) + if (md->src2 < EXT_CC_COUNT) g_string_append_printf(outstr, " %s_depth%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); } else if ((md->src == smsrc_filenv && md->dest == smdest_cutoff2) || (md->src == smsrc_fillfo && md->dest == smdest_cutoff2)) { - if (md->src2 < 120) + if (md->src2 < EXT_CC_COUNT) g_string_append_printf(outstr, " %s_depth2%s%d=%s", modsrc_names[md->src - smsrc_perchan_count], attrib, md->src2, floatbuf); } else - assert(md->src2 >= 120); + assert(md->src2 >= EXT_CC_COUNT); +} + +static void nif_attrib_to_string(GString *outstr, const char *attrib, const struct sampler_noteinitfunc *nd, const char *floatbuf) +{ + int v = nd->key.variant; + if (nd->value.value) + g_string_append_printf(outstr, " %s_cc%d=%s", attrib, v, floatbuf); + if (nd->value.curve_id) + g_string_append_printf(outstr, " %s_curvecc%d=%d", attrib, v, nd->value.curve_id); + if (nd->value.step) + { + char floatbuf2[G_ASCII_DTOSTR_BUF_SIZE]; + int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE; + g_ascii_dtostr(floatbuf2, floatbufsize, nd->value.step); + g_string_append_printf(outstr, " %s_stepcc%d=%s", attrib, v, floatbuf2); + } } gchar *sampler_layer_to_string(struct sampler_layer *lr, gboolean show_inherited) @@ -1623,173 +1629,191 @@ gchar *sampler_layer_to_string(struct sampler_layer *lr, gboolean show_inherited int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE; SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_FILEPTR) - for(GSList *nif = l->voice_nifs; nif; nif = nif->next) + for(struct sampler_noteinitfunc *nd = l->voice_nifs; nd; nd = nd->next) { - struct sampler_noteinitfunc *nd = nif->data; - if (!nd->has_value && !show_inherited) + if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited) continue; #define PROC_ENVSTAGE_NAME(name, index, def_value) #name, static const char *env_stages[] = { DAHDSR_FIELDS(PROC_ENVSTAGE_NAME) "start" }; - int v = nd->variant; - g_ascii_dtostr(floatbuf, floatbufsize, nd->param); + uint32_t v = nd->key.variant; + g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value); - if (nd->notefunc_voice == sampler_nif_addrandom && v >= 0 && v <= 2) - g_string_append_printf(outstr, " %s_random=%s", addrandom_variants[nd->variant], floatbuf); - else if (nd->notefunc_voice == sampler_nif_vel2pitch) + if (nd->key.notefunc_voice == sampler_nif_addrandom && v >= 0 && v <= 2) + g_string_append_printf(outstr, " %s_random=%s", addrandom_variants[v], floatbuf); + else if (nd->key.notefunc_voice == sampler_nif_vel2pitch) g_string_append_printf(outstr, " pitch_veltrack=%s", floatbuf); - else if (nd->notefunc_voice == sampler_nif_vel2reloffset) + else if (nd->key.notefunc_voice == sampler_nif_vel2reloffset) g_string_append_printf(outstr, " reloffset_veltrack=%s", floatbuf); - else if (nd->notefunc_voice == sampler_nif_cc2reloffset) - g_string_append_printf(outstr, " reloffset_cc%d=%s", nd->variant, floatbuf); - else if (nd->notefunc_voice == sampler_nif_vel2offset) + else if (nd->key.notefunc_voice == sampler_nif_cc2reloffset) + nif_attrib_to_string(outstr, "reloffset", nd, floatbuf); + else if (nd->key.notefunc_voice == sampler_nif_vel2offset) g_string_append_printf(outstr, " offset_veltrack=%s", floatbuf); - else if (nd->notefunc_voice == sampler_nif_cc2offset) - g_string_append_printf(outstr, " offset_cc%d=%s", nd->variant, floatbuf); - else if (nd->notefunc_voice == sampler_nif_vel2env && (v & 15) >= snif_env_delay && (v & 15) <= snif_env_start && ((v >> 4) & 3) < 3) - g_string_append_printf(outstr, " %seg_vel2%s=%s", addrandom_variants[nd->variant >> 4], env_stages[1 + (v & 15)], floatbuf); + else if (nd->key.notefunc_voice == sampler_nif_cc2offset) + nif_attrib_to_string(outstr, "offset", nd, floatbuf); + else if (nd->key.notefunc_voice == sampler_nif_vel2env && (v & 15) >= snif_env_delay && (v & 15) <= snif_env_start && ((v >> 4) & 3) < 3) + g_string_append_printf(outstr, " %seg_vel2%s=%s", addrandom_variants[v >> 4], env_stages[1 + (v & 15)], floatbuf); else assert(0); // unknown NIF } - for(GSList *nif = l->prevoice_nifs; nif; nif = nif->next) + for(struct sampler_noteinitfunc *nd = l->prevoice_nifs; nd; nd = nd->next) { - struct sampler_noteinitfunc *nd = nif->data; - if (!nd->has_value && !show_inherited) + if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited) continue; - int v = nd->variant; - g_ascii_dtostr(floatbuf, floatbufsize, nd->param); + g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value); - if (nd->notefunc_prevoice == sampler_nif_cc2delay) - g_string_append_printf(outstr, " delay_cc%d=%s", v, floatbuf); - else if (nd->notefunc_prevoice == sampler_nif_addrandomdelay) + if (nd->key.notefunc_prevoice == sampler_nif_cc2delay) + nif_attrib_to_string(outstr, "delay", nd, floatbuf); + else if (nd->key.notefunc_prevoice == sampler_nif_addrandomdelay) g_string_append_printf(outstr, " delay_random=%s", floatbuf); else assert(0); // unknown NIF } - for(GSList *mod = l->modulations; mod; mod = mod->next) + for(struct sampler_flex_lfo *flfo = l->flex_lfos; flfo; flfo = flfo->next) + { + if (flfo->value.has_freq || show_inherited) + { + g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.freq); + g_string_append_printf(outstr, " lfo%d_freq=%s", (int)flfo->key.id, floatbuf); + } + if (flfo->value.has_delay || show_inherited) + { + g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.delay); + g_string_append_printf(outstr, " lfo%d_delay=%s", (int)flfo->key.id, floatbuf); + } + if (flfo->value.has_fade || show_inherited) + { + g_ascii_dtostr(floatbuf, floatbufsize, flfo->value.fade); + g_string_append_printf(outstr, " lfo%d_fade=%s", (int)flfo->key.id, floatbuf); + } + if (flfo->value.has_wave || show_inherited) + g_string_append_printf(outstr, " lfo%d_wave=%d", (int)flfo->key.id, flfo->value.wave); + } + for(struct sampler_modulation *md = l->modulations; md; md = md->next) { - struct sampler_modulation *md = mod->data; - if (md->has_curve || show_inherited) + const struct sampler_modulation_key *mk = &md->key; + const struct sampler_modulation_value *mv = &md->value; + if (mv->has_curve || show_inherited) { - g_ascii_dtostr(floatbuf, floatbufsize, md->curve_id); - mod_cc_attrib_to_string(outstr, "_curvecc", md, floatbuf); + g_ascii_dtostr(floatbuf, floatbufsize, mv->curve_id); + mod_cc_attrib_to_string(outstr, "_curvecc", mk, floatbuf); } - if (md->has_smooth || show_inherited) + if (mv->has_smooth || show_inherited) { - g_ascii_dtostr(floatbuf, floatbufsize, md->smooth); - mod_cc_attrib_to_string(outstr, "_smoothcc", md, floatbuf); + g_ascii_dtostr(floatbuf, floatbufsize, mv->smooth); + mod_cc_attrib_to_string(outstr, "_smoothcc", mk, floatbuf); } - if (md->has_step || show_inherited) + if (mv->has_step || show_inherited) { - g_ascii_dtostr(floatbuf, floatbufsize, md->step); - mod_cc_attrib_to_string(outstr, "_stepcc", md, floatbuf); + g_ascii_dtostr(floatbuf, floatbufsize, mv->step); + mod_cc_attrib_to_string(outstr, "_stepcc", mk, floatbuf); } - if (md->has_amount || show_inherited) + if (mv->has_amount || show_inherited) { - gboolean is_egcc = md->dest >= smdest_eg_stage_start && md->dest <= smdest_eg_stage_end; - gboolean is_lfofreq = md->dest >= smdest_pitchlfo_freq && md->dest <= smdest_eq3_gain; - g_ascii_dtostr(floatbuf, floatbufsize, md->amount); + gboolean is_egcc = mk->dest >= smdest_eg_stage_start && mk->dest <= smdest_eg_stage_end; + gboolean is_lfofreq = mk->dest >= smdest_pitchlfo_freq && mk->dest <= smdest_eq3_gain; + g_ascii_dtostr(floatbuf, floatbufsize, mv->amount); - if (md->src2 == smsrc_none) + if (mk->src2 == smsrc_none) { if (is_egcc) { - uint32_t param = md->dest - smdest_eg_stage_start; - g_string_append_printf(outstr, " %seg_%scc%d=%s", addrandom_variants[(param >> 4) & 3], env_stages[param & 15], md->src, floatbuf); + uint32_t param = mk->dest - smdest_eg_stage_start; + g_string_append_printf(outstr, " %seg_%scc%d=%s", addrandom_variants[(param >> 4) & 3], env_stages[param & 15], mk->src, floatbuf); continue; } - if (md->src < smsrc_perchan_count) + if (mk->src < smsrc_perchan_count) { // Inconsistency: cutoff_cc5 but amplfo_freqcc5 if (is_lfofreq) - g_string_append_printf(outstr, " %scc%d=%s", moddest_names[md->dest], md->src, floatbuf); + g_string_append_printf(outstr, " %scc%d=%s", moddest_names[mk->dest], mk->src, floatbuf); else - g_string_append_printf(outstr, " %s_cc%d=%s", moddest_names[md->dest], md->src, floatbuf); + g_string_append_printf(outstr, " %s_cc%d=%s", moddest_names[mk->dest], mk->src, floatbuf); continue; } - if (md->src < smsrc_perchan_count + sizeof(modsrc_names) / sizeof(modsrc_names[0])) + if (mk->src < smsrc_perchan_count + sizeof(modsrc_names) / sizeof(modsrc_names[0])) { - if ((md->src == smsrc_filenv && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchenv && md->dest == smdest_pitch) || - (md->src == smsrc_amplfo && md->dest == smdest_gain) || - (md->src == smsrc_fillfo && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchlfo && md->dest == smdest_pitch)) - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); - else if ((md->src == smsrc_filenv && md->dest == smdest_cutoff2) || - (md->src == smsrc_fillfo && md->dest == smdest_cutoff2)) - g_string_append_printf(outstr, " %s_depth2=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); + if ((mk->src == smsrc_filenv && mk->dest == smdest_cutoff) || + (mk->src == smsrc_pitchenv && mk->dest == smdest_pitch) || + (mk->src == smsrc_amplfo && mk->dest == smdest_gain) || + (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff) || + (mk->src == smsrc_pitchlfo && mk->dest == smdest_pitch)) + g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); + else if ((mk->src == smsrc_filenv && mk->dest == smdest_cutoff2) || + (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff2)) + g_string_append_printf(outstr, " %s_depth2=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); else if (is_lfofreq) - g_string_append_printf(outstr, " %s%s=%s", moddest_names[md->dest], modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s%s=%s", moddest_names[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf); else - g_string_append_printf(outstr, " %s_%s=%s", moddest_names[md->dest], modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_%s=%s", moddest_names[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf); continue; } } - if ((md->src == smsrc_amplfo && md->dest == smdest_gain) || - (md->src == smsrc_fillfo && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchlfo && md->dest == smdest_pitch)) + if ((mk->src == smsrc_amplfo && mk->dest == smdest_gain) || + (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff) || + (mk->src == smsrc_pitchlfo && mk->dest == smdest_pitch)) { - switch(md->src2) + switch(mk->src2) { case smsrc_chanaft: case smsrc_polyaft: - g_string_append_printf(outstr, " %s_depth%s=%s", modsrc_names[md->src - smsrc_perchan_count], modsrc_names[md->src2 - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_depth%s=%s", modsrc_names[mk->src - smsrc_perchan_count], modsrc_names[mk->src2 - smsrc_perchan_count], floatbuf); continue; case smsrc_none: - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); continue; default: - if (md->src2 < 120) + if (mk->src2 < EXT_CC_COUNT) { - g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[md->src - smsrc_perchan_count], md->src2, floatbuf); + g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); continue; } break; } } - if ((md->src == smsrc_ampenv && md->dest == smdest_gain) || - (md->src == smsrc_filenv && md->dest == smdest_cutoff) || - (md->src == smsrc_pitchenv && md->dest == smdest_pitch)) + if ((mk->src == smsrc_ampenv && mk->dest == smdest_gain) || + (mk->src == smsrc_filenv && mk->dest == smdest_cutoff) || + (mk->src == smsrc_pitchenv && mk->dest == smdest_pitch)) { - if (md->src2 == smsrc_vel) + if (mk->src2 == smsrc_vel) { - g_string_append_printf(outstr, " %s_vel2depth=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_vel2depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); continue; } - if (md->src2 == smsrc_none) + if (mk->src2 == smsrc_none) { - g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); continue; } - if (md->src2 < 120) + if (mk->src2 < EXT_CC_COUNT) { - g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[md->src - smsrc_perchan_count], md->src2, floatbuf); + g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); continue; } } - if (md->src == smsrc_filenv && md->dest == smdest_cutoff2) + if (mk->src == smsrc_filenv && mk->dest == smdest_cutoff2) { - if (md->src2 == smsrc_vel) + if (mk->src2 == smsrc_vel) { - g_string_append_printf(outstr, " %s_vel2depth2=%s", modsrc_names[md->src - smsrc_perchan_count], floatbuf); + g_string_append_printf(outstr, " %s_vel2depth2=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf); continue; } - assert(md->src2 != smsrc_none); - if (md->src2 < 120) + assert(mk->src2 != smsrc_none); + if (mk->src2 < EXT_CC_COUNT) { - g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[md->src - smsrc_perchan_count], md->src2, floatbuf); + g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); continue; } } - if (md->src == smsrc_fillfo && md->dest == smdest_cutoff2) + if (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff2) { - assert(md->src2 != smsrc_none); - if (md->src2 < 120) + assert(mk->src2 != smsrc_none); + if (mk->src2 < EXT_CC_COUNT) { - g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[md->src - smsrc_perchan_count], md->src2, floatbuf); + g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf); continue; } } - g_string_append_printf(outstr, " genericmod_%d_%d_%d_%d=%s", md->src, md->src2, md->dest, md->curve_id, floatbuf); + g_string_append_printf(outstr, " genericmod_%d_%d_%d_%d=%s", mk->src, mk->src2, mk->dest, mv->curve_id, floatbuf); } } @@ -1815,13 +1839,18 @@ void sampler_layer_dump(struct sampler_layer *l, FILE *f) void sampler_layer_data_close(struct sampler_layer_data *l) { - g_slist_free_full(l->voice_nifs, g_free); - g_slist_free_full(l->prevoice_nifs, g_free); - g_slist_free_full(l->modulations, g_free); - if (l->eff_waveform) + sampler_flex_lfos_destroy(l->flex_lfos); + sampler_cc_ranges_destroy(l->cc); + sampler_cc_ranges_destroy(l->on_cc); + sampler_cc_ranges_destroy(l->xfin_cc); + sampler_cc_ranges_destroy(l->xfout_cc); + sampler_noteinitfuncs_destroy(l->voice_nifs); + sampler_noteinitfuncs_destroy(l->prevoice_nifs); + sampler_modulations_destroy(l->modulations); + if (l->computed.eff_waveform) { - cbox_waveform_unref(l->eff_waveform); - l->eff_waveform = NULL; + cbox_waveform_unref(l->computed.eff_waveform); + l->computed.eff_waveform = NULL; } g_free(l->sample); } diff --git a/template/calfbox/sampler_layer.h b/template/calfbox/sampler_layer.h index 19e7278..0a1b5bc 100644 --- a/template/calfbox/sampler_layer.h +++ b/template/calfbox/sampler_layer.h @@ -27,6 +27,8 @@ along with this program. If not, see . // arbitrary value that doesn't collide with a useful range #define SAMPLER_CURVE_GAP -100000 #define NO_HI_BPM_VALUE 10000 +#define CC_COUNT 128 +#define EXT_CC_COUNT 143 struct sampler_program; struct sampler_voice; @@ -239,11 +241,15 @@ enum sampler_moddest smdest_eg_stage_end = 0xAF, }; -struct sampler_modulation +struct sampler_modulation_key { enum sampler_modsrc src; enum sampler_modsrc src2; enum sampler_moddest dest; +}; + +struct sampler_modulation_value +{ float amount; float smooth; float step; @@ -254,17 +260,37 @@ struct sampler_modulation unsigned int has_step:1; }; +#define SAMPLER_COLL_FIELD_LIST_sampler_modulation(MACRO, ...) \ + MACRO(amount, has_amount, float, 0, ## __VA_ARGS__) \ + MACRO(curve_id, has_curve, uint32_t, 0, ## __VA_ARGS__) \ + MACRO(smooth, has_smooth, float, 0, ## __VA_ARGS__) \ + MACRO(step, has_step, float, 0, ## __VA_ARGS__) + +#define SAMPLER_COLL_CHAIN_LIST_sampler_modulation(MACRO, ...) \ + MACRO(modulations, modulation, ## __VA_ARGS__) + +////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*SamplerNoteInitFunc)(struct sampler_noteinitfunc *nif, struct sampler_voice *voice); typedef void (*SamplerNoteInitFunc2)(struct sampler_noteinitfunc *nif, struct sampler_prevoice *prevoice); -struct sampler_noteinitfunc +struct sampler_noteinitfunc_key +{ + union { + SamplerNoteInitFunc notefunc_voice; + SamplerNoteInitFunc2 notefunc_prevoice; + }; + int variant; +}; + +struct sampler_noteinitfunc_value { - SamplerNoteInitFunc notefunc_voice; - SamplerNoteInitFunc2 notefunc_prevoice; - int variant:31; + float value; + uint32_t curve_id; + float step; unsigned int has_value:1; - float param; - // XXXKF no destructor for now - might not be necessary + unsigned int has_curve:1; + unsigned int has_step:1; }; enum sampler_noteinitfunc_envelope_variant @@ -278,6 +304,90 @@ enum sampler_noteinitfunc_envelope_variant snif_env_start = 6, }; +#define SAMPLER_COLL_FIELD_LIST_sampler_noteinitfunc(MACRO, ...) \ + MACRO(value, has_value, float, 0, ## __VA_ARGS__) \ + MACRO(curve_id, has_curve, int, 0, ## __VA_ARGS__) \ + MACRO(step, has_step, float, 0, ## __VA_ARGS__) + +#define SAMPLER_COLL_CHAIN_LIST_sampler_noteinitfunc(MACRO, ...) \ + MACRO(voice_nifs, voice_nif, ## __VA_ARGS__) \ + MACRO(prevoice_nifs, prevoice_nif, ## __VA_ARGS__) + +////////////////////////////////////////////////////////////////////////////////////////////////// + +struct sampler_cc_range_key +{ + uint8_t cc_number; +}; + +struct sampler_cc_range_value +{ + uint8_t locc; + uint8_t hicc; + uint8_t has_locc:1; + uint8_t has_hicc:1; +}; + +#define SAMPLER_COLL_FIELD_LIST_sampler_cc_range(MACRO, ...) \ + MACRO(locc, has_locc, uint8_t, 0, ## __VA_ARGS__) \ + MACRO(hicc, has_hicc, uint8_t, 127, ## __VA_ARGS__) + +#define SAMPLER_COLL_CHAIN_LIST_sampler_cc_range(MACRO, ...) \ + MACRO(on_cc, on_cc, ## __VA_ARGS__) \ + MACRO(cc, cc, ## __VA_ARGS__) \ + MACRO(xfin_cc, xfin_cc, ## __VA_ARGS__) \ + MACRO(xfout_cc, xfout_cc, ## __VA_ARGS__) + +////////////////////////////////////////////////////////////////////////////////////////////////// + +struct sampler_flex_lfo_key +{ + uint32_t id; +}; + +struct sampler_flex_lfo_value +{ + // Note: layout/order must be identical to sampler_lfo_params + float freq; + float delay; + float fade; + int wave; + + uint8_t has_freq:1; + uint8_t has_delay:1; + uint8_t has_fade:1; + uint8_t has_wave:1; +}; + +#define SAMPLER_COLL_FIELD_LIST_sampler_flex_lfo(MACRO, ...) \ + MACRO(freq, has_freq, float, 0, ## __VA_ARGS__) \ + MACRO(delay, has_delay, float, 0, ## __VA_ARGS__) \ + MACRO(fade, has_fade, float, 0, ## __VA_ARGS__) \ + MACRO(wave, has_wave, int, 0, ## __VA_ARGS__) + +#define SAMPLER_COLL_CHAIN_LIST_sampler_flex_lfo(MACRO, ...) \ + MACRO(flex_lfos, flex_lfos, ## __VA_ARGS__) + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SAMPLER_COLL_LIST(MACRO) \ + MACRO(sampler_modulation) \ + MACRO(sampler_noteinitfunc) \ + MACRO(sampler_cc_range) \ + MACRO(sampler_flex_lfo) + +#define SAMPLER_COLL_DEFINITION(sname) \ + struct sname \ + { \ + struct sname##_key key; \ + struct sname##_value value; \ + struct sname *next; \ + }; + +SAMPLER_COLL_LIST(SAMPLER_COLL_DEFINITION) + +////////////////////////////////////////////////////////////////////////////////////////////////// + struct sampler_lfo_params { float freq; @@ -339,7 +449,6 @@ typedef int midi_note_t; MACRO(int, fil_veltrack, 0) \ MACRO(int, fil2_veltrack, 0) \ MACRO(int, amp_veltrack, 100) \ - MACRO(int, pitch_veltrack, 0) \ MACRO(int, lovel, 0) \ MACRO(int, hivel, 127) \ MACRO(int, lobend, -8192) \ @@ -448,9 +557,6 @@ typedef int midi_note_t; #define PROC_SUBSTRUCT_CLONE(name, index, def_value, param, dst, src) \ dst->param.name = src->param.name; \ dst->has_##param.name = src->has_##param.name; -#define PROC_SUBSTRUCT_CLONEPARENT(name, index, def_value, param, l) \ - if (!l->has_##param.name) \ - l->param.name = parent ? parent->param.name : def_value; struct sampler_dahdsr_has_fields { @@ -467,16 +573,6 @@ struct sampler_eq_has_fields EQ_FIELDS(PROC_SUBSTRUCT_HAS_FIELD, name) }; -struct sampler_cc_range -{ - uint8_t locc; - uint8_t hicc; - uint8_t cc_number; - uint8_t has_locc:1; - uint8_t has_hicc:1; - uint8_t is_active:1; -}; - struct sampler_midi_curve { float values[128]; @@ -501,10 +597,9 @@ struct sampler_midi_curve #define PROC_FIELDS_TO_STRUCT_eq(name, parname, index) \ struct sampler_eq_params name; #define PROC_FIELDS_TO_STRUCT_ccrange(name, parname) \ - struct sampler_cc_range name; + struct sampler_cc_range *name; #define PROC_FIELDS_TO_STRUCT_midicurve(name) \ - struct sampler_midi_curve name; \ - float eff_##name[128]; + struct sampler_midi_curve name; #define PROC_HAS_FIELD(type, name, def_value) \ unsigned int has_##name:1; @@ -533,28 +628,40 @@ enum sampler_layer_mod_bitmasks slmb_pitcheg_cc = 0x04, }; -struct sampler_layer_data +struct sampler_layer_computed { - SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_STRUCT) - SAMPLER_FIXED_FIELDS(PROC_HAS_FIELD) - - GSList *modulations; - GSList *voice_nifs, *prevoice_nifs; - // computed values: - float eff_freq; - gboolean eff_use_keyswitch; - gboolean eff_use_simple_trigger_logic; - enum sampler_loop_mode eff_loop_mode; struct cbox_waveform *eff_waveform; + enum sampler_loop_mode eff_loop_mode; + uint32_t eff_loop_start, eff_loop_end; + float eff_freq; + gboolean eff_use_keyswitch:1; + gboolean eff_use_simple_trigger_logic:1; + gboolean eff_use_xfcc:1; + gboolean eff_use_prevoice:1; + gboolean eff_use_channel_mixer:1; + gboolean eff_use_filter_mods:1; + gboolean eff_is_silent:1; int16_t scratch_loop[2 * MAX_INTERPOLATION_ORDER * 2]; int16_t scratch_end[2 * MAX_INTERPOLATION_ORDER * 2]; float resonance_scaled, resonance2_scaled; float logcutoff, logcutoff2; uint32_t eq_bitmask, mod_bitmask; - gboolean eff_use_xfcc; - gboolean use_prevoice; int eff_num_stages, eff_num_stages2; + + float eff_amp_velcurve[128]; +}; + +struct sampler_layer_data +{ + SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_STRUCT) + SAMPLER_FIXED_FIELDS(PROC_HAS_FIELD) + + struct sampler_modulation *modulations; + struct sampler_noteinitfunc *voice_nifs, *prevoice_nifs; + struct sampler_flex_lfo *flex_lfos; + + struct sampler_layer_computed computed; }; struct sampler_layer diff --git a/template/calfbox/sampler_nif.c b/template/calfbox/sampler_nif.c index 87821e8..b8c06c2 100644 --- a/template/calfbox/sampler_nif.c +++ b/template/calfbox/sampler_nif.c @@ -12,19 +12,20 @@ void sampler_nif_cc2delay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) { - pv->delay_computed += nif->param * sampler_channel_getcc_prevoice(pv->channel, pv, nif->variant); + float cc = sampler_channel_getcc_prevoice(pv->channel, pv, nif->key.variant, nif->value.curve_id, nif->value.step); + pv->delay_computed += nif->value.value * cc; } void sampler_nif_addrandomdelay(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) { - pv->delay_computed += nif->param * rand() * (1.0 / RAND_MAX); + pv->delay_computed += nif->value.value * rand() * (1.0 / RAND_MAX); } void sampler_nif_syncbeats(struct sampler_noteinitfunc *nif, struct sampler_prevoice *pv) { - if (nif->param > 0) + if (nif->value.value > 0) { - pv->sync_beats = nif->param; + pv->sync_beats = nif->value.value; double cur_beat = sampler_get_current_beat(pv->channel->module); pv->sync_initial_time = cur_beat; double cur_rel_beat = fmod(cur_beat, pv->sync_beats); @@ -39,42 +40,42 @@ void sampler_nif_syncbeats(struct sampler_noteinitfunc *nif, struct sampler_prev void sampler_nif_vel2pitch(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - v->pitch_shift += nif->param * v->vel * (1.0 / 127.0); + v->pitch_shift += nif->value.value * v->vel * (1.0 / 127.0); } void sampler_nif_vel2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - v->offset += nif->param * v->vel * (1.0 / 127.0); + v->offset += nif->value.value * v->vel * (1.0 / 127.0); } void sampler_nif_cc2offset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - v->offset += nif->param * sampler_channel_getcc(v->channel, v, nif->variant); + v->offset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); } void sampler_nif_vel2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - v->reloffset += nif->param * v->vel * (1.0 / 127.0); + v->reloffset += nif->value.value * v->vel * (1.0 / 127.0); } void sampler_nif_cc2reloffset(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - v->reloffset += nif->param * sampler_channel_getcc(v->channel, v, nif->variant); + v->reloffset += nif->value.value * sampler_channel_getcc_mod(v->channel, v, nif->key.variant, nif->value.curve_id, nif->value.step); } void sampler_nif_addrandom(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { float rnd = rand() * 1.0 / RAND_MAX; - switch(nif->variant) + switch(nif->key.variant) { case 0: - v->gain_shift += rnd * nif->param; + v->gain_shift += rnd * nif->value.value; break; case 1: - v->cutoff_shift += rnd * nif->param; + v->cutoff_shift += rnd * nif->value.value; break; case 2: - v->pitch_shift += rnd * nif->param; // this is in cents + v->pitch_shift += rnd * nif->value.value; // this is in cents break; } } @@ -102,7 +103,7 @@ static void modify_env_stage_by_nif(struct sampler_noteinitfunc *nif, struct sam memcpy(&v->vel_envs[env_type], env->shape, sizeof(struct cbox_envelope_shape)); env->shape = &v->vel_envs[env_type]; } - float param = nif->param * value; + float param = nif->value.value * value; if ((variant & 15) == snif_env_sustain || (variant & 15) == snif_env_start) param *= 0.01; cbox_envelope_modify_dahdsr(env->shape, variant & 15, param, v->channel->module->module.srate * (1.0 / CBOX_BLOCK_SIZE)); @@ -110,5 +111,5 @@ static void modify_env_stage_by_nif(struct sampler_noteinitfunc *nif, struct sam void sampler_nif_vel2env(struct sampler_noteinitfunc *nif, struct sampler_voice *v) { - modify_env_stage_by_nif(nif, v, nif->variant, v->vel * (1.0 / 127.0)); + modify_env_stage_by_nif(nif, v, nif->key.variant, v->vel * (1.0 / 127.0)); } diff --git a/template/calfbox/sampler_prevoice.c b/template/calfbox/sampler_prevoice.c index 30b52d2..7089550 100644 --- a/template/calfbox/sampler_prevoice.c +++ b/template/calfbox/sampler_prevoice.c @@ -20,13 +20,8 @@ void sampler_prevoice_start(struct sampler_prevoice *pv, struct sampler_channel pv->sync_initial_time = -1; pv->sync_trigger_time = -1; - GSList *nif = pv->layer_data->prevoice_nifs; - while(nif) - { - struct sampler_noteinitfunc *p = nif->data; - p->notefunc_prevoice(p, pv); - nif = nif->next; - } + for(struct sampler_noteinitfunc *nif = pv->layer_data->prevoice_nifs; nif; nif = nif->next) + nif->key.notefunc_prevoice(nif, pv); sampler_prevoice_unlink(&channel->module->prevoices_free, pv); sampler_prevoice_link(&channel->module->prevoices_running, pv); } diff --git a/template/calfbox/sampler_prg.c b/template/calfbox/sampler_prg.c index 1220e99..c8cc81c 100644 --- a/template/calfbox/sampler_prg.c +++ b/template/calfbox/sampler_prg.c @@ -40,10 +40,10 @@ retry: struct sampler_layer *lr = iter->next_layer->data; struct sampler_layer_data *l = lr->runtime; iter->next_layer = g_slist_next(iter->next_layer); - if (!l->eff_waveform) + if (!l->computed.eff_waveform) continue; - if (l->eff_use_simple_trigger_logic) + if (l->computed.eff_use_simple_trigger_logic) { if (iter->note >= l->lokey && iter->note <= l->hikey && iter->vel >= l->lovel && iter->vel <= l->hivel) @@ -56,7 +56,6 @@ retry: (l->trigger == stm_legato && iter->is_first) || (l->trigger == stm_release && !iter->is_release)) // sw_last keyswitches are still added to the note-on list in RLL continue; - int ccval = -1; struct sampler_channel *c = iter->channel; struct sampler_module *m = c->module; if (iter->note >= l->lokey && iter->note <= l->hikey && @@ -67,9 +66,9 @@ retry: c->last_chanaft >= l->lochanaft && c->last_chanaft <= l->hichanaft && c->last_polyaft >= l->lopolyaft && c->last_polyaft <= l->hipolyaft && c->module->module.engine->master->tempo >= l->lobpm && c->module->module.engine->master->tempo < l->hibpm && - (!l->cc.is_active || (ccval = sampler_channel_getintcc(c, NULL, l->cc.cc_number), ccval >= l->cc.locc && ccval <= l->cc.hicc))) + sampler_cc_range_is_in(l->cc, c)) { - if (!l->eff_use_keyswitch || + if (!l->computed.eff_use_keyswitch || ((l->sw_down == -1 || (c->switchmask[l->sw_down >> 5] & (1 << (l->sw_down & 31)))) && (l->sw_up == -1 || !(c->switchmask[l->sw_up >> 5] & (1 << (l->sw_up & 31)))) && (l->sw_previous == -1 || l->sw_previous == c->previous_note))) @@ -414,7 +413,7 @@ struct sampler_program *sampler_program_new_from_cfg(struct sampler_module *m, c else { sampler_layer_update(l); - if (!l->data.eff_waveform) + if (!l->data.computed.eff_waveform) { g_warning("Sample layer '%s' does not have a waveform - skipping", layer_section); CBOX_DELETE((struct sampler_layer *)l); @@ -443,7 +442,7 @@ struct sampler_program *sampler_program_new_from_cfg(struct sampler_module *m, c else { sampler_layer_update(l); - if (!l->data.eff_waveform) + if (!l->data.computed.eff_waveform) { g_warning("Sample layer '%s' does not have a waveform - skipping", layer_section); CBOX_DELETE((struct sampler_layer *)l); diff --git a/template/calfbox/sampler_rll.c b/template/calfbox/sampler_rll.c index 6f392bc..7e8198f 100644 --- a/template/calfbox/sampler_rll.c +++ b/template/calfbox/sampler_rll.c @@ -132,11 +132,16 @@ struct sampler_rll *sampler_rll_new_from_program(struct sampler_program *prg) ks_offset = ks->group_offset + rel_offset; } - if (l->data.on_cc.is_active) + struct sampler_cc_range *oncc = l->data.on_cc; + if (oncc) { - int cc = l->data.on_cc.cc_number; rll->layers_oncc = g_slist_prepend(rll->layers_oncc, l); - rll->cc_trigger_bitmask[cc >> 5] |= 1 << (cc & 31); + while(oncc) + { + int cc = oncc->key.cc_number; + rll->cc_trigger_bitmask[cc >> 5] |= 1 << (cc & 31); + oncc = oncc->next; + } } if (l->data.trigger == stm_release) add_layers(rll, rll->release_layers_by_range + ks_offset * range_count, l, l->data.lokey, l->data.hikey); diff --git a/template/calfbox/sampler_voice.c b/template/calfbox/sampler_voice.c index 66f37ef..b31f8a5 100644 --- a/template/calfbox/sampler_voice.c +++ b/template/calfbox/sampler_voice.c @@ -118,14 +118,14 @@ static inline float lfo_run(struct sampler_lfo *lfo) static gboolean is_tail_finished(struct sampler_voice *v) { - if (!v->layer->eff_num_stages) + if (!v->layer->computed.eff_num_stages) return TRUE; double eps = 1.0 / 65536.0; if (cbox_biquadf_is_audible(&v->filter.filter_left[0], eps)) return FALSE; if (cbox_biquadf_is_audible(&v->filter.filter_right[0], eps)) return FALSE; - int num_stages = v->layer->eff_num_stages; + int num_stages = v->layer->computed.eff_num_stages; if (num_stages > 1) { if (cbox_biquadf_is_audible(&v->filter.filter_left[num_stages - 1], eps)) @@ -259,7 +259,13 @@ void sampler_voice_activate(struct sampler_voice *v, enum sampler_player_type mo sampler_voice_link(&v->channel->voices_running, v); } -void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, int *exgroups, int *pexgroupcount) +void sampler_voice_start_silent(struct sampler_layer_data *l, struct sampler_released_groups *exgroupdata) +{ + if (l->group >= 1) + sampler_released_groups_add(exgroupdata, l->group); +} + +void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, struct sampler_released_groups *exgroupdata) { struct sampler_module *m = c->module; sampler_gen_reset(&v->gen); @@ -274,18 +280,18 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str if (age * l->rt_decay > 84) return; } - uint32_t end = l->eff_waveform->info.frames; + uint32_t end = l->computed.eff_waveform->info.frames; if (l->end != 0) end = (l->end == SAMPLER_NO_LOOP) ? 0 : l->end; - v->last_waveform = l->eff_waveform; + v->last_waveform = l->computed.eff_waveform; v->gen.cur_sample_end = end; - if (end > l->eff_waveform->info.frames) - end = l->eff_waveform->info.frames; + if (end > l->computed.eff_waveform->info.frames) + end = l->computed.eff_waveform->info.frames; assert(!v->current_pipe); - if (end > l->eff_waveform->preloaded_frames) + if (end > l->computed.eff_waveform->preloaded_frames) { - if (l->eff_loop_mode == slm_loop_continuous && l->loop_end < l->eff_waveform->preloaded_frames) + if (l->computed.eff_loop_mode == slm_loop_continuous && l->computed.eff_loop_end < l->computed.eff_waveform->preloaded_frames) { // Everything fits in prefetch, because loop ends in prefetch and post-loop part is not being played } @@ -294,17 +300,17 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str uint32_t loop_start = -1, loop_end = end; // If in loop mode, set the loop over the looped part... unless we're doing sustain-only loop on prefetch area only. Then // streaming will only cover the release part, and it shouldn't be looped. - if (l->eff_loop_mode == slm_loop_continuous || (l->eff_loop_mode == slm_loop_sustain && l->loop_end >= l->eff_waveform->preloaded_frames)) + if (l->computed.eff_loop_mode == slm_loop_continuous || (l->computed.eff_loop_mode == slm_loop_sustain && l->computed.eff_loop_end >= l->computed.eff_waveform->preloaded_frames)) { - loop_start = l->loop_start; - loop_end = l->loop_end; + loop_start = l->computed.eff_loop_start; + loop_end = l->computed.eff_loop_end; } // Those are initial values only, they will be adjusted in process function - v->current_pipe = cbox_prefetch_stack_pop(m->pipe_stack, l->eff_waveform, loop_start, loop_end, l->count); + v->current_pipe = cbox_prefetch_stack_pop(m->pipe_stack, l->computed.eff_waveform, loop_start, loop_end, l->count); if (!v->current_pipe) { g_warning("Prefetch pipe pool exhausted, no streaming playback will be possible"); - end = l->eff_waveform->preloaded_frames; + end = l->computed.eff_waveform->preloaded_frames; v->gen.cur_sample_end = end; } } @@ -315,7 +321,7 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str v->gen.loop_overlap = l->loop_overlap; v->gen.loop_overlap_step = l->loop_overlap > 0 ? 1.0 / l->loop_overlap : 0; - v->gain_fromvel = l->eff_amp_velcurve[vel]; + v->gain_fromvel = l->computed.eff_amp_velcurve[vel]; v->gain_shift = (note - l->amp_keycenter) * l->amp_keytrack; v->gain_fromvel *= sfz_crossfade(note, l->xfin_lokey, l->xfin_hikey, l->xfout_lokey, l->xfout_hikey, l->xf_keycurve); @@ -338,7 +344,7 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str v->cutoff_shift = vel * l->fil_veltrack / 127.0 + (note - l->fil_keycenter) * l->fil_keytrack; v->cutoff2_shift = vel * l->fil2_veltrack / 127.0 + (note - l->fil2_keycenter) * l->fil2_keytrack; - v->loop_mode = l->eff_loop_mode; + v->loop_mode = l->computed.eff_loop_mode; v->off_by = l->off_by; v->reloffset = l->reloffset; int auxes = (m->module.outputs - m->module.aux_offset) / 2; @@ -352,21 +358,9 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str v->send2bus = 0; v->send1gain = l->effect1 * 0.01; v->send2gain = l->effect2 * 0.01; - if (l->group >= 1 && *pexgroupcount < MAX_RELEASED_GROUPS) + if (l->group >= 1) { - gboolean found = FALSE; - for (int j = 0; j < *pexgroupcount; j++) - { - if (exgroups[j] == l->group) - { - found = TRUE; - break; - } - } - if (!found) - { - exgroups[(*pexgroupcount)++] = l->group; - } + sampler_released_groups_add(exgroupdata, l->group); } lfo_init(&v->amp_lfo, &l->amp_lfo, m->module.srate, m->module.srate_inv); lfo_init(&v->filter_lfo, &l->filter_lfo, m->module.srate, m->module.srate_inv); @@ -386,13 +380,8 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str cbox_onepolef_set_highshelf_tonectl(&v->onepole_coeffs, l->tonectl_freq * M_PI * m->module.srate_inv, 1.0); v->offset = l->offset; - GSList *nif = v->layer->voice_nifs; - while(nif) - { - struct sampler_noteinitfunc *p = nif->data; - p->notefunc_voice(p, v); - nif = nif->next; - } + for(struct sampler_noteinitfunc *nif = v->layer->voice_nifs; nif; nif = nif->next) + nif->key.notefunc_voice(nif, v); if (v->gain_shift) v->gain_fromvel *= dB2gain(v->gain_shift); @@ -400,7 +389,7 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str { // For streamed samples, allow only half the preload period worth of offset to avoid gaps // (maybe we can allow up to preload period minus one buffer size here?) - uint32_t maxend = v->current_pipe ? (l->eff_waveform->preloaded_frames >> 1) : l->eff_waveform->preloaded_frames; + uint32_t maxend = v->current_pipe ? (l->computed.eff_waveform->preloaded_frames >> 1) : l->computed.eff_waveform->preloaded_frames; int32_t pos = v->offset + v->reloffset * maxend * 0.01; if (pos < 0) pos = 0; @@ -415,7 +404,7 @@ void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, str v->last_eq_bitmask = 0; - sampler_voice_activate(v, l->eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16); + sampler_voice_activate(v, l->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16); uint32_t pos = v->offset; if (l->offset_random) @@ -557,6 +546,41 @@ static inline void sampler_filter_process_audio(struct sampler_filter *f, int nu cbox_biquadf_process_stereo(&f->filter_left[i], &f->filter_right[i], i ? f->second_filter : &f->filter_coeffs, leftright); } +static inline void do_channel_mixing(float *leftright, uint32_t numsamples, float position, float width) +{ + float crossmix = (100.0 - width) * 0.005f; + float amtleft = position > 0 ? 1 - 0.01 * position : 1; + float amtright = position < 0 ? 1 - 0.01 * -position : 1; + if (amtleft < 0) + amtleft = 0; + if (amtright < 0) + amtright = 0; + for (uint32_t i = 0; i < 2 * numsamples; i += 2) { + float left = leftright[i], right = leftright[i + 1]; + float newleft = left + crossmix * (right - left); + float newright = right + crossmix * (left - right); + leftright[i] = newleft * amtleft; + leftright[i + 1] = newright * amtright; + } +} + +static inline uint32_t sampler_gen_sample_playback_with_pipe(struct sampler_gen *gen, float *leftright, struct cbox_prefetch_pipe *current_pipe) +{ + if (!current_pipe) + return sampler_gen_sample_playback(gen, leftright, (uint32_t)-1); + + uint32_t limit = cbox_prefetch_pipe_get_remaining(current_pipe); + if (limit <= 4) + { + gen->mode = spt_inactive; + return 0; + } + uint32_t samples = sampler_gen_sample_playback(gen, leftright, limit - 4); + cbox_prefetch_pipe_consumed(current_pipe, gen->consumed); + gen->consumed = 0; + return samples; +} + static const float gain_for_num_stages[] = { 1, 1, 0.5, 0.33f }; void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cbox_sample_t **outputs) @@ -570,10 +594,10 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb const float velscl = v->vel * (1.f / 127.f); struct cbox_envelope_shape *pitcheg_shape = v->pitch_env.shape, *fileg_shape = v->filter_env.shape, *ampeg_shape = v->amp_env.shape; - if (__builtin_expect(l->mod_bitmask, 0)) + if (__builtin_expect(l->computed.mod_bitmask, 0)) { #define COPY_ORIG_SHAPE(envtype, envtype2, index) \ - if (l->mod_bitmask & slmb_##envtype2##eg_cc) { \ + if (l->computed.mod_bitmask & slmb_##envtype2##eg_cc) { \ memcpy(&v->cc_envs[index], envtype2##eg_shape, sizeof(struct cbox_envelope_shape)); \ envtype2##eg_shape = &v->cc_envs[index]; \ } @@ -581,24 +605,21 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb COPY_ORIG_SHAPE(filter, fil, 1) COPY_ORIG_SHAPE(pitch, pitch, 2) - GSList *mod = l->modulations; - while(mod) + for(struct sampler_modulation *sm = l->modulations; sm; sm = sm->next) { // Simplified modulations for EG stages (CCs only) - struct sampler_modulation *sm = mod->data; - if (sm->dest >= smdest_eg_stage_start && sm->dest <= smdest_eg_stage_end) + if (sm->key.dest >= smdest_eg_stage_start && sm->key.dest <= smdest_eg_stage_end) { float value = 0.f; - if (sm->src < smsrc_pernote_offset) - value = sampler_channel_getcc_mod(c, v, sm->src, sm); - uint32_t param = sm->dest - smdest_eg_stage_start; - if (value * sm->amount != 0) - cbox_envelope_modify_dahdsr(&v->cc_envs[(param >> 4)], param & 0x0F, value * sm->amount, m->module.srate * 1.0 / CBOX_BLOCK_SIZE); + if (sm->key.src < smsrc_pernote_offset) + value = sampler_channel_getcc_mod(c, v, sm->key.src, sm->value.curve_id, sm->value.step); + uint32_t param = sm->key.dest - smdest_eg_stage_start; + if (value * sm->value.amount != 0) + cbox_envelope_modify_dahdsr(&v->cc_envs[(param >> 4)], param & 0x0F, value * sm->value.amount, m->module.srate * 1.0 / CBOX_BLOCK_SIZE); } - mod = g_slist_next(mod); } #define UPDATE_ENV_POSITION(envtype, envtype2) \ - if (l->mod_bitmask & slmb_##envtype##eg_cc) \ + if (l->computed.mod_bitmask & slmb_##envtype##eg_cc) \ cbox_envelope_update_shape_after_modify(&v->envtype2##_env, envtype##eg_shape, m->module.srate * 1.0 / CBOX_BLOCK_SIZE); UPDATE_ENV_POSITION(amp, amp) UPDATE_ENV_POSITION(fil, filter) @@ -618,13 +639,13 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb if (__builtin_expect(v->layer_changed, 0)) { v->last_level = -1; - if (v->last_waveform != v->layer->eff_waveform) + if (v->last_waveform != v->layer->computed.eff_waveform) { - v->last_waveform = v->layer->eff_waveform; - if (v->layer->eff_waveform) + v->last_waveform = v->layer->computed.eff_waveform; + if (v->layer->computed.eff_waveform) { - v->gen.mode = v->layer->eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16; - v->gen.cur_sample_end = v->layer->eff_waveform->info.frames; + v->gen.mode = v->layer->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16; + v->gen.cur_sample_end = v->layer->computed.eff_waveform->info.frames; } else { @@ -632,36 +653,27 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb return; } } - if (l->eq_bitmask & (1 << 0)) recalc_eq_mask |= RECALC_EQ_MASK_EQ1; - if (l->eq_bitmask & (1 << 1)) recalc_eq_mask |= RECALC_EQ_MASK_EQ2; - if (l->eq_bitmask & (1 << 2)) recalc_eq_mask |= RECALC_EQ_MASK_EQ3; - v->last_eq_bitmask = l->eq_bitmask; + if (l->computed.eq_bitmask & (1 << 0)) recalc_eq_mask |= RECALC_EQ_MASK_EQ1; + if (l->computed.eq_bitmask & (1 << 1)) recalc_eq_mask |= RECALC_EQ_MASK_EQ2; + if (l->computed.eq_bitmask & (1 << 2)) recalc_eq_mask |= RECALC_EQ_MASK_EQ3; + v->last_eq_bitmask = l->computed.eq_bitmask; v->layer_changed = FALSE; } - float pitch = (v->note - l->pitch_keycenter) * l->pitch_keytrack + velscl * l->pitch_veltrack + l->tune + l->transpose * 100 + v->pitch_shift; + float pitch = (v->note - l->pitch_keycenter) * l->pitch_keytrack + l->tune + l->transpose * 100 + v->pitch_shift; float modsrcs[smsrc_pernote_count]; modsrcs[smsrc_vel - smsrc_pernote_offset] = v->vel * velscl; modsrcs[smsrc_pitch - smsrc_pernote_offset] = pitch * (1.f / 100.f); modsrcs[smsrc_chanaft - smsrc_pernote_offset] = c->last_chanaft * (1.f / 127.f); modsrcs[smsrc_polyaft - smsrc_pernote_offset] = sampler_channel_get_poly_pressure(c, v->note); modsrcs[smsrc_pitchenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->pitch_env, pitcheg_shape) * 0.01f; - modsrcs[smsrc_filenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->filter_env, fileg_shape) * 0.01f; + modsrcs[smsrc_filenv - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? cbox_envelope_get_value(&v->filter_env, fileg_shape) * 0.01f : 0; modsrcs[smsrc_ampenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->amp_env, ampeg_shape) * 0.01f; modsrcs[smsrc_amplfo - smsrc_pernote_offset] = lfo_run(&v->amp_lfo); - modsrcs[smsrc_fillfo - smsrc_pernote_offset] = lfo_run(&v->filter_lfo); + modsrcs[smsrc_fillfo - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? lfo_run(&v->filter_lfo) : 0; modsrcs[smsrc_pitchlfo - smsrc_pernote_offset] = lfo_run(&v->pitch_lfo); - if (__builtin_expect(v->amp_env.cur_stage < 0, 0)) - { - if (__builtin_expect(is_tail_finished(v), 0)) - { - sampler_voice_inactivate(v, TRUE); - return; - } - } - float moddests[smdestcount]; moddests[smdest_pitch] = pitch; moddests[smdest_cutoff] = v->cutoff_shift; @@ -677,7 +689,6 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb moddests[smdest_fillfo_freq] = 0; moddests[smdest_amplfo_freq] = 0; #endif - GSList *mod = l->modulations; if (__builtin_expect(l->trigger == stm_release, 0)) { moddests[smdest_gain] = -v->age * l->rt_decay * m->module.srate_inv; @@ -697,38 +708,50 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb moddests[smdest_pitch] += pw; } - while(mod) + for (struct sampler_modulation *sm = l->modulations; sm; sm = sm->next) { - struct sampler_modulation *sm = mod->data; + enum sampler_modsrc src = sm->key.src; + enum sampler_modsrc src2 = sm->key.src2; + enum sampler_moddest dest = sm->key.dest; float value = 0.f, value2 = 1.f; - if (sm->src < smsrc_pernote_offset) - value = sampler_channel_getcc_mod(c, v, sm->src, sm); + if (src < smsrc_pernote_offset) + value = sampler_channel_getcc_mod(c, v, src, sm->value.curve_id, sm->value.step); else - value = modsrcs[sm->src - smsrc_pernote_offset]; + value = modsrcs[src - smsrc_pernote_offset]; - if (sm->src2 != smsrc_none) + if (src2 != smsrc_none) { - if (sm->src2 < smsrc_pernote_offset) - value2 = sampler_channel_getcc_mod(c, v, sm->src2, sm); + if (src2 < smsrc_pernote_offset) + value2 = sampler_channel_getcc_mod(c, v, src2, sm->value.curve_id, sm->value.step); else - value2 = modsrcs[sm->src2 - smsrc_pernote_offset]; + value2 = modsrcs[src2 - smsrc_pernote_offset]; value *= value2; } - if (sm->dest < 32) + if (dest < 32) { - if (!(modmask & (1 << sm->dest))) // first value + if (dest == smdest_amplitude) + { + if (!(modmask & (1 << dest))) // first value + { + moddests[dest] = value * sm->value.amount; + modmask |= (1 << dest); + } + else + moddests[dest] *= value * sm->value.amount; + } + else if (!(modmask & (1 << dest))) // first value { - moddests[sm->dest] = value * sm->amount; - modmask |= (1 << sm->dest); + moddests[dest] = value * sm->value.amount; + modmask |= (1 << dest); } else - moddests[sm->dest] += value * sm->amount; + moddests[dest] += value * sm->value.amount; } - mod = g_slist_next(mod); } lfo_update_xdelta(m, &v->pitch_lfo, modmask, smdest_pitchlfo_freq, moddests); - lfo_update_xdelta(m, &v->filter_lfo, modmask, smdest_fillfo_freq, moddests); + if (l->computed.eff_use_filter_mods) + lfo_update_xdelta(m, &v->filter_lfo, modmask, smdest_fillfo_freq, moddests); lfo_update_xdelta(m, &v->amp_lfo, modmask, smdest_amplfo_freq, moddests); recalc_eq_mask |= modmask; @@ -752,11 +775,20 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb RECALC_EQ_IF(3) } cbox_envelope_advance(&v->pitch_env, v->released, pitcheg_shape); - cbox_envelope_advance(&v->filter_env, v->released, fileg_shape); + if (l->computed.eff_use_filter_mods) + cbox_envelope_advance(&v->filter_env, v->released, fileg_shape); cbox_envelope_advance(&v->amp_env, v->released, ampeg_shape); + if (__builtin_expect(v->amp_env.cur_stage < 0, 0)) + { + if (__builtin_expect(is_tail_finished(v), 0)) + { + sampler_voice_inactivate(v, TRUE); + return; + } + } double maxv = 127 << 7; - double freq = l->eff_freq * cent2factor(moddests[smdest_pitch]) ; + double freq = l->computed.eff_freq * cent2factor(moddests[smdest_pitch]) ; uint64_t freq64 = (uint64_t)(freq * 65536.0 * 65536.0 * m->module.srate_inv); gboolean playing_sustain_loop = !v->released && v->loop_mode == slm_loop_sustain; @@ -794,9 +826,9 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb } // XXXKF or maybe check for on-cc being in the on-cc range instead? - gboolean play_loop = v->layer->loop_end && (v->loop_mode == slm_loop_continuous || playing_sustain_loop) && !v->layer->on_cc.is_active; - loop_start = play_loop ? v->layer->loop_start : (v->layer->count ? 0 : (uint32_t)-1); - loop_end = play_loop ? v->layer->loop_end : v->gen.cur_sample_end; + gboolean play_loop = v->layer->computed.eff_loop_end && (v->loop_mode == slm_loop_continuous || playing_sustain_loop) && !v->layer->on_cc; + loop_start = play_loop ? v->layer->computed.eff_loop_start : (v->layer->count ? 0 : (uint32_t)-1); + loop_end = play_loop ? v->layer->computed.eff_loop_end : v->gen.cur_sample_end; if (v->current_pipe) { @@ -828,7 +860,7 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb if (!bandlimited) { // Use pre-calculated join - v->gen.scratch = loop_start == (uint32_t)-1 ? v->layer->scratch_end : v->layer->scratch_loop; + v->gen.scratch = loop_start == (uint32_t)-1 ? v->layer->computed.scratch_end : v->layer->computed.scratch_loop; } else { @@ -837,14 +869,14 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb // (i.e. partial loop or no loop) over bandlimited versions of the standard waveforms, and those are probably // not very useful anyway, as changing the loop removes the guarantee of the waveform being bandlimited and // may cause looping artifacts or introduce DC offset (e.g. if only a positive part of a sine wave is looped). - if (loop_start == 0 && loop_end == l->eff_waveform->info.frames) - v->gen.scratch = v->gen.sample_data + l->eff_waveform->info.frames - MAX_INTERPOLATION_ORDER; + if (loop_start == 0 && loop_end == l->computed.eff_waveform->info.frames) + v->gen.scratch = v->gen.sample_data + l->computed.eff_waveform->info.frames - MAX_INTERPOLATION_ORDER; else { // Generate the join for the current wave level // XXXKF this could be optimised further, by checking if waveform and loops are the same as the last // time. However, this code is not likely to be used... ever, so optimising it is not the priority. - int shift = l->eff_waveform->info.channels == 2 ? 1 : 0; + int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0; uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift; v->gen.scratch = v->gen.scratch_bandlimited; @@ -860,7 +892,7 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb if (l->timestretch) { v->gen.bigdelta = freq64; - v->gen.virtdelta = (uint64_t)(l->eff_freq * 65536.0 * 65536.0 * m->module.srate_inv); + v->gen.virtdelta = (uint64_t)(l->computed.eff_freq * 65536.0 * 65536.0 * m->module.srate_inv); v->gen.stretching_jump = l->timestretch_jump; v->gen.stretching_crossfade = l->timestretch_crossfade; } @@ -870,17 +902,18 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb v->gen.virtdelta = freq64; } float gain = modsrcs[smsrc_ampenv - smsrc_pernote_offset] * l->volume_linearized * v->gain_fromvel * c->channel_volume_cc * sampler_channel_addcc(c, 11) / (maxv * maxv); - if (l->eff_use_xfcc) { - gain *= - (l->xfin_cc.is_active ? sfz_crossfade2(c->intcc[l->xfin_cc.cc_number], l->xfin_cc.locc, l->xfin_cc.hicc, 0, 1, l->xf_cccurve) : 1.f) * - (l->xfout_cc.is_active ? sfz_crossfade2(c->intcc[l->xfout_cc.cc_number], l->xfout_cc.locc, l->xfout_cc.hicc, 1, 0, l->xf_cccurve) : 1.f); + if (l->computed.eff_use_xfcc) { + for(struct sampler_cc_range *p = l->xfin_cc; p; p = p->next) + gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 0, 1, l->xf_cccurve); + for(struct sampler_cc_range *p = l->xfout_cc; p; p = p->next) + gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 1, 0, l->xf_cccurve); } if ((modmask & (1 << smdest_gain)) && moddests[smdest_gain] != 0.f) gain *= dB2gain(moddests[smdest_gain]); float amplitude = l->amplitude; if ((modmask & (1 << smdest_amplitude))) - amplitude += moddests[smdest_amplitude]; + amplitude *= moddests[smdest_amplitude]; gain *= amplitude * (1.0 / 100.0); // http://drealm.info/sfz/plj-sfz.xhtml#amp "The overall gain must remain in the range -144 to 6 decibels." @@ -896,13 +929,13 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb if (l->cutoff != -1) { - float mod_resonance = (modmask & (1 << smdest_resonance)) ? dB2gain(gain_for_num_stages[l->eff_num_stages] * moddests[smdest_resonance]) : 1; - sampler_filter_process_control(&v->filter, l->fil_type, l->logcutoff + moddests[smdest_cutoff], l->resonance_scaled * mod_resonance, m->sincos); + float mod_resonance = (modmask & (1 << smdest_resonance)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages] * moddests[smdest_resonance]) : 1; + sampler_filter_process_control(&v->filter, l->fil_type, l->computed.logcutoff + moddests[smdest_cutoff], l->computed.resonance_scaled * mod_resonance, m->sincos); } if (l->cutoff2 != -1) { - float mod_resonance = (modmask & (1 << smdest_resonance2)) ? dB2gain(gain_for_num_stages[l->eff_num_stages2] * moddests[smdest_resonance2]) : 1; - sampler_filter_process_control(&v->filter2, l->fil2_type, l->logcutoff2 + moddests[smdest_cutoff2], l->resonance2_scaled * mod_resonance, m->sincos); + float mod_resonance = (modmask & (1 << smdest_resonance2)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages2] * moddests[smdest_resonance2]) : 1; + sampler_filter_process_control(&v->filter2, l->fil2_type, l->computed.logcutoff2 + moddests[smdest_cutoff2], l->computed.resonance2_scaled * mod_resonance, m->sincos); } if (__builtin_expect(l->tonectl_freq != 0, 0)) @@ -913,60 +946,29 @@ void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cb else cbox_onepolef_set_highshelf_setgain(&v->onepole_coeffs, 1.0); } - + + // Audio processing starts here float leftright[2 * CBOX_BLOCK_SIZE]; - uint32_t samples = 0; - - if (v->current_pipe) - { - uint32_t limit = cbox_prefetch_pipe_get_remaining(v->current_pipe); - if (limit <= 4) - v->gen.mode = spt_inactive; - else - { - samples = sampler_gen_sample_playback(&v->gen, leftright, limit - 4); - cbox_prefetch_pipe_consumed(v->current_pipe, v->gen.consumed); - v->gen.consumed = 0; - } - } - else - { - samples = sampler_gen_sample_playback(&v->gen, leftright, (uint32_t)-1); - } - - if (l->position != 0 || l->width != 100) { - float crossmix = (100.0 - l->width) * 0.005f; - float amtleft = l->position > 0 ? 1 - 0.01 * l->position : 1; - float amtright = l->position < 0 ? 1 - 0.01 * -l->position : 1; - if (amtleft < 0) - amtleft = 0; - if (amtright < 0) - amtright = 0; - for (uint32_t i = 0; i < 2 * samples; i += 2) { - float left = leftright[i], right = leftright[i + 1]; - float newleft = left + crossmix * (right - left); - float newright = right + crossmix * (left - right); - leftright[i] = newleft * amtleft; - leftright[i + 1] = newright * amtright; - } - } + uint32_t samples = sampler_gen_sample_playback_with_pipe(&v->gen, leftright, v->current_pipe); + if (l->computed.eff_use_channel_mixer) + do_channel_mixing(leftright, samples, l->position, l->width); for (int i = 2 * samples; i < 2 * CBOX_BLOCK_SIZE; i++) leftright[i] = 0.f; if (l->cutoff != -1) - sampler_filter_process_audio(&v->filter, l->eff_num_stages, leftright); + sampler_filter_process_audio(&v->filter, l->computed.eff_num_stages, leftright); if (l->cutoff2 != -1) - sampler_filter_process_audio(&v->filter2, l->eff_num_stages2, leftright); + sampler_filter_process_audio(&v->filter2, l->computed.eff_num_stages2, leftright); if (__builtin_expect(l->tonectl_freq != 0, 0)) cbox_onepolef_process_stereo(&v->onepole_left, &v->onepole_right, &v->onepole_coeffs, leftright); - if (__builtin_expect(l->eq_bitmask, 0)) + if (__builtin_expect(l->computed.eq_bitmask, 0)) { for (int eq = 0; eq < 3; eq++) { - if (l->eq_bitmask & (1 << eq)) + if (l->computed.eq_bitmask & (1 << eq)) { cbox_biquadf_process_stereo(&v->eq_left[eq], &v->eq_right[eq], &v->eq_coeffs[eq], leftright); } diff --git a/template/calfbox/sfzloader.c b/template/calfbox/sfzloader.c index c062dc9..8b64bbc 100644 --- a/template/calfbox/sfzloader.c +++ b/template/calfbox/sfzloader.c @@ -196,7 +196,7 @@ static gboolean load_sfz_key_value(struct sfz_parser_client *client, const char { int ctrl = atoi(key + 6); int val = atoi(value); - if (ctrl >= 0 && ctrl <= 119 && val >=0 && val <= 127) + if (ctrl >= 0 && ctrl < CC_COUNT && val >=0 && val <= 127) sampler_program_add_controller_init(ls->program, ctrl, val); else g_warning("Invalid CC initialisation: %s=%s", key, value); diff --git a/template/calfbox/tests.c b/template/calfbox/tests.c index 249e6f5..a2cba95 100644 --- a/template/calfbox/tests.c +++ b/template/calfbox/tests.c @@ -412,6 +412,70 @@ REGION_LOGIC_TEST_SETUP(cc, "locc16=32 hicc16=33 sample=*saw" ); +struct region_logic_test_setup_step steps_cc2[] = { + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x1F", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x20", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x11\x41", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x10\x21", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x21", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x11\x42", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x11\x41", 0), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x11\x3F", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_END, +}; + +REGION_LOGIC_TEST_SETUP(cc2, + "locc16=32 hicc16=33 locc17=64 hicc17=65 sample=*saw" +); + +struct region_logic_test_setup_step steps_cc3[] = { // CC16 <= 33, CC17 >= 64 + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x1F", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x20", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x11\x41", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x11\x71", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x10\x21", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x21", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x10\x1F", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x11\x42", 0), + MIDI_DATA_STEP("\x90\x20\x20", 1), + MIDI_DATA_STEP("\xB0\x11\x41", 0), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x11\x3F", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_STEP("\xB0\x10\x22", 0), + MIDI_DATA_STEP("\x90\x20\x20", 0), + MIDI_DATA_END, +}; + +REGION_LOGIC_TEST_SETUP(cc3, + "hicc16=33 locc17=64 sample=*saw" +); + struct region_logic_test_setup_step steps_oncc[] = { MIDI_DATA_STEP("\xB0\x10\x1F", 0), MIDI_DATA_STEP("\xB0\x10\x20", 1), @@ -625,6 +689,8 @@ struct test_info { { "test_sampler_note_region_logic/chanaft", test_sampler_note_region_logic, &setup_chanaft }, { "test_sampler_note_region_logic/polyaft", test_sampler_note_region_logic, &setup_polyaft }, { "test_sampler_note_region_logic/cc", test_sampler_note_region_logic, &setup_cc }, + { "test_sampler_note_region_logic/cc2", test_sampler_note_region_logic, &setup_cc2 }, + { "test_sampler_note_region_logic/cc3", test_sampler_note_region_logic, &setup_cc3 }, { "test_sampler_note_region_logic/oncc", test_sampler_note_region_logic, &setup_oncc }, { "test_sampler_note_region_logic/release", test_sampler_note_region_logic, &setup_release }, { "test_sampler_note_region_logic/firstlegato", test_sampler_note_region_logic, &setup_firstlegato }, diff --git a/template/documentation/index.adoc.template b/template/documentation/index.adoc.template index e1035ac..7bfa18b 100644 --- a/template/documentation/index.adoc.template +++ b/template/documentation/index.adoc.template @@ -22,6 +22,7 @@ https://asciidoctor.org/docs/user-manual/ image::logo.png["logo", 320, 180] For program version + This site is part of the https://www.laborejo.org[Laborejo Software Suite]