Browse Source

update template

master
Nils 4 years ago
parent
commit
eb3a05b119
  1. 4
      template/calfbox/appmenu.c
  2. 2
      template/calfbox/envelope.h
  3. 2
      template/calfbox/experiments/interactive.py
  4. 2
      template/calfbox/experiments/meta.py
  5. 2
      template/calfbox/experiments/playPatternsAsMeasures.py
  6. 2
      template/calfbox/experiments/printAllMidiEvents.py
  7. 2
      template/calfbox/experiments/printAllMidiEventsSpecificPort.py
  8. 2
      template/calfbox/experiments/simplePlayback.py
  9. 2
      template/calfbox/experiments/simplerPlayback.py
  10. 2
      template/calfbox/experiments/testmetadata.py
  11. 8
      template/calfbox/fluid.c
  12. 4
      template/calfbox/menu.c
  13. 13
      template/calfbox/sampler.c
  14. 79
      template/calfbox/sampler.h
  15. 81
      template/calfbox/sampler_api_test.py
  16. 66
      template/calfbox/sampler_channel.c
  17. 13
      template/calfbox/sampler_impl.h
  18. 1587
      template/calfbox/sampler_layer.c
  19. 181
      template/calfbox/sampler_layer.h
  20. 31
      template/calfbox/sampler_nif.c
  21. 9
      template/calfbox/sampler_prevoice.c
  22. 13
      template/calfbox/sampler_prg.c
  23. 11
      template/calfbox/sampler_rll.c
  24. 300
      template/calfbox/sampler_voice.c
  25. 2
      template/calfbox/sfzloader.c
  26. 66
      template/calfbox/tests.c
  27. 1
      template/documentation/index.adoc.template

4
template/calfbox/appmenu.c

@ -35,6 +35,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ui.h"
#include "wavebank.h"
#if USE_NCURSES
#include <assert.h>
#include <glib.h>
#include <glob.h>
@ -46,8 +48,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <string.h>
#include <unistd.h>
#if USE_NCURSES
int cmd_quit(struct cbox_menu_item_command *item, void *context)
{
return 1;

2
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)
{

2
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

2
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

2
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

2
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

2
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

2
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

2
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

2
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

8
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;

4
template/calfbox/menu.c

@ -20,14 +20,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "menuitem.h"
#include "ui.h"
#if USE_NCURSES
#include <assert.h>
#include <glib.h>
#include <malloc.h>
#include <ncurses.h>
#include <string.h>
#if USE_NCURSES
struct cbox_menu
{
GPtrArray *items;

13
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;

79
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) \

81
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)

66
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)

13
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)

1587
template/calfbox/sampler_layer.c

File diff suppressed because it is too large

181
template/calfbox/sampler_layer.h

@ -27,6 +27,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// 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

31
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));
}

9
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);
}

13
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);

11
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);

300
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);
}

2
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);

66
template/calfbox/tests.c

@ -412,6 +412,70 @@ REGION_LOGIC_TEST_SETUP(cc,
"<region>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,
"<region>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,
"<region>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 },

1
template/documentation/index.adoc.template

@ -22,6 +22,7 @@ https://asciidoctor.org/docs/user-manual/
image::logo.png["logo", 320, 180]
For program version <version>
This site is part of the https://www.laborejo.org[Laborejo Software Suite]

Loading…
Cancel
Save