#include #include #define SOKOL_IMPL #define SOKOL_GLCORE #include "sokol_app.h" #include "sokol_audio.h" #include "sokol_log.h" #include "../config.h" #include "../envelope.c" // #include "../interp.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; #define CROSSFADE_RING_RANGE \ (STFU_CROSSFADE_BUFFER_FRAMES * STFU_AUDIO_CHANNEL_COUNT) static float crossfade_ring[CROSSFADE_RING_RANGE] = {0}; static size_t crossfade_ring_needle = 0; static unsigned int crossfade_frames_left = 0; static void sample(float buffer[static 2]) { phaser0 = stfu_pump_phaser(phaser0); phaser1 = stfu_pump_phaser(phaser1); phaser2 = stfu_pump_phaser(phaser2); envelope = stfu_pump_envelope(envelope); float left = envelope.v * sinf(phaser2.v + sinf(phaser0.v + sinf(phaser1.v))); float right = envelope.v * sinf(phaser2.v + sinf(phaser0.v + sinf(phaser1.v))); buffer[0] = left; buffer[1] = right; } static void stream(float *buffer, int num_frames, int num_channels) { /* Fade away */ for (size_t i = 0; crossfade_frames_left > 0; crossfade_frames_left--, i += 2) { crossfade_ring[(crossfade_ring_needle + i) % CROSSFADE_RING_RANGE] *= (float)crossfade_frames_left / STFU_CROSSFADE_BUFFER_FRAMES; crossfade_ring[(crossfade_ring_needle + i + 1) % CROSSFADE_RING_RANGE] *= (float)crossfade_frames_left / STFU_CROSSFADE_BUFFER_FRAMES; } /* Drive the ring buffer */ // todo: Use memcpy for left and right side of the ring. while (num_frames--) { buffer[0] = crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE]; buffer[1] = crossfade_ring[(crossfade_ring_needle + 1) % CROSSFADE_RING_RANGE]; sample(&crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE]); buffer = &buffer[2]; crossfade_ring_needle += 2; } } static void init(void) { saudio_setup(&(saudio_desc){ .sample_rate = STFU_AUDIO_FRAME_RATE, .num_channels = STFU_AUDIO_CHANNEL_COUNT, // .stream_cb = stream, }); // 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.02, .vol = 1.0}, [1] = {.dur = 0.5, .vol = 0.33}, [2] = {.dur = 1.0, .vol = 0}}, 3); } static void cleanup(void) { saudio_shutdown(); } static void event(const sapp_event *e) { switch (e->type) { case SAPP_EVENTTYPE_KEY_DOWN: { if (e->key_repeat) break; switch (e->key_code) { case SAPP_KEYCODE_Z: key_frequency = STFU_C4_FREQUENCY; break; case SAPP_KEYCODE_X: key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR; break; case SAPP_KEYCODE_C: key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR; break; case SAPP_KEYCODE_V: key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR; break; case SAPP_KEYCODE_B: key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR; break; case SAPP_KEYCODE_N: key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR; break; case SAPP_KEYCODE_M: key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR; break; case SAPP_KEYCODE_COMMA: key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR; break; case SAPP_KEYCODE_PERIOD: key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR; break; case SAPP_KEYCODE_SLASH: key_frequency = STFU_A4_FREQUENCY; break; default: break; } phaser0 = stfu_set_phaser_frequency(phaser0, key_frequency); phaser1 = stfu_set_phaser_frequency(phaser1, key_frequency * 0.25); phaser2 = stfu_set_phaser_frequency(phaser2, key_frequency * 2.0); envelope = stfu_trigger_envelope(envelope); crossfade_frames_left = STFU_CROSSFADE_BUFFER_FRAMES; break; } default: break; } } static void frame(void) { int frames = (int)(sapp_frame_duration() * (float)STFU_AUDIO_FRAME_RATE); int expected = saudio_expect(); frames = frames > expected ? expected : frames; // todo: Prevent drifting apart. static float buffer[STFU_AUDIO_FRAME_RATE * STFU_AUDIO_CHANNEL_COUNT]; while (frames > 0) { int now = frames > STFU_AUDIO_FRAME_RATE ? STFU_AUDIO_FRAME_RATE : frames; stream(buffer, now, STFU_AUDIO_CHANNEL_COUNT); saudio_push(buffer, now); frames -= now; } } sapp_desc sokol_main(int argc, char *argv[]) { return (sapp_desc){ .width = 640, .height = 480, .init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup, .event_cb = event, .logger.func = slog_func, .gl_major_version = 2, .gl_minor_version = 0, // .swap_interval = 0, }; }