src/ds/audio.c (view raw)
1/* Copyright (c) 2013-2017 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/audio.h>
7
8#include <mgba/core/blip_buf.h>
9#include <mgba/core/sync.h>
10#include <mgba/internal/ds/ds.h>
11
12mLOG_DEFINE_CATEGORY(DS_AUDIO, "DS Audio", "ds.audio");
13
14static const unsigned BLIP_BUFFER_SIZE = 0x4000;
15static const int CLOCKS_PER_FRAME = 0x4000;
16const int DS_AUDIO_VOLUME_MAX = 0x100;
17
18static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate);
19static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
20static void _updateMixer(struct DSAudio*);
21
22static const int _adpcmIndexTable[8] = {
23 -1, -1, -1, -1, 2, 4, 6, 8
24};
25
26static const uint16_t _adpcmTable[89] = {
27 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x0010, 0x0011, 0x0013, 0x0015,
28 0x0017, 0x0019, 0x001C, 0x001F, 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
29 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, 0x009D, 0x00AD, 0x00BE, 0x00D1,
30 0x00E6, 0x00FD, 0x0117, 0x0133, 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
31 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, 0x0610, 0x06AB, 0x0756, 0x0812,
32 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
33 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, 0x3BB9, 0x41B2, 0x4844, 0x4F7E,
34 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF
35};
36
37void DSAudioInit(struct DSAudio* audio, size_t samples) {
38 audio->samples = samples;
39 audio->left = blip_new(BLIP_BUFFER_SIZE);
40 audio->right = blip_new(BLIP_BUFFER_SIZE);
41
42 audio->ch[0].updateEvent.name = "DS Audio Channel 0";
43 audio->ch[1].updateEvent.name = "DS Audio Channel 1";
44 audio->ch[2].updateEvent.name = "DS Audio Channel 2";
45 audio->ch[3].updateEvent.name = "DS Audio Channel 3";
46 audio->ch[4].updateEvent.name = "DS Audio Channel 4";
47 audio->ch[5].updateEvent.name = "DS Audio Channel 5";
48 audio->ch[6].updateEvent.name = "DS Audio Channel 6";
49 audio->ch[7].updateEvent.name = "DS Audio Channel 7";
50 audio->ch[8].updateEvent.name = "DS Audio Channel 8";
51 audio->ch[9].updateEvent.name = "DS Audio Channel 9";
52 audio->ch[10].updateEvent.name = "DS Audio Channel 10";
53 audio->ch[11].updateEvent.name = "DS Audio Channel 11";
54 audio->ch[12].updateEvent.name = "DS Audio Channel 12";
55 audio->ch[13].updateEvent.name = "DS Audio Channel 13";
56 audio->ch[14].updateEvent.name = "DS Audio Channel 14";
57 audio->ch[15].updateEvent.name = "DS Audio Channel 15";
58
59 int ch;
60 for (ch = 0; ch < 16; ++ch) {
61 audio->ch[ch].index = ch;
62 audio->ch[ch].updateEvent.priority = 0x10 | ch;
63 audio->ch[ch].updateEvent.context = &audio->ch[ch];
64 audio->ch[ch].updateEvent.callback = _updateChannel;
65 audio->ch[ch].p = audio;
66 audio->forceDisableCh[ch] = false;
67 }
68 audio->masterVolume = DS_AUDIO_VOLUME_MAX;
69
70 audio->sampleEvent.name = "DS Audio Sample";
71 audio->sampleEvent.context = audio;
72 audio->sampleEvent.callback = _sample;
73 audio->sampleEvent.priority = 0x110;
74
75 blip_set_rates(audio->left, DS_ARM7TDMI_FREQUENCY, 96000);
76 blip_set_rates(audio->right, DS_ARM7TDMI_FREQUENCY, 96000);
77}
78
79void DSAudioDeinit(struct DSAudio* audio) {
80 blip_delete(audio->left);
81 blip_delete(audio->right);
82}
83
84void DSAudioReset(struct DSAudio* audio) {
85 mTimingDeschedule(&audio->p->ds7.timing, &audio->sampleEvent);
86 mTimingSchedule(&audio->p->ds7.timing, &audio->sampleEvent, 0);
87 audio->sampleRate = 0x8000;
88 audio->sampleInterval = DS_ARM7TDMI_FREQUENCY / audio->sampleRate;
89
90 int ch;
91 for (ch = 0; ch < 16; ++ch) {
92 audio->ch[ch].source = 0;
93 audio->ch[ch].loopPoint = 0;
94 audio->ch[ch].length = 0;
95 audio->ch[ch].offset = 0;
96 audio->ch[ch].sample = 0;
97 audio->ch[ch].adpcmOffset = 0;
98 audio->ch[ch].adpcmStartSample = 0;
99 audio->ch[ch].adpcmStartIndex = 0;
100 audio->ch[ch].adpcmSample = 0;
101 audio->ch[ch].adpcmIndex = 0;
102 }
103
104 blip_clear(audio->left);
105 blip_clear(audio->right);
106 audio->clock = 0;
107 audio->bias = 0x200;
108}
109
110void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples) {
111 // TODO: Share between other cores
112 mCoreSyncLockAudio(audio->p->sync);
113 audio->samples = samples;
114 blip_clear(audio->left);
115 blip_clear(audio->right);
116 audio->clock = 0;
117 mCoreSyncConsumeAudio(audio->p->sync);
118}
119
120void DSAudioWriteSOUNDCNT_LO(struct DSAudio* audio, int chan, uint16_t value) {
121 audio->ch[chan].volume = DSRegisterSOUNDxCNTGetVolumeMul(value);
122 audio->ch[chan].divider = DSRegisterSOUNDxCNTGetVolumeDiv(value);
123 if (audio->ch[chan].divider == 3) {
124 ++audio->ch[chan].divider;
125 }
126}
127
128void DSAudioWriteSOUNDCNT_HI(struct DSAudio* audio, int chan, uint16_t value) {
129 DSRegisterSOUNDxCNT reg = value << 16;
130 struct DSAudioChannel* ch = &audio->ch[chan];
131
132 ch->panning = DSRegisterSOUNDxCNTGetPanning(reg);
133 ch->repeat = DSRegisterSOUNDxCNTGetRepeat(reg);
134 ch->format = DSRegisterSOUNDxCNTGetFormat(reg);
135
136 if (ch->format > 2) {
137 mLOG(DS_AUDIO, STUB, "Unimplemented audio format %i", ch->format);
138 }
139
140 if (ch->enable && !DSRegisterSOUNDxCNTIsBusy(reg)) {
141 mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
142 } else if (!ch->enable && DSRegisterSOUNDxCNTIsBusy(reg)) {
143 ch->offset = 0;
144 mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
145 mTimingSchedule(&audio->p->ds7.timing, &ch->updateEvent, 0);
146 if (ch->format == 2) {
147 uint32_t header = audio->p->ds7.cpu->memory.load32(audio->p->ds7.cpu, ch->source, NULL);
148 ch->offset += 4;
149 ch->adpcmStartSample = header & 0xFFFF;
150 ch->adpcmStartIndex = header >> 16;
151 ch->adpcmSample = ch->adpcmStartSample;
152 ch->adpcmIndex = ch->adpcmStartIndex;
153 }
154 }
155 ch->enable = DSRegisterSOUNDxCNTIsBusy(reg);
156}
157
158void DSAudioWriteSOUNDTMR(struct DSAudio* audio, int chan, uint16_t value) {
159 audio->ch[chan].period = (0x10000 - value) << 1;
160}
161
162void DSAudioWriteSOUNDPNT(struct DSAudio* audio, int chan, uint16_t value) {
163 audio->ch[chan].loopPoint = value << 2;
164}
165
166void DSAudioWriteSOUNDSAD(struct DSAudio* audio, int chan, uint32_t value) {
167 audio->ch[chan].source = value;
168}
169
170void DSAudioWriteSOUNDLEN(struct DSAudio* audio, int chan, uint32_t value) {
171 audio->ch[chan].length = value << 2;
172}
173
174static void _updateMixer(struct DSAudio* audio) {
175 int32_t sampleLeft = 0;
176 int32_t sampleRight = 0;
177 int ch;
178 for (ch = 0; ch < 16; ++ch) {
179 if (!audio->ch[ch].enable) {
180 continue;
181 }
182 int32_t sample = audio->ch[ch].sample << 4;
183 sample >>= audio->ch[ch].divider;
184 sample *= audio->ch[ch].volume;
185 sample >>= 2;
186
187 int32_t left = sample * (0x7F - audio->ch[ch].panning);
188 int32_t right = sample * audio->ch[ch].panning;
189 sampleLeft += left >>= 16;
190 sampleRight += right >>= 16;
191 }
192 audio->sampleLeft = sampleLeft >> 6;
193 audio->sampleRight = sampleRight >> 6;
194}
195
196static void _updateAdpcm(struct DSAudioChannel* ch, int sample) {
197 ch->sample = ch->adpcmSample;
198 if (ch->adpcmIndex < 0) {
199 ch->adpcmIndex = 0;
200 } else if (ch->adpcmIndex > 88) {
201 ch->adpcmIndex = 88;
202 }
203 int16_t diff = _adpcmTable[ch->adpcmIndex] >> 3;
204 if (sample & 1) {
205 diff += _adpcmTable[ch->adpcmIndex] >> 2;
206 }
207 if (sample & 2) {
208 diff += _adpcmTable[ch->adpcmIndex] >> 1;
209 }
210 if (sample & 4) {
211 diff += _adpcmTable[ch->adpcmIndex];
212 }
213 if (sample & 8) {
214 int32_t newSample = ch->adpcmSample - diff;
215 if (newSample < -0x7FFF) {
216 ch->adpcmSample = -0x7FFF;
217 } else {
218 ch->adpcmSample = newSample;
219 }
220 } else {
221 int32_t newSample = ch->adpcmSample + diff;
222 if (newSample > 0x7FFF) {
223 ch->adpcmSample = 0x7FFF;
224 } else {
225 ch->adpcmSample = newSample;
226 }
227 }
228 ch->adpcmIndex += _adpcmIndexTable[sample & 0x7];
229}
230
231static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate) {
232 struct DSAudioChannel* ch = user;
233 struct ARMCore* cpu = ch->p->p->ds7.cpu;
234 switch (ch->format) {
235 case 0:
236 ch->sample = cpu->memory.load8(cpu, ch->offset + ch->source, NULL) << 8;
237 ++ch->offset;
238 break;
239 case 1:
240 ch->sample = cpu->memory.load16(cpu, ch->offset + ch->source, NULL);
241 ch->offset += 2;
242 break;
243 case 2:
244 _updateAdpcm(ch, (cpu->memory.load8(cpu, ch->offset + ch->source, NULL) >> ch->adpcmOffset) & 0xF);
245 ch->offset += ch->adpcmOffset >> 2;
246 ch->adpcmOffset ^= 4;
247 if (ch->offset == ch->loopPoint && !ch->adpcmOffset) {
248 ch->adpcmStartSample = ch->adpcmSample;
249 ch->adpcmStartIndex = ch->adpcmIndex;
250 }
251 break;
252 }
253 _updateMixer(ch->p);
254 switch (ch->repeat) {
255 case 1:
256 if (ch->offset >= ch->length + ch->loopPoint) {
257 ch->offset = ch->loopPoint;
258 if (ch->format == 2) {
259 ch->adpcmSample = ch->adpcmStartSample;
260 ch->adpcmIndex = ch->adpcmStartIndex;
261 }
262 }
263 break;
264 case 2:
265 if (ch->offset >= ch->length + ch->loopPoint) {
266 ch->enable = false;
267 ch->p->p->memory.io7[(DS7_REG_SOUND0CNT_HI + (ch->index << 4)) >> 1] &= 0x7FFF;
268 }
269 break;
270 }
271 if (ch->enable) {
272 mTimingSchedule(timing, &ch->updateEvent, ch->period - cyclesLate);
273 }
274}
275
276static int _applyBias(struct DSAudio* audio, int sample) {
277 sample += audio->bias;
278 if (sample >= 0x400) {
279 sample = 0x3FF;
280 } else if (sample < 0) {
281 sample = 0;
282 }
283 return ((sample - audio->bias) * audio->masterVolume) >> 3;
284}
285
286static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
287 struct DSAudio* audio = user;
288
289 int16_t sampleLeft = _applyBias(audio, audio->sampleLeft);
290 int16_t sampleRight = _applyBias(audio, audio->sampleRight);
291
292 mCoreSyncLockAudio(audio->p->sync);
293 unsigned produced;
294 if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
295 blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
296 blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
297 audio->lastLeft = sampleLeft;
298 audio->lastRight = sampleRight;
299 audio->clock += audio->sampleInterval;
300 if (audio->clock >= CLOCKS_PER_FRAME) {
301 blip_end_frame(audio->left, audio->clock);
302 blip_end_frame(audio->right, audio->clock);
303 audio->clock -= CLOCKS_PER_FRAME;
304 }
305 }
306 produced = blip_samples_avail(audio->left);
307 if (audio->p->stream && audio->p->stream->postAudioFrame) {
308 audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
309 }
310 bool wait = produced >= audio->samples;
311 mCoreSyncProduceAudio(audio->p->sync, wait);
312
313 if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
314 audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
315 }
316 mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
317}