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 e41f221aa..59372def6 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 0d7c729ea..b55e00227 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 1ae94c1a0..028c0e7e0 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 05355e9f8..7439dc397 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 67a675d1f..9fcd09026 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 4dda0b0ec..91ec51720 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 ce2ac1925..46173ce09 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 801d98b51..45b67ce7f 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 d6f690532..821d73129 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 3d3c3fbe6..61f5c0320 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 7405e2adb..96025af90 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 05c97d631..8aaf1d5c3 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 000000000..a3fe191c5 --- /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 a8afe1753..c9c9eaec4 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 43ebb2e14..5dba7dc05 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 bed82c7a4..31b063028 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 19f6aeb9e..34bd1453f 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 ff700ba96..547d0f359 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 43c71074b..03a5de2fd 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 90212a695..b68a86662 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"