GB Audio: Increase sample rate

This commit is contained in:
Vicki Pfau 2022-08-06 23:08:28 -07:00
parent 137f3e5804
commit 1fb7d7a4a3
5 changed files with 105 additions and 31 deletions

View File

@ -75,6 +75,7 @@ Misc:
- Debugger: GDB now works while the game is paused
- Debugger: Add command to load external symbol file (fixes mgba.io/i/2480)
- FFmpeg: Support dynamic audio sample rate
- GB Audio: Increase sample rate
- GB MBC: Filter out MBC errors when cartridge is yanked (fixes mgba.io/i/2488)
- GB Video: Add default SGB border
- GBA: Automatically skip BIOS if ROM has invalid logo

View File

@ -10,8 +10,11 @@
CXX_GUARD_START
#include <mgba/core/interface.h>
#include <mgba/core/timing.h>
#define GB_MAX_SAMPLES 32
DECL_BITFIELD(GBAudioRegisterDuty, uint8_t);
DECL_BITS(GBAudioRegisterDuty, Length, 0, 6);
DECL_BITS(GBAudioRegisterDuty, Duty, 6, 2);
@ -195,6 +198,10 @@ struct GBAudio {
int32_t sampleInterval;
enum GBAudioStyle style;
int32_t lastSample;
int sampleIndex;
struct mStereoSample currentSamples[GB_MAX_SAMPLES];
struct mTimingEvent frameEvent;
struct mTimingEvent sampleEvent;
bool enable;

View File

@ -164,7 +164,12 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | 0x00197: Reserved (leave zero)
* 0x00198 - 0x0019F: Global cycle counter
* 0x001A0 - 0x001A1: Program counter for last cartridge read
* 0x001A2 - 0x0025F: Reserved (leave zero)
* 0x001A2 - 0x00247: Reserved (leave zero)
* 0x00248 - 0x0025F: Additional audio state
* | 0x00248 - 0x0024B: Last sample timestamp
* | 0x0024C: Current audio sample index
* | 0x0024D - 0x0024F: Reserved (leave zero)
* | 0x00250 - 0x0025F: Audio rendered samples
* 0x00260 - 0x002FF: OAM
* 0x00300 - 0x0037F: I/O memory
* 0x00380 - 0x003FE: HRAM
@ -430,7 +435,15 @@ struct GBSerializedState {
uint64_t globalCycles;
uint16_t cartBusPc;
uint16_t reserved[95];
uint16_t reserved[27];
struct {
int32_t lastSample;
uint8_t sampleIndex;
uint8_t reserved[3];
struct mStereoSample currentSamples[32];
} audio2;
uint8_t oam[GB_SIZE_OAM];

View File

@ -24,6 +24,8 @@
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 FILTER = 65368;
const int GB_AUDIO_VOLUME_MAX = 0x100;
static bool _writeSweep(struct GBAudioSweep* sweep, uint8_t value);
@ -44,6 +46,8 @@ static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch);
static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void GBAudioSample(struct GBAudio* audio, int32_t timestamp);
static const int _squareChannelDuty[4][8] = {
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1 },
@ -114,7 +118,9 @@ void GBAudioReset(struct GBAudio* audio) {
audio->ch3.wavedata8[15] = 0xFF;
audio->ch4 = (struct GBAudioNoiseChannel) { .envelope = { .dead = 2 } };
audio->frame = 0;
audio->sampleInterval = 128;
audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES;
audio->lastSample = 0;
audio->sampleIndex = 0;
audio->lastLeft = 0;
audio->lastRight = 0;
audio->capLeft = 0;
@ -468,6 +474,11 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
if (!audio->enable) {
return;
}
if (audio->p && channels != 0xF && timestamp - audio->lastSample > SAMPLE_INTERVAL) {
GBAudioSample(audio, timestamp);
return;
}
if (audio->playingCh1 && (channels & 0x1)) {
int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor;
int32_t diff = timestamp - audio->ch1.lastUpdate;
@ -735,41 +746,65 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
*right = sampleRight * (1 + audio->volumeRight);
}
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
void GBAudioSample(struct GBAudio* audio, int32_t timestamp) {
timestamp -= audio->lastSample;
timestamp -= audio->sampleIndex * SAMPLE_INTERVAL;
int interval = SAMPLE_INTERVAL * audio->timingFactor;
int sample;
for (sample = audio->sampleIndex; timestamp >= interval && sample < GB_MAX_SAMPLES; ++sample, timestamp -= interval) {
int16_t sampleLeft = 0;
int16_t sampleRight = 0;
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF);
GBAudioRun(audio, sample * interval + audio->lastSample, 0xF);
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7;
sampleRight = (sampleRight * audio->masterVolume * 6) >> 7;
mCoreSyncLockAudio(audio->p->sync);
unsigned produced;
int16_t degradedLeft = sampleLeft - (audio->capLeft >> 16);
int16_t degradedRight = sampleRight - (audio->capRight >> 16);
audio->capLeft = (sampleLeft << 16) - degradedLeft * 65184;
audio->capRight = (sampleRight << 16) - degradedRight * 65184;
sampleLeft = degradedLeft;
sampleRight = degradedRight;
audio->capLeft = (sampleLeft << 16) - degradedLeft * FILTER;
audio->capRight = (sampleRight << 16) - degradedRight * FILTER;
audio->currentSamples[sample].left = degradedLeft;
audio->currentSamples[sample].right = degradedRight;
}
audio->sampleIndex = sample;
if (sample == GB_MAX_SAMPLES) {
audio->lastSample += interval * GB_MAX_SAMPLES;
audio->sampleIndex = 0;
}
}
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
GBAudioSample(audio, mTimingCurrentTime(audio->timing));
mCoreSyncLockAudio(audio->p->sync);
unsigned produced;
int i;
for (i = 0; i < GB_MAX_SAMPLES; ++i) {
int16_t sampleLeft = audio->currentSamples[i].left;
int16_t sampleRight = audio->currentSamples[i].right;
if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
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;
}
}
produced = blip_samples_avail(audio->left);
if (audio->p->stream && audio->p->stream->postAudioFrame) {
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
}
}
produced = blip_samples_avail(audio->left);
bool wait = produced >= audio->samples;
if (!mCoreSyncProduceAudio(audio->p->sync, audio->left, audio->samples)) {
// Interrupted
@ -1035,6 +1070,15 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {
GBAudioPSGSerialize(audio, &state->audio.psg, &state->audio.flags);
size_t i;
for (i = 0; i < GB_MAX_SAMPLES; ++i) {
STORE_16LE(audio->currentSamples[i].left, 0, &state->audio2.currentSamples[i].left);
STORE_16LE(audio->currentSamples[i].right, 0, &state->audio2.currentSamples[i].right);
}
STORE_32LE(audio->lastSample, 0, &state->audio2.lastSample);
state->audio2.sampleIndex = audio->sampleIndex;
STORE_32LE(audio->capLeft, 0, &state->audio.capLeft);
STORE_32LE(audio->capRight, 0, &state->audio.capRight);
STORE_32LE(audio->sampleEvent.when - mTimingCurrentTime(audio->timing), 0, &state->audio.nextSample);
@ -1044,6 +1088,15 @@ void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* s
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
LOAD_32LE(audio->capLeft, 0, &state->audio.capLeft);
LOAD_32LE(audio->capRight, 0, &state->audio.capRight);
size_t i;
for (i = 0; i < GB_MAX_SAMPLES; ++i) {
LOAD_16LE(audio->currentSamples[i].left, 0, &state->audio2.currentSamples[i].left);
LOAD_16LE(audio->currentSamples[i].right, 0, &state->audio2.currentSamples[i].right);
}
LOAD_32LE(audio->lastSample, 0, &state->audio2.lastSample);
audio->sampleIndex = state->audio2.sampleIndex;
uint32_t when;
LOAD_32LE(when, 0, &state->audio.nextSample);
mTimingSchedule(audio->timing, &audio->sampleEvent, when);

View File

@ -428,7 +428,7 @@ static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
stream->videoDimensionsChanged(stream, width, height);
}
if (stream && stream->audioRateChanged) {
stream->audioRateChanged(stream, DMG_SM83_FREQUENCY / gb->audio.sampleInterval);
stream->audioRateChanged(stream, DMG_SM83_FREQUENCY / 32);
}
}