Core: Switch from blip to mAudioResampler

This commit is contained in:
Vicki Pfau 2024-04-22 00:42:26 -07:00
parent 89866aff95
commit f51cb153d1
28 changed files with 257 additions and 362 deletions

View File

@ -32,6 +32,7 @@ enum mCoreChecksumType {
mCHECKSUM_CRC32, mCHECKSUM_CRC32,
}; };
struct mAudioBuffer;
struct mCoreConfig; struct mCoreConfig;
struct mCoreSync; struct mCoreSync;
struct mDebuggerSymbols; struct mDebuggerSymbols;
@ -79,7 +80,7 @@ struct mCore {
void (*putPixels)(struct mCore*, const void* buffer, size_t stride); void (*putPixels)(struct mCore*, const void* buffer, size_t stride);
unsigned (*audioSampleRate)(const struct mCore*); unsigned (*audioSampleRate)(const struct mCore*);
struct blip_t* (*getAudioChannel)(struct mCore*, int ch); struct mAudioBuffer* (*getAudioBuffer)(struct mCore*);
void (*setAudioBufferSize)(struct mCore*, size_t samples); void (*setAudioBufferSize)(struct mCore*, size_t samples);
size_t (*getAudioBufferSize)(struct mCore*); size_t (*getAudioBufferSize)(struct mCore*);

View File

@ -16,7 +16,7 @@ CXX_GUARD_START
struct mCore; struct mCore;
struct mStateExtdataItem; struct mStateExtdataItem;
struct blip_t; struct mAudioBuffer;
enum mCoreFeature { enum mCoreFeature {
mCORE_FEATURE_OPENGL = 1, mCORE_FEATURE_OPENGL = 1,
@ -41,7 +41,7 @@ struct mAVStream {
void (*audioRateChanged)(struct mAVStream*, unsigned rate); void (*audioRateChanged)(struct mAVStream*, unsigned rate);
void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride); void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride);
void (*postAudioFrame)(struct mAVStream*, int16_t left, int16_t right); void (*postAudioFrame)(struct mAVStream*, int16_t left, int16_t right);
void (*postAudioBuffer)(struct mAVStream*, struct blip_t* left, struct blip_t* right); void (*postAudioBuffer)(struct mAVStream*, struct mAudioBuffer*);
}; };
struct mStereoSample { struct mStereoSample {

View File

@ -22,6 +22,7 @@ struct mCoreSync {
bool audioWait; bool audioWait;
Condition audioRequiredCond; Condition audioRequiredCond;
Mutex audioBufferMutex; Mutex audioBufferMutex;
size_t audioHighWater;
float fpsTarget; float fpsTarget;
}; };
@ -32,8 +33,8 @@ bool mCoreSyncWaitFrameStart(struct mCoreSync* sync);
void mCoreSyncWaitFrameEnd(struct mCoreSync* sync); void mCoreSyncWaitFrameEnd(struct mCoreSync* sync);
void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait); void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait);
struct blip_t; struct mAudioBuffer;
bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t*, size_t samples); bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct mAudioBuffer*);
void mCoreSyncLockAudio(struct mCoreSync* sync); void mCoreSyncLockAudio(struct mCoreSync* sync);
void mCoreSyncUnlockAudio(struct mCoreSync* sync); void mCoreSyncUnlockAudio(struct mCoreSync* sync);
void mCoreSyncConsumeAudio(struct mCoreSync* sync); void mCoreSyncConsumeAudio(struct mCoreSync* sync);

View File

@ -12,6 +12,7 @@ CXX_GUARD_START
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#include <mgba/core/timing.h> #include <mgba/core/timing.h>
#include <mgba-util/audio-buffer.h>
#define GB_MAX_SAMPLES 32 #define GB_MAX_SAMPLES 32
@ -166,14 +167,9 @@ struct GBAudio {
struct GBAudioWaveChannel ch3; struct GBAudioWaveChannel ch3;
struct GBAudioNoiseChannel ch4; struct GBAudioNoiseChannel ch4;
struct blip_t* left; struct mAudioBuffer buffer;
struct blip_t* right;
int16_t lastLeft;
int16_t lastRight;
int32_t capLeft; int32_t capLeft;
int32_t capRight; int32_t capRight;
int clock;
int32_t clockRate;
uint8_t volumeRight; uint8_t volumeRight;
uint8_t volumeLeft; uint8_t volumeLeft;

View File

@ -63,10 +63,6 @@ struct GBAAudio {
struct GBAAudioFIFO chA; struct GBAAudioFIFO chA;
struct GBAAudioFIFO chB; struct GBAAudioFIFO chB;
int16_t lastLeft;
int16_t lastRight;
int clock;
uint8_t volume; uint8_t volume;
bool volumeChA; bool volumeChA;
bool volumeChB; bool volumeChB;

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/sync.h> #include <mgba/core/sync.h>
#include <mgba/core/blip_buf.h> #include <mgba-util/audio-buffer.h>
static void _changeVideoSync(struct mCoreSync* sync, bool wait) { static void _changeVideoSync(struct mCoreSync* sync, bool wait) {
// Make sure the video thread can process events while the GBA thread is paused // Make sure the video thread can process events while the GBA thread is paused
@ -79,17 +79,17 @@ void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait) {
_changeVideoSync(sync, wait); _changeVideoSync(sync, wait);
} }
bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t* buf, size_t samples) { bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct mAudioBuffer* buf) {
if (!sync) { if (!sync) {
return true; return true;
} }
size_t produced = blip_samples_avail(buf); size_t produced = mAudioBufferAvailable(buf);
size_t producedNew = produced; size_t producedNew = produced;
while (sync->audioWait && producedNew >= samples) { while (sync->audioWait && sync->audioHighWater && producedNew >= sync->audioHighWater) {
ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex); ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
produced = producedNew; produced = producedNew;
producedNew = blip_samples_avail(buf); producedNew = mAudioBufferAvailable(buf);
} }
MutexUnlock(&sync->audioBufferMutex); MutexUnlock(&sync->audioBufferMutex);
return producedNew != produced; return producedNew != produced;

View File

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
#include <mgba/script/context.h> #include <mgba/script/context.h>
@ -368,7 +367,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
if (impl->sync.audioWait) { if (impl->sync.audioWait) {
MutexUnlock(&impl->stateMutex); MutexUnlock(&impl->stateMutex);
mCoreSyncLockAudio(&impl->sync); mCoreSyncLockAudio(&impl->sync);
mCoreSyncProduceAudio(&impl->sync, core->getAudioChannel(core, 0), core->getAudioBufferSize(core)); mCoreSyncProduceAudio(&impl->sync, core->getAudioBuffer(core));
MutexLock(&impl->stateMutex); MutexLock(&impl->stateMutex);
} }
} }
@ -498,6 +497,7 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) {
threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync; threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync;
threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync; threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync;
threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget; threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget;
threadContext->impl->sync.audioHighWater = 512;
MutexLock(&threadContext->impl->stateMutex); MutexLock(&threadContext->impl->stateMutex);
ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext); ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext);

View File

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gb/audio.h> #include <mgba/internal/gb/audio.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#include <mgba/core/sync.h> #include <mgba/core/sync.h>
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
@ -15,15 +14,10 @@
#include <mgba/internal/gba/audio.h> #include <mgba/internal/gba/audio.h>
#endif #endif
#ifdef __3DS__ #define AUDIO_BUFFER_SAMPLES 0x4000
#define blip_add_delta blip_add_delta_fast
#endif
#define FRAME_CYCLES (DMG_SM83_FREQUENCY >> 9) #define FRAME_CYCLES (DMG_SM83_FREQUENCY >> 9)
const uint32_t DMG_SM83_FREQUENCY = 0x400000; const uint32_t DMG_SM83_FREQUENCY = 0x400000;
static const int CLOCKS_PER_BLIP_FRAME = 0x1000;
static const unsigned BLIP_BUFFER_SIZE = 0x4000;
static const int SAMPLE_INTERVAL = 32; static const int SAMPLE_INTERVAL = 32;
static const int FILTER = 65368; static const int FILTER = 65368;
const int GB_AUDIO_VOLUME_MAX = 0x100; const int GB_AUDIO_VOLUME_MAX = 0x100;
@ -57,12 +51,7 @@ static const int _squareChannelDuty[4][8] = {
void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) { void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) {
audio->samples = samples; audio->samples = samples;
audio->left = blip_new(BLIP_BUFFER_SIZE); mAudioBufferInit(&audio->buffer, AUDIO_BUFFER_SAMPLES, 2);
audio->right = blip_new(BLIP_BUFFER_SIZE);
audio->clockRate = DMG_SM83_FREQUENCY;
// Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->left, DMG_SM83_FREQUENCY, 96000);
blip_set_rates(audio->right, DMG_SM83_FREQUENCY, 96000);
audio->forceDisableCh[0] = false; audio->forceDisableCh[0] = false;
audio->forceDisableCh[1] = false; audio->forceDisableCh[1] = false;
audio->forceDisableCh[2] = false; audio->forceDisableCh[2] = false;
@ -86,8 +75,7 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
} }
void GBAudioDeinit(struct GBAudio* audio) { void GBAudioDeinit(struct GBAudio* audio) {
blip_delete(audio->left); mAudioBufferDeinit(&audio->buffer);
blip_delete(audio->right);
} }
void GBAudioReset(struct GBAudio* audio) { void GBAudioReset(struct GBAudio* audio) {
@ -123,11 +111,9 @@ void GBAudioReset(struct GBAudio* audio) {
audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES; audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES;
audio->lastSample = 0; audio->lastSample = 0;
audio->sampleIndex = 0; audio->sampleIndex = 0;
audio->lastLeft = 0;
audio->lastRight = 0;
audio->capLeft = 0; audio->capLeft = 0;
audio->capRight = 0; audio->capRight = 0;
audio->clock = 0; mAudioBufferClear(&audio->buffer);
audio->playingCh1 = false; audio->playingCh1 = false;
audio->playingCh2 = false; audio->playingCh2 = false;
audio->playingCh3 = false; audio->playingCh3 = false;
@ -140,14 +126,8 @@ void GBAudioReset(struct GBAudio* audio) {
} }
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) { void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
if (samples > BLIP_BUFFER_SIZE / 2) {
samples = BLIP_BUFFER_SIZE / 2;
}
mCoreSyncLockAudio(audio->p->sync); mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples; audio->samples = samples;
blip_clear(audio->left);
blip_clear(audio->right);
audio->clock = 0;
mCoreSyncConsumeAudio(audio->p->sync); mCoreSyncConsumeAudio(audio->p->sync);
} }
@ -845,34 +825,25 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
unsigned produced; unsigned produced;
int i; int i;
for (i = 0; i < GB_MAX_SAMPLES; ++i) { for (i = 0; i < GB_MAX_SAMPLES; ++i) {
int16_t sampleLeft = audio->currentSamples[i].left; int16_t sample[2] = {
int16_t sampleRight = audio->currentSamples[i].right; audio->currentSamples[i].left,
if ((size_t) blip_samples_avail(audio->left) < audio->samples) { audio->currentSamples[i].right
blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft); };
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight); mAudioBufferWrite(&audio->buffer, sample, 1);
audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight;
audio->clock += SAMPLE_INTERVAL;
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME);
audio->clock -= CLOCKS_PER_BLIP_FRAME;
}
}
if (audio->p->stream && audio->p->stream->postAudioFrame) { if (audio->p->stream && audio->p->stream->postAudioFrame) {
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); audio->p->stream->postAudioFrame(audio->p->stream, sample[0], sample[1]);
} }
} }
produced = blip_samples_avail(audio->left); produced = mAudioBufferAvailable(&audio->buffer);
bool wait = produced >= audio->samples; bool wait = produced >= audio->samples;
if (!mCoreSyncProduceAudio(audio->p->sync, audio->left, audio->samples)) { if (!mCoreSyncProduceAudio(audio->p->sync, &audio->buffer)) {
// Interrupted // Interrupted
audio->p->earlyExit = true; audio->p->earlyExit = true;
} }
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) { if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right); audio->p->stream->postAudioBuffer(audio->p->stream, &audio->buffer);
} }
mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval * audio->timingFactor - cyclesLate); mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval * audio->timingFactor - cyclesLate);
} }

View File

@ -428,16 +428,9 @@ static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stri
gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer); gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer);
} }
static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) { static struct mAudioBuffer* _GBCoreGetAudioBuffer(struct mCore* core) {
struct GB* gb = core->board; struct GB* gb = core->board;
switch (ch) { return &gb->audio.buffer;
case 0:
return gb->audio.left;
case 1:
return gb->audio.right;
default:
return NULL;
}
} }
static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) { static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
@ -1308,7 +1301,7 @@ struct mCore* GBCoreCreate(void) {
core->getPixels = _GBCoreGetPixels; core->getPixels = _GBCoreGetPixels;
core->putPixels = _GBCorePutPixels; core->putPixels = _GBCorePutPixels;
core->audioSampleRate = _GBCoreAudioSampleRate; core->audioSampleRate = _GBCoreAudioSampleRate;
core->getAudioChannel = _GBCoreGetAudioChannel; core->getAudioBuffer = _GBCoreGetAudioBuffer;
core->setAudioBufferSize = _GBCoreSetAudioBufferSize; core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
core->getAudioBufferSize = _GBCoreGetAudioBufferSize; core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
core->setAVStream = _GBCoreSetAVStream; core->setAVStream = _GBCoreSetAVStream;

View File

@ -6,7 +6,6 @@
#include <mgba/internal/gba/audio.h> #include <mgba/internal/gba/audio.h>
#include <mgba/internal/arm/macros.h> #include <mgba/internal/arm/macros.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/sync.h> #include <mgba/core/sync.h>
#include <mgba/internal/gba/dma.h> #include <mgba/internal/gba/dma.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
@ -16,17 +15,12 @@
#define MP2K_LOCK_MAX 8 #define MP2K_LOCK_MAX 8
#ifdef __3DS__
#define blip_add_delta blip_add_delta_fast
#endif
mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio"); mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio");
const unsigned GBA_AUDIO_SAMPLES = 2048; const unsigned GBA_AUDIO_SAMPLES = 2048;
const int GBA_AUDIO_VOLUME_MAX = 0x100; const int GBA_AUDIO_VOLUME_MAX = 0x100;
static const int SAMPLE_INTERVAL = GBA_ARM7TDMI_FREQUENCY / 0x4000; static const int SAMPLE_INTERVAL = GBA_ARM7TDMI_FREQUENCY / 0x4000;
static const int CLOCKS_PER_FRAME = 0x800;
static int _applyBias(struct GBAAudio* audio, int sample); static int _applyBias(struct GBAAudio* audio, int sample);
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
@ -41,14 +35,10 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
++nr52; ++nr52;
#endif #endif
GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); GBAudioInit(&audio->psg, samples, nr52, GB_AUDIO_GBA);
audio->psg.timing = &audio->p->timing; audio->psg.timing = &audio->p->timing;
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
audio->psg.frameEvent.context = audio; audio->psg.frameEvent.context = audio;
audio->samples = samples; audio->samples = samples;
// Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
audio->forceDisableChA = false; audio->forceDisableChA = false;
audio->forceDisableChB = false; audio->forceDisableChB = false;
@ -93,10 +83,6 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->enable = false; audio->enable = false;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000;
audio->psg.sampleInterval = audio->sampleInterval; audio->psg.sampleInterval = audio->sampleInterval;
blip_clear(audio->psg.left);
blip_clear(audio->psg.right);
audio->clock = 0;
} }
void GBAAudioDeinit(struct GBAAudio* audio) { void GBAAudioDeinit(struct GBAAudio* audio) {
@ -104,14 +90,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
} }
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
if (samples > 0x2000) {
samples = 0x2000;
}
mCoreSyncLockAudio(audio->p->sync); mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples; audio->samples = samples;
blip_clear(audio->psg.left); audio->psg.samples = samples;
blip_clear(audio->psg.right);
audio->clock = 0;
mCoreSyncConsumeAudio(audio->p->sync); mCoreSyncConsumeAudio(audio->p->sync);
} }
@ -414,34 +395,24 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
unsigned produced; unsigned produced;
int i; int i;
for (i = 0; i < samples; ++i) { for (i = 0; i < samples; ++i) {
int16_t sampleLeft = audio->currentSamples[i].left; int16_t sample[2] = {
int16_t sampleRight = audio->currentSamples[i].right; audio->currentSamples[i].left,
if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { audio->currentSamples[i].right
blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft); };
blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight); mAudioBufferWrite(&audio->psg.buffer, sample, 1);
audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME);
blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME);
audio->clock -= CLOCKS_PER_FRAME;
}
}
if (audio->p->stream && audio->p->stream->postAudioFrame) { if (audio->p->stream && audio->p->stream->postAudioFrame) {
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); audio->p->stream->postAudioFrame(audio->p->stream, sample[0], sample[1]);
} }
} }
produced = blip_samples_avail(audio->psg.left); produced = mAudioBufferAvailable(&audio->psg.buffer);
bool wait = produced >= audio->samples; bool wait = produced >= audio->samples;
if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) { if (!mCoreSyncProduceAudio(audio->p->sync, &audio->psg.buffer)) {
// Interrupted // Interrupted
audio->p->earlyExit = true; audio->p->earlyExit = true;
} }
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) { if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right); audio->p->stream->postAudioBuffer(audio->p->stream, &audio->psg.buffer);
} }
mTimingSchedule(timing, &audio->sampleEvent, SAMPLE_INTERVAL - cyclesLate); mTimingSchedule(timing, &audio->sampleEvent, SAMPLE_INTERVAL - cyclesLate);

View File

@ -558,16 +558,9 @@ static unsigned _GBACoreAudioSampleRate(const struct mCore* core) {
return 65536; return 65536;
} }
static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) { static struct mAudioBuffer* _GBACoreGetAudioBuffer(struct mCore* core) {
struct GBA* gba = core->board; struct GBA* gba = core->board;
switch (ch) { return &gba->audio.psg.buffer;
case 0:
return gba->audio.psg.left;
case 1:
return gba->audio.psg.right;
default:
return NULL;
}
} }
static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) { static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
@ -1505,7 +1498,7 @@ struct mCore* GBACoreCreate(void) {
core->getPixels = _GBACoreGetPixels; core->getPixels = _GBACoreGetPixels;
core->putPixels = _GBACorePutPixels; core->putPixels = _GBACorePutPixels;
core->audioSampleRate = _GBACoreAudioSampleRate; core->audioSampleRate = _GBACoreAudioSampleRate;
core->getAudioChannel = _GBACoreGetAudioChannel; core->getAudioBuffer = _GBACoreGetAudioBuffer;
core->setAudioBufferSize = _GBACoreSetAudioBufferSize; core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
core->getAudioBufferSize = _GBACoreGetAudioBufferSize; core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
core->addCoreCallbacks = _GBACoreAddCoreCallbacks; core->addCoreCallbacks = _GBACoreAddCoreCallbacks;

View File

@ -4,7 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/serialize.h> #include <mgba/core/serialize.h>
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -61,7 +60,7 @@ static enum DarkenMode {
#define _3DS_INPUT 0x3344534B #define _3DS_INPUT 0x3344534B
#define AUDIO_SAMPLES 384 #define AUDIO_SAMPLES 1280
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16) #define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16)
#define DSP_BUFFERS 4 #define DSP_BUFFERS 4
@ -190,7 +189,7 @@ static void _map3DSKey(struct mInputMap* map, int ctrKey, int key) {
mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key); mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key);
} }
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right); static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer);
static void _drawStart(void) { static void _drawStart(void) {
if (frameStarted) { if (frameStarted) {
@ -343,12 +342,13 @@ static void _gameLoaded(struct mGUIRunner* runner) {
} }
osSetSpeedupEnable(true); osSetSpeedupEnable(true);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 32768);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 32768);
if (hasSound != NO_SOUND) { if (hasSound != NO_SOUND) {
audioPos = 0; audioPos = 0;
} }
if (hasSound == DSP_SUPPORTED) { if (hasSound == DSP_SUPPORTED) {
unsigned sampleRate = runner->core->audioSampleRate(runner->core);
double fauxClock = mCoreCalculateFramerateRatio(runner->core, 16756991. / 280095.);
ndspChnSetRate(0, sampleRate * fauxClock);
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t)); memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t));
} }
unsigned mode; unsigned mode;
@ -607,8 +607,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1)); GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
if (hasSound == NO_SOUND) { if (hasSound == NO_SOUND) {
blip_clear(runner->core->getAudioChannel(runner->core, 0)); mAudioBufferClear(runner->core->getAudioBuffer(runner->core));
blip_clear(runner->core->getAudioChannel(runner->core, 1));
} }
_drawTex(runner->core, faded, interframeBlending); _drawTex(runner->core, faded, interframeBlending);
@ -775,15 +774,14 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size
CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize); CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize);
} }
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) {
UNUSED(stream); UNUSED(stream);
if (hasSound == DSP_SUPPORTED) { if (hasSound == DSP_SUPPORTED) {
int startId = bufferId; int startId = bufferId;
while (dspBuffer[bufferId].status == NDSP_WBUF_QUEUED || dspBuffer[bufferId].status == NDSP_WBUF_PLAYING) { while (dspBuffer[bufferId].status == NDSP_WBUF_QUEUED || dspBuffer[bufferId].status == NDSP_WBUF_PLAYING) {
bufferId = (bufferId + 1) & (DSP_BUFFERS - 1); bufferId = (bufferId + 1) & (DSP_BUFFERS - 1);
if (bufferId == startId) { if (bufferId == startId) {
blip_clear(left); mAudioBufferClear(buffer);
blip_clear(right);
return; return;
} }
} }
@ -791,8 +789,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig
memset(&dspBuffer[bufferId], 0, sizeof(dspBuffer[bufferId])); memset(&dspBuffer[bufferId], 0, sizeof(dspBuffer[bufferId]));
dspBuffer[bufferId].data_pcm16 = tmpBuf; dspBuffer[bufferId].data_pcm16 = tmpBuf;
dspBuffer[bufferId].nsamples = AUDIO_SAMPLES; dspBuffer[bufferId].nsamples = AUDIO_SAMPLES;
blip_read_samples(left, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES, true); mAudioBufferRead(buffer, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES);
blip_read_samples(right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true);
DSP_FlushDataCache(dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES * 2 * sizeof(int16_t)); DSP_FlushDataCache(dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES * 2 * sizeof(int16_t));
ndspChnWaveBufAdd(0, &dspBuffer[bufferId]); ndspChnWaveBufAdd(0, &dspBuffer[bufferId]);
} }
@ -857,7 +854,6 @@ int main(int argc, char* argv[]) {
ndspChnReset(0); ndspChnReset(0);
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16);
ndspChnSetInterp(0, NDSP_INTERP_NONE); ndspChnSetInterp(0, NDSP_INTERP_NONE);
ndspChnSetRate(0, 32822);
ndspChnWaveBufClear(0); ndspChnWaveBufClear(0);
audioLeft = linearMemAlign(AUDIO_SAMPLES * DSP_BUFFERS * 2 * sizeof(int16_t), 0x80); audioLeft = linearMemAlign(AUDIO_SAMPLES * DSP_BUFFERS * 2 * sizeof(int16_t), 0x80);
memset(dspBuffer, 0, sizeof(dspBuffer)); memset(dspBuffer, 0, sizeof(dspBuffer));

View File

@ -7,7 +7,6 @@
#include <mgba-util/common.h> #include <mgba-util/common.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/cheats.h> #include <mgba/core/cheats.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
@ -30,7 +29,6 @@
#include "libretro_core_options.h" #include "libretro_core_options.h"
#define GB_SAMPLES 512 #define GB_SAMPLES 512
#define SAMPLE_RATE 32768
/* An alpha factor of 1/180 is *somewhat* equivalent /* An alpha factor of 1/180 is *somewhat* equivalent
* to calculating the average for the last 180 * to calculating the average for the last 180
* frames, or 3 seconds of runtime... */ * frames, or 3 seconds of runtime... */
@ -54,7 +52,7 @@ static retro_set_sensor_state_t sensorStateCallback;
static void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args); static void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
static void _postAudioBuffer(struct mAVStream*, blip_t* left, blip_t* right); static void _postAudioBuffer(struct mAVStream*, struct mAudioBuffer*);
static void _setRumble(struct mRumble* rumble, int enable); static void _setRumble(struct mRumble* rumble, int enable);
static uint8_t _readLux(struct GBALuminanceSource* lux); static uint8_t _readLux(struct GBALuminanceSource* lux);
static void _updateLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux);
@ -424,7 +422,7 @@ void retro_get_system_av_info(struct retro_system_av_info* info) {
info->geometry.aspect_ratio = width / (double) height; info->geometry.aspect_ratio = width / (double) height;
info->timing.fps = core->frequency(core) / (float) core->frameCycles(core); info->timing.fps = core->frequency(core) / (float) core->frameCycles(core);
info->timing.sample_rate = SAMPLE_RATE; info->timing.sample_rate = core->audioSampleRate(core);
} }
void retro_init(void) { void retro_init(void) {
@ -613,9 +611,8 @@ void retro_run(void) {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
if (core->platform(core) == mPLATFORM_GBA) { if (core->platform(core) == mPLATFORM_GBA) {
blip_t *audioChannelLeft = core->getAudioChannel(core, 0); struct mAudioBuffer *buffer = core->getAudioBuffer(core);
blip_t *audioChannelRight = core->getAudioChannel(core, 1); int samplesAvail = mAudioBufferAvailable(buffer);
int samplesAvail = blip_samples_avail(audioChannelLeft);
if (samplesAvail > 0) { if (samplesAvail > 0) {
/* Update 'running average' of number of /* Update 'running average' of number of
* samples per frame. * samples per frame.
@ -632,8 +629,7 @@ void retro_run(void) {
audioSampleBufferSize = (samplesToRead * 2); audioSampleBufferSize = (samplesToRead * 2);
audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t)); audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t));
} }
int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true); int produced = mAudioBufferRead(buffer, audioSampleBuffer, samplesToRead);
blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true);
if (produced > 0) { if (produced > 0) {
if (audioLowPassEnabled) { if (audioLowPassEnabled) {
_audioLowPassFilter(audioSampleBuffer, produced); _audioLowPassFilter(audioSampleBuffer, produced);
@ -884,9 +880,9 @@ bool retro_load_game(const struct retro_game_info* game) {
* to nominal number of samples per frame. * to nominal number of samples per frame.
* Buffer will be resized as required in * Buffer will be resized as required in
* retro_run(). */ * retro_run(). */
size_t audioSamplesPerFrame = (size_t)((float) SAMPLE_RATE * (float) core->frameCycles(core) / size_t audioSamplesPerFrame = (size_t)((float) core->audioSampleRate(core) * (float) core->frameCycles(core) /
(float)core->frequency(core)); (float)core->frequency(core));
audioSampleBufferSize = audioSamplesPerFrame * 2; audioSampleBufferSize = ceil(audioSamplesPerFrame) * 2;
audioSampleBuffer = malloc(audioSampleBufferSize * sizeof(int16_t)); audioSampleBuffer = malloc(audioSampleBufferSize * sizeof(int16_t));
audioSamplesPerFrameAvg = (float) audioSamplesPerFrame; audioSamplesPerFrameAvg = (float) audioSamplesPerFrame;
/* Internal audio buffer size should be /* Internal audio buffer size should be
@ -918,9 +914,6 @@ bool retro_load_game(const struct retro_game_info* game) {
core->setAudioBufferSize(core, GB_SAMPLES); core->setAudioBufferSize(core, GB_SAMPLES);
} }
blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), SAMPLE_RATE);
blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), SAMPLE_RATE);
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble); core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
core->setPeripheral(core, mPERIPH_ROTATION, &rotation); core->setPeripheral(core, mPERIPH_ROTATION, &rotation);
@ -1238,10 +1231,9 @@ void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, con
} }
/* Used only for GB/GBC content */ /* Used only for GB/GBC content */
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) {
UNUSED(stream); UNUSED(stream);
int produced = blip_read_samples(left, audioSampleBuffer, GB_SAMPLES, true); int produced = mAudioBufferRead(buffer, audioSampleBuffer, GB_SAMPLES);
blip_read_samples(right, audioSampleBuffer + 1, GB_SAMPLES, true);
if (produced > 0) { if (produced > 0) {
if (audioLowPassEnabled) { if (audioLowPassEnabled) {
_audioLowPassFilter(audioSampleBuffer, produced); _audioLowPassFilter(audioSampleBuffer, produced);

View File

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "psp2-context.h" #include "psp2-context.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -18,9 +17,10 @@
#include "feature/gui/gui-runner.h" #include "feature/gui/gui-runner.h"
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#include <mgba-util/memory.h> #include <mgba-util/audio-resampler.h>
#include <mgba-util/circle-buffer.h> #include <mgba-util/circle-buffer.h>
#include <mgba-util/math.h> #include <mgba-util/math.h>
#include <mgba-util/memory.h>
#include <mgba-util/threading.h> #include <mgba-util/threading.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
#include <mgba-util/platform/psp2/sce-vfs.h> #include <mgba-util/platform/psp2/sce-vfs.h>
@ -84,14 +84,21 @@ bool frameLimiter = true;
extern const uint8_t _binary_backdrop_png_start[]; extern const uint8_t _binary_backdrop_png_start[];
static vita2d_texture* backdrop = 0; static vita2d_texture* backdrop = 0;
#define BUFFERS 16
#define PSP2_SAMPLES 512 #define PSP2_SAMPLES 512
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16) #define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * BUFFERS)
struct mPSP2AudioBuffer {
int16_t samples[PSP2_SAMPLES * 2] __attribute__((__aligned__(64)));
bool full;
};
static struct mPSP2AudioContext { static struct mPSP2AudioContext {
struct mStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; struct mPSP2AudioBuffer outputBuffers[BUFFERS];
size_t writeOffset; int currentAudioBuffer;
size_t readOffset; int nextAudioBuffer;
size_t samples; struct mAudioBuffer buffer;
struct mAudioResampler resampler;
Mutex mutex; Mutex mutex;
Condition cond; Condition cond;
bool running; bool running;
@ -103,29 +110,26 @@ void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) {
static THREAD_ENTRY _audioThread(void* context) { static THREAD_ENTRY _audioThread(void* context) {
struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context; struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context;
uint32_t zeroBuffer[PSP2_SAMPLES] = {0}; const int16_t zeroBuffer[PSP2_SAMPLES * 2] __attribute__((__aligned__(64))) = {0};
void* buffer = zeroBuffer; const void* buffer = zeroBuffer;
int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO); int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO);
struct mPSP2AudioBuffer* outputBuffer = NULL;
while (audio->running) { while (audio->running) {
MutexLock(&audio->mutex); MutexLock(&audio->mutex);
if (buffer != zeroBuffer) { if (outputBuffer) {
// Can only happen in successive iterations // Can only happen in successive iterations
audio->samples -= PSP2_SAMPLES; outputBuffer->full = false;
ConditionWake(&audio->cond); ConditionWake(&audio->cond);
} }
if (audio->samples >= PSP2_SAMPLES) { outputBuffer = &audio->outputBuffers[audio->currentAudioBuffer];
buffer = &audio->buffer[audio->readOffset]; if (outputBuffer->full) {
audio->readOffset += PSP2_SAMPLES; buffer = outputBuffer->samples;
if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) { audio->currentAudioBuffer = (audio->currentAudioBuffer + 1) % BUFFERS;
audio->readOffset = 0;
}
// Don't mark samples as read until the next loop iteration to prevent
// writing to the buffer while being read (see above)
} else { } else {
buffer = zeroBuffer; buffer = zeroBuffer;
outputBuffer = NULL;
} }
MutexUnlock(&audio->mutex); MutexUnlock(&audio->mutex);
sceAudioOutOutput(audioPort, buffer); sceAudioOutOutput(audioPort, buffer);
} }
sceAudioOutReleasePort(audioPort); sceAudioOutReleasePort(audioPort);
@ -243,25 +247,21 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size
sceCameraRead(imageSource->cam - 1, &read); sceCameraRead(imageSource->cam - 1, &read);
} }
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buf) {
UNUSED(stream); UNUSED(stream);
MutexLock(&audioContext.mutex); MutexLock(&audioContext.mutex);
while (audioContext.samples + PSP2_SAMPLES >= PSP2_AUDIO_BUFFER_SIZE) { mAudioResamplerProcess(&audioContext.resampler);
if (!frameLimiter) { while (mAudioBufferAvailable(&audioContext.buffer) >= PSP2_SAMPLES) {
blip_clear(left); struct mPSP2AudioBuffer* buffer = &audioContext.outputBuffers[audioContext.nextAudioBuffer];
blip_clear(right); while (buffer->full) {
MutexUnlock(&audioContext.mutex); if (!frameLimiter) {
return; break;
}
ConditionWait(&audioContext.cond, &audioContext.mutex);
} }
ConditionWait(&audioContext.cond, &audioContext.mutex); mAudioBufferRead(&audioContext.buffer, buffer->samples, PSP2_SAMPLES);
} buffer->full = true;
struct mStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; audioContext.nextAudioBuffer = (audioContext.nextAudioBuffer + 1) % BUFFERS;
blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true);
blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true);
audioContext.samples += PSP2_SAMPLES;
audioContext.writeOffset += PSP2_SAMPLES;
if (audioContext.writeOffset >= PSP2_AUDIO_BUFFER_SIZE) {
audioContext.writeOffset = 0;
} }
MutexUnlock(&audioContext.mutex); MutexUnlock(&audioContext.mutex);
} }
@ -294,7 +294,11 @@ void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) {
UNUSED(runner); UNUSED(runner);
if (!frameLimiter && limit) { if (!frameLimiter && limit) {
MutexLock(&audioContext.mutex); MutexLock(&audioContext.mutex);
while (audioContext.samples) { while (true) {
struct mPSP2AudioBuffer* buffer = &audioContext.outputBuffers[audioContext.currentAudioBuffer];
if (!buffer->full) {
break;
}
ConditionWait(&audioContext.cond, &audioContext.mutex); ConditionWait(&audioContext.cond, &audioContext.mutex);
} }
MutexUnlock(&audioContext.mutex); MutexUnlock(&audioContext.mutex);
@ -334,6 +338,9 @@ void mPSP2Setup(struct mGUIRunner* runner) {
runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256); runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256);
runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES); runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES);
mAudioBufferInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE, 2);
mAudioResamplerInit(&audioContext.resampler, mINTERPOLATOR_COSINE);
mAudioResamplerSetDestination(&audioContext.resampler, &audioContext.buffer, 48000);
rotation.d.sample = _sampleRotation; rotation.d.sample = _sampleRotation;
rotation.d.readTiltX = _readTiltX; rotation.d.readTiltX = _readTiltX;
@ -374,12 +381,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
} }
void mPSP2LoadROM(struct mGUIRunner* runner) { void mPSP2LoadROM(struct mGUIRunner* runner) {
float rate = 60.0f / 1.001f;
sceDisplayGetRefreshRate(&rate);
double ratio = mCoreCalculateFramerateRatio(runner->core, rate);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
switch (runner->core->platform(runner->core)) { switch (runner->core->platform(runner->core)) {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
case mPLATFORM_GBA: case mPLATFORM_GBA:
@ -415,10 +416,17 @@ void mPSP2LoadROM(struct mGUIRunner* runner) {
MutexInit(&audioContext.mutex); MutexInit(&audioContext.mutex);
ConditionInit(&audioContext.cond); ConditionInit(&audioContext.cond);
memset(audioContext.buffer, 0, sizeof(audioContext.buffer)); mAudioBufferClear(&audioContext.buffer);
audioContext.readOffset = 0; audioContext.nextAudioBuffer = 0;
audioContext.writeOffset = 0; audioContext.currentAudioBuffer = 0;
audioContext.running = true; audioContext.running = true;
float rate = 60.0f / 1.001f;
sceDisplayGetRefreshRate(&rate);
double ratio = mCoreCalculateFramerateRatio(runner->core, rate);
unsigned sampleRate = runner->core->audioSampleRate(runner->core);
mAudioBufferClear(&audioContext.buffer);
mAudioResamplerSetSource(&audioContext.resampler, runner->core->getAudioBuffer(runner->core), sampleRate / ratio, true);
ThreadCreate(&audioThread, _audioThread, &audioContext); ThreadCreate(&audioThread, _audioThread, &audioContext);
} }
@ -483,6 +491,8 @@ void mPSP2Unpaused(struct mGUIRunner* runner) {
void mPSP2Teardown(struct mGUIRunner* runner) { void mPSP2Teardown(struct mGUIRunner* runner) {
UNUSED(runner); UNUSED(runner);
mCircleBufferDeinit(&rumble.history); mCircleBufferDeinit(&rumble.history);
mAudioResamplerDeinit(&audioContext.resampler);
mAudioBufferDeinit(&audioContext.buffer);
vita2d_free_texture(tex[0]); vita2d_free_texture(tex[0]);
vita2d_free_texture(tex[1]); vita2d_free_texture(tex[1]);
vita2d_free_texture(screenshot); vita2d_free_texture(screenshot);

View File

@ -36,7 +36,6 @@ void free(void*);
#include <mgba/flags.h> #include <mgba/flags.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h> #include <mgba/core/cache-set.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/map-cache.h> #include <mgba/core/map-cache.h>

View File

@ -21,7 +21,6 @@ ffi.set_source("mgba._pylib", """
#define MGBA_EXPORT #define MGBA_EXPORT
#include <mgba/flags.h> #include <mgba/flags.h>
#define OPAQUE_THREADING #define OPAQUE_THREADING
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h> #include <mgba/core/cache-set.h>
#include <mgba-util/common.h> #include <mgba-util/common.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>

View File

@ -1,57 +0,0 @@
# Copyright (c) 2013-2018 Jeffrey Pfau
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
class Buffer(object):
def __init__(self, native, internal_rate):
self._native = native
self._internal_rate = internal_rate
@property
def available(self):
return lib.blip_samples_avail(self._native)
def set_rate(self, rate):
lib.blip_set_rates(self._native, self._internal_rate, rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % samples)
count = self.read_into(buffer, samples, 1, 0)
return buffer[:count]
def read_into(self, buffer, samples, channels=1, interleave=0):
return lib.blip_read_samples(self._native, ffi.addressof(buffer, interleave), samples, channels == 2)
def clear(self):
lib.blip_clear(self._native)
class StereoBuffer(object):
def __init__(self, left, right):
self._left = left
self._right = right
@property
def available(self):
return min(self._left.available, self._right.available)
def set_rate(self, rate):
self._left.set_rate(rate)
self._right.set_rate(rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % (2 * samples))
count = self.read_into(buffer, samples)
return buffer[0:2 * count]
def read_into(self, buffer, samples):
samples = self._left.read_into(buffer, samples, 2, 0)
return self._right.read_into(buffer, samples, 2, 1)
def clear(self):
self._left.clear()
self._right.clear()

View File

@ -250,14 +250,6 @@ class Core(object):
def audio_buffer_size(self): def audio_buffer_size(self):
return self._core.getAudioBufferSize(self._core) return self._core.getAudioBufferSize(self._core)
@protected
def get_audio_channels(self):
return audio.StereoBuffer(self.get_audio_channel(0), self.get_audio_channel(1));
@protected
def get_audio_channel(self, channel):
return audio.Buffer(self._core.getAudioChannel(self._core, channel), self.frequency)
@protected @protected
def reset(self): def reset(self):
self._core.reset(self._core) self._core.reset(self._core)

View File

@ -7,11 +7,12 @@
#include "LogController.h" #include "LogController.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba/internal/gba/audio.h> #include <mgba/internal/gba/audio.h>
#include <QDebug>
using namespace QGBA; using namespace QGBA;
AudioDevice::AudioDevice(QObject* parent) AudioDevice::AudioDevice(QObject* parent)
@ -19,6 +20,13 @@ AudioDevice::AudioDevice(QObject* parent)
, m_context(nullptr) , m_context(nullptr)
{ {
setOpenMode(ReadOnly); setOpenMode(ReadOnly);
mAudioBufferInit(&m_buffer, 0x4000, 2);
mAudioResamplerInit(&m_resampler, mINTERPOLATOR_SINC);
}
AudioDevice::~AudioDevice() {
mAudioResamplerDeinit(&m_resampler);
mAudioBufferDeinit(&m_buffer);
} }
void AudioDevice::setFormat(const QAudioFormat& format) { void AudioDevice::setFormat(const QAudioFormat& format) {
@ -26,15 +34,18 @@ void AudioDevice::setFormat(const QAudioFormat& format) {
LOG(QT, INFO) << tr("Can't set format of context-less audio device"); LOG(QT, INFO) << tr("Can't set format of context-less audio device");
return; return;
} }
double fauxClock = mCoreCalculateFramerateRatio(m_context->core, m_context->impl->sync.fpsTarget);
mCoreSyncLockAudio(&m_context->impl->sync); mCoreSyncLockAudio(&m_context->impl->sync);
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 0), mCore* core = m_context->core;
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); mAudioResamplerSetSource(&m_resampler, core->getAudioBuffer(core), core->audioSampleRate(core), true);
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 1), m_format = format;
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); adjustResampler();
mCoreSyncUnlockAudio(&m_context->impl->sync); mCoreSyncUnlockAudio(&m_context->impl->sync);
} }
void AudioDevice::setBufferSamples(int samples) {
m_samples = samples;
}
void AudioDevice::setInput(mCoreThread* input) { void AudioDevice::setInput(mCoreThread* input) {
m_context = input; m_context = input;
} }
@ -45,15 +56,25 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) {
return 0; return 0;
} }
maxSize /= sizeof(mStereoSample); if (!maxSize) {
return 0;
}
mCoreSyncLockAudio(&m_context->impl->sync); mCoreSyncLockAudio(&m_context->impl->sync);
int available = std::min<qint64>({ mAudioResamplerProcess(&m_resampler);
blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)), if (mAudioBufferAvailable(&m_buffer) < 128) {
maxSize, mCoreSyncConsumeAudio(&m_context->impl->sync);
// Audio is running slow...let's wait a tiny bit for more to come in
QThread::usleep(100);
mCoreSyncLockAudio(&m_context->impl->sync);
mAudioResamplerProcess(&m_resampler);
}
quint64 available = std::min<quint64>({
mAudioBufferAvailable(&m_buffer),
static_cast<quint64>(maxSize / sizeof(mStereoSample)),
std::numeric_limits<int>::max() std::numeric_limits<int>::max()
}); });
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast<mStereoSample*>(data)->left, available, true); mAudioBufferRead(&m_buffer, reinterpret_cast<int16_t*>(data), available);
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast<mStereoSample*>(data)->right, available, true);
mCoreSyncConsumeAudio(&m_context->impl->sync); mCoreSyncConsumeAudio(&m_context->impl->sync);
return available * sizeof(mStereoSample); return available * sizeof(mStereoSample);
} }
@ -64,15 +85,33 @@ qint64 AudioDevice::writeData(const char*, qint64) {
} }
bool AudioDevice::atEnd() const { bool AudioDevice::atEnd() const {
return !bytesAvailable(); return false;
} }
qint64 AudioDevice::bytesAvailable() const { qint64 AudioDevice::bytesAvailable() const {
if (!m_context->core) {
return true;
}
int available = mAudioBufferAvailable(&m_buffer);
return available * sizeof(mStereoSample);
}
qint64 AudioDevice::bytesAvailable() {
if (!m_context->core) { if (!m_context->core) {
return true; return true;
} }
mCoreSyncLockAudio(&m_context->impl->sync); mCoreSyncLockAudio(&m_context->impl->sync);
int available = blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)); adjustResampler();
mAudioResamplerProcess(&m_resampler);
int available = mAudioBufferAvailable(&m_buffer);
mCoreSyncUnlockAudio(&m_context->impl->sync); mCoreSyncUnlockAudio(&m_context->impl->sync);
return available * sizeof(mStereoSample); return available * sizeof(mStereoSample);
} }
void AudioDevice::adjustResampler() {
mCore* core = m_context->core;
double fauxClock = mCoreCalculateFramerateRatio(m_context->core, m_context->impl->sync.fpsTarget);
mAudioResamplerSetDestination(&m_resampler, &m_buffer, m_format.sampleRate() * fauxClock);
m_context->impl->sync.audioHighWater = m_samples + m_resampler.highWaterMark + m_resampler.lowWaterMark;
m_context->impl->sync.audioHighWater *= core->audioSampleRate(core) / (m_format.sampleRate() * fauxClock);
}

View File

@ -8,6 +8,9 @@
#include <QAudioFormat> #include <QAudioFormat>
#include <QIODevice> #include <QIODevice>
#include <mgba-util/audio-buffer.h>
#include <mgba-util/audio-resampler.h>
struct mCoreThread; struct mCoreThread;
namespace QGBA { namespace QGBA {
@ -17,18 +20,28 @@ Q_OBJECT
public: public:
AudioDevice(QObject* parent = nullptr); AudioDevice(QObject* parent = nullptr);
virtual ~AudioDevice();
void setInput(mCoreThread* input); void setInput(mCoreThread* input);
void setFormat(const QAudioFormat& format); void setFormat(const QAudioFormat& format);
void setBufferSamples(int samples);
bool atEnd() const override; bool atEnd() const override;
qint64 bytesAvailable() const override; qint64 bytesAvailable() const override;
qint64 bytesAvailable();
bool isSequential() const override { return true; }
protected: protected:
virtual qint64 readData(char* data, qint64 maxSize) override; virtual qint64 readData(char* data, qint64 maxSize) override;
virtual qint64 writeData(const char* data, qint64 maxSize) override; virtual qint64 writeData(const char* data, qint64 maxSize) override;
private: private:
size_t m_samples = 512;
QAudioFormat m_format;
mCoreThread* m_context; mCoreThread* m_context;
mAudioBuffer m_buffer;
mAudioResampler m_resampler;
void adjustResampler();
}; };
} }

View File

@ -83,6 +83,7 @@ bool AudioProcessorQt::start() {
if (state != QAudio::IdleState) { if (state != QAudio::IdleState) {
return; return;
} }
recheckUnderflow();
m_recheckTimer.start(); m_recheckTimer.start();
}); });
#endif #endif
@ -91,6 +92,7 @@ bool AudioProcessorQt::start() {
if (m_audioOutput->state() == QAudio::SuspendedState) { if (m_audioOutput->state() == QAudio::SuspendedState) {
m_audioOutput->resume(); m_audioOutput->resume();
} else { } else {
m_device->setBufferSamples(m_samples);
m_device->setInput(input()); m_device->setInput(input());
m_device->setFormat(m_audioOutput->format()); m_device->setFormat(m_audioOutput->format());
m_audioOutput->start(m_device.get()); m_audioOutput->start(m_device.get());
@ -107,12 +109,17 @@ void AudioProcessorQt::pause() {
} }
} }
void AudioProcessorQt::setBufferSamples(int) { void AudioProcessorQt::setBufferSamples(int samples) {
m_samples = samples;
if (m_device) {
m_device->setBufferSamples(samples);
}
} }
void AudioProcessorQt::inputParametersChanged() { void AudioProcessorQt::inputParametersChanged() {
if (m_device) { if (m_device) {
m_device->setFormat(m_audioOutput->format()); m_device->setFormat(m_audioOutput->format());
m_device->setBufferSamples(m_samples);
} }
} }
@ -138,7 +145,7 @@ void AudioProcessorQt::recheckUnderflow() {
m_recheckTimer.stop(); m_recheckTimer.stop();
return; return;
} }
if (!m_device->atEnd()) { if (m_device->bytesAvailable()) {
start(); start();
m_recheckTimer.stop(); m_recheckTimer.stop();
} }

View File

@ -51,6 +51,7 @@ private:
std::unique_ptr<QAudioOutput> m_audioOutput; std::unique_ptr<QAudioOutput> m_audioOutput;
#endif #endif
std::unique_ptr<AudioDevice> m_device; std::unique_ptr<AudioDevice> m_device;
size_t m_samples = 1024;
unsigned m_sampleRate = 44100; unsigned m_sampleRate = 44100;
}; };

View File

@ -7,12 +7,6 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba/internal/gba/audio.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/core/blip_buf.h>
#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio"); mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio");
@ -47,6 +41,10 @@ bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext)
} }
context->core = 0; context->core = 0;
mAudioBufferInit(&context->buffer, context->samples, context->obtainedSpec.channels);
mAudioResamplerInit(&context->resampler, mINTERPOLATOR_SINC);
mAudioResamplerSetDestination(&context->resampler, &context->buffer, context->obtainedSpec.freq);
if (threadContext) { if (threadContext) {
context->core = threadContext->core; context->core = threadContext->core;
context->sync = &threadContext->impl->sync; context->sync = &threadContext->impl->sync;
@ -70,6 +68,8 @@ void mSDLDeinitAudio(struct mSDLAudio* context) {
SDL_PauseAudio(1); SDL_PauseAudio(1);
SDL_CloseAudio(); SDL_CloseAudio();
#endif #endif
mAudioBufferDeinit(&context->buffer);
mAudioResamplerDeinit(&context->resampler);
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
} }
@ -97,13 +97,11 @@ static void _mSDLAudioCallback(void* context, Uint8* data, int len) {
memset(data, 0, len); memset(data, 0, len);
return; return;
} }
blip_t* left = NULL; struct mAudioBuffer* buffer = NULL;
blip_t* right = NULL; unsigned sampleRate = 32768;
int32_t clockRate = 1;
if (audioContext->core) { if (audioContext->core) {
clockRate = audioContext->core->frequency(audioContext->core); buffer = audioContext->core->getAudioBuffer(audioContext->core);
left = audioContext->core->getAudioChannel(audioContext->core, 0); sampleRate = audioContext->core->audioSampleRate(audioContext->core);
right = audioContext->core->getAudioChannel(audioContext->core, 1);
} }
double fauxClock = 1; double fauxClock = 1;
if (audioContext->sync) { if (audioContext->sync) {
@ -111,18 +109,13 @@ static void _mSDLAudioCallback(void* context, Uint8* data, int len) {
fauxClock = mCoreCalculateFramerateRatio(audioContext->core, audioContext->sync->fpsTarget); fauxClock = mCoreCalculateFramerateRatio(audioContext->core, audioContext->sync->fpsTarget);
} }
mCoreSyncLockAudio(audioContext->sync); mCoreSyncLockAudio(audioContext->sync);
audioContext->sync->audioHighWater = audioContext->samples + audioContext->resampler.highWaterMark + audioContext->resampler.lowWaterMark;
audioContext->sync->audioHighWater *= sampleRate / (fauxClock * audioContext->obtainedSpec.freq);
} }
blip_set_rates(left, clockRate, audioContext->obtainedSpec.freq * fauxClock); mAudioResamplerSetSource(&audioContext->resampler, buffer, sampleRate / fauxClock, true);
blip_set_rates(right, clockRate, audioContext->obtainedSpec.freq * fauxClock); mAudioResamplerProcess(&audioContext->resampler);
len /= 2 * audioContext->obtainedSpec.channels; len /= 2 * audioContext->obtainedSpec.channels;
int available = blip_samples_avail(left); int available = mAudioBufferRead(&audioContext->buffer, (int16_t*) data, len);
if (available > len) {
available = len;
}
blip_read_samples(left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
if (audioContext->obtainedSpec.channels == 2) {
blip_read_samples(right, ((short*) data) + 1, available, 1);
}
if (audioContext->sync) { if (audioContext->sync) {
mCoreSyncConsumeAudio(audioContext->sync); mCoreSyncConsumeAudio(audioContext->sync);

View File

@ -11,6 +11,8 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba-util/audio-buffer.h>
#include <mgba-util/audio-resampler.h>
#include <SDL.h> #include <SDL.h>
// Altivec sometimes defines this // Altivec sometimes defines this
@ -30,6 +32,8 @@ struct mSDLAudio {
unsigned sampleRate; unsigned sampleRate;
// State // State
struct mAudioBuffer buffer;
struct mAudioResampler resampler;
SDL_AudioSpec desiredSpec; SDL_AudioSpec desiredSpec;
SDL_AudioSpec obtainedSpec; SDL_AudioSpec obtainedSpec;
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)

View File

@ -4,7 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "feature/gui/gui-runner.h" #include "feature/gui/gui-runner.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/internal/gb/video.h> #include <mgba/internal/gb/video.h>
#include <mgba/internal/gba/audio.h> #include <mgba/internal/gba/audio.h>
@ -305,9 +304,6 @@ static void _setup(struct mGUIRunner* runner) {
u32 samplerate = runner->core->audioSampleRate(runner->core); u32 samplerate = runner->core->audioSampleRate(runner->core);
double ratio = mCoreCalculateFramerateRatio(runner->core, 60.0); double ratio = mCoreCalculateFramerateRatio(runner->core, 60.0);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate);
audrvVoiceInit(&audrenDriver, 0, 2, PcmFormat_Int16, samplerate / ratio); audrvVoiceInit(&audrenDriver, 0, 2, PcmFormat_Int16, samplerate / ratio);
audrvVoiceSetDestinationMix(&audrenDriver, 0, AUDREN_FINAL_MIX_ID); audrvVoiceSetDestinationMix(&audrenDriver, 0, AUDREN_FINAL_MIX_ID);
audrvVoiceSetMixFactor(&audrenDriver, 0, 1.0f, 0, 0); audrvVoiceSetMixFactor(&audrenDriver, 0, 1.0f, 0, 0);
@ -576,7 +572,7 @@ static bool _running(struct mGUIRunner* runner) {
return appletMainLoop(); return appletMainLoop();
} }
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) {
UNUSED(stream); UNUSED(stream);
int i; int i;
while (true) { while (true) {
@ -590,15 +586,13 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig
break; break;
} }
if (!frameLimiter) { if (!frameLimiter) {
blip_clear(left); mAudioBufferClear(buffer);
blip_clear(right);
return; return;
} }
audrenWaitFrame(); audrenWaitFrame();
} }
struct mStereoSample* samples = audioBuffer[i]; struct mStereoSample* samples = audioBuffer[i];
blip_read_samples(left, &samples[0].left, SAMPLES, true); mAudioBufferRead(buffer, (int16_t*) samples, SAMPLES);
blip_read_samples(right, &samples[0].right, SAMPLES, true);
armDCacheFlush(samples, SAMPLES * sizeof(struct mStereoSample)); armDCacheFlush(samples, SAMPLES * sizeof(struct mStereoSample));
audrvVoiceAddWaveBuf(&audrenDriver, 0, &audrvBuffer[i]); audrvVoiceAddWaveBuf(&audrenDriver, 0, &audrvBuffer[i]);

View File

@ -3,7 +3,6 @@
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/blip_buf.h>
#include <mgba/core/cheats.h> #include <mgba/core/cheats.h>
#include <mgba/core/config.h> #include <mgba/core/config.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
@ -153,9 +152,6 @@ int main(int argc, char** argv) {
savestate = 0; savestate = 0;
} }
blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 0x8000);
blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 0x8000);
_fuzzRunloop(core, fuzzOpts.frames); _fuzzRunloop(core, fuzzOpts.frames);
if (hasDebugger) { if (hasDebugger) {
@ -188,8 +184,7 @@ static void _fuzzRunloop(struct mCore* core, int frames) {
do { do {
core->runFrame(core); core->runFrame(core);
--frames; --frames;
blip_clear(core->getAudioChannel(core, 0)); mAudioBufferClear(core->getAudioBuffer(core));
blip_clear(core->getAudioChannel(core, 1));
} while (frames > 0 && !_dispatchExiting); } while (frames > 0 && !_dispatchExiting);
} }

View File

@ -3,7 +3,6 @@
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/blip_buf.h>
#include <mgba/core/cheats.h> #include <mgba/core/cheats.h>
#include <mgba/core/config.h> #include <mgba/core/config.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>

View File

@ -14,13 +14,13 @@
#include <mgba-util/common.h> #include <mgba-util/common.h>
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include "feature/gui/gui-runner.h" #include "feature/gui/gui-runner.h"
#include <mgba/internal/gb/video.h> #include <mgba/internal/gb/video.h>
#include <mgba/internal/gba/audio.h> #include <mgba/internal/gba/audio.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#include <mgba-util/audio-resampler.h>
#include <mgba-util/gui.h> #include <mgba-util/gui.h>
#include <mgba-util/gui/file-select.h> #include <mgba-util/gui/file-select.h>
#include <mgba-util/gui/font.h> #include <mgba-util/gui/font.h>
@ -76,7 +76,7 @@ static enum VideoMode {
static void _retraceCallback(u32 count); static void _retraceCallback(u32 count);
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right); static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer*);
static void _audioDMA(void); static void _audioDMA(void);
static void _setRumble(struct mRumble* rumble, int enable); static void _setRumble(struct mRumble* rumble, int enable);
static void _sampleRotation(struct mRotationSource* source); static void _sampleRotation(struct mRotationSource* source);
@ -141,12 +141,14 @@ static void* framebuffer[2] = { 0, 0 };
static int whichFb = 0; static int whichFb = 0;
static struct AudioBuffer { static struct AudioBuffer {
struct mStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); int16_t samples[SAMPLES * 2] __attribute__((__aligned__(32)));
volatile size_t size; volatile bool full;
} audioBuffer[BUFFERS] = {0}; } audioBuffers[BUFFERS] = {0};
static struct mAudioBuffer audioBuffer;
static volatile int currentAudioBuffer = 0; static volatile int currentAudioBuffer = 0;
static volatile int nextAudioBuffer = 0; static volatile int nextAudioBuffer = 0;
static double audioSampleRate = 60.0 / 1.001; static double audioSampleRate = 60.0 / 1.001;
static struct mAudioResampler resampler;
static struct GUIFont* font; static struct GUIFont* font;
@ -246,11 +248,6 @@ static void reconfigureScreen(struct mGUIRunner* runner) {
if (runner) { if (runner) {
runner->params.width = vmode->fbWidth * guiScale * wAdjust; runner->params.width = vmode->fbWidth * guiScale * wAdjust;
runner->params.height = vmode->efbHeight * guiScale * hAdjust; runner->params.height = vmode->efbHeight * guiScale * hAdjust;
if (runner->core) {
double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
}
} }
} }
@ -269,7 +266,10 @@ int main(int argc, char* argv[]) {
AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
AUDIO_RegisterDMACallback(_audioDMA); AUDIO_RegisterDMACallback(_audioDMA);
memset(audioBuffer, 0, sizeof(audioBuffer)); memset(audioBuffers, 0, sizeof(audioBuffers));
mAudioBufferInit(&audioBuffer, SAMPLES * BUFFERS, 2);
mAudioResamplerInit(&resampler, mINTERPOLATOR_COSINE);
mAudioResamplerSetDestination(&resampler, &audioBuffer, 48000);
#ifdef FIXED_ROM_BUFFER #ifdef FIXED_ROM_BUFFER
romBufferSize = GBA_SIZE_ROM0; romBufferSize = GBA_SIZE_ROM0;
romBuffer = SYS_GetArena2Lo(); romBuffer = SYS_GetArena2Lo();
@ -663,6 +663,9 @@ int main(int argc, char* argv[]) {
VIDEO_WaitVSync(); VIDEO_WaitVSync();
mGUIDeinit(&runner); mGUIDeinit(&runner);
mAudioResamplerDeinit(&resampler);
mAudioBufferDeinit(&audioBuffer);
free(fifo); free(fifo);
free(texmem); free(texmem);
free(rescaleTexmem); free(rescaleTexmem);
@ -678,41 +681,38 @@ int main(int argc, char* argv[]) {
} }
static void _audioDMA(void) { static void _audioDMA(void) {
struct AudioBuffer* buffer = &audioBuffer[currentAudioBuffer]; struct AudioBuffer* buffer = &audioBuffers[currentAudioBuffer];
if (buffer->size != SAMPLES) { if (!buffer->full) {
printf("Recv %i %i%s", currentAudioBuffer, buffer->full, buffer->full ? "" : "!");
return; return;
} }
DCFlushRange(buffer->samples, SAMPLES * sizeof(struct mStereoSample)); DCFlushRange(buffer->samples, SAMPLES * sizeof(struct mStereoSample));
AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct mStereoSample)); AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct mStereoSample));
buffer->size = 0; buffer->full = false;
currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS; currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS;
} }
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buf) {
UNUSED(stream); UNUSED(stream);
UNUSED(buf);
mAudioResamplerProcess(&resampler);
u32 level = 0; u32 level = 0;
bool gotAudio = false;
_CPU_ISR_Disable(level); _CPU_ISR_Disable(level);
struct AudioBuffer* buffer = &audioBuffer[nextAudioBuffer]; while (mAudioBufferAvailable(&audioBuffer) >= SAMPLES) {
int available = blip_samples_avail(left); struct AudioBuffer* buffer = &audioBuffers[nextAudioBuffer];
if (available + buffer->size > SAMPLES) { if (buffer->full) {
available = SAMPLES - buffer->size; printf("Send %i %i%s", nextAudioBuffer, buffer->full, buffer->full ? "!!" : "");
} break;
if (available > 0) {
// These appear to be reversed for AUDIO_InitDMA
blip_read_samples(left, &buffer->samples[buffer->size].right, available, true);
blip_read_samples(right, &buffer->samples[buffer->size].left, available, true);
buffer->size += available;
}
if (buffer->size == SAMPLES) {
int next = (nextAudioBuffer + 1) % BUFFERS;
if ((currentAudioBuffer + BUFFERS - next) % BUFFERS != 1) {
nextAudioBuffer = next;
}
if (!AUDIO_GetDMAEnableFlag()) {
_audioDMA();
AUDIO_StartDMA();
} }
mAudioBufferRead(&audioBuffer, buffer->samples, SAMPLES);
buffer->full = true;
nextAudioBuffer = (nextAudioBuffer + 1) % BUFFERS;
gotAudio = true;
}
if (gotAudio && !AUDIO_GetDMAEnableFlag()) {
_audioDMA();
AUDIO_StartDMA();
} }
_CPU_ISR_Restore(level); _CPU_ISR_Restore(level);
} }
@ -1416,15 +1416,11 @@ void _setup(struct mGUIRunner* runner) {
nextAudioBuffer = 0; nextAudioBuffer = 0;
currentAudioBuffer = 0; currentAudioBuffer = 0;
int i; memset(audioBuffers, 0, sizeof(audioBuffers));
for (i = 0; i < BUFFERS; ++i) {
audioBuffer[i].size = 0;
}
runner->core->setAudioBufferSize(runner->core, SAMPLES); runner->core->setAudioBufferSize(runner->core, SAMPLES);
double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate); double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio); mAudioResamplerSetSource(&resampler, runner->core->getAudioBuffer(runner->core), runner->core->audioSampleRate(runner->core) / ratio, true);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
frameLimiter = true; frameLimiter = true;
} }
@ -1433,6 +1429,7 @@ void _gameUnloaded(struct mGUIRunner* runner) {
UNUSED(runner); UNUSED(runner);
AUDIO_StopDMA(); AUDIO_StopDMA();
frameLimiter = true; frameLimiter = true;
mAudioBufferClear(&audioBuffer);
VIDEO_SetBlack(true); VIDEO_SetBlack(true);
VIDEO_Flush(); VIDEO_Flush();
VIDEO_WaitVSync(); VIDEO_WaitVSync();