src/platform/sdl/sdl-audio.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "sdl-audio.h"
7
8#include "gba.h"
9#include "gba-thread.h"
10
11#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
12#include "third-party/blip_buf/blip_buf.h"
13#endif
14
15#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
16
17static void _GBASDLAudioCallback(void* context, Uint8* data, int len);
18
19bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext) {
20 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
21 GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system");
22 return false;
23 }
24
25 context->desiredSpec.freq = 44100;
26 context->desiredSpec.format = AUDIO_S16SYS;
27 context->desiredSpec.channels = 2;
28 context->desiredSpec.samples = context->samples;
29 context->desiredSpec.callback = _GBASDLAudioCallback;
30 context->desiredSpec.userdata = context;
31#if RESAMPLE_LIBRARY == RESAMPLE_NN
32 context->drift = 0.f;
33#endif
34 if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
35 GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
36 return false;
37 }
38 context->thread = threadContext;
39 context->samples = context->obtainedSpec.samples;
40 float ratio = GBAAudioCalculateRatio(0x8000, threadContext->fpsTarget, 44100);
41 threadContext->audioBuffers = context->samples / ratio;
42 if (context->samples > threadContext->audioBuffers) {
43 threadContext->audioBuffers = context->samples * 2;
44 }
45
46 SDL_PauseAudio(0);
47 return true;
48}
49
50void GBASDLDeinitAudio(struct GBASDLAudio* context) {
51 UNUSED(context);
52 SDL_PauseAudio(1);
53 SDL_CloseAudio();
54 SDL_QuitSubSystem(SDL_INIT_AUDIO);
55}
56
57void GBASDLPauseAudio(struct GBASDLAudio* context) {
58 UNUSED(context);
59 SDL_PauseAudio(1);
60}
61
62void GBASDLResumeAudio(struct GBASDLAudio* context) {
63 UNUSED(context);
64 SDL_PauseAudio(0);
65}
66
67static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
68 struct GBASDLAudio* audioContext = context;
69 if (!context || !audioContext->thread || !audioContext->thread->gba) {
70 memset(data, 0, len);
71 return;
72 }
73#if RESAMPLE_LIBRARY == RESAMPLE_NN
74 audioContext->ratio = GBAAudioCalculateRatio(audioContext->thread->gba->audio.sampleRate, audioContext->thread->fpsTarget, audioContext->obtainedSpec.freq);
75 if (audioContext->ratio == INFINITY) {
76 memset(data, 0, len);
77 return;
78 }
79 struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
80 len /= 2 * audioContext->obtainedSpec.channels;
81 if (audioContext->obtainedSpec.channels == 2) {
82 GBAAudioResampleNN(&audioContext->thread->gba->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
83 }
84#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
85 double fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
86 GBASyncLockAudio(&audioContext->thread->sync);
87 blip_set_rates(audioContext->thread->gba->audio.left, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
88 blip_set_rates(audioContext->thread->gba->audio.right, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
89 len /= 2 * audioContext->obtainedSpec.channels;
90 int available = blip_samples_avail(audioContext->thread->gba->audio.left);
91 if (available > len) {
92 available = len;
93 }
94 blip_read_samples(audioContext->thread->gba->audio.left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
95 if (audioContext->obtainedSpec.channels == 2) {
96 blip_read_samples(audioContext->thread->gba->audio.right, ((short*) data) + 1, available, 1);
97 }
98 GBASyncConsumeAudio(&audioContext->thread->sync);
99 if (available < len) {
100 memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, len - available);
101 }
102#endif
103}