mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into qt
This commit is contained in:
commit
ae534aff74
|
@ -4,8 +4,9 @@
|
|||
#include "gba-io.h"
|
||||
#include "gba-serialize.h"
|
||||
#include "gba-thread.h"
|
||||
#include "gba-video.h"
|
||||
|
||||
const unsigned GBA_AUDIO_SAMPLES = 512;
|
||||
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
||||
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
|
||||
#define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128)
|
||||
|
||||
|
@ -19,9 +20,9 @@ static int32_t _updateChannel4(struct GBAAudioChannel4* ch);
|
|||
static int _applyBias(struct GBAAudio* audio, int sample);
|
||||
static void _sample(struct GBAAudio* audio);
|
||||
|
||||
void GBAAudioInit(struct GBAAudio* audio) {
|
||||
CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES * sizeof(int32_t));
|
||||
CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES * sizeof(int32_t));
|
||||
void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
|
||||
CircleBufferInit(&audio->left, samples * sizeof(int32_t));
|
||||
CircleBufferInit(&audio->right, samples * sizeof(int32_t));
|
||||
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
|
||||
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
|
||||
}
|
||||
|
@ -69,6 +70,40 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
|
|||
CircleBufferDeinit(&audio->chB.fifo);
|
||||
}
|
||||
|
||||
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
|
||||
if (samples >= GBA_AUDIO_SAMPLES) {
|
||||
return;
|
||||
}
|
||||
|
||||
GBASyncLockAudio(audio->p->sync);
|
||||
int32_t buffer[GBA_AUDIO_SAMPLES];
|
||||
int32_t dummy;
|
||||
size_t read;
|
||||
size_t i;
|
||||
|
||||
read = CircleBufferDump(&audio->left, buffer, sizeof(buffer));
|
||||
CircleBufferDeinit(&audio->left);
|
||||
CircleBufferInit(&audio->left, samples * sizeof(int32_t));
|
||||
for (i = 0; i * sizeof(int32_t) < read; ++i) {
|
||||
if (!CircleBufferWrite32(&audio->left, buffer[i])) {
|
||||
CircleBufferRead32(&audio->left, &dummy);
|
||||
CircleBufferWrite32(&audio->left, buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
read = CircleBufferDump(&audio->right, buffer, sizeof(buffer));
|
||||
CircleBufferDeinit(&audio->right);
|
||||
CircleBufferInit(&audio->right, samples * sizeof(int32_t));
|
||||
for (i = 0; i * sizeof(int32_t) < read; ++i) {
|
||||
if (!CircleBufferWrite32(&audio->right, buffer[i])) {
|
||||
CircleBufferRead32(&audio->right, &dummy);
|
||||
CircleBufferWrite32(&audio->right, buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
GBASyncUnlockAudio(audio->p->sync);
|
||||
}
|
||||
|
||||
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {
|
||||
audio->nextEvent -= cycles;
|
||||
audio->eventDiff += cycles;
|
||||
|
@ -669,7 +704,7 @@ static void _sample(struct GBAAudio* audio) {
|
|||
CircleBufferWrite32(&audio->left, sampleLeft);
|
||||
CircleBufferWrite32(&audio->right, sampleRight);
|
||||
unsigned produced = CircleBufferSize(&audio->left);
|
||||
GBASyncProduceAudio(audio->p->sync, produced >= GBA_AUDIO_SAMPLES * 3);
|
||||
GBASyncProduceAudio(audio->p->sync, produced >= CircleBufferCapacity(&audio->left) / sizeof(int32_t) * 3);
|
||||
}
|
||||
|
||||
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
|
||||
|
@ -751,3 +786,7 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
|
|||
audio->eventDiff = state->audio.eventDiff;
|
||||
audio->nextSample = state->audio.nextSample;
|
||||
}
|
||||
|
||||
float GBAAudioCalculateRatio(struct GBAAudio* audio, float desiredFPS, float desiredSampleRate) {
|
||||
return desiredSampleRate * GBA_ARM7TDMI_FREQUENCY / (VIDEO_TOTAL_LENGTH * desiredFPS * audio->sampleRate);
|
||||
}
|
||||
|
|
|
@ -218,10 +218,12 @@ struct GBAStereoSample {
|
|||
int16_t right;
|
||||
};
|
||||
|
||||
void GBAAudioInit(struct GBAAudio* audio);
|
||||
void GBAAudioInit(struct GBAAudio* audio, size_t samples);
|
||||
void GBAAudioReset(struct GBAAudio* audio);
|
||||
void GBAAudioDeinit(struct GBAAudio* audio);
|
||||
|
||||
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples);
|
||||
|
||||
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles);
|
||||
void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info);
|
||||
|
||||
|
@ -251,4 +253,6 @@ struct GBASerializedState;
|
|||
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state);
|
||||
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state);
|
||||
|
||||
float GBAAudioCalculateRatio(struct GBAAudio* audio, float desiredFPS, float desiredSampleRatio);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <signal.h>
|
||||
|
||||
static const float _defaultFPSTarget = 60.f;
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
static pthread_key_t _contextKey;
|
||||
static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
|
||||
|
@ -84,6 +86,11 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
#else
|
||||
TlsSetValue(_contextKey, threadContext);
|
||||
#endif
|
||||
|
||||
if (threadContext->audioBuffers) {
|
||||
GBAAudioResizeBuffer(&gba.audio, threadContext->audioBuffers);
|
||||
}
|
||||
|
||||
if (threadContext->renderer) {
|
||||
GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
|
||||
}
|
||||
|
@ -202,6 +209,10 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
|
|||
threadContext->rewindBuffer = 0;
|
||||
}
|
||||
|
||||
if (!threadContext->fpsTarget) {
|
||||
threadContext->fpsTarget = _defaultFPSTarget;
|
||||
}
|
||||
|
||||
if (threadContext->rom && !GBAIsROM(threadContext->rom)) {
|
||||
threadContext->rom->close(threadContext->rom);
|
||||
threadContext->rom = 0;
|
||||
|
@ -545,6 +556,10 @@ void GBASyncLockAudio(struct GBASync* sync) {
|
|||
MutexLock(&sync->audioBufferMutex);
|
||||
}
|
||||
|
||||
void GBASyncUnlockAudio(struct GBASync* sync) {
|
||||
MutexUnlock(&sync->audioBufferMutex);
|
||||
}
|
||||
|
||||
void GBASyncConsumeAudio(struct GBASync* sync) {
|
||||
ConditionWake(&sync->audioRequiredCond);
|
||||
MutexUnlock(&sync->audioBufferMutex);
|
||||
|
|
|
@ -56,6 +56,8 @@ struct GBAThread {
|
|||
const char* fname;
|
||||
int activeKeys;
|
||||
int frameskip;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
|
||||
// Threading state
|
||||
Thread thread;
|
||||
|
@ -105,6 +107,7 @@ bool GBASyncDrawingFrame(struct GBASync* sync);
|
|||
|
||||
void GBASyncProduceAudio(struct GBASync* sync, int wait);
|
||||
void GBASyncLockAudio(struct GBASync* sync);
|
||||
void GBASyncUnlockAudio(struct GBASync* sync);
|
||||
void GBASyncConsumeAudio(struct GBASync* sync);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,7 +121,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
|||
GBAVideoInit(&gba->video);
|
||||
|
||||
gba->audio.p = gba;
|
||||
GBAAudioInit(&gba->audio);
|
||||
GBAAudioInit(&gba->audio, GBA_AUDIO_SAMPLES);
|
||||
|
||||
GBAIOInit(gba);
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
struct GBAThread context = {
|
||||
.renderer = &renderer.d.d,
|
||||
.audioBuffers = 512,
|
||||
.startCallback = _GBASDLStart,
|
||||
.cleanCallback = _GBASDLClean,
|
||||
.sync.videoFrameWait = 0,
|
||||
|
@ -96,6 +97,9 @@ int main(int argc, char** argv) {
|
|||
|
||||
GBAMapOptionsToContext(&opts, &context);
|
||||
|
||||
renderer.audio.samples = context.audioBuffers;
|
||||
GBASDLInitAudio(&renderer.audio);
|
||||
|
||||
GBAThreadStart(&context);
|
||||
|
||||
_GBASDLRunloop(&context, &renderer);
|
||||
|
@ -115,7 +119,6 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
|
|||
}
|
||||
|
||||
GBASDLInitEvents(&renderer->events);
|
||||
GBASDLInitAudio(&renderer->audio);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
@ -230,6 +233,7 @@ static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) {
|
|||
static void _GBASDLStart(struct GBAThread* threadContext) {
|
||||
struct GLSoftwareRenderer* renderer = threadContext->userData;
|
||||
renderer->audio.audio = &threadContext->gba->audio;
|
||||
renderer->audio.thread = threadContext;
|
||||
}
|
||||
|
||||
static void _GBASDLClean(struct GBAThread* threadContext) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "gba-thread.h"
|
||||
|
||||
#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
|
||||
#define FPS_TARGET 60.f
|
||||
|
||||
static void _GBASDLAudioCallback(void* context, Uint8* data, int len);
|
||||
|
||||
|
@ -17,10 +16,11 @@ bool GBASDLInitAudio(struct GBASDLAudio* context) {
|
|||
context->desiredSpec.freq = 44100;
|
||||
context->desiredSpec.format = AUDIO_S16SYS;
|
||||
context->desiredSpec.channels = 2;
|
||||
context->desiredSpec.samples = GBA_AUDIO_SAMPLES;
|
||||
context->desiredSpec.samples = context->samples;
|
||||
context->desiredSpec.callback = _GBASDLAudioCallback;
|
||||
context->desiredSpec.userdata = context;
|
||||
context->audio = 0;
|
||||
context->thread = 0;
|
||||
context->drift = 0.f;
|
||||
if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) {
|
||||
GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system");
|
||||
|
@ -43,8 +43,7 @@ static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
|
|||
memset(data, 0, len);
|
||||
return;
|
||||
}
|
||||
float ratio = 280896.0f * FPS_TARGET / GBA_ARM7TDMI_FREQUENCY;
|
||||
audioContext->ratio = audioContext->obtainedSpec.freq / ratio / (float) audioContext->audio->sampleRate;
|
||||
audioContext->ratio = GBAAudioCalculateRatio(audioContext->audio, audioContext->thread->fpsTarget, audioContext->obtainedSpec.freq);
|
||||
struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
|
||||
len /= 2 * audioContext->obtainedSpec.channels;
|
||||
if (audioContext->obtainedSpec.channels == 2) {
|
||||
|
|
|
@ -6,11 +6,17 @@
|
|||
#include <SDL.h>
|
||||
|
||||
struct GBASDLAudio {
|
||||
// Input
|
||||
size_t samples;
|
||||
|
||||
// State
|
||||
SDL_AudioSpec desiredSpec;
|
||||
SDL_AudioSpec obtainedSpec;
|
||||
float drift;
|
||||
float ratio;
|
||||
|
||||
struct GBAAudio* audio;
|
||||
struct GBAThread* thread;
|
||||
};
|
||||
|
||||
bool GBASDLInitAudio(struct GBASDLAudio* context);
|
||||
|
|
|
@ -248,14 +248,7 @@ static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEve
|
|||
void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
case SDL_QUIT:
|
||||
// FIXME: this isn't thread-safe
|
||||
if (context->debugger) {
|
||||
context->debugger->state = DEBUGGER_EXITING;
|
||||
}
|
||||
MutexLock(&context->stateMutex);
|
||||
context->state = THREAD_EXITING;
|
||||
ConditionWake(&context->stateCond);
|
||||
MutexUnlock(&context->stateMutex);
|
||||
GBAThreadEnd(context);
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
case SDL_WINDOWEVENT:
|
||||
|
|
|
@ -79,6 +79,9 @@ int main(int argc, char** argv) {
|
|||
|
||||
GBAMapOptionsToContext(&opts, &context);
|
||||
|
||||
renderer.audio.samples = context.audioBuffers;
|
||||
GBASDLInitAudio(&renderer.audio);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
renderer.events.fullscreen = graphicsOpts.fullscreen;
|
||||
renderer.window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer.viewportWidth, renderer.viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer.events.fullscreen));
|
||||
|
@ -145,7 +148,6 @@ static int _GBASDLInit(struct SoftwareRenderer* renderer) {
|
|||
}
|
||||
|
||||
GBASDLInitEvents(&renderer->events);
|
||||
GBASDLInitAudio(&renderer->audio);
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
#ifdef COLOR_16_BIT
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#ifndef NDEBUG
|
||||
static int _checkIntegrity(struct CircleBuffer* buffer) {
|
||||
if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == buffer->size) {
|
||||
if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == (ssize_t) buffer->size) {
|
||||
return 1;
|
||||
}
|
||||
if (buffer->capacity - buffer->size == ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr)) {
|
||||
if ((ssize_t) (buffer->capacity - buffer->size) == ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr)) {
|
||||
return 1;
|
||||
}
|
||||
if (buffer->capacity - buffer->size == ((int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr)) {
|
||||
if ((ssize_t) (buffer->capacity - buffer->size) == ((int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -26,10 +26,14 @@ void CircleBufferDeinit(struct CircleBuffer* buffer) {
|
|||
buffer->data = 0;
|
||||
}
|
||||
|
||||
unsigned CircleBufferSize(const struct CircleBuffer* buffer) {
|
||||
size_t CircleBufferSize(const struct CircleBuffer* buffer) {
|
||||
return buffer->size;
|
||||
}
|
||||
|
||||
size_t CircleBufferCapacity(const struct CircleBuffer* buffer) {
|
||||
return buffer->capacity;
|
||||
}
|
||||
|
||||
void CircleBufferClear(struct CircleBuffer* buffer) {
|
||||
buffer->size = 0;
|
||||
buffer->readPtr = buffer->data;
|
||||
|
@ -140,7 +144,7 @@ int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) {
|
|||
return 4;
|
||||
}
|
||||
|
||||
int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) {
|
||||
size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) {
|
||||
int8_t* data = buffer->readPtr;
|
||||
if (buffer->size == 0) {
|
||||
return 0;
|
||||
|
@ -171,7 +175,7 @@ int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) {
|
|||
return length;
|
||||
}
|
||||
|
||||
int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) {
|
||||
size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) {
|
||||
int8_t* data = buffer->readPtr;
|
||||
if (buffer->size == 0) {
|
||||
return 0;
|
||||
|
|
|
@ -5,21 +5,22 @@
|
|||
|
||||
struct CircleBuffer {
|
||||
void* data;
|
||||
unsigned capacity;
|
||||
unsigned size;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
void* readPtr;
|
||||
void* writePtr;
|
||||
};
|
||||
|
||||
void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity);
|
||||
void CircleBufferDeinit(struct CircleBuffer* buffer);
|
||||
unsigned CircleBufferSize(const struct CircleBuffer* buffer);
|
||||
size_t CircleBufferSize(const struct CircleBuffer* buffer);
|
||||
size_t CircleBufferCapacity(const struct CircleBuffer* buffer);
|
||||
void CircleBufferClear(struct CircleBuffer* buffer);
|
||||
int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value);
|
||||
int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value);
|
||||
int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value);
|
||||
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value);
|
||||
int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length);
|
||||
int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length);
|
||||
size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length);
|
||||
size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue