all repos — mgba @ 4edd7286f39fe940a890394626567211e072badb

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	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}