all repos — mgba @ 1093849ad5f6103d8eb1817e9b913255a3218eb9

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