uxn

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

audio.c (8373B)


      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(PEEK2(addr));
    254 		}
    255 		channel[n].duration -= SOUND_TIMER;
    256 		int x = 0;
    257 		if(channel[n].xfade) {
    258 			float delta = 1.0f / (XFADE_SAMPLES * 2);
    259 			while(x < XFADE_SAMPLES * 2) {
    260 				float alpha = x * delta;
    261 				float beta = 1.0f - alpha;
    262 				Sint16 next_a = next_sample(&channel[n].next_sample);
    263 				Sint16 next_b = 0;
    264 				if(channel[n].sample.data != 0) {
    265 					next_b = next_sample(&channel[n].sample);
    266 				}
    267 				Sint16 next = alpha * next_a + beta * next_b;
    268 				stream[x++] += next * channel[n].vol_l;
    269 				stream[x++] += next * channel[n].vol_r;
    270 			}
    271 			channel[n].sample = channel[n].next_sample;
    272 			channel[n].xfade = false;
    273 		}
    274 		Sample *sample = &channel[n].sample;
    275 		while(x < len / 2) {
    276 			if(sample->data == 0) {
    277 				break;
    278 			}
    279 			Sint16 next = next_sample(sample);
    280 			stream[x++] += next * channel[n].vol_l;
    281 			stream[x++] += next * channel[n].vol_r;
    282 		}
    283 	}
    284 	int i;
    285 	for(i = 0; i < len / 2; i++) {
    286 		stream[i] <<= 6;
    287 	}
    288 }
    289 
    290 float
    291 calc_duration(Uint16 len, Uint8 pitch)
    292 {
    293 	float scale = tuning[pitch - 20] / tuning[0x3c - 20];
    294 	return len / (scale * 44.1f);
    295 }
    296 
    297 void
    298 audio_start(int idx, Uint8 *d, Uxn *u)
    299 {
    300 	Uint16 dur = PEEK2(d + 0x5);
    301 	Uint8 off = d[0xf] == 0x00;
    302 	Uint16 len = PEEK2(d + 0xa);
    303 	Uint8 pitch = d[0xf] & 0x7f;
    304 	if(pitch < 20) {
    305 		pitch = 20;
    306 	}
    307 	float duration = dur > 0 ? dur : calc_duration(len, pitch);
    308 
    309 	if(!off) {
    310 		Uint16 addr = PEEK2(d + 0xc);
    311 		Uint8 *data = &u->ram[addr];
    312 		Uint8 volume = d[0xe];
    313 		bool loop = !(d[0xf] & 0x80);
    314 		Uint16 adsr = PEEK2(d + 0x8);
    315 		Uint8 attack = (adsr >> 12) & 0xF;
    316 		Uint8 decay = (adsr >> 8) & 0xF;
    317 		Uint8 sustain = (adsr >> 4) & 0xF;
    318 		Uint8 release = (adsr >> 0) & 0xF;
    319 		note_on(&channel[idx], duration, data, len, volume, attack, decay, sustain, release, pitch, loop);
    320 	} else {
    321 		note_off(&channel[idx], duration);
    322 	}
    323 }
    324 
    325 Uint8
    326 audio_get_vu(int instance)
    327 {
    328 	return channel[instance].sample.env.vol * 255.0f;
    329 }
    330 
    331 Uint16
    332 audio_get_position(int instance)
    333 {
    334 	return channel[instance].sample.pos;
    335 }
    336 
    337 Uint8
    338 audio_dei(int instance, Uint8 *d, Uint8 port)
    339 {
    340 	switch(port) {
    341 	case 0x2: return audio_get_position(instance) >> 8;
    342 	case 0x3: return audio_get_position(instance);
    343 	case 0x4: return audio_get_vu(instance);
    344 	}
    345 	return d[port];
    346 }