From 4fbafc27089ce2304cbb91ad309f44f899a06d50 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Fri, 20 May 2022 01:40:45 +0200 Subject: [PATCH] Fixed various audio bugs; DynOS can now detect texture duplicates to decrease generated bin files size (#110) Fixed the following audio bugs: Bug: Rom-hacks sequences don't seem to be affected by volume scaling and muting Fix: Force the BGM sequences to follow the vanilla behavior: Volume can't go higher than default volume Volume is reduced to 31% when the game is paused Audio is stopped when the game is paused outside the Castle levels Bug: (Pointed out by Draco) Mario's voice clips are not replaced by the player's character's in the following instances: fall to death barrier, "here we go" in the ending cutscene, "let's a go" after selecting a star, "okey dokey" after starting the game. Fix: The first two ones now call play_character_sound(m, CHAR_SOUND_...) instead of play_sound(SOUND_MARIO_..., pos). The last two ones couldn't be fixed the same way for two reasons: First, the corresponding sounds were not referenced in the sound table, second, the sound played is always cut-off after a few frames (due to how sm64 resets the sound banks after loading a level). Added SOUND_*_LETS_A_GO and SOUND_*_OKEY_DOKEY sounds for each playable character as Bass samples. Character Bass sounds work the same way as vanilla sounds (i.e. can be played with play_character_sound), but they cannot be prematurely stopped by sm64 sound banks shenanigans. This fixes the cut-off for both the star select and the castle grounds entry, plays the sound corresponding to the player's character, and doesn't need to extend or edit the sound table. DynOS can detect texture duplicates when generating a bin or lvl file. When a duplicate is detected, the name of the original texture node is written instead of the whole PNG data, decreasing significantly the resulting file size. --- Makefile | 4 ++ autogen/lua_definitions/constants.lua | 8 ++- autogen/lua_definitions/structs.lua | 2 + data/dynos.cpp.h | 1 + data/dynos_bin_tex.cpp | 64 +++++++++++++---- docs/lua/constants.md | 4 +- docs/lua/structs.md | 2 + src/audio/effects.c | 10 +++ src/audio/external.c | 22 ++++++ src/audio/external.h | 1 + src/game/characters.c | 48 +++++++++---- src/game/characters.h | 4 ++ src/game/characters_bass_sounds.h | 99 +++++++++++++++++++++++++++ src/game/interaction.c | 2 +- src/game/mario.c | 3 +- src/game/mario_actions_cutscene.c | 2 +- src/menu/star_select.c | 4 +- src/pc/djui/djui_panel_host_message.c | 3 +- src/pc/lua/smlua_cobject_autogen.c | 4 +- src/pc/lua/smlua_constants_autogen.c | 4 +- 20 files changed, 255 insertions(+), 36 deletions(-) create mode 100644 src/game/characters_bass_sounds.h diff --git a/Makefile b/Makefile index e41f221a..59372def 100644 --- a/Makefile +++ b/Makefile @@ -1194,6 +1194,7 @@ ifeq ($(TARGET_N64),1) $(BUILD_DIR)/lib/rsp.o: $(BUILD_DIR)/rsp/rspboot.bin $(BUILD_DIR)/rsp/fast3d.bin $(BUILD_DIR)/rsp/audio.bin endif +$(BUILD_DIR)/src/game/characters.o: $(SOUND_SAMPLE_TABLES) $(SOUND_BIN_DIR)/sound_data.o: $(SOUND_BIN_DIR)/sound_data.ctl.inc.c $(SOUND_BIN_DIR)/sound_data.tbl.inc.c $(SOUND_BIN_DIR)/sequences.bin.inc.c $(SOUND_BIN_DIR)/bank_sets.inc.c $(BUILD_DIR)/levels/scripts.o: $(BUILD_DIR)/include/level_headers.h @@ -1321,6 +1322,9 @@ endif $(BUILD_DIR)/%.table: %.aiff $(call print,Extracting codebook:,$<,$@) $(V)$(AIFF_EXTRACT_CODEBOOK) $< >$@ + $(call print,Piping:,$<,$@.inc.c) + $(V)hexdump -v -e '1/1 "0x%X,"' $< > $@.inc.c + $(V)echo >> $@.inc.c $(BUILD_DIR)/%.aifc: $(BUILD_DIR)/%.table %.aiff $(call print,Encoding VADPCM:,$<,$@) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 0d7c729e..b55e0022 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -2615,7 +2615,13 @@ CHAR_SOUND_SO_LONGA_BOWSER = 40 CHAR_SOUND_IMA_TIRED = 41 --- @type CharacterSound -CHAR_SOUND_MAX = 42 +CHAR_SOUND_LETS_A_GO = 42 + +--- @type CharacterSound +CHAR_SOUND_OKEY_DOKEY = 43 + +--- @type CharacterSound +CHAR_SOUND_MAX = 44 --- @class CharacterType diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 1ae94c1a..028c0e7e 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -269,7 +269,9 @@ --- @field public soundHoohoo integer --- @field public soundHrmm integer --- @field public soundImaTired integer +--- @field public soundLetsAGo integer --- @field public soundMamaMia integer +--- @field public soundOkeyDokey integer --- @field public soundOnFire integer --- @field public soundOoof integer --- @field public soundOoof2 integer diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index 05355e9f..7439dc39 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -11,6 +11,7 @@ extern "C" { #define FUNCTION_CODE (u32) 0x434E5546 #define POINTER_CODE (u32) 0x52544E50 #define LUA_VAR_CODE (u32) 0x5641554C +#define TEX_REF_CODE (u32) 0x52584554 #define MOD_PACK_INDEX 99 diff --git a/data/dynos_bin_tex.cpp b/data/dynos_bin_tex.cpp index 67a675d1..9fcd0902 100644 --- a/data/dynos_bin_tex.cpp +++ b/data/dynos_bin_tex.cpp @@ -159,6 +159,22 @@ void DynOS_Tex_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode) { aNode->mName.Write(aFile); // Data + // Look for texture duplicates + // If that's the case, store the name of the texture node instead of the whole PNG data + // (Don't bother to look for duplicates if there is no data to write) + if (!aNode->mData->mPngData.Empty()) { + for (const auto& _Node : aGfxData->mTextures) { + if (_Node->mLoadIndex < aNode->mLoadIndex && // Check load order: duplicates should reference only an already loaded node + _Node->mData != NULL && // Check node data + aNode->mData->mPngData.Count() == _Node->mData->mPngData.Count() && // Check PNG data lengths + memcmp(aNode->mData->mPngData.begin(), _Node->mData->mPngData.begin(), aNode->mData->mPngData.Count()) == 0) // Check PNG data content + { + WriteBytes(aFile, TEX_REF_CODE); + _Node->mName.Write(aFile); + return; + } + } + } aNode->mData->mPngData.Write(aFile); } @@ -220,19 +236,41 @@ DataNode* DynOS_Tex_Load(FILE *aFile, GfxData *aGfxData) { // Data _Node->mData = New(); _Node->mData->mUploaded = false; - _Node->mData->mPngData.Read(aFile); - if (!_Node->mData->mPngData.Empty()) { - u8 *_RawData = stbi_load_from_memory(_Node->mData->mPngData.begin(), _Node->mData->mPngData.Count(), &_Node->mData->mRawWidth, &_Node->mData->mRawHeight, NULL, 4); - _Node->mData->mRawFormat = G_IM_FMT_RGBA; - _Node->mData->mRawSize = G_IM_SIZ_32b; - _Node->mData->mRawData = Array(_RawData, _RawData + (_Node->mData->mRawWidth * _Node->mData->mRawHeight * 4)); - free(_RawData); - } else { // Probably a palette - _Node->mData->mRawData = Array(); - _Node->mData->mRawWidth = 0; - _Node->mData->mRawHeight = 0; - _Node->mData->mRawFormat = 0; - _Node->mData->mRawSize = 0; + + // Check for the texture ref magic + s32 _FileOffset = (s32) ftell(aFile); + u32 _TexRefCode = ReadBytes(aFile); + if (_TexRefCode == TEX_REF_CODE) { + + // That's a duplicate, find the original node and copy its content + String _NodeName; _NodeName.Read(aFile); + for (const auto& _LoadedNode : aGfxData->mTextures) { + if (_LoadedNode->mName == _NodeName) { + _Node->mData->mPngData = _LoadedNode->mData->mPngData; + _Node->mData->mRawData = _LoadedNode->mData->mRawData; + _Node->mData->mRawWidth = _LoadedNode->mData->mRawWidth; + _Node->mData->mRawHeight = _LoadedNode->mData->mRawHeight; + _Node->mData->mRawFormat = _LoadedNode->mData->mRawFormat; + _Node->mData->mRawSize = _LoadedNode->mData->mRawSize; + break; + } + } + } else { + fseek(aFile, _FileOffset, SEEK_SET); + _Node->mData->mPngData.Read(aFile); + if (!_Node->mData->mPngData.Empty()) { + u8 *_RawData = stbi_load_from_memory(_Node->mData->mPngData.begin(), _Node->mData->mPngData.Count(), &_Node->mData->mRawWidth, &_Node->mData->mRawHeight, NULL, 4); + _Node->mData->mRawFormat = G_IM_FMT_RGBA; + _Node->mData->mRawSize = G_IM_SIZ_32b; + _Node->mData->mRawData = Array(_RawData, _RawData + (_Node->mData->mRawWidth * _Node->mData->mRawHeight * 4)); + free(_RawData); + } else { // Probably a palette + _Node->mData->mRawData = Array(); + _Node->mData->mRawWidth = 0; + _Node->mData->mRawHeight = 0; + _Node->mData->mRawFormat = 0; + _Node->mData->mRawSize = 0; + } } // Append diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 4dda0b0e..91ec5172 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -836,7 +836,9 @@ | CHAR_SOUND_SNORING3 | 39 | | CHAR_SOUND_SO_LONGA_BOWSER | 40 | | CHAR_SOUND_IMA_TIRED | 41 | -| CHAR_SOUND_MAX | 42 | +| CHAR_SOUND_LETS_A_GO | 42 | +| CHAR_SOUND_OKEY_DOKEY | 43 | +| CHAR_SOUND_MAX | 44 | ### [enum CharacterType](#CharacterType) | Identifier | Value | diff --git a/docs/lua/structs.md b/docs/lua/structs.md index ce2ac192..46173ce0 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -436,7 +436,9 @@ | soundHoohoo | `integer` | read-only | | soundHrmm | `integer` | read-only | | soundImaTired | `integer` | read-only | +| soundLetsAGo | `integer` | read-only | | soundMamaMia | `integer` | read-only | +| soundOkeyDokey | `integer` | read-only | | soundOnFire | `integer` | read-only | | soundOoof | `integer` | read-only | | soundOoof2 | `integer` | read-only | diff --git a/src/audio/effects.c b/src/audio/effects.c index 801d98b5..45b67ce7 100644 --- a/src/audio/effects.c +++ b/src/audio/effects.c @@ -63,6 +63,16 @@ static void sequence_channel_process_sound(struct SequenceChannel *seqChannel) { f32 panFromChannel; s32 i; + // Rom-hacks audio fix + // Force the BGM sequence channels to follow the BGM sequence player mute behavior + // Make the audio completely silent if MUTE_BEHAVIOR_STOP_SCRIPT or MUTE_BEHAVIOR_STOP_NOTES is set + if (seqChannel->seqPlayer == &gSequencePlayers[0]) { + seqChannel->muteBehavior = seqChannel->seqPlayer->muteBehavior; + if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)) != 0) { + seqChannel->seqPlayer->muteVolumeScale = 0.f; + } + } + channelVolume = seqChannel->volume * seqChannel->volumeScale * seqChannel->seqPlayer->fadeVolume * seqChannel->seqPlayer->volumeScale; if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_SOFTEN) != 0) { diff --git a/src/audio/external.c b/src/audio/external.c index d6f69053..821d7312 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -2356,6 +2356,21 @@ void play_dialog_sound(u8 dialogID) { void set_sequence_player_volume(s32 player, f32 volume) { gSequencePlayers[player].volumeScale = volume; + + // Rom-hacks audio fix + // Custom sequences tend to ignore volume scaling and muting... + // Force the BGM sequence player to follow the Vanilla behavior: + // - Volume can't go higher than default volume + // - Volume is reduced to 31% when the game is paused + // - Audio is stopped when the game is paused outside the Castle levels + if (player == SEQ_PLAYER_LEVEL && sCurrentBackgroundMusicSeqId != 0xff) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + f32 maxVolume = get_current_background_music_default_volume() / 127.f; + seqPlayer->volume = MIN(seqPlayer->volume, maxVolume); + seqPlayer->fadeVolume = MIN(seqPlayer->fadeVolume, maxVolume); + seqPlayer->muteVolumeScale = 0.31f; + seqPlayer->muteBehavior = MUTE_BEHAVIOR_SOFTEN | ((gCurrCourseNum != 0) * (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)); + } } /** @@ -2509,6 +2524,13 @@ u16 get_current_background_music(void) { return -1; } +u8 get_current_background_music_default_volume(void) { + if (sCurrentBackgroundMusicSeqId != 0xff) { + return sBackgroundMusicDefaultVolume[sCurrentBackgroundMusicSeqId]; + } + return 0; +} + u8 get_current_background_music_target_volume(void) { return sBackgroundMusicTargetVolume; } diff --git a/src/audio/external.h b/src/audio/external.h index 3d3c3fbe..61f5c032 100644 --- a/src/audio/external.h +++ b/src/audio/external.h @@ -53,6 +53,7 @@ void stop_background_music(u16 seqId); void fadeout_background_music(u16 arg0, u16 fadeOut); void drop_queued_background_music(void); u16 get_current_background_music(void); +u8 get_current_background_music_default_volume(void); u8 get_current_background_music_target_volume(void); u8 get_current_background_music_max_target_volume(void); u8 is_current_background_music_volume_lowered(void); diff --git a/src/game/characters.c b/src/game/characters.c index 7405e2ad..96025af9 100644 --- a/src/game/characters.c +++ b/src/game/characters.c @@ -10,7 +10,7 @@ #include "pc/configfile.h" #include "audio/external.h" #include "engine/graph_node.h" -#include "types.h" +#include "characters_bass_sounds.h" extern Gfx mario_cap_seg3_dl_03022F48[]; extern Gfx luigi_cap_seg3_dl_03022F48[]; @@ -87,6 +87,8 @@ struct Character gCharacters[CT_MAX] = { .soundSnoring3 = SOUND_MARIO_SNORING3, .soundSoLongaBowser = SOUND_MARIO_SO_LONGA_BOWSER, .soundImaTired = SOUND_MARIO_IMA_TIRED, + .soundLetsAGo = CHAR_BASS_SOUND(SOUND_MARIO_LETS_A_GO), + .soundOkeyDokey = CHAR_BASS_SOUND(SOUND_MARIO_OKEY_DOKEY), }, [CT_LUIGI] = { @@ -150,6 +152,8 @@ struct Character gCharacters[CT_MAX] = { .soundSnoring3 = SOUND_LUIGI_SNORING3, .soundSoLongaBowser = SOUND_LUIGI_SO_LONGA_BOWSER, .soundImaTired = SOUND_LUIGI_IMA_TIRED, + .soundLetsAGo = CHAR_BASS_SOUND(SOUND_LUIGI_LETS_A_GO), + .soundOkeyDokey = CHAR_BASS_SOUND(SOUND_LUIGI_OKEY_DOKEY), }, [CT_TOAD] = { @@ -213,6 +217,8 @@ struct Character gCharacters[CT_MAX] = { .soundSnoring3 = SOUND_MARIO_SNORING3, .soundSoLongaBowser = SOUND_MARIO_SO_LONGA_BOWSER, .soundImaTired = SOUND_MARIO_IMA_TIRED, + .soundLetsAGo = CHAR_BASS_SOUND(SOUND_MARIO_LETS_A_GO), + .soundOkeyDokey = CHAR_BASS_SOUND(SOUND_MARIO_OKEY_DOKEY), }, [CT_WALUIGI] = { @@ -279,6 +285,8 @@ struct Character gCharacters[CT_MAX] = { .soundSnoring3 = SOUND_LUIGI_SNORING3, .soundSoLongaBowser = SOUND_LUIGI_SO_LONGA_BOWSER, .soundImaTired = SOUND_LUIGI_IMA_TIRED, + .soundLetsAGo = CHAR_BASS_SOUND(SOUND_LUIGI_LETS_A_GO), + .soundOkeyDokey = CHAR_BASS_SOUND(SOUND_LUIGI_OKEY_DOKEY), }, [CT_WARIO] = { @@ -342,6 +350,8 @@ struct Character gCharacters[CT_MAX] = { .soundSnoring3 = SOUND_WARIO_SNORING3, .soundSoLongaBowser = SOUND_WARIO_SO_LONGA_BOWSER, .soundImaTired = SOUND_WARIO_IMA_TIRED, + .soundLetsAGo = CHAR_BASS_SOUND(SOUND_WARIO_LETS_A_GO), + .soundOkeyDokey = CHAR_BASS_SOUND(SOUND_WARIO_OKEY_DOKEY), }, }; @@ -450,29 +460,41 @@ static s32 get_character_sound(struct MarioState* m, enum CharacterSound charact case CHAR_SOUND_SNORING3: return character->soundSnoring3; case CHAR_SOUND_SO_LONGA_BOWSER: return character->soundSoLongaBowser; case CHAR_SOUND_IMA_TIRED: return character->soundImaTired; + case CHAR_SOUND_LETS_A_GO: return character->soundLetsAGo; + case CHAR_SOUND_OKEY_DOKEY: return character->soundOkeyDokey; default: return 0; } } +static void play_character_sound_internal(struct MarioState *m, enum CharacterSound characterSound, u32 offset, u32 flags) { + if (m != NULL && (m->flags & flags) == 0) { + s32 sound = get_character_sound(m, characterSound); + if (sound != 0) { + struct Character* character = get_character(m); + f32 *pos = (m->marioObj != NULL ? m->marioObj->header.gfx.cameraToObject : gGlobalSoundSource); + if ((u32) (sound & CHAR_BASS_MAGIC) == CHAR_BASS_MAGIC) { + CharacterBassSound *cbs = get_character_bass_sound(sound); + if (cbs != NULL) { + play_character_bass_sound(cbs, pos, character->soundFreqScale); + } + } else { + play_sound_with_freq_scale(sound + offset, pos, character->soundFreqScale); + } + } + m->flags |= flags; + } +} + void play_character_sound(struct MarioState* m, enum CharacterSound characterSound) { - s32 sound = get_character_sound(m, characterSound); - if (sound == 0) { return; } - struct Character* character = get_character(m); - play_sound_with_freq_scale(sound, m->marioObj->header.gfx.cameraToObject, character->soundFreqScale); + play_character_sound_internal(m, characterSound, 0, 0); } void play_character_sound_offset(struct MarioState* m, enum CharacterSound characterSound, u32 offset) { - s32 sound = get_character_sound(m, characterSound); - if (sound == 0) { return; } - struct Character* character = get_character(m); - play_sound_with_freq_scale(sound + offset, m->marioObj->header.gfx.cameraToObject, character->soundFreqScale); + play_character_sound_internal(m, characterSound, offset, 0); } void play_character_sound_if_no_flag(struct MarioState* m, enum CharacterSound characterSound, u32 flags) { - if ((m->flags & flags) == 0) { - play_character_sound(m, characterSound); - m->flags |= flags; - } + play_character_sound_internal(m, characterSound, 0, flags); } f32 get_character_anim_offset(struct MarioState* m) { diff --git a/src/game/characters.h b/src/game/characters.h index 05c97d63..8aaf1d5c 100644 --- a/src/game/characters.h +++ b/src/game/characters.h @@ -81,6 +81,8 @@ struct Character { s32 soundSnoring3; s32 soundSoLongaBowser; s32 soundImaTired; + s32 soundLetsAGo; + s32 soundOkeyDokey; }; enum CharacterSound { @@ -126,6 +128,8 @@ enum CharacterSound { CHAR_SOUND_SNORING3, CHAR_SOUND_SO_LONGA_BOWSER, CHAR_SOUND_IMA_TIRED, + CHAR_SOUND_LETS_A_GO, + CHAR_SOUND_OKEY_DOKEY, CHAR_SOUND_MAX // MUST BE LAST }; diff --git a/src/game/characters_bass_sounds.h b/src/game/characters_bass_sounds.h new file mode 100644 index 00000000..a3fe191c --- /dev/null +++ b/src/game/characters_bass_sounds.h @@ -0,0 +1,99 @@ +#include "types.h" +#include "bass/bass.h" +#include "game/camera.h" +#undef min // Redefined in math_util.h, undef it to avoid compiler warnings +#undef max // Redefined in math_util.h, undef it to avoid compiler warnings +#include "engine/math_util.h" + +#define H01(s, i, x) (x * 65599u + (u8) s[(i) < sizeof(s) ? (i) : sizeof(s)]) +#define H04(s, i, x) H01(s, i, H01(s, i + 1, H01(s, i + 2, H01(s, i + 3, x)))) +#define H16(s, i, x) H04(s, i, H04(s, i + 4, H04(s, i + 8, H04(s, i + 12, x)))) +#define H64(s, i, x) H16(s, i, H16(s, i + 16, H16(s, i + 32, H16(s, i + 48, x)))) +#define CHAR_BASS_MAGIC 0xFF000000u +#define CHAR_BASS_SOUND_ID(name) (H64(#name "________________________________________________________________", 0, 0) & ~CHAR_BASS_MAGIC) +#define CHAR_BASS_SOUND(name) (CHAR_BASS_MAGIC | CHAR_BASS_SOUND_ID(name)) +#define CHAR_BASS_SOUND_NOT_LOADED 0xFFFFFFFFu +#define DECL_CHAR_BASS_SOUND_RAW(name, ...) static const u8 sCharacterBassSoundRaw_##name[] = +#define DECL_CHAR_BASS_SOUND(name) { CHAR_BASS_SOUND(name), (s32) sizeof(sCharacterBassSoundRaw_##name), sCharacterBassSoundRaw_##name, CHAR_BASS_SOUND_NOT_LOADED, 0.f } + +// Undef these to avoid naming issues +#undef SOUND_MARIO_LETS_A_GO +#undef SOUND_LUIGI_LETS_A_GO +#undef SOUND_WARIO_LETS_A_GO +#undef SOUND_MARIO_OKEY_DOKEY +#undef SOUND_LUIGI_OKEY_DOKEY +#undef SOUND_WARIO_OKEY_DOKEY + +///////////////// +// Bass sounds // +///////////////// + +DECL_CHAR_BASS_SOUND_RAW(SOUND_MARIO_LETS_A_GO) { +#include "sound/samples/sfx_mario/1A_mario_lets_a_go.table.inc.c" +}; + +DECL_CHAR_BASS_SOUND_RAW(SOUND_LUIGI_LETS_A_GO) { +#include "sound/samples/sfx_custom_luigi/1A.table.inc.c" +}; + +DECL_CHAR_BASS_SOUND_RAW(SOUND_WARIO_LETS_A_GO) { +#include "sound/samples/sfx_custom_wario/1A.table.inc.c" +}; + +DECL_CHAR_BASS_SOUND_RAW(SOUND_MARIO_OKEY_DOKEY) { +#include "sound/samples/sfx_mario_peach/0B_mario_okey_dokey.table.inc.c" +}; + +DECL_CHAR_BASS_SOUND_RAW(SOUND_LUIGI_OKEY_DOKEY) { +#include "sound/samples/sfx_custom_luigi_peach/0B.table.inc.c" +}; + +DECL_CHAR_BASS_SOUND_RAW(SOUND_WARIO_OKEY_DOKEY) { +#include "sound/samples/sfx_custom_wario_peach/0B.table.inc.c" +}; + +/////////////////////// +// Bass sounds table // +/////////////////////// + +typedef struct { s32 sound; s32 size; const u8 *raw; HSAMPLE sample; f32 freq; } CharacterBassSound; +static CharacterBassSound sCharacterBassSounds[] = { + DECL_CHAR_BASS_SOUND(SOUND_MARIO_LETS_A_GO), + DECL_CHAR_BASS_SOUND(SOUND_LUIGI_LETS_A_GO), + DECL_CHAR_BASS_SOUND(SOUND_WARIO_LETS_A_GO), + DECL_CHAR_BASS_SOUND(SOUND_MARIO_OKEY_DOKEY), + DECL_CHAR_BASS_SOUND(SOUND_LUIGI_OKEY_DOKEY), + DECL_CHAR_BASS_SOUND(SOUND_WARIO_OKEY_DOKEY), + { 0, 0, NULL, CHAR_BASS_SOUND_NOT_LOADED, 0.f }, +}; + +/////////////////////////// +// Bass sounds functions // +/////////////////////////// + +static CharacterBassSound *get_character_bass_sound(s32 sound) { + for (CharacterBassSound *cbs = sCharacterBassSounds; cbs->raw != NULL; cbs++) { + if (cbs->sound == sound) { + return cbs; + } + } + return NULL; +} + +static void play_character_bass_sound(CharacterBassSound *cbs, f32 *pos, f32 freqScale) { + if (cbs->sample == CHAR_BASS_SOUND_NOT_LOADED) { + cbs->sample = BASS_SampleLoad(TRUE, cbs->raw, 0, cbs->size, 32, BASS_SAMPLE_OVER_POS); + BASS_SAMPLE info; BASS_SampleGetInfo(cbs->sample, &info); + cbs->freq = info.freq; + } + DWORD handle = BASS_SampleGetChannel(cbs->sample, 0); + f32 dist = vec3f_length(pos); + f32 pan = (get_sound_pan(pos[0], pos[2]) - 0.5f) * 2.f; + f32 intensity = sound_get_level_intensity(dist); + f32 masterVolume = (f32) configMasterVolume / 127.f; + f32 sfxVolume = (f32) configSfxVolume / 127.f; + BASS_ChannelSetAttribute(handle, BASS_ATTRIB_VOL, masterVolume * sfxVolume * intensity * 0.75f); + BASS_ChannelSetAttribute(handle, BASS_ATTRIB_PAN, pan); + BASS_ChannelSetAttribute(handle, BASS_ATTRIB_FREQ, cbs->freq * freqScale); + BASS_ChannelPlay(handle, TRUE); +} diff --git a/src/game/interaction.c b/src/game/interaction.c index a8afe175..c9c9eaec 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -2237,7 +2237,7 @@ void check_death_barrier(struct MarioState *m) { } if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) { - play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); + play_character_sound(m, CHAR_SOUND_WAAAOOOW); } } } diff --git a/src/game/mario.c b/src/game/mario.c index 43ebb2e1..5dba7dc0 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -319,8 +319,7 @@ void play_sound_and_spawn_particles(struct MarioState *m, u32 soundBits, u32 wav return; } - if ((m->flags & MARIO_METAL_CAP) || soundBits == SOUND_ACTION_UNSTUCK_FROM_GROUND - || soundBits == SOUND_MARIO_PUNCH_HOO || soundBits == SOUND_LUIGI_PUNCH_HOO) { + if ((m->flags & MARIO_METAL_CAP) || soundBits == SOUND_ACTION_UNSTUCK_FROM_GROUND) { play_sound(soundBits, m->marioObj->header.gfx.cameraToObject); } else { play_sound(m->terrainSoundAddend + soundBits, m->marioObj->header.gfx.cameraToObject); diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index bed82c7a..31b06302 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -2643,7 +2643,7 @@ static void end_peach_cutscene_star_dance(struct MarioState *m) { cutscene_put_cap_on(m); } if (animFrame == 88 && m->playerIndex == 0) { - play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); + play_character_sound(m, CHAR_SOUND_HERE_WE_GO); } if (!nonMario && animFrame >= 98) { m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN; diff --git a/src/menu/star_select.c b/src/menu/star_select.c index 19f6aeb9..34bd1453 100644 --- a/src/menu/star_select.c +++ b/src/menu/star_select.c @@ -501,7 +501,9 @@ void star_select_finish_selection(void) { #if defined(VERSION_JP) play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); #else - play_sound(SOUND_MENU_STAR_SOUND_LETS_A_GO, gGlobalSoundSource); + if (gMarioState->marioObj) vec3f_copy(gMarioState->marioObj->header.gfx.cameraToObject, gGlobalSoundSource); + play_character_sound(gMarioState, CHAR_SOUND_LETS_A_GO); + play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); #endif #ifdef VERSION_SH queue_rumble_data(60, 70); diff --git a/src/pc/djui/djui_panel_host_message.c b/src/pc/djui/djui_panel_host_message.c index ff700ba9..547d0f35 100644 --- a/src/pc/djui/djui_panel_host_message.c +++ b/src/pc/djui/djui_panel_host_message.c @@ -46,7 +46,8 @@ void djui_panel_host_message_do_host(UNUSED struct DjuiBase* caller) { fake_lvl_init_from_save_file(); extern s16 gChangeLevelTransition; gChangeLevelTransition = gLevelValues.entryLevel; - play_sound(SOUND_MENU_STAR_SOUND_OKEY_DOKEY, gGlobalSoundSource); + if (gMarioState->marioObj) vec3f_copy(gMarioState->marioObj->header.gfx.cameraToObject, gGlobalSoundSource); + play_character_sound(gMarioState, CHAR_SOUND_OKEY_DOKEY); extern void play_transition(s16 transType, s16 time, u8 red, u8 green, u8 blue); play_transition(0x09, 0x14, 0x00, 0x00, 0x00); } diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index 43c71074..03a5de2f 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -293,7 +293,7 @@ static struct LuaObjectField sChainSegmentFields[LUA_CHAIN_SEGMENT_FIELD_COUNT] { "yaw", LVT_S16, offsetof(struct ChainSegment, yaw), false, LOT_NONE }, }; -#define LUA_CHARACTER_FIELD_COUNT 59 +#define LUA_CHARACTER_FIELD_COUNT 61 static struct LuaObjectField sCharacterFields[LUA_CHARACTER_FIELD_COUNT] = { { "animOffsetEnabled", LVT_U8, offsetof(struct Character, animOffsetEnabled), true, LOT_NONE }, { "animOffsetFeet", LVT_F32, offsetof(struct Character, animOffsetFeet), true, LOT_NONE }, @@ -329,7 +329,9 @@ static struct LuaObjectField sCharacterFields[LUA_CHARACTER_FIELD_COUNT] = { { "soundHoohoo", LVT_S32, offsetof(struct Character, soundHoohoo), true, LOT_NONE }, { "soundHrmm", LVT_S32, offsetof(struct Character, soundHrmm), true, LOT_NONE }, { "soundImaTired", LVT_S32, offsetof(struct Character, soundImaTired), true, LOT_NONE }, + { "soundLetsAGo", LVT_S32, offsetof(struct Character, soundLetsAGo), true, LOT_NONE }, { "soundMamaMia", LVT_S32, offsetof(struct Character, soundMamaMia), true, LOT_NONE }, + { "soundOkeyDokey", LVT_S32, offsetof(struct Character, soundOkeyDokey), true, LOT_NONE }, { "soundOnFire", LVT_S32, offsetof(struct Character, soundOnFire), true, LOT_NONE }, { "soundOoof", LVT_S32, offsetof(struct Character, soundOoof), true, LOT_NONE }, { "soundOoof2", LVT_S32, offsetof(struct Character, soundOoof2), true, LOT_NONE }, diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 90212a69..b68a8666 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1071,7 +1071,9 @@ char gSmluaConstants[] = "" "CHAR_SOUND_SNORING3 = 39\n" "CHAR_SOUND_SO_LONGA_BOWSER = 40\n" "CHAR_SOUND_IMA_TIRED = 41\n" -"CHAR_SOUND_MAX = 42\n" +"CHAR_SOUND_LETS_A_GO = 42\n" +"CHAR_SOUND_OKEY_DOKEY = 43\n" +"CHAR_SOUND_MAX = 44\n" "DIALOG_000 = 0\n" "DIALOG_001 = 1\n" "DIALOG_002 = 2\n"