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 _GBSDLAudioCallback(void* context, Uint8* data, int len);
16
17bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread* 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 = _GBSDLAudioCallback;
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->psg = 0;
41 context->thread = 0;
42
43 if (threadContext) {
44 context->thread = threadContext;
45 float ratio = GBAAudioCalculateRatio(0x8000, threadContext->fpsTarget, 44100);
46 threadContext->audioBuffers = context->samples / ratio;
47 if (context->samples > threadContext->audioBuffers) {
48 threadContext->audioBuffers = context->samples * 2;
49 }
50 context->sync = &threadContext->sync;
51
52#if SDL_VERSION_ATLEAST(2, 0, 0)
53 SDL_PauseAudioDevice(context->deviceId, 0);
54#else
55 SDL_PauseAudio(0);
56#endif
57 }
58
59 return true;
60}
61
62void GBSDLDeinitAudio(struct GBSDLAudio* context) {
63 UNUSED(context);
64 context->psg = 0;
65#if SDL_VERSION_ATLEAST(2, 0, 0)
66 SDL_PauseAudioDevice(context->deviceId, 1);
67 SDL_CloseAudioDevice(context->deviceId);
68#else
69 SDL_PauseAudio(1);
70 SDL_CloseAudio();
71#endif
72 SDL_QuitSubSystem(SDL_INIT_AUDIO);
73}
74
75void GBSDLPauseAudio(struct GBSDLAudio* context) {
76#if SDL_VERSION_ATLEAST(2, 0, 0)
77 SDL_PauseAudioDevice(context->deviceId, 1);
78#else
79 UNUSED(context);
80 SDL_PauseAudio(1);
81#endif
82}
83
84void GBSDLResumeAudio(struct GBSDLAudio* context) {
85#if SDL_VERSION_ATLEAST(2, 0, 0)
86 SDL_PauseAudioDevice(context->deviceId, 0);
87#else
88 UNUSED(context);
89 SDL_PauseAudio(0);
90#endif
91}
92
93static void _GBSDLAudioCallback(void* context, Uint8* data, int len) {
94 struct GBSDLAudio* audioContext = context;
95 if (!context || (!audioContext->psg && !audioContext->thread)) {
96 memset(data, 0, len);
97 return;
98 }
99 struct GBAudio* psg = audioContext->psg;
100 if (!psg) {
101 psg = &audioContext->thread->gba->audio.psg;
102 }
103 double fauxClock = 1;
104 if (audioContext->thread) {
105 fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
106 mCoreSyncLockAudio(&audioContext->thread->sync);
107 }
108 blip_set_rates(psg->left, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
109 blip_set_rates(psg->right, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
110 len /= 2 * audioContext->obtainedSpec.channels;
111 int available = blip_samples_avail(psg->left);
112 if (available > len) {
113 available = len;
114 }
115 blip_read_samples(psg->left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
116 if (audioContext->obtainedSpec.channels == 2) {
117 blip_read_samples(psg->right, ((short*) data) + 1, available, 1);
118 }
119
120 if (audioContext->sync) {
121 mCoreSyncConsumeAudio(audioContext->sync);
122 }
123 if (available < len) {
124 memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
125 }
126}