diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/apu.c (renamed from src/uxn/devices/apu.c) | 190 | ||||
-rw-r--r-- | src/common.h | 5 | ||||
-rw-r--r-- | src/filesystem.c | 2 | ||||
-rw-r--r-- | src/main.c | 46 |
4 files changed, 106 insertions, 137 deletions
diff --git a/src/uxn/devices/apu.c b/src/apu.c index 47151a6..8dd4ce5 100644 --- a/src/uxn/devices/apu.c +++ b/src/apu.c | |||
@@ -1,24 +1,3 @@ | |||
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 | // | 1 | // |
23 | // REG_TM0D frequency buffer size | 2 | // REG_TM0D frequency buffer size |
24 | // | | | | 3 | // | | | |
@@ -48,7 +27,7 @@ typedef struct Audio { | |||
48 | 27 | ||
49 | typedef struct AudioChannel { | 28 | typedef struct AudioChannel { |
50 | // Pointer to the raw data in the ROM. | 29 | // Pointer to the raw data in the ROM. |
51 | s8 *data; | 30 | u8 *data; |
52 | // Current position in the data (20.12 fixed-point). | 31 | // Current position in the data (20.12 fixed-point). |
53 | u32 pos; | 32 | u32 pos; |
54 | // Increment (20.12 fixed-point). | 33 | // Increment (20.12 fixed-point). |
@@ -63,70 +42,75 @@ typedef struct AudioChannel { | |||
63 | u8 pitch; | 42 | u8 pitch; |
64 | // Keeping track of the original adsr values. | 43 | // Keeping track of the original adsr values. |
65 | u16 adsr; | 44 | u16 adsr; |
66 | // The filter is built with the ADSR and has a duration of 60 ticks. Since | 45 | // The filter is built with the ADSR and has a maximum duration of |
67 | // weare synced at 60FPS it should be pretty straightforward. | 46 | // 4 seconds. Each component can last up to 1 second. |
68 | u8 filter[241]; | 47 | u8 filter[240]; |
69 | // Current position in the filter (0-60). | 48 | // Current position in the filter (0-60). |
70 | u8 filter_pos; | 49 | u8 filter_pos; |
71 | // Duration of the filter. | 50 | // Duration of the filter. |
72 | u8 filter_len; | 51 | u8 filter_len; |
73 | } AudioChannel; | 52 | } AudioChannel; |
74 | 53 | ||
75 | // NOTE: fixed-point (1.6) precision for volume adjustments. | 54 | // Calculated as ((261.6 / 18157) << 17) for C4. If multiplied by sampling rate |
76 | typedef u32 fp32; | 55 | // we will have a u32 (15.17) fixed-point number. This should be enough to |
77 | 56 | // accurately portray samples up to 75300 Hz. | |
78 | inline | 57 | static u16 pitch_table[120] = { |
79 | fp32 | 58 | 59, 62, 66, 70, 74, 78, 83, 88, |
80 | fp_mul(fp32 a, fp32 b) { | 59 | 93, 99, 105, 111, 118, 125, 132, 140, |
81 | return (a * b) >> 6; | 60 | 148, 157, 166, 176, 187, 198, 210, 222, |
82 | } | 61 | 236, 250, 264, 280, 297, 315, 333, 353, |
83 | 62 | 374, 396, 420, 445, 472, 500, 529, 561, | |
84 | inline | 63 | 594, 630, 667, 707, 749, 793, 841, 891, |
85 | fp32 | 64 | 944, 1000, 1059, 1122, 1189, 1260, 1335, 1414, |
86 | fp_div(fp32 a, fp32 b) { | 65 | 1498, 1587, 1682, 1782, 1888, 2000, 2119, 2245, |
87 | return (a << 6) / b; | 66 | 2379, 2520, 2670, 2829, 2997, 3175, 3364, 3564, |
88 | } | 67 | 3776, 4001, 4239, 4491, 4758, 5041, 5341, 5658, |
89 | 68 | 5995, 6351, 6729, 7129, 7553, 8002, 8478, 8982, | |
90 | inline | 69 | 9517, 10083, 10682, 11317, 11990, 12703, 13459, 14259, |
91 | fp32 | 70 | 15107, 16005, 16957, 17965, 19034, 20166, 21365, 22635, |
92 | fp_lerp(fp32 y0, fp32 y1, fp32 x) { | 71 | 23981, 25407, 26918, 28519, 30215, 32011, 33915, 35931, |
93 | return y0 + fp_mul(x, (y1 - y0)); | 72 | 38068, 40332, 42730, 45271, 47963, 50815, 53837, 57038, |
94 | } | 73 | }; |
95 | |||
96 | 74 | ||
97 | IWRAM_CODE | 75 | IWRAM_CODE |
98 | void | 76 | void |
99 | build_adsr(AudioChannel *chan, u16 adsr) { | 77 | build_adsr(AudioChannel *chan, u16 adsr) { |
100 | // u8 a = (adsr >> 12); | 78 | chan->filter_pos = 0; |
79 | if (adsr == chan->adsr) { | ||
80 | return; | ||
81 | } | ||
82 | |||
101 | u8 a = (adsr >> 12); | 83 | u8 a = (adsr >> 12); |
102 | u8 d = (adsr >> 8) & 0xF; | 84 | u8 d = (adsr >> 8) & 0xF; |
103 | u8 s = (adsr >> 4) & 0xF; | 85 | u8 s = (adsr >> 4) & 0xF; |
104 | u8 r = (adsr >> 0) & 0xF; | 86 | u8 r = (adsr >> 0) & 0xF; |
105 | 87 | ||
106 | // Initialize the filter array. | 88 | // Initialize the filter array. |
107 | memset(chan->filter, 0, sizeof(chan->filter)); | 89 | dma_fill(chan->filter, 0, sizeof(chan->filter), 3); |
108 | u8 k = 0; | 90 | u8 k = 0; |
109 | 91 | ||
110 | // Attack. | 92 | // Attack. |
93 | u32 interval = FP_DIV(1 << 6, (4 * a) << 6, 6); | ||
111 | for (u32 i = 0; i < 4 * a; ++i) { | 94 | for (u32 i = 0; i < 4 * a; ++i) { |
112 | chan->filter[k++] = fp_lerp(0, chan->vol, fp_div(i << 6, (4 * a) << 6)); | 95 | chan->filter[k++] = FP_LERP(0, chan->vol, i * interval, 6); |
113 | } | 96 | } |
114 | // Decay. | 97 | // Decay. |
98 | interval = FP_DIV(1 << 6, (4 * d) << 6, 6); | ||
115 | for (u32 i = 0; i < 4 * d; ++i) { | 99 | 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)); | 100 | chan->filter[k++] = FP_LERP(chan->vol, chan->vol / 2, i * interval, 6); |
117 | } | 101 | } |
118 | // Sustain. | 102 | // Sustain. |
119 | for (u32 i = 0; i < 4 * s; ++i) { | 103 | for (u32 i = 0; i < 4 * s; ++i) { |
120 | chan->filter[k++] = chan->vol / 2; | 104 | chan->filter[k++] = chan->vol / 2; |
121 | } | 105 | } |
122 | // Release. | 106 | // Release. |
107 | interval = FP_DIV(1 << 6, (4 * r) << 6, 6); | ||
123 | for (u32 i = 0; i < 4 * r; ++i) { | 108 | 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)); | 109 | chan->filter[k++] = FP_LERP(chan->vol / 2, 0, i * interval, 6); |
125 | } | 110 | } |
126 | 111 | ||
127 | // Setup the channel vars. | 112 | // Setup the channel vars. |
128 | chan->adsr = adsr; | 113 | chan->adsr = adsr; |
129 | chan->filter_pos = 0; | ||
130 | chan->filter_len = k; | 114 | chan->filter_len = k; |
131 | } | 115 | } |
132 | 116 | ||
@@ -153,35 +137,57 @@ init_sound(void) { | |||
153 | | SOUND_DSOUND_RIGHT_A | 137 | | SOUND_DSOUND_RIGHT_A |
154 | | SOUND_DSOUND_RESET_A; | 138 | | SOUND_DSOUND_RESET_A; |
155 | 139 | ||
140 | // The timer depends on the buffer length. | ||
156 | TIMER_DATA_0 = AUDIO_TIMER; | 141 | TIMER_DATA_0 = AUDIO_TIMER; |
157 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; | 142 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; |
158 | } | 143 | } |
159 | 144 | ||
160 | void sound_vsync() { | 145 | void |
161 | // buffer 1 just got over | 146 | sound_vsync() { |
162 | if(audio.active_buffer == 1) { | 147 | if(audio.active_buffer == 1) { |
163 | // Start playing buffer 0 | 148 | // Start playing and set the backbuffer. |
164 | dma_transfer_copy(SOUND_FIFO_A, audio.mix_buffer, 1, 1, | 149 | dma_transfer_copy(SOUND_FIFO_A, audio.mix_buffer, 1, 1, DMA_DST_FIXED |
165 | DMA_DST_FIXED | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); | 150 | | 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; | 151 | audio.current_buffer = audio.mix_buffer + AUDIO_BUF_LEN; |
169 | audio.active_buffer = 0; | 152 | audio.active_buffer = 0; |
170 | } else { | 153 | } else { |
171 | // buffer 0 just got over | 154 | // Flip front/backbuffer. |
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; | 155 | audio.current_buffer = audio.mix_buffer; |
175 | audio.active_buffer = 1; | 156 | audio.active_buffer = 1; |
176 | } | 157 | } |
177 | } | 158 | } |
178 | 159 | ||
179 | IWRAM_CODE | 160 | IWRAM_CODE |
180 | void sound_mix() { | 161 | void |
181 | // Initialize and clear mix_buffer. | 162 | update_channel(AudioChannel *c, u8 *data, u16 length, u8 pitch, u16 adsr, |
182 | s16 mix_buffer[AUDIO_BUF_LEN]; | 163 | u8 vol, bool loop) { |
183 | u32 fill = 0; | 164 | c->pos = 0; |
184 | dma_fill(mix_buffer, fill, sizeof(mix_buffer), 3); | 165 | c->length = length << 12; |
166 | c->data = data; | ||
167 | c->vol = vol; | ||
168 | c->pitch = pitch; | ||
169 | |||
170 | if (loop) { | ||
171 | c->loop_length = c->length; | ||
172 | } else { | ||
173 | c->loop_length = 0; | ||
174 | } | ||
175 | |||
176 | u32 sampling_rate = length; | ||
177 | if (length > 256) { | ||
178 | sampling_rate = 44100; | ||
179 | } | ||
180 | c->inc = (pitch_table[c->pitch] * sampling_rate) >> 5; | ||
181 | |||
182 | build_adsr(c, adsr); | ||
183 | } | ||
184 | |||
185 | IWRAM_CODE | ||
186 | void | ||
187 | sound_mix(void) { | ||
188 | // Clear mix_buffer. | ||
189 | u16 mix_buffer[AUDIO_BUF_LEN]; | ||
190 | dma_fill(mix_buffer, 0, sizeof(mix_buffer), 3); | ||
185 | 191 | ||
186 | // Mix channels into the temporary buffer. | 192 | // Mix channels into the temporary buffer. |
187 | for (size_t j = 0; j < POLYPHONY; ++j) { | 193 | for (size_t j = 0; j < POLYPHONY; ++j) { |
@@ -198,33 +204,23 @@ void sound_mix() { | |||
198 | continue; | 204 | continue; |
199 | } | 205 | } |
200 | } | 206 | } |
201 | if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) { | 207 | |
202 | // Sample is going to finish, need to consider this for looping or | 208 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { |
203 | // stopping. | 209 | // Remember we are using fixed point values. |
204 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { | 210 | mix_buffer[i] += (0x80 ^ (s8)ch->data[ch->pos >> 12]) * vol; |
205 | // Remember we are using fixed point values. | 211 | ch->pos += ch->inc; |
206 | mix_buffer[i] += (0x80 ^ ch->data[ch->pos >> 12]) * vol; | 212 | |
207 | ch->pos += ch->inc; | 213 | if (ch->pos >= ch->length) { |
208 | 214 | // If looping is not active disable the channel. | |
209 | if (ch->pos >= ch->length) { | 215 | if (ch->loop_length == 0) { |
210 | // If looping is not active disable the channel. | 216 | ch->data = NULL; |
211 | if (ch->loop_length == 0) { | 217 | break; |
212 | ch->data = NULL; | 218 | } |
213 | break; | 219 | |
214 | } | 220 | // Loop the sample. |
215 | 221 | while (ch->pos >= ch->length) { | |
216 | // Loop the sample. | 222 | ch->pos -= ch->loop_length; |
217 | while (ch->pos >= ch->length) { | ||
218 | ch->pos -= ch->loop_length; | ||
219 | } | ||
220 | } | 223 | } |
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 | } | 224 | } |
229 | } | 225 | } |
230 | } | 226 | } |
@@ -234,9 +230,9 @@ void sound_mix() { | |||
234 | u32 *buf_ptr = audio.current_buffer; | 230 | u32 *buf_ptr = audio.current_buffer; |
235 | for (size_t i = 0; i < AUDIO_BUF_LEN / 4; i++) { | 231 | for (size_t i = 0; i < AUDIO_BUF_LEN / 4; i++) { |
236 | u64 mix = mix_ptr[i]; | 232 | u64 mix = mix_ptr[i]; |
237 | buf_ptr[i] = (mix >> 8) & 0xFF | 233 | buf_ptr[i] = ((mix >> 8) & 0xFF) |
238 | | (mix >> 16) & 0xFF00 | 234 | | ((mix >> 16) & 0xFF00) |
239 | | (mix >> 24) & 0xFF0000 | 235 | | ((mix >> 24) & 0xFF0000) |
240 | | (mix >> 32) & 0xFF000000; | 236 | | ((mix >> 32) & 0xFF000000); |
241 | } | 237 | } |
242 | } | 238 | } |
diff --git a/src/common.h b/src/common.h index 048708b..0fe2752 100644 --- a/src/common.h +++ b/src/common.h | |||
@@ -722,6 +722,11 @@ wait_vsync(void) { | |||
722 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) | 722 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) |
723 | #define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) | 723 | #define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) |
724 | 724 | ||
725 | // Fixed-point arithmetic for (i.P) numbers. | ||
726 | #define FP_MUL(A,B,P) (((A) * (B)) >> (P)) | ||
727 | #define FP_DIV(A,B,P) (((A) << (P)) / (B)) | ||
728 | #define FP_LERP(Y0,Y1,X,P) ((Y0) + FP_MUL((X), ((Y1) - (Y0)), P)) | ||
729 | |||
725 | // | 730 | // |
726 | // Memory section macros for devkitARM. | 731 | // Memory section macros for devkitARM. |
727 | // | 732 | // |
diff --git a/src/filesystem.c b/src/filesystem.c index b60dbde..8aaa51c 100644 --- a/src/filesystem.c +++ b/src/filesystem.c | |||
@@ -114,7 +114,7 @@ fs_init(void) { | |||
114 | } | 114 | } |
115 | 115 | ||
116 | // Initialize filesystem. | 116 | // Initialize filesystem. |
117 | memset(&filesystem, 0, sizeof(FileSystem)); | 117 | dma_fill(&filesystem, 0, sizeof(FileSystem), 3); |
118 | filesystem.initialized = FS_INIT_PATTERN; | 118 | filesystem.initialized = FS_INIT_PATTERN; |
119 | for (size_t i = 0; i < FILE_INDEX_NUM; ++i) { | 119 | for (size_t i = 0; i < FILE_INDEX_NUM; ++i) { |
120 | filesystem.files[i].first_block = FS_NULL; | 120 | filesystem.files[i].first_block = FS_NULL; |
@@ -20,7 +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 | #include "apu.c" |
24 | 24 | ||
25 | #include "text.h" | 25 | #include "text.h" |
26 | 26 | ||
@@ -124,44 +124,12 @@ audio_talk(Device *d, u8 b0, u8 w) { | |||
124 | } | 124 | } |
125 | } else if(b0 == 0xf) { | 125 | } else if(b0 == 0xf) { |
126 | u16 length = mempeek16(d->dat, 0xa); | 126 | u16 length = mempeek16(d->dat, 0xa); |
127 | 127 | u8 *data = &d->mem[mempeek16(d->dat, 0xc)]; | |
128 | // Disable the channel before updating. | 128 | u8 pitch = d->dat[0xf] & 0x7f; |
129 | c->data = NULL; | ||
130 | |||
131 | // Data. | ||
132 | c->pos = 0; | ||
133 | c->length = length; | ||
134 | c->length <<= 12; // fixed point. | ||
135 | c->data = &d->mem[mempeek16(d->dat, 0xc)]; | ||
136 | |||
137 | // Volume (Mono only for now). | ||
138 | c->vol = MAX(d->dat[0xe] >> 4, d->dat[0xe] & 0xf); // 0-64 | ||
139 | c->vol *= 4 / 3; | ||
140 | |||
141 | // Looping. | ||
142 | if (!(d->dat[0xf] & 0x80)) { | ||
143 | c->loop_length = c->length; | ||
144 | } else { | ||
145 | c->loop_length = 0; | ||
146 | } | ||
147 | |||
148 | // Pitch | ||
149 | c->pitch = d->dat[0xf] & 0x7f; | ||
150 | |||
151 | // Initialization. | ||
152 | u32 sampling_rate = length; | ||
153 | if (length > 256) { | ||
154 | sampling_rate = 44100; | ||
155 | } | ||
156 | c->inc = (pitch_table[c->pitch] * sampling_rate) >> 5; | ||
157 | |||
158 | u16 adsr = mempeek16(d->dat, 0x8); | 129 | u16 adsr = mempeek16(d->dat, 0x8); |
159 | build_adsr(c, adsr); | 130 | u32 vol = MAX(d->dat[0xe] >> 4, d->dat[0xe] & 0xf) * 4 / 3; |
160 | txt_position(0, 0); | 131 | bool loop = !(d->dat[0xf] & 0x80); |
161 | txt_printf("note: %d \n", c->pitch); | 132 | update_channel(c, data, length, pitch, adsr, vol, loop); |
162 | txt_printf("inc: %lu \n", c->inc); | ||
163 | txt_printf("length: %lu \n", c->length >> 12); | ||
164 | txt_printf("chan: %lu \n", d - devaudio); | ||
165 | } | 133 | } |
166 | } | 134 | } |
167 | 135 | ||
@@ -412,7 +380,7 @@ int main(void) { | |||
412 | // irs_set(IRQ_TIMER_1, irs_stop_sample); | 380 | // irs_set(IRQ_TIMER_1, irs_stop_sample); |
413 | 381 | ||
414 | // Initialize VM. | 382 | // Initialize VM. |
415 | memset(&u, 0, sizeof(u)); | 383 | dma_fill(&u, 0, sizeof(u), 3); |
416 | u.ram.dat = umem; | 384 | u.ram.dat = umem; |
417 | init_uxn(&u); | 385 | init_uxn(&u); |
418 | 386 | ||