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(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance 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(BUILD_QT ON CACHE BOOL "Build Qt frontend")
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
@ -362,12 +361,7 @@ if(USE_FFMPEG)
endif()
endif()
if(USE_BLIP)
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()
list(APPEND THIRD_PARTY_SRC "${CMAKE_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c")
if(USE_MAGICK)
list(APPEND FEATURES MAGICK)
@ -515,6 +509,9 @@ if(M_CORE_GBA)
${GBA_CTX_SRC}
${DEBUGGER_SRC}
${GBA_RENDERER_SRC})
if(NOT M_CORE_GB)
list(APPEND CORE_SRC ${CMAKE_SOURCE_DIR}/src/gb/audio.c)
endif()
endif()
list(APPEND CORE_SRC
@ -702,7 +699,6 @@ message(STATUS " GIF recording: ${USE_MAGICK}")
message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}")
message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " Better audio resampling: ${USE_BLIP}")
message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:")
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;
GBVideoInit(&gb->video);
gb->audio.p = gb;
GBAudioInit(&gb->audio, 2048); // TODO: Remove magic constant
gb->timer.p = gb;
gb->romVf = 0;
@ -156,6 +159,7 @@ void GBReset(struct LR35902Core* cpu) {
GBVideoReset(&gb->video);
GBTimerReset(&gb->timer);
GBIOReset(gb);
GBAudioReset(&gb->audio);
}
void GBUpdateIRQs(struct GB* gb) {
@ -212,6 +216,11 @@ void GBProcessEvents(struct LR35902Core* cpu) {
nextEvent = testEvent;
}
testEvent = GBAudioProcessEvents(&gb->audio, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
testEvent = GBTimerProcessEvents(&gb->timer, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;

View File

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

View File

@ -55,6 +55,87 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
case REG_DIV:
GBTimerDivReset(&gb->timer);
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_TIMA:
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
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -12,78 +12,39 @@
#include "gba/video.h"
const unsigned GBA_AUDIO_SAMPLES = 2048;
const unsigned BLIP_BUFFER_SIZE = 0x4000;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
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;
#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 void _sample(struct GBAAudio* audio);
void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
audio->psg.p = NULL;
GBAudioInit(&audio->psg, 0);
audio->samples = samples;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
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);
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
// 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->right, GBA_ARM7TDMI_FREQUENCY, 96000);
#endif
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
CircleBufferInit(&audio->chA.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->forceDisableChB = false;
audio->masterVolume = GBA_AUDIO_VOLUME_MAX;
}
void GBAAudioReset(struct GBAAudio* audio) {
GBAudioReset(&audio->psg);
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->chB.sample = 0;
audio->eventDiff = 0;
audio->nextSample = 0;
audio->sampleRate = 0x8000;
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->volumeChA = false;
audio->volumeChB = false;
@ -93,33 +54,18 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->chBRight = false;
audio->chBLeft = false;
audio->chBTimer = false;
audio->playingCh1 = false;
audio->playingCh2 = false;
audio->playingCh3 = false;
audio->playingCh4 = false;
audio->enable = false;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
CircleBufferClear(&audio->left);
CircleBufferClear(&audio->right);
#else
blip_clear(audio->left);
blip_clear(audio->right);
blip_clear(audio->psg.left);
blip_clear(audio->psg.right);
audio->clock = 0;
#endif
CircleBufferClear(&audio->chA.fifo);
CircleBufferClear(&audio->chB.fifo);
}
void GBAAudioDeinit(struct GBAAudio* audio) {
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
CircleBufferDeinit(&audio->left);
CircleBufferDeinit(&audio->right);
#else
blip_delete(audio->left);
blip_delete(audio->right);
#endif
GBAudioDeinit(&audio->psg);
CircleBufferDeinit(&audio->chA.fifo);
CircleBufferDeinit(&audio->chB.fifo);
}
@ -127,40 +73,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
size_t oldCapacity = audio->left.capacity;
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);
blip_clear(audio->psg.left);
blip_clear(audio->psg.right);
audio->clock = 0;
#endif
mCoreSyncConsumeAudio(audio->p->sync);
}
@ -170,125 +85,16 @@ int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t 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.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->nextEvent = GBAudioProcessEvents(&audio->psg, audio->eventDiff / 4);
if (audio->nextEvent != INT_MAX) {
audio->nextEvent *= 4;
}
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->playingCh2 << 1;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh3 << 2;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->playingCh4 << 3;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh1;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh2 << 1;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh3 << 2;
audio->p->memory.io[REG_SOUNDCNT_X >> 1] |= audio->psg.playingCh4 << 3;
}
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) {
audio->ch1.sweep.shift = GBAAudioRegisterSquareSweepGetShift(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;
}
GBAudioWriteNR10(&audio->psg, value);
}
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch1.envelope, value)) {
audio->ch1.sample = 0;
}
GBAudioWriteNR11(&audio->psg, value);
GBAudioWriteNR12(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
audio->ch1.control.frequency = GBAAudioRegisterControlGetFrequency(value);
audio->ch1.control.stop = GBAAudioRegisterControlGetStop(value);
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;
}
}
GBAudioWriteNR13(&audio->psg, value);
GBAudioWriteNR14(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch2.envelope, value)) {
audio->ch2.sample = 0;
}
GBAudioWriteNR21(&audio->psg, value);
GBAudioWriteNR22(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch2.control.frequency = GBAAudioRegisterControlGetFrequency(value);
audio->ch2.control.stop = GBAAudioRegisterControlGetStop(value);
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;
}
GBAudioWriteNR23(&audio->psg, value);
GBAudioWriteNR24(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
audio->ch3.bank.size = GBAAudioRegisterBankGetSize(value);
audio->ch3.bank.bank = GBAAudioRegisterBankGetBank(value);
audio->ch3.bank.enable = GBAAudioRegisterBankGetEnable(value);
if (audio->ch3.control.endTime >= 0) {
audio->playingCh3 = audio->ch3.bank.enable;
}
audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
GBAudioWriteNR30(&audio->psg, value);
}
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch3.wave.length = GBAAudioRegisterBankWaveGetLength(value);
audio->ch3.wave.volume = GBAAudioRegisterBankWaveGetVolume(value);
GBAudioWriteNR31(&audio->psg, value);
audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
}
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
audio->ch3.control.rate = GBAAudioRegisterControlGetRate(value);
audio->ch3.control.stop = GBAAudioRegisterControlGetStop(value);
audio->ch3.control.endTime = (GBA_ARM7TDMI_FREQUENCY * (256 - audio->ch3.wave.length)) >> 8;
if (GBAAudioRegisterControlIsRestart(value)) {
audio->playingCh3 = audio->ch3.bank.enable;
}
GBAudioWriteNR33(&audio->psg, value);
GBAudioWriteNR34(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
if (!_writeEnvelope(&audio->ch4.envelope, value)) {
audio->ch4.sample = 0;
}
GBAudioWriteNR41(&audio->psg, value);
GBAudioWriteNR42(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->ch4.control.ratio = GBAAudioRegisterCh4ControlGetRatio(value);
audio->ch4.control.frequency = GBAAudioRegisterCh4ControlGetFrequency(value);
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;
}
GBAudioWriteNR43(&audio->psg, value);
GBAudioWriteNR44(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
audio->volumeRight = GBARegisterSOUNDCNT_LOGetVolumeRight(value);
audio->volumeLeft = GBARegisterSOUNDCNT_LOGetVolumeLeft(value);
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);
GBAudioWriteNR50(&audio->psg, value);
GBAudioWriteNR51(&audio->psg, value >> 8);
}
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) {
audio->enable = GBARegisterSOUNDCNT_XGetEnable(value);
audio->enable = GBAudioEnableGetEnable(value);
audio->psg.enable = audio->enable;
}
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) {
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) {
@ -532,214 +258,6 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
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) {
sample += GBARegisterSOUNDBIASGetBias(audio->soundbias);
if (sample >= 0x400) {
@ -754,49 +272,9 @@ static void _sample(struct GBAAudio* audio) {
int16_t sampleLeft = 0;
int16_t sampleRight = 0;
int psgShift = 5 - audio->volume;
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;
}
}
sampleLeft = (sampleLeft * (1 + audio->volumeLeft)) >> psgShift;
sampleRight = (sampleRight * (1 + audio->volumeRight)) >> psgShift;
GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
sampleLeft >>= psgShift;
sampleRight >>= psgShift;
if (!audio->forceDisableChA) {
if (audio->chALeft) {
@ -823,25 +301,19 @@ static void _sample(struct GBAAudio* audio) {
mCoreSyncLockAudio(audio->p->sync);
unsigned produced;
#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
CircleBufferWrite16(&audio->left, sampleLeft);
CircleBufferWrite16(&audio->right, sampleRight);
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);
if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft);
blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight);
audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->left, audio->clock);
blip_end_frame(audio->right, audio->clock);
blip_end_frame(audio->psg.left, audio->clock);
blip_end_frame(audio->psg.right, audio->clock);
audio->clock -= CLOCKS_PER_FRAME;
}
}
produced = blip_samples_avail(audio->left);
#endif
produced = blip_samples_avail(audio->psg.left);
if (audio->p->stream && audio->p->stream->postAudioFrame) {
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) {
uint32_t flags = 0;
flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead);
flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->ch1.control.hi);
STORE_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
STORE_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
STORE_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
STORE_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime);
STORE_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent);
flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->psg.ch1.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->psg.ch1.envelope.dead);
flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->psg.ch1.control.hi);
STORE_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
STORE_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
STORE_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
STORE_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);
STORE_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi);
STORE_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
STORE_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
STORE_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime);
STORE_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent);
flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->psg.ch2.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->psg.ch2.envelope.dead);
flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->psg.ch2.control.hi);
STORE_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
STORE_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
STORE_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);
STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
memcpy(state->audio.ch3.wavebanks, audio->ch3.wavedata, sizeof(state->audio.ch3.wavebanks));
STORE_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime);
STORE_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent);
memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks));
STORE_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime);
STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume);
state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead);
STORE_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
STORE_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr);
STORE_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime);
STORE_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent);
state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume);
state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead);
STORE_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
STORE_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
STORE_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime);
STORE_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
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) {
uint32_t flags;
LOAD_32(flags, 0, &state->audio.flags);
audio->ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags);
audio->ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags);
audio->ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags);
LOAD_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
LOAD_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
LOAD_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
LOAD_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime);
LOAD_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent);
audio->psg.ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags);
audio->psg.ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags);
audio->psg.ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags);
LOAD_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep);
LOAD_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep);
LOAD_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep);
LOAD_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);
LOAD_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
audio->ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags);
audio->ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags);
audio->ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags);
LOAD_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
LOAD_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
LOAD_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime);
LOAD_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent);
audio->psg.ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags);
audio->psg.ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags);
audio->psg.ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags);
LOAD_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep);
LOAD_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep);
LOAD_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);
LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
// TODO: Big endian?
memcpy(audio->ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->ch3.wavedata));
LOAD_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime);
LOAD_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent);
memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata));
LOAD_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime);
LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
audio->ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags);
audio->ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags);
LOAD_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
LOAD_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr);
LOAD_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime);
LOAD_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent);
audio->psg.ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags);
audio->psg.ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags);
LOAD_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
LOAD_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
LOAD_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime);
LOAD_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
CircleBufferClear(&audio->chA.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
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -9,147 +9,20 @@
#include "util/common.h"
#include "macros.h"
#include "gb/audio.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;
extern const unsigned GBA_AUDIO_SAMPLES;
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 CircleBuffer fifo;
int dmaSource;
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_BITS(GBARegisterSOUNDCNT_HI, Volume, 0, 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, 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_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10);
DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
@ -177,35 +43,13 @@ DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
struct GBAAudio {
struct GBA* p;
struct GBAAudioChannel1 ch1;
struct GBAAudioChannel2 ch2;
struct GBAAudioChannel3 ch3;
struct GBAAudioChannel4 ch4;
struct GBAudio psg;
struct GBAAudioFIFO chA;
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 lastRight;
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;
bool volumeChA;
@ -216,11 +60,6 @@ struct GBAAudio {
bool chBRight;
bool chBLeft;
bool chBTimer;
bool playingCh1;
bool playingCh2;
bool playingCh3;
bool playingCh4;
bool enable;
size_t samples;
@ -230,15 +69,10 @@ struct GBAAudio {
int32_t nextEvent;
int32_t eventDiff;
int32_t nextCh1;
int32_t nextCh2;
int32_t nextCh3;
int32_t nextCh4;
int32_t nextSample;
int32_t sampleInterval;
bool forceDisableCh[4];
bool forceDisableChA;
bool forceDisableChB;
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 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;
void GBAAudioSerialize(const struct GBAAudio* audio, 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_X, 0, 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) {
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_SOUNDCNT_LO:
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?
return 0;
}

View File

@ -8,15 +8,10 @@
#include "util/common.h"
#include "core/core.h"
#include "gba/memory.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_G5(X) (((X) >> 5) & 0x1F)
#define GBA_B5(X) (((X) >> 10) & 0x1F)

View File

@ -287,11 +287,9 @@ static void _gameLoaded(struct GBAGUIRunner* runner) {
}
osSetSpeedupEnable(true);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
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.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
#endif
blip_set_rates(runner->context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
blip_set_rates(runner->context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
if (hasSound != NO_SOUND) {
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_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
if (hasSound == NO_SOUND) {
blip_clear(runner->context.gba->audio.left);
blip_clear(runner->context.gba->audio.right);
blip_clear(runner->context.gba->audio.psg.left);
blip_clear(runner->context.gba->audio.psg.right);
}
#endif
gspWaitForPPF();
ctrActivateTexture(tex);
@ -514,12 +510,8 @@ static int32_t _readGyroZ(struct mRotationSource* source) {
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
UNUSED(stream);
if (hasSound == CSND_SUPPORTED) {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_read_samples(audio->left, &audioLeft[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(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
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]));
dspBuffer[bufferId].data_pcm16 = tmpBuf;
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->right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true);
#endif
DSP_FlushDataCache(dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES * 2 * sizeof(int16_t));
ndspChnWaveBufAdd(0, &dspBuffer[bufferId]);
}

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ffmpeg-encoder.h"
#include "core/core.h"
#include "gba/video.h"
#include <libavcodec/version.h>

View File

@ -222,10 +222,8 @@ void retro_init(void) {
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
#endif
blip_set_rates(context.gba->audio.psg.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.psg.right, GBA_ARM7TDMI_FREQUENCY, 32768);
GBACheatDeviceCreate(&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) {
UNUSED(stream);
int16_t samples[SAMPLES * 2];
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_read_samples(audio->left, samples, 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
blip_read_samples(audio->psg.left, samples, SAMPLES, true);
blip_read_samples(audio->psg.right, samples + 1, SAMPLES, true);
audioCallback(samples, SAMPLES);
}

View File

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

View File

@ -173,8 +173,8 @@ void GBAPSP2Setup(struct GBAGUIRunner* runner) {
void GBAPSP2LoadROM(struct GBAGUIRunner* runner) {
scePowerSetArmClockFrequency(444);
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.right, 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.psg.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
if (runner->context.gba->memory.hw.devices & (HW_TILT | HW_GYRO)) {
sceMotionStartSampling();
@ -189,13 +189,13 @@ void GBAPSP2LoadROM(struct GBAGUIRunner* runner) {
void GBAPSP2PrepareForFrame(struct GBAGUIRunner* runner) {
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)) {
break;
}
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.right, &samples[0].right, 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.psg.right, &samples[0].right, PSP2_SAMPLES, true);
int i;
for (i = 0; i < PSP2_SAMPLES; ++i) {
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");
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);
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.right, 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.psg.right, GBA_ARM7TDMI_FREQUENCY, format.sampleRate() * fauxClock);
mCoreSyncUnlockAudio(&m_context->sync);
#endif
}
void AudioDevice::setInput(GBAThread* input) {
@ -56,19 +50,15 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) {
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);
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)) {
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.right, &reinterpret_cast<GBAStereoSample*>(data)->right, 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.psg.right, &reinterpret_cast<GBAStereoSample*>(data)->right, available, true);
mCoreSyncConsumeAudio(&m_context->sync);
return available * sizeof(GBAStereoSample);
#endif
}
qint64 AudioDevice::writeData(const char*, qint64) {

View File

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

View File

@ -34,7 +34,7 @@ public slots:
virtual void requestSampleRate(unsigned) override;
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->rumble = controller->m_inputController->rumble();
context->gba->rotationSource = controller->m_inputController->rotationSource();
context->gba->audio.forceDisableCh[0] = !controller->m_audioChannels[0];
context->gba->audio.forceDisableCh[1] = !controller->m_audioChannels[1];
context->gba->audio.forceDisableCh[2] = !controller->m_audioChannels[2];
context->gba->audio.forceDisableCh[3] = !controller->m_audioChannels[3];
context->gba->audio.psg.forceDisableCh[0] = !controller->m_audioChannels[0];
context->gba->audio.psg.forceDisableCh[1] = !controller->m_audioChannels[1];
context->gba->audio.psg.forceDisableCh[2] = !controller->m_audioChannels[2];
context->gba->audio.psg.forceDisableCh[3] = !controller->m_audioChannels[3];
context->gba->audio.forceDisableChA = !controller->m_audioChannels[4];
context->gba->audio.forceDisableChB = !controller->m_audioChannels[5];
context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0];
@ -638,7 +638,7 @@ void GameController::setAudioChannelEnabled(int channel, bool enable) {
case 1:
case 2:
case 3:
m_threadContext.gba->audio.forceDisableCh[channel] = !enable;
m_threadContext.gba->audio.psg.forceDisableCh[channel] = !enable;
break;
case 4:
m_threadContext.gba->audio.forceDisableChA = !enable;
@ -973,17 +973,7 @@ void GameController::updateKeys() {
}
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;
#endif
if (m_threadContext.gba) {
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;
SDL_Event event;
struct VideoBackend* v = &renderer->gl.d;
renderer->audio.psg = &context->gba->audio.psg;
while (context->state < THREAD_EXITING) {
while (SDL_PollEvent(&event)) {
@ -86,6 +87,8 @@ void mSDLGLRunloopGBA(struct mSDLRenderer* renderer, void* user) {
v->drawFrame(v);
v->swap(v);
}
renderer->audio.psg = 0;
}
void mSDLGLDeinitGBA(struct mSDLRenderer* renderer) {
@ -130,6 +133,7 @@ void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
SDL_Event event;
struct VideoBackend* v = &renderer->gl.d;
int activeKeys = 0;
renderer->audio.psg = &((struct GB*) renderer->core->board)->audio;
while (true) {
renderer->core->runFrame(renderer->core);
@ -169,6 +173,7 @@ void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
v->drawFrame(v);
v->swap(v);
}
renderer->audio.psg = 0;
}
void mSDLGLDeinitGB(struct mSDLRenderer* renderer) {

View File

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

View File

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

View File

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

View File

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

View File

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