diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index 0ea13125ad..b06bdbe84e 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -1,3 +1,5 @@ #pragma once -void DuskAudioInitialize(); +namespace dusk::audio { + void Initialize(); +} diff --git a/libs/JSystem/src/JAudio2/JAUInitializer.cpp b/libs/JSystem/src/JAudio2/JAUInitializer.cpp index 7d56282b45..0cc3fcbb62 100644 --- a/libs/JSystem/src/JAudio2/JAUInitializer.cpp +++ b/libs/JSystem/src/JAudio2/JAUInitializer.cpp @@ -63,7 +63,7 @@ void JAU_JASInitializer::initJASystem(JKRSolidHeap* heap) { JASDvd::createThread(dvdThreadPriority_, 0x80, 0x1000); #if TARGET_PC - DuskAudioInitialize(); + dusk::audio::Initialize(); #else JASAudioThread::create(audioThreadPriority_); #endif diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 566d2ab400..191df90686 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "JSystem/JAudio2/JASAiCtrl.h" #include "JSystem/JAudio2/JASChannel.h" @@ -10,6 +12,9 @@ #include "JSystem/JAudio2/JASHeapCtrl.h" #include "DuskDsp.hpp" +#include "JSystem/JAudio2/JASAudioThread.h" + +using namespace dusk::audio; static DspSubframe AllSubframeBuffers[DSP_OUTPUT_CHANNELS]; @@ -39,7 +44,7 @@ static void InitSDL3Output() { nullptr); } -void DuskAudioInitialize() { +void dusk::audio::Initialize() { InitSDL3Output(); JASDsp::initBuffer(); @@ -53,22 +58,30 @@ void DuskAudioInitialize() { void SDLCALL GetNewAudio( void*, SDL_AudioStream*, - int, - int total_amount) { - while (total_amount > 0) { + int needed, + int) { + while (needed > 0) { const int rendered = RenderNewAudioFrame(); - total_amount -= rendered; + needed -= rendered; } } +static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); + int RenderNewAudioFrame() { JASCriticalSection section; const u32 countSubframes = JASDriver::getSubFrames(); + JASAudioThread::setDSPSyncCount(countSubframes); + for (u32 i = 0; i < countSubframes; i++) { RenderAudioSubframe(); + + JASAudioThread::snIntCount -= 1; } + outRaw.flush(); + return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } @@ -76,7 +89,9 @@ void RenderAudioSubframe() { DspSubframe& subFrame = AllSubframeBuffers[0]; JASDriver::updateDSP(); - DuskDspRender(subFrame); + DspRender(subFrame); + + outRaw.write((const char*)subFrame.data(), subFrame.size() * sizeof(s16)); SDL_PutAudioStreamData(PlaybackStream, &subFrame, sizeof(subFrame)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 63cc5707e3..cb2c01d1cb 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -1,16 +1,150 @@ +#include +#include + #include "DuskDsp.hpp" -#include +#include +#include +#include -static float SinePos; +#include "Adpcm.hpp" +#include "global.h" -void DuskDspRender(DspSubframe& subframe) { - subframe.fill(0); +using namespace dusk::audio; - for (auto& elem : subframe) { - elem = static_cast(sinf(SinePos) * std::numeric_limits::max() * 0.2); - SinePos += 0.05f; +ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; + +static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) { + if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm4FrameSize) + return true; + /* + if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm2FrameSize) + return true; + if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 8) + return true; + if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 16) + return true; + */ + return false; +} + +static void ValidateChannel(const JASDsp::TChannel& channel) { + if (!ValidateChannelWaveFormat(channel)) { + CRASH( + "Unable to handle channel format: %02x, %02x\n", + channel.mSamplesPerBlock, + channel.mBytesPerBlock); + } +} + +static u32 ConvertDataLengthToSamples(const JASDsp::TChannel& channel, u32 dataLen) { + if (dataLen % channel.mBytesPerBlock != 0) { + CRASH("Indivisible data length: %d\n", dataLen); } - auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); + return (dataLen / channel.mBytesPerBlock) * channel.mSamplesPerBlock; +} + +static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) { + if (samples % channel.mSamplesPerBlock != 0) { + CRASH("Indivisible sample count: %d\n", samples); + } + + return (samples / channel.mSamplesPerBlock) * channel.mBytesPerBlock; +} + +static void RenderChannel( + JASDsp::TChannel& channel, + ChannelAuxData& channelAux, + DspSubframe& subframe); + +static void ResetChannel(JASDsp::TChannel& channel) { + channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; + + channel.mResetFlag = false; +} + +static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { + for (int i = 0; i < dst.size(); i++) { + dst[i] = static_cast(dst[i] + src[i]); + } +} + +void dusk::audio::DspRender(DspSubframe& subframe) { + subframe.fill(0); + + // This cast half exists because my debugger sucks and this is an easy way to look at the data. + auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); + + for (int i = 0; i < channels.size(); i++) { + auto& channel = channels[i]; + auto& channelAux = ChannelAux[i]; + + if (!channel.mIsActive) { + continue; + } + + ValidateChannel(channel); + + DspSubframe channelSubframe = {}; + RenderChannel(channel, channelAux, channelSubframe); + MixSubframe(subframe, channelSubframe); + } +} + +static void RenderChannel( + JASDsp::TChannel& channel, + ChannelAuxData& channelAux, + DspSubframe& subframe) { + if (channel.mResetFlag) { + ResetChannel(channel); + } + + auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; + + // Streaming logic directly modifies mSamplesLeft. + // So we use that as our tracking of where we are. + auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + + u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(DSP_SUBFRAME_SIZE)); + + Adpcm4ToPcm16( + aramBase + dataPosition, + ConvertSamplesToDataLength(channel, renderSamples), + subframe.data(), + renderSamples, + channelAux.hist1, + channelAux.hist0); + + channel.mSamplesLeft -= renderSamples; + channel.mSamplePosition += renderSamples; + + if (channel.mSamplesLeft == 0) { + // Reached end of buffer. + if (!channel.mLoopFlag) { + // Finish. + channel.mIsFinished = true; + return; + } + + channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; + channel.mSamplePosition = channel.mLoopStartSample; + curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + + Adpcm4ToPcm16( + aramBase + dataPosition, + ConvertSamplesToDataLength(channel, DSP_SUBFRAME_SIZE - renderSamples), + subframe.data() + renderSamples, + subframe.size() - renderSamples, + channelAux.hist1, + channelAux.hist0); + + channel.mSamplesLeft -= (DSP_SUBFRAME_SIZE - renderSamples); + channel.mSamplePosition += (DSP_SUBFRAME_SIZE - renderSamples); + } + + channel.mAramStreamPosition = channel.mWaveAramAddress + + ConvertSamplesToDataLength(channel, channel.mSamplePosition); } diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 418c285686..a8013d6669 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -4,6 +4,15 @@ #include -using DspSubframe = std::array; +namespace dusk::audio { + struct ChannelAuxData { + s16 hist1; + s16 hist0; + }; -void DuskDspRender(DspSubframe& subframe); + extern ChannelAuxData ChannelAux[DSP_CHANNELS]; + + using DspSubframe = std::array; + + void DspRender(DspSubframe& subframe); +}