diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-25 19:02:20 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-25 19:02:20 +0200 |
commit | 5497bf0faae483daf44b4909cee81da48fdc86be (patch) | |
tree | 66f7263deab6f3d90a009967d520e671d0ee7eb4 /src | |
parent | c4041799fc3c6e6cdcce6121ea2c121fd504a3b6 (diff) | |
download | uxngba-5497bf0faae483daf44b4909cee81da48fdc86be.tar.gz uxngba-5497bf0faae483daf44b4909cee81da48fdc86be.zip |
[WIP] Add prototype APU
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 148 | ||||
-rw-r--r-- | src/uxn/devices/apu.c | 241 |
2 files changed, 294 insertions, 95 deletions
@@ -20,6 +20,7 @@ WITH REGARD TO THIS SOFTWARE. | |||
20 | #include "uxn/uxn.c" | 20 | #include "uxn/uxn.c" |
21 | #include "uxn/devices/ppu.h" | 21 | #include "uxn/devices/ppu.h" |
22 | #include "uxn/devices/ppu.c" | 22 | #include "uxn/devices/ppu.c" |
23 | #include "uxn/devices/apu.c" | ||
23 | 24 | ||
24 | #include "text.h" | 25 | #include "text.h" |
25 | 26 | ||
@@ -56,6 +57,7 @@ static Ppu ppu; | |||
56 | static Device *devscreen; | 57 | static Device *devscreen; |
57 | static Device *devctrl; | 58 | static Device *devctrl; |
58 | static Device *devmouse; | 59 | static Device *devmouse; |
60 | static Device *devaudio; | ||
59 | 61 | ||
60 | static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2}; | 62 | static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2}; |
61 | 63 | ||
@@ -110,6 +112,36 @@ screen_talk(Device *d, u8 b0, u8 w) { | |||
110 | } | 112 | } |
111 | } | 113 | } |
112 | 114 | ||
115 | static void | ||
116 | audio_talk(Device *d, u8 b0, u8 w) { | ||
117 | AudioChannel *c = &apu.chan_0; | ||
118 | if(!w) { | ||
119 | if(b0 == 0x2) { | ||
120 | mempoke16(d->dat, 0x2, c->position); | ||
121 | } else if(b0 == 0x4) { | ||
122 | // d->dat[0x4] = apu_get_vu(c); | ||
123 | } | ||
124 | } else if(b0 == 0xf) { | ||
125 | // SDL_LockAudioDevice(audio_id); | ||
126 | c->n_samples = mempeek16(d->dat, 0xa); | ||
127 | c->samples = &d->mem[mempeek16(d->dat, 0xc)]; | ||
128 | // Transform the samples from u8 to s8. | ||
129 | // for (size_t i = 0; i < c->n_samples; ++i) { | ||
130 | // c->samples[i] = c->samples[i] + 0x80; | ||
131 | // } | ||
132 | // c->volume[0] = d->dat[0xe] >> 4; | ||
133 | // c->volume[1] = d->dat[0xe] & 0xf; | ||
134 | c->loop = !(d->dat[0xf] & 0x80); | ||
135 | c->pitch = d->dat[0xf] & 0x7f; | ||
136 | init_sound(c); | ||
137 | reset_sound(c); | ||
138 | txt_printf("note: %d \n", c->pitch); | ||
139 | // apu_start(c, mempeek16(d->dat, 0x8), d->dat[0xf] & 0x7f); | ||
140 | // SDL_UnlockAudioDevice(audio_id); | ||
141 | } | ||
142 | // txt_printf("AUDIO TALK: %d\n", d); | ||
143 | } | ||
144 | |||
113 | void | 145 | void |
114 | datetime_talk(Device *d, u8 b0, u8 w) { | 146 | datetime_talk(Device *d, u8 b0, u8 w) { |
115 | (void)d; | 147 | (void)d; |
@@ -155,7 +187,7 @@ init_uxn(Uxn *u) { | |||
155 | portuxn(u, 0x0, "system", system_talk); | 187 | portuxn(u, 0x0, "system", system_talk); |
156 | portuxn(u, 0x1, "console", console_talk); | 188 | portuxn(u, 0x1, "console", console_talk); |
157 | devscreen = portuxn(u, 0x2, "screen", screen_talk); | 189 | devscreen = portuxn(u, 0x2, "screen", screen_talk); |
158 | portuxn(u, 0x3, "---", nil_talk); | 190 | devaudio = portuxn(u, 0x3, "audio0", audio_talk); |
159 | portuxn(u, 0x4, "---", nil_talk); | 191 | portuxn(u, 0x4, "---", nil_talk); |
160 | portuxn(u, 0x5, "---", nil_talk); | 192 | portuxn(u, 0x5, "---", nil_talk); |
161 | portuxn(u, 0x6, "---", nil_talk); | 193 | portuxn(u, 0x6, "---", nil_talk); |
@@ -344,75 +376,6 @@ static Uxn u; | |||
344 | EWRAM_BSS | 376 | EWRAM_BSS |
345 | static u8 umem[65536]; | 377 | static u8 umem[65536]; |
346 | 378 | ||
347 | #include "kick.c" | ||
348 | |||
349 | typedef struct AudioChannel { | ||
350 | u32 *samples; | ||
351 | u32 n_samples; | ||
352 | u16 sampling_freq; | ||
353 | bool loop; | ||
354 | // TODO: u16 adsr; // attack, decay, sustain, release | ||
355 | // TODO: u8 pitch; // Bit 8 is the "loop" bit | ||
356 | // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) | ||
357 | } AudioChannel; | ||
358 | |||
359 | typedef struct APU { | ||
360 | AudioChannel chan_0; | ||
361 | // u32 *samples_1; | ||
362 | // u32 *samples_2; | ||
363 | // u32 *samples_3; | ||
364 | } APU; | ||
365 | |||
366 | |||
367 | static APU apu = {0}; | ||
368 | |||
369 | void | ||
370 | reset_sound(AudioChannel *chan) { | ||
371 | TIMER_CTRL_0 = 0; | ||
372 | TIMER_CTRL_1 = 0; | ||
373 | DMA_CTRL(1) = 0; | ||
374 | |||
375 | // Set max volume, left-right sound, fifo reset and use timer 0 for | ||
376 | // DirectSound A. | ||
377 | SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A | ||
378 | | SOUND_DSOUND_LEFT_A | ||
379 | | SOUND_DSOUND_RIGHT_A | ||
380 | | SOUND_DSOUND_RESET_A; | ||
381 | |||
382 | // Prepare DMA copy. | ||
383 | dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, | ||
384 | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); | ||
385 | |||
386 | // Timer 1 used to stop playing samples. | ||
387 | u32 sample_duration = chan->n_samples; | ||
388 | TIMER_DATA_1 = 0xFFFF - sample_duration; | ||
389 | TIMER_CTRL_1 = TIMER_CTRL_IRQ | ||
390 | | TIMER_CTRL_ENABLE | ||
391 | | TIMER_CTRL_CASCADE; | ||
392 | |||
393 | // Timer 0 used to stop sample playing. | ||
394 | TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq; | ||
395 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; | ||
396 | } | ||
397 | |||
398 | void | ||
399 | irs_stop_sample(void) { | ||
400 | if (apu.chan_0.loop) { | ||
401 | reset_sound(&apu.chan_0); | ||
402 | } else { | ||
403 | TIMER_CTRL_0 = 0; | ||
404 | DMA_CTRL(1) = 0; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | void | ||
409 | init_sound(AudioChannel *chan) { | ||
410 | chan->samples = voiceraw; | ||
411 | chan->n_samples = LEN(voiceraw); | ||
412 | chan->sampling_freq = 44100; | ||
413 | chan->loop = true; | ||
414 | } | ||
415 | |||
416 | int main(void) { | 379 | int main(void) { |
417 | // Initialize filesystem. | 380 | // Initialize filesystem. |
418 | fs_init(); | 381 | fs_init(); |
@@ -431,39 +394,34 @@ int main(void) { | |||
431 | txt_init(1, TEXT_LAYER); | 394 | txt_init(1, TEXT_LAYER); |
432 | txt_position(0,0); | 395 | txt_position(0,0); |
433 | 396 | ||
434 | txt_printf("VOICE LOADED: %lu\n", LEN(voiceraw)); | 397 | // init_sound(&apu.chan_0); |
435 | 398 | // reset_sound(&apu.chan_0); | |
436 | // Enable sound. | ||
437 | SOUND_STATUS = SOUND_ENABLE; | ||
438 | |||
439 | init_sound(&apu.chan_0); | ||
440 | reset_sound(&apu.chan_0); | ||
441 | 399 | ||
442 | // Main loop. | 400 | // Main loop. |
443 | // int frame_counter = 0; | 401 | int frame_counter = 0; |
444 | // evaluxn(&u, 0x0100); | 402 | evaluxn(&u, 0x0100); |
445 | // u32 flip_cycles = 0; | 403 | u32 flip_cycles = 0; |
446 | while(true) { | 404 | while(true) { |
447 | bios_vblank_wait(); | 405 | bios_vblank_wait(); |
448 | // profile_start(); | 406 | profile_start(); |
449 | // handle_input(&u); | 407 | handle_input(&u); |
450 | // u32 input_cycles = profile_stop(); | 408 | u32 input_cycles = profile_stop(); |
451 | // profile_start(); | 409 | profile_start(); |
452 | // evaluxn(&u, mempeek16(devscreen->dat, 0)); | 410 | evaluxn(&u, mempeek16(devscreen->dat, 0)); |
453 | // u32 eval_cycles = profile_stop(); | 411 | u32 eval_cycles = profile_stop(); |
454 | // txt_position(0, 8); | 412 | txt_position(0, 8); |
455 | // txt_printf("INPUT: %lu \n", input_cycles); | 413 | // txt_printf("INPUT: %lu \n", input_cycles); |
456 | // txt_printf("EVAL: %lu \n", eval_cycles); | 414 | // txt_printf("EVAL: %lu \n", eval_cycles); |
457 | // txt_printf("FLIP: %lu \n", flip_cycles); | 415 | // txt_printf("FLIP: %lu \n", flip_cycles); |
458 | // profile_start(); | 416 | profile_start(); |
459 | flipbuf(&ppu); | 417 | flipbuf(&ppu); |
460 | // flip_cycles = profile_stop(); | 418 | flip_cycles = profile_stop(); |
461 | // frame_counter++; | 419 | frame_counter++; |
462 | poll_keys(); | 420 | // if (frame_counter == 120) { |
463 | if (key_tap(KEY_B)) { | 421 | // init_sound(&apu.chan_0); |
464 | txt_printf("TAP B\n"); | 422 | // reset_sound(&apu.chan_0); |
465 | reset_sound(&apu.chan_0); | 423 | // frame_counter = 0; |
466 | } | 424 | // } |
467 | } | 425 | } |
468 | 426 | ||
469 | return 0; | 427 | return 0; |
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c new file mode 100644 index 0000000..fbe7c3d --- /dev/null +++ b/src/uxn/devices/apu.c | |||
@@ -0,0 +1,241 @@ | |||
1 | #include "samples.c" | ||
2 | |||
3 | #define SAMPLE_FREQUENCY 44100 | ||
4 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) | ||
5 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) | ||
6 | |||
7 | typedef struct AudioChannel { | ||
8 | u8 *samples; | ||
9 | u16 n_samples; | ||
10 | u16 position; | ||
11 | u16 sampling_freq; | ||
12 | u8 pitch; | ||
13 | bool loop; | ||
14 | // TODO: u16 adsr; // attack, decay, sustain, release | ||
15 | // TODO: u8 pitch; // Bit 8 is the "loop" bit | ||
16 | // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) | ||
17 | // u8 *addr; | ||
18 | // u32 count, advance, period, age, a, d, s, r; | ||
19 | // s8 volume[2]; | ||
20 | // u8 pitch, repeat; | ||
21 | } AudioChannel; | ||
22 | |||
23 | typedef struct APU { | ||
24 | AudioChannel chan_0; | ||
25 | // u32 *samples_1; | ||
26 | // u32 *samples_2; | ||
27 | // u32 *samples_3; | ||
28 | } APU; | ||
29 | |||
30 | static APU apu; | ||
31 | |||
32 | |||
33 | static u16 pitch_table[] = { | ||
34 | 12173, | ||
35 | 11490, | ||
36 | 10845, | ||
37 | 10237, | ||
38 | 9662, | ||
39 | 9120, | ||
40 | 8608, | ||
41 | 8125, | ||
42 | 7669, | ||
43 | 7238, | ||
44 | 6832, | ||
45 | 6448, | ||
46 | 6086, | ||
47 | 5745, | ||
48 | 5422, | ||
49 | 5118, | ||
50 | 4831, | ||
51 | 4560, | ||
52 | 4304, | ||
53 | 4062, | ||
54 | 3834, | ||
55 | 3619, | ||
56 | 3416, | ||
57 | 3224, | ||
58 | 3043, | ||
59 | 2872, | ||
60 | 2711, | ||
61 | 2559, | ||
62 | 2415, | ||
63 | 2280, | ||
64 | 2152, | ||
65 | 2031, | ||
66 | 1917, | ||
67 | 1809, | ||
68 | 1708, | ||
69 | 1612, | ||
70 | 1521, | ||
71 | 1436, | ||
72 | 1355, | ||
73 | 1279, | ||
74 | 1207, | ||
75 | 1140, | ||
76 | 1076, | ||
77 | 1015, | ||
78 | 958, | ||
79 | 904, | ||
80 | 854, | ||
81 | 806, | ||
82 | 760, | ||
83 | 718, | ||
84 | 677, | ||
85 | 639, | ||
86 | 603, | ||
87 | 570, | ||
88 | 538, | ||
89 | 507, | ||
90 | 479, | ||
91 | 452, | ||
92 | 427, | ||
93 | 403, | ||
94 | 380, | ||
95 | 359, | ||
96 | 338, | ||
97 | 319, | ||
98 | 301, | ||
99 | 285, | ||
100 | 269, | ||
101 | 253, | ||
102 | 239, | ||
103 | 226, | ||
104 | 213, | ||
105 | 201, | ||
106 | 190, | ||
107 | 179, | ||
108 | 169, | ||
109 | 159, | ||
110 | 150, | ||
111 | 142, | ||
112 | 134, | ||
113 | 126, | ||
114 | 119, | ||
115 | 113, | ||
116 | 106, | ||
117 | 100, | ||
118 | 95, | ||
119 | 89, | ||
120 | 84, | ||
121 | 79, | ||
122 | 75, | ||
123 | 71, | ||
124 | 67, | ||
125 | 63, | ||
126 | 59, | ||
127 | 56, | ||
128 | 53, | ||
129 | 50, | ||
130 | 47, | ||
131 | 44, | ||
132 | 42, | ||
133 | 39, | ||
134 | 37, | ||
135 | 35, | ||
136 | 33, | ||
137 | 31, | ||
138 | 29, | ||
139 | 28, | ||
140 | 26, | ||
141 | 25, | ||
142 | 23, | ||
143 | 22, | ||
144 | 21, | ||
145 | 19, | ||
146 | 18, | ||
147 | 17, | ||
148 | 16, | ||
149 | 15, | ||
150 | 14, | ||
151 | 14, | ||
152 | 13, | ||
153 | 12, | ||
154 | 0, | ||
155 | }; | ||
156 | |||
157 | void | ||
158 | reset_sound(AudioChannel *chan) { | ||
159 | TIMER_CTRL_0 = 0; | ||
160 | TIMER_CTRL_1 = 0; | ||
161 | DMA_CTRL(1) = 0; | ||
162 | if (chan->pitch >= 108 || chan->n_samples == 0) { | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | // Set max volume, left-right sound, fifo reset and use timer 0 for | ||
167 | // DirectSound A. | ||
168 | SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A | ||
169 | | SOUND_DSOUND_LEFT_A | ||
170 | | SOUND_DSOUND_RIGHT_A | ||
171 | | SOUND_DSOUND_RESET_A; | ||
172 | |||
173 | // Prepare DMA copy. | ||
174 | dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, | ||
175 | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); | ||
176 | |||
177 | // Timer 1 used to stop playing samples. | ||
178 | u32 sample_duration = chan->n_samples; | ||
179 | TIMER_DATA_1 = 0xFFFF - sample_duration; | ||
180 | TIMER_CTRL_1 = TIMER_CTRL_IRQ | ||
181 | | TIMER_CTRL_ENABLE | ||
182 | | TIMER_CTRL_CASCADE; | ||
183 | |||
184 | // Timer 0 used to stop sample playing. | ||
185 | // TIMER_DATA_0 = 0xFFFF - sound_freq[chan->pitch]; | ||
186 | // TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq / chan->pitch; | ||
187 | TIMER_DATA_0 = 0xFFFF - pitch_table[chan->pitch]; | ||
188 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; | ||
189 | } | ||
190 | |||
191 | u8 square_wave[] = { | ||
192 | 0x00 + 0x80, 0xFF + 0x80 | ||
193 | }; | ||
194 | #include "text.h" | ||
195 | |||
196 | |||
197 | void | ||
198 | init_sound(AudioChannel *chan) { | ||
199 | // Enable sound. | ||
200 | SOUND_STATUS = SOUND_ENABLE; | ||
201 | |||
202 | // chan->samples = &square_wave; | ||
203 | // chan->n_samples = 2; | ||
204 | // chan->samples = &samples; | ||
205 | // chan->n_samples = LEN(samples); | ||
206 | // chan->pitch++; | ||
207 | // chan->pitch = chan->pitch == LEN(pitch_table) - 1 ? 0 : chan->pitch + 1; | ||
208 | // chan->loop = true; | ||
209 | // chan->sampling_freq = pitch_table[chan->pitch]; | ||
210 | // txt_printf("RATE: %lu", pitch_table[chan->pitch]) | ||
211 | } | ||
212 | |||
213 | void | ||
214 | irs_stop_sample(void) { | ||
215 | if (apu.chan_0.loop) { | ||
216 | reset_sound(&apu.chan_0); | ||
217 | } else { | ||
218 | TIMER_CTRL_0 = 0; | ||
219 | DMA_CTRL(1) = 0; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | // void | ||
224 | // apu_start(Apu *c, u16 adsr, u8 pitch) { | ||
225 | // // if(pitch < 108 && c->len) | ||
226 | // // c->advance = advances[pitch % 12] >> (8 - pitch / 12); | ||
227 | // // else { | ||
228 | // // c->advance = 0; | ||
229 | // // return; | ||
230 | // // } | ||
231 | // // c->a = ADSR_STEP * (adsr >> 12); | ||
232 | // // c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; | ||
233 | // // c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; | ||
234 | // // c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; | ||
235 | // // c->age = 0; | ||
236 | // // c->i = 0; | ||
237 | // // if(c->len <= 0x100) /* single cycle mode */ | ||
238 | // // c->period = NOTE_PERIOD * 337 / 2 / c->len; | ||
239 | // // else /* sample repeat mode */ | ||
240 | // // c->period = NOTE_PERIOD; | ||
241 | // } | ||