Sampled Instrument Player with static and monolithic design. All instruments are built-in.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

324 lines
10 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/>.
*/
#ifndef CBOX_ENVELOPE_H
#define CBOX_ENVELOPE_H
#include <glib.h>
#include <config-api.h>
struct cbox_envstage
{
double end_value;
int time;
int next_if_pressed, next_if_released, keep_last_value, break_on_release, is_exp;
};
#define MAX_ENV_STAGES 16
#define EXP_NOISE_FLOOR (100.0 / 16384.0)
struct cbox_envelope_shape
{
double start_value;
struct cbox_envstage stages[MAX_ENV_STAGES];
};
struct cbox_envelope
{
struct cbox_envelope_shape *shape;
double stage_start_value, cur_value, exp_factor, inv_time, cur_time, orig_time, orig_target;
int cur_stage;
};
static inline void cbox_envelope_init_stage(struct cbox_envelope *env)
{
struct cbox_envstage *es = &env->shape->stages[env->cur_stage];
env->orig_time = es->time;
env->orig_target = es->end_value;
env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6;
if (es->is_exp)
{
if (env->stage_start_value < EXP_NOISE_FLOOR)
env->stage_start_value = EXP_NOISE_FLOOR;
double ev = es->end_value;
if (ev < EXP_NOISE_FLOOR)
ev = EXP_NOISE_FLOOR;
env->exp_factor = log(ev / env->stage_start_value);
}
}
static inline void cbox_envelope_go_to(struct cbox_envelope *env, int stage)
{
env->stage_start_value = env->cur_value;
env->cur_stage = stage;
env->cur_time = 0;
cbox_envelope_init_stage(env);
}
static inline void cbox_envelope_reset(struct cbox_envelope *env)
{
env->cur_value = 0;
env->cur_stage = 0;
env->cur_time = 0;
cbox_envelope_init_stage(env);
}
static inline void cbox_envelope_update_shape(struct cbox_envelope *env, struct cbox_envelope_shape *shape)
{
struct cbox_envelope_shape *old_shape = env->shape;
env->shape = shape;
if (env->cur_stage < 0)
return;
struct cbox_envstage *ns = &env->shape->stages[env->cur_stage];
struct cbox_envstage *os = &old_shape->stages[env->cur_stage];
if (os->time > 0)
env->cur_time = env->cur_time * ns->time / os->time;
if (env->cur_time > ns->time)
env->cur_time = ns->time;
}
static inline float cbox_envelope_get_value(struct cbox_envelope *env, const struct cbox_envelope_shape *shape)
{
if (env->cur_stage < 0)
return env->cur_value;
const struct cbox_envstage *es = &shape->stages[env->cur_stage];
double pos = es->time > 0 ? env->cur_time * env->inv_time : 0;
if (pos > 1)
pos = 1;
if (es->is_exp)
{
// instead of exp, may use 2**x which can be factored
// into a shift and a table lookup
env->cur_value = env->stage_start_value * expf(pos * env->exp_factor);
if (env->cur_value <= EXP_NOISE_FLOOR)
env->cur_value = 0;
}
else
env->cur_value = env->stage_start_value + (es->end_value - env->stage_start_value) * pos;
return env->cur_value;
}
#define DEBUG_UPDATE_SHAPE(...)
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)
{
// Scale cur_time to reflect the same relative position within the stage
env->cur_time = env->cur_time * es->time / (env->orig_time > 0 ? env->orig_time : 1);
env->orig_time = es->time;
env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6;
}
if (es->end_value != env->orig_target)
{
// Adjust the start value to keep the current value intact given the change in the slope
double pos = es->time > 0 ? env->cur_time * env->inv_time : 1;
if (pos < 1)
{
if (es->is_exp)
env->stage_start_value /= pow(es->end_value / (env->orig_target >= EXP_NOISE_FLOOR ? env->orig_target : EXP_NOISE_FLOOR), pos / (1 - pos)); // untested, likely never used
else
env->stage_start_value -= (es->end_value - env->orig_target) * pos / (1 - pos);
}
env->orig_target = es->end_value;
}
}
static inline void cbox_envelope_advance(struct cbox_envelope *env, int released, const struct cbox_envelope_shape *shape)
{
if (env->cur_stage < 0)
return;
const struct cbox_envstage *es = &shape->stages[env->cur_stage];
double pos = es->time > 0 ? env->cur_time * env->inv_time : 1;
env->cur_time++;
if (pos >= 1 || (es->break_on_release && released))
{
int next_stage = released ? es->next_if_released : es->next_if_pressed;
if (!es->keep_last_value || pos >= 1 || (es->keep_last_value == 2 && !released) || next_stage == env->cur_stage)
env->stage_start_value = es->end_value;
else
env->stage_start_value = env->cur_value;
env->cur_stage = next_stage;
env->cur_time = 0;
cbox_envelope_init_stage(env);
}
}
struct cbox_adsr
{
float attack;
float decay;
float sustain;
float release;
};
static inline void cbox_envelope_init_adsr(struct cbox_envelope_shape *env, const struct cbox_adsr *adsr, int sr)
{
env->start_value = 0;
env->stages[0].end_value = 1;
env->stages[0].time = adsr->attack * sr;
env->stages[0].next_if_pressed = 1;
env->stages[0].next_if_released = 3;
env->stages[0].keep_last_value = 1;
env->stages[0].break_on_release = 0;
env->stages[0].is_exp = 0;
env->stages[1].end_value = adsr->sustain;
env->stages[1].time = adsr->decay * sr;
env->stages[1].next_if_pressed = 2;
env->stages[1].next_if_released = 3;
env->stages[1].keep_last_value = 1;
env->stages[1].break_on_release = 0;
env->stages[1].is_exp = 0;
env->stages[2].end_value = adsr->sustain;
env->stages[2].time = 1 * sr;
env->stages[2].next_if_pressed = 2;
env->stages[2].next_if_released = 3;
env->stages[2].keep_last_value = 0;
env->stages[2].break_on_release = 1;
env->stages[2].is_exp = 0;
env->stages[3].end_value = 0;
env->stages[3].time = adsr->release * sr;
env->stages[3].next_if_pressed = -1;
env->stages[3].next_if_released = -1;
env->stages[3].keep_last_value = 0;
env->stages[3].break_on_release = 0;
env->stages[3].is_exp = 1;
env->stages[15].end_value = 0;
env->stages[15].time = 0.01 * sr;
env->stages[15].next_if_pressed = -1;
env->stages[15].next_if_released = -1;
env->stages[15].keep_last_value = 0;
env->stages[15].break_on_release = 0;
env->stages[15].is_exp = 0;
}
struct cbox_dahdsr
{
float start;
float delay;
float attack;
float hold;
float decay;
float sustain;
float release;
};
static inline void cbox_dahdsr_init(struct cbox_dahdsr *dahdsr, float top_value)
{
dahdsr->start = 0.f;
dahdsr->delay = 0.f;
dahdsr->attack = 0.f;
dahdsr->hold = 0.f;
dahdsr->decay = 0.f;
dahdsr->sustain = top_value;
dahdsr->release = 0.05f;
}
static inline void cbox_envelope_init_dahdsr(struct cbox_envelope_shape *env, const struct cbox_dahdsr *dahdsr, int sr, float top_value, gboolean is_release_exp)
{
env->start_value = dahdsr->start;
env->stages[0].end_value = dahdsr->start;
env->stages[0].time = dahdsr->delay * sr;
env->stages[0].next_if_pressed = 1;
env->stages[0].next_if_released = 5;
env->stages[0].keep_last_value = 1;
env->stages[0].break_on_release = 0;
env->stages[0].is_exp = 0;
env->stages[1].end_value = top_value;
env->stages[1].time = dahdsr->attack * sr;
env->stages[1].next_if_pressed = 2;
env->stages[1].next_if_released = 5;
env->stages[1].keep_last_value = 2;
env->stages[1].break_on_release = 1;
env->stages[1].is_exp = 0;
env->stages[2].end_value = top_value;
env->stages[2].time = dahdsr->hold * sr;
env->stages[2].next_if_pressed = 3;
env->stages[2].next_if_released = 5;
env->stages[2].keep_last_value = 2;
env->stages[2].break_on_release = 1;
env->stages[2].is_exp = 0;
env->stages[3].end_value = dahdsr->sustain;
env->stages[3].time = dahdsr->decay * sr;
env->stages[3].next_if_pressed = 4;
env->stages[3].next_if_released = 5;
env->stages[3].keep_last_value = 1;
env->stages[3].break_on_release = 1;
env->stages[3].is_exp = 0;
env->stages[4].end_value = dahdsr->sustain;
env->stages[4].time = 1 * sr;
env->stages[4].next_if_pressed = 4;
env->stages[4].next_if_released = 5;
env->stages[4].keep_last_value = 1;
env->stages[4].break_on_release = 1;
env->stages[4].is_exp = 0;
env->stages[5].end_value = 0;
env->stages[5].time = dahdsr->release * sr;
env->stages[5].next_if_pressed = -1;
env->stages[5].next_if_released = -1;
env->stages[5].keep_last_value = 0;
env->stages[5].break_on_release = 0;
env->stages[5].is_exp = is_release_exp;
env->stages[15].end_value = 0;
env->stages[15].time = 0.01 * sr;
env->stages[15].next_if_pressed = -1;
env->stages[15].next_if_released = -1;
env->stages[15].keep_last_value = 0;
env->stages[15].break_on_release = 0;
env->stages[15].is_exp = 0;
}
static inline void cbox_envelope_modify_dahdsr(struct cbox_envelope_shape *env, int part, float value, int sr)
{
switch(part)
{
case 0: // delay
case 1: // attack
case 2: // hold
case 3: // decay
case 5: // release
env->stages[part].time += value * sr;
// Allow negative times (deal with them in get_next) to make multiple signed modulations work correctly
break;
case 4: // sustain
env->stages[3].end_value += value;
env->stages[4].end_value += value;
env->stages[4].time = 0.02 * sr; // more rapid transition
break;
case 6: // start
env->stages[0].end_value += value;
env->start_value += value;
break;
}
}
#endif