diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-28 19:22:49 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-28 19:22:49 +0200 |
commit | 65ce84c8a57956ac685256b50d496cf305eac66e (patch) | |
tree | 357de9155a66452f8b458113ed061fe41db77056 /src/uxn/devices/apu.c | |
parent | 4ea00e4dcbb390f9fec53034ac1a62cc6fb308d0 (diff) | |
download | uxngba-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.c | 242 |
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. | ||
4 | static 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 | |||
43 | typedef struct Audio { | ||
44 | s8 mix_buffer[AUDIO_BUF_LEN * 2]; | ||
45 | s8 *current_buffer; | ||
46 | u8 active_buffer; | ||
47 | } Audio; | ||
48 | |||
49 | typedef 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. | ||
76 | typedef u32 fp32; | ||
77 | |||
78 | inline | ||
79 | fp32 | ||
80 | fp_mul(fp32 a, fp32 b) { | ||
81 | return (a * b) >> 6; | ||
82 | } | ||
83 | |||
84 | inline | ||
85 | fp32 | ||
86 | fp_div(fp32 a, fp32 b) { | ||
87 | return (a << 6) / b; | ||
88 | } | ||
89 | |||
90 | inline | ||
91 | fp32 | ||
92 | fp_lerp(fp32 y0, fp32 y1, fp32 x) { | ||
93 | return y0 + fp_mul(x, (y1 - y0)); | ||
94 | } | ||
95 | |||
96 | |||
97 | IWRAM_CODE | ||
98 | void | ||
99 | build_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 | |||
133 | static Audio audio; | ||
134 | |||
135 | #define POLYPHONY 4 | ||
136 | static AudioChannel channels[POLYPHONY]; | ||
137 | |||
138 | void | ||
139 | init_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 | |||
160 | void 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 | |||
179 | IWRAM_CODE | ||
180 | void 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 | } | ||