Finish re-integration. Also support playback of raw samples as external sounds.

This commit is contained in:
Caturria 2023-08-29 00:03:28 -04:00
commit 56c6cc3848
5 changed files with 89 additions and 18 deletions

View file

@ -296,7 +296,10 @@ void accessible_goma(AccessibleActor* actor) {
} }
void accessible_door_of_time(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) { void accessible_sticks(AccessibleActor* actor) {

View file

@ -17,6 +17,7 @@
#include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h" #include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h"
#include "soh/Enhancements/tts/tts.h" #include "soh/Enhancements/tts/tts.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/audio/AudioDecoder.h"
extern "C" { extern "C" {
extern PlayState* gPlayState; extern PlayState* gPlayState;
extern bool freezeGame; extern bool freezeGame;
@ -48,13 +49,15 @@ typedef std::map<s32, VAList_t> VAZones_t;//Maps room/ scene indices to their co
typedef std::unordered_set<s16> 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 std::unordered_set<s16> 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 { typedef struct {
std::string hexName; std::string path;
std::shared_ptr<LUS::File> resource; std::shared_ptr<LUS::File> resource;
std::shared_ptr<s16*> decodedSample;//Set if the record is for a raw sample as opposed to a SFX.
}SfxRecord; }SfxRecord;
class ActorAccessibility { class ActorAccessibility {
public: public:
bool isOn = false; int isOn = 0;
uint64_t nextActorID = 0; uint64_t nextActorID = 0;
SupportedActors_t supportedActors; SupportedActors_t supportedActors;
TrackedActors_t trackedActors; TrackedActors_t trackedActors;
@ -64,6 +67,7 @@ class ActorAccessibility {
AccessibleAudioEngine* audioEngine; AccessibleAudioEngine* audioEngine;
SfxExtractor sfxExtractor; SfxExtractor sfxExtractor;
std::unordered_map<s16, SfxRecord> sfxMap;//Maps internal sfx to external (prerendered) resources. std::unordered_map<s16, SfxRecord> sfxMap;//Maps internal sfx to external (prerendered) resources.
std::unordered_map<std::string, SfxRecord> sampleMap;//Similar to above, but this one maps raw audio samples as opposed to SFX.
int extractSfx = 0; int extractSfx = 0;
}; };
static ActorAccessibility* aa; static ActorAccessibility* aa;
@ -100,6 +104,9 @@ void ActorAccessibility_OnGameStillFrozen()
void ActorAccessibility_Init() { void ActorAccessibility_Init() {
aa = new ActorAccessibility(); aa = new ActorAccessibility();
aa->isOn = CVarGetInteger("gA11yAudioInteraction", 0);
if (!aa->isOn)
return;
aa->extractSfx = CVarGetInteger("gExtractSfx", 0); aa->extractSfx = CVarGetInteger("gExtractSfx", 0);
if (aa->extractSfx) if (aa->extractSfx)
freezeGame = true; freezeGame = true;
@ -126,7 +133,7 @@ void ActorAccessibility_Shutdown() {
policy->pitch = 1.5; policy->pitch = 1.5;
policy->runsAlways = false; policy->runsAlways = false;
policy->sound = sfx; policy->sound = sfx;
policy->volume = 1.0; policy->volume = 0.5;
policy->initUserData = NULL; policy->initUserData = NULL;
policy->cleanupUserData = NULL; policy->cleanupUserData = NULL;
policy->pitchModifier = 0.1; 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); Audio_PlaySoundGeneral(sfxId, &actor->projectedPos, 4, &actor->currentPitch, &actor->currentVolume, &actor->currentReverb);
} }
const char* ActorAccessibility_MapSfxToExternalAudio(s16 sfxId); const char* ActorAccessibility_MapSfxToExternalAudio(s16 sfxId);
void ActorAccessibility_PlaySound(void* handle, int slot, s16 sfxId, bool looping) void ActorAccessibility_PlaySound(void* handle, int slot, s16 sfxId, bool looping)
{ {
const char* path = ActorAccessibility_MapSfxToExternalAudio(sfxId); const char* path = ActorAccessibility_MapSfxToExternalAudio(sfxId);
if (path == NULL)
return;
aa->audioEngine->playSound((uintptr_t)handle, slot, path, looping); 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) void ActorAccessibility_StopSound(void* handle, int slot)
{ {
aa->audioEngine->stopSound((uintptr_t) handle, 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); 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) void ActorAccessibility_PlaySoundForActor(AccessibleActor* actor, int slot, s16 sfxId, bool looping)
{ {
if (slot < 0 || slot > NUM_MANAGED_SOUND_SLOTS) if (slot < 0 || slot > NUM_MANAGED_SOUND_SLOTS)
return; return;
ActorAccessibility_PlaySound(actor, slot, sfxId, looping); ActorAccessibility_PlaySound(actor, slot, sfxId, looping);
ActorAccessibility_SetSoundPitch(actor, slot, actor->policy.pitch); ActorAccessibility_ConfigureSoundForActor(actor, slot);
ActorAccessibility_SetPitchBehindModifier(actor, slot, actor->policy.pitchModifier);
ActorAccessibility_SetSoundPos(actor, slot, &actor->projectedPos, actor->xzDistToPlayer, }
actor->policy.distance); void ActorAccessibility_PlaySampleForActor(AccessibleActor* actor, int slot, const char* name, bool looping)
ActorAccessibility_SetSoundVolume(actor, slot, actor->policy.volume); {
actor->managedSoundSlots[slot] = true; 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) void ActorAccessibility_StopSoundForActor(AccessibleActor* actor, int slot)
@ -509,9 +539,14 @@ int ActorAccessibility_GetRandomStartingFrameCount(int min, int max) {
return true; return true;
} }
void ActorAccessibility_ShutdownAudio() { void ActorAccessibility_ShutdownAudio() {
if (aa->isOn == 0)
return;
delete aa->audioEngine; delete aa->audioEngine;
} }
void ActorAccessibility_MixAccessibleAudioWithGameAudio(int16_t* ogBuffer, uint32_t nFrames) { void ActorAccessibility_MixAccessibleAudioWithGameAudio(int16_t* ogBuffer, uint32_t nFrames) {
if (aa->isOn == 0)
return;
aa->audioEngine->mix(ogBuffer, nFrames); aa->audioEngine->mix(ogBuffer, nFrames);
} }
@ -532,18 +567,48 @@ return NULL;//Resource doesn't exist, user's gotta run the extractor.
tempRecord.resource = res; tempRecord.resource = res;
std::stringstream ss; std::stringstream ss;
ss << std::setw(4) << std::setfill('0') << std::hex << sfxId; ss << std::setw(4) << std::setfill('0') << std::hex << sfxId;
tempRecord.hexName = ss.str(); tempRecord.path = ss.str();
aa->sfxMap[sfxId] = tempRecord; aa->sfxMap[sfxId] = tempRecord;
record = &aa->sfxMap[sfxId]; 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()); record->resource->Buffer.size());
} else } else
record = &it->second; 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<s16*>(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. // Call once per frame to tell the audio engine to start working on the latest batch of queued instructions.
void ActorAccessibility_PrepareNextAudioFrame() { void ActorAccessibility_PrepareNextAudioFrame() {

View file

@ -99,6 +99,9 @@ void ActorAccessibility_PlaySpecialSound(AccessibleActor* actor, s16 sfxId);
*looping: whether to play the sound just once or on a continuous loop. *looping: whether to play the sound just once or on a continuous loop.
*/ */
void ActorAccessibility_PlaySound(void* actor, int slot, s16 sfxId, bool looping); 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. //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_StopSound(void* handle, int slot);
void ActorAccessibility_StopAllSounds(void* handle); 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_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_StopSoundForActor(AccessibleActor* actor, int slot);
void ActorAccessibility_StopAllSoundsForActor(AccessibleActor* actor); void ActorAccessibility_StopAllSoundsForActor(AccessibleActor* actor);
f32 ActorAccessibility_ComputeCurrentVolume(f32 maxDistance, f32 xzDistToPlayer); f32 ActorAccessibility_ComputeCurrentVolume(f32 maxDistance, f32 xzDistToPlayer);

View file

@ -93,8 +93,6 @@ void SfxExtractor::renderOutput() {
} }
void SfxExtractor::setup() { void SfxExtractor::setup() {
try { 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()); 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. // Kill the audio thread so we can take control.
@ -158,6 +156,8 @@ void SfxExtractor::finished() {
CVarClear("gExtractSfx"); CVarClear("gExtractSfx");
CVarSave(); CVarSave();
archive = nullptr; archive = nullptr;
freezeGame = false;
Audio_QueueSeqCmd(NA_BGM_TITLE); Audio_QueueSeqCmd(NA_BGM_TITLE);
if (currentStep == STEP_ERROR || currentStep == STEP_ERROR_OTR) { if (currentStep == STEP_ERROR || currentStep == STEP_ERROR_OTR) {
@ -171,7 +171,6 @@ void SfxExtractor::finished() {
SpeechSynthesizer::Instance->Speak(ss.str().c_str(), GetLanguageCode()); SpeechSynthesizer::Instance->Speak(ss.str().c_str(), GetLanguageCode());
} else } else
Audio_PlayFanfare(NA_BGM_ITEM_GET); Audio_PlayFanfare(NA_BGM_ITEM_GET);
freezeGame = false;
} }
void SfxExtractor::maybeGiveProgressReport() { void SfxExtractor::maybeGiveProgressReport() {

View file

@ -10,7 +10,6 @@ class SfxExtractor {
s16 currentSfx; s16 currentSfx;
std::vector<int16_t> tempStorage; // Stores raw audio data for the sfx currently being ripped. std::vector<int16_t> tempStorage; // Stores raw audio data for the sfx currently being ripped.
int16_t* tempBuffer; // Raw pointer to the above vector. int16_t* tempBuffer; // Raw pointer to the above vector.
f32 ogMusicVolume;
int progressMilestones[9]; // Implements progress reports after every 10 percent. int progressMilestones[9]; // Implements progress reports after every 10 percent.
// Check if a buffer contains meaningful audio output. // Check if a buffer contains meaningful audio output.
bool isAllZero(int16_t* buffer, size_t count); bool isAllZero(int16_t* buffer, size_t count);