all repos — mgba @ 510a539a504ea914d7ea6f30ece1e15d11c02b39

mGBA Game Boy Advance Emulator

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 = 0x8000;
 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	audio->sampleDrift = 0;
 90
 91	int ch;
 92	for (ch = 0; ch < 16; ++ch) {
 93		audio->ch[ch].source = 0;
 94		audio->ch[ch].loopPoint = 0;
 95		audio->ch[ch].length = 0;
 96		audio->ch[ch].offset = 0;
 97		audio->ch[ch].sample = 0;
 98		audio->ch[ch].adpcmOffset = 0;
 99		audio->ch[ch].adpcmStartSample = 0;
100		audio->ch[ch].adpcmStartIndex = 0;
101		audio->ch[ch].adpcmSample = 0;
102		audio->ch[ch].adpcmIndex = 0;
103	}
104
105	blip_clear(audio->left);
106	blip_clear(audio->right);
107	audio->clock = 0;
108	audio->bias = 0x200;
109}
110
111void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples) {
112	// TODO: Share between other cores
113	mCoreSyncLockAudio(audio->p->sync);
114	audio->samples = samples;
115	blip_clear(audio->left);
116	blip_clear(audio->right);
117	audio->clock = 0;
118	mCoreSyncConsumeAudio(audio->p->sync);
119}
120
121void DSAudioWriteSOUNDCNT_LO(struct DSAudio* audio, int chan, uint16_t value) {
122	audio->ch[chan].volume = DSRegisterSOUNDxCNTGetVolumeMul(value);
123	audio->ch[chan].divider = DSRegisterSOUNDxCNTGetVolumeDiv(value);
124	if (audio->ch[chan].divider == 3) {
125		++audio->ch[chan].divider;
126	}
127}
128
129void DSAudioWriteSOUNDCNT_HI(struct DSAudio* audio, int chan, uint16_t value) {
130	DSRegisterSOUNDxCNT reg = value << 16;
131	struct DSAudioChannel* ch = &audio->ch[chan];
132
133	ch->panning = DSRegisterSOUNDxCNTGetPanning(reg);
134	ch->repeat = DSRegisterSOUNDxCNTGetRepeat(reg);
135	ch->format = DSRegisterSOUNDxCNTGetFormat(reg);
136	ch->duty = DSRegisterSOUNDxCNTGetDuty(reg);
137
138	if (ch->format > 2) {
139		mLOG(DS_AUDIO, STUB, "Unimplemented audio format %i", ch->format);
140	}
141
142	if (ch->enable && !DSRegisterSOUNDxCNTIsBusy(reg)) {
143		mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
144	} else if (!ch->enable && DSRegisterSOUNDxCNTIsBusy(reg)) {
145		ch->offset = 0;
146		ch->lfsr = 0x4000;
147		mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
148		mTimingSchedule(&audio->p->ds7.timing, &ch->updateEvent, 0);
149		if (ch->format == 2) {
150			uint32_t header = audio->p->ds7.cpu->memory.load32(audio->p->ds7.cpu, ch->source, NULL);
151			ch->offset += 4;
152			ch->adpcmStartSample = header & 0xFFFF;
153			ch->adpcmStartIndex = header >> 16;
154			ch->adpcmSample = ch->adpcmStartSample;
155			ch->adpcmIndex = ch->adpcmStartIndex;
156		}
157	}
158	ch->enable = DSRegisterSOUNDxCNTIsBusy(reg);
159}
160
161void DSAudioWriteSOUNDTMR(struct DSAudio* audio, int chan, uint16_t value) {
162	audio->ch[chan].period = (0x10000 - value) << 1;
163}
164
165void DSAudioWriteSOUNDPNT(struct DSAudio* audio, int chan, uint16_t value) {
166	audio->ch[chan].loopPoint = value << 2;
167}
168
169void DSAudioWriteSOUNDSAD(struct DSAudio* audio, int chan, uint32_t value) {
170	audio->ch[chan].source = value;
171}
172
173void DSAudioWriteSOUNDLEN(struct DSAudio* audio, int chan, uint32_t value) {
174	audio->ch[chan].length = value << 2;
175}
176
177static void _updateMixer(struct DSAudio* audio) {
178	int32_t sampleLeft = 0;
179	int32_t sampleRight = 0;
180	int ch;
181	for (ch = 0; ch < 16; ++ch) {
182		if (!audio->ch[ch].enable) {
183			continue;
184		}
185		int32_t sample = audio->ch[ch].sample << 4;
186		sample >>= audio->ch[ch].divider;
187		sample *= audio->ch[ch].volume;
188		sample >>= 2;
189
190		int32_t left = sample * (0x7F - audio->ch[ch].panning);
191		int32_t right = sample * audio->ch[ch].panning;
192		sampleLeft += left >>= 16;
193		sampleRight += right >>= 16;
194	}
195	audio->sampleLeft = sampleLeft >> 6;
196	audio->sampleRight = sampleRight >> 6;
197}
198
199static void _updateAdpcm(struct DSAudioChannel* ch, int sample) {
200	ch->sample = ch->adpcmSample;
201	if (ch->adpcmIndex < 0) {
202		ch->adpcmIndex = 0;
203	} else if (ch->adpcmIndex > 88) {
204		ch->adpcmIndex = 88;
205	}
206	int16_t diff = _adpcmTable[ch->adpcmIndex] >> 3;
207	if (sample & 1) {
208		diff += _adpcmTable[ch->adpcmIndex] >> 2;
209	}
210	if (sample & 2) {
211		diff += _adpcmTable[ch->adpcmIndex] >> 1;
212	}
213	if (sample & 4) {
214		diff += _adpcmTable[ch->adpcmIndex];
215	}
216	if (sample & 8) {
217		int32_t newSample = ch->adpcmSample - diff;
218		if (newSample < -0x7FFF) {
219			ch->adpcmSample = -0x7FFF;
220		} else {
221			ch->adpcmSample = newSample;
222		}
223	} else {
224		int32_t newSample = ch->adpcmSample + diff;
225		if (newSample > 0x7FFF) {
226			ch->adpcmSample = 0x7FFF;
227		} else {
228			ch->adpcmSample = newSample;
229		}
230	}
231	ch->adpcmIndex += _adpcmIndexTable[sample & 0x7];
232}
233
234static void _updateNoiseChannel(struct DSAudioChannel* ch) {
235	int lsb = ch->lfsr & 1;
236	ch->high = lsb;
237	ch->lfsr >>= 1;
238	ch->lfsr ^= lsb * 0x6000;
239}
240
241static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate) {
242	struct DSAudioChannel* ch = user;
243	struct ARMCore* cpu = ch->p->p->ds7.cpu;
244	switch (ch->format) {
245	case 0:
246		ch->sample = cpu->memory.load8(cpu, ch->offset + ch->source, NULL) << 8;
247		++ch->offset;
248		break;
249	case 1:
250		ch->sample = cpu->memory.load16(cpu, ch->offset + ch->source, NULL);
251		ch->offset += 2;
252		break;
253	case 2:
254		_updateAdpcm(ch, (cpu->memory.load8(cpu, ch->offset + ch->source, NULL) >> ch->adpcmOffset) & 0xF);
255		ch->offset += ch->adpcmOffset >> 2;
256		ch->adpcmOffset ^= 4;
257		if (ch->offset == ch->loopPoint && !ch->adpcmOffset) {
258			ch->adpcmStartSample = ch->adpcmSample;
259			ch->adpcmStartIndex = ch->adpcmIndex;
260		}
261		break;
262	case 3:
263		switch (ch->index) {
264		case 8:
265		case 9:
266		case 10:
267		case 11:
268		case 12:
269		case 13:
270			ch->high = !ch->high;
271			break;
272		case 14:
273		case 15:
274			_updateNoiseChannel(ch);
275			break;
276		}
277		ch->sample = (0xFFFF * ch->high) - 0x8000;
278	}
279	_updateMixer(ch->p);
280	if (ch->format == 3 && ch->index < 14) {
281		int32_t period = ch->period;
282		if (ch->high) {
283			period *= ch->duty + 1;
284		} else {
285			period *= 7 - ch->duty;
286		}
287		mTimingSchedule(timing, &ch->updateEvent, period - cyclesLate);
288		return;
289	}
290
291	switch (ch->repeat) {
292	case 1:
293		if (ch->offset >= ch->length + ch->loopPoint) {
294			ch->offset = ch->loopPoint;
295			if (ch->format == 2) {
296				ch->adpcmSample = ch->adpcmStartSample;
297				ch->adpcmIndex = ch->adpcmStartIndex;
298			}
299		}
300		break;
301	case 2:
302		if (ch->offset >= ch->length + ch->loopPoint) {
303			ch->enable = false;
304			ch->p->p->memory.io7[(DS7_REG_SOUND0CNT_HI + (ch->index << 4)) >> 1] &= 0x7FFF;
305		}
306		break;
307	}
308	if (ch->enable) {
309		mTimingSchedule(timing, &ch->updateEvent, ch->period - cyclesLate);
310	}
311}
312
313static int _applyBias(struct DSAudio* audio, int sample) {
314	sample += audio->bias;
315	if (sample >= 0x400) {
316		sample = 0x3FF;
317	} else if (sample < 0) {
318		sample = 0;
319	}
320	return ((sample - audio->bias) * audio->masterVolume) >> 3;
321}
322
323static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
324	struct DSAudio* audio = user;
325
326	int16_t sampleLeft = _applyBias(audio, audio->sampleLeft);
327	int16_t sampleRight = _applyBias(audio, audio->sampleRight);
328
329	audio->sampleDrift += DS_ARM7TDMI_FREQUENCY % audio->sampleRate;
330
331	if (audio->sampleDrift >= audio->sampleRate) {
332		++audio->sampleInterval;
333	}
334
335	mCoreSyncLockAudio(audio->p->sync);
336	unsigned produced;
337	if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
338		blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
339		blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
340		audio->lastLeft = sampleLeft;
341		audio->lastRight = sampleRight;
342		// blip clock is in ARM9 cycles, but sampleInterval is in ARM7 cycles
343		audio->clock += audio->sampleInterval * 2;
344		if (audio->clock >= CLOCKS_PER_FRAME) {
345			blip_end_frame(audio->left, CLOCKS_PER_FRAME);
346			blip_end_frame(audio->right, CLOCKS_PER_FRAME);
347			audio->clock -= CLOCKS_PER_FRAME;
348		}
349	}
350	produced = blip_samples_avail(audio->left);
351	if (audio->p->stream && audio->p->stream->postAudioFrame) {
352		audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
353	}
354	bool wait = produced >= audio->samples;
355	if (!mCoreSyncProduceAudio(audio->p->sync, audio->left, audio->samples)) {
356		// Interrupted
357		audio->p->earlyExit = true;
358	}
359
360	if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
361		audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
362	}
363	mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
364
365	if (audio->sampleDrift >= audio->sampleRate) {
366		--audio->sampleInterval;
367		audio->sampleDrift -= audio->sampleRate;
368	}
369}