all repos — mgba @ 98c8b42b0f2aa63f61dfb27d2a68bc437028b280

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