GB: First pass at audio refactor

This commit is contained in:
Jeffrey Pfau 2016-02-01 21:42:59 -08:00
parent 4edd7286f3
commit 92c6b90b03
27 changed files with 1156 additions and 973 deletions

View File

@ -15,7 +15,6 @@ set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support")
set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support") set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support")
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
set(USE_BLIP ON CACHE BOOL "Whether or not to enable blip_buf support")
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
set(BUILD_QT ON CACHE BOOL "Build Qt frontend") set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
@ -362,12 +361,7 @@ if(USE_FFMPEG)
endif() endif()
endif() endif()
if(USE_BLIP) list(APPEND THIRD_PARTY_SRC "${CMAKE_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c")
list(APPEND THIRD_PARTY_SRC "${CMAKE_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c")
add_definitions(-DRESAMPLE_LIBRARY=RESAMPLE_BLIP_BUF)
else()
add_definitions(-DRESAMPLE_LIBRARY=RESAMPLE_NN)
endif()
if(USE_MAGICK) if(USE_MAGICK)
list(APPEND FEATURES MAGICK) list(APPEND FEATURES MAGICK)
@ -515,6 +509,9 @@ if(M_CORE_GBA)
${GBA_CTX_SRC} ${GBA_CTX_SRC}
${DEBUGGER_SRC} ${DEBUGGER_SRC}
${GBA_RENDERER_SRC}) ${GBA_RENDERER_SRC})
if(NOT M_CORE_GB)
list(APPEND CORE_SRC ${CMAKE_SOURCE_DIR}/src/gb/audio.c)
endif()
endif() endif()
list(APPEND CORE_SRC list(APPEND CORE_SRC
@ -702,7 +699,6 @@ message(STATUS " GIF recording: ${USE_MAGICK}")
message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}") message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}")
message(STATUS " ZIP support: ${SUMMARY_ZIP}") message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " Better audio resampling: ${USE_BLIP}")
message(STATUS " OpenGL support: ${SUMMARY_GL}") message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:") message(STATUS "Frontends:")
message(STATUS " Qt: ${BUILD_QT}") message(STATUS " Qt: ${BUILD_QT}")

644
src/gb/audio.c Normal file
View File

@ -0,0 +1,644 @@
/* Copyright (c) 2013-2016 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/. */
#include "audio.h"
#include "core/sync.h"
#include "gb/gb.h"
#include "gb/io.h"
#define SWEEP_CYCLES (DMG_LR35902_FREQUENCY >> 7)
static const int CLOCKS_PER_FRAME = 0x1000;
static const unsigned BLIP_BUFFER_SIZE = 0x4000;
static void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value);
static bool _writeSweep(struct GBAudioEnvelope* envelope, uint8_t value);
static int32_t _updateSquareChannel(struct GBAudioSquareControl* envelope, int duty);
static void _updateEnvelope(struct GBAudioEnvelope* envelope);
static bool _updateSweep(struct GBAudioChannel1* ch);
static int32_t _updateChannel1(struct GBAudioChannel1* ch);
static int32_t _updateChannel2(struct GBAudioChannel2* ch);
static int32_t _updateChannel3(struct GBAudioChannel3* ch);
static int32_t _updateChannel4(struct GBAudioChannel4* ch);
static void _sample(struct GBAudio* audio, int32_t cycles);
void GBAudioInit(struct GBAudio* audio, size_t samples) {
audio->samples = samples;
audio->left = blip_new(BLIP_BUFFER_SIZE);
audio->right = blip_new(BLIP_BUFFER_SIZE);
audio->clockRate = DMG_LR35902_FREQUENCY;
// Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->left, DMG_LR35902_FREQUENCY, 96000);
blip_set_rates(audio->right, DMG_LR35902_FREQUENCY, 96000);
audio->forceDisableCh[0] = false;
audio->forceDisableCh[1] = false;
audio->forceDisableCh[2] = false;
audio->forceDisableCh[3] = false;
}
void GBAudioDeinit(struct GBAudio* audio) {
blip_delete(audio->left);
blip_delete(audio->right);
}
void GBAudioReset(struct GBAudio* audio) {
audio->nextEvent = 0;
audio->nextCh1 = 0;
audio->nextCh2 = 0;
audio->nextCh3 = 0;
audio->nextCh4 = 0;
audio->ch1 = (struct GBAudioChannel1) { .envelope = { .nextStep = INT_MAX }, .nextSweep = INT_MAX };
audio->ch2 = (struct GBAudioChannel2) { .envelope = { .nextStep = INT_MAX } };
audio->ch3 = (struct GBAudioChannel3) { .bank = 0 };
audio->ch4 = (struct GBAudioChannel4) { .envelope = { .nextStep = INT_MAX } };
audio->eventDiff = 0;
audio->nextSample = 0;
audio->sampleInterval = 128;
audio->volumeRight = 0;
audio->volumeLeft = 0;
audio->ch1Right = false;
audio->ch2Right = false;
audio->ch3Right = false;
audio->ch4Right = false;
audio->ch1Left = false;
audio->ch2Left = false;
audio->ch3Left = false;
audio->ch4Left = false;
audio->playingCh1 = false;
audio->playingCh2 = false;
audio->playingCh3 = false;
audio->playingCh4 = false;
}
void GBAudioWriteNR10(struct GBAudio* audio, uint8_t value) {
audio->ch1.shift = GBAudioRegisterSquareSweepGetShift(value);
audio->ch1.direction = GBAudioRegisterSquareSweepGetDirection(value);
audio->ch1.time = GBAudioRegisterSquareSweepGetTime(value);
if (audio->ch1.time) {
audio->ch1.nextSweep = audio->ch1.time * SWEEP_CYCLES;
} else {
audio->ch1.nextSweep = INT_MAX;
}
}
void GBAudioWriteNR11(struct GBAudio* audio, uint8_t value) {
_writeDuty(&audio->ch1.envelope, value);
}
void GBAudioWriteNR12(struct GBAudio* audio, uint8_t value) {
if (!_writeSweep(&audio->ch1.envelope, value)) {
audio->ch1.sample = 0;
}
}
void GBAudioWriteNR13(struct GBAudio* audio, uint8_t value) {
audio->ch1.control.frequency &= 0x700;
audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value);
}
void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
audio->ch1.control.frequency &= 0xFF;
audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8);
audio->ch1.control.stop = GBAudioRegisterControlGetStop(value << 8);
audio->ch1.control.endTime = (DMG_LR35902_FREQUENCY * (64 - audio->ch1.envelope.length)) >> 8;
if (GBAudioRegisterControlIsRestart(value << 8)) {
if (audio->ch1.time) {
audio->ch1.nextSweep = audio->ch1.time * SWEEP_CYCLES;
} else {
audio->ch1.nextSweep = INT_MAX;
}
if (audio->nextEvent == INT_MAX) {
audio->eventDiff = 0;
}
if (!audio->playingCh1) {
audio->nextCh1 = audio->eventDiff;
}
audio->playingCh1 = 1;
audio->ch1.envelope.currentVolume = audio->ch1.envelope.initialVolume;
if (audio->ch1.envelope.currentVolume > 0) {
audio->ch1.envelope.dead = 0;
}
if (audio->ch1.envelope.stepTime) {
audio->ch1.envelope.nextStep = audio->eventDiff;
} else {
audio->ch1.envelope.nextStep = INT_MAX;
}
audio->nextEvent = 0;
}
}
void GBAudioWriteNR21(struct GBAudio* audio, uint8_t value) {
_writeDuty(&audio->ch2.envelope, value);
}
void GBAudioWriteNR22(struct GBAudio* audio, uint8_t value) {
if (!_writeSweep(&audio->ch2.envelope, value)) {
audio->ch2.sample = 0;
}
}
void GBAudioWriteNR23(struct GBAudio* audio, uint8_t value) {
audio->ch2.control.frequency &= 0x700;
audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value);
}
void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
audio->ch2.control.frequency &= 0xFF;
audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8);
audio->ch2.control.stop = GBAudioRegisterControlGetStop(value << 8);
audio->ch2.control.endTime = (DMG_LR35902_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8;
if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh2 = 1;
audio->ch2.envelope.currentVolume = audio->ch2.envelope.initialVolume;
if (audio->ch2.envelope.currentVolume > 0) {
audio->ch2.envelope.dead = 0;
}
if (audio->nextEvent == INT_MAX) {
audio->eventDiff = 0;
}
if (!audio->playingCh2) {
audio->nextCh2 = audio->eventDiff;
}
if (audio->ch2.envelope.stepTime) {
audio->ch2.envelope.nextStep = audio->eventDiff;
} else {
audio->ch2.envelope.nextStep = INT_MAX;
}
audio->nextEvent = 0;
}
}
void GBAudioWriteNR30(struct GBAudio* audio, uint8_t value) {
audio->ch3.enable = GBAudioRegisterBankGetEnable(value);
if (audio->ch3.endTime >= 0) {
audio->playingCh3 = audio->ch3.enable;
}
}
void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) {
audio->ch3.length = value;
}
void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) {
audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value);
}
void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) {
audio->ch3.rate &= 0x700;
audio->ch3.rate |= GBAudioRegisterControlGetRate(value);
}
void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
audio->ch3.rate &= 0xFF;
audio->ch3.rate |= GBAudioRegisterControlGetRate(value << 8);
audio->ch3.stop = GBAudioRegisterControlGetStop(value << 8);
audio->ch3.endTime = (DMG_LR35902_FREQUENCY * (256 - audio->ch3.length)) >> 8;
if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh3 = audio->ch3.enable;
}
if (audio->playingCh3) {
if (audio->nextEvent == INT_MAX) {
audio->eventDiff = 0;
}
audio->nextCh3 = audio->eventDiff;
audio->nextEvent = 0;
}
}
void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) {
_writeDuty(&audio->ch4.envelope, value);
}
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) {
if (!_writeSweep(&audio->ch4.envelope, value)) {
audio->ch4.sample = 0;
}
}
void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) {
audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value);
audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value);
audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value);
}
void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
audio->ch4.stop = GBAudioRegisterNoiseControlGetStop(value);
audio->ch4.endTime = (DMG_LR35902_FREQUENCY * (64 - audio->ch4.envelope.length)) >> 8;
if (GBAudioRegisterNoiseControlIsRestart(value)) {
audio->playingCh4 = 1;
audio->ch4.envelope.currentVolume = audio->ch4.envelope.initialVolume;
if (audio->ch4.envelope.currentVolume > 0) {
audio->ch4.envelope.dead = 0;
}
if (audio->ch4.envelope.stepTime) {
audio->ch4.envelope.nextStep = 0;
} else {
audio->ch4.envelope.nextStep = INT_MAX;
}
if (audio->ch4.power) {
audio->ch4.lfsr = 0x40;
} else {
audio->ch4.lfsr = 0x4000;
}
if (audio->nextEvent == INT_MAX) {
audio->eventDiff = 0;
}
if (!audio->playingCh4) {
audio->nextCh4 = audio->eventDiff;
}
audio->nextEvent = 0;
}
}
void GBAudioWriteNR50(struct GBAudio* audio, uint8_t value) {
audio->volumeRight = GBRegisterNR50GetVolumeRight(value);
audio->volumeLeft = GBRegisterNR50GetVolumeLeft(value);
}
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t value) {
audio->ch1Right = GBRegisterNR51GetCh1Right(value);
audio->ch2Right = GBRegisterNR51GetCh2Right(value);
audio->ch3Right = GBRegisterNR51GetCh3Right(value);
audio->ch4Right = GBRegisterNR51GetCh4Right(value);
audio->ch1Left = GBRegisterNR51GetCh1Left(value);
audio->ch2Left = GBRegisterNR51GetCh2Left(value);
audio->ch3Left = GBRegisterNR51GetCh3Left(value);
audio->ch4Left = GBRegisterNR51GetCh4Left(value);
}
void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) {
audio->enable = GBAudioEnableGetEnable(value);
}
int32_t GBAudioProcessEvents(struct GBAudio* audio, int32_t cycles) {
if (audio->nextEvent == INT_MAX) {
return INT_MAX;
}
audio->nextEvent -= cycles;
audio->eventDiff += cycles;
while (audio->nextEvent <= 0) {
audio->nextEvent = INT_MAX;
if (audio->enable) {
if (audio->playingCh1 && !audio->ch1.envelope.dead) {
audio->nextCh1 -= audio->eventDiff;
if (audio->ch1.envelope.nextStep != INT_MAX) {
audio->ch1.envelope.nextStep -= audio->eventDiff;
if (audio->ch1.envelope.nextStep <= 0) {
int8_t sample = audio->ch1.control.hi * 0x10 - 0x8;
_updateEnvelope(&audio->ch1.envelope);
if (audio->ch1.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch1.envelope.nextStep;
}
audio->ch1.sample = sample * audio->ch1.envelope.currentVolume;
}
}
if (audio->ch1.nextSweep != INT_MAX) {
audio->ch1.nextSweep -= audio->eventDiff;
if (audio->ch1.nextSweep <= 0) {
audio->playingCh1 = _updateSweep(&audio->ch1);
if (audio->ch1.nextSweep < audio->nextEvent) {
audio->nextEvent = audio->ch1.nextSweep;
}
}
}
if (audio->nextCh1 <= 0) {
audio->nextCh1 += _updateChannel1(&audio->ch1);
}
if (audio->nextCh1 < audio->nextEvent) {
audio->nextEvent = audio->nextCh1;
}
if (audio->ch1.control.stop) {
audio->ch1.control.endTime -= audio->eventDiff;
if (audio->ch1.control.endTime <= 0) {
audio->playingCh1 = 0;
}
}
}
if (audio->playingCh2 && !audio->ch2.envelope.dead) {
audio->nextCh2 -= audio->eventDiff;
if (audio->ch2.envelope.nextStep != INT_MAX) {
audio->ch2.envelope.nextStep -= audio->eventDiff;
if (audio->ch2.envelope.nextStep <= 0) {
int8_t sample = audio->ch2.control.hi * 0x10 - 0x8;
_updateEnvelope(&audio->ch2.envelope);
if (audio->ch2.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch2.envelope.nextStep;
}
audio->ch2.sample = sample * audio->ch2.envelope.currentVolume;
}
}
if (audio->nextCh2 <= 0) {
audio->nextCh2 += _updateChannel2(&audio->ch2);
}
if (audio->nextCh2 < audio->nextEvent) {
audio->nextEvent = audio->nextCh2;
}
if (audio->ch2.control.stop) {
audio->ch2.control.endTime -= audio->eventDiff;
if (audio->ch2.control.endTime <= 0) {
audio->playingCh2 = 0;
}
}
}
if (audio->playingCh3) {
audio->nextCh3 -= audio->eventDiff;
if (audio->nextCh3 <= 0) {
audio->nextCh3 += _updateChannel3(&audio->ch3);
}
if (audio->nextCh3 < audio->nextEvent) {
audio->nextEvent = audio->nextCh3;
}
if (audio->ch3.stop) {
audio->ch3.endTime -= audio->eventDiff;
if (audio->ch3.endTime <= 0) {
audio->playingCh3 = 0;
}
}
}
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
audio->nextCh4 -= audio->eventDiff;
if (audio->ch4.envelope.nextStep != INT_MAX) {
audio->ch4.envelope.nextStep -= audio->eventDiff;
if (audio->ch4.envelope.nextStep <= 0) {
int8_t sample = (audio->ch4.sample >> 31) * 0x8;
_updateEnvelope(&audio->ch4.envelope);
if (audio->ch4.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch4.envelope.nextStep;
}
audio->ch4.sample = sample * audio->ch4.envelope.currentVolume;
}
}
if (audio->nextCh4 <= 0) {
audio->nextCh4 += _updateChannel4(&audio->ch4);
}
if (audio->nextCh4 < audio->nextEvent) {
audio->nextEvent = audio->nextCh4;
}
if (audio->ch4.stop) {
audio->ch4.endTime -= audio->eventDiff;
if (audio->ch4.endTime <= 0) {
audio->playingCh4 = 0;
}
}
}
}
if (audio->p) {
audio->p->memory.io[REG_NR51] &= ~0x000F;
audio->p->memory.io[REG_NR51] |= audio->playingCh1;
audio->p->memory.io[REG_NR51] |= audio->playingCh2 << 1;
audio->p->memory.io[REG_NR51] |= audio->playingCh3 << 2;
audio->p->memory.io[REG_NR51] |= audio->playingCh4 << 3;
audio->nextSample -= audio->eventDiff;
if (audio->nextSample <= 0) {
_sample(audio, audio->sampleInterval);
audio->nextSample += audio->sampleInterval;
}
if (audio->nextSample < audio->nextEvent) {
audio->nextEvent = audio->nextSample;
}
}
audio->eventDiff = 0;
}
return audio->nextEvent;
}
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
int sampleLeft = 0;
int sampleRight = 0;
if (audio->playingCh1 && !audio->forceDisableCh[0]) {
if (audio->ch1Left) {
sampleLeft += audio->ch1.sample;
}
if (audio->ch1Right) {
sampleRight += audio->ch1.sample;
}
}
if (audio->playingCh2 && !audio->forceDisableCh[1]) {
if (audio->ch2Left) {
sampleLeft += audio->ch2.sample;
}
if (audio->ch2Right) {
sampleRight += audio->ch2.sample;
}
}
if (audio->playingCh3 && !audio->forceDisableCh[2]) {
if (audio->ch3Left) {
sampleLeft += audio->ch3.sample;
}
if (audio->ch3Right) {
sampleRight += audio->ch3.sample;
}
}
if (audio->playingCh4 && !audio->forceDisableCh[3]) {
if (audio->ch4Left) {
sampleLeft += audio->ch4.sample;
}
if (audio->ch4Right) {
sampleRight += audio->ch4.sample;
}
}
*left = sampleLeft * (1 + audio->volumeLeft);
*right = sampleRight * (1 + audio->volumeRight);
}
void _sample(struct GBAudio* audio, int32_t cycles) {
int16_t sampleLeft = 0;
int16_t sampleRight = 0;
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
sampleLeft <<= 1;
sampleRight <<= 1;
mCoreSyncLockAudio(audio->p->sync);
unsigned produced;
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 += cycles;
if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->left, audio->clock);
blip_end_frame(audio->right, audio->clock);
audio->clock -= CLOCKS_PER_FRAME;
}
}
produced = blip_samples_avail(audio->left);
bool wait = produced >= audio->samples;
mCoreSyncProduceAudio(audio->p->sync, wait);
// TODO: Put AVStream back
}
void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value) {
envelope->length = GBAudioRegisterDutyGetLength(value);
envelope->duty = GBAudioRegisterDutyGetDuty(value);
}
bool _writeSweep(struct GBAudioEnvelope* envelope, uint8_t value) {
envelope->stepTime = GBAudioRegisterSweepGetStepTime(value);
envelope->direction = GBAudioRegisterSweepGetDirection(value);
envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);
envelope->dead = 0;
if (envelope->stepTime) {
envelope->nextStep = 0;
} else {
envelope->nextStep = INT_MAX;
if (envelope->initialVolume == 0) {
envelope->dead = 1;
return false;
}
}
return true;
}
static int32_t _updateSquareChannel(struct GBAudioSquareControl* control, int duty) {
control->hi = !control->hi;
int period = 4 * (2048 - control->frequency);
switch (duty) {
case 0:
return control->hi ? period : period * 7;
case 1:
return control->hi ? period * 2 : period * 6;
case 2:
return period * 4;
case 3:
return control->hi ? period * 6 : period * 2;
default:
// This should never be hit
return period * 4;
}
}
static void _updateEnvelope(struct GBAudioEnvelope* envelope) {
if (envelope->direction) {
++envelope->currentVolume;
} else {
--envelope->currentVolume;
}
if (envelope->currentVolume >= 15) {
envelope->currentVolume = 15;
envelope->nextStep = INT_MAX;
} else if (envelope->currentVolume <= 0) {
envelope->currentVolume = 0;
envelope->dead = 1;
envelope->nextStep = INT_MAX;
} else {
envelope->nextStep += envelope->stepTime * (DMG_LR35902_FREQUENCY >> 6);
}
}
static bool _updateSweep(struct GBAudioChannel1* ch) {
if (ch->direction) {
int frequency = ch->control.frequency;
frequency -= frequency >> ch->shift;
if (frequency >= 0) {
ch->control.frequency = frequency;
}
} else {
int frequency = ch->control.frequency;
frequency += frequency >> ch->shift;
if (frequency < 2048) {
ch->control.frequency = frequency;
} else {
return false;
}
}
ch->nextSweep += ch->time * SWEEP_CYCLES;
return true;
}
static int32_t _updateChannel1(struct GBAudioChannel1* ch) {
int timing = _updateSquareChannel(&ch->control, ch->envelope.duty);
ch->sample = ch->control.hi * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
return timing;
}
static int32_t _updateChannel2(struct GBAudioChannel2* ch) {
int timing = _updateSquareChannel(&ch->control, ch->envelope.duty);
ch->sample = ch->control.hi * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
return timing;
}
static int32_t _updateChannel3(struct GBAudioChannel3* ch) {
int i;
int start;
int end;
int volume;
switch (ch->volume) {
case 0:
volume = 0;
break;
case 1:
volume = 4;
break;
case 2:
volume = 2;
break;
case 3:
volume = 1;
break;
default:
volume = 3;
break;
}
if (ch->size) {
start = 7;
end = 0;
} else if (ch->bank) {
start = 7;
end = 4;
} else {
start = 3;
end = 0;
}
uint32_t bitsCarry = ch->wavedata[end] & 0x000000F0;
uint32_t bits;
for (i = start; i >= end; --i) {
bits = ch->wavedata[i] & 0x000000F0;
ch->wavedata[i] = ((ch->wavedata[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata[i] & 0xF0F0F000) >> 12);
ch->wavedata[i] |= bitsCarry << 20;
bitsCarry = bits;
}
ch->sample = bitsCarry >> 4;
ch->sample -= 8;
ch->sample *= volume * 4;
return 2 * (2048 - ch->rate);
}
static int32_t _updateChannel4(struct GBAudioChannel4* ch) {
int lsb = ch->lfsr & 1;
ch->sample = lsb * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
ch->lfsr >>= 1;
ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8);
int timing = ch->ratio ? 2 * ch->ratio : 1;
timing <<= ch->frequency;
timing *= 8;
return timing;
}

218
src/gb/audio.h Normal file
View File

@ -0,0 +1,218 @@
/* Copyright (c) 2013-2016 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/. */
#ifndef GB_AUDIO_H
#define GB_AUDIO_H
#include "util/common.h"
#include "third-party/blip_buf/blip_buf.h"
DECL_BITFIELD(GBAudioRegisterDuty, uint8_t);
DECL_BITS(GBAudioRegisterDuty, Length, 0, 6);
DECL_BITS(GBAudioRegisterDuty, Duty, 6, 2);
DECL_BITFIELD(GBAudioRegisterSweep, uint8_t);
DECL_BITS(GBAudioRegisterSweep, StepTime, 0, 3);
DECL_BIT(GBAudioRegisterSweep, Direction, 3);
DECL_BITS(GBAudioRegisterSweep, InitialVolume, 4, 4);
DECL_BITFIELD(GBAudioRegisterControl, uint16_t);
DECL_BITS(GBAudioRegisterControl, Rate, 0, 11);
DECL_BITS(GBAudioRegisterControl, Frequency, 0, 11);
DECL_BIT(GBAudioRegisterControl, Stop, 14);
DECL_BIT(GBAudioRegisterControl, Restart, 15);
DECL_BITFIELD(GBAudioRegisterSquareSweep, uint8_t);
DECL_BITS(GBAudioRegisterSquareSweep, Shift, 0, 3);
DECL_BIT(GBAudioRegisterSquareSweep, Direction, 3);
DECL_BITS(GBAudioRegisterSquareSweep, Time, 4, 3);
DECL_BITFIELD(GBAudioRegisterBank, uint8_t);
DECL_BIT(GBAudioRegisterBank, Size, 5);
DECL_BIT(GBAudioRegisterBank, Bank, 6);
DECL_BIT(GBAudioRegisterBank, Enable, 7);
DECL_BITFIELD(GBAudioRegisterBankVolume, uint8_t);
DECL_BITS(GBAudioRegisterBankVolume, VolumeGB, 5, 2);
DECL_BITS(GBAudioRegisterBankVolume, VolumeGBA, 5, 3);
DECL_BITFIELD(GBAudioRegisterNoiseFeedback, uint8_t);
DECL_BITS(GBAudioRegisterNoiseFeedback, Ratio, 0, 3);
DECL_BIT(GBAudioRegisterNoiseFeedback, Power, 3);
DECL_BITS(GBAudioRegisterNoiseFeedback, Frequency, 4, 4);
DECL_BITFIELD(GBAudioRegisterNoiseControl, uint8_t);
DECL_BIT(GBAudioRegisterNoiseControl, Stop, 6);
DECL_BIT(GBAudioRegisterNoiseControl, Restart, 7);
DECL_BITFIELD(GBRegisterNR50, uint8_t);
DECL_BITS(GBRegisterNR50, VolumeRight, 0, 3);
DECL_BITS(GBRegisterNR50, VolumeLeft, 4, 3);
DECL_BITFIELD(GBRegisterNR51, uint8_t);
DECL_BIT(GBRegisterNR51, Ch1Right, 0);
DECL_BIT(GBRegisterNR51, Ch2Right, 1);
DECL_BIT(GBRegisterNR51, Ch3Right, 2);
DECL_BIT(GBRegisterNR51, Ch4Right, 3);
DECL_BIT(GBRegisterNR51, Ch1Left, 4);
DECL_BIT(GBRegisterNR51, Ch2Left, 5);
DECL_BIT(GBRegisterNR51, Ch3Left, 6);
DECL_BIT(GBRegisterNR51, Ch4Left, 7);
DECL_BITFIELD(GBAudioEnable, uint8_t);
DECL_BIT(GBAudioEnable, PlayingCh1, 0);
DECL_BIT(GBAudioEnable, PlayingCh2, 1);
DECL_BIT(GBAudioEnable, PlayingCh3, 2);
DECL_BIT(GBAudioEnable, PlayingCh4, 3);
DECL_BIT(GBAudioEnable, Enable, 7);
struct GB;
struct GBAudioEnvelope {
uint8_t length;
uint8_t duty;
uint8_t stepTime;
uint8_t initialVolume;
bool direction;
int currentVolume;
int dead;
int32_t nextStep;
};
struct GBAudioSquareControl {
uint16_t frequency;
bool stop;
int hi;
int32_t nextStep;
int32_t endTime;
};
struct GBAudioChannel1 {
uint8_t shift;
uint8_t time;
bool direction;
int32_t nextSweep;
struct GBAudioEnvelope envelope;
struct GBAudioSquareControl control;
int8_t sample;
};
struct GBAudioChannel2 {
struct GBAudioEnvelope envelope;
struct GBAudioSquareControl control;
int8_t sample;
};
struct GBAudioChannel3 {
bool size;
bool bank;
bool enable;
uint8_t length;
uint8_t volume;
uint16_t rate;
bool stop;
int32_t endTime;
uint32_t wavedata[8];
int8_t sample;
};
struct GBAudioChannel4 {
struct GBAudioEnvelope envelope;
uint8_t ratio;
uint8_t frequency;
bool power;
bool stop;
int32_t endTime;
uint32_t lfsr;
int8_t sample;
};
struct GBAudio {
struct GB* p;
struct GBAudioChannel1 ch1;
struct GBAudioChannel2 ch2;
struct GBAudioChannel3 ch3;
struct GBAudioChannel4 ch4;
blip_t* left;
blip_t* right;
int16_t lastLeft;
int16_t lastRight;
int clock;
int32_t clockRate;
uint8_t volumeRight;
uint8_t volumeLeft;
bool ch1Right;
bool ch2Right;
bool ch3Right;
bool ch4Right;
bool ch1Left;
bool ch2Left;
bool ch3Left;
bool ch4Left;
bool playingCh1;
bool playingCh2;
bool playingCh3;
bool playingCh4;
int32_t nextEvent;
int32_t eventDiff;
int32_t nextSample;
int32_t sampleInterval;
int32_t nextCh1;
int32_t nextCh2;
int32_t nextCh3;
int32_t nextCh4;
bool enable;
size_t samples;
bool forceDisableCh[4];
};
void GBAudioInit(struct GBAudio* audio, size_t samples);
void GBAudioDeinit(struct GBAudio* audio);
void GBAudioReset(struct GBAudio* audio);
void GBAudioWriteNR10(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR11(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR12(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR13(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR14(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR21(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR22(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR23(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR24(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR30(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR31(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR32(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR33(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR34(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR41(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR43(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR44(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR50(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR52(struct GBAudio* audio, uint8_t);
int32_t GBAudioProcessEvents(struct GBAudio* audio, int32_t cycles);
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right);
#endif

View File

@ -45,6 +45,9 @@ static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component)
gb->video.p = gb; gb->video.p = gb;
GBVideoInit(&gb->video); GBVideoInit(&gb->video);
gb->audio.p = gb;
GBAudioInit(&gb->audio, 2048); // TODO: Remove magic constant
gb->timer.p = gb; gb->timer.p = gb;
gb->romVf = 0; gb->romVf = 0;
@ -156,6 +159,7 @@ void GBReset(struct LR35902Core* cpu) {
GBVideoReset(&gb->video); GBVideoReset(&gb->video);
GBTimerReset(&gb->timer); GBTimerReset(&gb->timer);
GBIOReset(gb); GBIOReset(gb);
GBAudioReset(&gb->audio);
} }
void GBUpdateIRQs(struct GB* gb) { void GBUpdateIRQs(struct GB* gb) {
@ -212,6 +216,11 @@ void GBProcessEvents(struct LR35902Core* cpu) {
nextEvent = testEvent; nextEvent = testEvent;
} }
testEvent = GBAudioProcessEvents(&gb->audio, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
testEvent = GBTimerProcessEvents(&gb->timer, cycles); testEvent = GBTimerProcessEvents(&gb->timer, cycles);
if (testEvent < nextEvent) { if (testEvent < nextEvent) {
nextEvent = testEvent; nextEvent = testEvent;

View File

@ -12,6 +12,7 @@
#include "lr35902/lr35902.h" #include "lr35902/lr35902.h"
#include "gb/audio.h"
#include "gb/memory.h" #include "gb/memory.h"
#include "gb/timer.h" #include "gb/timer.h"
#include "gb/video.h" #include "gb/video.h"
@ -39,6 +40,7 @@ enum GBIRQVector {
GB_VECTOR_KEYPAD = 0x60, GB_VECTOR_KEYPAD = 0x60,
}; };
struct mCoreSync;
struct GB { struct GB {
struct LR35902Component d; struct LR35902Component d;
@ -46,6 +48,9 @@ struct GB {
struct GBMemory memory; struct GBMemory memory;
struct GBVideo video; struct GBVideo video;
struct GBTimer timer; struct GBTimer timer;
struct GBAudio audio;
struct mCoreSync* sync;
uint8_t* keySource; uint8_t* keySource;

View File

@ -55,6 +55,87 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
case REG_DIV: case REG_DIV:
GBTimerDivReset(&gb->timer); GBTimerDivReset(&gb->timer);
return; return;
case REG_NR10:
GBAudioWriteNR10(&gb->audio, value);
break;
case REG_NR11:
GBAudioWriteNR11(&gb->audio, value);
break;
case REG_NR12:
GBAudioWriteNR12(&gb->audio, value);
break;
case REG_NR13:
GBAudioWriteNR13(&gb->audio, value);
break;
case REG_NR14:
GBAudioWriteNR14(&gb->audio, value);
break;
case REG_NR21:
GBAudioWriteNR21(&gb->audio, value);
break;
case REG_NR22:
GBAudioWriteNR22(&gb->audio, value);
break;
case REG_NR23:
GBAudioWriteNR23(&gb->audio, value);
break;
case REG_NR24:
GBAudioWriteNR24(&gb->audio, value);
break;
case REG_NR30:
GBAudioWriteNR30(&gb->audio, value);
break;
case REG_NR31:
GBAudioWriteNR31(&gb->audio, value);
break;
case REG_NR32:
GBAudioWriteNR32(&gb->audio, value);
break;
case REG_NR33:
GBAudioWriteNR33(&gb->audio, value);
break;
case REG_NR34:
GBAudioWriteNR34(&gb->audio, value);
break;
case REG_NR41:
GBAudioWriteNR41(&gb->audio, value);
break;
case REG_NR42:
GBAudioWriteNR42(&gb->audio, value);
break;
case REG_NR43:
GBAudioWriteNR43(&gb->audio, value);
break;
case REG_NR44:
GBAudioWriteNR44(&gb->audio, value);
break;
case REG_NR50:
GBAudioWriteNR50(&gb->audio, value);
break;
case REG_NR51:
GBAudioWriteNR51(&gb->audio, value);
break;
case REG_NR52:
GBAudioWriteNR52(&gb->audio, value);
break;
case REG_WAVE_0:
case REG_WAVE_1:
case REG_WAVE_2:
case REG_WAVE_3:
case REG_WAVE_4:
case REG_WAVE_5:
case REG_WAVE_6:
case REG_WAVE_7:
case REG_WAVE_8:
case REG_WAVE_9:
case REG_WAVE_A:
case REG_WAVE_B:
case REG_WAVE_C:
case REG_WAVE_D:
case REG_WAVE_E:
case REG_WAVE_F:
((uint8_t*) gb->audio.ch3.wavedata)[address - REG_WAVE_0] = value; // TODO: Big endian
break;
case REG_JOYP: case REG_JOYP:
case REG_TIMA: case REG_TIMA:
case REG_TMA: case REG_TMA:

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2016 Jeffrey Pfau
* *
* 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
@ -12,78 +12,39 @@
#include "gba/video.h" #include "gba/video.h"
const unsigned GBA_AUDIO_SAMPLES = 2048; const unsigned GBA_AUDIO_SAMPLES = 2048;
const unsigned BLIP_BUFFER_SIZE = 0x4000;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
const int GBA_AUDIO_VOLUME_MAX = 0x100; const int GBA_AUDIO_VOLUME_MAX = 0x100;
#define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128)
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
static const int CLOCKS_PER_FRAME = 0x400; static const int CLOCKS_PER_FRAME = 0x400;
#endif
static bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value);
static int32_t _updateSquareChannel(struct GBAAudioSquareControl* envelope, int duty);
static void _updateEnvelope(struct GBAAudioEnvelope* envelope);
static bool _updateSweep(struct GBAAudioChannel1* ch);
static int32_t _updateChannel1(struct GBAAudioChannel1* ch);
static int32_t _updateChannel2(struct GBAAudioChannel2* ch);
static int32_t _updateChannel3(struct GBAAudioChannel3* ch);
static int32_t _updateChannel4(struct GBAAudioChannel4* ch);
static int _applyBias(struct GBAAudio* audio, int sample); static int _applyBias(struct GBAAudio* audio, int sample);
static void _sample(struct GBAAudio* audio); static void _sample(struct GBAAudio* audio);
void GBAAudioInit(struct GBAAudio* audio, size_t samples) { void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
audio->psg.p = NULL;
GBAudioInit(&audio->psg, 0);
audio->samples = samples; audio->samples = samples;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
CircleBufferInit(&audio->left, samples * sizeof(int16_t));
CircleBufferInit(&audio->right, samples * sizeof(int16_t));
#else
audio->left = blip_new(BLIP_BUFFER_SIZE);
audio->right = blip_new(BLIP_BUFFER_SIZE);
// Guess too large; we hang producing extra samples if we guess too low // Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->left, GBA_ARM7TDMI_FREQUENCY, 96000); blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
blip_set_rates(audio->right, GBA_ARM7TDMI_FREQUENCY, 96000); blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
#endif
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
audio->forceDisableCh[0] = false;
audio->forceDisableCh[1] = false;
audio->forceDisableCh[2] = false;
audio->forceDisableCh[3] = false;
audio->forceDisableChA = false; audio->forceDisableChA = false;
audio->forceDisableChB = false; audio->forceDisableChB = false;
audio->masterVolume = GBA_AUDIO_VOLUME_MAX; audio->masterVolume = GBA_AUDIO_VOLUME_MAX;
} }
void GBAAudioReset(struct GBAAudio* audio) { void GBAAudioReset(struct GBAAudio* audio) {
GBAudioReset(&audio->psg);
audio->nextEvent = 0; audio->nextEvent = 0;
audio->nextCh1 = 0;
audio->nextCh2 = 0;
audio->nextCh3 = 0;
audio->nextCh4 = 0;
audio->ch1 = (struct GBAAudioChannel1) { .envelope = { .nextStep = INT_MAX }, .nextSweep = INT_MAX };
audio->ch2 = (struct GBAAudioChannel2) { .envelope = { .nextStep = INT_MAX } };
audio->ch3 = (struct GBAAudioChannel3) { .bank = { .bank = 0 } };
audio->ch4 = (struct GBAAudioChannel4) { .envelope = { .nextStep = INT_MAX } };
audio->chA.dmaSource = 1;
audio->chB.dmaSource = 2;
audio->chA.sample = 0; audio->chA.sample = 0;
audio->chB.sample = 0; audio->chB.sample = 0;
audio->eventDiff = 0; audio->eventDiff = 0;
audio->nextSample = 0; audio->nextSample = 0;
audio->sampleRate = 0x8000; audio->sampleRate = 0x8000;
audio->soundbias = 0x200; audio->soundbias = 0x200;
audio->volumeRight = 0;
audio->volumeLeft = 0;
audio->ch1Right = false;
audio->ch2Right = false;
audio->ch3Right = false;
audio->ch4Right = false;
audio->ch1Left = false;
audio->ch2Left = false;
audio->ch3Left = false;
audio->ch4Left = false;
audio->volume = 0; audio->volume = 0;
audio->volumeChA = false; audio->volumeChA = false;
audio->volumeChB = false; audio->volumeChB = false;
@ -93,33 +54,18 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->chBRight = false; audio->chBRight = false;
audio->chBLeft = false; audio->chBLeft = false;
audio->chBTimer = false; audio->chBTimer = false;
audio->playingCh1 = false;
audio->playingCh2 = false;
audio->playingCh3 = false;
audio->playingCh4 = false;
audio->enable = false; audio->enable = false;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF blip_clear(audio->psg.left);
CircleBufferClear(&audio->left); blip_clear(audio->psg.right);
CircleBufferClear(&audio->right);
#else
blip_clear(audio->left);
blip_clear(audio->right);
audio->clock = 0; audio->clock = 0;
#endif
CircleBufferClear(&audio->chA.fifo); CircleBufferClear(&audio->chA.fifo);
CircleBufferClear(&audio->chB.fifo); CircleBufferClear(&audio->chB.fifo);
} }
void GBAAudioDeinit(struct GBAAudio* audio) { void GBAAudioDeinit(struct GBAAudio* audio) {
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF GBAudioDeinit(&audio->psg);
CircleBufferDeinit(&audio->left);
CircleBufferDeinit(&audio->right);
#else
blip_delete(audio->left);
blip_delete(audio->right);
#endif
CircleBufferDeinit(&audio->chA.fifo); CircleBufferDeinit(&audio->chA.fifo);
CircleBufferDeinit(&audio->chB.fifo); CircleBufferDeinit(&audio->chB.fifo);
} }
@ -127,40 +73,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
mCoreSyncLockAudio(audio->p->sync); mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples; audio->samples = samples;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF blip_clear(audio->psg.left);
size_t oldCapacity = audio->left.capacity; blip_clear(audio->psg.right);
int16_t* buffer = malloc(oldCapacity);
int16_t dummy;
size_t read;
size_t i;
read = CircleBufferDump(&audio->left, buffer, oldCapacity);
CircleBufferDeinit(&audio->left);
CircleBufferInit(&audio->left, samples * sizeof(int16_t));
for (i = 0; i * sizeof(int16_t) < read; ++i) {
if (!CircleBufferWrite16(&audio->left, buffer[i])) {
CircleBufferRead16(&audio->left, &dummy);
CircleBufferWrite16(&audio->left, buffer[i]);
}
}
read = CircleBufferDump(&audio->right, buffer, oldCapacity);
CircleBufferDeinit(&audio->right);
CircleBufferInit(&audio->right, samples * sizeof(int16_t));
for (i = 0; i * sizeof(int16_t) < read; ++i) {
if (!CircleBufferWrite16(&audio->right, buffer[i])) {
CircleBufferRead16(&audio->right, &dummy);
CircleBufferWrite16(&audio->right, buffer[i]);
}
}
free(buffer);
#else
blip_clear(audio->left);
blip_clear(audio->right);
audio->clock = 0; audio->clock = 0;
#endif
mCoreSyncConsumeAudio(audio->p->sync); mCoreSyncConsumeAudio(audio->p->sync);
} }
@ -170,125 +85,16 @@ int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {
while (audio->nextEvent <= 0) { while (audio->nextEvent <= 0) {
audio->nextEvent = INT_MAX; audio->nextEvent = INT_MAX;
if (audio->enable) { if (audio->enable) {
if (audio->playingCh1 && !audio->ch1.envelope.dead) { audio->nextEvent = GBAudioProcessEvents(&audio->psg, audio->eventDiff / 4);
audio->nextCh1 -= audio->eventDiff; if (audio->nextEvent != INT_MAX) {
if (audio->ch1.envelope.nextStep != INT_MAX) { audio->nextEvent *= 4;
audio->ch1.envelope.nextStep -= audio->eventDiff;
if (audio->ch1.envelope.nextStep <= 0) {
int8_t sample = audio->ch1.control.hi * 0x10 - 0x8;
_updateEnvelope(&audio->ch1.envelope);
if (audio->ch1.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch1.envelope.nextStep;
}
audio->ch1.sample = sample * audio->ch1.envelope.currentVolume;
}
}
if (audio->ch1.nextSweep != INT_MAX) {
audio->ch1.nextSweep -= audio->eventDiff;
if (audio->ch1.nextSweep <= 0) {
audio->playingCh1 = _updateSweep(&audio->ch1);
if (audio->ch1.nextSweep < audio->nextEvent) {
audio->nextEvent = audio->ch1.nextSweep;
}
}
}
if (audio->nextCh1 <= 0) {
audio->nextCh1 += _updateChannel1(&audio->ch1);
if (audio->nextCh1 < audio->nextEvent) {
audio->nextEvent = audio->nextCh1;
}
}
if (audio->ch1.control.stop) {
audio->ch1.control.endTime -= audio->eventDiff;
if (audio->ch1.control.endTime <= 0) {
audio->playingCh1 = 0;
}
}
}
if (audio->playingCh2 && !audio->ch2.envelope.dead) {
audio->nextCh2 -= audio->eventDiff;
if (audio->ch2.envelope.nextStep != INT_MAX) {
audio->ch2.envelope.nextStep -= audio->eventDiff;
if (audio->ch2.envelope.nextStep <= 0) {
int8_t sample = audio->ch2.control.hi * 0x10 - 0x8;
_updateEnvelope(&audio->ch2.envelope);
if (audio->ch2.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch2.envelope.nextStep;
}
audio->ch2.sample = sample * audio->ch2.envelope.currentVolume;
}
}
if (audio->nextCh2 <= 0) {
audio->nextCh2 += _updateChannel2(&audio->ch2);
if (audio->nextCh2 < audio->nextEvent) {
audio->nextEvent = audio->nextCh2;
}
}
if (audio->ch2.control.stop) {
audio->ch2.control.endTime -= audio->eventDiff;
if (audio->ch2.control.endTime <= 0) {
audio->playingCh2 = 0;
}
}
}
if (audio->playingCh3) {
audio->nextCh3 -= audio->eventDiff;
if (audio->nextCh3 <= 0) {
audio->nextCh3 += _updateChannel3(&audio->ch3);
if (audio->nextCh3 < audio->nextEvent) {
audio->nextEvent = audio->nextCh3;
}
}
if (audio->ch3.control.stop) {
audio->ch3.control.endTime -= audio->eventDiff;
if (audio->ch3.control.endTime <= 0) {
audio->playingCh3 = 0;
}
}
}
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
audio->nextCh4 -= audio->eventDiff;
if (audio->ch4.envelope.nextStep != INT_MAX) {
audio->ch4.envelope.nextStep -= audio->eventDiff;
if (audio->ch4.envelope.nextStep <= 0) {
int8_t sample = (audio->ch4.sample >> 31) * 0x8;
_updateEnvelope(&audio->ch4.envelope);
if (audio->ch4.envelope.nextStep < audio->nextEvent) {
audio->nextEvent = audio->ch4.envelope.nextStep;
}
audio->ch4.sample = sample * audio->ch4.envelope.currentVolume;
}
}
if (audio->nextCh4 <= 0) {
audio->nextCh4 += _updateChannel4(&audio->ch4);
if (audio->nextCh4 < audio->nextEvent) {
audio->nextEvent = audio->nextCh4;
}
}
if (audio->ch4.control.stop) {
audio->ch4.control.endTime -= audio->eventDiff;
if (audio->ch4.control.endTime <= 0) {
audio->playingCh4 = 0;
}
}
} }
audio->p->memory.io[REG_SOUNDCNT_X >> 1] &= ~0x000F; audio->p->memory.io[REG_SOUNDCNT_X >> 1] &= ~0x000F;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh1; audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh1;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh2 << 1; audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh2 << 1;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh3 << 2; audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh3 << 2;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh4 << 3; audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh4 << 3;
} }
audio->nextSample -= audio->eventDiff; audio->nextSample -= audio->eventDiff;
@ -321,139 +127,58 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
} }
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
audio->ch1.sweep.shift = GBAAudioRegisterSquareSweepGetShift(value); GBAudioWriteNR10(&audio->psg, value);
audio->ch1.sweep.direction = GBAAudioRegisterSquareSweepGetDirection(value);
audio->ch1.sweep.time = GBAAudioRegisterSquareSweepGetTime(value);
if (audio->ch1.sweep.time) {
audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES;
} else {
audio->ch1.nextSweep = INT_MAX;
}
} }
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch1.envelope, value)) { GBAudioWriteNR11(&audio->psg, value);
audio->ch1.sample = 0; GBAudioWriteNR12(&audio->psg, value >> 8);
}
} }
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
audio->ch1.control.frequency = GBAAudioRegisterControlGetFrequency(value); GBAudioWriteNR13(&audio->psg, value);
audio->ch1.control.stop = GBAAudioRegisterControlGetStop(value); GBAudioWriteNR14(&audio->psg, value >> 8);
audio->ch1.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch1.envelope.length)) >> 8;
if (GBAAudioRegisterControlIsRestart(value)) {
if (audio->ch1.sweep.time) {
audio->ch1.nextSweep = audio->ch1.sweep.time * SWEEP_CYCLES;
} else {
audio->ch1.nextSweep = INT_MAX;
}
if (!audio->playingCh1) {
audio->nextCh1 = 0;
}
audio->playingCh1 = 1;
audio->ch1.envelope.currentVolume = audio->ch1.envelope.initialVolume;
if (audio->ch1.envelope.currentVolume > 0) {
audio->ch1.envelope.dead = 0;
}
if (audio->ch1.envelope.stepTime) {
audio->ch1.envelope.nextStep = 0;
} else {
audio->ch1.envelope.nextStep = INT_MAX;
}
}
} }
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch2.envelope, value)) { GBAudioWriteNR21(&audio->psg, value);
audio->ch2.sample = 0; GBAudioWriteNR22(&audio->psg, value >> 8);
}
} }
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch2.control.frequency = GBAAudioRegisterControlGetFrequency(value); GBAudioWriteNR23(&audio->psg, value);
audio->ch2.control.stop = GBAAudioRegisterControlGetStop(value); GBAudioWriteNR24(&audio->psg, value >> 8);
audio->ch2.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch2.envelope.length)) >> 8;
if (GBAAudioRegisterControlIsRestart(value)) {
audio->playingCh2 = 1;
audio->ch2.envelope.currentVolume = audio->ch2.envelope.initialVolume;
if (audio->ch2.envelope.currentVolume > 0) {
audio->ch2.envelope.dead = 0;
}
if (audio->ch2.envelope.stepTime) {
audio->ch2.envelope.nextStep = 0;
} else {
audio->ch2.envelope.nextStep = INT_MAX;
}
audio->nextCh2 = 0;
}
} }
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
audio->ch3.bank.size = GBAAudioRegisterBankGetSize(value); audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
audio->ch3.bank.bank = GBAAudioRegisterBankGetBank(value); audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
audio->ch3.bank.enable = GBAAudioRegisterBankGetEnable(value); GBAudioWriteNR30(&audio->psg, value);
if (audio->ch3.control.endTime >= 0) {
audio->playingCh3 = audio->ch3.bank.enable;
}
} }
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch3.wave.length = GBAAudioRegisterBankWaveGetLength(value); GBAudioWriteNR31(&audio->psg, value);
audio->ch3.wave.volume = GBAAudioRegisterBankWaveGetVolume(value); audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
} }
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
audio->ch3.control.rate = GBAAudioRegisterControlGetRate(value); GBAudioWriteNR33(&audio->psg, value);
audio->ch3.control.stop = GBAAudioRegisterControlGetStop(value); GBAudioWriteNR34(&audio->psg, value >> 8);
audio->ch3.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (256 - audio->ch3.wave.length)) >> 8;
if (GBAAudioRegisterControlIsRestart(value)) {
audio->playingCh3 = audio->ch3.bank.enable;
}
} }
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch4.envelope, value)) { GBAudioWriteNR41(&audio->psg, value);
audio->ch4.sample = 0; GBAudioWriteNR42(&audio->psg, value >> 8);
}
} }
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch4.control.ratio = GBAAudioRegisterCh4ControlGetRatio(value); GBAudioWriteNR43(&audio->psg, value);
audio->ch4.control.frequency = GBAAudioRegisterCh4ControlGetFrequency(value); GBAudioWriteNR44(&audio->psg, value >> 8);
audio->ch4.control.power = GBAAudioRegisterCh4ControlGetPower(value);
audio->ch4.control.stop = GBAAudioRegisterCh4ControlGetStop(value);
audio->ch4.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (64 - audio->ch4.envelope.length)) >> 8;
if (GBAAudioRegisterCh4ControlIsRestart(value)) {
audio->playingCh4 = 1;
audio->ch4.envelope.currentVolume = audio->ch4.envelope.initialVolume;
if (audio->ch4.envelope.currentVolume > 0) {
audio->ch4.envelope.dead = 0;
}
if (audio->ch4.envelope.stepTime) {
audio->ch4.envelope.nextStep = 0;
} else {
audio->ch4.envelope.nextStep = INT_MAX;
}
if (audio->ch4.control.power) {
audio->ch4.lfsr = 0x40;
} else {
audio->ch4.lfsr = 0x4000;
}
audio->nextCh4 = 0;
}
} }
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
audio->volumeRight = GBARegisterSOUNDCNT_LOGetVolumeRight(value); GBAudioWriteNR50(&audio->psg, value);
audio->volumeLeft = GBARegisterSOUNDCNT_LOGetVolumeLeft(value); GBAudioWriteNR51(&audio->psg, value >> 8);
audio->ch1Right = GBARegisterSOUNDCNT_LOGetCh1Right(value);
audio->ch2Right = GBARegisterSOUNDCNT_LOGetCh2Right(value);
audio->ch3Right = GBARegisterSOUNDCNT_LOGetCh3Right(value);
audio->ch4Right = GBARegisterSOUNDCNT_LOGetCh4Right(value);
audio->ch1Left = GBARegisterSOUNDCNT_LOGetCh1Left(value);
audio->ch2Left = GBARegisterSOUNDCNT_LOGetCh2Left(value);
audio->ch3Left = GBARegisterSOUNDCNT_LOGetCh3Left(value);
audio->ch4Left = GBARegisterSOUNDCNT_LOGetCh4Left(value);
} }
void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
@ -475,7 +200,8 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
} }
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
audio->enable = GBARegisterSOUNDCNT_XGetEnable(value); audio->enable = GBAudioEnableGetEnable(value);
audio->psg.enable = audio->enable;
} }
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
@ -483,7 +209,7 @@ void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
} }
void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
audio->ch3.wavedata[address | (!audio->ch3.bank.bank * 4)] = value; audio->psg.ch3.wavedata[address | (!audio->psg.ch3.bank * 4)] = value;
} }
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) { void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
@ -532,214 +258,6 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
CircleBufferRead8(&channel->fifo, (int8_t*) &channel->sample); CircleBufferRead8(&channel->fifo, (int8_t*) &channel->sample);
} }
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned nSamples) {
mCoreSyncLockAudio(audio->p->sync);
unsigned read = 0;
if (left) {
unsigned readL = CircleBufferRead(&audio->left, left, nSamples * sizeof(int16_t)) >> 1;
if (readL < nSamples) {
memset((int16_t*) left + readL, 0, nSamples - readL);
}
read = readL;
}
if (right) {
unsigned readR = CircleBufferRead(&audio->right, right, nSamples * sizeof(int16_t)) >> 1;
if (readR < nSamples) {
memset((int16_t*) right + readR, 0, nSamples - readR);
}
read = read >= readR ? read : readR;
}
mCoreSyncConsumeAudio(audio->p->sync);
return read;
}
unsigned GBAAudioResampleNN(struct GBAAudio* audio, float ratio, float* drift, struct GBAStereoSample* output, unsigned nSamples) {
int16_t left[GBA_AUDIO_SAMPLES];
int16_t right[GBA_AUDIO_SAMPLES];
// toRead is in GBA samples
// TODO: Do this with fixed-point math
unsigned toRead = ceilf(nSamples / ratio);
unsigned totalRead = 0;
while (nSamples) {
unsigned currentRead = GBA_AUDIO_SAMPLES;
if (currentRead > toRead) {
currentRead = toRead;
}
unsigned read = GBAAudioCopy(audio, left, right, currentRead);
toRead -= read;
unsigned i;
for (i = 0; i < read; ++i) {
*drift += ratio;
while (*drift >= 1.f) {
output->left = left[i];
output->right = right[i];
++output;
++totalRead;
--nSamples;
*drift -= 1.f;
if (!nSamples) {
return totalRead;
}
}
}
if (read < currentRead) {
memset(output, 0, nSamples * sizeof(struct GBAStereoSample));
break;
}
}
return totalRead;
}
#endif
bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value) {
envelope->length = GBAAudioRegisterEnvelopeGetLength(value);
envelope->duty = GBAAudioRegisterEnvelopeGetDuty(value);
envelope->stepTime = GBAAudioRegisterEnvelopeGetStepTime(value);
envelope->direction = GBAAudioRegisterEnvelopeGetDirection(value);
envelope->initialVolume = GBAAudioRegisterEnvelopeGetInitialVolume(value);
envelope->dead = 0;
if (envelope->stepTime) {
envelope->nextStep = 0;
} else {
envelope->nextStep = INT_MAX;
if (envelope->initialVolume == 0) {
envelope->dead = 1;
return false;
}
}
return true;
}
static int32_t _updateSquareChannel(struct GBAAudioSquareControl* control, int duty) {
control->hi = !control->hi;
int period = 16 * (2048 - control->frequency);
switch (duty) {
case 0:
return control->hi ? period : period * 7;
case 1:
return control->hi ? period * 2 : period * 6;
case 2:
return period * 4;
case 3:
return control->hi ? period * 6 : period * 2;
default:
// This should never be hit
return period * 4;
}
}
static void _updateEnvelope(struct GBAAudioEnvelope* envelope) {
if (envelope->direction) {
++envelope->currentVolume;
} else {
--envelope->currentVolume;
}
if (envelope->currentVolume >= 15) {
envelope->currentVolume = 15;
envelope->nextStep = INT_MAX;
} else if (envelope->currentVolume <= 0) {
envelope->currentVolume = 0;
envelope->dead = 1;
envelope->nextStep = INT_MAX;
} else {
envelope->nextStep += envelope->stepTime * (GBA_ARM7TDMI_FREQUENCY >> 6);
}
}
static bool _updateSweep(struct GBAAudioChannel1* ch) {
if (ch->sweep.direction) {
int frequency = ch->control.frequency;
frequency -= frequency >> ch->sweep.shift;
if (frequency >= 0) {
ch->control.frequency = frequency;
}
} else {
int frequency = ch->control.frequency;
frequency += frequency >> ch->sweep.shift;
if (frequency < 2048) {
ch->control.frequency = frequency;
} else {
return false;
}
}
ch->nextSweep += ch->sweep.time * SWEEP_CYCLES;
return true;
}
static int32_t _updateChannel1(struct GBAAudioChannel1* ch) {
int timing = _updateSquareChannel(&ch->control, ch->envelope.duty);
ch->sample = ch->control.hi * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
return timing;
}
static int32_t _updateChannel2(struct GBAAudioChannel2* ch) {
int timing = _updateSquareChannel(&ch->control, ch->envelope.duty);
ch->sample = ch->control.hi * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
return timing;
}
static int32_t _updateChannel3(struct GBAAudioChannel3* ch) {
int i;
int start;
int end;
int volume;
switch (ch->wave.volume) {
case 0:
volume = 0;
break;
case 1:
volume = 4;
break;
case 2:
volume = 2;
break;
case 3:
volume = 1;
break;
default:
volume = 3;
break;
}
if (ch->bank.size) {
start = 7;
end = 0;
} else if (ch->bank.bank) {
start = 7;
end = 4;
} else {
start = 3;
end = 0;
}
uint32_t bitsCarry = ch->wavedata[end] & 0x000000F0;
uint32_t bits;
for (i = start; i >= end; --i) {
bits = ch->wavedata[i] & 0x000000F0;
ch->wavedata[i] = ((ch->wavedata[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata[i] & 0xF0F0F000) >> 12);
ch->wavedata[i] |= bitsCarry << 20;
bitsCarry = bits;
}
ch->sample = bitsCarry >> 4;
ch->sample -= 8;
ch->sample *= volume * 4;
return 8 * (2048 - ch->control.rate);
}
static int32_t _updateChannel4(struct GBAAudioChannel4* ch) {
int lsb = ch->lfsr & 1;
ch->sample = lsb * 0x10 - 0x8;
ch->sample *= ch->envelope.currentVolume;
ch->lfsr >>= 1;
ch->lfsr ^= (lsb * 0x60) << (ch->control.power ? 0 : 8);
int timing = ch->control.ratio ? 2 * ch->control.ratio : 1;
timing <<= ch->control.frequency;
timing *= 32;
return timing;
}
static int _applyBias(struct GBAAudio* audio, int sample) { static int _applyBias(struct GBAAudio* audio, int sample) {
sample += GBARegisterSOUNDBIASGetBias(audio->soundbias); sample += GBARegisterSOUNDBIASGetBias(audio->soundbias);
if (sample >= 0x400) { if (sample >= 0x400) {
@ -754,49 +272,9 @@ static void _sample(struct GBAAudio* audio) {
int16_t sampleLeft = 0; int16_t sampleLeft = 0;
int16_t sampleRight = 0; int16_t sampleRight = 0;
int psgShift = 5 - audio->volume; int psgShift = 5 - audio->volume;
GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
if (audio->playingCh1 && !audio->forceDisableCh[0]) { sampleLeft >>= psgShift;
if (audio->ch1Left) { sampleRight >>= psgShift;
sampleLeft += audio->ch1.sample;
}
if (audio->ch1Right) {
sampleRight += audio->ch1.sample;
}
}
if (audio->playingCh2 && !audio->forceDisableCh[1]) {
if (audio->ch2Left) {
sampleLeft += audio->ch2.sample;
}
if (audio->ch2Right) {
sampleRight += audio->ch2.sample;
}
}
if (audio->playingCh3 && !audio->forceDisableCh[2]) {
if (audio->ch3Left) {
sampleLeft += audio->ch3.sample;
}
if (audio->ch3Right) {
sampleRight += audio->ch3.sample;
}
}
if (audio->playingCh4 && !audio->forceDisableCh[3]) {
if (audio->ch4Left) {
sampleLeft += audio->ch4.sample;
}
if (audio->ch4Right) {
sampleRight += audio->ch4.sample;
}
}
sampleLeft = (sampleLeft * (1 + audio->volumeLeft)) >> psgShift;
sampleRight = (sampleRight * (1 + audio->volumeRight)) >> psgShift;
if (!audio->forceDisableChA) { if (!audio->forceDisableChA) {
if (audio->chALeft) { if (audio->chALeft) {
@ -823,25 +301,19 @@ static void _sample(struct GBAAudio* audio) {
mCoreSyncLockAudio(audio->p->sync); mCoreSyncLockAudio(audio->p->sync);
unsigned produced; unsigned produced;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
CircleBufferWrite16(&audio->left, sampleLeft); blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft);
CircleBufferWrite16(&audio->right, sampleRight); blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight);
produced = CircleBufferSize(&audio->left) / 2;
#else
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->lastLeft = sampleLeft;
audio->lastRight = sampleRight; audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval; audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_FRAME) { if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->left, audio->clock); blip_end_frame(audio->psg.left, audio->clock);
blip_end_frame(audio->right, audio->clock); blip_end_frame(audio->psg.right, audio->clock);
audio->clock -= CLOCKS_PER_FRAME; audio->clock -= CLOCKS_PER_FRAME;
} }
} }
produced = blip_samples_avail(audio->left); produced = blip_samples_avail(audio->psg.left);
#endif
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, sampleLeft, sampleRight);
} }
@ -856,33 +328,33 @@ static void _sample(struct GBAAudio* audio) {
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
uint32_t flags = 0; uint32_t flags = 0;
flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->psg.ch1.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead); flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->psg.ch1.envelope.dead);
flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->ch1.control.hi); flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->psg.ch1.control.hi);
STORE_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); STORE_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
STORE_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); STORE_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
STORE_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); STORE_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
STORE_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime); STORE_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);
STORE_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent); STORE_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->psg.ch2.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead); flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->psg.ch2.envelope.dead);
flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi); flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->psg.ch2.control.hi);
STORE_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); STORE_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
STORE_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); STORE_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
STORE_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime); STORE_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);
STORE_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent); STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
memcpy(state->audio.ch3.wavebanks, audio->ch3.wavedata, sizeof(state->audio.ch3.wavebanks)); memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks));
STORE_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime); STORE_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime);
STORE_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent); STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume); state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume);
state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead); state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead);
STORE_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); STORE_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
STORE_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr); STORE_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
STORE_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime); STORE_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime);
STORE_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent); STORE_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
STORE_32(flags, 0, &state->audio.flags); STORE_32(flags, 0, &state->audio.flags);
@ -899,34 +371,34 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) { void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
uint32_t flags; uint32_t flags;
LOAD_32(flags, 0, &state->audio.flags); LOAD_32(flags, 0, &state->audio.flags);
audio->ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags); audio->psg.ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags);
audio->ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags); audio->psg.ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags);
audio->ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags); audio->psg.ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags);
LOAD_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); LOAD_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
LOAD_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); LOAD_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
LOAD_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); LOAD_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
LOAD_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime); LOAD_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);
LOAD_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent); LOAD_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
audio->ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags); audio->psg.ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags);
audio->ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags); audio->psg.ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags);
audio->ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags); audio->psg.ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags);
LOAD_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); LOAD_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
LOAD_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); LOAD_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
LOAD_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime); LOAD_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);
LOAD_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent); LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
// TODO: Big endian? // TODO: Big endian?
memcpy(audio->ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->ch3.wavedata)); memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata));
LOAD_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime); LOAD_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime);
LOAD_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent); LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
audio->ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags); audio->psg.ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags);
audio->ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags); audio->psg.ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags);
LOAD_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); LOAD_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
LOAD_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr); LOAD_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
LOAD_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime); LOAD_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime);
LOAD_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent); LOAD_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
CircleBufferClear(&audio->chA.fifo); CircleBufferClear(&audio->chA.fifo);
CircleBufferClear(&audio->chB.fifo); CircleBufferClear(&audio->chB.fifo);

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2016 Jeffrey Pfau
* *
* 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
@ -9,147 +9,20 @@
#include "util/common.h" #include "util/common.h"
#include "macros.h" #include "macros.h"
#include "gb/audio.h"
#include "util/circle-buffer.h" #include "util/circle-buffer.h"
#define RESAMPLE_NN 0
#define RESAMPLE_BLIP_BUF 2
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
#include "third-party/blip_buf/blip_buf.h"
#endif
struct GBADMA; struct GBADMA;
extern const unsigned GBA_AUDIO_SAMPLES; extern const unsigned GBA_AUDIO_SAMPLES;
extern const int GBA_AUDIO_VOLUME_MAX; extern const int GBA_AUDIO_VOLUME_MAX;
DECL_BITFIELD(GBAAudioRegisterEnvelope, uint16_t);
DECL_BITS(GBAAudioRegisterEnvelope, Length, 0, 6);
DECL_BITS(GBAAudioRegisterEnvelope, Duty, 6, 2);
DECL_BITS(GBAAudioRegisterEnvelope, StepTime, 8, 3);
DECL_BIT(GBAAudioRegisterEnvelope, Direction, 11);
DECL_BITS(GBAAudioRegisterEnvelope, InitialVolume, 12, 4);
DECL_BITFIELD(GBAAudioRegisterControl, uint16_t);
DECL_BITS(GBAAudioRegisterControl, Rate, 0, 11);
DECL_BITS(GBAAudioRegisterControl, Frequency, 0, 11);
DECL_BIT(GBAAudioRegisterControl, Stop, 14);
DECL_BIT(GBAAudioRegisterControl, Restart, 15);
DECL_BITFIELD(GBAAudioRegisterSquareSweep, uint16_t);
DECL_BITS(GBAAudioRegisterSquareSweep, Shift, 0, 3);
DECL_BIT(GBAAudioRegisterSquareSweep, Direction, 3);
DECL_BITS(GBAAudioRegisterSquareSweep, Time, 4, 3);
DECL_BITFIELD(GBAAudioRegisterBank, uint16_t);
DECL_BIT(GBAAudioRegisterBank, Size, 5);
DECL_BIT(GBAAudioRegisterBank, Bank, 6);
DECL_BIT(GBAAudioRegisterBank, Enable, 7);
DECL_BITFIELD(GBAAudioRegisterBankWave, uint16_t);
DECL_BITS(GBAAudioRegisterBankWave, Length, 0, 8);
DECL_BITS(GBAAudioRegisterBankWave, Volume, 13, 3);
DECL_BITFIELD(GBAAudioRegisterCh4Control, uint16_t);
DECL_BITS(GBAAudioRegisterCh4Control, Ratio, 0, 3);
DECL_BIT(GBAAudioRegisterCh4Control, Power, 3);
DECL_BITS(GBAAudioRegisterCh4Control, Frequency, 4, 4);
DECL_BIT(GBAAudioRegisterCh4Control, Stop, 14);
DECL_BIT(GBAAudioRegisterCh4Control, Restart, 15);
struct GBAAudioEnvelope {
uint8_t length;
uint8_t duty;
uint8_t stepTime;
uint8_t initialVolume;
bool direction;
int currentVolume;
int dead;
int32_t nextStep;
};
struct GBAAudioSquareControl {
uint16_t frequency;
bool stop;
int hi;
int32_t nextStep;
int32_t endTime;
};
struct GBAAudioChannel1 {
struct GBAAudioSquareSweep {
uint8_t shift;
uint8_t time;
bool direction;
} sweep;
int32_t nextSweep;
struct GBAAudioEnvelope envelope;
struct GBAAudioSquareControl control;
int8_t sample;
};
struct GBAAudioChannel2 {
struct GBAAudioEnvelope envelope;
struct GBAAudioSquareControl control;
int8_t sample;
};
struct GBAAudioChannel3 {
struct {
bool size;
bool bank;
bool enable;
} bank;
struct {
uint8_t length;
uint8_t volume;
} wave;
struct {
uint16_t rate;
bool stop;
int32_t endTime;
} control;
uint32_t wavedata[8];
int8_t sample;
};
struct GBAAudioChannel4 {
struct GBAAudioEnvelope envelope;
struct {
uint8_t ratio;
uint8_t frequency;
bool power;
bool stop;
int32_t endTime;
} control;
uint32_t lfsr;
int8_t sample;
};
struct GBAAudioFIFO { struct GBAAudioFIFO {
struct CircleBuffer fifo; struct CircleBuffer fifo;
int dmaSource; int dmaSource;
int8_t sample; int8_t sample;
}; };
DECL_BITFIELD(GBARegisterSOUNDCNT_LO, uint16_t);
DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeRight, 0, 3);
DECL_BITS(GBARegisterSOUNDCNT_LO, VolumeLeft, 4, 3);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Right, 8);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Right, 9);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Right, 10);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Right, 11);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch1Left, 12);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch2Left, 13);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch3Left, 14);
DECL_BIT(GBARegisterSOUNDCNT_LO, Ch4Left, 15);
DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t); DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t);
DECL_BITS(GBARegisterSOUNDCNT_HI, Volume, 0, 2); DECL_BITS(GBARegisterSOUNDCNT_HI, Volume, 0, 2);
DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChA, 2); DECL_BIT(GBARegisterSOUNDCNT_HI, VolumeChA, 2);
@ -163,13 +36,6 @@ DECL_BIT(GBARegisterSOUNDCNT_HI, ChBLeft, 13);
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBTimer, 14); DECL_BIT(GBARegisterSOUNDCNT_HI, ChBTimer, 14);
DECL_BIT(GBARegisterSOUNDCNT_HI, ChBReset, 15); DECL_BIT(GBARegisterSOUNDCNT_HI, ChBReset, 15);
DECL_BITFIELD(GBARegisterSOUNDCNT_X, uint16_t);
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh1, 0);
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh2, 1);
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh3, 2);
DECL_BIT(GBARegisterSOUNDCNT_X, PlayingCh4, 3);
DECL_BIT(GBARegisterSOUNDCNT_X, Enable, 7);
DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t); DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t);
DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10); DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10);
DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2); DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
@ -177,35 +43,13 @@ DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
struct GBAAudio { struct GBAAudio {
struct GBA* p; struct GBA* p;
struct GBAAudioChannel1 ch1; struct GBAudio psg;
struct GBAAudioChannel2 ch2;
struct GBAAudioChannel3 ch3;
struct GBAAudioChannel4 ch4;
struct GBAAudioFIFO chA; struct GBAAudioFIFO chA;
struct GBAAudioFIFO chB; struct GBAAudioFIFO chB;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
struct CircleBuffer left;
struct CircleBuffer right;
#else
blip_t* left;
blip_t* right;
int16_t lastLeft; int16_t lastLeft;
int16_t lastRight; int16_t lastRight;
int clock; int clock;
#endif
uint8_t volumeRight;
uint8_t volumeLeft;
bool ch1Right;
bool ch2Right;
bool ch3Right;
bool ch4Right;
bool ch1Left;
bool ch2Left;
bool ch3Left;
bool ch4Left;
uint8_t volume; uint8_t volume;
bool volumeChA; bool volumeChA;
@ -216,11 +60,6 @@ struct GBAAudio {
bool chBRight; bool chBRight;
bool chBLeft; bool chBLeft;
bool chBTimer; bool chBTimer;
bool playingCh1;
bool playingCh2;
bool playingCh3;
bool playingCh4;
bool enable; bool enable;
size_t samples; size_t samples;
@ -230,15 +69,10 @@ struct GBAAudio {
int32_t nextEvent; int32_t nextEvent;
int32_t eventDiff; int32_t eventDiff;
int32_t nextCh1;
int32_t nextCh2;
int32_t nextCh3;
int32_t nextCh4;
int32_t nextSample; int32_t nextSample;
int32_t sampleInterval; int32_t sampleInterval;
bool forceDisableCh[4];
bool forceDisableChA; bool forceDisableChA;
bool forceDisableChB; bool forceDisableChB;
int masterVolume; int masterVolume;
@ -277,12 +111,6 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles); void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned nSamples);
unsigned GBAAudioResampleNN(struct GBAAudio*, float ratio, float* drift, struct GBAStereoSample* output,
unsigned nSamples);
#endif
struct GBASerializedState; struct GBASerializedState;
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state);
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state);

View File

@ -85,7 +85,7 @@ static void _RegisterRamReset(struct GBA* gba) {
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0); cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0); cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0); cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0);
memset(gba->audio.ch3.wavedata, 0, sizeof(gba->audio.ch3.wavedata)); memset(gba->audio.psg.ch3.wavedata, 0, sizeof(gba->audio.psg.ch3.wavedata));
} }
if (registers & 0x80) { if (registers & 0x80) {
cpu->memory.store16(cpu, BASE_IO | 0x00, 0, 0); cpu->memory.store16(cpu, BASE_IO | 0x00, 0, 0);

View File

@ -774,7 +774,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case REG_SOUND4CNT_HI: case REG_SOUND4CNT_HI:
case REG_SOUNDCNT_LO: case REG_SOUNDCNT_LO:
case REG_SOUNDCNT_HI: case REG_SOUNDCNT_HI:
if (!GBARegisterSOUNDCNT_XIsEnable(gba->memory.io[REG_SOUNDCNT_X >> 1])) { if (!GBAudioEnableIsEnable(gba->memory.io[REG_SOUNDCNT_X >> 1])) {
// TODO: Is writing allowed when the circuit is disabled? // TODO: Is writing allowed when the circuit is disabled?
return 0; return 0;
} }

View File

@ -8,15 +8,10 @@
#include "util/common.h" #include "util/common.h"
#include "core/core.h"
#include "gba/memory.h" #include "gba/memory.h"
#include "macros.h" #include "macros.h"
#ifdef COLOR_16_BIT
#define BYTES_PER_PIXEL 2
#else
#define BYTES_PER_PIXEL 4
#endif
#define GBA_R5(X) ((X) & 0x1F) #define GBA_R5(X) ((X) & 0x1F)
#define GBA_G5(X) (((X) >> 5) & 0x1F) #define GBA_G5(X) (((X) >> 5) & 0x1F)
#define GBA_B5(X) (((X) >> 10) & 0x1F) #define GBA_B5(X) (((X) >> 10) & 0x1F)

View File

@ -287,11 +287,9 @@ static void _gameLoaded(struct GBAGUIRunner* runner) {
} }
osSetSpeedupEnable(true); osSetSpeedupEnable(true);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
double ratio = GBAAudioCalculateRatio(1, 59.8260982880808, 1); double ratio = GBAAudioCalculateRatio(1, 59.8260982880808, 1);
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio); blip_set_rates(runner->context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio); blip_set_rates(runner->context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
#endif
if (hasSound != NO_SOUND) { if (hasSound != NO_SOUND) {
audioPos = 0; audioPos = 0;
} }
@ -377,12 +375,10 @@ static void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1)); GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
if (hasSound == NO_SOUND) { if (hasSound == NO_SOUND) {
blip_clear(runner->context.gba->audio.left); blip_clear(runner->context.gba->audio.psg.left);
blip_clear(runner->context.gba->audio.right); blip_clear(runner->context.gba->audio.psg.right);
} }
#endif
gspWaitForPPF(); gspWaitForPPF();
ctrActivateTexture(tex); ctrActivateTexture(tex);
@ -514,12 +510,8 @@ static int32_t _readGyroZ(struct mRotationSource* source) {
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) { static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
UNUSED(stream); UNUSED(stream);
if (hasSound == CSND_SUPPORTED) { if (hasSound == CSND_SUPPORTED) {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false); blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false); blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false);
#elif RESAMPLE_LIBRARY == RESAMPLE_NN
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
#endif
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t)); GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t)); GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER; audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
@ -546,10 +538,8 @@ static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio)
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;
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_read_samples(audio->left, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES, true); blip_read_samples(audio->left, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES, true);
blip_read_samples(audio->right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true); blip_read_samples(audio->right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true);
#endif
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]);
} }

View File

@ -5,6 +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 "ffmpeg-encoder.h" #include "ffmpeg-encoder.h"
#include "core/core.h"
#include "gba/video.h" #include "gba/video.h"
#include <libavcodec/version.h> #include <libavcodec/version.h>

View File

@ -222,10 +222,8 @@ void retro_init(void) {
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES); GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF blip_set_rates(context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); blip_set_rates(context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
#endif
GBACheatDeviceCreate(&cheats); GBACheatDeviceCreate(&cheats);
GBACheatAttachDevice(context.gba, &cheats); GBACheatAttachDevice(context.gba, &cheats);
@ -467,18 +465,8 @@ void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* f
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) { static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
UNUSED(stream); UNUSED(stream);
int16_t samples[SAMPLES * 2]; int16_t samples[SAMPLES * 2];
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF blip_read_samples(audio->psg.left, samples, SAMPLES, true);
blip_read_samples(audio->left, samples, SAMPLES, true); blip_read_samples(audio->psg.right, samples + 1, SAMPLES, true);
blip_read_samples(audio->right, samples + 1, SAMPLES, true);
#else
int16_t samplesR[SAMPLES];
GBAAudioCopy(audio, &samples[SAMPLES], samplesR, SAMPLES);
size_t i;
for (i = 0; i < SAMPLES; ++i) {
samples[i * 2] = samples[SAMPLES + i];
samples[i * 2 + 1] = samplesR[i];
}
#endif
audioCallback(samples, SAMPLES); audioCallback(samples, SAMPLES);
} }

View File

@ -130,13 +130,9 @@
int16_t samples[SAMPLES * 2]; int16_t samples[SAMPLES * 2];
size_t available = 0; size_t available = 0;
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF available = blip_samples_avail(context.gba->audio.psg.left);
available = blip_samples_avail(context.gba->audio.left); blip_read_samples(context.gba->audio.psg.left, samples, available, true);
blip_read_samples(context.gba->audio.left, samples, available, true); blip_read_samples(context.gba->audio.psg.right, samples + 1, available, true);
blip_read_samples(context.gba->audio.right, samples + 1, available, true);
#else
#error BLIP_BUF is required for now
#endif
[[self ringBufferAtIndex:0] write:samples maxLength:available * 4]; [[self ringBufferAtIndex:0] write:samples maxLength:available * 4];
} }
@ -153,10 +149,8 @@
- (void)setupEmulation - (void)setupEmulation
{ {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF blip_set_rates(context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); blip_set_rates(context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
#endif
} }
#pragma mark - Video #pragma mark - Video

View File

@ -173,8 +173,8 @@ void GBAPSP2Setup(struct GBAGUIRunner* runner) {
void GBAPSP2LoadROM(struct GBAGUIRunner* runner) { void GBAPSP2LoadROM(struct GBAGUIRunner* runner) {
scePowerSetArmClockFrequency(444); scePowerSetArmClockFrequency(444);
double ratio = GBAAudioCalculateRatio(1, 60, 1); double ratio = GBAAudioCalculateRatio(1, 60, 1);
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); blip_set_rates(runner->context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); blip_set_rates(runner->context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
if (runner->context.gba->memory.hw.devices & (HW_TILT | HW_GYRO)) { if (runner->context.gba->memory.hw.devices & (HW_TILT | HW_GYRO)) {
sceMotionStartSampling(); sceMotionStartSampling();
@ -189,13 +189,13 @@ void GBAPSP2LoadROM(struct GBAGUIRunner* runner) {
void GBAPSP2PrepareForFrame(struct GBAGUIRunner* runner) { void GBAPSP2PrepareForFrame(struct GBAGUIRunner* runner) {
MutexLock(&audioContext.mutex); MutexLock(&audioContext.mutex);
while (blip_samples_avail(runner->context.gba->audio.left) >= PSP2_SAMPLES) { while (blip_samples_avail(runner->context.gba->audio.psg.left) >= PSP2_SAMPLES) {
if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) { if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) {
break; break;
} }
struct GBAStereoSample samples[PSP2_SAMPLES]; struct GBAStereoSample samples[PSP2_SAMPLES];
blip_read_samples(runner->context.gba->audio.left, &samples[0].left, PSP2_SAMPLES, true); blip_read_samples(runner->context.gba->audio.psg.left, &samples[0].left, PSP2_SAMPLES, true);
blip_read_samples(runner->context.gba->audio.right, &samples[0].right, PSP2_SAMPLES, true); blip_read_samples(runner->context.gba->audio.psg.right, &samples[0].right, PSP2_SAMPLES, true);
int i; int i;
for (i = 0; i < PSP2_SAMPLES; ++i) { for (i = 0; i < PSP2_SAMPLES; ++i) {
CircleBufferWrite16(&audioContext.buffer, samples[i].left); CircleBufferWrite16(&audioContext.buffer, samples[i].left);

View File

@ -29,17 +29,11 @@ void AudioDevice::setFormat(const QAudioFormat& format) {
LOG(INFO) << tr("Can't set format of context-less audio device"); LOG(INFO) << tr("Can't set format of context-less audio device");
return; return;
} }
#if RESAMPLE_LIBRARY == RESAMPLE_NN
GBAThreadInterrupt(m_context);
m_ratio = GBAAudioCalculateRatio(m_context->gba->audio.sampleRate, m_context->fpsTarget, format.sampleRate());
GBAThreadContinue(m_context);
#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
double fauxClock = GBAAudioCalculateRatio(1, m_context->fpsTarget, 1); double fauxClock = GBAAudioCalculateRatio(1, m_context->fpsTarget, 1);
mCoreSyncLockAudio(&m_context->sync); mCoreSyncLockAudio(&m_context->sync);
blip_set_rates(m_context->gba->audio.left, GBA_ARM7TDMI_FREQUENCY, format.sampleRate() * fauxClock); blip_set_rates(m_context->gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, format.sampleRate() * fauxClock);
blip_set_rates(m_context->gba->audio.right, GBA_ARM7TDMI_FREQUENCY, format.sampleRate() * fauxClock); blip_set_rates(m_context->gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, format.sampleRate() * fauxClock);
mCoreSyncUnlockAudio(&m_context->sync); mCoreSyncUnlockAudio(&m_context->sync);
#endif
} }
void AudioDevice::setInput(GBAThread* input) { void AudioDevice::setInput(GBAThread* input) {
@ -56,19 +50,15 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) {
return 0; return 0;
} }
#if RESAMPLE_LIBRARY == RESAMPLE_NN
return GBAAudioResampleNN(&m_context->gba->audio, m_ratio, &m_drift, reinterpret_cast<GBAStereoSample*>(data), maxSize / sizeof(GBAStereoSample)) * sizeof(GBAStereoSample);
#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
mCoreSyncLockAudio(&m_context->sync); mCoreSyncLockAudio(&m_context->sync);
int available = blip_samples_avail(m_context->gba->audio.left); int available = blip_samples_avail(m_context->gba->audio.psg.left);
if (available > maxSize / sizeof(GBAStereoSample)) { if (available > maxSize / sizeof(GBAStereoSample)) {
available = maxSize / sizeof(GBAStereoSample); available = maxSize / sizeof(GBAStereoSample);
} }
blip_read_samples(m_context->gba->audio.left, &reinterpret_cast<GBAStereoSample*>(data)->left, available, true); blip_read_samples(m_context->gba->audio.psg.left, &reinterpret_cast<GBAStereoSample*>(data)->left, available, true);
blip_read_samples(m_context->gba->audio.right, &reinterpret_cast<GBAStereoSample*>(data)->right, available, true); blip_read_samples(m_context->gba->audio.psg.right, &reinterpret_cast<GBAStereoSample*>(data)->right, available, true);
mCoreSyncConsumeAudio(&m_context->sync); mCoreSyncConsumeAudio(&m_context->sync);
return available * sizeof(GBAStereoSample); return available * sizeof(GBAStereoSample);
#endif
} }
qint64 AudioDevice::writeData(const char*, qint64) { qint64 AudioDevice::writeData(const char*, qint64) {

View File

@ -20,7 +20,7 @@ AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
} }
AudioProcessorSDL::~AudioProcessorSDL() { AudioProcessorSDL::~AudioProcessorSDL() {
GBASDLDeinitAudio(&m_audio); GBSDLDeinitAudio(&m_audio);
} }
bool AudioProcessorSDL::start() { bool AudioProcessorSDL::start() {
@ -30,26 +30,26 @@ bool AudioProcessorSDL::start() {
} }
if (m_audio.thread) { if (m_audio.thread) {
GBASDLResumeAudio(&m_audio); GBSDLResumeAudio(&m_audio);
return true; return true;
} else { } else {
if (!m_audio.samples) { if (!m_audio.samples) {
m_audio.samples = input()->audioBuffers; m_audio.samples = input()->audioBuffers;
} }
return GBASDLInitAudio(&m_audio, input()); return GBSDLInitAudio(&m_audio, input());
} }
} }
void AudioProcessorSDL::pause() { void AudioProcessorSDL::pause() {
GBASDLPauseAudio(&m_audio); GBSDLPauseAudio(&m_audio);
} }
void AudioProcessorSDL::setBufferSamples(int samples) { void AudioProcessorSDL::setBufferSamples(int samples) {
AudioProcessor::setBufferSamples(samples); AudioProcessor::setBufferSamples(samples);
m_audio.samples = samples; m_audio.samples = samples;
if (m_audio.thread) { if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio); GBSDLDeinitAudio(&m_audio);
GBASDLInitAudio(&m_audio, input()); GBSDLInitAudio(&m_audio, input());
} }
} }
@ -59,8 +59,8 @@ void AudioProcessorSDL::inputParametersChanged() {
void AudioProcessorSDL::requestSampleRate(unsigned rate) { void AudioProcessorSDL::requestSampleRate(unsigned rate) {
m_audio.sampleRate = rate; m_audio.sampleRate = rate;
if (m_audio.thread) { if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio); GBSDLDeinitAudio(&m_audio);
GBASDLInitAudio(&m_audio, input()); GBSDLInitAudio(&m_audio, input());
} }
} }

View File

@ -34,7 +34,7 @@ public slots:
virtual void requestSampleRate(unsigned) override; virtual void requestSampleRate(unsigned) override;
private: private:
GBASDLAudio m_audio; GBSDLAudio m_audio;
}; };
} }

View File

@ -102,10 +102,10 @@ GameController::GameController(QObject* parent)
context->gba->rtcSource = &controller->m_rtc.d; context->gba->rtcSource = &controller->m_rtc.d;
context->gba->rumble = controller->m_inputController->rumble(); context->gba->rumble = controller->m_inputController->rumble();
context->gba->rotationSource = controller->m_inputController->rotationSource(); context->gba->rotationSource = controller->m_inputController->rotationSource();
context->gba->audio.forceDisableCh[0] = !controller->m_audioChannels[0]; context->gba->audio.psg.forceDisableCh[0] = !controller->m_audioChannels[0];
context->gba->audio.forceDisableCh[1] = !controller->m_audioChannels[1]; context->gba->audio.psg.forceDisableCh[1] = !controller->m_audioChannels[1];
context->gba->audio.forceDisableCh[2] = !controller->m_audioChannels[2]; context->gba->audio.psg.forceDisableCh[2] = !controller->m_audioChannels[2];
context->gba->audio.forceDisableCh[3] = !controller->m_audioChannels[3]; context->gba->audio.psg.forceDisableCh[3] = !controller->m_audioChannels[3];
context->gba->audio.forceDisableChA = !controller->m_audioChannels[4]; context->gba->audio.forceDisableChA = !controller->m_audioChannels[4];
context->gba->audio.forceDisableChB = !controller->m_audioChannels[5]; context->gba->audio.forceDisableChB = !controller->m_audioChannels[5];
context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0]; context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0];
@ -638,7 +638,7 @@ void GameController::setAudioChannelEnabled(int channel, bool enable) {
case 1: case 1:
case 2: case 2:
case 3: case 3:
m_threadContext.gba->audio.forceDisableCh[channel] = !enable; m_threadContext.gba->audio.psg.forceDisableCh[channel] = !enable;
break; break;
case 4: case 4:
m_threadContext.gba->audio.forceDisableChA = !enable; m_threadContext.gba->audio.forceDisableChA = !enable;
@ -973,17 +973,7 @@ void GameController::updateKeys() {
} }
void GameController::redoSamples(int samples) { void GameController::redoSamples(int samples) {
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
float sampleRate = 0x8000;
float ratio;
if (m_threadContext.gba) {
sampleRate = m_threadContext.gba->audio.sampleRate;
}
ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, m_audioProcess->sampleRate());
m_threadContext.audioBuffers = ceil(samples / ratio);
#else
m_threadContext.audioBuffers = samples; m_threadContext.audioBuffers = samples;
#endif
if (m_threadContext.gba) { if (m_threadContext.gba) {
GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers); GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
} }

View File

@ -65,6 +65,7 @@ void mSDLGLRunloopGBA(struct mSDLRenderer* renderer, void* user) {
struct GBAThread* context = user; struct GBAThread* context = user;
SDL_Event event; SDL_Event event;
struct VideoBackend* v = &renderer->gl.d; struct VideoBackend* v = &renderer->gl.d;
renderer->audio.psg = &context->gba->audio.psg;
while (context->state < THREAD_EXITING) { while (context->state < THREAD_EXITING) {
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
@ -86,6 +87,8 @@ void mSDLGLRunloopGBA(struct mSDLRenderer* renderer, void* user) {
v->drawFrame(v); v->drawFrame(v);
v->swap(v); v->swap(v);
} }
renderer->audio.psg = 0;
} }
void mSDLGLDeinitGBA(struct mSDLRenderer* renderer) { void mSDLGLDeinitGBA(struct mSDLRenderer* renderer) {
@ -130,6 +133,7 @@ void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
SDL_Event event; SDL_Event event;
struct VideoBackend* v = &renderer->gl.d; struct VideoBackend* v = &renderer->gl.d;
int activeKeys = 0; int activeKeys = 0;
renderer->audio.psg = &((struct GB*) renderer->core->board)->audio;
while (true) { while (true) {
renderer->core->runFrame(renderer->core); renderer->core->runFrame(renderer->core);
@ -169,6 +173,7 @@ void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
v->drawFrame(v); v->drawFrame(v);
v->swap(v); v->swap(v);
} }
renderer->audio.psg = 0;
} }
void mSDLGLDeinitGB(struct mSDLRenderer* renderer) { void mSDLGLDeinitGB(struct mSDLRenderer* renderer) {

View File

@ -239,7 +239,7 @@ int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct
if (opts->sampleRate) { if (opts->sampleRate) {
renderer->audio.sampleRate = opts->sampleRate; renderer->audio.sampleRate = opts->sampleRate;
} }
if (!GBASDLInitAudio(&renderer->audio, &context)) { if (!GBSDLInitAudio(&renderer->audio, &context)) {
didFail = true; didFail = true;
} }
@ -249,7 +249,9 @@ int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct
GBASDLSuspendScreensaver(&renderer->events); GBASDLSuspendScreensaver(&renderer->events);
#endif #endif
if (GBAThreadStart(&context)) { if (GBAThreadStart(&context)) {
renderer->audio.psg = &context.gba->audio.psg;
renderer->runloop(renderer, &context); renderer->runloop(renderer, &context);
renderer->audio.psg = 0;
GBAThreadJoin(&context); GBAThreadJoin(&context);
} else { } else {
didFail = true; didFail = true;
@ -278,6 +280,11 @@ int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
struct VFile* vf = VFileOpen(args->fname, O_RDONLY); struct VFile* vf = VFileOpen(args->fname, O_RDONLY);
struct VFile* savVf = 0; struct VFile* savVf = 0;
renderer->audio.samples = 1024;
renderer->audio.sampleRate = 44100;
GBSDLInitAudio(&renderer->audio, 0);
{ {
char savepath[PATH_MAX]; char savepath[PATH_MAX];
char dirname[PATH_MAX]; char dirname[PATH_MAX];
@ -289,6 +296,8 @@ int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
renderer->core->loadROM(renderer->core, vf, savVf, args->fname); renderer->core->loadROM(renderer->core, vf, savVf, args->fname);
renderer->core->reset(renderer->core); renderer->core->reset(renderer->core);
renderer->audio.psg = 0;
GBSDLResumeAudio(&renderer->audio);
renderer->runloop(renderer, NULL); renderer->runloop(renderer, NULL);
renderer->core->unloadROM(renderer->core); renderer->core->unloadROM(renderer->core);
vf->close(vf); vf->close(vf);
@ -307,7 +316,7 @@ static bool mSDLInit(struct mSDLRenderer* renderer) {
static void mSDLDeinit(struct mSDLRenderer* renderer) { static void mSDLDeinit(struct mSDLRenderer* renderer) {
GBASDLDeinitEvents(&renderer->events); GBASDLDeinitEvents(&renderer->events);
GBASDLDeinitAudio(&renderer->audio); GBSDLDeinitAudio(&renderer->audio);
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_DestroyWindow(renderer->window); SDL_DestroyWindow(renderer->window);
#endif #endif

View File

@ -48,7 +48,7 @@ struct mSDLRenderer {
// TODO: Remove // TODO: Remove
struct GBAVideoSoftwareRenderer d; struct GBAVideoSoftwareRenderer d;
#endif #endif
struct GBASDLAudio audio; struct GBSDLAudio audio;
struct GBASDLEvents events; struct GBASDLEvents events;
struct GBASDLPlayer player; struct GBASDLPlayer player;

View File

@ -8,15 +8,13 @@
#include "gba/gba.h" #include "gba/gba.h"
#include "gba/supervisor/thread.h" #include "gba/supervisor/thread.h"
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
#include "third-party/blip_buf/blip_buf.h" #include "third-party/blip_buf/blip_buf.h"
#endif
#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) #define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
static void _GBASDLAudioCallback(void* context, Uint8* data, int len); static void _GBSDLAudioCallback(void* context, Uint8* data, int len);
bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext) { bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread* threadContext) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError()); GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());
return false; return false;
@ -26,11 +24,8 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
context->desiredSpec.format = AUDIO_S16SYS; context->desiredSpec.format = AUDIO_S16SYS;
context->desiredSpec.channels = 2; context->desiredSpec.channels = 2;
context->desiredSpec.samples = context->samples; context->desiredSpec.samples = context->samples;
context->desiredSpec.callback = _GBASDLAudioCallback; context->desiredSpec.callback = _GBSDLAudioCallback;
context->desiredSpec.userdata = context; context->desiredSpec.userdata = context;
#if RESAMPLE_LIBRARY == RESAMPLE_NN
context->drift = 0.f;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
context->deviceId = SDL_OpenAudioDevice(0, 0, &context->desiredSpec, &context->obtainedSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); context->deviceId = SDL_OpenAudioDevice(0, 0, &context->desiredSpec, &context->obtainedSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
@ -42,7 +37,7 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
return false; return false;
} }
context->samples = context->obtainedSpec.samples; context->samples = context->obtainedSpec.samples;
context->gba = 0; context->psg = 0;
context->thread = 0; context->thread = 0;
if (threadContext) { if (threadContext) {
@ -63,10 +58,9 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
return true; return true;
} }
void GBASDLDeinitAudio(struct GBASDLAudio* context) { void GBSDLDeinitAudio(struct GBSDLAudio* context) {
UNUSED(context); UNUSED(context);
context->thread = 0; context->psg = 0;
context->gba = 0;
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_PauseAudioDevice(context->deviceId, 1); SDL_PauseAudioDevice(context->deviceId, 1);
SDL_CloseAudioDevice(context->deviceId); SDL_CloseAudioDevice(context->deviceId);
@ -77,7 +71,7 @@ void GBASDLDeinitAudio(struct GBASDLAudio* context) {
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
} }
void GBASDLPauseAudio(struct GBASDLAudio* context) { void GBSDLPauseAudio(struct GBSDLAudio* context) {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_PauseAudioDevice(context->deviceId, 1); SDL_PauseAudioDevice(context->deviceId, 1);
#else #else
@ -86,7 +80,7 @@ void GBASDLPauseAudio(struct GBASDLAudio* context) {
#endif #endif
} }
void GBASDLResumeAudio(struct GBASDLAudio* context) { void GBSDLResumeAudio(struct GBSDLAudio* context) {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_PauseAudioDevice(context->deviceId, 0); SDL_PauseAudioDevice(context->deviceId, 0);
#else #else
@ -95,45 +89,31 @@ void GBASDLResumeAudio(struct GBASDLAudio* context) {
#endif #endif
} }
static void _GBASDLAudioCallback(void* context, Uint8* data, int len) { static void _GBSDLAudioCallback(void* context, Uint8* data, int len) {
struct GBASDLAudio* audioContext = context; struct GBSDLAudio* audioContext = context;
if (!context || ((!audioContext->thread || !audioContext->thread->gba) && !audioContext->gba)) { if (!context || (!audioContext->psg && !audioContext->thread)) {
memset(data, 0, len); memset(data, 0, len);
return; return;
} }
struct GBA* gba; struct GBAudio* psg = audioContext->psg;
if (audioContext->thread) { if (!psg) {
gba = audioContext->thread->gba; psg = &audioContext->thread->gba->audio.psg;
} else {
gba = audioContext->gba;
} }
#if RESAMPLE_LIBRARY == RESAMPLE_NN
audioContext->ratio = GBAAudioCalculateRatio(gba->audio.sampleRate, audioContext->fpsTarget, audioContext->obtainedSpec.freq);
if (audioContext->ratio == INFINITY) {
memset(data, 0, len);
return;
}
struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data;
len /= 2 * audioContext->obtainedSpec.channels;
if (audioContext->obtainedSpec.channels == 2) {
GBAAudioResampleNN(&gba->audio, audioContext->ratio, &audioContext->drift, ssamples, len);
}
#elif RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
double fauxClock = 1; double fauxClock = 1;
if (audioContext->thread) { if (audioContext->thread) {
fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1); fauxClock = GBAAudioCalculateRatio(1, audioContext->thread->fpsTarget, 1);
mCoreSyncLockAudio(&audioContext->thread->sync); mCoreSyncLockAudio(&audioContext->thread->sync);
} }
blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock); blip_set_rates(psg->left, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, audioContext->obtainedSpec.freq * fauxClock); blip_set_rates(psg->right, psg->clockRate, audioContext->obtainedSpec.freq * fauxClock);
len /= 2 * audioContext->obtainedSpec.channels; len /= 2 * audioContext->obtainedSpec.channels;
int available = blip_samples_avail(gba->audio.left); int available = blip_samples_avail(psg->left);
if (available > len) { if (available > len) {
available = len; available = len;
} }
blip_read_samples(gba->audio.left, (short*) data, available, audioContext->obtainedSpec.channels == 2); blip_read_samples(psg->left, (short*) data, available, audioContext->obtainedSpec.channels == 2);
if (audioContext->obtainedSpec.channels == 2) { if (audioContext->obtainedSpec.channels == 2) {
blip_read_samples(gba->audio.right, ((short*) data) + 1, available, 1); blip_read_samples(psg->right, ((short*) data) + 1, available, 1);
} }
if (audioContext->thread) { if (audioContext->thread) {
@ -142,5 +122,4 @@ static void _GBASDLAudioCallback(void* context, Uint8* data, int len) {
if (available < len) { if (available < len) {
memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short)); memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));
} }
#endif
} }

View File

@ -10,9 +10,7 @@
#include <SDL.h> #include <SDL.h>
#include "gba/audio.h" struct GBSDLAudio {
struct GBASDLAudio {
// Input // Input
size_t samples; size_t samples;
unsigned sampleRate; unsigned sampleRate;
@ -20,23 +18,18 @@ struct GBASDLAudio {
// State // State
SDL_AudioSpec desiredSpec; SDL_AudioSpec desiredSpec;
SDL_AudioSpec obtainedSpec; SDL_AudioSpec obtainedSpec;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
float ratio;
#endif
#if RESAMPLE_LIBRARY == RESAMPLE_NN
float drift;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_AudioDeviceID deviceId; SDL_AudioDeviceID deviceId;
#endif #endif
struct GBAudio* psg;
struct GBAThread* thread; struct GBAThread* thread;
struct GBA* gba; struct mCoreSync* sync;
}; };
bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContext); bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread*);
void GBASDLDeinitAudio(struct GBASDLAudio* context); void GBSDLDeinitAudio(struct GBSDLAudio* context);
void GBASDLPauseAudio(struct GBASDLAudio* context); void GBSDLPauseAudio(struct GBSDLAudio* context);
void GBASDLResumeAudio(struct GBASDLAudio* context); void GBSDLResumeAudio(struct GBSDLAudio* context);
#endif #endif

View File

@ -126,8 +126,8 @@ int main(int argc, char** argv) {
savestate = 0; savestate = 0;
} }
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000); blip_set_rates(context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 0x8000);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000); blip_set_rates(context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 0x8000);
_GBAFuzzRunloop(&context, fuzzOpts.frames); _GBAFuzzRunloop(&context, fuzzOpts.frames);

View File

@ -566,11 +566,9 @@ void _setup(struct GBAGUIRunner* runner) {
GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES); GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1); double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); blip_set_rates(runner->context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); blip_set_rates(runner->context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
#endif
} }
void _gameUnloaded(struct GBAGUIRunner* runner) { void _gameUnloaded(struct GBAGUIRunner* runner) {
@ -617,23 +615,21 @@ void _unpaused(struct GBAGUIRunner* runner) {
} }
void _drawFrame(struct GBAGUIRunner* runner, bool faded) { void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF int available = blip_samples_avail(runner->context.gba->audio.psg.left);
int available = blip_samples_avail(runner->context.gba->audio.left);
if (available + audioBufferSize > SAMPLES) { if (available + audioBufferSize > SAMPLES) {
available = SAMPLES - audioBufferSize; available = SAMPLES - audioBufferSize;
} }
available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
if (available > 0) { if (available > 0) {
// These appear to be reversed for AUDIO_InitDMA // These appear to be reversed for AUDIO_InitDMA
blip_read_samples(runner->context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true); blip_read_samples(runner->context.gba->audio.psg.left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
blip_read_samples(runner->context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true); blip_read_samples(runner->context.gba->audio.psg.right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
audioBufferSize += available; audioBufferSize += available;
} }
if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) { if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
_audioDMA(); _audioDMA();
AUDIO_StartDMA(); AUDIO_StartDMA();
} }
#endif
uint32_t color = 0xFFFFFF3F; uint32_t color = 0xFFFFFF3F;
if (!faded) { if (!faded) {