mirror of https://github.com/mgba-emu/mgba.git
GB: First pass at audio refactor
This commit is contained in:
parent
4edd7286f3
commit
92c6b90b03
|
@ -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}")
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
81
src/gb/io.c
81
src/gb/io.c
|
@ -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:
|
||||||
|
|
730
src/gba/audio.c
730
src/gba/audio.c
|
@ -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);
|
||||||
|
|
178
src/gba/audio.h
178
src/gba/audio.h
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public slots:
|
||||||
virtual void requestSampleRate(unsigned) override;
|
virtual void requestSampleRate(unsigned) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GBASDLAudio m_audio;
|
GBSDLAudio m_audio;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue