all repos — mgba @ 48e89fb5eb6aa1151d211084c177517307d73ac4

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	int toRead = samples / context->ratio;
50	while (samples > 0) {
51		int currentRead = BUFFER_SIZE >> 2;
52		if (currentRead > toRead) {
53			currentRead = toRead;
54		}
55		unsigned read = GBAAudioCopy(context->audio, left, right, currentRead);
56		toRead -= read;
57		unsigned i;
58		for (i = 0; i < read; ++i) {
59			context->drift += context->ratio;
60			while (context->drift >= 0) {
61				output->left = left[i];
62				output->right = right[i];
63				++output;
64				--samples;
65#ifndef NDEBUG
66				if (samples < 0) {
67					abort();
68				}
69#endif
70				context->drift -= 1.f;
71			}
72		}
73		if (read < BUFFER_SIZE >> 2) {
74			memset(output, 0, toRead);
75			return;
76		}
77	}
78}
79
80static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
81	struct GBASDLAudio* audioContext = context;
82	if (!context || !audioContext->audio) {
83		memset(data, 0, len);
84		return;
85	}
86	audioContext->ratio = audioContext->obtainedSpec.freq / (float) audioContext->audio->sampleRate;
87	struct StereoSample* ssamples = (struct StereoSample*) data;
88	len /= 2 * audioContext->obtainedSpec.channels;
89	if (audioContext->obtainedSpec.channels == 2) {
90		_pulldownResample(audioContext, ssamples, len);
91	}
92}