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}