all repos — mgba @ 94cc48faf11e9065025c9529e19b670bd5dd0c2a

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