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}