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