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