1 /* Copyright (c) 2013-2016 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/gba/audio.h>
7 
8 #include <mgba/internal/arm/macros.h>
9 #include <mgba/core/blip_buf.h>
10 #include <mgba/core/sync.h>
11 #include <mgba/internal/gba/dma.h>
12 #include <mgba/internal/gba/gba.h>
13 #include <mgba/internal/gba/io.h>
14 #include <mgba/internal/gba/serialize.h>
15 #include <mgba/internal/gba/video.h>
16 
17 #define MP2K_LOCK_MAX 8
18 
19 #ifdef _3DS
20 #define blip_add_delta blip_add_delta_fast
21 #endif
22 
23 mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio");
24 
25 const unsigned GBA_AUDIO_SAMPLES = 2048;
26 const int GBA_AUDIO_VOLUME_MAX = 0x100;
27 
28 static const int CLOCKS_PER_FRAME = 0x800;
29 
30 static int _applyBias(struct GBAAudio* audio, int sample);
31 static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
32 
GBAAudioInit(struct GBAAudio * audio,size_t samples)33 void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
34 	audio->sampleEvent.context = audio;
35 	audio->sampleEvent.name = "GBA Audio Sample";
36 	audio->sampleEvent.callback = _sample;
37 	audio->sampleEvent.priority = 0x18;
38 	audio->psg.p = NULL;
39 	uint8_t* nr52 = (uint8_t*) &audio->p->memory.io[REG_SOUNDCNT_X >> 1];
40 #ifdef __BIG_ENDIAN__
41 	++nr52;
42 #endif
43 	GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA);
44 	audio->psg.timing = &audio->p->timing;
45 	audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
46 	audio->samples = samples;
47 	// Guess too large; we hang producing extra samples if we guess too low
48 	blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
49 	blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
50 
51 	audio->externalMixing = false;
52 	audio->forceDisableChA = false;
53 	audio->forceDisableChB = false;
54 	audio->masterVolume = GBA_AUDIO_VOLUME_MAX;
55 	audio->mixer = NULL;
56 }
57 
GBAAudioReset(struct GBAAudio * audio)58 void GBAAudioReset(struct GBAAudio* audio) {
59 	GBAudioReset(&audio->psg);
60 	mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
61 	mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
62 	audio->chA.dmaSource = 1;
63 	audio->chB.dmaSource = 2;
64 	audio->chA.fifoWrite = 0;
65 	audio->chA.fifoRead = 0;
66 	audio->chA.internalSample = 0;
67 	audio->chA.internalRemaining = 0;
68 	memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo));
69 	audio->chA.sample = 0;
70 	audio->chB.fifoWrite = 0;
71 	audio->chB.fifoRead = 0;
72 	audio->chB.internalSample = 0;
73 	audio->chB.internalRemaining = 0;
74 	memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo));
75 	audio->chB.sample = 0;
76 	audio->sampleRate = 0x8000;
77 	audio->soundbias = 0x200;
78 	audio->volume = 0;
79 	audio->volumeChA = false;
80 	audio->volumeChB = false;
81 	audio->chARight = false;
82 	audio->chALeft = false;
83 	audio->chATimer = false;
84 	audio->chBRight = false;
85 	audio->chBLeft = false;
86 	audio->chBTimer = false;
87 	audio->enable = false;
88 	audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
89 	audio->psg.sampleInterval = audio->sampleInterval;
90 
91 	blip_clear(audio->psg.left);
92 	blip_clear(audio->psg.right);
93 	audio->clock = 0;
94 }
95 
GBAAudioDeinit(struct GBAAudio * audio)96 void GBAAudioDeinit(struct GBAAudio* audio) {
97 	GBAudioDeinit(&audio->psg);
98 }
99 
GBAAudioResizeBuffer(struct GBAAudio * audio,size_t samples)100 void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
101 	mCoreSyncLockAudio(audio->p->sync);
102 	audio->samples = samples;
103 	blip_clear(audio->psg.left);
104 	blip_clear(audio->psg.right);
105 	audio->clock = 0;
106 	mCoreSyncConsumeAudio(audio->p->sync);
107 }
108 
GBAAudioScheduleFifoDma(struct GBAAudio * audio,int number,struct GBADMA * info)109 void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info) {
110 	info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
111 	info->reg = GBADMARegisterSetWidth(info->reg, 1);
112 	switch (info->dest) {
113 	case BASE_IO | REG_FIFO_A_LO:
114 		audio->chA.dmaSource = number;
115 		break;
116 	case BASE_IO | REG_FIFO_B_LO:
117 		audio->chB.dmaSource = number;
118 		break;
119 	default:
120 		mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
121 		return;
122 	}
123 	uint32_t source = info->source;
124 	uint32_t magic[2] = {
125 		audio->p->cpu->memory.load32(audio->p->cpu, source - 0x350, NULL),
126 		audio->p->cpu->memory.load32(audio->p->cpu, source - 0x980, NULL)
127 	};
128 	if (audio->mixer) {
129 		if (magic[0] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
130 			audio->mixer->engage(audio->mixer, source - 0x350);
131 		} else if (magic[1] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
132 			audio->mixer->engage(audio->mixer, source - 0x980);
133 		} else {
134 			audio->externalMixing = false;
135 		}
136 	}
137 }
138 
GBAAudioWriteSOUND1CNT_LO(struct GBAAudio * audio,uint16_t value)139 void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
140 	GBAudioWriteNR10(&audio->psg, value);
141 }
142 
GBAAudioWriteSOUND1CNT_HI(struct GBAAudio * audio,uint16_t value)143 void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
144 	GBAudioWriteNR11(&audio->psg, value);
145 	GBAudioWriteNR12(&audio->psg, value >> 8);
146 }
147 
GBAAudioWriteSOUND1CNT_X(struct GBAAudio * audio,uint16_t value)148 void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
149 	GBAudioWriteNR13(&audio->psg, value);
150 	GBAudioWriteNR14(&audio->psg, value >> 8);
151 }
152 
GBAAudioWriteSOUND2CNT_LO(struct GBAAudio * audio,uint16_t value)153 void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
154 	GBAudioWriteNR21(&audio->psg, value);
155 	GBAudioWriteNR22(&audio->psg, value >> 8);
156 }
157 
GBAAudioWriteSOUND2CNT_HI(struct GBAAudio * audio,uint16_t value)158 void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
159 	GBAudioWriteNR23(&audio->psg, value);
160 	GBAudioWriteNR24(&audio->psg, value >> 8);
161 }
162 
GBAAudioWriteSOUND3CNT_LO(struct GBAAudio * audio,uint16_t value)163 void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
164 	audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
165 	audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
166 	GBAudioWriteNR30(&audio->psg, value);
167 }
168 
GBAAudioWriteSOUND3CNT_HI(struct GBAAudio * audio,uint16_t value)169 void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
170 	GBAudioWriteNR31(&audio->psg, value);
171 	audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
172 }
173 
GBAAudioWriteSOUND3CNT_X(struct GBAAudio * audio,uint16_t value)174 void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
175 	GBAudioWriteNR33(&audio->psg, value);
176 	GBAudioWriteNR34(&audio->psg, value >> 8);
177 }
178 
GBAAudioWriteSOUND4CNT_LO(struct GBAAudio * audio,uint16_t value)179 void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
180 	GBAudioWriteNR41(&audio->psg, value);
181 	GBAudioWriteNR42(&audio->psg, value >> 8);
182 }
183 
GBAAudioWriteSOUND4CNT_HI(struct GBAAudio * audio,uint16_t value)184 void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
185 	GBAudioWriteNR43(&audio->psg, value);
186 	GBAudioWriteNR44(&audio->psg, value >> 8);
187 }
188 
GBAAudioWriteSOUNDCNT_LO(struct GBAAudio * audio,uint16_t value)189 void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
190 	GBAudioWriteNR50(&audio->psg, value);
191 	GBAudioWriteNR51(&audio->psg, value >> 8);
192 }
193 
GBAAudioWriteSOUNDCNT_HI(struct GBAAudio * audio,uint16_t value)194 void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
195 	audio->volume = GBARegisterSOUNDCNT_HIGetVolume(value);
196 	audio->volumeChA = GBARegisterSOUNDCNT_HIGetVolumeChA(value);
197 	audio->volumeChB = GBARegisterSOUNDCNT_HIGetVolumeChB(value);
198 	audio->chARight = GBARegisterSOUNDCNT_HIGetChARight(value);
199 	audio->chALeft = GBARegisterSOUNDCNT_HIGetChALeft(value);
200 	audio->chATimer = GBARegisterSOUNDCNT_HIGetChATimer(value);
201 	audio->chBRight = GBARegisterSOUNDCNT_HIGetChBRight(value);
202 	audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value);
203 	audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value);
204 	if (GBARegisterSOUNDCNT_HIIsChAReset(value)) {
205 		audio->chA.fifoWrite = 0;
206 		audio->chA.fifoRead = 0;
207 	}
208 	if (GBARegisterSOUNDCNT_HIIsChBReset(value)) {
209 		audio->chB.fifoWrite = 0;
210 		audio->chB.fifoRead = 0;
211 	}
212 }
213 
GBAAudioWriteSOUNDCNT_X(struct GBAAudio * audio,uint16_t value)214 void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
215 	audio->enable = GBAudioEnableGetEnable(value);
216 	GBAudioWriteNR52(&audio->psg, value);
217 }
218 
GBAAudioWriteSOUNDBIAS(struct GBAAudio * audio,uint16_t value)219 void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
220 	audio->soundbias = value;
221 }
222 
GBAAudioWriteWaveRAM(struct GBAAudio * audio,int address,uint32_t value)223 void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
224 	int bank = !audio->psg.ch3.bank;
225 
226 	// When the audio hardware is turned off, it acts like bank 0 has been
227 	// selected in SOUND3CNT_L, so any read comes from bank 1.
228 	if (!audio->enable) {
229 		bank = 1;
230 	}
231 
232 	audio->psg.ch3.wavedata32[address | (bank * 4)] = value;
233 }
234 
GBAAudioReadWaveRAM(struct GBAAudio * audio,int address)235 uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address) {
236 	int bank = !audio->psg.ch3.bank;
237 
238 	// When the audio hardware is turned off, it acts like bank 0 has been
239 	// selected in SOUND3CNT_L, so any read comes from bank 1.
240 	if (!audio->enable) {
241 		bank = 1;
242 	}
243 
244 	return audio->psg.ch3.wavedata32[address | (bank * 4)];
245 }
246 
GBAAudioWriteFIFO(struct GBAAudio * audio,int address,uint32_t value)247 uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
248 	struct GBAAudioFIFO* channel;
249 	switch (address) {
250 	case REG_FIFO_A_LO:
251 		channel = &audio->chA;
252 		break;
253 	case REG_FIFO_B_LO:
254 		channel = &audio->chB;
255 		break;
256 	default:
257 		mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", address);
258 		return value;
259 	}
260 	channel->fifo[channel->fifoWrite] = value;
261 	++channel->fifoWrite;
262 	if (channel->fifoWrite == GBA_AUDIO_FIFO_SIZE) {
263 		channel->fifoWrite = 0;
264 	}
265 	return channel->fifo[channel->fifoWrite];
266 }
267 
GBAAudioSampleFIFO(struct GBAAudio * audio,int fifoId,int32_t cycles)268 void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
269 	struct GBAAudioFIFO* channel;
270 	if (fifoId == 0) {
271 		channel = &audio->chA;
272 	} else if (fifoId == 1) {
273 		channel = &audio->chB;
274 	} else {
275 		mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", fifoId);
276 		return;
277 	}
278 	int fifoSize;
279 	if (channel->fifoWrite >= channel->fifoRead) {
280 		fifoSize = channel->fifoWrite - channel->fifoRead;
281 	} else {
282 		fifoSize = GBA_AUDIO_FIFO_SIZE - channel->fifoRead + channel->fifoWrite;
283 	}
284 	if (GBA_AUDIO_FIFO_SIZE - fifoSize > 4 && channel->dmaSource > 0) {
285 		struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
286 		if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
287 			dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
288 			dma->nextCount = 4;
289 			GBADMASchedule(audio->p, channel->dmaSource, dma);
290 		}
291 	}
292 	if (!channel->internalRemaining && fifoSize) {
293 		channel->internalSample = channel->fifo[channel->fifoRead];
294 		channel->internalRemaining = 4;
295 		++channel->fifoRead;
296 		if (channel->fifoRead == GBA_AUDIO_FIFO_SIZE) {
297 			channel->fifoRead = 0;
298 		}
299 	}
300 	channel->sample = channel->internalSample;
301 	if (channel->internalRemaining) {
302 		channel->internalSample >>= 8;
303 		--channel->internalRemaining;
304 	}
305 }
306 
_applyBias(struct GBAAudio * audio,int sample)307 static int _applyBias(struct GBAAudio* audio, int sample) {
308 	sample += GBARegisterSOUNDBIASGetBias(audio->soundbias);
309 	if (sample >= 0x400) {
310 		sample = 0x3FF;
311 	} else if (sample < 0) {
312 		sample = 0;
313 	}
314 	return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4;
315 }
316 
_sample(struct mTiming * timing,void * user,uint32_t cyclesLate)317 static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
318 	struct GBAAudio* audio = user;
319 	int16_t sampleLeft = 0;
320 	int16_t sampleRight = 0;
321 	int psgShift = 4 - audio->volume;
322 	GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
323 	sampleLeft >>= psgShift;
324 	sampleRight >>= psgShift;
325 
326 	if (audio->mixer) {
327 		audio->mixer->step(audio->mixer);
328 	}
329 	if (!audio->externalMixing) {
330 		if (!audio->forceDisableChA) {
331 			if (audio->chALeft) {
332 				sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA;
333 			}
334 
335 			if (audio->chARight) {
336 				sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA;
337 			}
338 		}
339 
340 		if (!audio->forceDisableChB) {
341 			if (audio->chBLeft) {
342 				sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB;
343 			}
344 
345 			if (audio->chBRight) {
346 				sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB;
347 			}
348 		}
349 	}
350 
351 	sampleLeft = _applyBias(audio, sampleLeft);
352 	sampleRight = _applyBias(audio, sampleRight);
353 
354 	mCoreSyncLockAudio(audio->p->sync);
355 	unsigned produced;
356 	if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
357 		blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft);
358 		blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight);
359 		audio->lastLeft = sampleLeft;
360 		audio->lastRight = sampleRight;
361 		audio->clock += audio->sampleInterval;
362 		if (audio->clock >= CLOCKS_PER_FRAME) {
363 			blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME);
364 			blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME);
365 			audio->clock -= CLOCKS_PER_FRAME;
366 		}
367 	}
368 	produced = blip_samples_avail(audio->psg.left);
369 	if (audio->p->stream && audio->p->stream->postAudioFrame) {
370 		audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
371 	}
372 	bool wait = produced >= audio->samples;
373 	if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) {
374 		// Interrupted
375 		audio->p->earlyExit = true;
376 	}
377 
378 	if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
379 		audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right);
380 	}
381 
382 	mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
383 }
384 
GBAAudioSerialize(const struct GBAAudio * audio,struct GBASerializedState * state)385 void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
386 	GBAudioPSGSerialize(&audio->psg, &state->audio.psg, &state->audio.flags);
387 
388 	STORE_32(audio->chA.internalSample, 0, &state->audio.internalA);
389 	STORE_32(audio->chB.internalSample, 0, &state->audio.internalB);
390 	state->audio.sampleA = audio->chA.sample;
391 	state->audio.sampleB = audio->chB.sample;
392 
393 	int readA = audio->chA.fifoRead;
394 	int readB = audio->chB.fifoRead;
395 	size_t i;
396 	for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
397 		STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
398 		STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
399 		++readA;
400 		if (readA == GBA_AUDIO_FIFO_SIZE) {
401 			readA = 0;
402 		}
403 		++readB;
404 		if (readB == GBA_AUDIO_FIFO_SIZE) {
405 			readB = 0;
406 		}
407 	}
408 
409 	int fifoSizeA;
410 	if (audio->chA.fifoWrite >= audio->chA.fifoRead) {
411 		fifoSizeA = audio->chA.fifoWrite - audio->chA.fifoRead;
412 	} else {
413 		fifoSizeA = GBA_AUDIO_FIFO_SIZE - audio->chA.fifoRead + audio->chA.fifoWrite;
414 	}
415 
416 	int fifoSizeB;
417 	if (audio->chB.fifoWrite >= audio->chB.fifoRead) {
418 		fifoSizeB = audio->chB.fifoWrite - audio->chB.fifoRead;
419 	} else {
420 		fifoSizeB = GBA_AUDIO_FIFO_SIZE - audio->chB.fifoRead + audio->chB.fifoWrite;
421 	}
422 
423 	GBASerializedAudioFlags flags = 0;
424 	flags = GBASerializedAudioFlagsSetFIFOSamplesA(flags, fifoSizeA);
425 	flags = GBASerializedAudioFlagsSetFIFOSamplesB(flags, fifoSizeB);
426 	flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining);
427 	flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
428 	STORE_16(flags, 0, &state->audio.gbaFlags);
429 	STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
430 }
431 
GBAAudioDeserialize(struct GBAAudio * audio,const struct GBASerializedState * state)432 void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
433 	GBAudioPSGDeserialize(&audio->psg, &state->audio.psg, &state->audio.flags);
434 
435 	LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA);
436 	LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB);
437 	audio->chA.sample = state->audio.sampleA;
438 	audio->chB.sample = state->audio.sampleB;
439 
440 	int readA = 0;
441 	int readB = 0;
442 	size_t i;
443 	for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
444 		LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
445 		LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
446 		++readA;
447 		++readB;
448 	}
449 	audio->chA.fifoRead = 0;
450 	audio->chB.fifoRead = 0;
451 
452 	GBASerializedAudioFlags flags;
453 	LOAD_16(flags, 0, &state->audio.gbaFlags);
454 	audio->chA.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesA(flags);
455 	audio->chB.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesB(flags);
456 	audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags);
457 	audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
458 
459 	uint32_t when;
460 	LOAD_32(when, 0, &state->audio.nextSample);
461 	mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
462 }
463 
GBAAudioCalculateRatio(float inputSampleRate,float desiredFPS,float desiredSampleRate)464 float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRate) {
465 	return desiredSampleRate * GBA_ARM7TDMI_FREQUENCY / (VIDEO_TOTAL_LENGTH * desiredFPS * inputSampleRate);
466 }
467