aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-25 19:02:20 +0200
committerBad Diode <bd@badd10de.dev>2021-05-25 19:02:20 +0200
commit5497bf0faae483daf44b4909cee81da48fdc86be (patch)
tree66f7263deab6f3d90a009967d520e671d0ee7eb4
parentc4041799fc3c6e6cdcce6121ea2c121fd504a3b6 (diff)
downloaduxngba-5497bf0faae483daf44b4909cee81da48fdc86be.tar.gz
uxngba-5497bf0faae483daf44b4909cee81da48fdc86be.zip
[WIP] Add prototype APU
-rw-r--r--roms/audio.rombin437 -> 435 bytes
-rw-r--r--src/main.c148
-rw-r--r--src/uxn/devices/apu.c241
3 files changed, 294 insertions, 95 deletions
diff --git a/roms/audio.rom b/roms/audio.rom
index 462204d..b0bae38 100644
--- a/roms/audio.rom
+++ b/roms/audio.rom
Binary files differ
diff --git a/src/main.c b/src/main.c
index 0862ca5..fc7dad1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
56static Device *devscreen; 57static Device *devscreen;
57static Device *devctrl; 58static Device *devctrl;
58static Device *devmouse; 59static Device *devmouse;
60static Device *devaudio;
59 61
60static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2}; 62static 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
115static void
116audio_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
113void 145void
114datetime_talk(Device *d, u8 b0, u8 w) { 146datetime_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;
344EWRAM_BSS 376EWRAM_BSS
345static u8 umem[65536]; 377static u8 umem[65536];
346 378
347#include "kick.c"
348
349typedef 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
359typedef struct APU {
360 AudioChannel chan_0;
361 // u32 *samples_1;
362 // u32 *samples_2;
363 // u32 *samples_3;
364} APU;
365
366
367static APU apu = {0};
368
369void
370reset_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
398void
399irs_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
408void
409init_sound(AudioChannel *chan) {
410 chan->samples = voiceraw;
411 chan->n_samples = LEN(voiceraw);
412 chan->sampling_freq = 44100;
413 chan->loop = true;
414}
415
416int main(void) { 379int 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
7typedef 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
23typedef struct APU {
24 AudioChannel chan_0;
25 // u32 *samples_1;
26 // u32 *samples_2;
27 // u32 *samples_3;
28} APU;
29
30static APU apu;
31
32
33static 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
157void
158reset_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
191u8 square_wave[] = {
192 0x00 + 0x80, 0xFF + 0x80
193};
194#include "text.h"
195
196
197void
198init_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
213void
214irs_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// }