envelope.c: point-based linear interpolating envelope generator
This commit is contained in:
parent
9923a7270e
commit
b35498e230
@ -8,12 +8,15 @@
|
||||
#include "sokol_log.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include "../envelope.c"
|
||||
#include "../oscillators.c"
|
||||
#include "../phaser.c"
|
||||
|
||||
static struct stfu_sinewave sine0;
|
||||
static struct stfu_phaser phaser0;
|
||||
static struct stfu_phaser phaser1;
|
||||
static struct stfu_phaser phaser2;
|
||||
static struct stfu_envelope envelope;
|
||||
|
||||
static float key_frequency = STFU_C4_FREQUENCY;
|
||||
|
||||
@ -22,8 +25,12 @@ static void stream(float *buffer, int num_frames, int num_channels) {
|
||||
// sine0 = stfu_pump_sinewave(sine0); // Carrier
|
||||
phaser0 = stfu_pump_phaser(phaser0);
|
||||
phaser1 = stfu_pump_phaser(phaser1);
|
||||
buffer[2 * i + 0] = sinf(phaser0.v + sinf(phaser1.v));
|
||||
buffer[2 * i + 1] = sinf(phaser0.v + sinf(phaser1.v));
|
||||
phaser2 = stfu_pump_phaser(phaser2);
|
||||
envelope = stfu_pump_envelope(envelope);
|
||||
buffer[2 * i + 0] =
|
||||
envelope.v * sinf(phaser2.v + sinf(phaser0.v + sinf(phaser1.v)));
|
||||
buffer[2 * i + 1] =
|
||||
envelope.v * sinf(phaser2.v + sinf(phaser0.v + sinf(phaser1.v)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +44,11 @@ static void init(void) {
|
||||
// sine0 = stfu_init_sinewave(STFU_A4_FREQUENCY / 2.0f, 0.0f, 0.8f);
|
||||
phaser0 = stfu_init_phaser(key_frequency);
|
||||
phaser1 = stfu_init_phaser(key_frequency * 0.25);
|
||||
phaser2 = stfu_init_phaser(key_frequency * 2.0);
|
||||
envelope = stfu_init_envelope(
|
||||
(struct stfu_envelope_point[]){
|
||||
[0] = {.dur = 0.5, .vol = 0.33}, [1] = {.dur = 1.0, .vol = 0}},
|
||||
2);
|
||||
}
|
||||
|
||||
static void cleanup(void) { saudio_shutdown(); }
|
||||
@ -90,8 +102,10 @@ static void event(const sapp_event *e) {
|
||||
break;
|
||||
}
|
||||
|
||||
phaser0 = stfu_set_phaser_frequency(phaser0, key_frequency);
|
||||
phaser1 = stfu_set_phaser_frequency(phaser1, key_frequency * 0.25);
|
||||
phaser0 = stfu_set_phaser_frequency(phaser0, key_frequency * 0.25);
|
||||
phaser1 = stfu_set_phaser_frequency(phaser1, key_frequency * 0.125);
|
||||
phaser2 = stfu_set_phaser_frequency(phaser2, key_frequency);
|
||||
envelope = stfu_trigger_envelope(envelope);
|
||||
|
||||
break;
|
||||
}
|
||||
|
65
Source/envelope.c
Normal file
65
Source/envelope.c
Normal file
@ -0,0 +1,65 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "interp.c"
|
||||
|
||||
struct stfu_envelope stfu_pump_envelope(struct stfu_envelope envelope);
|
||||
float stfu_get_envelope_duration(struct stfu_envelope envelope);
|
||||
struct stfu_envelope stfu_trigger_envelope(struct stfu_envelope envelope);
|
||||
|
||||
/* Basic linear envelope generator with up to 16 points max.
|
||||
* First virtual point is always max volume (1.0f).
|
||||
*
|
||||
* todo: Sustain?
|
||||
*/
|
||||
struct stfu_envelope {
|
||||
/* Initial volume is assumed from previous point */
|
||||
struct stfu_envelope_point {
|
||||
float dur, vol;
|
||||
} pnts[16];
|
||||
uint8_t n_pnts;
|
||||
float p, v;
|
||||
} stfu_init_envelope(struct stfu_envelope_point *points, uint8_t n_points) {
|
||||
struct stfu_envelope r = {0};
|
||||
memcpy(&r.pnts, points, sizeof(struct stfu_envelope_point) * n_points);
|
||||
r.n_pnts = n_points;
|
||||
r = stfu_trigger_envelope(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
struct stfu_envelope stfu_pump_envelope(struct stfu_envelope envelope) {
|
||||
envelope.p += 1.0f / STFU_AUDIO_FRAME_RATE;
|
||||
float d = stfu_get_envelope_duration(envelope);
|
||||
if (envelope.p >= d)
|
||||
/* Needs retriggering after that point */
|
||||
return envelope;
|
||||
|
||||
float p = 0.0f;
|
||||
for (uint8_t i = 0;; ++i) {
|
||||
float ends = p + envelope.pnts[i].dur;
|
||||
if (envelope.p < ends) {
|
||||
float pv = i == 0 ? 1.0f : envelope.pnts[i - 1].vol;
|
||||
envelope.v = stfu_linear_interpolate(p, pv, ends, envelope.pnts[i].vol,
|
||||
envelope.p);
|
||||
break;
|
||||
}
|
||||
p += envelope.pnts[i].dur;
|
||||
}
|
||||
|
||||
return envelope;
|
||||
}
|
||||
|
||||
float stfu_get_envelope_duration(struct stfu_envelope envelope) {
|
||||
float r = 0.0f;
|
||||
for (uint8_t i = 0; i < envelope.n_pnts; ++i) {
|
||||
r += envelope.pnts[i].dur;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
struct stfu_envelope stfu_trigger_envelope(struct stfu_envelope envelope) {
|
||||
envelope.p = 0.0f;
|
||||
envelope.v = 1.0f;
|
||||
return envelope;
|
||||
}
|
4
Source/interp.c
Normal file
4
Source/interp.c
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
float stfu_linear_interpolate(float x0, float y0, float x1, float y1, float n) {
|
||||
return (y0 * (x1 - n) + y1 * (n - x0)) / (x1 - x0);
|
||||
}
|
@ -7,4 +7,4 @@ Grassroot music format specifically designed for game soundtracks with accent on
|
||||
- Live and proceduraly editable.
|
||||
|
||||
## Maker
|
||||
Crossplatform Sokol headers based live editor.
|
||||
Crossplatform Sokol headers based live editor. Interface is touch first, allowing for touch screen and mouse editing.
|
||||
|
Loading…
Reference in New Issue
Block a user