all repos — mgba @ 5fde0d8c35c5bc9f8164b62e2af94f8f17578520

mGBA Game Boy Advance Emulator

src/platform/sdl/sdl-audio.c (view raw)

 1#include "sdl-audio.h"
 2
 3#include "gba.h"
 4#include "gba-thread.h"
 5
 6#include <math.h>
 7
 8#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
 9#define FPS_TARGET 60.f
10
11struct StereoSample {
12	Sint16 left;
13	Sint16 right;
14};
15
16static void _GBASDLAudioCallback(void* context, Uint8* data, int len);
17
18int GBASDLInitAudio(struct GBASDLAudio* context) {
19	if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
20		GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system");
21		return 0;
22	}
23
24	context->desiredSpec.freq = 44100;
25	context->desiredSpec.format = AUDIO_S16SYS;
26	context->desiredSpec.channels = 2;
27	context->desiredSpec.samples = GBA_AUDIO_SAMPLES;
28	context->desiredSpec.callback = _GBASDLAudioCallback;
29	context->desiredSpec.userdata = context;
30	context->audio = 0;
31	context->drift = 0.f;
32	if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
33		GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
34		return 0;
35	}
36	SDL_PauseAudio(0);
37	return 1;
38}
39
40void GBASDLDeinitAudio(struct GBASDLAudio* context) {
41	(void)(context);
42	SDL_PauseAudio(1);
43	SDL_CloseAudio();
44	SDL_QuitSubSystem(SDL_INIT_AUDIO);
45}
46
47static void _pulldownResample(struct GBASDLAudio* context, struct StereoSample* output, ssize_t samples) {
48	int32_t left[BUFFER_SIZE];
49	int32_t right[BUFFER_SIZE];
50
51	// toRead is in GBA samples
52	// TODO: Do this with fixed-point math
53	unsigned toRead = ceilf(samples / context->ratio);
54	while (samples > 0) {
55		unsigned currentRead = BUFFER_SIZE >> 2;
56		if (currentRead > toRead) {
57			currentRead = toRead;
58		}
59		unsigned read = GBAAudioCopy(context->audio, left, right, currentRead);
60		toRead -= read;
61		unsigned i;
62		for (i = 0; i < read; ++i) {
63			context->drift += context->ratio;
64			while (context->drift >= 1.f) {
65				output->left = left[i];
66				output->right = right[i];
67				++output;
68				--samples;
69				context->drift -= 1.f;
70				if (samples < 0) {
71					return;
72				}
73			}
74		}
75		if (read < currentRead) {
76			memset(output, 0, samples * sizeof(struct StereoSample));
77			return;
78		}
79	}
80}
81
82static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
83	struct GBASDLAudio* audioContext = context;
84	if (!context || !audioContext->audio) {
85		memset(data, 0, len);
86		return;
87	}
88	float ratio = 280896.0f * FPS_TARGET / GBA_ARM7TDMI_FREQUENCY;
89	audioContext->ratio = audioContext->obtainedSpec.freq / ratio / (float) audioContext->audio->sampleRate;
90	struct StereoSample* ssamples = (struct StereoSample*) data;
91	len /= 2 * audioContext->obtainedSpec.channels;
92	if (audioContext->obtainedSpec.channels == 2) {
93		_pulldownResample(audioContext, ssamples, len);
94	}
95}