#include #include #define SOKOL_IMPL #include "sokol_app.h" #include "sokol_audio.h" #include "sokol_gfx.h" #include "sokol_gl.h" #include "sokol_glue.h" #include "sokol_log.h" #define STFU_MAIN #include "../config.h" #include "../envelope.c" #include "../oscillators.c" #include "../phaser.c" #include "config.h" #include "../Instruments/Trojka/trojka.c" #include "Widgets/slider.c" static struct stfu_trojka synth = {0}; // todo: Separate to playback.c, generic for Player and Maker #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; // 1.0 is with A at 440 static float octave = 0.5; static struct stfu_slider feedback_slider = {.x = 16, .y = 16, .width = 120, .height = 16, .min = 0, .max = 1, .cur = 0.25}; 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]; synth = stfu_sample_trojka( synth, &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, .logger.func = slog_func, }); sg_setup(&(sg_desc){ .environment = sglue_environment(), .logger.func = slog_func, }); sgl_setup(&(sgl_desc_t){ .logger.func = slog_func, }); synth.freq_scales[0] = 1.0; synth.freq_scales[1] = 0.25; synth.freq_scales[2] = 2.0; synth.indices[0] = 2.0; synth.indices[1] = 10.0; synth.feedback_gain = 0.25; for (int i = 0; i < STFU_TROJKA_OP_COUNT; ++i) { synth.envelopes[i] = 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); synth.gains[i] = 1.0; } } static void cleanup(void) { saudio_shutdown(); } static void event(const sapp_event *e) { switch (e->type) { case SAPP_EVENTTYPE_MOUSE_DOWN: { if (e->mouse_button != SAPP_MOUSEBUTTON_LEFT) break; feedback_slider = stfu_press_slider(feedback_slider, e->mouse_x, e->mouse_y); synth.feedback_gain = feedback_slider.cur; break; } case SAPP_EVENTTYPE_KEY_DOWN: { if (e->key_repeat) break; bool key_was_pressed = false; switch (e->key_code) { case SAPP_KEYCODE_Z: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[0] * octave); break; case SAPP_KEYCODE_X: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[1] * octave); break; case SAPP_KEYCODE_C: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[2] * octave); break; case SAPP_KEYCODE_V: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[3] * octave); break; case SAPP_KEYCODE_B: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[4] * octave); break; case SAPP_KEYCODE_N: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[5] * octave); break; case SAPP_KEYCODE_M: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[6] * octave); break; case SAPP_KEYCODE_COMMA: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[7] * octave); break; case SAPP_KEYCODE_PERIOD: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[8] * octave); break; case SAPP_KEYCODE_SLASH: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[9] * octave); break; case SAPP_KEYCODE_A: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[0] * (octave * 2)); break; case SAPP_KEYCODE_S: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[1] * (octave * 2)); break; case SAPP_KEYCODE_D: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[2] * (octave * 2)); break; case SAPP_KEYCODE_F: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[3] * (octave * 2)); break; case SAPP_KEYCODE_G: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[4] * (octave * 2)); break; case SAPP_KEYCODE_H: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[5] * (octave * 2)); break; case SAPP_KEYCODE_J: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[6] * (octave * 2)); break; case SAPP_KEYCODE_K: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[7] * (octave * 2)); break; case SAPP_KEYCODE_L: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[8] * (octave * 2)); break; case SAPP_KEYCODE_SEMICOLON: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[9] * (octave * 2)); break; case SAPP_KEYCODE_APOSTROPHE: key_was_pressed = true; synth = stfu_press_trojka(synth, STFU_SCALE[10] * (octave * 2)); break; default: break; } if (key_was_pressed) { 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; } sg_pass pass = {.swapchain = sglue_swapchain()}; sg_begin_pass(&pass); sgl_load_default_pipeline(); sgl_load_identity(); sgl_ortho(0, STFU_MAKER_WINDOW_WIDTH, STFU_MAKER_WINDOW_HEIGHT, 0, -1, 1); stfu_draw_slider(feedback_slider); // sgl_pop_matrix(); sgl_draw(); sg_end_pass(); sg_commit(); } sapp_desc sokol_main(int argc, char *argv[]) { return (sapp_desc){ .width = STFU_MAKER_WINDOW_WIDTH, .height = STFU_MAKER_WINDOW_HEIGHT, .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, .window_title = "stfu-maker", }; }