aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-27 16:46:36 +0200
committerBad Diode <bd@badd10de.dev>2021-05-27 16:46:36 +0200
commitbc7f470714032c9e97798a5e532517c3d01adeef (patch)
treeb76500ee4fbbe0deacc73944db55bf7d6d03b12e
parentf32eda6df8e1df42e06eae55d28d9a45a92e7ecd (diff)
downloaduxngba-bc7f470714032c9e97798a5e532517c3d01adeef.tar.gz
uxngba-bc7f470714032c9e97798a5e532517c3d01adeef.zip
Add ADSR filter
-rw-r--r--src/main.c16
-rw-r--r--src/uxn/devices/apu.c79
2 files changed, 88 insertions, 7 deletions
diff --git a/src/main.c b/src/main.c
index cbf781d..aee68f8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -155,10 +155,13 @@ audio_talk(Device *d, u8 b0, u8 w) {
155 } 155 }
156 c->inc = (pitch_table[c->pitch] * sampling_rate) >> 5; 156 c->inc = (pitch_table[c->pitch] * sampling_rate) >> 5;
157 157
158 u16 adsr = mempeek16(d->dat, 0x8);
159 build_adsr(c, adsr);
158 txt_position(0, 0); 160 txt_position(0, 0);
159 txt_printf("note: %d \n", c->pitch); 161 txt_printf("note: %d \n", c->pitch);
160 txt_printf("inc: %ld \n", c->inc); 162 txt_printf("inc: %lu \n", c->inc);
161 txt_printf("length: %ld \n", c->length >> 12); 163 txt_printf("length: %lu \n", c->length >> 12);
164 txt_printf("chan: %lu \n", d - devaudio);
162 } 165 }
163} 166}
164 167
@@ -423,21 +426,24 @@ int main(void) {
423 int frame_counter = 0; 426 int frame_counter = 0;
424 evaluxn(&u, 0x0100); 427 evaluxn(&u, 0x0100);
425 u32 flip_cycles = 0; 428 u32 flip_cycles = 0;
429 u32 eval_cycles = 0;
430 u32 input_cycles = 0;
431 u32 mix_cycles = 0;
426 while(true) { 432 while(true) {
427 bios_vblank_wait(); 433 bios_vblank_wait();
428 profile_start(); 434 profile_start();
429 handle_input(&u); 435 handle_input(&u);
430 u32 input_cycles = profile_stop(); 436 input_cycles = MAX(profile_stop(), input_cycles);
431 profile_start(); 437 profile_start();
432 evaluxn(&u, mempeek16(devscreen->dat, 0)); 438 evaluxn(&u, mempeek16(devscreen->dat, 0));
433 u32 eval_cycles = profile_stop(); 439 eval_cycles = MAX(profile_stop(), eval_cycles);
434 txt_position(0, 8); 440 txt_position(0, 8);
435 profile_start(); 441 profile_start();
436 flip_cycles = profile_stop(); 442 flip_cycles = profile_stop();
437 frame_counter++; 443 frame_counter++;
438 profile_start(); 444 profile_start();
439 sound_mix(); 445 sound_mix();
440 u32 mix_cycles = profile_stop(); 446 mix_cycles = MAX(profile_stop(), mix_cycles);
441 447
442 txt_position(0, 15); 448 txt_position(0, 15);
443 txt_printf("INPUT: %lu \n", input_cycles); 449 txt_printf("INPUT: %lu \n", input_cycles);
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c
index 4447e12..d968840 100644
--- a/src/uxn/devices/apu.c
+++ b/src/uxn/devices/apu.c
@@ -1,7 +1,7 @@
1// Calculated as ((261.6 / 18157) << 17) for C4. If multiplied by sampling rate 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 2// we will have a u32 (15.17) fixed-point number. This should be enough to
3// accurately portray samples up to 75300 Hz. 3// accurately portray samples up to 75300 Hz.
4static u32 pitch_table[120] = { 4static u16 pitch_table[120] = {
5 59, 62, 66, 70, 74, 78, 83, 88, 5 59, 62, 66, 70, 74, 78, 83, 88,
6 93, 99, 105, 111, 118, 125, 132, 140, 6 93, 99, 105, 111, 118, 125, 132, 140,
7 148, 157, 166, 176, 187, 198, 210, 222, 7 148, 157, 166, 176, 187, 198, 210, 222,
@@ -61,8 +61,75 @@ typedef struct AudioChannel {
61 u32 loop_length; 61 u32 loop_length;
62 // Pitch encoded as a MIDI note. 62 // Pitch encoded as a MIDI note.
63 u8 pitch; 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;
64} AudioChannel; 73} AudioChannel;
65 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
66static Audio audio; 133static Audio audio;
67 134
68#define POLYPHONY 4 135#define POLYPHONY 4
@@ -123,12 +190,20 @@ void sound_mix() {
123 if (ch->data == NULL || ch->pitch >= 108) { 190 if (ch->data == NULL || ch->pitch >= 108) {
124 continue; 191 continue;
125 } 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 }
126 if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) { 201 if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) {
127 // Sample is going to finish, need to consider this for looping or 202 // Sample is going to finish, need to consider this for looping or
128 // stopping. 203 // stopping.
129 for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { 204 for(size_t i = 0; i < AUDIO_BUF_LEN; i++) {
130 // Remember we are using fixed point values. 205 // Remember we are using fixed point values.
131 mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos >> 12]) * ch->vol; 206 mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos >> 12]) * vol;
132 ch->pos += ch->inc; 207 ch->pos += ch->inc;
133 208
134 if (ch->pos >= ch->length) { 209 if (ch->pos >= ch->length) {