Skip to content

Instantly share code, notes, and snippets.

@madgarden
Created November 21, 2019 16:05
Show Gist options
  • Save madgarden/76d8a8c19a95663cb7e1def43c70ae1a to your computer and use it in GitHub Desktop.
Save madgarden/76d8a8c19a95663cb7e1def43c70ae1a to your computer and use it in GitHub Desktop.
Arecibo Man bytebeat synthesizer
#include <stdint.h>
#include <memory.h>
#include "bytebeat_fx.h"
#include "mad.h"
static int init_ = 0;
static unsigned int glitch_t = 0;
static float glitch_rate = 8000;
static float glitch_volume = 1;
static int16_t val16 = 0;
static int16_t lastval16 = 0;
static int16_t otm = 0;
static BYTEBEAT bytebeats[NUM_BYTEBEATS];
static void synth_callback(int16_t *buffer, int stereo_sample_count, int sample_rate)
{
int16_t *wave = buffer;
int i;
uint32_t old_t;
uint32_t t_freq = 0;
uint32_t divstep = (glitch_rate / sample_rate) * 1000000000;
int32_t sum = 0;
int j;
for(i = 0; i < stereo_sample_count; i++)
{
t_freq += divstep;
old_t = glitch_t;
// Skip some t if necessary based on the rate
while(t_freq >= 1000000000)
{
t_freq -= 1000000000;
glitch_t++;
}
if(glitch_t > old_t)
{
int j;
char sound = 0;
unsigned int t = glitch_t;
sum = 0;
for(j = 0; j < NUM_BYTEBEATS; j++)
{
BYTEBEAT *bb = bytebeats + j;
BYTEBEAT_FUNC func = bb->func;
// Store the function pointer just in-case it becomes
// NULL in another thread. Yes... I know.
if(!func) continue;
if(bb->duration && bb->t > bb->duration)
{
bb->func = NULL;
continue;
}
bb->t++;
sound = func(bb->t, bb->userdata);
sum += (sound << 6) * bb->volume * glitch_volume;
}
// Clamp
sum = sum > 32767 ? 32767 : sum;
sum = sum < -32768 ? -32768 : sum;
// Center the waveform
otm = 0.999f * otm + sum - lastval16;
lastval16 = sum;
val16 = otm;
}
// Mono output in stereo, could do panning here if you wanted
*wave += val16;
wave++;
*wave += val16;
wave++;
}
}
void bytebeat_fx_set_volume(float volume)
{
glitch_volume = volume;
if(glitch_volume < 0) glitch_volume = 0;
if(glitch_volume > 1) glitch_volume = 1;
}
static void init_sys(void)
{
memset(bytebeats, 0, sizeof(bytebeats));
glitch_t = 0;
glitch_rate = 8000;
glitch_volume = 1;
val16 = 0;
lastval16 = 0;
otm = 0;
init_ = 1;
// TODO: This is my toolkit's audio streaming setup, GET YOUR OWN DAMN AUDIO STREEAM
mad_set_glitch_callback(synth_callback);
}
void bytebeat_fx_stop(void)
{
mad_set_glitch_callback(NULL);
}
void bytebeat_fx_set_rate(unsigned int rate)
{
glitch_rate = rate;
if(!glitch_rate) glitch_rate = 8000;
}
void bytebeat_fx_start_ex(unsigned int rate, float volume)
{
bytebeat_fx_stop();
init_sys();
glitch_rate = rate;
if(!glitch_rate) glitch_rate = 8000;
glitch_volume = volume;
if(volume < 0) volume = 0;
if(volume > 1) volume = 1;
}
void bytebeat_fx_start(void)
{
bytebeat_fx_start_ex(0, 0.75);
}
int bytebeat_fx_find_slot(void)
{
int i;
for(i = 0; i < NUM_BYTEBEATS; i++)
{
if(!bytebeats[i].func)
{
return i;
}
}
return BBFX_NO_SLOTS_FREE;
}
BYTEBEAT *bytebeat_fx_get(int slot)
{
if(slot < 0 || slot >= NUM_BYTEBEATS) return NULL;
return bytebeats + slot;
}
BYTEBEAT_FUNC bytebeat_fx_get_func(int slot)
{
BYTEBEAT *b = bytebeat_fx_get(slot);
if(!b) return NULL;
return b->func;
}
int bytebeat_fx_is_free(int slot)
{
BYTEBEAT *fx = bytebeat_fx_get(slot);
if(!fx) return 0;
return fx->func == 0;
}
BYTEBEAT *bytebeat_fx_ex(int slot, float volume,
unsigned int duration, BYTEBEAT_FUNC func, void *data)
{
BYTEBEAT *fx;
static BYTEBEAT dummy;
if(!init_) init_sys();
if(slot < 0) slot = bytebeat_fx_find_slot();
slot &= (NUM_BYTEBEATS - 1);
fx = &bytebeats[slot];
if(fx->func)
{
if(fx->priority)
{
if(func != fx->func)
{
return &dummy;
}
}
}
fx->func = NULL;
fx->duration = duration * (glitch_rate / 1000);
fx->t = 0;
fx->volume = volume > 0 ? (volume < 1 ? volume : 1) : 0;
fx->userdata = data;
fx->func = func;
mad_set_glitch_callback(synth_callback);
return bytebeats + slot;
}
int bytebeat_fx(int slot, float volume, unsigned int duration,
BYTEBEAT_FUNC func)
{
return bytebeat_fx_ex(slot, volume, duration, func, NULL);
}
void bytebeat_fx_clear(int slot)
{
if(!init_) init_sys();
if(slot < 0) return;
slot &= (NUM_BYTEBEATS - 1);
bytebeats[slot].func = NULL;
memset(bytebeats + slot, 0, sizeof(BYTEBEAT));
}
/*
// Paradroid-like example sound
static unsigned char warble_fx(unsigned int t, void *data)
{
return (t >> 7) * t * ((t >> 10) + 10) & 128;
}
*/
#ifndef __BYTEBEAT_FX_H__
#define __BYTEBEAT_FX_H__
#define BBFX_ERROR -1
#define BBFX_NO_SLOTS_FREE -2
// Must be a power of 2
#define NUM_BYTEBEATS 8
typedef unsigned char (*BYTEBEAT_FUNC)(unsigned int t, void *data);
typedef struct
{
BYTEBEAT_FUNC func;
unsigned int duration;
unsigned int t;
unsigned char out;
char priority;
float volume;
void *userdata;
int v1, v2, v3, v4;
}BYTEBEAT;
void bytebeat_fx_start(void);
void bytebeat_fx_start_ex(unsigned int rate, float volume);
void bytebeat_fx_set_rate(unsigned int rate);
void bytebeat_fx_set_volume(float vol);
void bytebeat_fx_stop(void);
int bytebeat_fx(int slot, float volume, unsigned int duration,
BYTEBEAT_FUNC func);
BYTEBEAT *bytebeat_fx_ex(int slot, float volume, unsigned int duration,
BYTEBEAT_FUNC func, void *data);
BYTEBEAT *bytebeat_fx_get(int slot);
BYTEBEAT_FUNC bytebeat_fx_get_func(int slot);
int bytebeat_fx_is_free(int slot);
void bytebeat_fx_clear(int slot);
int bytebeat_fx_find_slot(void);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment