all repos — mgba @ 498aa541fc660c8755cda99cf8889171b3bd5381

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 = 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
 22void DSAudioInit(struct DSAudio* audio, size_t samples) {
 23	audio->samples = samples;
 24	audio->left = blip_new(BLIP_BUFFER_SIZE);
 25	audio->right = blip_new(BLIP_BUFFER_SIZE);
 26
 27	audio->ch[0].updateEvent.name = "DS Audio Channel 0";
 28	audio->ch[1].updateEvent.name = "DS Audio Channel 1";
 29	audio->ch[2].updateEvent.name = "DS Audio Channel 2";
 30	audio->ch[3].updateEvent.name = "DS Audio Channel 3";
 31	audio->ch[4].updateEvent.name = "DS Audio Channel 4";
 32	audio->ch[5].updateEvent.name = "DS Audio Channel 5";
 33	audio->ch[6].updateEvent.name = "DS Audio Channel 6";
 34	audio->ch[7].updateEvent.name = "DS Audio Channel 7";
 35	audio->ch[8].updateEvent.name = "DS Audio Channel 8";
 36	audio->ch[9].updateEvent.name = "DS Audio Channel 9";
 37	audio->ch[10].updateEvent.name = "DS Audio Channel 10";
 38	audio->ch[11].updateEvent.name = "DS Audio Channel 11";
 39	audio->ch[12].updateEvent.name = "DS Audio Channel 12";
 40	audio->ch[13].updateEvent.name = "DS Audio Channel 13";
 41	audio->ch[14].updateEvent.name = "DS Audio Channel 14";
 42	audio->ch[15].updateEvent.name = "DS Audio Channel 15";
 43
 44	int ch;
 45	for (ch = 0; ch < 16; ++ch) {
 46		audio->ch[ch].index = ch;
 47		audio->ch[ch].updateEvent.priority = 0x10 | ch;
 48		audio->ch[ch].updateEvent.context = &audio->ch[ch];
 49		audio->ch[ch].updateEvent.callback = _updateChannel;
 50		audio->ch[ch].p = audio;
 51		audio->forceDisableCh[ch] = false;
 52	}
 53	audio->masterVolume = DS_AUDIO_VOLUME_MAX;
 54
 55	audio->sampleEvent.name = "DS Audio Sample";
 56	audio->sampleEvent.context = audio;
 57	audio->sampleEvent.callback = _sample;
 58	audio->sampleEvent.priority = 0x110;
 59
 60	blip_set_rates(audio->left, DS_ARM7TDMI_FREQUENCY, 96000);
 61	blip_set_rates(audio->right, DS_ARM7TDMI_FREQUENCY, 96000);
 62}
 63
 64void DSAudioDeinit(struct DSAudio* audio) {
 65	blip_delete(audio->left);
 66	blip_delete(audio->right);
 67}
 68
 69void DSAudioReset(struct DSAudio* audio) {
 70	mTimingDeschedule(&audio->p->ds7.timing, &audio->sampleEvent);
 71	mTimingSchedule(&audio->p->ds7.timing, &audio->sampleEvent, 0);
 72	audio->sampleRate = 0x8000;
 73	audio->sampleInterval = DS_ARM7TDMI_FREQUENCY / audio->sampleRate;
 74
 75	int ch;
 76	for (ch = 0; ch < 16; ++ch) {
 77		audio->ch[ch].source = 0;
 78		audio->ch[ch].loopPoint = 0;
 79		audio->ch[ch].length = 0;
 80		audio->ch[ch].offset = 0;
 81		audio->ch[ch].sample = 0;
 82	}
 83
 84	blip_clear(audio->left);
 85	blip_clear(audio->right);
 86	audio->clock = 0;
 87	audio->bias = 0x200;
 88}
 89
 90void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples) {
 91	// TODO: Share between other cores
 92	mCoreSyncLockAudio(audio->p->sync);
 93	audio->samples = samples;
 94	blip_clear(audio->left);
 95	blip_clear(audio->right);
 96	audio->clock = 0;
 97	mCoreSyncConsumeAudio(audio->p->sync);
 98}
 99
100void DSAudioWriteSOUNDCNT_LO(struct DSAudio* audio, int chan, uint16_t value) {
101	audio->ch[chan].volume = DSRegisterSOUNDxCNTGetVolumeMul(value);
102	audio->ch[chan].divider = DSRegisterSOUNDxCNTGetVolumeDiv(value);
103	if (audio->ch[chan].divider == 3) {
104		++audio->ch[chan].divider;
105	}
106}
107
108void DSAudioWriteSOUNDCNT_HI(struct DSAudio* audio, int chan, uint16_t value) {
109	DSRegisterSOUNDxCNT reg = value << 16;
110	struct DSAudioChannel* ch = &audio->ch[chan];
111
112	ch->panning = DSRegisterSOUNDxCNTGetPanning(reg);
113	ch->repeat = DSRegisterSOUNDxCNTGetRepeat(reg);
114	ch->format = DSRegisterSOUNDxCNTGetFormat(reg);
115
116	if (ch->format >= 2) {
117		mLOG(DS_AUDIO, STUB, "Unimplemented audio format %i", ch->format);
118	}
119
120	if (ch->enable && !DSRegisterSOUNDxCNTIsBusy(reg)) {
121		mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
122	} else if (!ch->enable && DSRegisterSOUNDxCNTIsBusy(reg)) {
123		ch->offset = 0;
124		mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
125		mTimingSchedule(&audio->p->ds7.timing, &ch->updateEvent, 0);
126	}
127	ch->enable = DSRegisterSOUNDxCNTIsBusy(reg);
128}
129
130void DSAudioWriteSOUNDTMR(struct DSAudio* audio, int chan, uint16_t value) {
131	audio->ch[chan].period = (0x10000 - value) << 1;
132}
133
134void DSAudioWriteSOUNDPNT(struct DSAudio* audio, int chan, uint16_t value) {
135	audio->ch[chan].loopPoint = value << 2;
136}
137
138void DSAudioWriteSOUNDSAD(struct DSAudio* audio, int chan, uint32_t value) {
139	audio->ch[chan].source = value;
140}
141
142void DSAudioWriteSOUNDLEN(struct DSAudio* audio, int chan, uint32_t value) {
143	audio->ch[chan].length = value << 2;
144}
145
146static void _updateMixer(struct DSAudio* audio) {
147	int32_t sampleLeft = 0;
148	int32_t sampleRight = 0;
149	int ch;
150	for (ch = 0; ch < 16; ++ch) {
151		if (!audio->ch[ch].enable) {
152			continue;
153		}
154		int32_t sample = audio->ch[ch].sample << 4;
155		sample >>= audio->ch[ch].divider;
156		sample *= audio->ch[ch].volume;
157		sample >>= 2;
158
159		int32_t left = sample * (0x7F - audio->ch[ch].panning);
160		int32_t right = sample * audio->ch[ch].panning;
161		sampleLeft += left >>= 16;
162		sampleRight += right >>= 16;
163	}
164	audio->sampleLeft = sampleLeft >> 6;
165	audio->sampleRight = sampleRight >> 6;
166}
167
168static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate) {
169	struct DSAudioChannel* ch = user;
170	struct ARMCore* cpu = ch->p->p->ds7.cpu;
171	switch (ch->format) {
172	case 0:
173		ch->sample = cpu->memory.load8(cpu, ch->offset + ch->source, NULL) << 8;
174		++ch->offset;
175		break;
176	case 1:
177		ch->sample = cpu->memory.load16(cpu, ch->offset + ch->source, NULL);
178		ch->offset += 2;
179		break;
180	case 2:
181		// TODO
182		ch->enable = false;
183		break;
184	}
185	_updateMixer(ch->p);
186	switch (ch->repeat) {
187	case 1:
188		if (ch->offset >= ch->length) {
189			ch->offset = ch->loopPoint;
190		}
191		break;
192	case 2:
193		if (ch->offset >= ch->length) {
194			ch->enable = false;
195			ch->p->p->memory.io7[(DS7_REG_SOUND0CNT_HI + (ch->index << 4)) >> 1] &= 0x7FFF;
196		}
197		break;
198	}
199	if (ch->enable) {
200		mTimingSchedule(timing, &ch->updateEvent, ch->period - cyclesLate);
201	}
202}
203
204static int _applyBias(struct DSAudio* audio, int sample) {
205	sample += audio->bias;
206	if (sample >= 0x400) {
207		sample = 0x3FF;
208	} else if (sample < 0) {
209		sample = 0;
210	}
211	return ((sample - audio->bias) * audio->masterVolume) >> 3;
212}
213
214static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
215	struct DSAudio* audio = user;
216
217	int16_t sampleLeft = _applyBias(audio, audio->sampleLeft);
218	int16_t sampleRight = _applyBias(audio, audio->sampleRight);
219
220	mCoreSyncLockAudio(audio->p->sync);
221	unsigned produced;
222	if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
223		blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
224		blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
225		audio->lastLeft = sampleLeft;
226		audio->lastRight = sampleRight;
227		audio->clock += audio->sampleInterval;
228		if (audio->clock >= CLOCKS_PER_FRAME) {
229			blip_end_frame(audio->left, audio->clock);
230			blip_end_frame(audio->right, audio->clock);
231			audio->clock -= CLOCKS_PER_FRAME;
232		}
233	}
234	produced = blip_samples_avail(audio->left);
235	if (audio->p->stream && audio->p->stream->postAudioFrame) {
236		audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
237	}
238	bool wait = produced >= audio->samples;
239	mCoreSyncProduceAudio(audio->p->sync, wait);
240
241	if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
242		audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
243	}
244	mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
245}