The base-application for Laborejo, Fluajho, Patroneo, Vico etc.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

2020 lignes
85 KiB

/*
Calf Box, an open source musical instrument.
Copyright (C) 2010-2011 Krzysztof Foltman
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config-api.h"
#include "dspmath.h"
#include "errors.h"
#include "midi.h"
#include "module.h"
#include "rt.h"
#include "sampler.h"
#include "sampler_impl.h"
#include "sfzloader.h"
#include <assert.h>
#include <errno.h>
#include <glib.h>
#include <math.h>
#include <memory.h>
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
static inline gboolean sampler_modulation_key_equal(const struct sampler_modulation_key *k1, const struct sampler_modulation_key *k2)
{
return (k1->src == k2->src && k1->src2 == k2->src2 && k1->dest == k2->dest);
}
static inline void sampler_modulation_dump_one(const struct sampler_modulation *sm)
{
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 inline gboolean sampler_noteinitfunc_key_equal(const struct sampler_noteinitfunc_key *k1, const struct sampler_noteinitfunc_key *k2)
{
return (k1->notefunc_voice == k2->notefunc_voice && k1->variant == k2->variant);
}
static inline void sampler_noteinitfunc_dump_one(const struct sampler_noteinitfunc *nif)
{
printf("%p(%d) = %f\n", nif->key.notefunc_voice, nif->key.variant, nif->value.value);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
static inline gboolean sampler_cc_range_key_equal(const struct sampler_cc_range_key *k1, const struct sampler_cc_range_key *k2)
{
return k1->cc_number == k2->cc_number;
}
static inline void sampler_cc_range_dump_one(const struct sampler_cc_range *ccrange)
{
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);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
static inline gboolean sampler_flex_lfo_key_equal(const struct sampler_flex_lfo_key *k1, const struct sampler_flex_lfo_key *k2)
{
return k1->id == k2->id;
}
static inline void sampler_flex_lfo_dump_one(const struct sampler_flex_lfo *lfo)
{
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
);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
#define SAMPLER_COLL_FUNC_DUMP(sname) \
void sname##_dump(const struct sname *p) \
{ \
for(; p; p = p->next) \
sname##_dump_one(p); \
}
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; \
}
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; \
}
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; \
} \
}
SAMPLER_COLL_LIST(SAMPLER_COLL_FUNC_DESTROY)
#define SAMPLER_COLL_FIELD_ISNULL(name, has_name, type, init_value) \
if (d->name != init_value || d->has_name) \
return FALSE;
#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; \
}
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; \
}
#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; \
}
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
{
slpt_invalid,
slpt_alias,
slpt_int,
slpt_uint32_t,
slpt_float,
slpt_dBamp,
slpt_midi_note_t,
slpt_enum,
slpt_string,
slpt_midicurve,
slpt_ccrange,
// modulation matrix
slpt_mod_amount, // src (or CC) * src2 (or CC) -> dest
slpt_mod_curveid,
slpt_mod_smooth,
slpt_mod_step,
slpt_generic_modulation,
// note init functions
slpt_voice_nif,
slpt_prevoice_nif,
slpt_flex_lfo,
slpt_reserved,
};
struct sampler_layer_param_entry
{
const char *name;
size_t offset;
enum sampler_layer_param_type type;
double def_value;
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 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 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 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) \
PROC_FIELD_SETHASFUNC(type, name, default_value)
#define PROC_FIELD_SETHASFUNC_enum(type, name, default_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)
#define PROC_FIELD_SETHASFUNC_midicurve(name) \
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) \
FIELD_MOD(name, amount, src, none, dest)
#define FIELD_AMOUNT_CC(name, dest) \
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) \
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, LOFS(voice_nifs), slpt_voice_nif, 0, variant, nif, NULL, NULL },
#define FIELD_PREVOICE_NIF(name, nif, variant) \
{ 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, NULL },
#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, 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, 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, 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, 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, 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, sampler_layer_data_get_has_##name },
#define PROC_FIELD_DESCRIPTOR_midicurve(name) \
{ #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_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 "_freqpolyaft", polyaft, name##_freq) \
FIELD_AMOUNT(#name "_freqchanaft", chanaft, name##_freq) \
FIELD_AMOUNT_CC(#name "_freq", name##_freq) \
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) \
FIELD_AMOUNT_CC(#name "_freq", name##_freq) \
FIELD_AMOUNT_CC(#name "_bw", name##_bw) \
FIELD_AMOUNT_CC(#name "_gain", name##_gain)
#define PROC_FIELD_DESCRIPTOR_ccrange(field, parname) \
{ #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)
FIELD_AMOUNT("cutoff_chanaft", chanaft, cutoff)
FIELD_AMOUNT("resonance_chanaft", chanaft, resonance)
FIELD_AMOUNT("cutoff_polyaft", polyaft, cutoff)
FIELD_AMOUNT("resonance_polyaft", polyaft, resonance)
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)
FIELD_AMOUNT("cutoff2_polyaft", polyaft, cutoff2)
FIELD_AMOUNT("resonance2_polyaft", polyaft, resonance2)
FIELD_AMOUNT_CC_("gain", gain)
FIELD_AMOUNT_CC_("cutoff", cutoff)
FIELD_AMOUNT_CC_("resonance", resonance)
FIELD_AMOUNT_CC_("cutoff2", cutoff2)
FIELD_AMOUNT_CC_("resonance2", resonance2)
FIELD_AMOUNT_CC_("pitch", pitch)
FIELD_AMOUNT_CC_("tune", pitch)
FIELD_AMOUNT_CC_("tonectl", tonectl)
FIELD_AMOUNT_CC_("pan", pan)
FIELD_AMOUNT_CC_("amplitude", amplitude)
FIELD_VOICE_NIF("amp_random", sampler_nif_addrandom, 0)
FIELD_VOICE_NIF("fil_random", sampler_nif_addrandom, 1)
FIELD_VOICE_NIF("pitch_random", sampler_nif_addrandom, 2)
FIELD_VOICE_NIF("pitch_veltrack", sampler_nif_vel2pitch, 0)
FIELD_VOICE_NIF("offset_veltrack", sampler_nif_vel2offset, 0)
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_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")
FIELD_ALIAS("loopstart", "loop_start")
FIELD_ALIAS("loopend", "loop_end")
FIELD_ALIAS("loopmode", "loop_mode")
FIELD_ALIAS("bendup", "bend_up")
FIELD_ALIAS("benddown", "bend_down")
FIELD_ALIAS("offby", "off_by")
FIELD_ALIAS("offset_oncc#", "offset_cc#")
FIELD_ALIAS("reloffset_oncc#", "reloffset_cc#")
FIELD_ALIAS("delay_oncc#", "delay_cc#")
{ "genericmod_#_#_#_#", -1, slpt_generic_modulation, 0, 0, NULL, NULL, NULL },
};
#define NPARAMS (sizeof(sampler_layer_params) / sizeof(sampler_layer_params[0]))
static int compare_entries(const void *p1, const void *p2)
{
const struct sampler_layer_param_entry *e1 = p1, *e2 = p2;
return strcmp(e1->name, e2->name);
}
void sampler_layer_prepare_params(void)
{
qsort(sampler_layer_params, NPARAMS, sizeof(struct sampler_layer_param_entry), compare_entries);
for (size_t i = 0; i < NPARAMS; ++i)
{
struct sampler_layer_param_entry *e = &sampler_layer_params[i];
if (e->type == slpt_alias)
{
struct sampler_layer_param_entry prototype;
prototype.name = e->extra_ptr;
void *found = bsearch(&prototype, sampler_layer_params, NPARAMS, sizeof(sampler_layer_params[0]), compare_entries);
if (!found)
printf("Alias %s redirects to non-existent name (%s)\n", e->name, prototype.name);
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);
}
}
}
}
// 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);
}
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 = 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:
{
midi_note_t note = sfz_note_from_string(value);
if (note < -1)
{
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;
}
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &note, args, error);
}
case slpt_int:
{
char *endptr;
errno = 0;
int number = strtol(value, &endptr, 10);
if (errno || *endptr || endptr == value)
{
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;
}
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;
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;
}
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &data, args, error);
}
case slpt_uint32_t:
{
char *endptr;
errno = 0;
uint32_t number = (uint32_t)strtoul(value, &endptr, 10);
if (errno || *endptr || endptr == value || value[0] == '-')
{
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;
}
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error);
}
case slpt_string:
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, value, args, error);
case slpt_ccrange:
{
char *endptr;
errno = 0;
int number = strtol(value, &endptr, 10);
if (errno || *endptr || endptr == value || number < 0 || number > 127)
{
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;
}
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &number, args, error);
}
case slpt_float:
case slpt_dBamp:
default:
VERIFY_FLOAT_VALUE;
return sampler_layer_param_entry_set_from_ptr(e, l, set_local_value, &fvalue, args, error);
}
}
#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); \
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)
{
COPY_NUM_FROM_PARENT(slpt_midi_note_t, midi_note_t)
COPY_NUM_FROM_PARENT(slpt_int, int)
COPY_NUM_FROM_PARENT(slpt_enum, uint32_t) // XXXKF that's a kludge, enums are not guaranteed to be uint32_t (but they should be on common platforms)
COPY_NUM_FROM_PARENT(slpt_uint32_t, uint32_t)
COPY_NUM_FROM_PARENT(slpt_float, float)
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;
e->set_has_value(&l->data, 0);
gboolean *changed_ptr = (gboolean *)(((uint8_t *)&l->data) + e->extra_int);
*changed_ptr = 1;
}
return TRUE;
case slpt_midicurve:
if (args[0] >= 0 && args[0] <= 127)
{
struct sampler_midi_curve *curve = p, *parent_curve = pp;
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
{
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;
}
case slpt_ccrange:
cc = args[0];
if (!sampler_cc_range_unset_by_offset(l, e->offset, &(struct sampler_cc_range_key){cc}, unset_local_value, 1 << e->extra_int))
{
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;
}
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:
case slpt_prevoice_nif:
{
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:
default:
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
// Compare against a template that uses # to represent a number, extract
// any such numbers.
static int templcmp(const char *key, const char *templ, uint32_t *numbers)
{
while(*key && *templ)
{
if (*templ == '#')
{
if (isdigit(*key)) {
uint32_t num = 0;
do {
num = num * 10 + (unsigned char)(*key - '0');
key++;
} while (isdigit(*key));
*numbers++ = num;
templ++;
continue;
}
}
else if (*key == *templ)
{
templ++, key++;
continue;
}
if (*key < *templ)
return -1;
else
return +1;
}
if (*key)
return +1;
if (*templ)
return -1;
return 0;
}
const struct sampler_layer_param_entry *sampler_layer_param_find(const char *key, uint32_t *args)
{
static int prepared = 0;
if (!prepared)
{
sampler_layer_prepare_params();
prepared = 1;
}
int niter = 0;
uint32_t lo = 0, hi = NPARAMS;
while(lo < hi) {
++niter;
uint32_t mid = (lo + hi) >> 1;
const struct sampler_layer_param_entry *e = &sampler_layer_params[mid];
int res = templcmp(key, e->name, args);
if (res == 0)
{
// printf("%s found in %d iterations\n", key, niter);
if (e->type == slpt_alias)
return (const struct sampler_layer_param_entry *)e->extra_ptr;
return e;
}
if (res < 0)
hi = mid;
else
lo = mid + 1;
}
return NULL;
}
int sampler_layer_apply_fixed_param(struct sampler_layer *l, const char *key, const char *value, GError **error)
{
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, TRUE, value, args, error);
else
return -1;
}
int sampler_layer_unapply_fixed_param(struct sampler_layer *l, const char *key, GError **error)
{
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, TRUE, args, error);
else
return -1;
}
static gboolean sampler_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
{
struct sampler_layer *layer = ct->user_data;
if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
{
if (!cbox_check_fb_channel(fb, cmd->command, error))
return FALSE;
if (!((!layer->parent_program || cbox_execute_on(fb, NULL, "/parent_program", "o", error, layer->parent_program)) &&
(!layer->parent || cbox_execute_on(fb, NULL, "/parent", "o", error, layer->parent)) &&
CBOX_OBJECT_DEFAULT_STATUS(layer, fb, error)))
return FALSE;
return TRUE;
}
if ((!strcmp(cmd->command, "/as_string") || !strcmp(cmd->command, "/as_string_full")) && !strcmp(cmd->arg_types, ""))
{
if (!cbox_check_fb_channel(fb, cmd->command, error))
return FALSE;
gchar *res = sampler_layer_to_string(layer, !strcmp(cmd->command, "/as_string_full"));
gboolean result = cbox_execute_on(fb, NULL, "/value", "s", error, res[0] == ' ' ? res + 1 : res);
g_free(res);
return result;
}
if (!strcmp(cmd->command, "/set_param") && !strcmp(cmd->arg_types, "ss"))
{
const char *key = CBOX_ARG_S(cmd, 0);
const char *value = CBOX_ARG_S(cmd, 1);
if (sampler_layer_apply_param(layer, key, value, error))
{
sampler_layer_update(layer);
sampler_program_update_layers(layer->parent_program);
return TRUE;
}
return FALSE;
}
if (!strcmp(cmd->command, "/unset_param") && !strcmp(cmd->arg_types, "s"))
{
const char *key = CBOX_ARG_S(cmd, 0);
if (sampler_layer_unapply_param(layer, key, error))
{
sampler_layer_update(layer);
sampler_program_update_layers(layer->parent_program);
return TRUE;
}
return FALSE;
}
if (!strcmp(cmd->command, "/new_child") && !strcmp(cmd->arg_types, ""))
{
// XXXKF needs a string argument perhaps
if (layer->parent && layer->parent->parent && layer->parent->parent->parent)
{
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create a region within a region");
return FALSE;
}
struct sampler_layer *l = sampler_layer_new(layer->module, layer->parent_program, layer);
sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, layer->parent_program);
sampler_layer_reset_switches(l, l->module);
sampler_layer_update(l);
if (l->parent && l->parent->parent && l->parent->parent->parent)
{
sampler_program_add_layer(layer->parent_program, l);
sampler_program_update_layers(layer->parent_program);
}
return cbox_execute_on(fb, NULL, "/uuid", "o", error, l);
}
if (!strcmp(cmd->command, "/get_children") && !strcmp(cmd->arg_types, ""))
{
if (!cbox_check_fb_channel(fb, cmd->command, error))
return FALSE;
GHashTableIter iter;
g_hash_table_iter_init(&iter, layer->child_layers);
gpointer key, value;
while(g_hash_table_iter_next(&iter, &key, &value))
{
if (!cbox_execute_on(fb, NULL, "/child", "o", error, key))
return FALSE;
}
return TRUE;
}
// otherwise, treat just like an command on normal (non-aux) output
return cbox_object_default_process_cmd(ct, fb, cmd, error);
}
#define PROC_FIELDS_INITIALISER(type, name, def_value) \
ld->name = def_value; \
ld->has_##name = 0;
#define PROC_FIELDS_INITIALISER_string(name) \
ld->name = NULL; \
ld->name##_changed = FALSE; \
ld->has_##name = 0;
#define PROC_FIELDS_INITIALISER_midicurve(name) \
sampler_midi_curve_init(&ld->name);
#define PROC_FIELDS_INITIALISER_enum(type, name, def_value) \
PROC_FIELDS_INITIALISER(type, name, def_value)
#define PROC_FIELDS_INITIALISER_dBamp(type, name, def_value) \
ld->name = def_value; \
ld->name##_linearized = -1; \
ld->has_##name = 0;
#define PROC_FIELDS_INITIALISER_dahdsr(name, parname, index) \
DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \
DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld)
#define PROC_FIELDS_INITIALISER_lfo(name, parname, index) \
LFO_FIELDS(PROC_SUBSTRUCT_RESET_FIELD, name, ld); \
LFO_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, ld)
#define PROC_FIELDS_INITIALISER_eq(name, parname, index) \
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 = NULL;
CBOX_CLASS_DEFINITION_ROOT(sampler_layer)
struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent)
{
struct sampler_layer *l = calloc(1, sizeof(struct sampler_layer));
struct cbox_document *doc = CBOX_GET_DOCUMENT(parent_program);
memset(l, 0, sizeof(struct sampler_layer));
CBOX_OBJECT_HEADER_INIT(l, sampler_layer, doc);
cbox_command_target_init(&l->cmd_target, sampler_layer_process_cmd, l);
l->module = m;
l->child_layers = g_hash_table_new(NULL, NULL);
if (parent)
{
sampler_layer_data_clone(&l->data, &parent->data, FALSE);
l->parent_program = parent_program;
l->parent = parent;
g_hash_table_replace(parent->child_layers, l, l);
l->runtime = NULL;
CBOX_OBJECT_REGISTER(l);
return l;
}
l->parent_program = parent_program;
struct sampler_layer_data *ld = &l->data;
SAMPLER_FIXED_FIELDS(PROC_FIELDS_INITIALISER)
ld->computed.eff_waveform = NULL;
ld->computed.eff_freq = 44100;
ld->modulations = NULL;
ld->voice_nifs = NULL;
ld->prevoice_nifs = NULL;
ld->computed.eff_use_keyswitch = 0;
if (!parent)
{
// Systemwide default instead?
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;
CBOX_OBJECT_REGISTER(l);
return l;
}
#define PROC_FIELDS_CLONE(type, name, def_value) \
dst->name = src->name; \
dst->has_##name = copy_hasattr ? src->has_##name : FALSE;
#define PROC_FIELDS_CLONE_string(name) \
dst->name = src->name ? g_strdup(src->name) : NULL; \
dst->name##_changed = src->name##_changed; \
dst->has_##name = copy_hasattr ? src->has_##name : FALSE;
#define PROC_FIELDS_CLONE_midicurve(name) \
memcpy(dst->name.values, src->name.values, sizeof(float) * 128); \
if(copy_hasattr) \
memcpy(dst->name.has_values, src->name.has_values, sizeof(src->name.has_values));
#define PROC_FIELDS_CLONE_dBamp PROC_FIELDS_CLONE
#define PROC_FIELDS_CLONE_enum PROC_FIELDS_CLONE
#define PROC_FIELDS_CLONE_dahdsr(name, parname, index) \
DAHDSR_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \
if (!copy_hasattr) \
DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst)
#define PROC_FIELDS_CLONE_lfo(name, parname, index) \
LFO_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \
if (!copy_hasattr) \
LFO_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst)
#define PROC_FIELDS_CLONE_eq(name, parname, index) \
EQ_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \
if (!copy_hasattr) \
EQ_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst)
#define PROC_FIELDS_CLONE_ccrange(name, parname) \
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 = 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)
{
for (uint32_t i = 0; i < 128; ++i)
curve->values[i] = SAMPLER_CURVE_GAP;
memset(curve->has_values, 0, 128);
}
void sampler_midi_curve_interpolate(const struct sampler_midi_curve *curve, float dest[128], float def_start, float def_end, gboolean is_quadratic)
{
const float *src = curve->values;
int start = 0;
float sv = src[start];
if (sv == SAMPLER_CURVE_GAP)
sv = def_start;
if (is_quadratic && sv >= 0)
sv = sqrtf(sv);
for (int i = 1; i < 128; i++)
{
float ev = src[i];
if (ev == SAMPLER_CURVE_GAP)
{
if (i < 127)
continue;
else
ev = def_end;
}
if (is_quadratic && ev >= 0)
ev = sqrtf(ev);
if (is_quadratic)
{
for (int j = start; j <= i; j++)
dest[j] = powf(sv + (ev - sv) * (j - start) / (i - start), 2.f);
}
else
{
for (int j = start; j <= i; j++)
dest[j] = sv + (ev - sv) * (j - start) / (i - start);
}
start = i;
sv = ev;
}
}
static inline int sampler_filter_num_stages(float cutoff, enum sampler_filter_type fil_type)
{
if (cutoff == -1)
return 0;
if (fil_type == sft_lp24hybrid || fil_type == sft_lp24 || fil_type == sft_lp24nr || fil_type == sft_hp24 || fil_type == sft_hp24nr || fil_type == sft_bp12)
return 2;
if (fil_type == sft_lp36)
return 3;
return 1;
}
// If veltrack > 0, then the default range goes from -84dB to 0dB
// If veltrack == 0, then the default range is all 0dB
// If veltrack < 0, then the default range goes from 0dB to -84dB
#define START_VALUE_amp_velcurve (l->amp_veltrack > 0 ? dB2gain(-l->amp_veltrack * 84.0 / 100.0) : 1)
#define END_VALUE_amp_velcurve (l->amp_veltrack < 0 ? dB2gain(l->amp_veltrack * 84.0 / 100.0) : 1)
#define IS_QUADRATIC_amp_velcurve l->velcurve_quadratic
#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->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);
#define PROC_FIELDS_FINALISER_dahdsr(name, parname, index) \
cbox_envelope_init_dahdsr(&l->name##_shape, &l->name, m->module.srate / CBOX_BLOCK_SIZE, 100.f, &l->name##_shape == &l->amp_env_shape);
#define PROC_FIELDS_FINALISER_lfo(name, parname, index) /* no finaliser required */
#define PROC_FIELDS_FINALISER_eq(name, parname, index) l->name.effective_freq = (l->name.freq ? l->name.freq : 5 * powf(10.f, 1 + (index)));
#define PROC_FIELDS_FINALISER_ccrange(name, parname) /* no finaliser required */
void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_layer_data *parent, struct sampler_program *p)
{
struct sampler_module *m = p->module;
SAMPLER_FIXED_FIELDS(PROC_FIELDS_FINALISER)
// 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->computed.eff_waveform;
if (l->sample && *l->sample)
{
GError *error = NULL;
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\" in sample_dir \"%s\" : \"%s\"", l->sample, p->sample_dir, error ? error->message : "unknown error");
g_error_free(error);
}
}
else
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->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) &&
(l->lorand == 0 && l->hirand == 1) &&
(l->lobend == -8192 && l->hibend == 8192) &&
(l->lochanaft == 0 && l->hichanaft == 127) &&
(l->lopolyaft == 0 && l->hipolyaft == 127) &&
(l->lobpm == 0 && l->hibpm == NO_HI_BPM_VALUE) &&
!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->computed.eff_waveform && l->computed.eff_waveform->has_loop)
l->computed.eff_loop_mode = slm_loop_continuous;
else
if (l->computed.eff_waveform)
l->computed.eff_loop_mode = l->loop_end == 0 ? slm_no_loop : slm_loop_continuous;
}
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;
// XXXKF this is dodgy, needs to convert to use 'programmed vs effective' values pattern
if (l->key >= 0 && l->key <= 127)
l->lokey = l->hikey = l->pitch_keycenter = l->key;
// 'linearize' the virtual circular buffer - write 3 (or N) frames before end of the loop
// 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->computed.eff_waveform && l->computed.eff_waveform->preloaded_frames == (size_t)l->computed.eff_waveform->info.frames)
{
int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0;
uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift;
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->computed.scratch_loop + halfscratch, 0, halfscratch * sizeof(int16_t));
}
if (l->cutoff < 20)
l->computed.logcutoff = -1;
else
l->computed.logcutoff = 1200.0 * log(l->cutoff / 440.0) / log(2) + 5700.0;
if (l->cutoff2 < 20)
l->computed.logcutoff2 = -1;
else
l->computed.logcutoff2 = 1200.0 * log(l->cutoff2 / 440.0) / log(2) + 5700.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->computed.mod_bitmask = 0;
for(struct sampler_modulation *mod = l->modulations; mod; mod = mod->next)
{
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->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->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)
{
l->current_seq_position = l->data.seq_position;
}
struct layer_foreach_struct
{
struct sampler_layer *layer;
const char *cfg_section;
};
static void layer_foreach_func(void *user_data, const char *key)
{
if (!strcmp(key, "file"))
key = "sample";
// import is handled in sampler_load_layer_overrides
if (!strcmp(key, "import"))
return;
// layer%d should be ignored, it's handled by sampler_program_new_from_cfg
if (!strncmp(key, "layer", 5) && isdigit(key[5]))
return;
struct layer_foreach_struct *lfs = user_data;
const char *value = cbox_config_get_string(lfs->cfg_section, key);
GError *error = NULL;
if (!sampler_layer_apply_param(lfs->layer, key, value, &error))
{
if (error)
g_warning("Error '%s', context: %s in section %s", error->message, key, lfs->cfg_section);
else
g_warning("Unknown sample layer parameter: %s in section %s", key, lfs->cfg_section);
}
}
void sampler_layer_load_overrides(struct sampler_layer *l, const char *cfg_section)
{
char *imp = cbox_config_get_string(cfg_section, "import");
if (imp)
sampler_layer_load_overrides(l, imp);
struct layer_foreach_struct lfs = {
.layer = l,
.cfg_section = cfg_section
};
cbox_config_foreach_key(layer_foreach_func, cfg_section, &lfs);
}
struct sampler_layer *sampler_layer_new_from_section(struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent, const char *cfg_section)
{
struct sampler_layer *l = sampler_layer_new(m, parent_program, parent ? parent : parent_program->global->default_child->default_child);
sampler_layer_load_overrides(l, cfg_section);
sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, parent_program);
sampler_layer_reset_switches(l, m);
return l;
}
static void sampler_layer_apply_unknown(struct sampler_layer *l, const char *key, const char *value)
{
if (!l->unknown_keys)
l->unknown_keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert(l->unknown_keys, g_strdup(key), g_strdup(value));
}
gboolean sampler_layer_apply_param(struct sampler_layer *l, const char *key, const char *value, GError **error)
{
int res = sampler_layer_apply_fixed_param(l, key, value, error);
if (res >= 0)
return res;
sampler_layer_apply_unknown(l, key, value);
//g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown SFZ property key: '%s'", key);
//return FALSE;
g_warning("Unknown SFZ property key: '%s'", key);
return TRUE;
}
gboolean sampler_layer_unapply_param(struct sampler_layer *layer, const char *key, GError **error)
{
int res = sampler_layer_unapply_fixed_param(layer, key, error);
if (res >= 0)
return res;
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown SFZ property key: '%s'", key);
return FALSE;
}
#define TYPE_PRINTF_uint32_t(name, def_value) \
if (show_inherited || l->has_##name) \
g_string_append_printf(outstr, " %s=%u", #name, (unsigned)(l->name));
#define TYPE_PRINTF_int(name, def_value) \
if (show_inherited || l->has_##name) \
g_string_append_printf(outstr, " %s=%d", #name, (int)(l->name));
#define TYPE_PRINTF_midi_note_t(name, def_value) \
if (show_inherited || l->has_##name) { \
int val = l->name; \
if (val == -1) \
g_string_append_printf(outstr, " %s=-1", #name); \
else \
g_string_append_printf(outstr, " %s=%c%s%d", #name, "ccddeffggaab"[val%12], "\000#\000#\000\000#\000#\000#\000#\000"+(val%12), (val/12-1)); \
} else {}
#define TYPE_PRINTF_float(name, def_value) \
if (show_inherited || l->has_##name) \
g_string_append_printf(outstr, " %s=%s", #name, g_ascii_dtostr(floatbuf, floatbufsize, l->name));
#define PROC_FIELDS_TO_FILEPTR(type, name, def_value) \
TYPE_PRINTF_##type(name, def_value)
#define PROC_FIELDS_TO_FILEPTR_string(name) \
if (show_inherited || l->has_##name) \
g_string_append_printf(outstr, " %s=%s", #name, l->name ? l->name : "");
#define PROC_FIELDS_TO_FILEPTR_midicurve(name) \
for (uint32_t i = 0; i < 128; ++i) { \
if ((show_inherited || l->name.has_values[i]) && l->name.values[i] != SAMPLER_CURVE_GAP) \
g_string_append_printf(outstr, " %s_%u=%s", #name, (unsigned)i, g_ascii_dtostr(floatbuf, floatbufsize, l->name.values[i])); \
}
#define PROC_FIELDS_TO_FILEPTR_dBamp(type, name, def_value) \
if (show_inherited || l->has_##name) \
g_string_append_printf(outstr, " %s=%s", #name, g_ascii_dtostr(floatbuf, floatbufsize, l->name));
#define PROC_FIELDS_TO_FILEPTR_enum(enumtype, name, def_value) \
if ((show_inherited || l->has_##name) && (tmpstr = enumtype##_to_string(l->name)) != NULL) \
g_string_append_printf(outstr, " %s=%s", #name, tmpstr);
#define ENV_PARAM_OUTPUT(param, index, def_value, env, envfield, envname) \
if (show_inherited || l->has_##envfield.param) \
g_string_append_printf(outstr, " " #envname "_" #param "=%s", g_ascii_dtostr(floatbuf, floatbufsize, env.param));
#define PROC_FIELDS_TO_FILEPTR_dahdsr(name, parname, index) \
DAHDSR_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname)
#define PROC_FIELDS_TO_FILEPTR_lfo(name, parname, index) \
LFO_FIELDS(ENV_PARAM_OUTPUT, l->name, name, parname)
#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) \
{ \
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" };
static const char *modsrc_names[] = { "vel", "chanaft", "polyaft", "pitch", "pitcheg", "fileg", "ampeg", "pitchlfo", "fillfo", "amplfo", "" };
static const char *moddest_names[] = { "gain", "pitch", "cutoff", "resonance", "tonectl", "pan", "amplitude", "cutoff2", "resonance2", "pitchlfo_freq", "fillfo_freq", "amplfo_freq",
"eq1_freq", "eq1_bw", "eq1_gain",
"eq2_freq", "eq2_bw", "eq2_gain",
"eq3_freq", "eq3_bw", "eq3_gain",
};
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)
{
uint32_t param = md->dest - smdest_eg_stage_start;
g_string_append_printf(outstr, " %seg_%s%s%d=%s", addrandom_variants[(param >> 4) & 3], env_stages[param & 15], attrib, md->src, floatbuf);
}
else if (md->src < smsrc_perchan_count)
{
g_string_append_printf(outstr, " %s%s%d=%s", moddest_names[md->dest], attrib, md->src, floatbuf);
}
else 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 (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 < 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 < 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 >= 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)
{
struct sampler_layer_data *l = &lr->data;
GString *outstr = g_string_sized_new(200);
const char *tmpstr;
char floatbuf[G_ASCII_DTOSTR_BUF_SIZE];
int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE;
SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_FILEPTR)
for(struct sampler_noteinitfunc *nd = l->voice_nifs; nd; nd = nd->next)
{
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" };
uint32_t v = nd->key.variant;
g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value);
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->key.notefunc_voice == sampler_nif_vel2reloffset)
g_string_append_printf(outstr, " reloffset_veltrack=%s", floatbuf);
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->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(struct sampler_noteinitfunc *nd = l->prevoice_nifs; nd; nd = nd->next)
{
if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited)
continue;
g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value);
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(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)
{
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, mv->curve_id);
mod_cc_attrib_to_string(outstr, "_curvecc", mk, floatbuf);
}
if (mv->has_smooth || show_inherited)
{
g_ascii_dtostr(floatbuf, floatbufsize, mv->smooth);
mod_cc_attrib_to_string(outstr, "_smoothcc", mk, floatbuf);
}
if (mv->has_step || show_inherited)
{
g_ascii_dtostr(floatbuf, floatbufsize, mv->step);
mod_cc_attrib_to_string(outstr, "_stepcc", mk, floatbuf);
}
if (mv->has_amount || show_inherited)
{
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 (mk->src2 == smsrc_none)
{
if (is_egcc)
{
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 (mk->src < smsrc_perchan_count)
{
// Inconsistency: cutoff_cc5 but amplfo_freqcc5
if (is_lfofreq)
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[mk->dest], mk->src, floatbuf);
continue;
}
if (mk->src < smsrc_perchan_count + sizeof(modsrc_names) / sizeof(modsrc_names[0]))
{
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[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf);
else
g_string_append_printf(outstr, " %s_%s=%s", moddest_names[mk->dest], modsrc_names[mk->src - smsrc_perchan_count], floatbuf);
continue;
}
}
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(mk->src2)
{
case smsrc_chanaft:
case smsrc_polyaft:
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[mk->src - smsrc_perchan_count], floatbuf);
continue;
default:
if (mk->src2 < EXT_CC_COUNT)
{
g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf);
continue;
}
break;
}
}
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 (mk->src2 == smsrc_vel)
{
g_string_append_printf(outstr, " %s_vel2depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf);
continue;
}
if (mk->src2 == smsrc_none)
{
g_string_append_printf(outstr, " %s_depth=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf);
continue;
}
if (mk->src2 < EXT_CC_COUNT)
{
g_string_append_printf(outstr, " %s_depthcc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf);
continue;
}
}
if (mk->src == smsrc_filenv && mk->dest == smdest_cutoff2)
{
if (mk->src2 == smsrc_vel)
{
g_string_append_printf(outstr, " %s_vel2depth2=%s", modsrc_names[mk->src - smsrc_perchan_count], floatbuf);
continue;
}
assert(mk->src2 != smsrc_none);
if (mk->src2 < EXT_CC_COUNT)
{
g_string_append_printf(outstr, " %s_depth2cc%d=%s", modsrc_names[mk->src - smsrc_perchan_count], mk->src2, floatbuf);
continue;
}
}
if (mk->src == smsrc_fillfo && mk->dest == smdest_cutoff2)
{
assert(mk->src2 != smsrc_none);
if (mk->src2 < EXT_CC_COUNT)
{
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", mk->src, mk->src2, mk->dest, mv->curve_id, floatbuf);
}
}
if (lr->unknown_keys)
{
GHashTableIter hti;
gchar *key, *value;
g_hash_table_iter_init(&hti, lr->unknown_keys);
while(g_hash_table_iter_next(&hti, (gpointer *)&key, (gpointer *)&value))
g_string_append_printf(outstr, " %s=%s", key, value);
}
gchar *res = outstr->str;
g_string_free(outstr, FALSE);
return res;
}
void sampler_layer_dump(struct sampler_layer *l, FILE *f)
{
gchar *str = sampler_layer_to_string(l, FALSE);
fprintf(f, "%s\n", str);
}
void sampler_layer_data_close(struct sampler_layer_data *l)
{
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->computed.eff_waveform);
l->computed.eff_waveform = NULL;
}
g_free(l->sample);
}
void sampler_layer_data_destroy(struct sampler_layer_data *l)
{
sampler_layer_data_close(l);
free(l);
}
struct sampler_layer *sampler_layer_new_clone(struct sampler_layer *layer,
struct sampler_module *m, struct sampler_program *parent_program, struct sampler_layer *parent)
{
struct sampler_layer *l = sampler_layer_new(m, parent_program, parent);
sampler_layer_data_clone(&l->data, &layer->data, TRUE);
sampler_layer_reset_switches(l, m);
if (layer->unknown_keys)
{
GHashTableIter iter;
g_hash_table_iter_init(&iter, layer->unknown_keys);
gpointer key, value;
while(g_hash_table_iter_next(&iter, &key, &value))
sampler_layer_apply_param(l, (gchar *)key, (gchar *)value, NULL);
}
GHashTableIter iter;
g_hash_table_iter_init(&iter, layer->child_layers);
gpointer key, value;
gboolean is_child_a_region = layer->parent && layer->parent->parent;
while(g_hash_table_iter_next(&iter, &key, &value))
{
struct sampler_layer *chl = sampler_layer_new_clone(key, m, parent_program, l);
g_hash_table_insert(l->child_layers, chl, NULL);
if (key == layer->default_child)
l->default_child = chl;
if (is_child_a_region)
sampler_program_add_layer(parent_program, chl);
}
return l;
}
void sampler_layer_destroyfunc(struct cbox_objhdr *objhdr)
{
struct sampler_layer *l = CBOX_H2O(objhdr);
struct sampler_program *prg = l->parent_program;
assert(g_hash_table_size(l->child_layers) == 0);
if (l->parent)
{
g_hash_table_remove(l->parent->child_layers, l);
if (prg && prg->rll)
{
sampler_program_delete_layer(prg, l);
sampler_program_update_layers(l->parent_program);
}
l->parent = NULL;
}
sampler_layer_data_close(&l->data);
if (l->runtime)
sampler_layer_data_destroy(l->runtime);
if (l->unknown_keys)
g_hash_table_destroy(l->unknown_keys);
if (l->child_layers)
g_hash_table_destroy(l->child_layers);
free(l);
}
//////////////////////////////////////////////////////////////////////////
struct sampler_layer_update_cmd
{
struct sampler_module *module;
struct sampler_layer *layer;
struct sampler_layer_data *new_data;
struct sampler_layer_data *old_data;
};
static int sampler_layer_update_cmd_prepare(void *data)
{
struct sampler_layer_update_cmd *cmd = data;
cmd->old_data = cmd->layer->runtime;
cmd->new_data = calloc(1, sizeof(struct sampler_layer_data));
sampler_layer_data_clone(cmd->new_data, &cmd->layer->data, TRUE);
sampler_layer_data_finalize(cmd->new_data, cmd->layer->parent ? &cmd->layer->parent->data : NULL, cmd->layer->parent_program);
if (cmd->layer->runtime == NULL)
{
// initial update of the layer, so none of the voices need updating yet
// because the layer hasn't been allocated to any voice
cmd->layer->runtime = cmd->new_data;
free(cmd);
return 1;
}
return 0;
}
static int sampler_layer_update_cmd_execute(void *data)
{
struct sampler_layer_update_cmd *cmd = data;
for (int i = 0; i < 16; i++)
{
FOREACH_VOICE(cmd->module->channels[i].voices_running, v)
{
if (v->layer == cmd->layer->runtime)
{
v->layer = cmd->new_data;
v->layer_changed = TRUE;
sampler_voice_update_params_from_layer(v);
}
}
}
FOREACH_PREVOICE(cmd->module->prevoices_running, pv)
{
if (pv->layer_data == cmd->layer->runtime)
{
pv->layer_data = cmd->new_data;
// XXXKF when need arises
// pv->layer_changed = TRUE;
// sampler_prevoice_update_params_from_layer(v);
}
}
cmd->old_data = cmd->layer->runtime;
cmd->layer->runtime = cmd->new_data;
return 10;
}
static void sampler_layer_update_cmd_cleanup(void *data)
{
struct sampler_layer_update_cmd *cmd = data;
sampler_layer_data_destroy(cmd->old_data);
free(cmd);
}
void sampler_layer_update(struct sampler_layer *l)
{
// if changing a group, update all child regions instead
if (g_hash_table_size(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))
{
sampler_layer_data_finalize(&((struct sampler_layer *)key)->data, &l->data, l->parent_program);
sampler_layer_update((struct sampler_layer *)key);
}
return;
}
static struct cbox_rt_cmd_definition rtcmd = {
.prepare = sampler_layer_update_cmd_prepare,
.execute = sampler_layer_update_cmd_execute,
.cleanup = sampler_layer_update_cmd_cleanup,
};
struct sampler_layer_update_cmd *lcmd = malloc(sizeof(struct sampler_layer_update_cmd));
lcmd->module = l->module;
lcmd->layer = l;
lcmd->new_data = NULL;
lcmd->old_data = NULL;
cbox_rt_execute_cmd_async(l->module->module.rt, &rtcmd, lcmd);
}