uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

audio.c (8378B)


      1 #include "../uxn.h"
      2 #include "audio.h"
      3 #include <stdbool.h>
      4 #include <string.h>
      5 
      6 /*
      7 Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick, Bad Diode
      8 
      9 Permission to use, copy, modify, and distribute this software for any
     10 purpose with or without fee is hereby granted, provided that the above
     11 copyright notice and this permission notice appear in all copies.
     12 
     13 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     14 WITH REGARD TO THIS SOFTWARE.
     15 */
     16 
     17 #define SOUND_TIMER (AUDIO_BUFSIZE / SAMPLE_FREQUENCY * 1000.0f)
     18 #define XFADE_SAMPLES 100
     19 #define INTERPOL_METHOD 1
     20 
     21 typedef enum EnvStage {
     22 	ENV_ATTACK = (1 << 0),
     23 	ENV_DECAY = (1 << 1),
     24 	ENV_SUSTAIN = (1 << 2),
     25 	ENV_RELEASE = (1 << 3),
     26 } EnvStage;
     27 
     28 typedef struct Envelope {
     29 	float a;
     30 	float d;
     31 	float s;
     32 	float r;
     33 	float vol;
     34 	EnvStage stage;
     35 } Envelope;
     36 
     37 typedef struct Sample {
     38 	Uint8 *data;
     39 	float len;
     40 	float pos;
     41 	float inc;
     42 	float loop;
     43 	Uint8 pitch;
     44 	Envelope env;
     45 } Sample;
     46 
     47 typedef struct AudioChannel {
     48 	Sample sample;
     49 	Sample next_sample;
     50 	bool xfade;
     51 	float duration;
     52 	float vol_l;
     53 	float vol_r;
     54 } AudioChannel;
     55 
     56 AudioChannel channel[POLYPHONY];
     57 
     58 /* clang-format off */
     59 
     60 const float tuning[109] = {
     61         0.00058853f, 0.00062352f, 0.00066060f, 0.00069988f, 0.00074150f,
     62         0.00078559f, 0.00083230f, 0.00088179f, 0.00093423f, 0.00098978f,
     63         0.00104863f, 0.00111099f, 0.00117705f, 0.00124704f, 0.00132120f,
     64         0.00139976f, 0.00148299f, 0.00157118f, 0.00166460f, 0.00176359f,
     65         0.00186845f, 0.00197956f, 0.00209727f, 0.00222198f, 0.00235410f,
     66         0.00249409f, 0.00264239f, 0.00279952f, 0.00296599f, 0.00314235f,
     67         0.00332921f, 0.00352717f, 0.00373691f, 0.00395912f, 0.00419454f,
     68         0.00444396f, 0.00470821f, 0.00498817f, 0.00528479f, 0.00559904f,
     69         0.00593197f, 0.00628471f, 0.00665841f, 0.00705434f, 0.00747382f,
     70         0.00791823f, 0.00838908f, 0.00888792f, 0.00941642f, 0.00997635f,
     71         0.01056957f, 0.01119807f, 0.01186395f, 0.01256941f, 0.01331683f,
     72         0.01410869f, 0.01494763f, 0.01583647f, 0.01677815f, 0.01777583f,
     73         0.01883284f, 0.01995270f, 0.02113915f, 0.02239615f, 0.02372789f,
     74         0.02513882f, 0.02663366f, 0.02821738f, 0.02989527f, 0.03167293f,
     75         0.03355631f, 0.03555167f, 0.03766568f, 0.03990540f, 0.04227830f,
     76         0.04479229f, 0.04745578f, 0.05027765f, 0.05326731f, 0.05643475f,
     77         0.05979054f, 0.06334587f, 0.06711261f, 0.07110333f, 0.07533136f,
     78         0.07981079f, 0.08455659f, 0.08958459f, 0.09491156f, 0.10055530f,
     79         0.10653463f, 0.11286951f, 0.11958108f, 0.12669174f, 0.13422522f,
     80         0.14220667f, 0.15066272f, 0.15962159f, 0.16911318f, 0.17916918f,
     81         0.18982313f, 0.20111060f, 0.21306926f, 0.22573902f, 0.23916216f,
     82         0.25338348f, 0.26845044f, 0.28441334f, 0.30132544f,
     83 };
     84 
     85 /* clang-format on */
     86 
     87 void
     88 env_on(Envelope *env)
     89 {
     90 	env->stage = ENV_ATTACK;
     91 	env->vol = 0.0f;
     92 	if(env->a > 0) {
     93 		env->a = (SOUND_TIMER / AUDIO_BUFSIZE) / env->a;
     94 	} else if(env->stage == ENV_ATTACK) {
     95 		env->stage = ENV_DECAY;
     96 		env->vol = 1.0f;
     97 	}
     98 	if(env->d < 10.0f) {
     99 		env->d = 10.0f;
    100 	}
    101 	env->d = (SOUND_TIMER / AUDIO_BUFSIZE) / env->d;
    102 	if(env->r < 10.0f) {
    103 		env->r = 10.0f;
    104 	}
    105 	env->r = (SOUND_TIMER / AUDIO_BUFSIZE) / env->r;
    106 }
    107 
    108 void
    109 env_off(Envelope *env)
    110 {
    111 	env->stage = ENV_RELEASE;
    112 }
    113 
    114 void
    115 note_on(AudioChannel *channel, float duration, Uint8 *data, Uint16 len, Uint8 vol, Uint8 attack, Uint8 decay, Uint8 sustain, Uint8 release, Uint8 pitch, bool loop)
    116 {
    117 	channel->duration = duration;
    118 	channel->vol_l = (vol >> 4) / 15.0f;
    119 	channel->vol_r = (vol & 0xf) / 15.0f;
    120 
    121 	Sample sample = {0};
    122 	sample.data = data;
    123 	sample.len = len;
    124 	sample.pos = 0;
    125 	sample.env.a = attack * 64.0f;
    126 	sample.env.d = decay * 64.0f;
    127 	sample.env.s = sustain / 16.0f;
    128 	sample.env.r = release * 64.0f;
    129 	if(loop) {
    130 		sample.loop = len;
    131 	} else {
    132 		sample.loop = 0;
    133 	}
    134 	env_on(&sample.env);
    135 	float sample_rate = 44100 / 261.60;
    136 	if(len <= 256) {
    137 		sample_rate = len;
    138 	}
    139 	const float *inc = &tuning[pitch - 20];
    140 	sample.inc = *(inc)*sample_rate;
    141 
    142 	channel->next_sample = sample;
    143 	channel->xfade = true;
    144 }
    145 
    146 void
    147 note_off(AudioChannel *channel, float duration)
    148 {
    149 	channel->duration = duration;
    150 	env_off(&channel->sample.env);
    151 }
    152 
    153 void
    154 env_advance(Envelope *env)
    155 {
    156 	switch(env->stage) {
    157 	case ENV_ATTACK: {
    158 		env->vol += env->a;
    159 		if(env->vol >= 1.0f) {
    160 			env->stage = ENV_DECAY;
    161 			env->vol = 1.0f;
    162 		}
    163 	} break;
    164 	case ENV_DECAY: {
    165 		env->vol -= env->d;
    166 		if(env->vol <= env->s || env->d <= 0) {
    167 			env->stage = ENV_SUSTAIN;
    168 			env->vol = env->s;
    169 		}
    170 	} break;
    171 	case ENV_SUSTAIN: {
    172 		env->vol = env->s;
    173 	} break;
    174 	case ENV_RELEASE: {
    175 		if(env->vol <= 0 || env->r <= 0) {
    176 			env->vol = 0;
    177 		} else {
    178 			env->vol -= env->r;
    179 		}
    180 	} break;
    181 	}
    182 }
    183 
    184 float
    185 interpolate_sample(Uint8 *data, Uint16 len, float pos)
    186 {
    187 #if INTERPOL_METHOD == 0
    188 	return data[(int)pos];
    189 
    190 #elif INTERPOL_METHOD == 1
    191 	float x = pos;
    192 	int x0 = (int)x;
    193 	int x1 = (x0 + 1);
    194 	float y0 = data[x0];
    195 	float y1 = data[x1 % len];
    196 	x = x - x0;
    197 	float y = y0 + x * (y1 - y0);
    198 	return y;
    199 
    200 #elif INTERPOL_METHOD == 2
    201 	float x = pos;
    202 	int x0 = x - 1;
    203 	int x1 = x;
    204 	int x2 = x + 1;
    205 	int x3 = x + 2;
    206 	float y0 = data[x0 % len];
    207 	float y1 = data[x1];
    208 	float y2 = data[x2 % len];
    209 	float y3 = data[x3 % len];
    210 	x = x - x1;
    211 	float c0 = y1;
    212 	float c1 = 0.5f * (y2 - y0);
    213 	float c2 = y0 - 2.5f * y1 + 2.f * y2 - 0.5f * y3;
    214 	float c3 = 1.5f * (y1 - y2) + 0.5f * (y3 - y0);
    215 	return ((c3 * x + c2) * x + c1) * x + c0;
    216 #endif
    217 }
    218 
    219 Sint16
    220 next_sample(Sample *sample)
    221 {
    222 	if(sample->pos >= sample->len) {
    223 		if(sample->loop == 0) {
    224 			sample->data = 0;
    225 			return 0;
    226 		}
    227 		while(sample->pos >= sample->len) {
    228 			sample->pos -= sample->loop;
    229 		}
    230 	}
    231 
    232 	float val = interpolate_sample(sample->data, sample->len, sample->pos);
    233 	val *= sample->env.vol;
    234 	Sint8 next = (Sint8)0x80 ^ (Uint8)val;
    235 
    236 	sample->pos += sample->inc;
    237 	env_advance(&sample->env);
    238 	return next;
    239 }
    240 
    241 void
    242 audio_handler(void *ctx, Uint8 *out_stream, int len)
    243 {
    244 	Sint16 *stream = (Sint16 *)out_stream;
    245 	memset(stream, 0x00, len);
    246 
    247 	int n;
    248 	for(n = 0; n < POLYPHONY; n++) {
    249 		Uint8 device = (3 + n) << 4;
    250 		Uxn *u = (Uxn *)ctx;
    251 		Uint8 *addr = &u->dev[device];
    252 		if(channel[n].duration <= 0 && PEEK2(addr)) {
    253 			uxn_eval(u, PEEK2(addr));
    254 		}
    255 		channel[n].duration -= SOUND_TIMER;
    256 
    257 		int x = 0;
    258 		if(channel[n].xfade) {
    259 			float delta = 1.0f / (XFADE_SAMPLES * 2);
    260 			while(x < XFADE_SAMPLES * 2) {
    261 				float alpha = x * delta;
    262 				float beta = 1.0f - alpha;
    263 				Sint16 next_a = next_sample(&channel[n].next_sample);
    264 				Sint16 next_b = 0;
    265 				if(channel[n].sample.data != 0) {
    266 					next_b = next_sample(&channel[n].sample);
    267 				}
    268 				Sint16 next = alpha * next_a + beta * next_b;
    269 				stream[x++] += next * channel[n].vol_l;
    270 				stream[x++] += next * channel[n].vol_r;
    271 			}
    272 			channel[n].sample = channel[n].next_sample;
    273 			channel[n].xfade = false;
    274 		}
    275 		Sample *sample = &channel[n].sample;
    276 		while(x < len / 2) {
    277 			if(sample->data == 0) {
    278 				break;
    279 			}
    280 			Sint16 next = next_sample(sample);
    281 			stream[x++] += next * channel[n].vol_l;
    282 			stream[x++] += next * channel[n].vol_r;
    283 		}
    284 	}
    285 	int i;
    286 	for(i = 0; i < len / 2; i++) {
    287 		stream[i] <<= 6;
    288 	}
    289 }
    290 
    291 float
    292 calc_duration(Uint16 len, Uint8 pitch)
    293 {
    294 	float scale = tuning[pitch - 20] / tuning[0x3c - 20];
    295 	return len / (scale * 44.1f);
    296 }
    297 
    298 void
    299 audio_start(int idx, Uint8 *d, Uxn *u)
    300 {
    301 	Uint16 dur = PEEK2(d + 0x5);
    302 	Uint8 off = d[0xf] == 0x00;
    303 	Uint16 len = PEEK2(d + 0xa);
    304 	Uint8 pitch = d[0xf] & 0x7f;
    305 	if(pitch < 20) {
    306 		pitch = 20;
    307 	}
    308 	float duration = dur > 0 ? dur : calc_duration(len, pitch);
    309 
    310 	if(!off) {
    311 		Uint16 addr = PEEK2(d + 0xc);
    312 		Uint8 *data = &u->ram[addr];
    313 		Uint8 volume = d[0xe];
    314 		bool loop = !(d[0xf] & 0x80);
    315 		Uint16 adsr = PEEK2(d + 0x8);
    316 		Uint8 attack = (adsr >> 12) & 0xF;
    317 		Uint8 decay = (adsr >> 8) & 0xF;
    318 		Uint8 sustain = (adsr >> 4) & 0xF;
    319 		Uint8 release = (adsr >> 0) & 0xF;
    320 		note_on(&channel[idx], duration, data, len, volume, attack, decay, sustain, release, pitch, loop);
    321 	} else {
    322 		note_off(&channel[idx], duration);
    323 	}
    324 }
    325 
    326 Uint8
    327 audio_get_vu(int instance)
    328 {
    329 	return channel[instance].sample.env.vol * 255.0f;
    330 }
    331 
    332 Uint16
    333 audio_get_position(int instance)
    334 {
    335 	return channel[instance].sample.pos;
    336 }
    337 
    338 Uint8
    339 audio_dei(int instance, Uint8 *d, Uint8 port)
    340 {
    341 	switch(port) {
    342 	case 0x2: return audio_get_position(instance) >> 8;
    343 	case 0x3: return audio_get_position(instance);
    344 	case 0x4: return audio_get_vu(instance);
    345 	}
    346 	return d[port];
    347 }