commit dfc5071

pnthr  ·  2026-07-19 17:56:39 +0000 UTC
parent a3ebd68
ADDED AUDIO BACKEND TO UXN
1 files changed,  +247, -6
+247, -6
  1@@ -4,6 +4,7 @@
  2 #include <string.h>
  3 #include <sys/mman.h>
  4 #include <poll.h>
  5+#include <time.h>
  6 #include <wayland-client.h>
  7 #include <xkbcommon/xkbcommon.h>
  8 #include <linux/input-event-codes.h>
  9@@ -24,10 +25,14 @@ WITH REGARD TO THIS SOFTWARE.
 10 cc -DNDEBUG -O2 -g0 -s uxn11.c -lX11 -lutil -o uxn11
 11 */
 12 
 13+extern int mkstemp(char *template);
 14+
 15 typedef signed char Sint8;
 16 typedef unsigned char Uint8;
 17 typedef signed short Sint16;
 18 typedef unsigned short Uint16;
 19+typedef signed int Sint32;
 20+typedef unsigned int Uint32;
 21 typedef void (*deo_handler)(void);
 22 typedef Uint8 (*dei_handler)(void);
 23 
 24@@ -36,6 +41,26 @@ static Uint8 *ram, dev[0x100], stk[2][0x100], ptr[2];
 25 static int console_vector, screen_vector, controller_vector, mouse_vector;
 26 static int rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY, rL1, rL2;
 27 
 28+#define SAMPLE_FREQUENCY 44100
 29+#define POLYPHONY 4
 30+#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025)
 31+#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
 32+
 33+typedef struct {
 34+	Uint8 *addr;
 35+	Uint32 count, advance, period, age, a, d, s, r;
 36+	Uint16 i, len;
 37+	Sint8 volume[2];
 38+	Uint8 pitch, repeat;
 39+}	UxnAudio;
 40+
 41+static UxnAudio uxn_audio[POLYPHONY];
 42+
 43+static Uint32 advances[12] = {
 44+	0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
 45+	0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
 46+};
 47+
 48 /* clang-format off */
 49 
 50 #define BANKS 0x10
 51@@ -538,6 +563,180 @@ static void screen_deo_addr(void) { rA = peek2(&dev[0x2c]); }
 52 
 53 /* clang-format on */
 54 
 55+/*
 56+@|Audio ------------------------------------------------------------- */
 57+
 58+static unsigned audio_rate;
 59+
 60+static Sint32
 61+envelope(UxnAudio *c, Uint32 age)
 62+{
 63+	if(!c->r)
 64+		return 0x0888;
 65+	if(age < c->a)
 66+		return 0x0888 * age / c->a;
 67+	if(age < c->d)
 68+		return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a);
 69+	if(age < c->s)
 70+		return 0x0444;
 71+	if(age < c->r)
 72+		return 0x0444 * (c->r - age) / (c->r - c->s);
 73+	c->advance = 0;
 74+	return 0x0000;
 75+}
 76+
 77+static Uint8
 78+audio_get_vu(int instance)
 79+{
 80+	int i;
 81+	UxnAudio *c = &uxn_audio[instance];
 82+	Sint32 sum[2] = {0, 0};
 83+	if(!c->advance || !c->period)
 84+		return 0;
 85+	for(i = 0; i < 2; i++) {
 86+		if(!c->volume[i])
 87+			continue;
 88+		sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800;
 89+		if(sum[i] > 0xf)
 90+			sum[i] = 0xf;
 91+	}
 92+	return (sum[0] << 4) | sum[1];
 93+}
 94+
 95+static Uint16
 96+audio_get_position(int instance)
 97+{
 98+	return uxn_audio[instance].i;
 99+}
100+
101+static void
102+audio_start(int instance, Uint8 *d)
103+{
104+	UxnAudio *c = &uxn_audio[instance];
105+	Uint8 pitch = d[0xf] & 0x7f;
106+	Uint16 addr = peek2(d + 0xc);
107+	Uint16 adsr = peek2(d + 0x8);
108+	c->len = peek2(d + 0xa);
109+	if(c->len > 0x10000 - addr)
110+		c->len = 0x10000 - addr;
111+	c->addr = &ram[addr]; // this isnt a thing
112+	c->volume[0] = d[0xe] >> 4;
113+	c->volume[1] = d[0xe] & 0xf;
114+	c->repeat = !(d[0xf] & 0x80);
115+	if(pitch < 108 && c->len)
116+		c->advance = advances[pitch % 12] >> (8 - pitch / 12);
117+	else {
118+		c->advance = 0;
119+		return;
120+	}
121+	c->a = ADSR_STEP * (adsr >> 12);
122+	c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a;
123+	c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d;
124+	c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s;
125+	c->age = 0;
126+	c->i = 0;
127+	if(c->len <= 0x100)
128+		c->period = NOTE_PERIOD * 337 / 2 / c->len;
129+	else
130+		c->period = NOTE_PERIOD;
131+  	/* Debug Print */
132+	fprintf(stderr, "[APU %d] PLAY: addr=0x%04x len=%u adsr=0x%04x vol=%d/%d pitch=%d repeat=%d\n",
133+	        instance, addr, c->len, adsr, c->volume[0], c->volume[1], pitch, c->repeat);
134+
135+}
136+
137+#define AUDIO_CHAN(n, base)							\
138+static void audio##n##_deo_vector      (void) {}				\
139+static void audio##n##_deo_volume      (void) {}				\
140+static void audio##n##_deo_pitch       (void) {					\
141+	       audio_start(n, &dev[base]);					\
142+}										\
143+static Uint8 audio##n##_dei_position   (void) {					\
144+	return audio_get_position(n) >> 8;					\
145+}										\
146+static Uint8 audio##n##_dei_position_lo(void) {					\
147+	return (Uint8)audio_get_position(n);					\
148+}										\
149+static Uint8 audio##n##_dei_output     (void) {					\
150+	return audio_get_vu(n);							\
151+}
152+
153+AUDIO_CHAN(0, 0x30)
154+AUDIO_CHAN(1, 0x40)
155+AUDIO_CHAN(2, 0x50)
156+AUDIO_CHAN(3, 0x60)
157+
158+  // speed i need this my mom is very rich and my dad has a good job i am in a good familily and i will never need financial support from anybody 
159+
160+void
161+audio_init(void)
162+{
163+	audio_rate = SAMPLE_FREQUENCY;
164+}
165+
166+void
167+audio_write(Sint16 *buf, unsigned count)
168+{
169+	(void)buf;
170+	(void)count;
171+	// that white cat thing presenting his hand in a confused matter
172+}
173+
174+static void
175+audio_finished_handler(int instance)
176+{
177+	Uint8 *port_value = &dev[0x30 + 0x10 * instance];
178+	Uint16 vector = port_value[0] << 8 | port_value[1];
179+
180+	/* Debug Print */
181+	fprintf(stderr, "[APU %d] FINISHED: vector=0x%04x\n", instance, vector);
182+
183+	if(vector)
184+		uxn_eval(vector);
185+}
186+
187+static int
188+audio_render(int instance, Sint16 *sample, Sint16 *end)
189+{
190+	UxnAudio *c = &uxn_audio[instance];
191+	Sint32 s;
192+	if(!c->advance || !c->period)
193+		return 0;
194+	while(sample < end) {
195+		c->count += c->advance;
196+		c->i += c->count / c->period;
197+		c->count %= c->period;
198+		if(c->i >= c->len) {
199+			if(!c->repeat) {
200+				c->advance = 0;
201+				break;
202+			}
203+			c->i %= c->len;
204+		}
205+		s = (Sint8)(*(c->addr + c->i) + 0x80) * envelope(c, c->age++);
206+		*sample++ += s * c->volume[0] / 0x180;
207+		*sample++ += s * c->volume[1] / 0x180;
208+	}
209+	if(!c->advance)
210+		audio_finished_handler(instance);
211+	return 1;
212+}
213+
214+static void
215+audio_output(void)
216+{
217+	int c;
218+	Sint16 mix[2048];
219+	unsigned count = audio_rate / 60;
220+	if(count > 1024)
221+		count = 1024;
222+	memset(mix, 0, count * 2 * sizeof(Sint16));
223+	for(c = 0; c < POLYPHONY; c++) {
224+		audio_render(c, mix, mix + count * 2);
225+	}
226+	audio_write(mix, count);
227+}
228+
229 /*
230 @|Controller -------------------------------------------------------- */
231 
232@@ -895,8 +1094,6 @@ static void fileb_deo_write(void) { poke2(&dev[0xb2], file_write(1, peek2(&dev[0
233 /*
234 @|Datetime ---------------------------------------------------------- */
235 
236-#include <time.h>
237-
238 int datetime_busy;
239 time_t datetime_seconds;
240 struct tm *datetime_t, datetime_zt = {0};
241@@ -946,6 +1143,18 @@ static const dei_handler dei_handlers[256] = {
242 	[0x28] = screen_dei_rx,
243 	[0x2a] = screen_dei_ry,
244 	[0x2c] = screen_dei_ra,
245+	[0x32] = audio0_dei_position,
246+	[0x33] = audio0_dei_position_lo,
247+	[0x34] = audio0_dei_output,
248+	[0x42] = audio1_dei_position,
249+	[0x43] = audio1_dei_position_lo,
250+	[0x44] = audio1_dei_output,
251+	[0x52] = audio2_dei_position,
252+	[0x53] = audio2_dei_position_lo,
253+	[0x54] = audio2_dei_output,
254+	[0x62] = audio3_dei_position,
255+	[0x63] = audio3_dei_position_lo,
256+	[0x64] = audio3_dei_output,
257 	[0xc0] = datetime_dei_y,
258 	[0xc2] = datetime_dei_mon,
259 	[0xc3] = datetime_dei_day,
260@@ -979,6 +1188,18 @@ static const deo_handler deo_handlers[256] = {
261 	[0x2d] = screen_deo_addr,
262 	[0x2e] = screen_deo_pixel,
263 	[0x2f] = screen_deo_sprite,
264+	[0x30] = audio0_deo_vector,
265+	[0x3e] = audio0_deo_volume,
266+	[0x3f] = audio0_deo_pitch,
267+	[0x40] = audio1_deo_vector,
268+	[0x4e] = audio1_deo_volume,
269+	[0x4f] = audio1_deo_pitch,
270+	[0x50] = audio2_deo_vector,
271+	[0x5e] = audio2_deo_volume,
272+	[0x5f] = audio2_deo_pitch,
273+	[0x60] = audio3_deo_vector,
274+	[0x6e] = audio3_deo_volume,
275+	[0x6f] = audio3_deo_pitch,
276 	[0x81] = controller_deo_vector,
277 	[0x91] = mouse_deo_vector,
278 	[0xa5] = filea_deo_stat,
279@@ -1174,8 +1395,6 @@ frame_callback(void *data, struct wl_callback *cb, uint32_t time)
280 	memcpy((Uint8 *)shm_data + active_buf * shm_bufsize,
281 	       (Uint8 *)shm_data + old * shm_bufsize, shm_bufsize);
282 	screen_pixels = (int *)((Uint8 *)shm_data + active_buf * shm_bufsize);
283-	screen_update();
284-	emu_redraw();
285 }
286 
287 static void
288@@ -1469,13 +1688,34 @@ emu_run(void)
289 	int has_input, stdin_active = 1;
290 	char coninp[CONINBUFSIZE];
291 	struct pollfd fds[2];
292+	struct timespec next_refresh;
293 	fds[0].fd = wl_display_get_fd(emu_dpy), fds[0].events = POLLIN;
294 	fds[1].fd = STDIN_FILENO, fds[1].events = POLLIN | POLLHUP;
295+	clock_gettime(CLOCK_MONOTONIC, &next_refresh);
296 	while(!dev[0x0f]) {
297 		wl_display_dispatch_pending(emu_dpy);
298 		wl_display_flush(emu_dpy);
299-		/* only poll stdin while it's still active */
300-		if(poll(fds, stdin_active ? 2 : 1, -1) <= 0)
301+		struct timespec now;
302+		clock_gettime(CLOCK_MONOTONIC, &now);
303+		long long now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
304+		long long next_ms = (long long)next_refresh.tv_sec * 1000 + next_refresh.tv_nsec / 1000000;
305+		if(now_ms >= next_ms) {
306+			audio_output();
307+			screen_update();
308+			emu_redraw();
309+			next_ms += 16;
310+			if(now_ms > next_ms + 100)
311+				next_ms = now_ms +16;
312+			next_refresh.tv_sec = next_ms / 1000;
313+			next_refresh.tv_nsec = (next_ms % 1000) * 1000000;
314+		}
315+		clock_gettime(CLOCK_MONOTONIC, &now);
316+		now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
317+		int timeout = next_ms - now_ms;
318+		if(timeout < 0)
319+			timeout = 0;
320+		/* only pull stdin while it's still active */
321+		if(poll(fds, stdin_active ? 2 : 1, timeout) <= 0)
322 			continue;
323 		if(fds[0].revents & POLLIN)
324 			wl_display_dispatch(emu_dpy);
325@@ -1504,6 +1744,7 @@ emu_run(void)
326 		zwlr_layer_surface_v1_destroy(emu_layer_surf);
327 	if(emu_surf)
328 		wl_surface_destroy(emu_surf);
329+	// need to close audio
330 	if(emu_dpy)
331 		wl_display_disconnect(emu_dpy);
332 	console_close();