diff options
Diffstat (limited to 'src/uxn/devices/apu.c')
-rw-r--r-- | src/uxn/devices/apu.c | 142 |
1 files changed, 25 insertions, 117 deletions
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c index c3168c2..2b1c8ff 100644 --- a/src/uxn/devices/apu.c +++ b/src/uxn/devices/apu.c | |||
@@ -1,36 +1,7 @@ | |||
1 | #include "samples.c" | ||
2 | |||
3 | #define SAMPLE_FREQUENCY 44100 | 1 | #define SAMPLE_FREQUENCY 44100 |
4 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) | 2 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) |
5 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) | 3 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) |
6 | 4 | ||
7 | // FIXME: We have two structs for now, needs fixing. | ||
8 | typedef struct AudioChannel { | ||
9 | u8 *samples; | ||
10 | u16 n_samples; | ||
11 | u16 position; | ||
12 | u16 sampling_freq; | ||
13 | u8 pitch; | ||
14 | bool loop; | ||
15 | // TODO: u16 adsr; // attack, decay, sustain, release | ||
16 | // TODO: u8 pitch; // Bit 8 is the "loop" bit | ||
17 | // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) | ||
18 | // u8 *addr; | ||
19 | // u32 count, advance, period, age, a, d, s, r; | ||
20 | // s8 volume[2]; | ||
21 | // u8 pitch, repeat; | ||
22 | } AudioChannel; | ||
23 | |||
24 | typedef struct APU { | ||
25 | AudioChannel chan_0; | ||
26 | // u32 *samples_1; | ||
27 | // u32 *samples_2; | ||
28 | // u32 *samples_3; | ||
29 | } APU; | ||
30 | |||
31 | static APU apu; | ||
32 | |||
33 | |||
34 | static u16 pitch_table[] = { | 5 | static u16 pitch_table[] = { |
35 | 12173, 11490, 10845, 10237, 9662, 9120, 8608, 8125, | 6 | 12173, 11490, 10845, 10237, 9662, 9120, 8608, 8125, |
36 | 7669, 7238, 6832, 6448, 6086, 5745, 5422, 5118, | 7 | 7669, 7238, 6832, 6448, 6086, 5745, 5422, 5118, |
@@ -50,48 +21,12 @@ static u16 pitch_table[] = { | |||
50 | 2, | 21 | 2, |
51 | }; | 22 | }; |
52 | 23 | ||
53 | void | ||
54 | reset_sound(AudioChannel *chan) { | ||
55 | TIMER_CTRL_0 = 0; | ||
56 | TIMER_CTRL_1 = 0; | ||
57 | DMA_CTRL(1) = 0; | ||
58 | if (chan->pitch >= 108 || chan->n_samples == 0) { | ||
59 | return; | ||
60 | } | ||
61 | |||
62 | // Set max volume, left-right sound, fifo reset and use timer 0 for | ||
63 | // DirectSound A. | ||
64 | SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A | ||
65 | | SOUND_DSOUND_LEFT_A | ||
66 | | SOUND_DSOUND_RIGHT_A | ||
67 | | SOUND_DSOUND_RESET_A; | ||
68 | |||
69 | // Prepare DMA copy. | ||
70 | dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, | ||
71 | DMA_DST_FIXED | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); | ||
72 | |||
73 | // Timer 1 used to stop playing samples. | ||
74 | u32 sample_duration = chan->n_samples; | ||
75 | TIMER_DATA_1 = 0xFFFF - sample_duration; | ||
76 | TIMER_CTRL_1 = TIMER_CTRL_IRQ | ||
77 | | TIMER_CTRL_ENABLE | ||
78 | | TIMER_CTRL_CASCADE; | ||
79 | |||
80 | // Timer 0 used to stop sample playing. | ||
81 | // TIMER_DATA_0 = 0xFFFF - sound_freq[chan->pitch]; | ||
82 | // TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq / chan->pitch; | ||
83 | TIMER_DATA_0 = 0xFFFF - pitch_table[chan->pitch]; | ||
84 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; | ||
85 | } | ||
86 | |||
87 | s8 square_wave[] = { | 24 | s8 square_wave[] = { |
88 | (s8)0x00 + (s8)0x80, (s8)0xFF + (s8)0x80 | 25 | (s8)0x00 + (s8)0x80, (s8)0xFF + (s8)0x80 |
89 | }; | 26 | }; |
90 | 27 | ||
91 | #include "text.h" | 28 | #include "text.h" |
92 | 29 | ||
93 | |||
94 | |||
95 | // | 30 | // |
96 | // REG_TM0D frequency buffer size | 31 | // REG_TM0D frequency buffer size |
97 | // | | | | 32 | // | | | |
@@ -119,7 +54,7 @@ typedef struct Audio { | |||
119 | u8 active_buffer; | 54 | u8 active_buffer; |
120 | } Audio; | 55 | } Audio; |
121 | 56 | ||
122 | typedef struct Channel { | 57 | typedef struct AudioChannel { |
123 | // Pointer to the raw data in the ROM. | 58 | // Pointer to the raw data in the ROM. |
124 | s8 *data; | 59 | s8 *data; |
125 | // Current position in the data (20.12 fixed-point). | 60 | // Current position in the data (20.12 fixed-point). |
@@ -132,28 +67,31 @@ typedef struct Channel { | |||
132 | u32 length; | 67 | u32 length; |
133 | // Length of looped portion (20.12 fixed-point, 0 to disable looping). | 68 | // Length of looped portion (20.12 fixed-point, 0 to disable looping). |
134 | u32 loop_length; | 69 | u32 loop_length; |
135 | } Channel; | 70 | // TODO: this should be different? |
71 | u8 pitch; | ||
72 | } AudioChannel; | ||
136 | 73 | ||
137 | static Audio audio; | 74 | static Audio audio; |
138 | 75 | ||
139 | #define POLYPHONY 4 | 76 | #define POLYPHONY 4 |
140 | static Channel channels[POLYPHONY]; | 77 | static AudioChannel channels[POLYPHONY]; |
141 | 78 | ||
142 | void | 79 | void |
143 | init_sound(void) { | 80 | init_sound(void) { |
144 | // Initialize audio buffers/channels. | 81 | // Initialize audio buffers/channels. |
145 | audio = (Audio){0}; | 82 | audio = (Audio){0}; |
146 | for (size_t i = 0; i < POLYPHONY; ++i) { | 83 | for (size_t i = 0; i < POLYPHONY; ++i) { |
147 | channels[i] = (Channel){0}; | 84 | channels[i] = (AudioChannel){0}; |
148 | } | 85 | } |
149 | 86 | ||
150 | // DEBUG: testing channel 0 with square wave | 87 | // DEBUG: testing channel 0 with square wave |
151 | channels[0].data = samples; | 88 | // channels[0].data = samples; |
152 | channels[0].inc = (8363 << 12) / AUDIO_FREQ; | 89 | // channels[0].length = (LEN(samples) - 1) << 12; |
153 | channels[0].length = (LEN(samples) - 1) << 12; | 90 | // channels[0].pos = 0; |
154 | channels[0].pos = 0; | 91 | // channels[0].inc = (44100 << 12) / AUDIO_FREQ; |
155 | channels[0].vol = 64; | 92 | // channels[0].vol = 32; |
156 | channels[0].loop_length = channels[0].length; | 93 | // channels[0].loop_length = channels[0].length; |
94 | // channels[0].loop_length = 0; | ||
157 | 95 | ||
158 | // Enable the sound chip. | 96 | // Enable the sound chip. |
159 | SOUND_STATUS = SOUND_ENABLE; | 97 | SOUND_STATUS = SOUND_ENABLE; |
@@ -197,29 +135,29 @@ void sound_mix() { | |||
197 | 135 | ||
198 | // Mix channels into the temporary buffer. | 136 | // Mix channels into the temporary buffer. |
199 | for (size_t j = 0; j < POLYPHONY; ++j) { | 137 | for (size_t j = 0; j < POLYPHONY; ++j) { |
200 | Channel *chan = &channels[j]; | 138 | AudioChannel *ch = &channels[j]; |
201 | // Check if channel is active. | 139 | // Check if channel is active. |
202 | if (chan->data == NULL) { | 140 | if (ch->data == NULL || ch->pitch >= 108) { |
203 | continue; | 141 | continue; |
204 | } | 142 | } |
205 | if (chan->pos + chan->inc * AUDIO_BUF_LEN >= chan->length) { | 143 | if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) { |
206 | // Sample is going to finish, need to consider this for looping or | 144 | // Sample is going to finish, need to consider this for looping or |
207 | // stopping. | 145 | // stopping. |
208 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { | 146 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { |
209 | // Remember we are using fixed point values. | 147 | // Remember we are using fixed point values. |
210 | mix_buffer[i] += chan->data[chan->pos >> 12] * chan->vol; | 148 | mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos >> 12]) * ch->vol; |
211 | chan->pos += chan->inc; | 149 | ch->pos += ch->inc; |
212 | 150 | ||
213 | if (chan->pos >= chan->length) { | 151 | if (ch->pos >= ch->length) { |
214 | // If looping is not active disable the channel. | 152 | // If looping is not active disable the channel. |
215 | if (chan->loop_length == 0) { | 153 | if (ch->loop_length == 0) { |
216 | chan->data = NULL; | 154 | ch->data = NULL; |
217 | break; | 155 | break; |
218 | } | 156 | } |
219 | 157 | ||
220 | // Loop the sample. | 158 | // Loop the sample. |
221 | while (chan->pos >= chan->length) { | 159 | while (ch->pos >= ch->length) { |
222 | chan->pos -= chan->loop_length; | 160 | ch->pos -= ch->loop_length; |
223 | } | 161 | } |
224 | } | 162 | } |
225 | } | 163 | } |
@@ -227,8 +165,8 @@ void sound_mix() { | |||
227 | // Sample still have room to go, no need to check for looping or | 165 | // Sample still have room to go, no need to check for looping or |
228 | // end of sample. | 166 | // end of sample. |
229 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { | 167 | for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { |
230 | mix_buffer[i] += chan->data[chan->pos>>12] * chan->vol; | 168 | mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos>>12]) * ch->vol; |
231 | chan->pos += chan->inc; | 169 | ch->pos += ch->inc; |
232 | } | 170 | } |
233 | } | 171 | } |
234 | } | 172 | } |
@@ -240,33 +178,3 @@ void sound_mix() { | |||
240 | audio.current_buffer[i] = mix_buffer[i] >> 8; | 178 | audio.current_buffer[i] = mix_buffer[i] >> 8; |
241 | } | 179 | } |
242 | } | 180 | } |
243 | |||
244 | void | ||
245 | irs_stop_sample(void) { | ||
246 | if (apu.chan_0.loop) { | ||
247 | reset_sound(&apu.chan_0); | ||
248 | } else { | ||
249 | TIMER_CTRL_0 = 0; | ||
250 | DMA_CTRL(1) = 0; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | // void | ||
255 | // apu_start(Apu *c, u16 adsr, u8 pitch) { | ||
256 | // // if(pitch < 108 && c->len) | ||
257 | // // c->advance = advances[pitch % 12] >> (8 - pitch / 12); | ||
258 | // // else { | ||
259 | // // c->advance = 0; | ||
260 | // // return; | ||
261 | // // } | ||
262 | // // c->a = ADSR_STEP * (adsr >> 12); | ||
263 | // // c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; | ||
264 | // // c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; | ||
265 | // // c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; | ||
266 | // // c->age = 0; | ||
267 | // // c->i = 0; | ||
268 | // // if(c->len <= 0x100) /* single cycle mode */ | ||
269 | // // c->period = NOTE_PERIOD * 337 / 2 / c->len; | ||
270 | // // else /* sample repeat mode */ | ||
271 | // // c->period = NOTE_PERIOD; | ||
272 | // } | ||