aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-26 15:08:23 +0200
committerBad Diode <bd@badd10de.dev>2021-05-26 15:08:23 +0200
commite6235422f3746683e679abd353d9385835c8187f (patch)
tree6d83a3520a5ab10390d52487b90a92f32a865e85
parent2a7893e230c521bc0f8aed7c4307ccf4cacaa767 (diff)
downloaduxngba-e6235422f3746683e679abd353d9385835c8187f.tar.gz
uxngba-e6235422f3746683e679abd353d9385835c8187f.zip
[WIP] Prototype of audio mixer following deku tutorial
-rw-r--r--src/main.c41
-rw-r--r--src/uxn/devices/apu.c289
2 files changed, 181 insertions, 149 deletions
diff --git a/src/main.c b/src/main.c
index a6fb761..6e7d3e5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -184,7 +184,7 @@ init_uxn(Uxn *u) {
184 initppu(&ppu, 30, 20, 0); 184 initppu(&ppu, 30, 20, 0);
185 185
186 // Enable sound. 186 // Enable sound.
187 SOUND_STATUS = SOUND_ENABLE; 187 init_sound();
188 188
189 // Copy rom to VM. 189 // Copy rom to VM.
190 memcpy(u->ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); 190 memcpy(u->ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom));
@@ -388,8 +388,8 @@ int main(void) {
388 388
389 // Register interrupts. 389 // Register interrupts.
390 irq_init(); 390 irq_init();
391 irs_set(IRQ_VBLANK, irs_stub); 391 irs_set(IRQ_VBLANK, sound_vsync);
392 irs_set(IRQ_TIMER_1, irs_stop_sample); 392 // irs_set(IRQ_TIMER_1, irs_stop_sample);
393 393
394 // Initialize VM. 394 // Initialize VM.
395 memset(&u, 0, sizeof(u)); 395 memset(&u, 0, sizeof(u));
@@ -400,34 +400,43 @@ int main(void) {
400 txt_init(1, TEXT_LAYER); 400 txt_init(1, TEXT_LAYER);
401 txt_position(0,0); 401 txt_position(0,0);
402 402
403 // init_sound(&apu.chan_0); 403 init_sound();
404 // reset_sound(&apu.chan_0); 404 // reset_sound(&apu.chan_0);
405 // txt_printf("DING\n");
406 // sound_mix();
405 407
406 // Main loop. 408 // Main loop.
407 int frame_counter = 0; 409 int frame_counter = 0;
408 evaluxn(&u, 0x0100); 410 // evaluxn(&u, 0x0100);
409 u32 flip_cycles = 0; 411 // u32 flip_cycles = 0;
410 while(true) { 412 while(true) {
411 bios_vblank_wait(); 413 bios_vblank_wait();
412 profile_start(); 414 // profile_start();
413 handle_input(&u); 415 // handle_input(&u);
414 u32 input_cycles = profile_stop(); 416 // u32 input_cycles = profile_stop();
415 profile_start(); 417 // profile_start();
416 evaluxn(&u, mempeek16(devscreen->dat, 0)); 418 // evaluxn(&u, mempeek16(devscreen->dat, 0));
417 u32 eval_cycles = profile_stop(); 419 // u32 eval_cycles = profile_stop();
418 txt_position(0, 8); 420 // txt_position(0, 8);
419 // txt_printf("INPUT: %lu \n", input_cycles); 421 // txt_printf("INPUT: %lu \n", input_cycles);
420 // txt_printf("EVAL: %lu \n", eval_cycles); 422 // txt_printf("EVAL: %lu \n", eval_cycles);
421 // txt_printf("FLIP: %lu \n", flip_cycles); 423 // txt_printf("FLIP: %lu \n", flip_cycles);
422 profile_start(); 424 // profile_start();
423 flipbuf(&ppu); 425 // flip_cycles = profile_stop();
424 flip_cycles = profile_stop();
425 frame_counter++; 426 frame_counter++;
426 // if (frame_counter == 120) { 427 // if (frame_counter == 120) {
427 // init_sound(&apu.chan_0); 428 // init_sound(&apu.chan_0);
428 // reset_sound(&apu.chan_0); 429 // reset_sound(&apu.chan_0);
429 // frame_counter = 0; 430 // frame_counter = 0;
430 // } 431 // }
432 profile_start();
433 sound_mix();
434 u32 mix_cycles = profile_stop();
435
436 txt_position(0, 18);
437 txt_printf("MIX CYCLES: %lu \n", mix_cycles);
438 txt_printf("FRAME: %lu \n", frame_counter);
439 flipbuf(&ppu);
431 } 440 }
432 441
433 return 0; 442 return 0;
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c
index b2ccc18..ba9fe88 100644
--- a/src/uxn/devices/apu.c
+++ b/src/uxn/devices/apu.c
@@ -4,6 +4,7 @@
4#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) 4#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025)
5#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) 5#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
6 6
7// FIXME: We have two structs for now, needs fixing.
7typedef struct AudioChannel { 8typedef struct AudioChannel {
8 u8 *samples; 9 u8 *samples;
9 u16 n_samples; 10 u16 n_samples;
@@ -31,127 +32,22 @@ static APU apu;
31 32
32 33
33static u16 pitch_table[] = { 34static u16 pitch_table[] = {
34 12173, 35 12173, 11490, 10845, 10237, 9662, 9120, 8608, 8125,
35 11490, 36 7669, 7238, 6832, 6448, 6086, 5745, 5422, 5118,
36 10845, 37 4831, 4560, 4304, 4062, 3834, 3619, 3416, 3224,
37 10237, 38 3043, 2872, 2711, 2559, 2415, 2280, 2152, 2031,
38 9662, 39 1917, 1809, 1708, 1612, 1521, 1436, 1355, 1279,
39 9120, 40 1207, 1140, 1076, 1015, 958, 904, 854, 806,
40 8608, 41 760, 718, 677, 639, 603, 570, 538, 507,
41 8125, 42 479, 452, 427, 403, 380, 359, 338, 319,
42 7669, 43 301, 285, 269, 253, 239, 226, 213, 201,
43 7238, 44 190, 179, 169, 159, 150, 142, 134, 126,
44 6832, 45 119, 113, 106, 100, 95, 89, 84, 79,
45 6448, 46 75, 71, 67, 63, 59, 56, 53, 50,
46 6086, 47 47, 44, 42, 39, 37, 35, 33, 31,
47 5745, 48 29, 28, 26, 25, 23, 22, 21, 19,
48 5422, 49 18, 17, 16, 15, 14, 14, 13, 12,
49 5118, 50 2,
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}; 51};
156 52
157void 53void
@@ -188,23 +84,150 @@ reset_sound(AudioChannel *chan) {
188 TIMER_CTRL_0 = TIMER_CTRL_ENABLE; 84 TIMER_CTRL_0 = TIMER_CTRL_ENABLE;
189} 85}
190 86
191u8 square_wave[] = { 87s8 square_wave[] = {
192 0x00 + 0x80, 0xFF + 0x80 88 (s8)0x00 + (s8)0x80, (s8)0xFF + (s8)0x80
193}; 89};
90
194#include "text.h" 91#include "text.h"
195 92
196 93
94
95//
96// REG_TM0D frequency buffer size
97// | | |
98// V V V
99//
100// Timer = 62610 = 65536 - (16777216 / 5734), buf = 96
101// Timer = 63940 = 65536 - (16777216 / 10512), buf = 176
102// Timer = 64282 = 65536 - (16777216 / 13379), buf = 224
103// Timer = 64612 = 65536 - (16777216 / 18157), buf = 304
104// Timer = 64738 = 65536 - (16777216 / 21024), buf = 352
105// Timer = 64909 = 65536 - (16777216 / 26758), buf = 448
106// Timer = 65004 = 65536 - (16777216 / 31536), buf = 528
107// Timer = 65073 = 65536 - (16777216 / 36314), buf = 608
108// Timer = 65118 = 65536 - (16777216 / 40137), buf = 672
109// Timer = 65137 = 65536 - (16777216 / 42048), buf = 704
110//
111// Source: https://deku.gbadev.org/program/sound1.html
112#define AUDIO_FREQ 18157
113#define AUDIO_BUF_LEN 304
114#define AUDIO_TIMER 64612
115
116typedef struct Audio {
117 s8 mix_buffer[AUDIO_BUF_LEN * 2];
118 s8 *current_buffer;
119 u8 active_buffer;
120} Audio;
121
122typedef struct Channel {
123 // Pointer to the raw data in the ROM.
124 s8 *data;
125 // Current position in the data (20.12 fixed-point).
126 u32 pos;
127 // Increment (20.12 fixed-point).
128 u32 inc;
129 // Volume (0-64, 1.6 fixed-point).
130 u32 vol;
131 // Sound length (20.12 fixed-point).
132 u32 length;
133 // Length of looped portion (20.12 fixed-point, 0 to disable looping).
134 u32 loop_length;
135} Channel;
136
137static Audio audio;
138
139#define POLYPHONY 4
140static Channel channels[POLYPHONY];
141
197void 142void
198init_sound(AudioChannel *chan) { 143init_sound(void) {
199 // chan->samples = &square_wave; 144 // Initialize audio buffers/channels.
200 // chan->n_samples = 2; 145 audio = (Audio){0};
201 // chan->samples = &samples; 146 for (size_t i = 0; i < POLYPHONY; ++i) {
202 // chan->n_samples = LEN(samples); 147 channels[i] = (Channel){0};
203 // chan->pitch++; 148 }
204 // chan->pitch = chan->pitch == LEN(pitch_table) - 1 ? 0 : chan->pitch + 1; 149
205 // chan->loop = true; 150 // DEBUG: testing channel 0 with square wave
206 // chan->sampling_freq = pitch_table[chan->pitch]; 151 channels[0].data = samples;
207 // txt_printf("RATE: %lu", pitch_table[chan->pitch]) 152 channels[0].inc = (8363 << 12) / AUDIO_FREQ;
153 channels[0].length = (LEN(samples) - 1) << 12;
154 channels[0].pos = 0;
155 channels[0].vol = 64;
156 channels[0].loop_length = channels[0].length;
157
158 // Enable the sound chip.
159 SOUND_STATUS = SOUND_ENABLE;
160
161 // Set max volume, left-right sound, fifo reset and use timer 0 for
162 // DirectSound A.
163 SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A
164 | SOUND_DSOUND_LEFT_A
165 | SOUND_DSOUND_RIGHT_A
166 | SOUND_DSOUND_RESET_A;
167
168 // TODO: No pitch selection yet.
169 TIMER_DATA_0 = AUDIO_TIMER;
170 TIMER_CTRL_0 = TIMER_CTRL_ENABLE;
171}
172
173void sound_vsync() {
174 // buffer 1 just got over
175 if(audio.active_buffer == 1) {
176 // Start playing buffer 0
177 dma_transfer_copy(SOUND_FIFO_A, audio.mix_buffer, 1, 1,
178 DMA_DST_FIXED | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE);
179
180 // Set the current buffer pointer to the start of buffer 1
181 audio.current_buffer = audio.mix_buffer + AUDIO_BUF_LEN;
182 audio.active_buffer = 0;
183 } else {
184 // buffer 0 just got over
185 // DMA points to buffer 1 already, so don't bother stopping and resetting it
186 // Set the current buffer pointer to the start of buffer 0
187 audio.current_buffer = audio.mix_buffer;
188 audio.active_buffer = 1;
189 }
190}
191
192void sound_mix() {
193 // Initialize and clear mix_buffer.
194 s16 mix_buffer[AUDIO_BUF_LEN];
195 u32 fill = 0;
196 dma_fill(mix_buffer, fill, sizeof(mix_buffer), 3);
197
198 // Mix channels into the temporary buffer.
199 for (size_t j = 0; j < POLYPHONY; ++j) {
200 Channel *chan = &channels[j];
201 // Check if channel is active.
202 if (chan->data == NULL) {
203 continue;
204 }
205 for(size_t i = 0; i < AUDIO_BUF_LEN; i++) {
206 // Remember we are using fixed point values.
207 mix_buffer[i] += chan->data[chan->pos >> 12] * chan->vol;
208 chan->pos += chan->inc;
209
210 if (chan->pos >= chan->length) {
211 // If looping is not active disable the channel.
212 if (chan->loop_length == 0) {
213 chan->data = NULL;
214 break;
215 }
216
217 // Loop the sample.
218 while (chan->pos >= chan->length) {
219 chan->pos -= chan->loop_length;
220 }
221 }
222 }
223 }
224
225 // Downsample and copy to the playing buffer.
226 for (size_t i = 0; i < AUDIO_BUF_LEN; ++i) {
227 // >> 8 to divide off the volume, >> 2 to divide by 4 channels to
228 // prevent overflow.
229 audio.current_buffer[i] = mix_buffer[i] >> 8;
230 }
208} 231}
209 232
210void 233void