all repos — mgba @ 50402c830729f2ba5a6fc3e6facfd8b258f7f97d

mGBA Game Boy Advance Emulator

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
 47	if (threadContext) {
 48		context->thread = threadContext;
 49		float ratio = GBAAudioCalculateRatio(0x8000, threadContext->fpsTarget, 44100);
 50		threadContext->audioBuffers = context->samples / ratio;
 51		if (context->samples > threadContext->audioBuffers) {
 52			threadContext->audioBuffers = context->samples * 2;
 53		}
 54
 55#if SDL_VERSION_ATLEAST(2, 0, 0)
 56		SDL_PauseAudioDevice(context->deviceId, 0);
 57#else
 58		SDL_PauseAudio(0);
 59#endif
 60	}
 61
 62	return true;
 63}
 64
 65void GBASDLDeinitAudio(struct GBASDLAudio* context) {
 66	UNUSED(context);
 67#if SDL_VERSION_ATLEAST(2, 0, 0)
 68	SDL_PauseAudioDevice(context->deviceId, 1);
 69	SDL_CloseAudioDevice(context->deviceId);
 70#else
 71	SDL_PauseAudio(1);
 72	SDL_CloseAudio();
 73#endif
 74	SDL_QuitSubSystem(SDL_INIT_AUDIO);
 75}
 76
 77void GBASDLPauseAudio(struct GBASDLAudio* context) {
 78#if SDL_VERSION_ATLEAST(2, 0, 0)
 79	SDL_PauseAudioDevice(context->deviceId, 1);
 80#else
 81	UNUSED(context);
 82	SDL_PauseAudio(1);
 83#endif
 84}
 85
 86void GBASDLResumeAudio(struct GBASDLAudio* context) {
 87#if SDL_VERSION_ATLEAST(2, 0, 0)
 88	SDL_PauseAudioDevice(context->deviceId, 0);
 89#else
 90	UNUSED(context);
 91	SDL_PauseAudio(0);
 92#endif
 93}
 94
 95static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
 96	struct GBASDLAudio* audioContext = context;
 97	if (!context || (!audioContext->gba && (!audioContext->thread || !audioContext->thread->gba))) {
 98		memset(data, 0, len);
 99		return;
100	}
101	if (!audioContext->gba) {
102		audioContext->gba = audioContext->thread->gba;
103	}
104#if RESAMPLE_LIBRARY == RESAMPLE_NN
105	audioContext->ratio = GBAAudioCalculateRatio(audioContext->gba->audio.sampleRate, audioContext->fpsTarget, audioContext->obtainedSpec.freq);
106	if (audioContext->ratio == INFINITY) {
107		memset(data, 0, len);
108		return;
109	}
110	struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
111	len /= 2 * audioContext->obtainedSpec.channels;
112	if (audioContext->obtainedSpec.channels == 2) {
113		GBAAudioResampleNN(&audioContext->gba->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
114	}
115#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
116	double fauxClock = 1;
117	if (audioContext->thread) {
118		GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
119		GBASyncLockAudio(&audioContext->thread->sync);
120	}
121	blip_set_rates(audioContext->gba->audio.left, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
122	blip_set_rates(audioContext->gba->audio.right, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock);
123	len /= 2 * audioContext->obtainedSpec.channels;
124	int available = blip_samples_avail(audioContext->gba->audio.left);
125	if (available > len) {
126		available = len;
127	}
128	blip_read_samples(audioContext->gba->audio.left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
129	if (audioContext->obtainedSpec.channels == 2) {
130		blip_read_samples(audioContext->gba->audio.right, ((short*) data) + 1, available, 1);
131	}
132
133	if (audioContext->thread) {
134		GBASyncConsumeAudio(&audioContext->thread->sync);
135	}
136	if (available < len) {
137		memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
138	}
139#endif
140}