all repos — mgba @ 28268a601bcd1fcc71389c1b8a278dd69bcefd9f

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#include "third-party/blip_buf/blip_buf.h"
 12
 13#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
 14
 15static void _GBSDLAudioCallback(void* context, Uint8* data, int len);
 16
 17bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread* 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 = _GBSDLAudioCallback;
 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->psg = 0;
 41	context->thread = 0;
 42
 43	if (threadContext) {
 44		context->thread = threadContext;
 45		float ratio = GBAAudioCalculateRatio(0x8000, threadContext->fpsTarget, 44100);
 46		threadContext->audioBuffers = context->samples / ratio;
 47		if (context->samples > threadContext->audioBuffers) {
 48			threadContext->audioBuffers = context->samples * 2;
 49		}
 50		context->sync = &threadContext->sync;
 51
 52#if SDL_VERSION_ATLEAST(2, 0, 0)
 53		SDL_PauseAudioDevice(context->deviceId, 0);
 54#else
 55		SDL_PauseAudio(0);
 56#endif
 57	}
 58
 59	return true;
 60}
 61
 62void GBSDLDeinitAudio(struct GBSDLAudio* context) {
 63	UNUSED(context);
 64	context->psg = 0;
 65#if SDL_VERSION_ATLEAST(2, 0, 0)
 66	SDL_PauseAudioDevice(context->deviceId, 1);
 67	SDL_CloseAudioDevice(context->deviceId);
 68#else
 69	SDL_PauseAudio(1);
 70	SDL_CloseAudio();
 71#endif
 72	SDL_QuitSubSystem(SDL_INIT_AUDIO);
 73}
 74
 75void GBSDLPauseAudio(struct GBSDLAudio* context) {
 76#if SDL_VERSION_ATLEAST(2, 0, 0)
 77	SDL_PauseAudioDevice(context->deviceId, 1);
 78#else
 79	UNUSED(context);
 80	SDL_PauseAudio(1);
 81#endif
 82}
 83
 84void GBSDLResumeAudio(struct GBSDLAudio* context) {
 85#if SDL_VERSION_ATLEAST(2, 0, 0)
 86	SDL_PauseAudioDevice(context->deviceId, 0);
 87#else
 88	UNUSED(context);
 89	SDL_PauseAudio(0);
 90#endif
 91}
 92
 93static void _GBSDLAudioCallback(void* context, Uint8* data, int len) {
 94	struct GBSDLAudio* audioContext = context;
 95	if (!context || (!audioContext->psg && !audioContext->thread)) {
 96		memset(data, 0, len);
 97		return;
 98	}
 99	struct GBAudio* psg = audioContext->psg;
100	if (!psg) {
101		psg = &audioContext->thread->gba->audio.psg;
102	}
103	double fauxClock = 1;
104	if (audioContext->thread) {
105		fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
106		mCoreSyncLockAudio(&audioContext->thread->sync);
107	}
108	blip_set_rates(psg->left, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
109	blip_set_rates(psg->right, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
110	len /= 2 * audioContext->obtainedSpec.channels;
111	int available = blip_samples_avail(psg->left);
112	if (available > len) {
113		available = len;
114	}
115	blip_read_samples(psg->left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
116	if (audioContext->obtainedSpec.channels == 2) {
117		blip_read_samples(psg->right, ((short*) data) + 1, available, 1);
118	}
119
120	if (audioContext->sync) {
121		mCoreSyncConsumeAudio(audioContext->sync);
122	}
123	if (available < len) {
124		memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
125	}
126}