all repos — mgba @ 6d898542c765f4efc4a094c5ebd3f3465c36f417

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