Skip to content

Instantly share code, notes, and snippets.

@madgarden
Last active February 16, 2022 15:12
Show Gist options
  • Save madgarden/78bcd6e2af91b89d181fe5e26edd30d9 to your computer and use it in GitHub Desktop.
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/
// 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