diff options
Diffstat (limited to 'src/uxn/devices/apu.c')
-rw-r--r-- | src/uxn/devices/apu.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c new file mode 100644 index 0000000..fbe7c3d --- /dev/null +++ b/src/uxn/devices/apu.c | |||
@@ -0,0 +1,241 @@ | |||
1 | #include "samples.c" | ||
2 | |||
3 | #define SAMPLE_FREQUENCY 44100 | ||
4 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) | ||
5 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) | ||
6 | |||
7 | typedef struct AudioChannel { | ||
8 | u8 *samples; | ||
9 | u16 n_samples; | ||
10 | u16 position; | ||
11 | u16 sampling_freq; | ||
12 | u8 pitch; | ||
13 | bool loop; | ||
14 | // TODO: u16 adsr; // attack, decay, sustain, release | ||
15 | // TODO: u8 pitch; // Bit 8 is the "loop" bit | ||
16 | // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) | ||
17 | // u8 *addr; | ||
18 | // u32 count, advance, period, age, a, d, s, r; | ||
19 | // s8 volume[2]; | ||
20 | // u8 pitch, repeat; | ||
21 | } AudioChannel; | ||
22 | |||
23 | typedef struct APU { | ||
24 | AudioChannel chan_0; | ||
25 | // u32 *samples_1; | ||
26 | // u32 *samples_2; | ||
27 | // u32 *samples_3; | ||
28 | } APU; | ||
29 | |||
30 | static APU apu; | ||
31 | |||
32 | |||
33 | static u16 pitch_table[] = { | ||
34 | 12173, | ||
35 | 11490, | ||
36 | 10845, | ||
37 | 10237, | ||
38 | 9662, | ||
39 | 9120, | ||
40 | 8608, | ||
41 | 8125, | ||
42 | 7669, | ||
43 | 7238, | ||
44 | 6832, | ||
45 | 6448, | ||
46 | 6086, | ||
47 | 5745, | ||
48 | 5422, | ||
49 | 5118, | ||
50 | 4831, | ||
51 | 4560, | ||
52 | 4304, | ||
53 | 4062, | ||
54 | 3834, | ||
55 | 3619, | ||
56 | 3416, | ||
57 | 3224, | ||
58 | 3043, | ||
59 | 2872, | ||
60 | 2711, | ||
61 | 2559, | ||
62 | 2415, | ||
63 | 2280, | ||
64 | 2152, | ||
65 | 2031, | ||
66 | 1917, | ||
67 | 1809, | ||
68 | 1708, | ||
69 | 1612, | ||
70 | 1521, | ||
71 | 1436, | ||
72 | 1355, | ||
73 | 1279, | ||
74 | 1207, | ||
75 | 1140, | ||
76 | 1076, | ||
77 | 1015, | ||
78 | 958, | ||
79 | 904, | ||
80 | 854, | ||
81 | 806, | ||
82 | 760, | ||
83 | 718, | ||
84 | 677, | ||
85 | 639, | ||
86 | 603, | ||
87 | 570, | ||
88 | 538, | ||
89 | 507, | ||
90 | 479, | ||
91 | 452, | ||
92 | 427, | ||
93 | 403, | ||
94 | 380, | ||
95 | 359, | ||
96 | 338, | ||
97 | 319, | ||
98 | 301, | ||
99 | 285, | ||
100 | 269, | ||
101 | 253, | ||
102 | 239, | ||
103 | 226, | ||
104 | 213, | ||
105 | 201, | ||
106 | 190, | ||
107 | 179, | ||
108 | 169, | ||
109 | 159, | ||
110 | 150, | ||
111 | 142, | ||
112 | 134, | ||
113 | 126, | ||
114 | 119, | ||
115 | 113, | ||
116 | 106, | ||
117 | 100, | ||
118 | 95, | ||
119 | 89, | ||
120 | 84, | ||
121 | 79, | ||
122 | 75, | ||
123 | 71, | ||
124 | 67, | ||
125 | 63, | ||
126 | 59, | ||
127 | 56, | ||
128 | 53, | ||
129 | 50, | ||
130 | 47, | ||
131 | 44, | ||
132 | 42, | ||
133 | 39, | ||
134 | 37, | ||
135 | 35, | ||
136 | 33, | ||
137 | 31, | ||
138 | 29, | ||
139 | 28, | ||
140 | 26, | ||
141 | 25, | ||
142 | 23, | ||
143 | 22, | ||
144 | 21, | ||
145 | 19, | ||
146 | 18, | ||
147 | 17, | ||
148 | 16, | ||
149 | 15, | ||
150 | 14, | ||
151 | 14, | ||
152 | 13, | ||
153 | 12, | ||
154 | 0, | ||
155 | }; | ||
156 | |||
157 | void | ||
158 | reset_sound(AudioChannel *chan) { | ||
159 | TIMER_CTRL_0 = 0; | ||
160 | TIMER_CTRL_1 = 0; | ||
161 | DMA_CTRL(1) = 0; | ||
162 | if (chan->pitch >= 108 || chan->n_samples == 0) { | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | // Set max volume, left-right sound, fifo reset and use timer 0 for | ||
167 | // DirectSound A. | ||
168 | SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A | ||
169 | | SOUND_DSOUND_LEFT_A | ||
170 | | SOUND_DSOUND_RIGHT_A | ||
171 | | SOUND_DSOUND_RESET_A; | ||
172 | |||
173 | // Prepare DMA copy. | ||
174 | dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, | ||
175 | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); | ||
176 | |||
177 | // Timer 1 used to stop playing samples. | ||
178 | u32 sample_duration = chan->n_samples; | ||
179 | TIMER_DATA_1 = 0xFFFF - sample_duration; | ||
180 | TIMER_CTRL_1 = TIMER_CTRL_IRQ | ||
181 | | TIMER_CTRL_ENABLE | ||
182 | | TIMER_CTRL_CASCADE; | ||
183 | |||
184 | // Timer 0 used to stop sample playing. | ||
185 | // TIMER_DATA_0 = 0xFFFF - sound_freq[chan->pitch]; | ||
186 | // TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq / chan->pitch; | ||
187 | TIMER_DATA_0 = 0xFFFF - pitch_table[chan->pitch]; | ||
188 | TIMER_CTRL_0 = TIMER_CTRL_ENABLE; | ||
189 | } | ||
190 | |||
191 | u8 square_wave[] = { | ||
192 | 0x00 + 0x80, 0xFF + 0x80 | ||
193 | }; | ||
194 | #include "text.h" | ||
195 | |||
196 | |||
197 | void | ||
198 | init_sound(AudioChannel *chan) { | ||
199 | // Enable sound. | ||
200 | SOUND_STATUS = SOUND_ENABLE; | ||
201 | |||
202 | // chan->samples = &square_wave; | ||
203 | // chan->n_samples = 2; | ||
204 | // chan->samples = &samples; | ||
205 | // chan->n_samples = LEN(samples); | ||
206 | // chan->pitch++; | ||
207 | // chan->pitch = chan->pitch == LEN(pitch_table) - 1 ? 0 : chan->pitch + 1; | ||
208 | // chan->loop = true; | ||
209 | // chan->sampling_freq = pitch_table[chan->pitch]; | ||
210 | // txt_printf("RATE: %lu", pitch_table[chan->pitch]) | ||
211 | } | ||
212 | |||
213 | void | ||
214 | irs_stop_sample(void) { | ||
215 | if (apu.chan_0.loop) { | ||
216 | reset_sound(&apu.chan_0); | ||
217 | } else { | ||
218 | TIMER_CTRL_0 = 0; | ||
219 | DMA_CTRL(1) = 0; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | // void | ||
224 | // apu_start(Apu *c, u16 adsr, u8 pitch) { | ||
225 | // // if(pitch < 108 && c->len) | ||
226 | // // c->advance = advances[pitch % 12] >> (8 - pitch / 12); | ||
227 | // // else { | ||
228 | // // c->advance = 0; | ||
229 | // // return; | ||
230 | // // } | ||
231 | // // c->a = ADSR_STEP * (adsr >> 12); | ||
232 | // // c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; | ||
233 | // // c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; | ||
234 | // // c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; | ||
235 | // // c->age = 0; | ||
236 | // // c->i = 0; | ||
237 | // // if(c->len <= 0x100) /* single cycle mode */ | ||
238 | // // c->period = NOTE_PERIOD * 337 / 2 / c->len; | ||
239 | // // else /* sample repeat mode */ | ||
240 | // // c->period = NOTE_PERIOD; | ||
241 | // } | ||