Last active
September 29, 2015 16:08
-
-
Save tong/1627181 to your computer and use it in GitHub Desktop.
SFXR, C++ original
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 <stdio.h> | |
//#include <stdarg.h> | |
//#include <stdlib.h> | |
//#include <time.h> | |
#include <math.h> | |
#include <SDL/SDL.h> | |
#include <neko.h> | |
#define rnd(n) (rand()%(n+1)) | |
#define PI 3.14159265f | |
float frnd(float range) { | |
return (float) rnd(10000) / 10000 * range; | |
} | |
bool playing_sample = false; | |
int phase; | |
double fperiod; | |
double fmaxperiod; | |
double fslide; | |
double fdslide; | |
int period; | |
float square_duty; | |
float square_slide; | |
int env_stage; | |
int env_time; | |
int env_length[3]; | |
float env_vol; | |
float fphase; | |
float fdphase; | |
int iphase; | |
float phaser_buffer[1024]; | |
int ipp; | |
float noise_buffer[32]; | |
float fltp; | |
float fltdp; | |
float fltw; | |
float fltw_d; | |
float fltdmp; | |
float fltphp; | |
float flthp; | |
float flthp_d; | |
float vib_phase; | |
float vib_speed; | |
float vib_amp; | |
int rep_time; | |
int rep_limit; | |
int arp_time; | |
int arp_limit; | |
double arp_mod; | |
int wave_type; | |
float p_base_freq; | |
float p_freq_limit; | |
float p_freq_ramp; | |
float p_freq_dramp; | |
float p_duty; | |
float p_duty_ramp; | |
float p_vib_strength; | |
float p_vib_speed; | |
float p_vib_delay; | |
float p_env_attack; | |
float p_env_sustain; | |
float p_env_decay; | |
float p_env_punch; | |
bool filter_on; | |
float p_lpf_resonance; | |
float p_lpf_freq; | |
float p_lpf_ramp; | |
float p_hpf_freq; | |
float p_hpf_ramp; | |
float p_pha_offset; | |
float p_pha_ramp; | |
float p_repeat_speed; | |
float p_arp_speed; | |
float p_arp_mod; | |
float master_vol = 0.05f; | |
float sound_vol = 0.5f; | |
int wav_bits = 16; | |
int wav_freq = 44100; | |
int file_sampleswritten; | |
float filesample = 0.0f; | |
int fileacc = 0; | |
void resetParams() { | |
wave_type = 0; | |
p_base_freq = 0.3f; | |
p_freq_limit = 0.0f; | |
p_freq_ramp = 0.0f; | |
p_freq_dramp = 0.0f; | |
p_duty = 0.0f; | |
p_duty_ramp = 0.0f; | |
p_vib_strength = 0.0f; | |
p_vib_speed = 0.0f; | |
p_vib_delay = 0.0f; | |
p_env_attack = 0.0f; | |
p_env_sustain = 0.3f; | |
p_env_decay = 0.4f; | |
p_env_punch = 0.0f; | |
filter_on = false; | |
p_lpf_resonance = 0.0f; | |
p_lpf_freq = 1.0f; | |
p_lpf_ramp = 0.0f; | |
p_hpf_freq = 0.0f; | |
p_hpf_ramp = 0.0f; | |
p_pha_offset = 0.0f; | |
p_pha_ramp = 0.0f; | |
p_repeat_speed = 0.0f; | |
p_arp_speed = 0.0f; | |
p_arp_mod = 0.0f; | |
} | |
void resetSample(bool restart) { | |
if (!restart) | |
phase = 0; | |
fperiod = 100.0 / (p_base_freq * p_base_freq + 0.001); | |
period = (int) fperiod; | |
fmaxperiod = 100.0 / (p_freq_limit * p_freq_limit + 0.001); | |
fslide = 1.0 - pow((double) p_freq_ramp, 3.0) * 0.01; | |
fdslide = -pow((double) p_freq_dramp, 3.0) * 0.000001; | |
square_duty = 0.5f - p_duty * 0.5f; | |
square_slide = -p_duty_ramp * 0.00005f; | |
if (p_arp_mod >= 0.0f) | |
arp_mod = 1.0 - pow((double) p_arp_mod, 2.0) * 0.9; | |
else | |
arp_mod = 1.0 + pow((double) p_arp_mod, 2.0) * 10.0; | |
arp_time = 0; | |
arp_limit = (int) (pow(1.0f - p_arp_speed, 2.0f) * 20000 + 32); | |
if (p_arp_speed == 1.0f) | |
arp_limit = 0; | |
if (!restart) { | |
// reset filter | |
fltp = 0.0f; | |
fltdp = 0.0f; | |
fltw = pow(p_lpf_freq, 3.0f) * 0.1f; | |
fltw_d = 1.0f + p_lpf_ramp * 0.0001f; | |
fltdmp = 5.0f / (1.0f + pow(p_lpf_resonance, 2.0f) * 20.0f) | |
* (0.01f + fltw); | |
if (fltdmp > 0.8f) | |
fltdmp = 0.8f; | |
fltphp = 0.0f; | |
flthp = pow(p_hpf_freq, 2.0f) * 0.1f; | |
flthp_d = 1.0 + p_hpf_ramp * 0.0003f; | |
// reset vibrato | |
vib_phase = 0.0f; | |
vib_speed = pow(p_vib_speed, 2.0f) * 0.01f; | |
vib_amp = p_vib_strength * 0.5f; | |
// reset envelope | |
env_vol = 0.0f; | |
env_stage = 0; | |
env_time = 0; | |
env_length[0] = (int) (p_env_attack * p_env_attack * 100000.0f); | |
env_length[1] = (int) (p_env_sustain * p_env_sustain * 100000.0f); | |
env_length[2] = (int) (p_env_decay * p_env_decay * 100000.0f); | |
fphase = pow(p_pha_offset, 2.0f) * 1020.0f; | |
if (p_pha_offset < 0.0f) | |
fphase = -fphase; | |
fdphase = pow(p_pha_ramp, 2.0f) * 1.0f; | |
if (p_pha_ramp < 0.0f) | |
fdphase = -fdphase; | |
iphase = abs((int) fphase); | |
//int i; | |
for (int i = 0; i < 1024; i++) | |
phaser_buffer[i] = 0.0f; | |
for (int i = 0; i < 32; i++) | |
noise_buffer[i] = frnd(2.0f) - 1.0f; | |
rep_time = 0; | |
rep_limit = (int) (pow(1.0f - p_repeat_speed, 2.0f) * 20000 + 32); | |
if (p_repeat_speed == 0.0f) | |
rep_limit = 0; | |
} | |
} | |
/* | |
void playSample() { | |
resetSample( false ); | |
playing_sample = true; | |
printf("PLAYEDSAMPLE\n"); | |
} | |
*/ | |
static inline void synthSample(int length, float* buffer, FILE* file) { | |
for (int i = 0; i < length; i++) { | |
if (!playing_sample) | |
break; | |
rep_time++; | |
if (rep_limit != 0 && rep_time >= rep_limit) { | |
rep_time = 0; | |
resetSample(true); | |
} | |
// frequency envelopes/arpeggios | |
arp_time++; | |
if (arp_limit != 0 && arp_time >= arp_limit) { | |
arp_limit = 0; | |
fperiod *= arp_mod; | |
} | |
fslide += fdslide; | |
fperiod *= fslide; | |
if (fperiod > fmaxperiod) { | |
fperiod = fmaxperiod; | |
if (p_freq_limit > 0.0f) | |
playing_sample = false; | |
} | |
float rfperiod = fperiod; | |
if (vib_amp > 0.0f) { | |
vib_phase += vib_speed; | |
rfperiod = fperiod * (1.0 + sin(vib_phase) * vib_amp); | |
} | |
period = (int) rfperiod; | |
if (period < 8) | |
period = 8; | |
square_duty += square_slide; | |
if (square_duty < 0.0f) | |
square_duty = 0.0f; | |
if (square_duty > 0.5f) | |
square_duty = 0.5f; | |
// volume envelope | |
env_time++; | |
if (env_time > env_length[env_stage]) { | |
env_time = 0; | |
env_stage++; | |
if (env_stage == 3) | |
playing_sample = false; | |
} | |
if (env_stage == 0) | |
env_vol = (float) env_time / env_length[0]; | |
if (env_stage == 1) | |
env_vol = 1.0f | |
+ pow(1.0f - (float) env_time / env_length[1], 1.0f) * 2.0f | |
* p_env_punch; | |
if (env_stage == 2) | |
env_vol = 1.0f - (float) env_time / env_length[2]; | |
// phaser step | |
fphase += fdphase; | |
iphase = abs((int) fphase); | |
if (iphase > 1023) | |
iphase = 1023; | |
if (flthp_d != 0.0f) { | |
flthp *= flthp_d; | |
if (flthp < 0.00001f) | |
flthp = 0.00001f; | |
if (flthp > 0.1f) | |
flthp = 0.1f; | |
} | |
float ssample = 0.0f; | |
int si = 0; | |
for (; si < 8; si++) // 8x supersampling | |
{ | |
float sample = 0.0f; | |
phase++; | |
if (phase >= period) { | |
// phase=0; | |
phase %= period; | |
if (wave_type == 3) { | |
int i = 0; | |
for (; i < 32; i++) | |
noise_buffer[i] = frnd(2.0f) - 1.0f; | |
} | |
} | |
// base waveform | |
float fp = (float) phase / period; | |
switch (wave_type) { | |
case 0: // square | |
if (fp < square_duty) | |
sample = 0.5f; | |
else | |
sample = -0.5f; | |
break; | |
case 1: // sawtooth | |
sample = 1.0f - fp * 2; | |
break; | |
case 2: // sine | |
sample = (float) sin(fp * 2 * PI); | |
break; | |
case 3: // noise | |
sample = noise_buffer[phase * 32 / period]; | |
break; | |
} | |
// lp filter | |
float pp = fltp; | |
fltw *= fltw_d; | |
if (fltw < 0.0f) | |
fltw = 0.0f; | |
if (fltw > 0.1f) | |
fltw = 0.1f; | |
if (p_lpf_freq != 1.0f) { | |
fltdp += (sample - fltp) * fltw; | |
fltdp -= fltdp * fltdmp; | |
} else { | |
fltp = sample; | |
fltdp = 0.0f; | |
} | |
fltp += fltdp; | |
// hp filter | |
fltphp += fltp - pp; | |
fltphp -= fltphp * flthp; | |
sample = fltphp; | |
// phaser | |
phaser_buffer[ipp & 1023] = sample; | |
sample += phaser_buffer[(ipp - iphase + 1024) & 1023]; | |
ipp = (ipp + 1) & 1023; | |
// final accumulation and envelope application | |
ssample += sample * env_vol; | |
} | |
ssample = ssample / 8 * master_vol; | |
ssample *= 2.0f * sound_vol; | |
if (buffer != NULL) { | |
if (ssample > 1.0f) | |
ssample = 1.0f; | |
if (ssample < -1.0f) | |
ssample = -1.0f; | |
*buffer++ = ssample; | |
} | |
if (file != NULL) { | |
// quantize depending on format | |
// accumulate/count to accomodate variable sample rate? | |
ssample *= 4.0f; // arbitrary gain to get reasonable output volume... | |
if (ssample > 1.0f) | |
ssample = 1.0f; | |
if (ssample < -1.0f) | |
ssample = -1.0f; | |
filesample += ssample; | |
fileacc++; | |
if (wav_freq == 44100 || fileacc == 2) { | |
filesample /= fileacc; | |
fileacc = 0; | |
if (wav_bits == 16) { | |
short isample = (short) (filesample * 32000); | |
fwrite(&isample, 1, 2, file); | |
} else { | |
unsigned char isample = (unsigned char) (filesample * 127 | |
+ 128); | |
fwrite(&isample, 1, 1, file); | |
} | |
filesample = 0.0f; | |
} | |
file_sampleswritten++; | |
} | |
} | |
} | |
bool mute_stream; | |
static void audioCallback(void *userdata, Uint8 *stream, int len) { | |
//printf("audioCallback\n"); | |
if (playing_sample && !mute_stream) { | |
unsigned int l = len / 2; | |
float fbuf[l]; | |
memset(fbuf, 0, sizeof(fbuf)); | |
synthSample(l, fbuf, NULL); | |
while (l--) { | |
float f = fbuf[l]; | |
if (f < -1.0) | |
f = -1.0; | |
if (f > 1.0) | |
f = 1.0; | |
((Sint16*) stream)[l] = (Sint16)(f * 32767); | |
} | |
} else | |
memset(stream, 0, len); | |
} | |
//////////////////////////////////////////////////////////////////// | |
static value sfxr_init() { | |
printf( "sfxr_initsfxr_initsfxr_init\n" ); | |
SDL_AudioSpec des; | |
des.freq = 44100; | |
des.format = AUDIO_S16SYS; | |
des.channels = 1; | |
//des.samples = 512; | |
des.samples = 4096; //8192; | |
des.callback = audioCallback; | |
des.userdata = NULL; | |
//VERIFY( !SDL_OpenAudio( &des, NULL ) ); | |
//printf( "%i\n", !SDL_OpenAudio( &des, NULL ) ); | |
if (SDL_OpenAudio(&des, NULL) < 0) | |
return val_false; | |
SDL_PauseAudio(0); | |
/* | |
// pickup/coin | |
resetParams(); | |
p_base_freq=0.4f+frnd(0.5f); | |
p_env_attack=0.0f; | |
p_env_sustain=frnd(0.1f); | |
p_env_decay=0.1f+frnd(0.4f); | |
p_env_punch=0.3f+frnd(0.3f); | |
if(rnd(1)) { | |
p_arp_speed=0.5f+frnd(0.2f); | |
p_arp_mod=0.2f+frnd(0.4f); | |
} | |
*/ | |
printf( "SoundFX engine initialized\n" ); | |
//#ifdef FNORD_DEBUG | |
//#endif | |
return val_true; | |
} | |
static value sfxr_set(value i) { | |
switch (val_int(i)) { | |
case 0: // pickup/coin | |
resetParams(); | |
p_base_freq = 0.4f + frnd(0.5f); | |
p_env_attack = 0.0f; | |
p_env_sustain = frnd(0.1f); | |
p_env_decay = 0.1f + frnd(0.4f); | |
p_env_punch = 0.3f + frnd(0.3f); | |
if (rnd(1)) { | |
p_arp_speed = 0.5f + frnd(0.2f); | |
p_arp_mod = 0.2f + frnd(0.4f); | |
} | |
break; | |
case 1: | |
///// | |
break; | |
} | |
} | |
static value sfxr_play() { | |
//playSample(); | |
resetSample(false); | |
playing_sample = true; | |
printf("PLAYEDSAMPLE\n"); | |
return val_null; | |
} | |
static value sfxr_stop() { | |
//TODO | |
return val_null; | |
} | |
DEFINE_PRIM( sfxr_init, 0 ); | |
DEFINE_PRIM( sfxr_set, 1 ); | |
DEFINE_PRIM( sfxr_play, 0 ); | |
DEFINE_PRIM( sfxr_stop, 0 ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment