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}