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#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
12#include "third-party/blip_buf/blip_buf.h"
13#endif
14
15#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
16
17static void _GBASDLAudioCallback(void* context, Uint8* data, int len);
18
19bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext) {
20 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
21 GBALog(0, GBA_LOG_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 = _GBASDLAudioCallback;
30 context->desiredSpec.userdata = context;
31#if RESAMPLE_LIBRARY == RESAMPLE_NN
32 context->drift = 0.f;
33#endif
34
35#if SDL_VERSION_ATLEAST(2, 0, 0)
36 context->deviceId = SDL_OpenAudioDevice(0, 0, &context->desiredSpec, &context->obtainedSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
37 if (context->deviceId == 0) {
38#else
39 if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
40#endif
41 GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
42 return false;
43 }
44 context->samples = context->obtainedSpec.samples;
45 context->gba = 0;
46 context->thread = 0;
47
48 if (threadContext) {
49 context->thread = threadContext;
50 float ratio = GBAAudioCalculateRatio(0x8000, threadContext->fpsTarget, 44100);
51 threadContext->audioBuffers = context->samples / ratio;
52 if (context->samples > threadContext->audioBuffers) {
53 threadContext->audioBuffers = context->samples * 2;
54 }
55
56#if SDL_VERSION_ATLEAST(2, 0, 0)
57 SDL_PauseAudioDevice(context->deviceId, 0);
58#else
59 SDL_PauseAudio(0);
60#endif
61 }
62
63 return true;
64}
65
66void GBASDLDeinitAudio(struct GBASDLAudio* context) {
67 UNUSED(context);
68 context->thread = 0;
69 context->gba = 0;
70#if SDL_VERSION_ATLEAST(2, 0, 0)
71 SDL_PauseAudioDevice(context->deviceId, 1);
72 SDL_CloseAudioDevice(context->deviceId);
73#else
74 SDL_PauseAudio(1);
75 SDL_CloseAudio();
76#endif
77 SDL_QuitSubSystem(SDL_INIT_AUDIO);
78}
79
80void GBASDLPauseAudio(struct GBASDLAudio* context) {
81#if SDL_VERSION_ATLEAST(2, 0, 0)
82 SDL_PauseAudioDevice(context->deviceId, 1);
83#else
84 UNUSED(context);
85 SDL_PauseAudio(1);
86#endif
87}
88
89void GBASDLResumeAudio(struct GBASDLAudio* context) {
90#if SDL_VERSION_ATLEAST(2, 0, 0)
91 SDL_PauseAudioDevice(context->deviceId, 0);
92#else
93 UNUSED(context);
94 SDL_PauseAudio(0);
95#endif
96}
97
98static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
99 struct GBASDLAudio* audioContext = context;
100 if (!context || ((!audioContext->thread || !audioContext->thread->gba) && !audioContext->gba)) {
101 memset(data, 0, len);
102 return;
103 }
104 struct GBA* gba;
105 if (audioContext->thread) {
106 gba = audioContext->thread->gba;
107 } else {
108 gba = audioContext->gba;
109 }
110#if RESAMPLE_LIBRARY == RESAMPLE_NN
111 audioContext->ratio = GBAAudioCalculateRatio(gba->audio.sampleRate, audioContext->fpsTarget, audioContext->obtainedSpec.freq);
112 if (audioContext->ratio == INFINITY) {
113 memset(data, 0, len);
114 return;
115 }
116 struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
117 len /= 2 * audioContext->obtainedSpec.channels;
118 if (audioContext->obtainedSpec.channels == 2) {
119 GBAAudioResampleNN(&gba->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
120 }
121#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
122 double fauxClock = 1;
123 if (audioContext->thread) {
124 fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
125 mCoreSyncLockAudio(&audioContext->thread->sync);
126 }
127 blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
128 blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
129 len /= 2 * audioContext->obtainedSpec.channels;
130 int available = blip_samples_avail(gba->audio.left);
131 if (available > len) {
132 available = len;
133 }
134 blip_read_samples(gba->audio.left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
135 if (audioContext->obtainedSpec.channels == 2) {
136 blip_read_samples(gba->audio.right, ((short*) data) + 1, available, 1);
137 }
138
139 if (audioContext->thread) {
140 mCoreSyncConsumeAudio(&audioContext->thread->sync);
141 }
142 if (available < len) {
143 memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
144 }
145#endif
146}