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:
parent
f2fac87ba3
commit
d9bfcb7991
23
adsr.c
23
adsr.c
@ -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
19
adsr.h
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user