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();