aboutsummaryrefslogtreecommitdiffstats
path: root/src/uxn/devices/apu.c
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-28 19:22:49 +0200
committerBad Diode <bd@badd10de.dev>2021-05-28 19:22:49 +0200
commit65ce84c8a57956ac685256b50d496cf305eac66e (patch)
tree357de9155a66452f8b458113ed061fe41db77056 /src/uxn/devices/apu.c
parent4ea00e4dcbb390f9fec53034ac1a62cc6fb308d0 (diff)
downloaduxngba-65ce84c8a57956ac685256b50d496cf305eac66e.tar.gz
uxngba-65ce84c8a57956ac685256b50d496cf305eac66e.zip
Cleaup some leftover code and comments
Diffstat (limited to 'src/uxn/devices/apu.c')
-rw-r--r--src/uxn/devices/apu.c242
1 files changed, 0 insertions, 242 deletions
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c
deleted file mode 100644
index 47151a6..0000000
--- a/src/uxn/devices/apu.c
+++ /dev/null
@@ -1,242 +0,0 @@
1// Calculated as ((261.6 / 18157) << 17) for C4. If multiplied by sampling rate
2// we will have a u32 (15.17) fixed-point number. This should be enough to
3// accurately portray samples up to 75300 Hz.
4static u16 pitch_table[120] = {
5 59, 62, 66, 70, 74, 78, 83, 88,
6 93, 99, 105, 111, 118, 125, 132, 140,
7 148, 157, 166, 176, 187, 198, 210, 222,
8 236, 250, 264, 280, 297, 315, 333, 353,
9 374, 396, 420, 445, 472, 500, 529, 561,
10 594, 630, 667, 707, 749, 793, 841, 891,
11 944, 1000, 1059, 1122, 1189, 1260, 1335, 1414,
12 1498, 1587, 1682, 1782, 1888, 2000, 2119, 2245,
13 2379, 2520, 2670, 2829, 2997, 3175, 3364, 3564,
14 3776, 4001, 4239, 4491, 4758, 5041, 5341, 5658,
15 5995, 6351, 6729, 7129, 7553, 8002, 8478, 8982,
16 9517, 10083, 10682, 11317, 11990, 12703, 13459, 14259,
17 15107, 16005, 16957, 17965, 19034, 20166, 21365, 22635,
18 23981, 25407, 26918, 28519, 30215, 32011, 33915, 35931,
19 38068, 40332, 42730, 45271, 47963, 50815, 53837, 57038,
20};
21
22//
23// REG_TM0D frequency buffer size
24// | | |
25// V V V
26//
27// Timer = 62610 = 65536 - (16777216 / 5734), buf = 96
28// Timer = 63940 = 65536 - (16777216 / 10512), buf = 176
29// Timer = 64282 = 65536 - (16777216 / 13379), buf = 224
30// Timer = 64612 = 65536 - (16777216 / 18157), buf = 304
31// Timer = 64738 = 65536 - (16777216 / 21024), buf = 352
32// Timer = 64909 = 65536 - (16777216 / 26758), buf = 448
33// Timer = 65004 = 65536 - (16777216 / 31536), buf = 528
34// Timer = 65073 = 65536 - (16777216 / 36314), buf = 608
35// Timer = 65118 = 65536 - (16777216 / 40137), buf = 672
36// Timer = 65137 = 65536 - (16777216 / 42048), buf = 704
37//
38// Source: https://deku.gbadev.org/program/sound1.html
39#define AUDIO_FREQ 18157
40#define AUDIO_BUF_LEN 304
41#define AUDIO_TIMER 64612
42
43typedef struct Audio {
44 s8 mix_buffer[AUDIO_BUF_LEN * 2];
45 s8 *current_buffer;
46 u8 active_buffer;
47} Audio;
48
49typedef struct AudioChannel {
50 // Pointer to the raw data in the ROM.
51 s8 *data;
52 // Current position in the data (20.12 fixed-point).
53 u32 pos;
54 // Increment (20.12 fixed-point).
55 u32 inc;
56 // Volume (0-64, 1.6 fixed-point).
57 u32 vol;
58 // Sound length (20.12 fixed-point).
59 u32 length;
60 // Length of looped portion (20.12 fixed-point, 0 to disable looping).
61 u32 loop_length;
62 // Pitch encoded as a MIDI note.
63 u8 pitch;
64 // Keeping track of the original adsr values.
65 u16 adsr;
66 // The filter is built with the ADSR and has a duration of 60 ticks. Since
67 // weare synced at 60FPS it should be pretty straightforward.
68 u8 filter[241];
69 // Current position in the filter (0-60).
70 u8 filter_pos;
71 // Duration of the filter.
72 u8 filter_len;
73} AudioChannel;
74
75// NOTE: fixed-point (1.6) precision for volume adjustments.
76typedef u32 fp32;
77
78inline
79fp32
80fp_mul(fp32 a, fp32 b) {
81 return (a * b) >> 6;
82}
83
84inline
85fp32
86fp_div(fp32 a, fp32 b) {
87 return (a << 6) / b;
88}
89
90inline
91fp32
92fp_lerp(fp32 y0, fp32 y1, fp32 x) {
93 return y0 + fp_mul(x, (y1 - y0));
94}
95
96
97IWRAM_CODE
98void
99build_adsr(AudioChannel *chan, u16 adsr) {
100 // u8 a = (adsr >> 12);
101 u8 a = (adsr >> 12);
102 u8 d = (adsr >> 8) & 0xF;
103 u8 s = (adsr >> 4) & 0xF;
104 u8 r = (adsr >> 0) & 0xF;
105
106 // Initialize the filter array.
107 memset(chan->filter, 0, sizeof(chan->filter));
108 u8 k = 0;
109
110 // Attack.
111 for (u32 i = 0; i < 4 * a; ++i) {
112 chan->filter[k++] = fp_lerp(0, chan->vol, fp_div(i << 6, (4 * a) << 6));
113 }
114 // Decay.
115 for (u32 i = 0; i < 4 * d; ++i) {
116 chan->filter[k++] = fp_lerp(chan->vol, chan->vol / 2, fp_div(i << 6, (4 * d) << 6));
117 }
118 // Sustain.
119 for (u32 i = 0; i < 4 * s; ++i) {
120 chan->filter[k++] = chan->vol / 2;
121 }
122 // Release.
123 for (u32 i = 0; i < 4 * r; ++i) {
124 chan->filter[k++] = fp_lerp(chan->vol / 2, 0, fp_div(i << 6, (4 * r) << 6));
125 }
126
127 // Setup the channel vars.
128 chan->adsr = adsr;
129 chan->filter_pos = 0;
130 chan->filter_len = k;
131}
132
133static Audio audio;
134
135#define POLYPHONY 4
136static AudioChannel channels[POLYPHONY];
137
138void
139init_sound(void) {
140 // Initialize audio buffers/channels.
141 audio = (Audio){0};
142 for (size_t i = 0; i < POLYPHONY; ++i) {
143 channels[i] = (AudioChannel){0};
144 }
145
146 // Enable the sound chip.
147 SOUND_STATUS = SOUND_ENABLE;
148
149 // Set max volume, left-right sound, fifo reset and use timer 0 for
150 // DirectSound A.
151 SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A
152 | SOUND_DSOUND_LEFT_A
153 | SOUND_DSOUND_RIGHT_A
154 | SOUND_DSOUND_RESET_A;
155
156 TIMER_DATA_0 = AUDIO_TIMER;
157 TIMER_CTRL_0 = TIMER_CTRL_ENABLE;
158}
159
160void sound_vsync() {
161 // buffer 1 just got over
162 if(audio.active_buffer == 1) {
163 // Start playing buffer 0
164 dma_transfer_copy(SOUND_FIFO_A, audio.mix_buffer, 1, 1,
165 DMA_DST_FIXED | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE);
166
167 // Set the current buffer pointer to the start of buffer 1
168 audio.current_buffer = audio.mix_buffer + AUDIO_BUF_LEN;
169 audio.active_buffer = 0;
170 } else {
171 // buffer 0 just got over
172 // DMA points to buffer 1 already, so don't bother stopping and resetting it
173 // Set the current buffer pointer to the start of buffer 0
174 audio.current_buffer = audio.mix_buffer;
175 audio.active_buffer = 1;
176 }
177}
178
179IWRAM_CODE
180void sound_mix() {
181 // Initialize and clear mix_buffer.
182 s16 mix_buffer[AUDIO_BUF_LEN];
183 u32 fill = 0;
184 dma_fill(mix_buffer, fill, sizeof(mix_buffer), 3);
185
186 // Mix channels into the temporary buffer.
187 for (size_t j = 0; j < POLYPHONY; ++j) {
188 AudioChannel *ch = &channels[j];
189 // Check if channel is active.
190 if (ch->data == NULL || ch->pitch >= 108) {
191 continue;
192 }
193
194 u32 vol = ch->vol;
195 if (ch->adsr != 0) {
196 vol = ch->filter[ch->filter_pos++];
197 if (ch->filter_pos == ch->filter_len) {
198 continue;
199 }
200 }
201 if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) {
202 // Sample is going to finish, need to consider this for looping or
203 // stopping.
204 for(size_t i = 0; i < AUDIO_BUF_LEN; i++) {
205 // Remember we are using fixed point values.
206 mix_buffer[i] += (0x80 ^ ch->data[ch->pos >> 12]) * vol;
207 ch->pos += ch->inc;
208
209 if (ch->pos >= ch->length) {
210 // If looping is not active disable the channel.
211 if (ch->loop_length == 0) {
212 ch->data = NULL;
213 break;
214 }
215
216 // Loop the sample.
217 while (ch->pos >= ch->length) {
218 ch->pos -= ch->loop_length;
219 }
220 }
221 }
222 } else {
223 // Sample still have room to go, no need to check for looping or
224 // end of sample.
225 for(size_t i = 0; i < AUDIO_BUF_LEN; i++) {
226 mix_buffer[i] += (0x80 ^ ch->data[ch->pos>>12]) * vol;
227 ch->pos += ch->inc;
228 }
229 }
230 }
231
232 // Downsample and copy to the playing buffer (Vectorized).
233 u64 *mix_ptr = mix_buffer;
234 u32 *buf_ptr = audio.current_buffer;
235 for (size_t i = 0; i < AUDIO_BUF_LEN / 4; i++) {
236 u64 mix = mix_ptr[i];
237 buf_ptr[i] = (mix >> 8) & 0xFF
238 | (mix >> 16) & 0xFF00
239 | (mix >> 24) & 0xFF0000
240 | (mix >> 32) & 0xFF000000;
241 }
242}