1
0
mirror of https://github.com/sjlongland/atinysynth.git synced 2025-09-13 10:03:15 +10:00

adsr: Support "infinite" delay and sustain.

This is useful if you don't know how long a given note will be held for…
you can just trigger the note upon pressing the button and have it
progress to the "sustain" state, at which point it'll hold the note.

You can then call `adsr_continue` to "release" that note when you detect
the button release and have it decay.
This commit is contained in:
Stuart Longland 2017-05-27 15:14:44 +10:00
parent f2fac87ba3
commit d9bfcb7991
Signed by: stuartl
GPG Key ID: F954BBBB7948D546
2 changed files with 34 additions and 8 deletions

23
adsr.c
View File

@ -28,6 +28,18 @@
/* ADSR attack/decay adjustments */
#define ADSR_LIN_AMP_FACTOR (5)
/*!
* Helper macro, returns the time in samples if the number of
* time units UINT8_MAX (infinite), otherwise it returns
* UINT32_MAX.
*/
static inline uint32_t adsr_num_samples(uint32_t scale, uint8_t units) {
if (~units)
return scale * units;
else
return UINT32_MAX;
}
/*!
* ADSR Attack amplitude exponential shift.
*/
@ -52,7 +64,8 @@ static uint8_t adsr_release_amp(uint8_t amp, uint8_t count) {
uint8_t adsr_next(struct adsr_env_gen_t* const adsr) {
if (adsr->next_event) {
/* Still waiting for next event */
adsr->next_event--;
if (~adsr->next_event)
adsr->next_event--;
_DPRINTF("adsr=%p amp=%d next_in=%d\n",
adsr, adsr->amplitude, adsr->next_event);
return adsr->amplitude;
@ -109,8 +122,8 @@ uint8_t adsr_next(struct adsr_env_gen_t* const adsr) {
/* Setting up a delay */
adsr->amplitude = 0;
adsr->next_event = adsr->time_scale
* adsr->delay_time;
adsr->next_event = adsr_num_samples(
adsr->time_scale, adsr->delay_time);
adsr->state = ADSR_STATE_DELAY_EXPIRE;
/* Wait for delay */
return adsr->amplitude;
@ -214,8 +227,8 @@ uint8_t adsr_next(struct adsr_env_gen_t* const adsr) {
_DPRINTF("adsr=%p SUSTAIN INIT\n", adsr);
adsr->amplitude = adsr->sustain_amp;
adsr->next_event = adsr->time_scale
* adsr->sustain_time;
adsr->next_event = adsr_num_samples(
adsr->time_scale, adsr->sustain_time);
adsr->state = ADSR_STATE_SUSTAIN_EXPIRE;
/* Wait for delay */
return adsr->amplitude;

19
adsr.h
View File

@ -40,23 +40,29 @@
#define ADSR_STATE_RELEASE_EXPIRE (0x5f)
#define ADSR_STATE_DONE (0xff)
/*!
* Hold this state until `adsr_continue` is called. Valid for
* `delay_time` and `sustain_time` only.
*/
#define ADSR_INFINITE UINT8_MAX
/*!
* ADSR Envelope Generator data. 20 bytes.
*/
struct adsr_env_gen_t {
/*! Time to next event, samples */
/*! Time to next event, samples. UINT32_MAX = infinite */
uint32_t next_event;
/*! Time step, samples */
uint16_t time_step;
/*! Time scale, samples per unit */
uint32_t time_scale;
/*! Delay period, time units */
/*! Delay period, time units. UINT8_MAX = infinite */
uint8_t delay_time;
/*! Attack period, time units */
uint8_t attack_time;
/*! Decay period, time units */
uint8_t decay_time;
/*! Sustain period, time units */
/*! Sustain period, time units. UINT8_MAX = infinite */
uint8_t sustain_time;
/*! Release period, time units */
uint8_t release_time;
@ -129,6 +135,13 @@ static inline uint8_t adsr_is_done(struct adsr_env_gen_t* const adsr) {
return (adsr->state == ADSR_STATE_DONE);
}
/*!
* Tell the ADSR to move onto the next state.
*/
static inline void adsr_continue(struct adsr_env_gen_t* const adsr) {
adsr->next_event = 0;
}
#endif
/*
* vim: set sw=8 ts=8 noet si tw=72