Merge pull request #135 from TakaRikka/pjb-audio

Audio
This commit is contained in:
TakaRikka
2026-03-27 20:36:06 -07:00
committed by GitHub
111 changed files with 2651 additions and 966 deletions
+4
View File
@@ -552,7 +552,11 @@ JAIAudible* Z2Audience::newAudible(const JGeometry::TVec3<f32>& pos, JAISoundID
}
void Z2Audience::deleteAudible(JAIAudible* audible) {
#if TARGET_PC
JKR_DELETE(static_cast<Z2Audible*>(audible));
#else
JKR_DELETE(audible);
#endif
}
Z2Audible::~Z2Audible() {}
+5 -18
View File
@@ -31,15 +31,15 @@ Z2AudioMgr::Z2AudioMgr() : mSoundStarter(true) {
void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive* seqArc) {
JAU_JASInitializer JASInitializer;
JASInitializer.audioMemSize_ = memSize;
JASInitializer.field_0x1c = 140;
JASInitializer.mJasTrackPoolSize = 140;
JASInitializer.dspLevel_ = 1.3f;
JASInitializer.waveArcDir_ = "Audiores/Waves/";
JASInitializer.initJASystem(heap);
JAU_JAIInitializer JAIInitializer;
JAIInitializer.field_0x0 = 78;
JAIInitializer.field_0x4 = 4;
JAIInitializer.field_0xc = 48;
JAIInitializer.mJaiSePoolSize = 78;
JAIInitializer.mJaiSeqPoolSize = 4;
JAIInitializer.mJaiSoundChildPoolSize = 48;
JAIInitializer.initJAInterface();
JAISeMgr* seMgr = mSoundMgr.getSeMgr();
@@ -107,19 +107,6 @@ void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive
JASPoolAllocObject<Z2SoundHandlePool>::newMemPool(0x4e);
OS_REPORT("[Z2AudioMgr::init]before Create Section: %d\n", heap->getFreeSize());
#if TARGET_PC
// Fix a race condition with OS threading where JAUNewSectionHeap will use all the remaining
// space in the JASDram heap before JASAudioThread has finished initializing.
OSLockMutex(&JASAudioThread::sThreadInitCompleteMutex);
while (!JASAudioThread::sThreadInitComplete) {
OSWaitCond(
&JASAudioThread::sThreadInitCompleteCond,
&JASAudioThread::sThreadInitCompleteMutex);
}
OSUnlockMutex(&JASAudioThread::sThreadInitCompleteMutex);
#endif
JAUSectionHeap* sectionHeap = JAUNewSectionHeap(true);
sectionHeap->setSeqDataArchive(seqArc);
size_t resMaxSize = JASResArcLoader::getResMaxSize(seqArc);
@@ -158,7 +145,7 @@ void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive
}
void Z2AudioMgr::setOutputMode(u32 mode) {
if (mode <= 2) {
if (mode <= JAS_OUTPUT_SURROUND) {
JAISetOutputMode(mode);
}
}
-5
View File
@@ -211,11 +211,6 @@ Z2SoundHandlePool* Z2Creature::startCreatureSoundLevel(JAISoundID soundID, u32 m
}
Z2SoundHandlePool* Z2Creature::startCreatureVoice(JAISoundID soundID, s8 reverb) {
#if TARGET_PC
// stubbed
return nullptr;
#endif
switch (soundID) {
case Z2SE_MDN_V_FLY_OUT:
case Z2SE_MDN_V_MGN_TAME:
+2 -11
View File
@@ -2018,10 +2018,6 @@ bool Z2SceneMgr::loadSceneWave(u32 wave, u32 bank) {
#endif
bool Z2SceneMgr::loadSeWave(u32 wave) {
return 0;
}
/* //lw stub lw
JAUSectionHeap* sectionHeap = JASGlobalInstance<JAUSectionHeap>::getInstance();
JUT_ASSERT(3030, sectionHeap);
@@ -2036,13 +2032,8 @@ bool Z2SceneMgr::loadSeWave(u32 wave) {
JUT_WARN_DEVICE(3038, 1, "Z2SceneMgr::cannot load SE wave:%d\n", wave);
return false;
}
*/
bool Z2SceneMgr::loadBgmWave(u32 wave) {
return true;
}
/* //lw stub lw
JAUSectionHeap* sectionHeap = JASGlobalInstance<JAUSectionHeap>::getInstance();
JUT_ASSERT(3047, sectionHeap);
@@ -2056,4 +2047,4 @@ bool Z2SceneMgr::loadBgmWave(u32 wave) {
JUT_WARN_DEVICE(3055, 1, "Z2SceneMgr::cannot load BGM wave:%d\n", wave);
return false;
} */
}
+1 -1
View File
@@ -40,7 +40,7 @@ Z2SoundHandlePool* Z2SoundHandles::getHandleSoundID(JAISoundID soundID) {
return NULL;
}
Z2SoundHandlePool* Z2SoundHandles::getHandleUserData(u32 userData) {
Z2SoundHandlePool* Z2SoundHandles::getHandleUserData(uintptr_t userData) {
JSULink<Z2SoundHandlePool>* i;
for (i = getFirst(); i != NULL; i = i->getNext()) {
Z2SoundHandlePool* handle = i->getObject();
+1 -1
View File
@@ -502,7 +502,7 @@ void Z2SoundObjAnime::startSoundInner(const JGeometry::TVec3<f32>& pos, f32 para
JUT_ASSERT(747, curSoundIndex_ < animation_->getNumSounds());
const JAUSoundAnimationSound* animationSound = animation_->getSound(curSoundIndex_);
u32 user_data = (uintptr_t)animationSound;
uintptr_t user_data = (uintptr_t)animationSound;
if (reverse_) {
curSoundIndex_--;
} else {
+1 -1
View File
@@ -1467,7 +1467,7 @@ static f32 l_ladderAnmBaseTransY = 102.00054168701172f;
static dCcD_SrcCyl l_cylSrc = {
{
{0, {{AT_TYPE_WOLF_ATTACK, 3, 0x1A}, {0xD8FFFDFF, 5}, 0x73}},
{0, {{(u32)AT_TYPE_WOLF_ATTACK, 3, 0x1A}, {0xD8FFFDFF, 5}, 0x73}},
{dCcD_SE_WOLF_BITE, 3, 1, 0, {1}},
{dCcD_SE_NONE, 6, 0, 0, {0}},
{0},
+1 -1
View File
@@ -3268,7 +3268,7 @@ static void daMP_MixAudio(s16* destination, s16*, u32 sample) {
if (r_mix > 32767)
r_mix = 32767;
if (JASDriver::getOutputMode() == 0) {
if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) {
l_mix = r_mix = ((r_mix >> 1) + (l_mix >> 1));
r_mix = (s16)r_mix;
l_mix = (s16)l_mix;
+8 -6
View File
@@ -24,6 +24,8 @@
#include "m_Do/m_Do_graphic.h"
#include <cstring>
#include "JSystem/JAudio2/JASDriverIF.h"
typedef void (dMenu_Option_c::*initFunc)();
static initFunc init[] = {
&dMenu_Option_c::atten_init,
@@ -89,7 +91,7 @@ dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) {
dMenu_Option_c::~dMenu_Option_c() {}
static const u32 dMo_soundMode[3] = {0, 1, 2};
static const u32 dMo_soundMode[3] = {JAS_OUTPUT_MONO, JAS_OUTPUT_STEREO, JAS_OUTPUT_SURROUND};
void dMenu_Option_c::_create() {
static const u64 text_a_tag[5] = {MULTI_CHAR('atext1_1'), MULTI_CHAR('atext1_2'), MULTI_CHAR('atext1_3'), MULTI_CHAR('atext1_4'), MULTI_CHAR('atext1_5')};
@@ -1755,13 +1757,13 @@ void dMenu_Option_c::screenSet() {
}
}
void dMenu_Option_c::setSoundMode(u32 param_0) {
switch (param_0) {
case 0:
void dMenu_Option_c::setSoundMode(u32 soundMode) {
switch (soundMode) {
case JAS_OUTPUT_MONO:
OSSetSoundMode(OS_SOUND_MODE_MONO);
break;
case 1:
case 2:
case JAS_OUTPUT_STEREO:
case JAS_OUTPUT_SURROUND: // Via dolby pro logic 2, so it's over 2 output channels.
OSSetSoundMode(OS_SOUND_MODE_STEREO);
break;
}
+5
View File
@@ -1634,7 +1634,12 @@ void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount)
#elif REGION_JPN
sprintf(arcName, "/res/Msgjp/bmgres%d.arc", msgGroup);
#else
#if TARGET_PC
// Original game UB
snprintf(arcName, sizeof(arcName), "/res/Msgus/bmgres%d.arc", msgGroup);
#else
sprintf(arcName, "/res/Msgus/bmgres%d.arc", msgGroup);
#endif
#endif
*p_arcMount = mDoDvdThd_mountXArchive_c::create(arcName, 0, JKRArchive::MOUNT_MEM, NULL);
+9
View File
@@ -21,6 +21,8 @@
#include "m_Do/m_Do_Reset.h"
#include <cstdio>
#include <cstring>
#include "dusk/string.hpp"
#if TARGET_PC
#include <format>
#include <fmt/ranges.h>
@@ -151,7 +153,14 @@ static int dStage_RoomKeepDoorInit(dStage_dt_c* i_stage, void* i_data, int entry
}
void dStage_startStage_c::set(const char* i_Name, s8 i_RoomNo, s16 i_Point, s8 i_Layer) {
#if TARGET_PC
// UB fix.
if (mName != i_Name) {
dusk::SafeStringCopy(mName, i_Name);
}
#else
strcpy(mName, i_Name);
#endif
mRoomNo = i_RoomNo;
mPoint = i_Point;
mLayer = i_Layer;
+30
View File
@@ -17,6 +17,12 @@
#include <memory>
#include "JSystem/JKernel/JKRHeap.h"
#include "dusk/os.h"
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#endif
// ============================================================================
// Side-table: native thread data per OSThread
@@ -695,6 +701,30 @@ OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask mask) {
return 0;
}
void OSSetCurrentThreadName(const char* name) {
// "Why is this current thread only?", you might ask?
// Because macOS requires that. For some reason.
#if _WIN32
wchar_t buffer[256];
const auto converted = MultiByteToWideChar(
CP_UTF8,
0,
name,
-1,
buffer,
sizeof(buffer)/sizeof(wchar_t));
if (converted == 0) {
CRASH("OSSetThreadName: MultiByteToWideChar failed");
}
const auto result = SetThreadDescription(GetCurrentThread(), buffer);
if (!SUCCEEDED(result)) {
CRASH("OSSetThreadName: SetThreadDescription failed");
}
#endif
}
#ifdef __cplusplus
}
#endif
+61
View File
@@ -0,0 +1,61 @@
#include "Adpcm.hpp"
#include <cassert>
#include <fstream>
#include "JSystem/JAudio2/JASAramStream.h"
// https://github.com/magcius/vgmtrans/blob/8e34ddc2fb43948dc1e1a8759c739a0c1c7b62d7/src/main/formats/JaiSeqScanner.cpp#L489-L531
// https://github.com/XAYRGA/JaiSeqX/blob/f29c024ec3663503f506aa02bcd503ada6e7d8aa/JaiSeqXLJA/DSP/JAIDSPADPCM4.cs#L86-L87
static constexpr u16 Coefficient0[] = {
0,0x0800,0,0x0400,
0x1000,0x0e00,0x0c00,0x1200,
0x1068,0x12c0,0x1400,0x0800,
0x0400,0xfc00,0xfc00,0xf800
};
static constexpr u16 Coefficient1[] = {
0,0,0x0800,0x0400,0xf800,
0xfa00,0xfc00,0xf600,0xf738,
0xf704,0xf400,0xf800,0xfc00,
0x0400,0,0,
};
constexpr int AdpcmFrameSize = 9;
static s16 Clamp16(s32 value) {
if (value > 0x7FFF) return 0x7FFF;
if (value < -0x8000) return -0x8000;
return value;
}
void dusk::audio::Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, size_t pcmLength, s16& hist2, s16& hist1) {
assert (adpcmLength % AdpcmFrameSize == 0 && "ADPCM must be divisible by frame size");
auto endPtr = pcm + pcmLength;
for (int i = 0; i < adpcmLength; i += AdpcmFrameSize) {
u8 header = adpcm[i];
s32 scale = 1 << (header >> 4);
u8 coefIndex = header & 0xF;
s16 coef0 = (s16)Coefficient0[coefIndex];
s16 coef1 = (s16)Coefficient1[coefIndex];
for (int sampleIdx = 0; sampleIdx < 16; sampleIdx++) {
u8 adpcmValue = adpcm[i + 1 + sampleIdx / 2];
u8 unsignedNibble = sampleIdx % 2 == 0 ? adpcmValue >> 4 : adpcmValue & 0xF;
s8 signedNibble = ((s8)(unsignedNibble << 4)) >> 4;
s16 sample = Clamp16((((signedNibble * scale) << 11) + (coef0 * hist1 + coef1 * hist2)) >> 11);
hist2 = hist1;
hist1 = sample;
*pcm++ = sample;
if (endPtr == pcm)
return;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef DUSK_ADPCM_HPP
#define DUSK_ADPCM_HPP
#include <dolphin/types.h>
namespace dusk::audio {
constexpr u32 Adpcm4FrameSize = 9;
constexpr u32 AdpcmSampleCount = 16;
constexpr u32 Adpcm2FrameSize = 5;
void Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, size_t pcmLength, s16& hist1, s16& hist0);
}
#endif // DUSK_ADPCM_HPP
+35
View File
@@ -0,0 +1,35 @@
#include "JSystem/JAudio2/dspproc.h"
#include "JSystem/JAudio2/osdsp_task.h"
#include "JSystem/JAudio2/dsptask.h"
#include "global.h"
#include "os.h"
void DSPReleaseHalt2(u32) {
CRASH("We do not directly emulate the DSP");
}
void DsetupTable(u32, u32, u32, u32, u32) {
// Nada.
}
void DsetMixerLevel(f32) {
// Nada for now, but maybe we should care about this?
}
void DsyncFrame2ch(u32, u32, u32) {
CRASH("We do not directly emulate the DSP");
}
void DsyncFrame4ch(u32, u32, u32, u32, u32) {
CRASH("We do not directly emulate the DSP");
}
void DspBoot(void (*)(void*)) {
CRASH("We do not directly emulate the DSP");
}
void DspFinishWork(u16) {
CRASH("We do not directly emulate the DSP");
}
int DSPSendCommands2(u32*, u32, void (*)(u16)) {
CRASH("We do not directly emulate the DSP");
}
void DsyncFrame2(u32, u32, u32) {
CRASH("We do not directly emulate the DSP");
}
+149
View File
@@ -0,0 +1,149 @@
#include "dusk/audio/DuskAudioSystem.h"
#include <SDL3/SDL_init.h>
#include <array>
#include <cassert>
#include <fstream>
#include <ios>
#include <span>
#include "JSystem/JAudio2/JASAiCtrl.h"
#include "JSystem/JAudio2/JASChannel.h"
#include "JSystem/JAudio2/JASCriticalSection.h"
#include "JSystem/JAudio2/JASDSPChannel.h"
#include "JSystem/JAudio2/JASHeapCtrl.h"
#include "DuskDsp.hpp"
#include "JSystem/JAudio2/JASAudioThread.h"
#include "JSystem/JAudio2/JASDriverIF.h"
// #define DUSK_DUMP_AUDIO
using namespace dusk::audio;
static OutputSubframe OutBuffer;
static std::array<f32, DSP_SUBFRAME_SIZE * OutputSubframe::NUM_CHANNELS> OutInterleaveBuffer;
static SDL_AudioStream* PlaybackStream;
/**
* SDL audiostream callback to trigger rendering of new audio data.
*/
static void SDLCALL GetNewAudio(
void*,
SDL_AudioStream*,
int needed,
int);
/**
* Render an entire new frame of audio and output it to SDL3.
* Note: "audio frames" are unrelated to video frames.
* @return Amount of audio samples rendered.
*/
static int RenderNewAudioFrame();
/**
* Render an audio subframe and output it to SDL3.
*/
static void RenderAudioSubframe();
static void InitSDL3Output() {
SDL_Init(SDL_INIT_AUDIO);
constexpr SDL_AudioSpec spec = {
SDL_AUDIO_F32,
2,
SampleRate,
};
PlaybackStream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&spec,
&GetNewAudio,
nullptr);
}
void dusk::audio::Initialize() {
InitSDL3Output();
DspInit();
JASDsp::initBuffer();
JASDSPChannel::initAll();
JASPoolAllocObject_MultiThreaded<JASChannel>::newMemPool(0x48);
SDL_ResumeAudioStreamDevice(PlaybackStream);
}
void dusk::audio::SetMasterVolume(const f32 value) {
JASCriticalSection section;
MasterVolume = value;
}
void SDLCALL GetNewAudio(
void*,
SDL_AudioStream*,
int needed,
int) {
while (needed > 0) {
const int rendered = RenderNewAudioFrame();
needed -= rendered;
}
}
#if defined(DUSK_DUMP_AUDIO)
static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary);
#endif
int RenderNewAudioFrame() {
JASCriticalSection section;
const u32 countSubframes = JASDriver::getSubFrames();
JASAudioThread::setDSPSyncCount(countSubframes);
for (u32 i = 0; i < countSubframes; i++) {
RenderAudioSubframe();
JASAudioThread::snIntCount -= 1;
}
#if defined(DUSK_DUMP_AUDIO)
outRaw.flush();
#endif
return static_cast<u16>(countSubframes) * DSP_SUBFRAME_SIZE;
}
static void InterleaveOutputData(const OutputSubframe& data, std::span<f32> target) {
assert(target.size() >= data.channels[0].size() * OutputSubframe::NUM_CHANNELS);
size_t outPos = 0;
for (size_t inPos = 0; inPos < data.channels[0].size(); inPos++) {
for (size_t channelIdx = 0; channelIdx < OutputSubframe::NUM_CHANNELS; channelIdx++) {
target[outPos++] = data.channels[channelIdx][inPos];
}
}
}
void RenderAudioSubframe() {
OutBuffer = {};
JASDriver::updateDSP();
DspRender(OutBuffer);
InterleaveOutputData(OutBuffer, OutInterleaveBuffer);
#if defined(DUSK_DUMP_AUDIO)
outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer));
#endif
SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer));
}
u32 dusk::audio::GetResetCount(int channelIdx) {
return ChannelAux[channelIdx].resetCount;
}
f32 dusk::audio::VolumeFromU16(u16 value) {
return static_cast<f32>(value) / static_cast<f32>(JASDriver::getChannelLevel_dsp());
}
+497
View File
@@ -0,0 +1,497 @@
#include <ar.h>
#include <dolphin/os.h>
#include "DuskDsp.hpp"
#include <algorithm>
#include <cassert>
#include <span>
#include "Adpcm.hpp"
#include "JSystem/JAudio2/JASDriverIF.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/endian.h"
#include "global.h"
using namespace dusk::audio;
ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {};
f32 dusk::audio::MasterVolume = 1.0f;
f32 dusk::audio::PrevMasterVolume = 1.0f;
/**
* Validate that a DSP channel's format is actually something we know how to play.
*/
static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) {
if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm4FrameSize)
return true;
if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 16)
return true;
/*
if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm2FrameSize)
return true;
if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 8)
return true;
*/
return false;
}
/**
* Validate that a DSP channel is actually something we know how to play.
*/
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 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) {
if (samples % channel.mSamplesPerBlock != 0) {
// Ensure we round up.
samples += channel.mSamplesPerBlock;
//CRASH("Indivisible sample count: %d\n", samples);
}
return (samples / channel.mSamplesPerBlock) * BlockBytes(channel);
}
/**
* Render the audio data contributed by a single DSP channel. Reads & decodes new input samples.
*/
static void RenderChannel(
JASDsp::TChannel& channel,
ChannelAuxData& channelAux,
OutputSubframe& subframe);
/**
* Converts a pitch value on a DSP channel to a sample rate.
*/
constexpr static int PitchToSampleRate(u16 value) {
return static_cast<int>(static_cast<u64>(SampleRate) * value / 4096);
}
static void UpdateSampleRate(const JASDsp::TChannel& channel, ChannelAuxData& aux) {
auto sampleRate = PitchToSampleRate(channel.mPitch);
const SDL_AudioSpec spec = {
SDL_AUDIO_S16,
1,
sampleRate
};
SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr);
aux.prevPitch = channel.mPitch;
}
/**
* Reset state for a DSP channel between independent playbacks.
*/
static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) {
aux.resetCount += 1;
channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition;
aux.hist0 = 0;
aux.hist1 = 0;
SDL_ClearAudioStream(aux.resampleStream);
UpdateSampleRate(channel, aux);
for (auto& volume : aux.prevVolume) {
volume = NAN;
}
channel.mResetFlag = false;
}
/**
* Mix subframe data from src into dst.
*/
static void MixSubframe(DspSubframe& dst, const DspSubframe& src) {
for (int i = 0; i < dst.size(); i++) {
dst[i] += src[i];
}
}
void dusk::audio::DspRender(OutputSubframe& subframe) {
std::span channels(JASDsp::CH_BUF, DSP_CHANNELS);
for (int i = 0; i < channels.size(); i++) {
auto& channel = channels[i];
auto& channelAux = ChannelAux[i];
if (!channel.mIsActive) {
continue;
}
if (channel.mPauseFlag) {
// Not really sure what the practical difference between pause and
// deactivation is. Either avoids clearing state or allows the DSP to avoid popping?
continue;
}
if (channel.mForcedStop) {
channel.mIsFinished = true;
continue;
}
if (channel.mWaveAramAddress == 0) {
// I think these are oscillator channels? Not backed by audio.
// No idea how to implement these yet, so skip them.
channel.mIsFinished = true;
continue;
}
ValidateChannel(channel);
OutputSubframe channelSubframe = {};
RenderChannel(channel, channelAux, channelSubframe);
for (int o = 0; o < subframe.channels.size(); o++) {
MixSubframe(subframe.channels[o], channelSubframe.channels[o]);
}
}
for (auto& channel : subframe.channels) {
ApplyVolume(channel, channel, PrevMasterVolume, MasterVolume);
}
PrevMasterVolume = MasterVolume;
}
/**
* Actually decode samples from memory for the given audio channel.
*/
static void ReadSampleData(
const JASDsp::TChannel& channel,
ChannelAuxData& aux,
const u8* data,
size_t dataLength,
s16* pcm,
size_t pcmLength) {
if (channel.mSamplesPerBlock == 1) {
if (channel.mBytesPerBlock == 0x10) {
// PCM16
assert(reinterpret_cast<uintptr_t>(data) % 2 == 0 && "PCM data must be aligned");
assert(dataLength % 2 == 0 && "Data length must be multiple of 2");
assert(dataLength * 2 >= pcmLength && "Input too small!");
auto srcPcm = reinterpret_cast<const BE(s16)*>(data);
for (size_t i = 0; i < pcmLength; i++) {
pcm[i] = srcPcm[i];
}
} else {
CRASH("Unsupported format: PCM8");
}
} else {
if (channel.mBytesPerBlock == 9) {
Adpcm4ToPcm16(data, dataLength, pcm, pcmLength, aux.hist1, aux.hist0);
} else {
CRASH("Unsupported format: ADPCM2");
}
}
}
/**
* Read a single *contiguous* chunk of sample data from a channel,
* writes the samples to the channel's resampler stream.
*
* @returns Amount of samples actually read. Can be greater than the amount requested.
*/
static int ReadChannelSamplesChunk(
JASDsp::TChannel& channel,
ChannelAuxData& aux,
int desiredSamples) {
assert(desiredSamples >= 0);
auto aramBase = static_cast<u8*>(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;
u32 skipSamples = curSamplePosition % channel.mSamplesPerBlock;
if (skipSamples != 0) {
// We need to start reading in the middle of a block. This can happen thanks to loops.
// So we move back to the start of the block and keep track that those samples should
// *not* be emitted.
desiredSamples += static_cast<int>(skipSamples);
curSamplePosition -= skipSamples;
channel.mSamplesLeft += skipSamples;
channel.mSamplePosition -= skipSamples;
}
// Pad desiredSamples so that we always leave the channel block-aligned.
desiredSamples = ALIGN_NEXT(desiredSamples, channel.mSamplesPerBlock);
assert(curSamplePosition % channel.mSamplesPerBlock == 0);
auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition);
u32 renderSamples = std::min(channel.mSamplesLeft, static_cast<u32>(desiredSamples));
int renderSize = static_cast<int>(sizeof(s16) * renderSamples);
auto renderData = static_cast<s16*>(alloca(renderSize));
memset(renderData, 0, renderSize);
ReadSampleData(
channel,
aux,
aramBase + dataPosition,
ConvertSamplesToDataLength(channel, renderSamples),
renderData,
renderSamples);
channel.mSamplesLeft -= renderSamples;
channel.mSamplePosition += renderSamples;
SDL_PutAudioStreamData(
aux.resampleStream,
renderData + skipSamples,
static_cast<int>(renderSize - skipSamples * sizeof(u16)));
assert(channel.mSamplePosition % channel.mSamplesPerBlock == 0 || channel.mSamplesLeft == 0);
return static_cast<int>(renderSamples - skipSamples);
}
/**
* Reads new audio channels from a DSP channel and writes them to the resampler stream.
*/
static void SDLCALL ReadChannelSamples(
void *userdata,
SDL_AudioStream*,
int additional_amount,
int) {
if (additional_amount == 0) {
return;
}
const auto index = static_cast<u32>(reinterpret_cast<uintptr_t>(userdata));
auto& channel = JASDsp::CH_BUF[index];
auto& aux = ChannelAux[index];
if (channel.mSamplesLeft == 0 && !channel.mLoopFlag) {
// May get called when we're out of data to read.
// This is expected, as we need to drain the resampler channel before we mark the channel as finished.
return;
}
auto samplesRead = ReadChannelSamplesChunk(channel, aux, additional_amount);
additional_amount -= samplesRead;
if (channel.mSamplesLeft == 0) {
// Reached end of buffer.
if (!channel.mLoopFlag) {
return;
}
channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample;
channel.mSamplePosition = channel.mLoopStartSample;
aux.hist1 = channel.mpPenult;
aux.hist0 = channel.mpLast;
}
if (additional_amount >= 0) {
ReadChannelSamplesChunk(channel, aux, additional_amount);
}
channel.mAramStreamPosition = channel.mWaveAramAddress
+ ConvertSamplesToDataLength(channel, channel.mSamplePosition);
}
/**
* Get the expected BusConnect value needed to define the given output channel in a DSP channel.
*/
constexpr u16 GetBusConnect(const OutputChannel channel) {
switch (channel) {
// TODO: This is a guess for now.
case OutputChannel::LEFT:
return 0x0D00;
case OutputChannel::RIGHT:
return 0x0D60;
default:
CRASH("Invalid output channel!");
}
}
/**
* For a DSP channel the JASDsp::OutputChannelConfig value targeting the given output channel.
* Returns null if the DSP channel does not output to this output channel.
*/
static const JASDsp::OutputChannelConfig* GetOutputConfig(
const JASDsp::TChannel& sourceChannel,
OutputChannel channel) {
auto busConnect = GetBusConnect(channel);
for (const auto& mOutputChannel : sourceChannel.mOutputChannels) {
auto config = &mOutputChannel;
if (config->mBusConnect == busConnect) {
return config;
}
}
return nullptr;
}
struct VolumeValue {
f32 Target;
f32 Init;
};
/**
* Get the volume that the given DSP channel should render to the given output channel at.
*/
static VolumeValue GetVolumeForOutputChannel(
const JASDsp::TChannel& sourceChannel,
OutputChannel outputChannel) {
u16 volume;
u16 initVolume;
f32 panValue = 1;
if (sourceChannel.mAutoMixerBeenSet) {
volume = sourceChannel.mAutoMixerVolume;
initVolume = sourceChannel.mAutoMixerInitVolume;
auto autoMixerPan = static_cast<f32>(sourceChannel.mAutoMixerPanDolby >> 8) / 127;
switch (outputChannel) {
case OutputChannel::LEFT:
panValue = 1 - autoMixerPan;
break;
case OutputChannel::RIGHT:
panValue = autoMixerPan;
break;
default:
CRASH("Unhandled output channel: OutputChannel");
}
} else {
auto config = GetOutputConfig(sourceChannel, outputChannel);
if (config == nullptr) {
return {0, 0};
}
volume = config->mTargetVolume;
initVolume = config->mCurrentVolume;
}
// TODO: interpolate to avoid popping.
f32 targetRatio = VolumeFromU16(volume);
targetRatio *= panValue;
f32 initRatio = VolumeFromU16(initVolume);
initRatio *= panValue;
return {targetRatio, initRatio};
}
/**
* Given decoded & resampled input samples, render a DSP channel to a given output channel.
*/
static void RenderOutputChannel(
const JASDsp::TChannel& sourceChannel,
ChannelAuxData& aux,
OutputChannel outputChannel,
const std::span<f32> inputSamples,
OutputSubframe& fullOutputSubframe) {
auto& outputSubframe = fullOutputSubframe[outputChannel];
assert(inputSamples.size() <= outputSubframe.size());
auto volume = GetVolumeForOutputChannel(sourceChannel, outputChannel);
f32 targetVolume = volume.Target;
auto& prevVolume = aux.PrevVolume(outputChannel);
if (std::isnan(prevVolume)) {
// Initialize previous volume to new volume on first render.
prevVolume = volume.Init;
}
if (prevVolume == 0 && targetVolume == 0) {
return;
}
ApplyVolume(outputSubframe, inputSamples, prevVolume, targetVolume);
prevVolume = targetVolume;
}
static void RenderChannel(
JASDsp::TChannel& channel,
ChannelAuxData& channelAux,
OutputSubframe& subframe) {
if (channel.mResetFlag) {
ResetChannel(channel, channelAux);
} else if (channelAux.prevPitch != channel.mPitch) {
UpdateSampleRate(channel, channelAux);
}
DspSubframe audioLoadBuffer = {};
int wantRead = sizeof(audioLoadBuffer);
auto read = SDL_GetAudioStreamData(
channelAux.resampleStream,
&audioLoadBuffer,
wantRead);
if (read < wantRead) {
channel.mIsFinished = true;
}
auto hasReadSamples = std::span(audioLoadBuffer).subspan(0, wantRead / sizeof(f32));
static_assert(OutputSubframe::NUM_CHANNELS == 2, "Keep RenderChannel in sync!");
RenderOutputChannel(channel, channelAux, OutputChannel::LEFT, hasReadSamples, subframe);
RenderOutputChannel(channel, channelAux,OutputChannel::RIGHT, hasReadSamples, subframe);
}
void dusk::audio::DspInit() {
constexpr SDL_AudioSpec srcSpec = {
SDL_AUDIO_S16,
1,
SampleRate
};
constexpr SDL_AudioSpec dstSpec = {
SDL_AUDIO_F32,
1,
SampleRate
};
for (u32 i = 0; i < DSP_CHANNELS; i++) {
auto& aux = ChannelAux[i];
aux.resampleStream = SDL_CreateAudioStream(&srcSpec, &dstSpec);
SDL_SetAudioStreamGetCallback(
aux.resampleStream,
ReadChannelSamples,
reinterpret_cast<void*>(static_cast<uintptr_t>(i)));
}
}
void dusk::audio::ApplyVolume(
std::span<f32> dst,
const std::span<f32> src,
const f32 startVolume,
const f32 endVolume) {
assert(dst.size() >= src.size());
if (startVolume == endVolume) {
for (int i = 0; i < src.size(); i++) {
dst[i] = src[i] * startVolume;
}
} else {
const f32 step = (endVolume - startVolume) / static_cast<f32>(src.size());
auto curVolume = startVolume;
for (int i = 0; i < src.size(); i++) {
dst[i] = src[i] * curVolume;
curVolume += step;
}
}
}
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include "JSystem/JAudio2/JASDSPInterface.h"
#include <array>
#include <cassert>
#include "SDL3/SDL_audio.h"
#include <span>
// ReSharper disable once CppUnusedIncludeDirective
#include "global.h"
namespace dusk::audio {
constexpr int SampleRate = 32000;
enum class OutputChannel : u8 {
LEFT,
RIGHT,
OutputChannel_MAX
};
/**
* Data stored by DSP implementation for each DSP channel.
*/
struct ChannelAuxData {
s16 hist1;
s16 hist0;
SDL_AudioStream* resampleStream;
u16 prevPitch;
// Used for debugging tools.
u32 resetCount;
/**
* Previous volume values, per output channel.
* Used to avoid clicking when volumes change.
* Set to NaN after channel reset, indicating that initial volume value is previous.
*/
f32 prevVolume[static_cast<int>(OutputChannel::OutputChannel_MAX)];
f32& PrevVolume(OutputChannel channel) {
assert(channel < OutputChannel::OutputChannel_MAX);
return prevVolume[static_cast<int>(channel)];
}
};
extern ChannelAuxData ChannelAux[DSP_CHANNELS];
/**
* Data storage for a single subframe and output channel's worth of samples.
*/
using DspSubframe = std::array<f32, DSP_SUBFRAME_SIZE>;
/**
* Data storage for a single subframe's worth of samples, across all output channels.
*/
struct OutputSubframe {
static constexpr int NUM_CHANNELS = static_cast<int>(OutputChannel::OutputChannel_MAX);
std::array<DspSubframe, NUM_CHANNELS> channels;
DspSubframe& operator[](OutputChannel channel) {
assert(channel < OutputChannel::OutputChannel_MAX);
return channels[static_cast<int>(channel)];
}
};
/**
* Initialize the DSP system, creating data storage needed for channels and such.
*/
void DspInit();
/**
* Render a subframe of audio with the current DSP state.
*/
void DspRender(OutputSubframe& subframe);
/**
* Get the amount of samples a single "block" of this channel's data has.
*/
constexpr u32 BlockSamples(const JASDsp::TChannel& channel) {
return channel.mSamplesPerBlock;
}
/**
* Get the amount of bytes a single "block" of this channel's data has.
*/
constexpr u32 BlockBytes(const JASDsp::TChannel& channel) {
if (channel.mSamplesPerBlock == 1) {
if (channel.mBytesPerBlock == 16) {
return 2;
}
if (channel.mBytesPerBlock == 8) {
return 1;
}
CRASH("Unknown format");
}
return channel.mBytesPerBlock;
}
/**
* Apply a volume level to audio data.
* Interpolates across the two provided volume levels to avoid clicking.
*/
void ApplyVolume(std::span<f32> dst, std::span<f32> src, f32 startVolume, f32 endVolume);
extern f32 MasterVolume;
extern f32 PrevMasterVolume;
}
+13
View File
@@ -0,0 +1,13 @@
#include "JSystem/JAudio2/JASCriticalSection.h"
#include <mutex>
static std::recursive_mutex gAudioThreadMutex;
JASCriticalSection::JASCriticalSection() {
gAudioThreadMutex.lock();
}
JASCriticalSection::~JASCriticalSection() {
gAudioThreadMutex.unlock();
}
+252
View File
@@ -0,0 +1,252 @@
#include "ImGuiConsole.hpp"
#include "ImGuiMenuTools.hpp"
#include "JSystem/JAudio2/JAISeMgr.h"
#include "JSystem/JAudio2/JAISeqMgr.h"
#include "JSystem/JAudio2/JAIStreamMgr.h"
#include "JSystem/JAudio2/JASCriticalSection.h"
#include "JSystem/JAudio2/JASDSPChannel.h"
#include "JSystem/JAudio2/JASDSPInterface.h"
#include "JSystem/JAudio2/JASTrack.h"
#include "dusk/audio/DuskAudioSystem.h"
static std::array<u8, DSP_CHANNELS> channelSortIndices = {};
static std::array<u32, DSP_CHANNELS> lastResetCounts = {};
static bool sortUpdateCount = true;
static void DisplayDspChannel(int i) {
using namespace dusk::audio;
auto& channel = JASDsp::CH_BUF[i];
auto& jasChannel = JASDSPChannel::sDspChannels[i];
if (!channel.mIsActive) {
return;
}
char buf[64];
snprintf(buf, sizeof(buf), "%d", i);
if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
ImGui::Text("[%02X]", i);
ImGui::SameLine();
auto resetCount = GetResetCount(i);
ImColor color = IM_COL32_WHITE;
if (lastResetCounts[i] != resetCount) {
lastResetCounts[i] = resetCount;
color = IM_COL32(0, 0xFF, 0, 0xFF);
}
ImGui::TextColored(color, "Update count: %d, reset count: %ud", jasChannel.mUpdateCounter, resetCount);
ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false");
ImGui::SameLine();
ImGui::Text("Priority: %hd", jasChannel.mPriority);
ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock);
ImGui::Text("Position: %08X/%08X", channel.mSamplePosition, channel.mEndSample);
ImGui::SameLine();
ImGui::Text("Memory: %08X/%08X", channel.mWaveAramAddress, channel.mAramStreamPosition);
if (channel.mAutoMixerBeenSet) {
auto pan = (channel.mAutoMixerPanDolby >> 8) / 127.5f;
auto dolby = (channel.mAutoMixerPanDolby & 0xFF) / 127.5f;
auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f;
auto volume = VolumeFromU16(channel.mAutoMixerVolume);
ImGui::Text(
"Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f)",
pan, dolby, fxMix, volume);
} else {
ImGui::Text(
"Bus connect: %04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f)",
channel.mOutputChannels[0].mBusConnect,
VolumeFromU16(channel.mOutputChannels[0].mTargetVolume),
channel.mOutputChannels[1].mBusConnect,
VolumeFromU16(channel.mOutputChannels[1].mTargetVolume),
channel.mOutputChannels[2].mBusConnect,
VolumeFromU16(channel.mOutputChannels[2].mTargetVolume),
channel.mOutputChannels[3].mBusConnect,
VolumeFromU16(channel.mOutputChannels[3].mTargetVolume),
channel.mOutputChannels[4].mBusConnect,
VolumeFromU16(channel.mOutputChannels[4].mTargetVolume),
channel.mOutputChannels[5].mBusConnect,
VolumeFromU16(channel.mOutputChannels[5].mTargetVolume));
}
}
ImGui::EndChild();
}
static void InitChannelSortIndices() {
for (int i = 0; i < channelSortIndices.size(); i++) {
channelSortIndices[i] = i;
}
}
static void SortChannelsByUpdateCount() {
InitChannelSortIndices();
std::ranges::stable_sort(
channelSortIndices,
[](u8 a, u8 b) {
auto& jasChannelA = JASDSPChannel::sDspChannels[a];
auto& jasChannelB = JASDSPChannel::sDspChannels[b];
return jasChannelA.mUpdateCounter > jasChannelB.mUpdateCounter;
});
}
static void ShowAllDspChannels() {
int activeChannels = 0;
for (int i = 0; i < DSP_CHANNELS; i++) {
if (JASDsp::CH_BUF[i].mIsActive) {
activeChannels++;
}
}
ImGui::Text("Active channels: %d", activeChannels);
ImGui::Checkbox("Sort by update count", &sortUpdateCount);
if (sortUpdateCount) {
SortChannelsByUpdateCount();
for (u8 index : channelSortIndices) {
DisplayDspChannel(index);
}
} else {
for (int i = 0; i < DSP_CHANNELS; i++) {
DisplayDspChannel(i);
}
}
}
static void ShowAllTracks() {
if (ImGui::Button("Pause all")) {
for (auto& track : JASTrack::sTrackList) {
track.pause(true);
}
}
for (auto& track : JASTrack::sTrackList) {
char buf[32];
snprintf(buf, sizeof(buf), "%p", &track);
if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
ImGui::Text("[%p]", &track);
bool paused = track.mFlags.pause;
ImGui::Checkbox("Paused", &paused);
track.mFlags.pause = paused;
bool muted = track.mFlags.mute;
ImGui::Checkbox("Muted", &muted);
track.mFlags.mute = muted;
for (int i = 0; i < JASTrack::MAX_CHILDREN; i++) {
const auto child = track.getChild(i);
if (child != nullptr) {
ImGui::Text("child: [%p]", child);
}
}
}
ImGui::EndChild();
}
}
static void ShowAllJAIStreams() {
auto& mgr = *JAIStreamMgr::getInstance();
for (auto streamLink = mgr.getStreamList()->getFirst(); streamLink != nullptr; streamLink = streamLink->getNext()) {
auto& stream = *streamLink->getObject();
char buf[32];
snprintf(buf, sizeof(buf), "%p", &stream);
if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
ImGui::Text("[%p]", &stream);
bool paused = stream.status_.field_0x0.flags.paused;
ImGui::Checkbox("Paused", &paused);
stream.status_.field_0x0.flags.paused = paused;
}
ImGui::EndChild();
}
}
static void ShowAllJAISes() {
auto& mgr = *JAISeMgr::getInstance();
for (int i = 0; i < JAISeMgr::NUM_CATEGORIES; i++) {
const auto category = mgr.getCategory(i);
char buf[32];
snprintf(buf, sizeof(buf), "%i", i);
if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
ImGui::Text("Category: %i", i);
if (ImGui::Button("Pause All")) {
category->pause(true);
}
for (auto seLink = category->getSeList()->getFirst(); seLink != nullptr; seLink = seLink->getNext()) {
const auto se = seLink->getObject();
ImGui::Text("[%p]", se);
ImGui::Text(se->status_.field_0x0.flags.paused ? "Paused" : "Not paused");
}
}
ImGui::EndChild();
}
}
static void ShowAllJAISeqs() {
auto& mgr = *JAISeqMgr::getInstance();
if (ImGui::Button("Pause")) {
mgr.pause(true);
}
ImGui::SameLine();
if (ImGui::Button("Unpause")) {
mgr.pause(false);
}
}
void dusk::ImGuiMenuTools::ShowAudioDebug() {
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showAudioDebug)) {
return;
}
if (!ImGui::Begin("Audio Debug", &m_showAudioDebug)) {
ImGui::End();
return;
}
{
JASCriticalSection cs;
if (ImGui::BeginTabBar("Tabs")) {
if (ImGui::BeginTabItem("DSP channels")) {
ShowAllDspChannels();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("JAITrack")) {
ShowAllTracks();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("JAIStream")) {
ShowAllJAIStreams();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("JAISe")) {
ShowAllJAISes();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("JAISeq")) {
ShowAllJAISeqs();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
ImGui::End();
}
+7 -2
View File
@@ -7,8 +7,9 @@
#include <imgui_internal.h>
#include "JSystem/JUtility/JUTGamePad.h"
#include "m_Do/m_Do_controller_pad.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "m_Do/m_Do_audio.h"
#include "m_Do/m_Do_controller_pad.h"
namespace dusk {
ImGuiMenuGame::ImGuiMenuGame() {}
@@ -35,6 +36,8 @@ namespace dusk {
ImGui::Text("Master Volume");
ImGui::SliderFloat("##m_masterVolume", &m_audioSettings.m_masterVolume, 0.0f, 1.0f, "");
/*
// TODO: implement additional settings
ImGui::Text("Main Music Volume");
ImGui::SliderFloat("##m_mainMusicVolume", &m_audioSettings.m_mainMusicVolume, 0.0f, 1.0f, "");
@@ -49,8 +52,10 @@ namespace dusk {
Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface();
if (audioMgr != nullptr) {
// TODO: actually apply volume settings
}
*/
audio::SetMasterVolume(m_audioSettings.m_masterVolume);
ImGui::EndMenu();
}
+3 -1
View File
@@ -20,7 +20,7 @@ namespace dusk {
if (ImGui::Checkbox("Development Mode", &m_isDevelopmentMode)) {
isToggleDevelopmentMode = true;
}
ImGui::Separator();
if (ImGui::BeginMenu("Collision View")) {
@@ -45,6 +45,7 @@ namespace dusk {
ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader);
ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo);
ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor);
ImGui::MenuItem("Audio Debug", "F7", &m_showAudioDebug);
ImGui::EndMenu();
}
@@ -59,6 +60,7 @@ namespace dusk {
ShowStubLog();
ShowMapLoader();
ShowPlayerInfo();
ShowAudioDebug();
if (m_showSaveEditor) {
m_saveEditor.draw(m_showSaveEditor);
+3
View File
@@ -31,6 +31,7 @@ namespace dusk {
void ShowStubLog();
void ShowMapLoader();
void ShowPlayerInfo();
void ShowAudioDebug();
CollisionViewSettings& getCollisionViewSettings() { return m_collisionViewSettings; }
@@ -48,6 +49,8 @@ namespace dusk {
bool m_showStubLog = false;
bool m_showMapLoader = false;
bool m_showAudioDebug = false;
struct {
int mapIdx = -1;
int regionIdx = -1;
+7 -1
View File
@@ -1,4 +1,5 @@
#include <vector>
#include <mutex>
#include "dusk/logging.h"
#include "imgui.h"
@@ -9,6 +10,7 @@ namespace dusk {
static ImGuiTextBuffer StubLogBuffer;
static std::vector<int> LineOffsets;
static bool StubLogPaused;
static std::mutex StubLogMutex;
const char* LogLevelName(const AuroraLogLevel level) {
switch (level) {
@@ -32,6 +34,8 @@ namespace dusk {
return;
}
std::lock_guard lock(StubLogMutex);
LineOffsets.push_back(StubLogBuffer.size());
const auto levelName = LogLevelName(level);
StubLogBuffer.appendf("[%s | %s] %s\n", levelName, module, message);
@@ -40,6 +44,8 @@ namespace dusk {
static void ClearPastFrame();
void ImGuiMenuTools::ShowStubLog() {
std::lock_guard lock(StubLogMutex);
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F5, m_showStubLog)) {
ClearPastFrame();
return;
@@ -56,7 +62,7 @@ namespace dusk {
if (ImGui::BeginChild("scrolling")) {
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.size());
clipper.Begin(static_cast<int>(LineOffsets.size()));
while (clipper.Step()) {
for (int idx = clipper.DisplayStart; idx < clipper.DisplayEnd; idx++) {
const char* lineStart = StubLogBuffer.begin() + LineOffsets[idx];
-40
View File
@@ -367,46 +367,6 @@ VIRetraceCallback VISetPreRetraceCallback(VIRetraceCallback cb) {
} // extern "C"
#pragma mark DSP
#include <dolphin/dsp.h>
extern "C" void __DSP_insert_task(DSPTaskInfo* task) {
STUB_LOG();
}
extern "C" void __DSP_boot_task(DSPTaskInfo*) {
STUB_LOG();
}
extern "C" void __DSP_exec_task(DSPTaskInfo*, DSPTaskInfo*) {
STUB_LOG();
}
extern "C" void __DSP_remove_task(DSPTaskInfo* task) {
STUB_LOG();
}
void DSPAssertInt(void) {
STUB_LOG();
}
u32 DSPCheckMailFromDSP(void) {
STUB_LOG();
return 0;
}
u32 DSPCheckMailToDSP(void) {
STUB_LOG();
return 0;
}
void DSPInit(void) {
STUB_LOG();
}
u32 DSPReadMailFromDSP(void) {
STUB_LOG();
return 0;
}
void DSPSendMailToDSP(u32 mail) {
STUB_LOG();
}
#pragma mark Z2Audio
class Z2AudioCS {
public:
+5
View File
@@ -1019,10 +1019,15 @@ cull_sphere l_cullSizeSphere[fopAc_CULLSPHERE_MAX_e] = {
s32 fopAcM_cullingCheck(fopAc_ac_c const* i_actor) {
MtxP mtx_p;
#if AVOID_UB
Mtx concat_mtx;
#endif
if (fopAcM_GetMtx(i_actor) == NULL) {
mtx_p = j3dSys.getViewMtx();
} else {
#if !AVOID_UB
Mtx concat_mtx;
#endif
cMtx_concat(j3dSys.getViewMtx(), fopAcM_GetMtx(i_actor), concat_mtx);
mtx_p = concat_mtx;
}
+7
View File
@@ -14,6 +14,8 @@
#include <chrono>
#include <thread>
#include "dusk/os.h"
#if PLATFORM_GCN
const int stack_size = 3072;
#else
@@ -65,6 +67,11 @@ static void mDoDvdErr_Watch(void*) {
OSDisableInterrupts();
#endif
#endif
#if TARGET_PC
OSSetCurrentThreadName("DVD error thread");
#endif
JKRThread(OSGetCurrentThread(), 0);
JKRSetCurrentHeap(mDoExt_getAssertHeap());
+5
View File
@@ -10,6 +10,7 @@
#include "m_Do/m_Do_MemCardRWmng.h"
#include "m_Do/m_Do_Reset.h"
#include "os_report.h"
#include "dusk/os.h"
#if PLATFORM_WII || PLATFORM_SHIELD
#include <revolution/nand.h>
@@ -866,6 +867,10 @@ mDoMemCd_Ctrl_c g_mDoMemCd_control;
static int mDoMemCd_main(void*) {
JKRThread(OSGetCurrentThread(), 0);
#if TARGET_PC
OSSetCurrentThreadName("MemCardThread");
#endif
JKRSetCurrentHeap(mDoExt_getAssertHeap());
g_mDoMemCd_control.main();
+8
View File
@@ -10,6 +10,7 @@
#include "JSystem/JKernel/JKRDvdRipper.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#include "JSystem/JKernel/JKRMemArchive.h"
#include "dusk/os.h"
#include "m_Do/m_Do_Reset.h"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_ext.h"
@@ -17,6 +18,9 @@
s32 mDoDvdThd::main(void* param_0) {
JKRThread(OSGetCurrentThread(), 0);
#if TARGET_PC
OSSetCurrentThreadName("DVD thread");
#endif
JKRSetCurrentHeap(mDoExt_getAssertHeap());
mDoDvdThd_param_c* param = static_cast<mDoDvdThd_param_c*>(param_0);
param->mainLoop();
@@ -159,7 +163,11 @@ void mDoDvdThd_param_c::mainLoop() {
while ((command = this->getFirstCommand())) {
this->cut(command);
if (mDoDvdThd::SyncWidthSound) {
#if TARGET_PC
JASDvd::getThreadPointer()->sendCmdMsg(cb, &command, sizeof(void*));
#else
JASDvd::getThreadPointer()->sendCmdMsg(cb, &command, 4);
#endif
} else {
cb(&command);
}
+7
View File
@@ -47,6 +47,7 @@
#include "dusk/dusk.h"
#include "dusk/logging.h"
#include "dusk/time.h"
#include "dusk/main.h"
#include <aurora/aurora.h>
#include <aurora/event.h>
@@ -73,6 +74,10 @@ const int audioHeapSize = 0x14D800;
// =========================================================================
#define COPYDATE_PATH "/str/Final/Release/COPYDATE"
#if TARGET_PC
bool dusk::IsShuttingDown = false;
#endif
s32 LOAD_COPYDATE(void*) {
char buffer[32];
memset(buffer, 0, sizeof(buffer));
@@ -307,6 +312,8 @@ int game_main(int argc, char* argv[]) {
fflush(stdout);
fflush(stderr);
dusk::IsShuttingDown = true;
aurora_shutdown();
return 0;