Created
November 21, 2019 16:05
-
-
Save madgarden/76d8a8c19a95663cb7e1def43c70ae1a to your computer and use it in GitHub Desktop.
Arecibo Man bytebeat synthesizer
This file contains 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
#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; | |
} | |
*/ |
This file contains 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
#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