From 56c6cc38488c3743a626d9ea3b7a17f4fdd53fb2 Mon Sep 17 00:00:00 2001 From: Caturria Date: Tue, 29 Aug 2023 00:03:28 -0400 Subject: [PATCH] Finish re-integration. Also support playback of raw samples as external sounds. --- .../accessible-actors/AccessibleActorList.cpp | 5 +- .../accessible-actors/ActorAccessibility.cpp | 91 ++++++++++++++++--- .../accessible-actors/ActorAccessibility.h | 5 + .../accessible-actors/SfxExtractor.cpp | 5 +- .../accessible-actors/SfxExtractor.h | 1 - 5 files changed, 89 insertions(+), 18 deletions(-) diff --git a/soh/soh/Enhancements/accessible-actors/AccessibleActorList.cpp b/soh/soh/Enhancements/accessible-actors/AccessibleActorList.cpp index 601a71feb..398bca94f 100644 --- a/soh/soh/Enhancements/accessible-actors/AccessibleActorList.cpp +++ b/soh/soh/Enhancements/accessible-actors/AccessibleActorList.cpp @@ -296,7 +296,10 @@ void accessible_goma(AccessibleActor* actor) { } void accessible_door_of_time(AccessibleActor* actor) { - ActorAccessibility_PlaySoundForActor(actor, 0, NA_SE_EV_DIAMOND_SWITCH, false); + ActorAccessibility_PlaySampleForActor(actor, 0, "Chanting", false); + ActorAccessibility_SetSoundPitch(actor, 0, 1.0); + + //ActorAccessibility_PlaySoundForActor(actor, 0, NA_SE_EV_DIAMOND_SWITCH, false); } void accessible_sticks(AccessibleActor* actor) { diff --git a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp index 66bbaa15a..4b4e58a62 100644 --- a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp +++ b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp @@ -17,6 +17,7 @@ #include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h" #include "soh/Enhancements/tts/tts.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/audio/AudioDecoder.h" extern "C" { extern PlayState* gPlayState; extern bool freezeGame; @@ -48,13 +49,15 @@ typedef std::map VAZones_t;//Maps room/ scene indices to their co typedef std::unordered_set SceneList_t;//A list of scenes which have already been visited (since the game was launched). Used to prevent re-creation of terrain VAs every time the player reloads a scene. typedef struct { - std::string hexName; + std::string path; std::shared_ptr resource; + std::shared_ptr decodedSample;//Set if the record is for a raw sample as opposed to a SFX. + }SfxRecord; class ActorAccessibility { public: - bool isOn = false; + int isOn = 0; uint64_t nextActorID = 0; SupportedActors_t supportedActors; TrackedActors_t trackedActors; @@ -64,6 +67,7 @@ class ActorAccessibility { AccessibleAudioEngine* audioEngine; SfxExtractor sfxExtractor; std::unordered_map sfxMap;//Maps internal sfx to external (prerendered) resources. + std::unordered_map sampleMap;//Similar to above, but this one maps raw audio samples as opposed to SFX. int extractSfx = 0; }; static ActorAccessibility* aa; @@ -100,6 +104,9 @@ void ActorAccessibility_OnGameStillFrozen() void ActorAccessibility_Init() { aa = new ActorAccessibility(); + aa->isOn = CVarGetInteger("gA11yAudioInteraction", 0); + if (!aa->isOn) + return; aa->extractSfx = CVarGetInteger("gExtractSfx", 0); if (aa->extractSfx) freezeGame = true; @@ -126,7 +133,7 @@ void ActorAccessibility_Shutdown() { policy->pitch = 1.5; policy->runsAlways = false; policy->sound = sfx; - policy->volume = 1.0; + policy->volume = 0.5; policy->initUserData = NULL; policy->cleanupUserData = NULL; policy->pitchModifier = 0.1; @@ -216,12 +223,23 @@ int ActorAccessibility_GetRandomStartingFrameCount(int min, int max) { Audio_PlaySoundGeneral(sfxId, &actor->projectedPos, 4, &actor->currentPitch, &actor->currentVolume, &actor->currentReverb); } const char* ActorAccessibility_MapSfxToExternalAudio(s16 sfxId); - void ActorAccessibility_PlaySound(void* handle, int slot, s16 sfxId, bool looping) { const char* path = ActorAccessibility_MapSfxToExternalAudio(sfxId); + if (path == NULL) + return; aa->audioEngine->playSound((uintptr_t)handle, slot, path, looping); } + const char* ActorAccessibility_MapRawSampleToExternalAudio(const char* name); + + void ActorAccessibility_PlayRawSample(void* handle, int slot, const char* name, bool looping) + { + const char* path = ActorAccessibility_MapRawSampleToExternalAudio(name); + if (path == NULL) + return; + aa->audioEngine->playSound((uintptr_t)handle, slot, path, looping); + + } void ActorAccessibility_StopSound(void* handle, int slot) { aa->audioEngine->stopSound((uintptr_t) handle, slot); @@ -269,17 +287,29 @@ int ActorAccessibility_GetRandomStartingFrameCount(int min, int max) { aa->audioEngine->seekSound((uintptr_t)handle, slot, offset); } + void ActorAccessibility_ConfigureSoundForActor(AccessibleActor* actor, int slot) + { + ActorAccessibility_SetSoundPitch(actor, slot, actor->policy.pitch); + ActorAccessibility_SetPitchBehindModifier(actor, slot, actor->policy.pitchModifier); + ActorAccessibility_SetSoundPos(actor, slot, &actor->projectedPos, actor->xyzDistToPlayer, + actor->policy.distance); + ActorAccessibility_SetSoundVolume(actor, slot, actor->policy.volume); + actor->managedSoundSlots[slot] = true; + } void ActorAccessibility_PlaySoundForActor(AccessibleActor* actor, int slot, s16 sfxId, bool looping) { if (slot < 0 || slot > NUM_MANAGED_SOUND_SLOTS) return; ActorAccessibility_PlaySound(actor, slot, sfxId, looping); - ActorAccessibility_SetSoundPitch(actor, slot, actor->policy.pitch); - ActorAccessibility_SetPitchBehindModifier(actor, slot, actor->policy.pitchModifier); - ActorAccessibility_SetSoundPos(actor, slot, &actor->projectedPos, actor->xzDistToPlayer, - actor->policy.distance); - ActorAccessibility_SetSoundVolume(actor, slot, actor->policy.volume); - actor->managedSoundSlots[slot] = true; + ActorAccessibility_ConfigureSoundForActor(actor, slot); + + } + void ActorAccessibility_PlaySampleForActor(AccessibleActor* actor, int slot, const char* name, bool looping) + { + if (slot < 0 || slot > NUM_MANAGED_SOUND_SLOTS) + return; + ActorAccessibility_PlayRawSample(actor, slot, name, looping); + ActorAccessibility_ConfigureSoundForActor(actor, slot); } void ActorAccessibility_StopSoundForActor(AccessibleActor* actor, int slot) @@ -509,9 +539,14 @@ int ActorAccessibility_GetRandomStartingFrameCount(int min, int max) { return true; } void ActorAccessibility_ShutdownAudio() { + if (aa->isOn == 0) + return; delete aa->audioEngine; } void ActorAccessibility_MixAccessibleAudioWithGameAudio(int16_t* ogBuffer, uint32_t nFrames) { + if (aa->isOn == 0) + return; + aa->audioEngine->mix(ogBuffer, nFrames); } @@ -532,18 +567,48 @@ return NULL;//Resource doesn't exist, user's gotta run the extractor. tempRecord.resource = res; std::stringstream ss; ss << std::setw(4) << std::setfill('0') << std::hex << sfxId; - tempRecord.hexName = ss.str(); + tempRecord.path = ss.str(); aa->sfxMap[sfxId] = tempRecord; record = &aa->sfxMap[sfxId]; - aa->audioEngine->cacheDecodedSample(record->hexName, record->resource->Buffer.data(), + aa->audioEngine->cacheDecodedSample(record->path, record->resource->Buffer.data(), record->resource->Buffer.size()); } else record = &it->second; - return record->hexName.c_str(); + return record->path.c_str(); } +//Map the path to a raw sample to the external audio engine. + const char* ActorAccessibility_MapRawSampleToExternalAudio(const char* name) + { + SfxRecord* record; + std::string key(name); + auto it = aa->sampleMap.find(key); + if (it == aa->sampleMap.end()) { + SfxRecord tempRecord; + std::stringstream ss; + ss << "audio/samples/" << key; + std::string fullPath = ss.str(); + auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResource(fullPath); + if (res == nullptr) +return NULL; // Resource doesn't exist, user's gotta run the extractor. + AudioDecoder decoder; + decoder.setSample((LUS::AudioSample*)res.get()); + s16* wav; + size_t wavSize = decoder.decodeToWav(&wav); + + tempRecord.path = key; + tempRecord.decodedSample = std::make_shared(wav); + aa->sampleMap[key] = tempRecord; + record = &aa->sampleMap[key]; + aa->audioEngine->cacheDecodedSample(record->path, wav, + wavSize); + } else + record = &it->second; + + return record->path.c_str(); + } // Call once per frame to tell the audio engine to start working on the latest batch of queued instructions. void ActorAccessibility_PrepareNextAudioFrame() { diff --git a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.h b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.h index a363cf803..f8ebe7851 100644 --- a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.h +++ b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.h @@ -99,6 +99,9 @@ void ActorAccessibility_PlaySpecialSound(AccessibleActor* actor, s16 sfxId); *looping: whether to play the sound just once or on a continuous loop. */ void ActorAccessibility_PlaySound(void* actor, int slot, s16 sfxId, bool looping); +//Play one of the game's internal samples. + void ActorAccessibility_PlayRawSample(void* handle, int slot, const char* name, bool looping); +// //Stop a sound. Todo: consider making this a short fade instead of just cutting it off. void ActorAccessibility_StopSound(void* handle, int slot); void ActorAccessibility_StopAllSounds(void* handle); @@ -121,6 +124,8 @@ void ActorAccessibility_SeekSound(void* handle, int slot, size_t offset); * */ void ActorAccessibility_PlaySoundForActor(AccessibleActor* actor, int slot, s16 sfxId, bool looping); +void ActorAccessibility_PlaySampleForActor(AccessibleActor* actor, int slot, const char* name, bool looping); + void ActorAccessibility_StopSoundForActor(AccessibleActor* actor, int slot); void ActorAccessibility_StopAllSoundsForActor(AccessibleActor* actor); f32 ActorAccessibility_ComputeCurrentVolume(f32 maxDistance, f32 xzDistToPlayer); diff --git a/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp b/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp index 5fcb1067d..0202ac5a9 100644 --- a/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp +++ b/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp @@ -93,8 +93,6 @@ void SfxExtractor::renderOutput() { } void SfxExtractor::setup() { try { - ogMusicVolume = CVarGetFloat("gMainMusicVolume", 1.0); - CVarSetFloat("gMainMusicVolume", 0.0); SpeechSynthesizer::Instance->Speak("Sfx extraction speedrun initiated. Please wait. This will take a few minutes.", GetLanguageCode()); // Kill the audio thread so we can take control. @@ -158,6 +156,8 @@ void SfxExtractor::finished() { CVarClear("gExtractSfx"); CVarSave(); archive = nullptr; + freezeGame = false; + Audio_QueueSeqCmd(NA_BGM_TITLE); if (currentStep == STEP_ERROR || currentStep == STEP_ERROR_OTR) { @@ -171,7 +171,6 @@ void SfxExtractor::finished() { SpeechSynthesizer::Instance->Speak(ss.str().c_str(), GetLanguageCode()); } else Audio_PlayFanfare(NA_BGM_ITEM_GET); - freezeGame = false; } void SfxExtractor::maybeGiveProgressReport() { diff --git a/soh/soh/Enhancements/accessible-actors/SfxExtractor.h b/soh/soh/Enhancements/accessible-actors/SfxExtractor.h index 28703f2a7..98087d37c 100644 --- a/soh/soh/Enhancements/accessible-actors/SfxExtractor.h +++ b/soh/soh/Enhancements/accessible-actors/SfxExtractor.h @@ -10,7 +10,6 @@ class SfxExtractor { s16 currentSfx; std::vector tempStorage; // Stores raw audio data for the sfx currently being ripped. int16_t* tempBuffer; // Raw pointer to the above vector. - f32 ogMusicVolume; int progressMilestones[9]; // Implements progress reports after every 10 percent. // Check if a buffer contains meaningful audio output. bool isAllZero(int16_t* buffer, size_t count);