Browse code

Returned SDL calls from apu.c to emulator.c

Andrew Alderwick authored on 08/04/2021 20:14:40
Showing 3 changed files
... ...
@@ -1,5 +1,3 @@
1
-#include <SDL2/SDL.h>
2
-
3 1
 /*
4 2
 Copyright (c) 2021 Devine Lu Linvega
5 3
 Copyright (c) 2021 Andrew Alderwick
... ...
@@ -13,11 +11,9 @@ WITH REGARD TO THIS SOFTWARE.
13 11
 */
14 12
 
15 13
 #include "uxn.h"
14
+#include "apu.h"
16 15
 
17
-#define SAMPLE_FREQUENCY 48000
18
-
19
-extern SDL_AudioDeviceID audio_id;
20
-int error(char *msg, const char *err);
16
+extern Device *devapu;
21 17
 
22 18
 static Uint32 note_advances[12] = {
23 19
 	0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */
... ...
@@ -34,55 +30,33 @@ static Uint32 note_advances[12] = {
34 30
 	0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */
35 31
 };
36 32
 
37
-typedef struct {
38
-	Uint16 *dat;
39
-	Uint8 i, n, sz, ends;
40
-} Queue;
41
-
42
-typedef struct {
43
-	Uint32 count, advance, period;
44
-	Uint16 vector;
45
-	Sint16 start_value, end_value;
46
-	Queue queue;
47
-} WaveformGenerator;
48
-
49
-typedef struct {
50
-	WaveformGenerator wv[2];
51
-	Sint8 volume[2], playing;
52
-} Note;
53
-
54
-static Note *notes = NULL;
55
-static int n_notes = 0;
56
-static Queue *q;
57
-static Uint16 id_addr;
58
-
59 33
 static void
60
-play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples)
34
+render_note(Apu *apu, Uxn *u, int note_i, Sint16 *samples, int n_samples)
61 35
 {
62 36
 	int i;
63
-	Note *note = &notes[note_i];
37
+	Note *note = &apu->notes[note_i];
64 38
 	while(n_samples--) {
65 39
 		Sint32 sample = 1;
66 40
 		for(i = 0; i < 2; ++i) {
67 41
 			WaveformGenerator *wv = &note->wv[i];
68
-			q = &wv->queue;
42
+			apu->queue = &wv->queue;
69 43
 			wv->count += wv->advance;
70 44
 			while(wv->count > wv->period) {
71 45
 				wv->count -= wv->period;
72 46
 				wv->start_value = wv->end_value;
73
-				if(q->i == q->n) {
74
-					q->i = q->n = 0;
75
-					if(!q->ends) {
76
-						u->ram.dat[id_addr] = note_i;
47
+				if(apu->queue->i == apu->queue->n) {
48
+					apu->queue->i = apu->queue->n = 0;
49
+					if(!apu->queue->finishes) {
50
+						u->ram.dat[devapu->addr + 0xa] = note_i;
77 51
 						evaluxn(u, wv->vector);
78 52
 					}
79 53
 				}
80
-				if(!q->n) {
54
+				if(!apu->queue->n) {
81 55
 					note->playing = 0;
82 56
 					return;
83 57
 				}
84
-				wv->end_value = (Sint16)q->dat[q->i++];
85
-				wv->period = (30 << 4) * q->dat[q->i++];
58
+				wv->end_value = (Sint16)apu->queue->dat[apu->queue->i++];
59
+				wv->period = (30 << 4) * apu->queue->dat[apu->queue->i++];
86 60
 			}
87 61
 			if(wv->period >> 9)
88 62
 				sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10);
... ...
@@ -94,70 +68,31 @@ play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples)
94 68
 	}
95 69
 }
96 70
 
97
-static void
98
-play_all_notes(void *u, Uint8 *stream, int len)
71
+void
72
+apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples)
99 73
 {
100 74
 	int i;
101
-	SDL_memset(stream, 0, len);
102
-	for(i = 0; i < n_notes; ++i)
103
-		if(notes[i].playing) play_note(u, i, (Sint16 *)stream, len >> 2);
104
-	q = NULL;
105
-}
106
-
107
-static Note *
108
-get_note(Uint8 i)
109
-{
110
-	if(i >= n_notes) notes = SDL_realloc(notes, (i + 1) * sizeof(Note));
111
-	while(i >= n_notes) SDL_zero(notes[n_notes++]);
112
-	return &notes[i];
75
+	for(i = 0; i < n_samples * 2; ++i)
76
+		samples[i] = 0;
77
+	for(i = 0; i < apu->n_notes; ++i)
78
+		if(apu->notes[i].playing) render_note(apu, u, i, samples, n_samples);
79
+	apu->queue = NULL;
113 80
 }
114 81
 
115
-static Uint8
116
-audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
82
+void
83
+apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch)
117 84
 {
118
-	Uint8 *m = u->ram.dat + ptr;
119 85
 	int i;
120
-	if(b0 == 0xa) {
121
-		Note *note = get_note(b1);
122
-		note->playing = 1;
123
-		for(i = 0; i < 2; ++i) {
124
-			note->volume[i] = 0xf & (m[0x8] >> 4 * (1 - i));
125
-			note->wv[i].vector = (m[0x0 + i * 2] << 8) + m[0x1 + i * 2];
126
-			note->wv[i].count = note->wv[i].period = 0;
127
-			note->wv[i].end_value = 0;
128
-			note->wv[i].queue.n = note->wv[i].queue.i = 0;
129
-			note->wv[i].queue.ends = 0;
130
-		}
131
-		note->wv[0].advance = note_advances[m[0x9] % 12] >> (8 - m[0x9] / 12);
132
-		note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY;
133
-	} else if(b0 == 0xe && q != NULL) {
134
-		if(q->n == q->sz) {
135
-			q->sz = q->sz < 4 ? 4 : q->sz * 2;
136
-			q->dat = SDL_realloc(q->dat, q->sz * sizeof(*q->dat));
137
-		}
138
-		q->dat[q->n++] = (m[0xb] << 8) + m[0xc];
139
-		q->dat[q->n++] = (m[0xd] << 8) + b1;
140
-	} else if(b0 == 0xf && q != NULL) {
141
-		q->ends = 1;
86
+	note->playing = 1;
87
+	for(i = 0; i < 2; ++i) {
88
+		note->volume[i] = 0xf & (volume >> 4 * (1 - i));
89
+		note->wv[i].count = note->wv[i].period = 0;
90
+		note->wv[i].end_value = 0;
91
+		note->wv[i].queue.n = note->wv[i].queue.i = 0;
92
+		note->wv[i].queue.finishes = 0;
142 93
 	}
143
-	return b1;
144
-}
145
-
146
-int
147
-initapu(Uxn *u, Uint8 id)
148
-{
149
-	SDL_AudioSpec as;
150
-	SDL_zero(as);
151
-	as.freq = SAMPLE_FREQUENCY;
152
-	as.format = AUDIO_S16;
153
-	as.channels = 2;
154
-	as.callback = play_all_notes;
155
-	as.samples = 2048;
156
-	as.userdata = u;
157
-	audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
158
-	if(!audio_id)
159
-		return error("Audio", SDL_GetError());
160
-	id_addr = portuxn(u, id, "audio", audio_poke)->addr + 0xa;
161
-	SDL_PauseAudioDevice(audio_id, 0);
162
-	return 1;
94
+	note->wv[0].vector = wave_vector;
95
+	note->wv[0].advance = note_advances[pitch % 12] >> (8 - pitch / 12);
96
+	note->wv[1].vector = envelope_vector;
97
+	note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY;
163 98
 }
164 99
new file mode 100644
... ...
@@ -0,0 +1,46 @@
1
+/*
2
+Copyright (c) 2021 Devine Lu Linvega
3
+Copyright (c) 2021 Andrew Alderwick
4
+
5
+Permission to use, copy, modify, and distribute this software for any
6
+purpose with or without fee is hereby granted, provided that the above
7
+copyright notice and this permission notice appear in all copies.
8
+
9
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+WITH REGARD TO THIS SOFTWARE.
11
+*/
12
+
13
+typedef unsigned char Uint8;
14
+typedef signed char Sint8;
15
+typedef unsigned short Uint16;
16
+typedef signed short Sint16;
17
+typedef unsigned int Uint32;
18
+typedef signed int Sint32;
19
+
20
+#define SAMPLE_FREQUENCY 48000
21
+
22
+typedef struct {
23
+	Uint16 *dat;
24
+	Uint8 i, n, sz, finishes;
25
+} Queue;
26
+
27
+typedef struct {
28
+	Uint32 count, advance, period;
29
+	Uint16 vector;
30
+	Sint16 start_value, end_value;
31
+	Queue queue;
32
+} WaveformGenerator;
33
+
34
+typedef struct {
35
+	WaveformGenerator wv[2];
36
+	Sint8 volume[2], playing;
37
+} Note;
38
+
39
+typedef struct {
40
+    Queue *queue;
41
+    Note *notes;
42
+    int n_notes;
43
+} Apu;
44
+
45
+void apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples);
46
+void apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch);
... ...
@@ -15,15 +15,18 @@ WITH REGARD TO THIS SOFTWARE.
15 15
 
16 16
 #include "uxn.h"
17 17
 #include "ppu.h"
18
+#include "apu.h"
18 19
 
19 20
 int initapu(Uxn *u, Uint8 id);
20 21
 
21
-SDL_AudioDeviceID audio_id;
22
+static SDL_AudioDeviceID audio_id;
22 23
 static SDL_Window *gWindow;
23 24
 static SDL_Renderer *gRenderer;
24 25
 static SDL_Texture *gTexture;
25 26
 static Ppu ppu;
27
+static Apu apu;
26 28
 static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl;
29
+Device *devapu;
27 30
 
28 31
 #pragma mark - Helpers
29 32
 
... ...
@@ -41,6 +44,12 @@ error(char *msg, const char *err)
41 44
 	return 0;
42 45
 }
43 46
 
47
+static void
48
+audio_callback(void *u, Uint8 *stream, int len)
49
+{
50
+	apu_render(&apu, (Uxn *)u, (Sint16 *)stream, len >> 2);
51
+}
52
+
44 53
 void
45 54
 redraw(Uint32 *dst, Uxn *u)
46 55
 {
... ...
@@ -86,8 +95,9 @@ quit(void)
86 95
 }
87 96
 
88 97
 int
89
-init(void)
98
+init(Uxn *u)
90 99
 {
100
+	SDL_AudioSpec as;
91 101
 	if(!initppu(&ppu, 48, 32, 16))
92 102
 		return error("PPU", "Init failure");
93 103
 	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
... ...
@@ -103,6 +113,17 @@ init(void)
103 113
 		return error("Texture", SDL_GetError());
104 114
 	SDL_StartTextInput();
105 115
 	SDL_ShowCursor(SDL_DISABLE);
116
+	SDL_zero(as);
117
+	as.freq = SAMPLE_FREQUENCY;
118
+	as.format = AUDIO_S16;
119
+	as.channels = 2;
120
+	as.callback = audio_callback;
121
+	as.samples = 2048;
122
+	as.userdata = u;
123
+	audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
124
+	if(!audio_id)
125
+		return error("Audio", SDL_GetError());
126
+	SDL_PauseAudioDevice(audio_id, 0);
106 127
 	return 1;
107 128
 }
108 129
 
... ...
@@ -258,6 +279,27 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
258 279
 	return b1;
259 280
 }
260 281
 
282
+static Uint8
283
+audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
284
+{
285
+	Uint8 *m = u->ram.dat + ptr;
286
+	if(b0 == 0xa) {
287
+		if(b1 >= apu.n_notes) apu.notes = SDL_realloc(apu.notes, (b1 + 1) * sizeof(Note));
288
+		while(b1 >= apu.n_notes) SDL_zero(apu.notes[apu.n_notes++]);
289
+		apu_play_note(&apu.notes[b1], (m[0x0] << 8) + m[0x1], (m[0x2] << 8) + m[0x3], m[0x8], m[0x9]);
290
+	} else if(b0 == 0xe && apu.queue != NULL) {
291
+		if(apu.queue->n == apu.queue->sz) {
292
+			apu.queue->sz = apu.queue->sz < 4 ? 4 : apu.queue->sz * 2;
293
+			apu.queue->dat = SDL_realloc(apu.queue->dat, apu.queue->sz * sizeof(*apu.queue->dat));
294
+		}
295
+		apu.queue->dat[apu.queue->n++] = (m[0xb] << 8) + m[0xc];
296
+		apu.queue->dat[apu.queue->n++] = (m[0xd] << 8) + b1;
297
+	} else if(b0 == 0xf && apu.queue != NULL) {
298
+		apu.queue->finishes = 1;
299
+	}
300
+	return b1;
301
+}
302
+
261 303
 Uint8
262 304
 midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
263 305
 {
... ...
@@ -367,7 +409,7 @@ main(int argc, char **argv)
367 409
 		return error("Boot", "Failed");
368 410
 	if(!loaduxn(&u, argv[1]))
369 411
 		return error("Load", "Failed");
370
-	if(!init())
412
+	if(!init(&u))
371 413
 		return error("Init", "Failed");
372 414
 
373 415
 	devsystem = portuxn(&u, 0x00, "system", system_poke);
... ...
@@ -378,8 +420,7 @@ main(int argc, char **argv)
378 420
 	devkey = portuxn(&u, 0x05, "key", ppnil);
379 421
 	devmouse = portuxn(&u, 0x06, "mouse", ppnil);
380 422
 	portuxn(&u, 0x07, "file", file_poke);
381
-	if(!initapu(&u, 0x08))
382
-		return 1;
423
+	devapu = portuxn(&u, 0x08, "audio", audio_poke);
383 424
 	portuxn(&u, 0x09, "midi", ppnil);
384 425
 	portuxn(&u, 0x0a, "datetime", datetime_poke);
385 426
 	portuxn(&u, 0x0b, "---", ppnil);