all repos — mgba @ a75c019fab24f045069cb9a923d02795f4d6f564

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