windows build, separation of synth code, 3-op now, modulator feedback
This commit is contained in:
parent
ce5cfdd95b
commit
561dece028
2
Source/Instruments/Trojka/readme.md
Normal file
2
Source/Instruments/Trojka/readme.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Trojka
|
||||||
|
3-op minimalist FM synth to the mega!
|
56
Source/Instruments/Trojka/trojka.c
Normal file
56
Source/Instruments/Trojka/trojka.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_TROJKA)
|
||||||
|
#define STFU_TROJKA
|
||||||
|
|
||||||
|
#include "../../phaser.c"
|
||||||
|
#include "../../envelope.c"
|
||||||
|
#include "../../config.h"
|
||||||
|
|
||||||
|
#define STFU_TROJKA_OP_COUNT 3
|
||||||
|
|
||||||
|
/* https://multimed.org/student/eim/en/04-FM.pdf */
|
||||||
|
|
||||||
|
// todo: A few more algorithm configurations.
|
||||||
|
// todo: Other oscillators.
|
||||||
|
|
||||||
|
struct stfu_trojka {
|
||||||
|
struct stfu_phaser phasers[STFU_TROJKA_OP_COUNT];
|
||||||
|
struct stfu_envelope envelopes[STFU_TROJKA_OP_COUNT];
|
||||||
|
float gains[STFU_TROJKA_OP_COUNT];
|
||||||
|
float freq_scales[STFU_TROJKA_OP_COUNT];
|
||||||
|
float key_frequency;
|
||||||
|
float feedback_gain; // todo: Separate gain for channels?
|
||||||
|
float left_feedback, right_feedback;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stfu_trojka stfu_sample_trojka(struct stfu_trojka synth, float buffer[static 2]) {
|
||||||
|
for (int i = 0; i < STFU_TROJKA_OP_COUNT; ++i) {
|
||||||
|
synth.phasers[i] = stfu_pump_phaser(synth.phasers[i]);
|
||||||
|
synth.envelopes[i] = stfu_pump_envelope(synth.envelopes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
float left_modulator_stack = synth.envelopes[1].v * sinf(synth.phasers[1].v + synth.envelopes[0].v * sinf(synth.phasers[0].v)) + synth.left_feedback * synth.feedback_gain;
|
||||||
|
float right_modulator_stack = synth.envelopes[1].v * sinf(synth.phasers[1].v + synth.envelopes[0].v * sinf(synth.phasers[0].v)) + synth.right_feedback * synth.feedback_gain;
|
||||||
|
|
||||||
|
synth.left_feedback = left_modulator_stack;
|
||||||
|
synth.right_feedback = right_modulator_stack;
|
||||||
|
|
||||||
|
float left = synth.envelopes[2].v * sinf(synth.phasers[2].v + left_modulator_stack);
|
||||||
|
float right =
|
||||||
|
synth.envelopes[2].v * sinf(synth.phasers[2].v + right_modulator_stack);
|
||||||
|
|
||||||
|
buffer[0] = left;
|
||||||
|
buffer[1] = right;
|
||||||
|
|
||||||
|
return synth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct stfu_trojka stfu_press_trojka(struct stfu_trojka synth, float key_frequency) {
|
||||||
|
for (int i = 0; i < STFU_TROJKA_OP_COUNT; ++i) {
|
||||||
|
synth.phasers[i] = stfu_set_phaser_frequency(synth.phasers[i], key_frequency * synth.freq_scales[i]);
|
||||||
|
synth.envelopes[i] = stfu_trigger_envelope(synth.envelopes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return synth;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
BIN
Source/Maker/Build/stfu-maker.exe
Normal file
BIN
Source/Maker/Build/stfu-maker.exe
Normal file
Binary file not shown.
1
Source/Maker/Build/windows-clang.bat
Normal file
1
Source/Maker/Build/windows-clang.bat
Normal file
@ -0,0 +1 @@
|
|||||||
|
clang ../main.c -lkernel32 -luser32 -lshell32 -ld3d11 -ldxgi -lgdi32 -lole32 -DSOKOL_D3D11 -o stfu-maker.exe -O3
|
@ -3,45 +3,27 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define SOKOL_IMPL
|
#define SOKOL_IMPL
|
||||||
#define SOKOL_GLCORE
|
|
||||||
#include "sokol_app.h"
|
#include "sokol_app.h"
|
||||||
#include "sokol_audio.h"
|
#include "sokol_audio.h"
|
||||||
#include "sokol_log.h"
|
#include "sokol_log.h"
|
||||||
|
|
||||||
|
#define STFU_MAIN
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include "../envelope.c"
|
#include "../envelope.c"
|
||||||
// #include "../interp.c"
|
|
||||||
#include "../oscillators.c"
|
#include "../oscillators.c"
|
||||||
#include "../phaser.c"
|
#include "../phaser.c"
|
||||||
|
|
||||||
static struct stfu_sinewave sine0;
|
#include "../Instruments/Trojka/trojka.c"
|
||||||
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;
|
static struct stfu_trojka synth = {0};
|
||||||
|
|
||||||
|
// todo: Separate to playback.c, generic for Player and Maker
|
||||||
#define CROSSFADE_RING_RANGE \
|
#define CROSSFADE_RING_RANGE \
|
||||||
(STFU_CROSSFADE_BUFFER_FRAMES * STFU_AUDIO_CHANNEL_COUNT)
|
(STFU_CROSSFADE_BUFFER_FRAMES * STFU_AUDIO_CHANNEL_COUNT)
|
||||||
static float crossfade_ring[CROSSFADE_RING_RANGE] = {0};
|
static float crossfade_ring[CROSSFADE_RING_RANGE] = {0};
|
||||||
static size_t crossfade_ring_needle = 0;
|
static size_t crossfade_ring_needle = 0;
|
||||||
static unsigned int crossfade_frames_left = 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) {
|
static void stream(float *buffer, int num_frames, int num_channels) {
|
||||||
/* Fade away */
|
/* Fade away */
|
||||||
for (size_t i = 0; crossfade_frames_left > 0;
|
for (size_t i = 0; crossfade_frames_left > 0;
|
||||||
@ -58,7 +40,7 @@ static void stream(float *buffer, int num_frames, int num_channels) {
|
|||||||
buffer[0] = crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE];
|
buffer[0] = crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE];
|
||||||
buffer[1] =
|
buffer[1] =
|
||||||
crossfade_ring[(crossfade_ring_needle + 1) % CROSSFADE_RING_RANGE];
|
crossfade_ring[(crossfade_ring_needle + 1) % CROSSFADE_RING_RANGE];
|
||||||
sample(&crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE]);
|
synth = stfu_sample_trojka(synth, &crossfade_ring[crossfade_ring_needle % CROSSFADE_RING_RANGE]);
|
||||||
buffer = &buffer[2];
|
buffer = &buffer[2];
|
||||||
crossfade_ring_needle += 2;
|
crossfade_ring_needle += 2;
|
||||||
}
|
}
|
||||||
@ -71,15 +53,19 @@ static void init(void) {
|
|||||||
// .stream_cb = stream,
|
// .stream_cb = stream,
|
||||||
});
|
});
|
||||||
|
|
||||||
// sine0 = stfu_init_sinewave(STFU_A4_FREQUENCY / 2.0f, 0.0f, 0.8f);
|
synth.phasers[0] = stfu_init_phaser(220);
|
||||||
phaser0 = stfu_init_phaser(key_frequency);
|
synth.phasers[1] = stfu_init_phaser(220 * 0.25);
|
||||||
phaser1 = stfu_init_phaser(key_frequency * 0.25);
|
synth.phasers[2] = stfu_init_phaser(220 * 2.0);
|
||||||
phaser2 = stfu_init_phaser(key_frequency * 2.0);
|
synth.feedback_gain = 0.25;
|
||||||
envelope = stfu_init_envelope(
|
for (int i = 0; i < STFU_TROJKA_OP_COUNT; ++i) {
|
||||||
(struct stfu_envelope_point[]){[0] = {.dur = 0.02, .vol = 1.0},
|
synth.envelopes[i] = stfu_init_envelope(
|
||||||
[1] = {.dur = 0.5, .vol = 0.33},
|
(struct stfu_envelope_point[]){[0] = {.dur = 0.02, .vol = 1.0},
|
||||||
[2] = {.dur = 1.0, .vol = 0}},
|
[1] = {.dur = 0.5, .vol = 0.33},
|
||||||
3);
|
[2] = {.dur = 1.0, .vol = 0}},
|
||||||
|
3);
|
||||||
|
synth.gains[i] = 1.0;
|
||||||
|
synth.freq_scales[i] = 1.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup(void) { saudio_shutdown(); }
|
static void cleanup(void) { saudio_shutdown(); }
|
||||||
@ -92,53 +78,38 @@ static void event(const sapp_event *e) {
|
|||||||
|
|
||||||
switch (e->key_code) {
|
switch (e->key_code) {
|
||||||
case SAPP_KEYCODE_Z:
|
case SAPP_KEYCODE_Z:
|
||||||
key_frequency = STFU_C4_FREQUENCY;
|
synth = stfu_press_trojka(synth, STFU_SCALE[0]);
|
||||||
break;
|
|
||||||
case SAPP_KEYCODE_X:
|
case SAPP_KEYCODE_X:
|
||||||
key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR;
|
synth = stfu_press_trojka(synth, STFU_SCALE[1]);
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_C:
|
case SAPP_KEYCODE_C:
|
||||||
key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[2]);
|
||||||
STFU_NOTE_UPSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_V:
|
case SAPP_KEYCODE_V:
|
||||||
key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[3]);
|
||||||
STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_B:
|
case SAPP_KEYCODE_B:
|
||||||
key_frequency = STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[4]);
|
||||||
STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR *
|
|
||||||
STFU_NOTE_UPSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_N:
|
case SAPP_KEYCODE_N:
|
||||||
key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[5]);
|
||||||
STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR *
|
|
||||||
STFU_NOTE_DOWNSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_M:
|
case SAPP_KEYCODE_M:
|
||||||
key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[6]);
|
||||||
STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_COMMA:
|
case SAPP_KEYCODE_COMMA:
|
||||||
key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
synth = stfu_press_trojka(synth, STFU_SCALE[7]);
|
||||||
STFU_NOTE_DOWNSCALE_FACTOR;
|
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_PERIOD:
|
case SAPP_KEYCODE_PERIOD:
|
||||||
key_frequency = STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR;
|
synth = stfu_press_trojka(synth, STFU_SCALE[8]);
|
||||||
break;
|
break;
|
||||||
case SAPP_KEYCODE_SLASH:
|
case SAPP_KEYCODE_SLASH:
|
||||||
key_frequency = STFU_A4_FREQUENCY;
|
synth = stfu_press_trojka(synth, STFU_SCALE[9]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -172,5 +143,6 @@ sapp_desc sokol_main(int argc, char *argv[]) {
|
|||||||
.gl_major_version = 2,
|
.gl_major_version = 2,
|
||||||
.gl_minor_version = 0,
|
.gl_minor_version = 0,
|
||||||
// .swap_interval = 0,
|
// .swap_interval = 0,
|
||||||
|
.window_title = "stfu-maker",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#if !defined(STFU_CONFIG)
|
||||||
|
#define STFU_CONFIG
|
||||||
|
|
||||||
/* Determines output framerate, in times per second. */
|
/* Determines output framerate, in times per second. */
|
||||||
#define STFU_AUDIO_FRAME_RATE 44100
|
#define STFU_AUDIO_FRAME_RATE 44100
|
||||||
@ -10,6 +12,27 @@
|
|||||||
|
|
||||||
/* Introduces delayed buffering for purposes of crossfading channels when notes
|
/* Introduces delayed buffering for purposes of crossfading channels when notes
|
||||||
* are retriggered, needed for filtering clicks */
|
* are retriggered, needed for filtering clicks */
|
||||||
#define STFU_CROSSFADE_BUFFER_FRAMES 64
|
#define STFU_CROSSFADE_BUFFER_FRAMES 32
|
||||||
|
|
||||||
// todo: Array of precomputed note frequencies?
|
static const float STFU_SCALE[12] = {
|
||||||
|
STFU_C4_FREQUENCY,
|
||||||
|
STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR,
|
||||||
|
STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
||||||
|
STFU_NOTE_UPSCALE_FACTOR,
|
||||||
|
STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
||||||
|
STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR,
|
||||||
|
STFU_C4_FREQUENCY * STFU_NOTE_UPSCALE_FACTOR *
|
||||||
|
STFU_NOTE_UPSCALE_FACTOR * STFU_NOTE_UPSCALE_FACTOR *
|
||||||
|
STFU_NOTE_UPSCALE_FACTOR,
|
||||||
|
STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
||||||
|
STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR *
|
||||||
|
STFU_NOTE_DOWNSCALE_FACTOR,
|
||||||
|
STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
||||||
|
STFU_NOTE_DOWNSCALE_FACTOR * STFU_NOTE_DOWNSCALE_FACTOR,
|
||||||
|
STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR *
|
||||||
|
STFU_NOTE_DOWNSCALE_FACTOR,
|
||||||
|
STFU_A4_FREQUENCY * STFU_NOTE_DOWNSCALE_FACTOR,
|
||||||
|
STFU_A4_FREQUENCY,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_ENVELOPE)
|
||||||
|
#define STFU_ENVELOPE
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -29,12 +32,13 @@ struct stfu_envelope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct stfu_envelope stfu_pump_envelope(struct stfu_envelope envelope) {
|
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);
|
float d = stfu_get_envelope_duration(envelope);
|
||||||
if (envelope.p >= d)
|
if (envelope.p >= d)
|
||||||
/* Needs retriggering after that point */
|
/* Needs retriggering after that point */
|
||||||
return envelope;
|
return envelope;
|
||||||
|
|
||||||
|
envelope.p += 1.0f / STFU_AUDIO_FRAME_RATE;
|
||||||
|
|
||||||
float p = 0.0f;
|
float p = 0.0f;
|
||||||
for (uint8_t i = 0;; ++i) {
|
for (uint8_t i = 0;; ++i) {
|
||||||
float ends = p + envelope.pnts[i].dur;
|
float ends = p + envelope.pnts[i].dur;
|
||||||
@ -63,3 +67,5 @@ struct stfu_envelope stfu_trigger_envelope(struct stfu_envelope envelope) {
|
|||||||
envelope.v = 0.0f;
|
envelope.v = 0.0f;
|
||||||
return envelope;
|
return envelope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_INTERP)
|
||||||
|
#define STFU_INTERP
|
||||||
|
|
||||||
float stfu_linear_interpolate(float x0, float y0, float x1, float y1, float n) {
|
float stfu_linear_interpolate(float x0, float y0, float x1, float y1, float n) {
|
||||||
return (y0 * (x1 - n) + y1 * (n - x0)) / (x1 - x0);
|
return (y0 * (x1 - n) + y1 * (n - x0)) / (x1 - x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_OSCILLATORS)
|
||||||
|
#define STFU_OSCILLATORS
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/* https://multimed.org/student/eim/en/04-FM.pdf */
|
|
||||||
|
|
||||||
/* Intended to be executed offline with values then embedded in the binary.
|
/* Intended to be executed offline with values then embedded in the binary.
|
||||||
* By having usage of glibc sin and cos functions strictly offline it's easier
|
* By having usage of glibc sin and cos functions strictly offline it's easier
|
||||||
* to have it freestanding
|
* to have it freestanding
|
||||||
@ -76,3 +77,5 @@ struct stfu_sawtooth stfu_pump_sawtooth(struct stfu_sawtooth wave) {
|
|||||||
wave.v -= wave.a * 2.f;
|
wave.v -= wave.a * 2.f;
|
||||||
return wave;
|
return wave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_PHASER)
|
||||||
|
#define STFU_PHASER
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -29,3 +32,5 @@ struct stfu_phaser stfu_set_phaser_frequency(struct stfu_phaser phaser,
|
|||||||
phaser.a = 2 * ((float)M_PI / (float)STFU_AUDIO_FRAME_RATE) * frequency;
|
phaser.a = 2 * ((float)M_PI / (float)STFU_AUDIO_FRAME_RATE) * frequency;
|
||||||
return phaser;
|
return phaser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#if defined(STFU_MAIN) && !defined(STFU_TIMER)
|
||||||
|
#define STFU_TIMER
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -18,3 +21,5 @@ struct stfu_timer stfu_pump_timer(struct stfu_timer wave) {
|
|||||||
wave.v -= 1000;
|
wave.v -= 1000;
|
||||||
return wave;
|
return wave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user