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