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