Custom Sequences (#2066)

* Allows OTRExporter to parse pairs of .seq and .meta files

* Gets added sequences available to SfxEditor and playing in game.

* Some cleanup of the names appearing in the SfxEditor.

* Moves sequence swap lower in the audio command stack.

* Increases temp cache memory available on title/file-select screen.

Certain sequences wouldn't play on the file select and title screen
because they were too large to be cached.

* Introduces workaround for 255 sequence limit.

* Bug fixes and cleanup.

* Fixes bug where fanfares would sometimes disable the sequence player.

* Fixes bug causing certain areas to discard caches when loading enemy music.

* Fixes potential config-related crash by replacing invalid characters.

* Allows custom bgm to play in all BGM categories.

* Properly randomizes the custom tracks.

* Moves custom sequences to a patch OTR.

* If custom music was not loaded, fall back to default values.

* Prevents OOB crash on Synthwave array and adds octave drop feature.

Added octave drop to experimental features, which drops the octave of
a note that is too high for the audio engine to actually play. Without
this, some custom sequences have notes which cap at a specific value
and sound terrible. At least with this they will still harmonize with
the other notes. Experimental tab added to the SfxEditor to house
the checkbox for the octave drop feature.

* Adds more pool memory for a few tracks that couldn't fit.

* Some cleanup on the generated music archive process.

* Fixes missed memory boost from earlier.

* Adds ability to remove enemy proximity music.

* Applies correct cache policy to fanfares to prevent unloading sequences.

* Removes case-sensitiveness of the sequence type.

* Fixes not reverting to sequence after miniboss.

* Fixes transition to/from miniboss (again) and ocarina bug.

To be clear, fixes the more rampant portable ocarina bug present in my earlier builds, not the authentic one.

* Finally properly fixes transitions between sequences

For miniboss fights and SfxEditor previews.

* Removes unneeded boolean expression.

* Adds randomize button to individual SFX Editor entries.

* Fixes lost woods music overwriting goron city music.

* Plays swapped Hyrule Field music when transitioning to daytime.

* Fixes swapping Gerudo Valley music when transitioning from daytime.

* Updates custom sequence OTRPath to match SequenceOTRizer.

* Reverts changes to OTRExporter in favor of external tool

* Fixes formatting issues.

* Attempts to fix formatting issue in git diff.

* Should actually fix formatting issues.

* Should fix mac/linux exclusive build error.

* Fixes segfault on macos.

* sort custom seqs

* Fixes audioseq crash when under 255 seqs

* Removes magic numbers.

* Removes commented out code.

* fixes formatting in SfxEditor.h

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>

* Cleans up the one hardcoded QueueSeqCmd call.

* Fixes unneeded erroneous memory boost applied earlier.

* Applies additional formatting/cleanliness suggestions from review

* Fixes small logic bug

Co-authored-by: RaelCappra <rael.cappra@gmail.com>
Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
This commit is contained in:
Christopher Leggett 2022-12-08 23:07:45 -05:00 committed by GitHub
parent 4a35b9e798
commit 8c8c761726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 242 additions and 69 deletions

View File

@ -22,6 +22,8 @@
#define CALC_RESAMPLE_FREQ(sampleRate) ((float)sampleRate / (s32)gAudioContext.audioBufferParameters.frequency)
#define MAX_SEQUENCES 0x400
extern char* fontMap[256];
typedef enum {
@ -259,7 +261,7 @@ typedef struct {
/* 0x001 */ u8 state;
/* 0x002 */ u8 noteAllocPolicy;
/* 0x003 */ u8 muteBehavior;
/* 0x004 */ u8 seqId;
/* 0x004 */ u16 seqId;
/* 0x005 */ u8 defaultFont;
/* 0x006 */ u8 unk_06[1];
/* 0x007 */ s8 playerIdx;
@ -913,7 +915,7 @@ typedef struct {
/* 0x342C */ AudioPoolSplit3 temporaryCommonPoolSplit;
/* 0x3438 */ u8 sampleFontLoadStatus[0x30];
/* 0x3468 */ u8 fontLoadStatus[0x30];
/* 0x3498 */ u8 seqLoadStatus[0x80];
/* 0x3498 */ u8 seqLoadStatus[MAX_SEQUENCES];
/* 0x3518 */ volatile u8 resetStatus;
/* 0x3519 */ u8 audioResetSpecIdToLoad;
/* 0x351C */ s32 audioResetFadeOutFramesLeft;
@ -941,6 +943,8 @@ typedef struct {
/* 0x5C3C */ OSMesg audioResetMesgs[1];
/* 0x5C40 */ OSMesg cmdProcMsgs[4];
/* 0x5C50 */ AudioCmd cmdBuf[0x100];
u16 seqToPlay[4];
u8 seqReplaced[4];
} AudioContext; // size = 0x6450
typedef struct {
@ -1105,7 +1109,7 @@ typedef enum {
typedef struct {
char* seqData;
int32_t seqDataSize;
uint8_t seqNumber;
uint16_t seqNumber;
uint8_t medium;
uint8_t cachePolicy;
int32_t numFonts;

View File

@ -6,13 +6,16 @@
#include <ImGuiImpl.h>
#include <functions.h>
#include "../randomizer/3drando/random.hpp"
#include "../../OTRGlobals.h"
#include <Utils/StringHelper.h>
#include "../../UIWidgets.hpp"
Vec3f pos = { 0.0f, 0.0f, 0.0f };
f32 freqScale = 1.0f;
s8 reverbAdd = 0;
// {originalSequenceId, {label, sfxKey, category},
const std::map<u16, std::tuple<std::string, std::string, SeqType>> sequenceMap = {
std::map<u16, std::tuple<std::string, std::string, SeqType>> sfxEditorSequenceMap = {
{NA_BGM_FIELD_LOGIC, {"Hyrule Field", "NA_BGM_FIELD_LOGIC", SEQ_BGM_WORLD}},
{NA_BGM_DUNGEON, {"Dodongo's Cavern", "NA_BGM_DUNGEON", SEQ_BGM_WORLD}},
{NA_BGM_KAKARIKO_ADULT, {"Kakariko Village (Adult)", "NA_BGM_KAKARIKO_ADULT", SEQ_BGM_WORLD}},
@ -39,7 +42,6 @@ const std::map<u16, std::tuple<std::string, std::string, SeqType>> sequenceMap =
{NA_BGM_GANON_TOWER, {"Ganondorf's Theme", "NA_BGM_GANON_TOWER", SEQ_BGM_WORLD}},
{NA_BGM_LONLON, {"Lon Lon Ranch", "NA_BGM_LONLON", SEQ_BGM_WORLD}},
{NA_BGM_GORON_CITY, {"Goron City", "NA_BGM_GORON_CITY", SEQ_BGM_WORLD}},
{NA_BGM_FIELD_MORNING, {"Hyrule Field Morning Theme", "NA_BGM_FIELD_MORNING", SEQ_BGM_WORLD}},
{NA_BGM_SPIRITUAL_STONE, {"Spiritual Stone Get", "NA_BGM_SPIRITUAL_STONE", SEQ_FANFARE}},
{NA_BGM_OCA_BOLERO, {"Bolero of Fire", "NA_BGM_OCA_BOLERO", SEQ_OCARINA}},
{NA_BGM_OCA_MINUET, {"Minuet of Forest", "NA_BGM_OCA_MINUET", SEQ_OCARINA}},
@ -196,7 +198,7 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
if (ImGui::Button(randomizeAllButton.c_str())) {
std::vector<u16> values;
for (const auto& [value, seqData] : map) {
if (std::get<2>(seqData) == type) {
if (std::get<2>(seqData) & type) {
values.push_back(value);
}
}
@ -204,7 +206,10 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
for (const auto& [defaultValue, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
const std::string cvarKey = "gSfxEditor_" + sfxKey;
if (seqType == type) {
if (seqType & type) {
if (((seqType & SEQ_BGM_CUSTOM) || seqType == SEQ_FANFARE) && defaultValue > MAX_AUTHENTIC_SEQID) {
continue;
}
const int randomValue = values.back();
CVar_SetS32(cvarKey.c_str(), randomValue);
values.pop_back();
@ -216,11 +221,14 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 110.0f);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 190.0f);
for (const auto& [defaultValue, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
if (seqType != type) {
if (~(seqType) & type) {
continue;
}
if (((seqType & SEQ_BGM_CUSTOM) || seqType == SEQ_FANFARE) && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
@ -229,6 +237,7 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
const std::string stopButton = " Stop " + hiddenKey;
const std::string previewButton = "Preview" + hiddenKey;
const std::string resetButton = "Reset" + hiddenKey;
const std::string randomizeButton = "Randomize" + hiddenKey;
const int currentValue = CVar_GetS32(cvarKey.c_str(), defaultValue);
ImGui::TableNextRow();
@ -236,10 +245,11 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
ImGui::Text(name.c_str());
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::BeginCombo(hiddenKey.c_str(), std::get<0>(map.at(currentValue)).c_str())) {
const int initialValue = map.contains(currentValue) ? currentValue : defaultValue;
if (ImGui::BeginCombo(hiddenKey.c_str(), std::get<0>(map.at(initialValue)).c_str())) {
for (const auto& [value, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
if (seqType != type) {
if (~(seqType) & type) {
continue;
}
@ -283,25 +293,53 @@ void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::s
CVar_SetS32(cvarKey.c_str(), defaultValue);
SohImGui::RequestCvarSaveOnNextTick();
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::Button(randomizeButton.c_str())) {
bool valid = false;
uint32_t value;
while (!valid) {
value = Random(2, map.size());
if (map.contains(value)) {
auto [name, sfxKey, seqType] = map.at(value);
if (seqType & type) {
valid = true;
}
}
}
CVar_SetS32(cvarKey.c_str(), value);
SohImGui::RequestCvarSaveOnNextTick();
}
}
ImGui::EndTable();
}
extern "C" u16 SfxEditor_GetReplacementSeq(u16 seqId) {
if (sequenceMap.find(seqId) == sequenceMap.end()) {
// if Hyrule Field Morning is about to play, but Hyrule Field is swapped, get the replacement sequence
// for Hyrule Field instead. Otherwise, leave it alone, so that without any sfx editor modifications we will
// play the normal track as usual.
if (seqId == NA_BGM_FIELD_MORNING) {
if (CVar_GetS32("gSfxEditor_NA_BGM_FIELD_LOGIC", NA_BGM_FIELD_LOGIC) != NA_BGM_FIELD_LOGIC) {
seqId = NA_BGM_FIELD_LOGIC;
}
}
if (sfxEditorSequenceMap.find(seqId) == sfxEditorSequenceMap.end()) {
return seqId;
}
const auto& [name, sfxKey, seqType] = sequenceMap.at(seqId);
const auto& [name, sfxKey, seqType] = sfxEditorSequenceMap.at(seqId);
const std::string cvarKey = "gSfxEditor_" + sfxKey;
const int replacementSeq = CVar_GetS32(cvarKey.c_str(), seqId);
int replacementSeq = CVar_GetS32(cvarKey.c_str(), seqId);
if (!sfxEditorSequenceMap.contains(replacementSeq)) {
replacementSeq = seqId;
}
return static_cast<u16>(replacementSeq);
}
extern "C" u16 SfxEditor_GetReverseReplacementSeq(u16 seqId) {
for (const auto& [id, nameAndsfxKey] : sequenceMap) {
const auto& [name, sfxKey, seqType] = sequenceMap.at(id);
for (const auto& [id, nameAndsfxKey] : sfxEditorSequenceMap) {
const auto& [name, sfxKey, seqType] = sfxEditorSequenceMap.at(id);
const std::string cvarKey = "gSfxEditor_" + sfxKey;
if (CVar_GetS32(cvarKey.c_str(), id) == seqId){
return static_cast<u16>(id);
@ -324,28 +362,56 @@ void DrawSfxEditor(bool& open) {
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", sequenceMap, SEQ_BGM_WORLD);
Draw_SfxTab("backgroundMusic", sfxEditorSequenceMap, SEQ_BGM_WORLD);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Fanfares")) {
Draw_SfxTab("fanfares", sequenceMap, SEQ_FANFARE);
Draw_SfxTab("fanfares", sfxEditorSequenceMap, SEQ_FANFARE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Events")) {
Draw_SfxTab("event", sequenceMap, SEQ_BGM_EVENT);
Draw_SfxTab("event", sfxEditorSequenceMap, SEQ_BGM_EVENT);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Battle Music")) {
Draw_SfxTab("battleMusic", sequenceMap, SEQ_BGM_BATTLE);
Draw_SfxTab("battleMusic", sfxEditorSequenceMap, SEQ_BGM_BATTLE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Ocarina")) {
Draw_SfxTab("instrument", sequenceMap, SEQ_INSTRUMENT);
Draw_SfxTab("ocarina", sequenceMap, SEQ_OCARINA);
Draw_SfxTab("instrument", sfxEditorSequenceMap, SEQ_INSTRUMENT);
Draw_SfxTab("ocarina", sfxEditorSequenceMap, SEQ_OCARINA);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Sound Effects")) {
Draw_SfxTab("sfx", sequenceMap, SEQ_SFX);
Draw_SfxTab("sfx", sfxEditorSequenceMap, SEQ_SFX);
ImGui::EndTabItem();
}
static ImVec2 cellPadding(8.0f, 8.0f);
if (ImGui::BeginTabItem("Options")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
ImGui::BeginTable("Options", 1, ImGuiTableFlags_SizingStretchSame);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginChild("SfxOptions", ImVec2(0, -8))) {
ImGui::PushItemWidth(-FLT_MIN);
UIWidgets::EnhancementCheckbox("Disable Enemy Proximity Music", "gEnemyBGMDisable");
UIWidgets::InsertHelpHoverText(
"Disables the music change when getting close to enemies. Useful for hearing "
"your custom music for each scene more often.");
UIWidgets::PaddedSeparator();
UIWidgets::PaddedText("The following options are experimental and may cause music\nto sound odd or have other undesireable effects.");
UIWidgets::EnhancementCheckbox("Lower Octaves of Unplayable High Notes", "gExperimentalOctaveDrop");
UIWidgets::InsertHelpHoverText("Some custom sequences may have notes that are too high for the game's audio "
"engine to play. Enabling this checkbox will cause these notes to drop a "
"couple of octaves so they can still harmonize with the other notes of the "
"sequence.");
ImGui::PopItemWidth();
}
ImGui::EndChild();
ImGui::EndTable();
ImGui::PopStyleVar(1);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
@ -357,3 +423,22 @@ void InitSfxEditor() {
//Draw the bar in the menu.
SohImGui::AddWindow("Enhancements", "SFX Editor", DrawSfxEditor);
}
extern "C" void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum) {
std::vector<std::string> splitName = StringHelper::Split(otrPath, "/");
std::string fileName = splitName[splitName.size() - 1];
std::vector<std::string> splitFileName = StringHelper::Split(fileName, "_");
std::string sequenceName = splitFileName[0];
SeqType type = SEQ_BGM_CUSTOM;
std::string typeString = splitFileName[splitFileName.size() - 1];
std::locale loc;
for (int i = 0; i < typeString.length(); i++) {
typeString[i] = std::tolower(typeString[i], loc);
}
if (typeString == "fanfare") {
type = SEQ_FANFARE;
}
auto tuple = std::make_tuple(
sequenceName, StringHelper::Replace(StringHelper::Replace(sequenceName, " ", "_"), "~", "-"), type);
sfxEditorSequenceMap.emplace(seqNum, tuple);
}

View File

@ -1,8 +1,13 @@
#pragma once
#include "stdint.h"
void InitSfxEditor();
#ifndef __cplusplus
void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum);
#endif
#define INSTRUMENT_OFFSET 0x81
#define MAX_AUTHENTIC_SEQID 110
enum SeqType {
SEQ_NOSHUFFLE = 0,
@ -14,4 +19,5 @@ enum SeqType {
SEQ_BGM_ERROR = 1 << 5,
SEQ_SFX = 1 << 6,
SEQ_INSTRUMENT = 1 << 7,
SEQ_BGM_CUSTOM = SEQ_BGM_WORLD | SEQ_BGM_EVENT | SEQ_BGM_BATTLE,
};

View File

@ -177,6 +177,10 @@ bool OTRGlobals::HasOriginal() {
return hasOriginal;
}
std::shared_ptr<std::vector<std::string>> OTRGlobals::ListFiles(std::string path) {
return context->GetResourceManager()->ListFiles(path);
}
struct ExtensionEntry {
std::string path;
std::string ext;

View File

@ -10,6 +10,7 @@
#include <Window.h>
#include "Enhancements/savestates.h"
#include "Enhancements/randomizer/randomizer.h"
#include <vector>
const std::string customMessageTableID = "BaseGameOverrides";
@ -27,6 +28,7 @@ public:
bool HasMasterQuest();
bool HasOriginal();
std::shared_ptr<std::vector<std::string>> ListFiles(std::string path);
private:
void CheckSaveFile(size_t sramSize) const;

View File

@ -53,7 +53,7 @@ void AudioHeap_ResetLoadStatus(void) {
}
}
for (i = 0; i < 0x80; i++) {
for (i = 0; i < MAX_SEQUENCES; i++) {
if (gAudioContext.seqLoadStatus[i] != 5) {
gAudioContext.seqLoadStatus[i] = 0;
}

View File

@ -68,22 +68,22 @@ ReverbSettings D_80133420[][3] = {
};
AudioSpec gAudioSpecs[18] = {
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x4000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[1], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[4], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[5], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[6], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[7], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[9], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 2, D_80133420[10], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x2800, 0x2880, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0x4800, 0, 0x4000, 0, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0, 0, 0x4000, 0x4800, 0, 0, 0 },
{ 44100, 1, 22, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 22, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 16, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 22050, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3600, 0x2600, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[1], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[4], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[5], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[6], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[7], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[9], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 2, D_80133420[10], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0x4800, 0, 0x5000, 0, 0, 0, 0 },
{ 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0, 0, 0x5000, 0x4800, 0, 0, 0 },
{ 44100, 1, 22, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 22, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 16, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 22050, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 },
{ 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2600, 0, 0, 0 },
};

View File

@ -5,6 +5,7 @@
#include "ultra64.h"
#include "global.h"
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/sfx-editor/SfxEditor.h"
#define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status))
#define ASYNC_TBLTYPE(v) ((u8)(v >> 16))
@ -76,7 +77,7 @@ void* sUnusedHandler = NULL;
s32 gAudioContextInitalized = false;
char* sequenceMap[256];
char* sequenceMap[MAX_SEQUENCES];
char* fontMap[256];
uintptr_t fontStart;
@ -481,10 +482,14 @@ void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* ret
u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) {
s32 index;
if (seqId == 255)
return NULL;
if (seqId == NA_BGM_DISABLED)
return NULL;
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId);
if (!sequenceMap[newSeqId]){
return NULL;
}
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[newSeqId]);
if (sDat.numFonts == 0)
return NULL;
@ -568,9 +573,10 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) {
AudioSeq_SequencePlayerDisable(seqPlayer);
fontId = 0xFF;
//index = ((u16*)gAudioContext.sequenceFontTable)[seqId];
//numFonts = gAudioContext.sequenceFontTable[index++];
if (gAudioContext.seqReplaced[playerIdx]) {
seqId = gAudioContext.seqToPlay[playerIdx];
}
SequenceData seqData2 = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
for (int i = 0; i < seqData2.numFonts; i++)
@ -984,7 +990,6 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData,
u32 temp_v0;
u32 realId;
realId = AudioLoad_GetRealTableIndex(tableType, id);
switch (tableType) {
case SEQUENCE_TABLE:
if (gAudioContext.seqLoadStatus[realId] == 1) {
@ -1211,6 +1216,12 @@ void AudioLoad_InitSwapFont(void) {
#undef BASE_ROM_OFFSET
int strcmp_sort( const void *str1, const void *str2 ) {
char *const *pp1 = str1;
char *const *pp2 = str2;
return strcmp(*pp1, *pp2);
}
void AudioLoad_Init(void* heap, size_t heapSize) {
char pad[0x48];
s32 numFonts;
@ -1223,6 +1234,8 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
gAudioContext.resetTimer = 0;
memset(&gAudioContext, 0, sizeof(gAudioContext));
memset(gAudioContext.seqToPlay, 0, 8);
memset(gAudioContext.seqReplaced, 0, 8);
switch (osTvType) {
case OS_TV_PAL:
@ -1307,6 +1320,25 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
free(seqList);
int customSeqListSize = 0;
int startingSeqNum = MAX_AUTHENTIC_SEQID; // 109 is the highest vanilla sequence
char** customSeqList = ResourceMgr_ListFiles("custom/music/*", &customSeqListSize);
qsort(customSeqList, customSeqListSize, sizeof(char*), strcmp_sort);
for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) {
int j = i - startingSeqNum;
SfxEditor_AddSequence(customSeqList[j], i);
SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]);
sDat.seqNumber = i;
char* str = malloc(strlen(customSeqList[j]) + 1);
strcpy(str, customSeqList[j]);
sequenceMap[sDat.seqNumber] = str;
}
free(customSeqList);
int fntListSize = 0;
char** fntList = ResourceMgr_ListFiles("audio/fonts*", &fntListSize);
@ -1499,6 +1531,22 @@ s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* isDone) {
size_t size;
seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId);
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId);
if (seqId != newSeqId) {
gAudioContext.seqToPlay[SEQ_PLAYER_BGM_MAIN] = newSeqId;
gAudioContext.seqReplaced[SEQ_PLAYER_BGM_MAIN] = 1;
// This sequence command starts playing a sequence specified by seqId on the main BGM seq player.
// The sequence command is a bitpacked u32 where different bits of the number indicated different parameters.
// What those parameters are is dependent on the first 8 bits which represent an operation.
// First two digits (bits 31-24) - Sequence Command Operation (0x0 = play sequence immediately)
// Next two digits (bits 23-16) - Index of the SeqPlayer to operate on. (0, which is the main BGM player.)
// Next two digits (bits 15-8) - Fade Timer (0 in this case, we don't want any fade-in or out here.)
// Last two digits (bits 7-0) - the sequence ID to play. Not actually sure why it is cast to u16 instead of u8,
// copied this from authentic game code and adapted it. I think it might be so that you can choose to encode the
// fade timer into the seqId if you want to for some reason.
Audio_QueueSeqCmd(0x00000000 | ((u8)SEQ_PLAYER_BGM_MAIN << 24) | ((u8)(0) << 16) | (u16)seqId);
return 0;
}
seqTable = AudioLoad_GetLoadTable(SEQUENCE_TABLE);
slowLoad = &gAudioContext.slowLoads[gAudioContext.slowLoadPos];
if (slowLoad->status == LOAD_STATUS_DONE) {

View File

@ -120,7 +120,11 @@ void Audio_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput)
} else {
noteSubEu->bitField1.hasTwoParts = true;
if (3.99996f < resamplingRateInput) {
resamplingRate = 1.99998f;
if (CVar_GetS32("gExperimentalOctaveDrop", 0)) {
resamplingRate = resamplingRateInput * 0.25;
} else {
resamplingRate = 1.99998f;
}
} else {
resamplingRate = resamplingRateInput * 0.5f;
}
@ -347,7 +351,7 @@ Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) {
}
Drum* Audio_GetDrum(s32 fontId, s32 drumId) {
Drum* drum;
Drum* drum = NULL;
if (fontId == 0xFF) {
return NULL;
@ -360,7 +364,9 @@ Drum* Audio_GetDrum(s32 fontId, s32 drumId) {
SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]);
drum = sf->drums[drumId];
if (drumId < sf->numDrums) {
drum = sf->drums[drumId];
}
if (drum == NULL) {
gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x5000000;
@ -538,6 +544,9 @@ s32 Audio_BuildSyntheticWave(Note* note, SequenceLayer* layer, s32 waveId) {
if (waveId < 128) {
waveId = 128;
}
if (waveId > 136) {
waveId = 136;
}
freqScale = layer->freqScale;
if (layer->portamento.mode != 0 && 0.0f < layer->portamento.extent) {

View File

@ -3,7 +3,7 @@
#include "ultra64.h"
#include "global.h"
extern char* sequenceMap[256];
extern char* sequenceMap[MAX_SEQUENCES];
#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80)
#define PORTAMENTO_MODE(x) ((x).mode & ~0x80)
@ -1063,7 +1063,12 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
if (seqPlayer->defaultFont != 0xFF)
{
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqPlayer->seqId]);
if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
}
u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId);
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
command = sDat.fonts[sDat.numFonts - result - 1];
}
@ -1175,7 +1180,12 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
if (seqPlayer->defaultFont != 0xFF)
{
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqPlayer->seqId]);
if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
}
u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId);
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
// The game apparantely would sometimes do negative array lookups, the result of which would get rejected by AudioHeap_SearchCaches, never
// changing the actual fontid.

View File

@ -134,7 +134,7 @@ u8 sAudioExtraFilter2 = 0;
Vec3f* sSariaBgmPtr = NULL;
f32 D_80130650 = 2000.0f;
u8 sSeqModeInput = 0;
u8 sSeqFlags[0x6E] = {
u8 sSeqFlags[0x6F] = {
0x2, // NA_BGM_GENERAL_SFX
0x1, // NA_BGM_NATURE_BACKGROUND
0, // NA_BGM_FIELD_LOGIC
@ -245,6 +245,7 @@ u8 sSeqFlags[0x6E] = {
0, // NA_BGM_FIRE_BOSS
0x8, // NA_BGM_TIMED_MINI_GAME
0, // NA_BGM_VARIOUS_SFX
1, // NA_BGM_CUSTOM_SEQ
};
s8 sSpecReverbs[20] = { 0, 0, 0, 0, 0, 0, 0, 40, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@ -1701,6 +1702,7 @@ void Audio_OcaSetInstrument(u8 arg0) {
u16 sfxEditorId = arg0 + 0x81;
u16 newArg0 = SfxEditor_GetReplacementSeq(sfxEditorId);
if (newArg0 != sfxEditorId) {
gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1;
arg0 = newArg0 - 0x81;
}
@ -4586,7 +4588,6 @@ s32 func_800F5A58(u8 arg0) {
*/
void func_800F5ACC(u16 seqId) {
u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
curSeqId = SfxEditor_GetReverseReplacementSeq(curSeqId);
if ((curSeqId & 0xFF) != NA_BGM_GANON_TOWER && (curSeqId & 0xFF) != NA_BGM_ESCAPE && curSeqId != seqId) {
Audio_SetSequenceMode(SEQ_MODE_IGNORE);
@ -4648,7 +4649,7 @@ void Audio_PlayFanfare(u16 seqId)
sp26 = func_800FA0B4(SEQ_PLAYER_FANFARE);
sp1C = func_800E5E84(sp26 & 0xFF, &sp20);
sp18 = func_800E5E84(seqId & 0xFF, &sp20);
sp18 = func_800E5E84(seqId, &sp20);
if (!sp1C || !sp18) {
// disable BGM, we're about to null deref!
D_8016B9F4 = 1;

View File

@ -221,7 +221,11 @@ void Audio_ProcessSoundRequest(void)
if (req->sfxId == 0) {
return;
}
req->sfxId = SfxEditor_GetReplacementSeq(req->sfxId);
u16 newSfxId = SfxEditor_GetReplacementSeq(req->sfxId);
if (req->sfxId != newSfxId) {
gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1;
req->sfxId = newSfxId;
}
bankId = SFX_BANK(req->sfxId);
if ((1 << bankId) & D_801333F0) {
AudioDebug_ScrPrt((const s8*)D_80133340, req->sfxId);

View File

@ -106,7 +106,7 @@ void Audio_ProcessSeqCmd(u32 cmd) {
u8 op;
u8 subOp;
u8 playerIdx;
u8 seqId;
u16 seqId;
u8 seqArgs;
u8 found;
u8 port;
@ -369,14 +369,14 @@ extern f32 D_80130F28;
void Audio_QueueSeqCmd(u32 cmd)
{
u8 op = cmd >> 28;
if (op == 0 || op == 2 || op == 12){
u16 oldSeqId = cmd & 0xFFFF;
u16 newSeqId = SfxEditor_GetReplacementSeq(oldSeqId);
if (newSeqId != oldSeqId) {
cmd &= ~0xFFFF;
cmd |= newSeqId;
if (op == 0 || op == 2 || op == 12) {
u8 seqId = cmd & 0xFF;
u8 playerIdx = (cmd >> 24) & 0xFF;
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId);
gAudioContext.seqReplaced[playerIdx] = (seqId != newSeqId);
gAudioContext.seqToPlay[playerIdx] = newSeqId;
cmd |= (seqId & 0xFF);
}
}
sAudioSeqCmds[sSeqCmdWrPos++] = cmd;
}

View File

@ -10271,7 +10271,7 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) {
seqMode = SEQ_MODE_STILL;
}
if (play->actorCtx.targetCtx.bgmEnemy != NULL) {
if (play->actorCtx.targetCtx.bgmEnemy != NULL && !CVar_GetS32("gEnemyBGMDisable", 0)) {
seqMode = SEQ_MODE_ENEMY;
Audio_SetBgmEnemyVolume(sqrtf(play->actorCtx.targetCtx.bgmEnemy->xyzDistToPlayerSq));
}