Last active
February 16, 2022 15:12
-
-
Save madgarden/78bcd6e2af91b89d181fe5e26edd30d9 to your computer and use it in GitHub Desktop.
Williams Robotron sound synth, adapted to C from https://www.lomont.org/software/misc/robotron/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Williams Robotron sound synth, adapted to C from https://www.lomont.org/software/misc/robotron/ | |
// LICENSE | |
// | |
// This software is dual-licensed to the public domain and under the following | |
// license: you are granted a perpetual, irrevocable license to copy, modify, | |
// publish, and distribute this file as you see fit. | |
#define CR_START(n) { int *crptr = (n); switch(*crptr) { case 0: | |
#define CR_YIELD_VOID do { *crptr =__LINE__; return; case __LINE__:;}while(0); | |
#define CR_YIELD(x) do { *state =__LINE__; return x; case __LINE__:; }while(0); | |
#define CR_END }} | |
typedef struct | |
{ | |
float vol; | |
float duration; | |
}WILLIAMS_CONFIG; | |
typedef struct | |
{ | |
// Mysterious parameters | |
union | |
{ | |
struct | |
{ | |
unsigned char b1, b2, b3, b4, b5, b6, b7; | |
}; | |
unsigned char b[7]; | |
}; | |
uint16_t u1; | |
// Internal storage | |
unsigned char c1, c2, t; | |
uint16_t t1, t2; | |
// Internal counter | |
uint16_t count; | |
int d; | |
// Current sound level | |
unsigned char sound; | |
// For waveform centering | |
int16_t val16; | |
int16_t lastval16; | |
// Running? | |
int run; | |
int frames; | |
}WILLIAMS_STATE; | |
typedef struct | |
{ | |
WILLIAMS_STATE state; | |
WILLIAMS_CONFIG config; | |
}WILLIAMS_SYNTH; | |
static WILLIAMS_SYNTH set_synth; | |
static WILLIAMS_SYNTH run_synth; | |
static int synthset = 0; | |
static void synth_callback(int16_t *buffer, int stereo_sample_count, | |
int sample_rate) | |
{ | |
int16_t *ptr = buffer; | |
int sample_count = stereo_sample_count; | |
int16_t *wave = buffer; | |
int16_t tmp; | |
static int runstate = 0; | |
static int div_count = 0; | |
int div = 894750 / sample_rate; | |
int max_time = run_synth.config.duration * sample_rate; | |
#define S run_synth.state | |
#define C run_synth.config | |
#define W run_synth | |
if(S.run && max_time && S.frames > max_time) | |
{ | |
memset(&S, 0, sizeof(S)); | |
runstate = 0; | |
return; | |
} | |
CR_START(&runstate); | |
// Copy the current sound value this many times into the output | |
// - downsamples to sample_rate | |
// - centers output waveform | |
// - will stop sound if WILLIAMS_CONFIG duration reached | |
#define DUP(n) {S.d = n; while(S.d-- > 0){ \ | |
div_count++; if(div_count >= div){ div_count = 0; \ | |
S.frames++; tmp = S.sound << 7; \ | |
S.val16 = 0.999f * S.val16 + tmp - S.lastval16; \ | |
S.lastval16 = tmp; tmp = S.val16 * C.vol; \ | |
*wave += tmp; wave++; *wave += tmp; wave++; \ | |
sample_count--; if(sample_count < 1) CR_YIELD_VOID; \ | |
}}}; | |
if(S.run) | |
{ | |
DUP(8); | |
S.sound = S.b7; | |
do | |
{ | |
DUP(14); | |
S.c1 = S.b1; | |
S.c2 = S.b2; | |
do | |
{ | |
DUP(4); | |
S.count = S.u1; | |
while(!synthset) | |
{ | |
DUP(9); | |
S.sound = (unsigned char) ~S.sound; | |
S.t1 = (S.c1 != 0 ? S.c1 : 256); | |
DUP(MMIN(S.count, S.t1) * 14 - 6); | |
if (S.count <= S.t1) break; | |
DUP(12); | |
S.count -= S.t1; | |
S.sound = (unsigned char) ~S.sound; | |
S.t2 = (S.c2 != 0 ? S.c2 : 256); | |
DUP(MMIN(S.count, S.t2) * 14 - 3); | |
if (S.count <= S.t2) break; | |
DUP(10); | |
S.count -= S.t2; | |
} | |
DUP(15); | |
if(S.sound < 128) | |
{ | |
DUP(2); | |
S.sound = (unsigned char)~S.sound; | |
} | |
DUP(27); | |
S.c1 += S.b3; | |
S.c2 += S.b4; | |
if(synthset) break; | |
} while(S.c2 != S.b5); | |
DUP(7); | |
if(S.b6 == 0) break; | |
DUP(11); | |
S.b1 += S.b6; | |
if(synthset) break; | |
} while(S.b1 != 0); | |
} | |
CR_END; | |
if(S.run) | |
{ | |
//~ PRINTF("RESET\n"); | |
runstate = 0; | |
memset(&S, 0, sizeof(S)); | |
} | |
if(synthset) | |
{ | |
W = set_synth; | |
synthset = 0; | |
runstate = 0; | |
} | |
#undef S | |
#undef C | |
#undef W | |
} | |
static void set_parameters( | |
unsigned char a1, | |
unsigned char a2, | |
unsigned char a3, | |
unsigned char a4, | |
unsigned char a5, | |
unsigned char a6, | |
unsigned char a7, | |
uint16_t v1) | |
{ | |
int go = 0; | |
#define SET(n, v) set_synth.state.n = v; go |= v; | |
memset(&set_synth.state, 0, sizeof(set_synth.state)); | |
SET(b1, a1); | |
SET(b2, a2); | |
SET(b3, a3); | |
SET(b4, a4); | |
SET(b5, a5); | |
SET(b6, a6); | |
SET(b7, a7); | |
SET(u1, v1); | |
set_synth.state.run = go != 0; | |
PRINTF("PARAMS: %d %d %d %d %d %d %d %d\n", | |
a1, a2, a3, a4, a5, a6, a7, v1); | |
synthset = 1; | |
} | |
static void set_config(float vol, float duration) | |
{ | |
set_synth.config.vol = CLAMP(vol, 0, 1); | |
set_synth.config.duration = duration; | |
} | |
static void reset(void) | |
{ | |
memset(&set_synth, 0, sizeof(set_synth)); | |
set_parameters(0, 0, 0, 0, 0, 0, 0, 0); | |
set_config(1, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment