src/platform/sdl/sdl-audio.c (view raw)
1/* Copyright (c) 2013-2015 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/gba.h"
9#include "gba/supervisor/thread.h"
10
11#include "third-party/blip_buf/blip_buf.h"
12
13#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
14
15static void _mSDLAudioCallback(void* context, Uint8* data, int len);
16
17bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext) {
18 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
19 GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());
20 return false;
21 }
22
23 context->desiredSpec.freq = context->sampleRate;
24 context->desiredSpec.format = AUDIO_S16SYS;
25 context->desiredSpec.channels = 2;
26 context->desiredSpec.samples = context->samples;
27 context->desiredSpec.callback = _mSDLAudioCallback;
28 context->desiredSpec.userdata = context;
29
30#if SDL_VERSION_ATLEAST(2, 0, 0)
31 context->deviceId = SDL_OpenAudioDevice(0, 0, &context->desiredSpec, &context->obtainedSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
32 if (context->deviceId == 0) {
33#else
34 if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
35#endif
36 GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
37 return false;
38 }
39 context->samples = context->obtainedSpec.samples;
40 context->core = 0;
41
42 if (threadContext) {
43 context->core = threadContext->core;
44 context->sync = &threadContext->sync;
45
46#if SDL_VERSION_ATLEAST(2, 0, 0)
47 SDL_PauseAudioDevice(context->deviceId, 0);
48#else
49 SDL_PauseAudio(0);
50#endif
51 }
52
53 return true;
54}
55
56void mSDLDeinitAudio(struct mSDLAudio* context) {
57 UNUSED(context);
58#if SDL_VERSION_ATLEAST(2, 0, 0)
59 SDL_PauseAudioDevice(context->deviceId, 1);
60 SDL_CloseAudioDevice(context->deviceId);
61#else
62 SDL_PauseAudio(1);
63 SDL_CloseAudio();
64#endif
65 SDL_QuitSubSystem(SDL_INIT_AUDIO);
66}
67
68void mSDLPauseAudio(struct mSDLAudio* context) {
69#if SDL_VERSION_ATLEAST(2, 0, 0)
70 SDL_PauseAudioDevice(context->deviceId, 1);
71#else
72 UNUSED(context);
73 SDL_PauseAudio(1);
74#endif
75}
76
77void mSDLResumeAudio(struct mSDLAudio* context) {
78#if SDL_VERSION_ATLEAST(2, 0, 0)
79 SDL_PauseAudioDevice(context->deviceId, 0);
80#else
81 UNUSED(context);
82 SDL_PauseAudio(0);
83#endif
84}
85
86static void _mSDLAudioCallback(void* context, Uint8* data, int len) {
87 struct mSDLAudio* audioContext = context;
88 if (!context || !audioContext->core) {
89 memset(data, 0, len);
90 return;
91 }
92 blip_t* left = NULL;
93 blip_t* right = NULL;
94 int32_t clockRate;
95 if (audioContext->core) {
96 left = audioContext->core->getAudioChannel(audioContext->core, 0);
97 right = audioContext->core->getAudioChannel(audioContext->core, 1);
98 clockRate = audioContext->core->frequency(audioContext->core);
99 }
100 double fauxClock = 1;
101 if (audioContext->sync) {
102 if (audioContext->sync->fpsTarget > 0) {
103 fauxClock = GBAAudioCalculateRatio(1, audioContext->sync->fpsTarget, 1);
104 }
105 mCoreSyncLockAudio(audioContext->sync);
106 }
107 blip_set_rates(left, clockRate, audioContext->obtainedSpec.freq * fauxClock);
108 blip_set_rates(right, clockRate, audioContext->obtainedSpec.freq * fauxClock);
109 len /= 2 * audioContext->obtainedSpec.channels;
110 int available = blip_samples_avail(left);
111 if (available > len) {
112 available = len;
113 }
114 blip_read_samples(left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
115 if (audioContext->obtainedSpec.channels == 2) {
116 blip_read_samples(right, ((short*) data) + 1, available, 1);
117 }
118
119 if (audioContext->sync) {
120 mCoreSyncConsumeAudio(audioContext->sync);
121 }
122 if (available < len) {
123 memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
124 }
125}