252 lines
7.4 KiB
C
252 lines
7.4 KiB
C
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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 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_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((struct stfu_slider){.x = 16,
|
|
.y = 16,
|
|
.width = 120,
|
|
.height = 16,
|
|
.min = 10,
|
|
.max = 100,
|
|
.cur = 55});
|
|
// 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",
|
|
};
|
|
}
|