diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25f966d11..1b3b169e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -148,6 +148,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_mapuser.c k_powerup.cpp k_hitlag.c + music.cpp + music_manager.cpp ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index fa09c1ee4..65cc8c64e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -63,6 +63,7 @@ #include "k_vote.h" #include "k_serverstats.h" #include "k_zvote.h" +#include "music.h" // cl loading screen #include "v_video.h" @@ -2747,7 +2748,10 @@ static void Command_connect(void) // Menu restore state. restoreMenu = &PLAY_MP_OptSelectDef; - S_ChangeMusicInternal("NETMD2", true); + + Music_Remap("menu", "NETMD2"); + Music_Play("menu"); + if (setup_numplayers == 0) { setup_numplayers = 1; diff --git a/src/d_main.c b/src/d_main.c index 6cb34996d..ec81b9458 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -83,6 +83,7 @@ #include "k_podium.h" #include "k_vote.h" #include "k_serverstats.h" +#include "music.h" #ifdef HWRENDER #include "hardware/hw_main.h" // 3D View Rendering @@ -926,6 +927,8 @@ void D_SRB2Loop(void) } #endif + Music_Tick(); + // Fully completed frame made. finishprecise = I_GetPreciseTime(); if (!singletics) @@ -1015,8 +1018,7 @@ void D_ClearState(void) // void D_StartTitle(void) { - if (!demo.title) - S_StopMusic(); + Music_StopAll(); D_ClearState(); F_StartTitleScreen(); @@ -1654,6 +1656,9 @@ void D_SRB2Main(void) I_InitMusic(); S_InitSfxChannels(cv_soundvolume.value); } + + Music_Init(); + CON_SetLoadingProgress(LOADED_SINITSFXCHANNELS); S_InitMusicDefs(); diff --git a/src/deh_tables.c b/src/deh_tables.c index 211b3dcb2..a7a0ada1e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6609,14 +6609,6 @@ struct int_const_s const INT_CONST[] = { {"int_score",int_score}, {"int_scoreortimeattack", int_scoreortimeattack}, - // Jingles (jingletype_t) - {"JT_NONE",JT_NONE}, - {"JT_OTHER",JT_OTHER}, - {"JT_MASTER",JT_MASTER}, - - {"JT_INVINCIBILITY",JT_INVINCIBILITY}, - {"JT_GROW",JT_GROW}, - // Overlay exception settings {"OV_DONTSCREENOFFSET", OV_DONTSCREENOFFSET}, {"OV_DONT3DOFFSET", OV_DONT3DOFFSET}, diff --git a/src/f_finale.c b/src/f_finale.c index ccb8fbb30..6f3dcaaf6 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -46,6 +46,7 @@ // SRB2Kart #include "k_menu.h" #include "k_grandprix.h" +#include "music.h" // Stage of animation: // 0 = text, 1 = art screen @@ -298,8 +299,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset void F_StartIntro(void) { - S_StopMusicCredit(); - if (gamestate) { F_WipeStartScreen(); @@ -308,7 +307,7 @@ void F_StartIntro(void) F_RunWipe(wipe_intro_toblack, wipedefs[wipe_intro_toblack], false, "FADEMAP0", false, false); } - S_StopMusic(); + Music_StopAll(); S_StopSounds(); if (introtoplay) @@ -331,7 +330,8 @@ void F_StartIntro(void) intro_scenenum = 0; finalecount = animtimer = skullAnimCounter = stoptimer = 0; timetonext = introscenetime[intro_scenenum]; - S_StopMusic(); + + Music_StopAll(); } // @@ -701,11 +701,10 @@ void F_StartCredits(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - S_StopMusic(); + Music_StopAll(); S_StopSounds(); - S_ChangeMusicInternal("_creds", false); - S_ShowMusicCredit(); + Music_Play("credits"); finalecount = 0; animtimer = 0; @@ -907,7 +906,9 @@ UINT16 finaleemeralds = 0; void F_StartGameEvaluation(void) { - S_FadeMusic(0, MUSICRATE/4); + Music_DelayEnd("credits", TICRATE/4); + Music_Tick(); // it needs to fade out right now + S_StopMusicCredit(); // Credits option in extras menu @@ -1253,7 +1254,8 @@ void F_GameEvaluationTicker(void) if (finalecount == 1) { // sitting on that distant _shore - S_ChangeMusicInternal("_SHORE", false); + Music_Remap("shore", "_SHORE"); + Music_Play("shore"); } } else @@ -1261,7 +1263,8 @@ void F_GameEvaluationTicker(void) if (finalecount == 1) { // _drift across open waters - S_ChangeMusicInternal("_DRIFT", false); + Music_Remap("shore", "_DRIFT"); + Music_Play("shore"); } } @@ -1766,8 +1769,12 @@ void F_TitleScreenTicker(boolean run) { if (finalecount == 0) { - // Now start the music - S_ChangeMusicInternal("_title", looptitle); + if (!Music_Playing("title")) + { + // Now start the music + Music_Loop("title", looptitle); + Music_Play("title"); + } } else if (menumessage.active) { @@ -1956,7 +1963,7 @@ void F_WaitingPlayersTicker(void) // dumb hack, only start the music on the 1st tick so if you instantly go into the map you aren't hearing a tic of music if (finalecount == 2) - S_ChangeMusicInternal("WAIT2J", true); + Music_Play("wait"); } void F_WaitingPlayersDrawer(void) @@ -2002,11 +2009,14 @@ static void F_AdvanceToNextScene(void) picxpos = cutscenes[cutnum]->scene[scenenum].xcoord[picnum]; picypos = cutscenes[cutnum]->scene[scenenum].ycoord[picnum]; + // FIXME - port to new music system +#if 0 if (cutscenes[cutnum]->scene[scenenum].musswitch[0]) S_ChangeMusicEx(cutscenes[cutnum]->scene[scenenum].musswitch, cutscenes[cutnum]->scene[scenenum].musswitchflags, cutscenes[cutnum]->scene[scenenum].musicloop, cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0); +#endif // Fade to the next F_NewCutscene(cutscenes[cutnum]->scene[scenenum].text); @@ -2075,6 +2085,8 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset animtimer = cutscenes[cutnum]->scene[0].picduration[0]; // Picture duration stoptimer = 0; + // FIXME - port to new music system +#if 0 if (cutscenes[cutnum]->scene[0].musswitch[0]) S_ChangeMusicEx(cutscenes[cutnum]->scene[0].musswitch, cutscenes[cutnum]->scene[0].musswitchflags, @@ -2082,6 +2094,7 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0); else S_StopMusic(); +#endif S_StopSounds(); } @@ -2365,11 +2378,14 @@ static void F_AdvanceToNextPage(void) picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum]; animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum]; + // FIXME - port to new music system +#if 0 // music change if (textprompts[cutnum]->page[scenenum].musswitch[0]) S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch, textprompts[cutnum]->page[scenenum].musswitchflags, textprompts[cutnum]->page[scenenum].musicloop); +#endif } } @@ -2453,11 +2469,14 @@ void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postex picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum]; animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum]; + // FIXME - port to new music system +#if 0 // music change if (textprompts[cutnum]->page[scenenum].musswitch[0]) S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch, textprompts[cutnum]->page[scenenum].musswitchflags, textprompts[cutnum]->page[scenenum].musicloop); +#endif // get the calling player if (promptblockcontrols && mo && mo->player) diff --git a/src/g_game.c b/src/g_game.c index 1de396135..d4b916f4a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -69,6 +69,7 @@ #include "k_vote.h" #include "k_serverstats.h" #include "k_zvote.h" +#include "music.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2402,7 +2403,7 @@ void G_Ticker(boolean run) musiccountdown--; if (musiccountdown == 1) { - S_ChangeMusicInternal("racent", true); + Music_Play("intermission"); } else if (musiccountdown == (MUSICCOUNTDOWNMAX - (3*TICRATE)/2)) { @@ -2410,6 +2411,8 @@ void G_Ticker(boolean run) } } + P_InvincGrowMusic(); + K_TickMidVote(); } } @@ -2510,7 +2513,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 itemtype; INT32 itemamount; INT32 growshrinktimer; - boolean songcredit = false; UINT16 nocontrol; INT32 khudfault; INT32 kickstartaccel; @@ -2850,11 +2852,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (exiting) return; - - P_RestoreMusic(p); - - if (songcredit) - S_ShowMusicCredit(); } // @@ -4695,7 +4692,7 @@ void G_EndGame(void) // In a netgame, don't unwittingly boot everyone. if (netgame) { - S_StopMusic(); + Music_StopAll(); G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat if (server) diff --git a/src/k_kart.c b/src/k_kart.c index 274ce9da6..a7596bfde 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -47,6 +47,7 @@ #include "k_podium.h" #include "k_powerup.h" #include "k_hitlag.h" +#include "music.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -3843,8 +3844,6 @@ void K_RemoveGrowShrink(player_t *player) } player->growshrinktimer = 0; - - P_RestoreMusic(player); } boolean K_IsBigger(mobj_t *compare, mobj_t *other) @@ -4380,7 +4379,7 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A { P_DamageMobj(player->mo, inflictor, source, 1, DMG_INSTAKILL); player->SPBdistance = 0; - S_StopMusic(); + Music_StopAll(); } spbMultiplier = inflictor->movefactor; @@ -6197,18 +6196,12 @@ void K_DoInvincibility(player_t *player, tic_t time) P_SetScale(overlay, player->mo->scale); } - if (P_IsLocalPlayer(player) == true && player->invincibilitytimer == 0) - { - S_ChangeMusicSpecial("kinvnc"); - } - else //used to be "if (P_IsDisplayPlayer(player) == false)" + if (P_IsLocalPlayer(player) == false) { S_StartSound(player->mo, sfx_alarmi); } player->invincibilitytimer += time; - - P_RestoreMusic(player); } void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) @@ -11281,25 +11274,15 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } - if (player->invincibilitytimer > 0) - { - ; // invincibility has priority in P_RestoreMusic, no point in starting here - } - else if (P_IsLocalPlayer(player) == true) - { - if (player->growshrinktimer < 1) - S_ChangeMusicSpecial("kgrow"); - } - else //used to be "if (P_IsDisplayPlayer(player) == false)" + if (P_IsLocalPlayer(player) == false && player->invincibilitytimer == 0) { + // don't play this if the player has invincibility -- that takes priority S_StartSound(player->mo, sfx_alarmg); } player->growshrinktimer = max(0, player->growshrinktimer); player->growshrinktimer += ((gametyperules & GTR_CLOSERPLAYERS) ? 8 : 12) * TICRATE; - P_RestoreMusic(player); - S_StartSound(player->mo, sfx_kc5a); player->itemamount--; @@ -11934,7 +11917,7 @@ void K_CheckSpectateStatus(boolean considermapreset) // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled if (i > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) { - S_ChangeMusicInternal("chalng", false); // COME ON + Music_Play("comeon"); // COME ON mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1b4bddb01..8a596b663 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -16,6 +16,7 @@ #include "f_finale.h" #include "m_misc.h" #include "m_cond.h" +#include "music.h" #ifdef PC_DOS #include // for snprintf @@ -366,7 +367,7 @@ boolean M_Responder(event_t *ev) return true; } -#define NotCurrentlyPlaying(desiredname) (!S_MusicPlaying() || strcmp(desiredname, S_MusicName())) +#define NotCurrentlyPlaying(desiredname) strcmp(desiredname, Music_CurrentSong()) void M_PlayMenuJam(void) { @@ -375,8 +376,7 @@ void M_PlayMenuJam(void) if (challengesmenu.pending) { - S_StopMusic(); - S_StopMusicCredit(); + Music_StopAll(); musicstatepermitted = true; return; @@ -391,16 +391,15 @@ void M_PlayMenuJam(void) { if (refMenu->music[0] == '.' && refMenu->music[1] == '\0') { - S_StopMusic(); - S_StopMusicCredit(); + Music_StopAll(); return; } else if (override == 0) { if (NotCurrentlyPlaying(refMenu->music)) { - S_ChangeMusicInternal(refMenu->music, true); - S_ShowMusicCredit(); + Music_Remap("menu", refMenu->music); + Music_Play("menu"); } return; } @@ -416,8 +415,8 @@ void M_PlayMenuJam(void) if (refMenu != NULL && NotCurrentlyPlaying(overridetotrack[override - 1])) { - S_ChangeMusicInternal(overridetotrack[override - 1], true); - S_ShowMusicCredit(); + Music_Remap("menu", overridetotrack[override - 1]); + Music_Play("menu"); if (override < GDMUSIC_KEEPONMENU) gamedata->musicstate = GDMUSIC_NONE; @@ -435,8 +434,8 @@ void M_PlayMenuJam(void) if (!NotCurrentlyPlaying(cv_menujam.string)) return; - S_ChangeMusicInternal(cv_menujam.string, true); - S_ShowMusicCredit(); + Music_Remap("menu", cv_menujam.string); + Music_Play("menu"); } #undef IsCurrentlyPlaying @@ -574,6 +573,8 @@ void M_StartControlPanel(void) modeattacking = ATTACKING_NONE; } + Music_Stop("title"); + if (cv_currprofile.value == -1) // Only ask once per session. { // Make sure the profile data is ready now since we need to select a profile. @@ -596,7 +597,8 @@ void M_StartControlPanel(void) CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. // Ambient ocean sounds - S_ChangeMusicInternal("_OCEAN", true); + Music_Remap("menu_nocred", "_OCEAN"); + Music_Play("menu_nocred"); } else { diff --git a/src/k_vote.c b/src/k_vote.c index af5d430bb..730300dd5 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -51,6 +51,7 @@ #include "k_pwrlv.h" #include "k_grandprix.h" #include "k_color.h" +#include "music.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -1174,7 +1175,7 @@ static void Y_TickVoteRoulette(void) vote.roulette.endOffset++; } - S_ChangeMusicInternal("voteeb", false); + Music_Play("vote_end"); break; } } @@ -1461,8 +1462,7 @@ void Y_VoteTicker(void) if (vote.tic == 0) { - S_ChangeMusicInternal("vote", true); - S_ShowMusicCredit(); + Music_Play("vote"); } if (g_pickedVote != VOTE_NOT_PICKED) @@ -1728,13 +1728,13 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) { // Only one unique vote, so just end it immediately. vote.endtic = vote.tic + (5*TICRATE); - S_ChangeMusicInternal("voteeb", false); + Music_Play("vote_end"); Y_VoteStops(pick, level); break; } default: { - S_ChangeMusicInternal("voteea", true); + Music_Play("vote_suspense"); break; } } diff --git a/src/m_cheat.c b/src/m_cheat.c index cb424c6b7..ecfb9a79c 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1018,7 +1018,6 @@ void Command_ObjectPlace_f(void) op_oldflags1 = mobjinfo[MT_PLAYER].flags; ++players[0].lives; players[0].playerstate = PST_LIVE; - P_RestoreMusic(&players[0]); } else op_oldstate = (statenum_t)(players[0].mo->state-states); diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index e50c67ef0..4ca2363e2 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -4,6 +4,7 @@ #include "../k_menu.h" #include "../m_cond.h" // Condition Sets #include "../m_random.h" // And just some randomness for the exits. +#include "../music.h" #include "../z_zone.h" #include "../r_skins.h" #include "../s_sound.h" @@ -220,7 +221,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) if ((challengesmenu.pending = (newunlock != MAXUNLOCKABLES))) { - S_StopMusic(); + Music_StopAll(); MISC_ChallengesDef.prevMenu = desiredmenu; } diff --git a/src/menus/extras-wrong.c b/src/menus/extras-wrong.c index 4d4befc22..fc613b7cc 100644 --- a/src/menus/extras-wrong.c +++ b/src/menus/extras-wrong.c @@ -4,6 +4,7 @@ #include "../k_menu.h" #include "../s_sound.h" #include "../m_random.h" +#include "../music.h" #include "../r_skins.h" struct wrongwarp_s wrongwarp; @@ -22,7 +23,8 @@ void M_WrongWarp(INT32 choice) M_SetupNextMenu(&MISC_WrongWarpDef, false); // Done here to avoid immediate music credit - S_ChangeMusicInternal("YEAWAY", true); + Music_Remap("menu_nocred", "YEAWAY"); + Music_Play("menu_nocred"); } static void M_WrongWarpTick(void) diff --git a/src/music.cpp b/src/music.cpp new file mode 100644 index 000000000..533c925d9 --- /dev/null +++ b/src/music.cpp @@ -0,0 +1,322 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include "music_manager.hpp" +#include "music_tune.hpp" + +#include "doomtype.h" +#include "music.h" + +using namespace srb2::music; + +namespace +{ + +TuneManager g_tunes; + +}; // namespace + +void Music_Init(void) +{ + { + Tune& tune = g_tunes.insert("level"); + + tune.priority = 1; + tune.resume_fade_in = 750; + tune.sync = true; + tune.credit = true; + } + + { + Tune& tune = g_tunes.insert("position"); + + tune.priority = 10; + tune.fade_out = 3500; + } + + { + Tune& tune = g_tunes.insert("grow"); + + tune.song = "kgrow"; + tune.priority = 20; + tune.resume_fade_in = 200; + } + + { + Tune& tune = g_tunes.insert("invinc"); + + tune.song = "kinvnc"; + tune.priority = 21; + } + + { + Tune& tune = g_tunes.insert("finish"); + + tune.priority = 30; + tune.loop = false; + } + + { + Tune& tune = g_tunes.insert("comeon"); + + tune.song = "chalng"; + tune.priority = 35; + tune.loop = false; + } + + { + Tune& tune = g_tunes.insert("intermission"); + + tune.song = "racent"; + tune.priority = 40; + } + + { + Tune& tune = g_tunes.insert("vote"); + + tune.song = "vote"; + tune.priority = 50; + tune.credit = true; + } + + { + Tune& tune = g_tunes.insert("vote_suspense"); + + tune.song = "voteea"; + tune.priority = 51; + } + + { + Tune& tune = g_tunes.insert("vote_end"); + + tune.song = "voteeb"; + tune.priority = 52; + tune.loop = false; + } + + { + Tune& tune = g_tunes.insert("wait"); + + tune.song = "WAIT2J"; + tune.priority = 60; + } + + { + Tune& tune = g_tunes.insert("title"); + + tune.song = "_title"; + tune.priority = 100; + tune.resist = true; + } + + { + Tune& tune = g_tunes.insert("menu"); + + tune.priority = 100; + tune.credit = true; + } + + { + Tune& tune = g_tunes.insert("menu_nocred"); + + tune.priority = 100; + } + + { + Tune& tune = g_tunes.insert("credits"); + + tune.priority = 100; + tune.song = "_creds"; + tune.fade_out = 250; + tune.loop = false; + tune.credit = true; + } + + { + Tune& tune = g_tunes.insert("shore"); + + tune.priority = 100; + tune.loop = false; + } +} + +void Music_Tick(void) +{ + g_tunes.tick(); +} + +void Music_Play(const char* id) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->play(); + g_tunes.tick(); // play this immediately + } +} + +void Music_DelayEnd(const char* id, tic_t duration) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->delay_end(duration); + + if (tune->time_remaining() <= detail::msec_to_tics(tune->fade_out)) + { + // If this action would cause a fade out, start + // fading immediately. + g_tunes.tick(); + } + } +} + +void Music_Seek(const char* id, tic_t set) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->seek = set; + tune->needs_seek = true; + } +} + +void Music_Stop(const char* id) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + g_tunes.stop(*tune); + g_tunes.tick(); + } +} + +void Music_Pause(const char* id) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->pause(); + g_tunes.pause_unpause(); + } +} + +void Music_UnPause(const char* id) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->unpause(); + g_tunes.pause_unpause(); + } +} + +void Music_PauseAll(void) +{ + g_tunes.for_each([](Tune& tune) { tune.pause(); }); + g_tunes.pause_unpause(); +} + +void Music_UnPauseAll(void) +{ + g_tunes.for_each([](Tune& tune) { tune.unpause(); }); + g_tunes.pause_unpause(); +} + +void Music_StopAll(void) +{ + g_tunes.stop_all(); + g_tunes.tick(); +} + +void Music_Remap(const char* id, const char* song) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + tune->song = song; + } +} + +boolean Music_Playing(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune && tune->playing(); +} + +boolean Music_Paused(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune && tune->paused(); +} + +tic_t Music_Elapsed(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune ? tune->elapsed() : 0u; +} + +tic_t Music_DurationLeft(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune ? tune->time_remaining() : 0u; +} + +tic_t Music_TotalDuration(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune ? tune->duration() : 0u; +} + +void Music_Loop(const char* id, boolean loop) +{ + Tune* tune = g_tunes.find(id); + + if (tune) + { + // FIXME: this has no effect if the song is already + // playing + tune->loop = loop; + } +} + +boolean Music_CanLoop(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune && tune->loop; +} + +boolean Music_CanEnd(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune && tune->can_end(); +} + +const char* Music_Song(const char* id) +{ + const Tune* tune = g_tunes.find(id); + return tune ? tune->song.c_str() : ""; +} + +const char* Music_CurrentSong(void) +{ + return g_tunes.current_song().c_str(); +} + +const char* Music_CurrentId(void) +{ + return g_tunes.current_id(); +} diff --git a/src/music.h b/src/music.h new file mode 100644 index 000000000..c7712a472 --- /dev/null +++ b/src/music.h @@ -0,0 +1,148 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +// +// A brief description of "tunes". +// +// Tunes are how the game identifies music. A tune may be +// remapped to any music lump. For example, the "level" tune +// represents the level music, but the actual song that plays +// is different between levels. +// +// Tunes store info, such as for how long they will play (in +// the case of "invinc" and "grow"), or whether they fade in +// or out and for how long they fade. +// +// Tunes are given a priority. Only the highest priority tune +// is heard, even if the others haven't ended. Tunes with the +// same priority are sorted by picking the last one that begun +// playing. +// + +#ifndef MUSIC_H +#define MUSIC_H + +#include "doomtype.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// +// Get the currently playing tune. +// + + +// Returns the song name for the currently playing tune. +// Returns empty string if no tune is playing. +const char *Music_CurrentSong(void); + +// Returns the id of the currently playing tune. Returns empty +// string if no tune is playing. +const char *Music_CurrentId(void); + + +// +// Actions that take effect immediately. +// + + +// Begin playing a tune, duration is infinite. If the tune was +// already playing, this resets its current position (seeks +// back to the start.) +void Music_Play(const char *id); + +// Postpone the end of this tune until N tics from now. The +// tune should already be playing before calling this. +void Music_DelayEnd(const char *id, tic_t duration); + +// Stop playing a tune. +void Music_Stop(const char *id); +void Music_StopAll(void); + +// Pause a tune. This effectively extends its duration, +// relative to game time. Unpausing will resume the tune +// exactly where it left off. +void Music_Pause(const char *id); +void Music_UnPause(const char *id); +void Music_PauseAll(void); +void Music_UnPauseAll(void); + + +// +// Change properties. May be called before calling Music_Play. +// These take effect on the next tick. +// + + +// Seek to a specific time in the tune. +void Music_Seek(const char *id, tic_t set); + +// Remap a tune to another song. Use the lump name, with the +// 'O_' at the beginning removed. song is case insensitive. +void Music_Remap(const char *id, const char *song); + +// Set whether a tune should loop. +void Music_Loop(const char *id, boolean loop); + + +// +// Query properties. +// + + +// Returns true if the tune is configured to loop. +boolean Music_CanLoop(const char *id); + +// Returns true if the tune does not play indefinitely, i.e. +// has a limited duration. +boolean Music_CanEnd(const char *id); + +// Returns true if the tune is playing. This does not +// necessarily mean it is audible, because it has to be at the +// highest priority to be heard. +boolean Music_Playing(const char *id); + +// Returns true if the tune is paused. +boolean Music_Paused(const char *id); + +// Returns the number of tics elapsed since the start of the +// tune. +tic_t Music_Elapsed(const char *id); + +// Returns the number of tics remaining until the tune ends. +tic_t Music_DurationLeft(const char *id); + +// Returns the total duration of the tune, in tics. +tic_t Music_TotalDuration(const char *id); + +// Returns the song name mapped to a tune. +const char *Music_Song(const char *id); + + +// +// Low level program state. +// + + +// Loads certain data structures used for the lifetime of the +// program. Only needs to be called once, before any other +// functions. +void Music_Init(void); + +// Call this every tic to update the music. +void Music_Tick(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MUSIC_H diff --git a/src/music_detail.hpp b/src/music_detail.hpp new file mode 100644 index 000000000..f4c96adca --- /dev/null +++ b/src/music_detail.hpp @@ -0,0 +1,38 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef MUSIC_DETAIL_HPP +#define MUSIC_DETAIL_HPP + +#include "doomdef.h" +#include "doomstat.h" +#include "doomtype.h" + +namespace srb2::music::detail +{ + +inline constexpr tic_t msec_to_tics(int msec) +{ + return msec * (TICRATE / 1000.f); +} + +inline constexpr int tics_to_msec(tic_t tics) +{ + return tics * (1000.f / TICRATE); +} + +inline tic_t tic_time() +{ + // Use gametic so it is synced with game logic. + return gametic; +} + +}; // namespace srb2::music::detail + +#endif // MUSIC_DETAIL_HPP diff --git a/src/music_manager.cpp b/src/music_manager.cpp new file mode 100644 index 000000000..5d5edf058 --- /dev/null +++ b/src/music_manager.cpp @@ -0,0 +1,204 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include + +#include "music_manager.hpp" +#include "music_tune.hpp" + +#include "doomtype.h" +#include "i_sound.h" +#include "i_time.h" +#include "s_sound.h" +#include "w_wad.h" +#include "z_zone.h" + +using namespace srb2::music; + +namespace +{ + +// How many tics music is allowed to drift away from game +// logic. +constexpr int kResyncTics = 2; + +}; // namespace + +void TuneManager::tick() +{ + if (S_MusicDisabled()) + { + return; + } + + Tune* tune = current_tune(); + + std::string old_song = current_song_; + current_song_ = tune && tune->playing() ? tune->song : std::string{}; + + bool changed = current_song_ != old_song; + + if (stop_credit_) + { + if (changed) + { + // Only stop the music credit if the song actually + // changed. + S_StopMusicCredit(); + } + + stop_credit_ = false; + } + + if (!tune) + { + if (changed) + { + I_UnloadSong(); + } + + return; + } + + if (changed) + { + if (load()) + { + I_FadeInPlaySong(tune->resume ? tune->resume_fade_in : tune->fade_in, tune->loop); + seek(tune); + + adjust_volume(); + I_SetSongSpeed(tune->speed()); + + if (tune->credit && !tune->resume) + { + S_ShowMusicCredit(); + } + + tune->resume = true; + + gme_ = !std::strcmp(I_SongType(), "GME"); + } + else + { + I_UnloadSong(); + } + } + else if (tune->needs_seek || (tune->sync && resync())) + { + seek(tune); + } + else if (tune->time_remaining() <= detail::msec_to_tics(tune->fade_out)) + { + if (tune->can_fade_out) + { + I_FadeSong(0, tune->fade_out, nullptr); + tune->can_fade_out = false; + } + + if (!tune->keep_open) + { + tune->ending = true; + } + } +} + +void TuneManager::pause_unpause() const +{ + const Tune* tune = current_tune(); + + if (tune) + { + if (tune->paused()) + { + I_PauseSong(); + } + else + { + I_ResumeSong(); + } + } +} + +bool TuneManager::load() const +{ + lumpnum_t lumpnum = W_CheckNumForLongName(fmt::format("O_{}", current_song_).c_str()); + + if (lumpnum == LUMPERROR) + { + return false; + } + + return I_LoadSong(static_cast(W_CacheLumpNum(lumpnum, PU_MUSIC)), W_LumpLength(lumpnum)); +} + +void TuneManager::adjust_volume() const +{ + UINT8 i; + const musicdef_t* def = S_FindMusicDef(current_song_.c_str(), &i); + + if (!def) + { + return; + } + + I_SetCurrentSongVolume(def->debug_volume != 0 ? def->debug_volume : def->volume); +} + +bool TuneManager::resync() +{ + if (gme_) + { + // This is dodging the problem. GME can be very slow + // for seeking, since it (probably) just emulates the + // entire song up to where its seeking. + // + // The time loss is not easily predictable, and it + // causes repeated resyncing, so just don't sync if + // it's GME. + return false; + } + + long d_local = I_GetTime() - time_local_; + long d_sync = detail::tic_time() - time_sync_; + + if (std::abs(d_local - d_sync) >= kResyncTics) + { + time_sync_ = detail::tic_time(); + time_local_ = I_GetTime(); + + return true; + } + + return false; +} + +void TuneManager::seek(Tune* tune) +{ + uint32_t end = I_GetSongLength(); + uint32_t loop = I_GetSongLoopPoint(); + + uint32_t pos = detail::tics_to_msec(tune->seek + tune->elapsed()) * tune->speed(); + + if (pos > end && (end - loop) > 0u) + { + pos = loop + ((pos - end) % (end - loop)); + } + + I_SetSongPosition(pos); + tune->needs_seek = false; +} diff --git a/src/music_manager.hpp b/src/music_manager.hpp new file mode 100644 index 000000000..9ee69adb5 --- /dev/null +++ b/src/music_manager.hpp @@ -0,0 +1,113 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef MUSIC_MANAGER_HPP +#define MUSIC_MANAGER_HPP + +#include +#include +#include +#include + +#include "cxxutil.hpp" +#include "music_tune.hpp" + +namespace srb2::music +{ + +class TuneManager +{ +public: + const std::string& current_song() const { return current_song_; } + + Tune* current_tune() const + { + auto it = current_iterator(); + return it != map_.end() ? const_cast(&it->second) : nullptr; + } + + const char* current_id() const + { + auto it = current_iterator(); + return it != map_.end() ? it->first.c_str() : ""; + } + + Tune* find(const char* id) const + { + auto it = map_.find(id); + return it != map_.end() ? const_cast(&it->second) : nullptr; + } + + Tune& insert(const char* id) + { + auto [it, inserted] = map_.emplace(id, Tune{}); + + SRB2_ASSERT(inserted); + + return it->second; + } + + void tick(); + void pause_unpause() const; + + void stop(Tune& tune) + { + tune.stop(); + stop_credit_ = true; + } + + void stop_all() + { + for_each([](Tune& tune) { tune.stop(); }); + stop_credit_ = true; + } + + template + void for_each(F&& f) + { + for (auto& [_, tune] : map_) + { + if (!tune.resist) + { + f(tune); + } + } + } + +private: + std::unordered_map map_; + std::string current_song_; + + tic_t time_sync_; + tic_t time_local_; + + bool stop_credit_ = false; + + bool gme_; // hack + + decltype(map_)::const_iterator current_iterator() const + { + return std::max_element( + map_.begin(), + map_.end(), + [](const auto& a, const auto& b) { return a.second < b.second; } + ); + } + + bool load() const; + void adjust_volume() const; + + bool resync(); + + static void seek(Tune* tune); +}; + +}; // namespace srb2::music + +#endif // MUSIC_MANAGER_HPP diff --git a/src/music_tune.hpp b/src/music_tune.hpp new file mode 100644 index 000000000..2b5880145 --- /dev/null +++ b/src/music_tune.hpp @@ -0,0 +1,194 @@ +// DR ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef MUSIC_TUNE_HPP +#define MUSIC_TUNE_HPP + +#include +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "music_detail.hpp" + +namespace srb2::music +{ + +class Tune +{ +public: + explicit Tune() {} + + std::string song; // looks up the lump + + // Higher priority tunes play first. + int priority = 0; + + // Fade at the beginning or end of the tune. + int fade_in = 0; // in milliseconds + int fade_out = 0; + + // Fade time subtracts from the duration of the song? + int fade_out_inclusive = true; + + // Fade in when a higher priority tune ends and this one + // resumes. + int resume_fade_in = 0; + + // Sync this tune to game logic. + bool sync = false; + + // This tune loops at the end. + bool loop = true; + + // When this tune runs out of duration, keep playing + // silence. + bool keep_open = false; + + // This tune does not respect mass stop or pause actions + // from TuneManager::stop_all etc. It must be + // stopped/paused individually. + bool resist = false; + + // This tune shows a credit when first played (not + // resumed). + bool credit = false; + + // Start playing this number of tics into the tune. + tic_t seek = 0; + + // these track state + bool can_fade_out = true; + bool needs_seek = false; + bool resume = false; + bool ending = false; + + tic_t elapsed() const { return std::max(pause_.value_or(detail::tic_time()), begin_) - begin_; } + tic_t time_remaining() const { return end_ - std::min(pause_.value_or(detail::tic_time()), end_); } + tic_t duration() const { return end_ - begin_; } + + bool playing() const { return begin_ <= detail::tic_time() && (!ending || time_remaining()); } + bool paused() const { return pause_.has_value(); } + bool can_end() const { return end_ != INFTICS; } + + float speed() const + { + // Slow level music down a bit in Encore. (Values are vibe-based. WE GET IT YOU VAPE) + if (encoremode && gamestate == GS_LEVEL) + { + return 0.86471f; + } + + return 1.f; + } + + bool operator <(const Tune& b) const + { + // If this song is not playing, it has lowest + // priority. + if (!playing()) + { + return true; + } + + // If the other song is not playing, we automatically + // have higher priority. + if (!b.playing()) + { + return false; + } + + // The highest priority song is preferred. + if (priority != b.priority) + { + return priority < b.priority; + } + + // If both songs have the same priority, prefer the + // one that begun later. + return begin_ < b.begin_; + } + + void play() + { + if (!needs_seek) + { + seek = 0; + } + + can_fade_out = true; + needs_seek = true; + resume = false; + ending = false; + + begin_ = detail::tic_time(); + end_ = INFTICS; + pause_.reset(); + } + + void delay_end(tic_t duration) + { + end_ = detail::tic_time() + duration; + + if (!fade_out_inclusive) + { + end_ += detail::msec_to_tics(fade_out); + } + + if (playing()) + { + can_fade_out = true; + ending = false; + } + } + + void stop() + { + begin_ = INFTICS; + end_ = 0; + pause_.reset(); + } + + void pause() + { + pause_ = detail::tic_time(); + } + + void unpause() + { + if (!pause_) + { + return; + } + + if (playing()) + { + tic_t n = detail::tic_time() - *pause_; + + begin_ += n; + + if (can_end()) + { + end_ += n; + } + } + + pause_.reset(); + } + +private: + tic_t begin_ = INFTICS; + tic_t end_ = 0; + std::optional pause_; +}; + +}; // namespace srb2::music + +#endif // MUSIC_TUNE_HPP diff --git a/src/objects/shrink.c b/src/objects/shrink.c index debefa955..5336630ba 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -22,6 +22,7 @@ #include "../g_game.h" #include "../z_zone.h" #include "../k_waypoint.h" +#include "../music.h" // // ███████╗██╗██╗░░██╗███╗░░░███╗███████╗ @@ -556,21 +557,11 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); } - if (victim->player->invincibilitytimer > 0) - { - ; // invincibility has priority in P_RestoreMusic, no point in starting here - } - else if (P_IsLocalPlayer(victim->player) == true) - { - S_ChangeMusicSpecial("kgrow"); - } - else //used to be "if (P_IsDisplayPlayer(victim->player) == false)" + if (P_IsLocalPlayer(victim->player) == false && victim->player->invincibilitytimer == 0) { + // don't play this if the player has invincibility -- that takes priority S_StartSound(victim, sfx_alarmg); } - - P_RestoreMusic(victim->player); - } } else diff --git a/src/p_local.h b/src/p_local.h index 3bc622743..20ad1d5d2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -183,9 +183,9 @@ boolean P_InQuicksand(mobj_t *mo); boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, angle_t oldRoll); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); -void P_RestoreMusic(player_t *player); void P_StartPositionMusic(boolean exact); void P_EndingMusic(void); +void P_InvincGrowMusic(void); mobj_t *P_SpawnGhostMobj(mobj_t *mobj); mobj_t *P_SpawnFakeShadow(mobj_t *mobj, UINT8 offset); INT32 P_GivePlayerRings(player_t *player, INT32 num_rings); @@ -237,36 +237,6 @@ boolean P_GetLives(player_t *player); boolean P_SpectatorJoinGame(player_t *player); void P_RestoreMultiMusic(player_t *player); -/// ------------------------ -/// Jingle stuff -/// ------------------------ - -typedef enum -{ - JT_NONE, // Null state - JT_OTHER, // Other state - JT_MASTER, // Main level music - - JT_INVINCIBILITY, // Invincibility - JT_GROW, // Grow - - NUMJINGLES -} jingletype_t; - -struct jingle_t -{ - char musname[7]; - boolean looping; -}; - -extern jingle_t jingleinfo[NUMJINGLES]; - -#define JINGLEPOSTFADE 1000 - -void P_PlayJingle(player_t *player, jingletype_t jingletype); -boolean P_EvaluateMusicStatus(UINT16 status, const char *musname); -void P_PlayJingleMusic(player_t *player, const char *musname, UINT16 musflags, boolean looping, UINT16 status); - // // P_MOBJ // diff --git a/src/p_setup.c b/src/p_setup.c index 75ac73f4c..30c249617 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -104,6 +104,7 @@ #include "k_podium.h" #include "k_rank.h" #include "k_mapuser.h" +#include "music.h" // Replay names have time #if !defined (UNDER_CE) @@ -7923,6 +7924,23 @@ static void P_InitMinimapInfo(void) minimapinfo.offs_y = FixedMul((minimapinfo.min_y + minimapinfo.map_h/2) << FRACBITS, minimapinfo.zoom); } +void P_ResetLevelMusic(void) +{ + if (mapheaderinfo[gamemap-1]->musname_size > 1) + mapmusrng = P_RandomKey(PR_MUSICSELECT, mapheaderinfo[gamemap-1]->musname_size); + else + mapmusrng = 0; +} + +void P_LoadLevelMusic(void) +{ + tic_t level_music_start = starttime + (TICRATE/2); + + Music_StopAll(); + Music_Remap("level", mapheaderinfo[gamemap-1]->musname[mapmusrng]); + Music_Seek("level", max(leveltime, level_music_start) - level_music_start); +} + /** Loads a level from a lump or external wad. * * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot. @@ -7969,6 +7987,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_InitLevelSettings(); + if (!demo.title) + { + Music_Stop("title"); + } + for (i = 0; i <= r_splitscreen; i++) postimgtype[i] = postimg_none; @@ -7987,7 +8010,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) { tic_t locstarttime, endtime, nowtime; - S_StopMusic(); // er, about that... + Music_StopAll(); // er, about that... // Fade to an inverted screen, with a circle fade... F_WipeStartScreen(); @@ -8072,16 +8095,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) { int wipetype = wipe_level_toblack; + // TODO: What is this?? This does nothing because P_LoadLevelMusic is gonna halt music, anyway. +#if 0 // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. if (gamestate == GS_LEVEL) S_FadeMusic(0, FixedMul( FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); +#endif if (K_PodiumSequence()) { // mapmusrng is set by local player position in K_ResetCeremony - S_InitLevelMusic(true); + P_ResetLevelMusic(); + P_LoadLevelMusic(); } else if (gamestate == GS_LEVEL) { @@ -8093,7 +8120,12 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // We should be fine starting music here. // Don't do this during titlemap, because the menu code handles music by itself. - S_InitLevelMusic(fromnetsave); + // Netsave loading does this itself, because leveltime isn't set yet! + if (!fromnetsave) + { + P_ResetLevelMusic(); + P_LoadLevelMusic(); + } } if (gametyperules & GTR_SPECIALSTART) diff --git a/src/p_setup.h b/src/p_setup.h index 8a77fc8dd..89ab09673 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -103,6 +103,8 @@ extern mapthing_t *mapthings; void P_SetupLevelSky(const char *skytexname, boolean global); void P_RespawnThings(void); +void P_ResetLevelMusic(void); +void P_LoadLevelMusic(void); boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); void P_PostLoadLevel(void); #ifdef HWRENDER diff --git a/src/p_spec.c b/src/p_spec.c index 96aae8f18..df4206f22 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2887,6 +2887,8 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha break; case 413: // Change music + // FIXME: port to new music system +#if 0 // console player only unless TMM_ALLPLAYERS is set if ((args[1] & TMM_ALLPLAYERS) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction) { @@ -2963,6 +2965,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha // Except, you can use the TMM_NORELOAD flag to change this behavior. // if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn. } +#endif break; case 414: // Play SFX diff --git a/src/p_tick.c b/src/p_tick.c index 981e40fa2..b6b470ce5 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -42,6 +42,7 @@ #include "k_specialstage.h" #include "acs/interface.h" #include "k_objects.h" +#include "music.h" #ifdef PARANOIA #include "deh_tables.h" // MOBJTYPE_LIST @@ -714,7 +715,6 @@ void P_Ticker(boolean run) P_MoveChaseCamera(&players[0], &camera[0], false); R_UpdateViewInterpolation(); P_MapEnd(); - S_SetStackAdjustmentStart(); return; } } @@ -731,13 +731,9 @@ void P_Ticker(boolean run) } else if (demo.freecam && democam.cam) // special case: allow freecam to MOVE during pause! P_DemoCameraMovement(democam.cam); - S_SetStackAdjustmentStart(); return; } - if (!S_MusicPaused()) - S_AdjustMusicStackTics(); - for (i = 0; i <= r_splitscreen; i++) postimgtype[i] = postimg_none; @@ -874,8 +870,7 @@ void P_Ticker(boolean run) // Bosses have a punchy start, so no position. if (leveltime == 1) { - S_ChangeMusic(mapmusname, mapmusflags, true); - S_ShowMusicCredit(); + Music_Play("level"); } } else if (leveltime < starttime + TICRATE) @@ -883,8 +878,7 @@ void P_Ticker(boolean run) if (leveltime == (starttime + (TICRATE/2))) { // Plays the music after the starting countdown. - S_ChangeMusic(mapmusname, mapmusflags, true); - S_ShowMusicCredit(); + Music_Play("level"); } else if (starttime != introtime) { @@ -892,7 +886,6 @@ void P_Ticker(boolean run) if (leveltime == starttime-(3*TICRATE)) { S_StartSound(NULL, sfx_s3ka7); // 3, - S_FadeMusic(0, 3500); //S_FadeOutStopMusic(3500); -- TODO the S_StopMusic callback can halt successor music instead } else if ((leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) diff --git a/src/p_user.c b/src/p_user.c index 360085dfa..d2f148283 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -64,6 +64,7 @@ #include "k_director.h" #include "g_party.h" #include "k_profiles.h" +#include "music.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -78,20 +79,6 @@ static void P_NukeAllPlayers(player_t *player); #endif -// -// Jingle stuff. -// - -jingle_t jingleinfo[NUMJINGLES] = { - // {musname, looping, reset, nest} - {"" , false}, // JT_NONE - {"" , false}, // JT_OTHER - {"" , false}, // JT_MASTER - - {"kinvnc" , true}, // JT_INVINCIBILITY - {"kgrow" , true}, // JT_GROW -}; - // // Movement. // @@ -597,90 +584,6 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) player->roundscore = MAXSCORE; } -void P_PlayJingle(player_t *player, jingletype_t jingletype) -{ - const char *musname = jingleinfo[jingletype].musname; - UINT16 musflags = 0; - boolean looping = jingleinfo[jingletype].looping; - - char newmusic[7]; - strncpy(newmusic, musname, 7); -#ifdef HAVE_LUA_MUSICPLUS - if(LUAh_MusicJingle(jingletype, newmusic, &musflags, &looping)) - return; -#endif - newmusic[6] = 0; - - P_PlayJingleMusic(player, newmusic, musflags, looping, jingletype); -} - -// -// P_PlayJingleMusic -// -void P_PlayJingleMusic(player_t *player, const char *musname, UINT16 musflags, boolean looping, UINT16 status) -{ - // If gamestate != GS_LEVEL, always play the jingle (1-up intermission) - if (gamestate == GS_LEVEL && player && !P_IsLocalPlayer(player)) - return; - - S_RetainMusic(musname, musflags, looping, 0, status); - //S_StopMusic(); - S_ChangeMusicInternal(musname, looping); -} - -boolean P_EvaluateMusicStatus(UINT16 status, const char *musname) -{ - // \todo lua hook - int i; - boolean result = false; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!P_IsLocalPlayer(&players[i])) - continue; - - switch(status) - { - case JT_INVINCIBILITY: // Invincibility - if (players[i].invincibilitytimer > 1) - { - strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14); - S_StartCaption(sfx_None, -1, players[i].invincibilitytimer); - result = true; - } - else - { - result = false; - } - break; - - case JT_GROW: // Grow - if (players[i].growshrinktimer > 1) - { - strlcpy(S_sfx[sfx_None].caption, "Grow", 14); - S_StartCaption(sfx_None, -1, players[i].growshrinktimer); - result = true; - } - else - { - result = false; - } - break; - - case JT_OTHER: // Other state - case JT_NONE: // Null state - case JT_MASTER: // Main level music - default: - result = true; - } - - if (result) - break; - } - - return result; -} - void P_PlayRinglossSound(mobj_t *source) { if (source->player && K_GetShieldFromItem(source->player->itemtype) != KSHIELD_NONE) @@ -716,7 +619,7 @@ void P_StartPositionMusic(boolean exact) : (leveltime < 1)) return; - S_ChangeMusicInternal("encore", true); + Music_Remap("position", "encore"); } else { @@ -725,12 +628,15 @@ void P_StartPositionMusic(boolean exact) : (leveltime < introtime)) return; - S_ChangeMusicInternal( + Music_Remap("position", (mapheaderinfo[gamemap-1]->positionmus[0] ? mapheaderinfo[gamemap-1]->positionmus : "postn" - ), true); + )); } + + Music_Play("position"); + Music_DelayEnd("position", (starttime + (TICRATE/2)) - leveltime); } // @@ -870,110 +776,54 @@ skippingposition: if (jingle == NULL) return; - S_ChangeMusicInternal(jingle, false); + Music_Remap("finish", jingle); + Music_Play("finish"); } -// -// P_RestoreMusic -// -// Restores music after some special music change -// -void P_RestoreMusic(player_t *player) +void P_InvincGrowMusic(void) { - UINT8 overrideLevel = 0; - SINT8 i; + INT32 invinc = 0; + INT32 grow = 0; - if (P_IsLocalPlayer(player) == false) - { - // Only applies to local players - return; - } + UINT8 i; - // Event - HERE COMES A NEW CHALLENGER - if (mapreset) + for (i = 0; i <= r_splitscreen; ++i) { - S_ChangeMusicInternal("chalng", false); - return; - } + player_t *player = &players[displayplayers[i]]; - // Event - Level Ending - if (musiccountdown > 0) - { - return; - } - - // Event - Level Start - if ((K_CheckBossIntro() == false) - && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled - { - P_StartPositionMusic(false); // inexact timing permitted - return; - } - - for (i = 0; i <= r_splitscreen; i++) - { - player_t *checkPlayer = &players[displayplayers[i]]; - if (!checkPlayer) + if (!P_IsLocalPlayer(player)) { + // Director cam on another player? Don't play + // this. continue; } - if (checkPlayer->exiting) + // Find the longest running timer among splitscreen + // players and use that. + + if (player->invincibilitytimer > invinc) { - return; + invinc = player->invincibilitytimer; } - if (checkPlayer->invincibilitytimer > 1) + if (player->growshrinktimer > grow) { - overrideLevel = max(overrideLevel, 2); - } - else if (checkPlayer->growshrinktimer > 1) - { - overrideLevel = max(overrideLevel, 1); + grow = player->growshrinktimer; } } - if (overrideLevel != 0) + if (invinc && !Music_Playing("invinc")) { - // Do a jingle override. - jingletype_t jt = JT_NONE; - - switch (overrideLevel) - { - // Lowest priority to highest priority. - case 1: - jt = JT_GROW; - break; - case 2: - jt = JT_INVINCIBILITY; - break; - default: - break; - } - - if (jt != JT_NONE) - { - //CONS_Printf("JINGLE: %d\n", jt); - //if (S_RecallMusic(jt, false) == false) - //{ - P_PlayJingle(player, jt); - //} - return; - } + Music_Play("invinc"); } -#if 0 - // Event - Final Lap - // Still works for GME, but disabled for consistency - if ((gametyperules & GTR_CIRCUIT) && player->laps >= numlaps) - S_SpeedMusic(1.2f); -#endif - - if (S_RecallMusic(JT_NONE, false) == false) // go down the stack + if (grow && !Music_Playing("grow")) { - CONS_Debug(DBG_BASIC, "Cannot find any music in resume stack!\n"); - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + Music_Play("grow"); } + + Music_DelayEnd("invinc", invinc); + Music_DelayEnd("grow", grow); } // @@ -1423,7 +1273,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) if (P_IsLocalPlayer(player) && !specialout) { - S_StopMusic(); + Music_StopAll(); musiccountdown = MUSICCOUNTDOWNMAX; } @@ -1945,8 +1795,6 @@ static void P_CheckInvincibilityTimer(player_t *player) { //K_KartResetPlayerColor(player); -- this gets called every tic anyways G_GhostAddColor((INT32) (player - players), GHC_NORMAL); - - P_RestoreMusic(player); return; } } @@ -4036,7 +3884,7 @@ void P_DoTimeOver(player_t *player) if (P_IsLocalPlayer(player)) { - S_StopMusic(); + Music_StopAll(); musiccountdown = MUSICCOUNTDOWNMAX; } diff --git a/src/s_sound.c b/src/s_sound.c index ecaaba460..c057ad435 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -35,6 +35,7 @@ #include "m_random.h" // P_RandomKey #include "i_time.h" #include "v_video.h" // V_ThinStringWidth +#include "music.h" #ifdef HW3SOUND // 3D Sound Interface @@ -62,10 +63,6 @@ static void PlaySoundIfUnfocused_OnChange(void); static void ModFilter_OnChange(void); #endif -static lumpnum_t S_GetMusicLumpNum(const char *mname); - -static boolean S_CheckQueue(void); - consvar_t cv_samplerate = CVAR_INIT ("samplerate", "22050", 0, CV_Unsigned, NULL); //Alam: For easy hacking? // stereo reverse @@ -1347,21 +1344,6 @@ void S_InitSfxChannels(INT32 sfxVolume) /// Music /// ------------------------ -static char music_name[7]; // up to 6-character name -static void *music_data; -static UINT16 music_flags; -static boolean music_looping; -static consvar_t *music_refade_cv; -static int music_usage; - -static char queue_name[7]; -static UINT16 queue_flags; -static boolean queue_looping; -static UINT32 queue_position; -static UINT32 queue_fadeinms; - -static tic_t pause_starttic; - void S_AttemptToRestoreMusic(void) { switch (gamestate) @@ -1369,18 +1351,20 @@ void S_AttemptToRestoreMusic(void) case GS_LEVEL: if (musiccountdown != 1) { - P_RestoreMusic(&players[consoleplayer]); + P_LoadLevelMusic(); + Music_Play("level"); break; } // FALLTHRU case GS_INTERMISSION: - S_ChangeMusicInternal("racent", true); + Music_Play("intermission"); break; case GS_CEREMONY: - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + Music_Play("level"); break; case GS_TITLESCREEN: - S_ChangeMusicInternal("_title", looptitle); + Music_Loop("title", looptitle); + Music_Play("title"); break; case GS_MENU: M_PlayMenuJam(); @@ -1822,16 +1806,6 @@ handlenextsong: S_UpdateSoundTestDef(false, true, true); } -boolean S_PlaysimMusicDisabled(void) -{ - if (soundtest.privilegedrequest) - return false; - - return (soundtest.playing // Ring Racers: Stereo Mode - || demo.rewinding // Don't mess with music while rewinding! - || demo.title); // SRB2Kart: Demos don't interrupt title screen music -} - // // S_FindMusicDef // @@ -2130,14 +2104,14 @@ void S_InitMusicDefs(void) void S_ShowMusicCredit(void) { UINT8 i = 0; - musicdef_t *def = S_FindMusicDef(music_name, &i); + musicdef_t *def = S_FindMusicDef(Music_CurrentSong(), &i); char credittext[128] = ""; char *work = NULL; size_t len = 128, worklen; INT32 widthused = BASEVIDWIDTH, workwidth; - if (!cv_songcredits.value || S_PlaysimMusicDisabled()) + if (!cv_songcredits.value) return; if (!def) // No definitions @@ -2206,9 +2180,6 @@ void S_ShowMusicCredit(void) void S_StopMusicCredit(void) { - if (S_PlaysimMusicDisabled()) - return; - cursongcredit.def = NULL; } @@ -2216,26 +2187,11 @@ void S_StopMusicCredit(void) /// Music Status /// ------------------------ -boolean S_DigMusicDisabled(void) -{ - return digital_disabled; -} - boolean S_MusicDisabled(void) { return digital_disabled; } -boolean S_MusicPlaying(void) -{ - return I_SongPlaying(); -} - -boolean S_MusicPaused(void) -{ - return I_SongPaused(); -} - boolean S_MusicNotInFocus(void) { return ( @@ -2243,616 +2199,16 @@ boolean S_MusicNotInFocus(void) ); } -const char *S_MusicType(void) -{ - return I_SongType(); -} - -const char *S_MusicName(void) -{ - return music_name; -} - -boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping) -{ - if (!I_SongPlaying()) - return false; - - strncpy(mname, music_name, 7); - mname[6] = 0; - *mflags = music_flags; - *looping = music_looping; - - return (boolean)mname[0]; -} - -boolean S_MusicExists(const char *mname) -{ - return W_CheckNumForName(va("O_%s", mname)) != LUMPERROR; -} - -/// ------------------------ -/// Music Effects -/// ------------------------ - -boolean S_SpeedMusic(float speed) -{ - return I_SetSongSpeed(speed); -} - -/// ------------------------ -/// Music Seeking -/// ------------------------ - -UINT32 S_GetMusicLength(void) -{ - return I_GetSongLength(); -} - -boolean S_SetMusicLoopPoint(UINT32 looppoint) -{ - return I_SetSongLoopPoint(looppoint); -} - -UINT32 S_GetMusicLoopPoint(void) -{ - return I_GetSongLoopPoint(); -} - -boolean S_SetMusicPosition(UINT32 position) -{ - if (S_PlaysimMusicDisabled()) - return false; - - return I_SetSongPosition(position); -} - -UINT32 S_GetMusicPosition(void) -{ - return I_GetSongPosition(); -} - -/// ------------------------ -/// Music Stacking (Jingles) -/// In this section: mazmazz doesn't know how to do dynamic arrays or struct pointers! -/// ------------------------ - -char music_stack_nextmusname[7]; -boolean music_stack_noposition = false; -UINT32 music_stack_fadeout = 0; -UINT32 music_stack_fadein = 0; -static musicstack_t *music_stacks = NULL; -static musicstack_t *last_music_stack = NULL; - -void S_SetStackAdjustmentStart(void) -{ - if (!pause_starttic) - pause_starttic = gametic; -} - -void S_AdjustMusicStackTics(void) -{ - if (pause_starttic) - { - musicstack_t *mst; - for (mst = music_stacks; mst; mst = mst->next) - mst->tic += gametic - pause_starttic; - pause_starttic = 0; - } -} - -static void S_ResetMusicStack(void) -{ - musicstack_t *mst, *mst_next; - for (mst = music_stacks; mst; mst = mst_next) - { - mst_next = mst->next; - Z_Free(mst); - } - music_stacks = last_music_stack = NULL; -} - -static void S_RemoveMusicStackEntry(musicstack_t *entry) -{ - musicstack_t *mst; - for (mst = music_stacks; mst; mst = mst->next) - { - if (mst == entry) - { - // Remove ourselves from the chain and link - // prev and next together - - if (mst->prev) - mst->prev->next = mst->next; - else - music_stacks = mst->next; - - if (mst->next) - mst->next->prev = mst->prev; - else - last_music_stack = mst->prev; - - break; - } - } - Z_Free(entry); -} - -static void S_RemoveMusicStackEntryByStatus(UINT16 status) -{ - musicstack_t *mst, *mst_next; - - if (!status) - return; - - for (mst = music_stacks; mst; mst = mst_next) - { - mst_next = mst->next; - if (mst->status == status) - S_RemoveMusicStackEntry(mst); - } -} - -static void S_AddMusicStackEntry(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status) -{ - musicstack_t *mst, *new_mst; - - // if the first entry is empty, force master onto it - if (!music_stacks) - { - music_stacks = Z_Calloc(sizeof (*mst), PU_MUSIC, NULL); - strncpy(music_stacks->musname, (status == JT_MASTER ? mname : (S_CheckQueue() ? queue_name : mapmusname)), 7); - music_stacks->musflags = (status == JT_MASTER ? mflags : (S_CheckQueue() ? queue_flags : mapmusflags)); - music_stacks->looping = (status == JT_MASTER ? looping : (S_CheckQueue() ? queue_looping : true)); - music_stacks->position = (status == JT_MASTER ? position : (S_CheckQueue() ? queue_position : S_GetMusicPosition())); - music_stacks->tic = gametic; - music_stacks->status = JT_MASTER; - music_stacks->mlumpnum = S_GetMusicLumpNum(music_stacks->musname); - music_stacks->noposition = S_CheckQueue(); - - if (status == JT_MASTER) - return; // we just added the user's entry here - } - - // look for an empty slot to park ourselves - for (mst = music_stacks; mst->next; mst = mst->next); - - // create our new entry - new_mst = Z_Calloc(sizeof (*new_mst), PU_MUSIC, NULL); - strncpy(new_mst->musname, mname, 7); - new_mst->musname[6] = 0; - new_mst->musflags = mflags; - new_mst->looping = looping; - new_mst->position = position; - new_mst->tic = gametic; - new_mst->status = status; - new_mst->mlumpnum = S_GetMusicLumpNum(new_mst->musname); - new_mst->noposition = false; - - mst->next = new_mst; - new_mst->prev = mst; - new_mst->next = NULL; - last_music_stack = new_mst; -} - -static musicstack_t *S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 startindex) -{ - musicstack_t *mst, *start_mst = NULL, *mst_next; - - // if the first entry is empty, force master onto it - // fixes a memory corruption bug - if (!music_stacks && status != JT_MASTER) - S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER); - - if (startindex >= 0) - { - INT16 i = 0; - for (mst = music_stacks; mst && i <= startindex; mst = mst->next, i++) - start_mst = mst; - } - else - start_mst = (fromfirst ? music_stacks : last_music_stack); - - for (mst = start_mst; mst; mst = mst_next) - { - mst_next = (fromfirst ? mst->next : mst->prev); - - if (!status || mst->status == status) - { - if (P_EvaluateMusicStatus(mst->status, mst->musname)) - { - if (!S_MusicExists(mst->musname)) // paranoia - S_RemoveMusicStackEntry(mst); // then continue - else - return mst; - } - else - S_RemoveMusicStackEntry(mst); // then continue - } - } - - return NULL; -} - -void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status) -{ - musicstack_t *mst; - - if (!status) // we use this as a null indicator, don't push - { - CONS_Alert(CONS_ERROR, "Music stack entry must have a nonzero status.\n"); - return; - } - else if (status == JT_MASTER) // enforce only one JT_MASTER - { - for (mst = music_stacks; mst; mst = mst->next) - { - if (mst->status == JT_MASTER) - { - CONS_Alert(CONS_ERROR, "Music stack can only have one JT_MASTER entry.\n"); - return; - } - } - } - else // remove any existing status - S_RemoveMusicStackEntryByStatus(status); - - S_AddMusicStackEntry(mname, mflags, looping, position, status); -} - -boolean S_RecallMusic(UINT16 status, boolean fromfirst) -{ - UINT32 newpos = 0; - boolean mapmuschanged = false; - musicstack_t *result; - musicstack_t *entry; - - if (S_PlaysimMusicDisabled()) - return false; - - entry = Z_Calloc(sizeof (*result), PU_MUSIC, NULL); - - if (status) - result = S_GetMusicStackEntry(status, fromfirst, -1); - else - result = S_GetMusicStackEntry(JT_NONE, false, -1); - - if (result && !S_MusicExists(result->musname)) - { - Z_Free(entry); - return false; // music doesn't exist, so don't do anything - } - - // make a copy of result, since we make modifications to our copy - if (result) - { - *entry = *result; - strncpy(entry->musname, result->musname, 7); - } - - // no result, just grab mapmusname - if (!result || !entry->musname[0] || ((status == JT_MASTER || (music_stacks ? !music_stacks->status : false)) && !entry->status)) - { - strncpy(entry->musname, mapmusname, 7); - entry->musflags = mapmusflags; - entry->looping = true; - entry->position = mapmusposition; - entry->tic = gametic; - entry->status = JT_MASTER; - entry->mlumpnum = S_GetMusicLumpNum(entry->musname); - entry->noposition = false; // don't set this until we do the mapmuschanged check, below. Else, this breaks some resumes. - } - - if (entry->status == JT_MASTER) - { - mapmuschanged = strnicmp(entry->musname, mapmusname, 7); - if (mapmuschanged) - { - strncpy(entry->musname, mapmusname, 7); - entry->musflags = mapmusflags; - entry->looping = true; - entry->position = mapmusposition; - entry->tic = gametic; - entry->status = JT_MASTER; - entry->mlumpnum = S_GetMusicLumpNum(entry->musname); - entry->noposition = true; - } - S_ResetMusicStack(); - } - else if (!entry->status) - { - Z_Free(entry); - return false; - } - - if (strncmp(entry->musname, S_MusicName(), 7)) // don't restart music if we're already playing it - { - if (music_stack_fadeout) - S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, music_stack_fadeout, 0); - else - { - S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, 0, music_stack_fadein); - - if (!entry->noposition && !music_stack_noposition) // HACK: Global boolean to toggle position resuming, e.g., de-superize - { - UINT32 poslapse = 0; - - // To prevent the game from jumping past the end of the music, we need - // to check if we can get the song's length. Otherwise, if the lapsed resume time goes - // over a LOOPPOINT, mixer_sound.c will be unable to calculate the new resume position. - if (S_GetMusicLength()) - poslapse = (UINT32)((float)(gametic - entry->tic)/(float)TICRATE*(float)MUSICRATE); - - newpos = entry->position + poslapse; - } - - // If the newly recalled music lumpnum does not match the lumpnum that we stored in stack, - // then discard the new position. That way, we will not recall an invalid position - // when the music is replaced or digital/MIDI is toggled. - if (newpos > 0 && S_MusicPlaying() && S_GetMusicLumpNum(entry->musname) == entry->mlumpnum) - S_SetMusicPosition(newpos); - else - { - S_StopFadingMusic(); - S_SetInternalMusicVolume(100); - } - } - music_stack_noposition = false; - music_stack_fadeout = 0; - music_stack_fadein = JINGLEPOSTFADE; - } - - Z_Free(entry); - return true; -} - /// ------------------------ /// Music Playback /// ------------------------ -static lumpnum_t S_GetMusicLumpNum(const char *mname) -{ - if (S_MusicExists(mname)) - return W_GetNumForName(va("o_%s", mname)); - else - return LUMPERROR; -} - -static boolean S_LoadMusic(const char *mname) -{ - lumpnum_t mlumpnum; - void *mdata; - - if (S_MusicDisabled()) - return false; - - mlumpnum = S_GetMusicLumpNum(mname); - - if (mlumpnum == LUMPERROR) - { - CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: lump not found!\n", mname); - return false; - } - - // load & register it - mdata = W_CacheLumpNum(mlumpnum, PU_MUSIC); - - if (I_LoadSong(mdata, W_LumpLength(mlumpnum))) - { - strncpy(music_name, mname, 7); - music_name[6] = 0; - music_data = mdata; - return true; - } - else - { - CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: engine failure!\n", mname); - return false; - } -} - -static void S_UnloadMusic(void) -{ - I_UnloadSong(); - -#ifndef HAVE_SDL //SDL uses RWOPS - Z_ChangeTag(music_data, PU_CACHE); -#endif - music_data = NULL; - - music_name[0] = 0; - music_flags = 0; - music_looping = false; - - music_refade_cv = 0; - music_usage = 0; -} - -static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) -{ - //musicdef_t *def; - - if (S_MusicDisabled()) - return false; - - I_UpdateSongLagConditions(); - - if ((!fadeinms && !I_PlaySong(looping)) || - (fadeinms && !I_FadeInPlaySong(fadeinms, looping))) - { - CONS_Alert(CONS_ERROR, "Music %.6s could not be played: engine failure!\n", music_name); - S_UnloadMusic(); - return false; - } - -#if 0 - /* set loop point from MUSICDEF */ - for (def = musicdefstart; def; def = def->next) - { - if (strcasecmp(def->name, music_name) == 0) - { - if (def->loop_ms) - S_SetMusicLoopPoint(def->loop_ms); - break; - } - } -#endif - - S_InitMusicVolume(); // switch between digi and sequence volume - - if (S_MusicNotInFocus()) - I_SetMusicVolume(0); - - return true; -} - -static void S_QueueMusic(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 fadeinms) -{ - strncpy(queue_name, mmusic, 7); - queue_flags = mflags; - queue_looping = looping; - queue_position = position; - queue_fadeinms = fadeinms; -} - -static boolean S_CheckQueue(void) -{ - return queue_name[0]; -} - -static void S_ClearQueue(void) -{ - queue_name[0] = queue_flags = queue_looping = queue_position = queue_fadeinms = 0; -} - -static void S_ChangeMusicToQueue(void) -{ - S_ChangeMusicEx(queue_name, queue_flags, queue_looping, queue_position, 0, queue_fadeinms); - S_ClearQueue(); -} - -void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms) -{ - char newmusic[7]; - - if (S_MusicDisabled() || S_PlaysimMusicDisabled()) - return; - - strncpy(newmusic, mmusic, 7); - - newmusic[6] = 0; - - // No Music (empty string) - if (newmusic[0] == 0) - { - if (prefadems) - I_FadeSong(0, prefadems, &S_StopMusic); - else - S_StopMusic(); - return; - } - - if (prefadems) // queue music change for after fade // allow even if the music is the same - // && S_MusicPlaying() // Let the delay happen even if we're not playing music - { - CONS_Debug(DBG_DETAILED, "Now fading out song %s\n", music_name); - S_QueueMusic(newmusic, mflags, looping, position, fadeinms); - I_FadeSong(0, prefadems, S_ChangeMusicToQueue); - return; - } - else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET)) - { - CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic); - - S_StopMusic(); - - if (!S_LoadMusic(newmusic)) - return; - - music_flags = mflags; - music_looping = looping; - - { - UINT8 i = 0; - musicdef_t *def = S_FindMusicDef(music_name, &i); - - if (def) - { - I_SetCurrentSongVolume(def->debug_volume != 0 ? def->debug_volume : def->volume); - } - } - - if (!S_PlayMusic(looping, fadeinms)) - return; - - if (position) - I_SetSongPosition(position); - - I_SetSongTrack(mflags & MUSIC_TRACKMASK); - - // Slow level music down a bit in Encore. (Values are vibe-based. WE GET IT YOU VAPE) - S_SpeedMusic((encoremode && gamestate == GS_LEVEL) ? 0.86471f : 1.f); - } - else if (fadeinms) // let fades happen with same music - { - I_SetSongPosition(position); - I_FadeSong(100, fadeinms, NULL); - } - else // reset volume to 100 with same music - { - I_StopFadingSong(); - I_FadeSong(100, 500, NULL); - } -} - -void S_ChangeMusicSpecial (const char *mmusic) -{ - if (cv_resetspecialmusic.value) - S_ChangeMusic(mmusic, MUSIC_FORCERESET, true); - else - S_ChangeMusicInternal(mmusic, true); -} - -void S_StopMusic(void) -{ - if (!I_SongPlaying() || S_PlaysimMusicDisabled()) - return; - - if (strcasecmp(music_name, mapmusname) == 0) - mapmusresume = I_GetSongPosition(); - - if (I_SongPaused()) - I_ResumeSong(); - - I_StopSong(); - S_UnloadMusic(); // for now, stopping also means you unload the song - - if (cv_closedcaptioning.value) - { - if (closedcaptions[0].s-S_sfx == sfx_None) - { - if (gamestate != wipegamestate) - { - closedcaptions[0].c = NULL; - closedcaptions[0].s = NULL; - closedcaptions[0].t = 0; - closedcaptions[0].b = 0; - } - else - closedcaptions[0].t = CAPTIONFADETICS; - } - } -} - // // Stop and resume music, during game PAUSE. // void S_PauseAudio(void) { - if (I_SongPlaying() && !I_SongPaused()) - I_PauseSong(); - - S_SetStackAdjustmentStart(); + Music_PauseAll(); } void S_ResumeAudio(void) @@ -2860,13 +2216,7 @@ void S_ResumeAudio(void) if (S_MusicNotInFocus()) return; - if (soundtest.paused == true) - return; - - if (I_SongPlaying() && I_SongPaused()) - I_ResumeSong(); - - S_AdjustMusicStackTics(); + Music_UnPauseAll(); } void S_SetMusicVolume(INT32 digvolume) @@ -2879,103 +2229,10 @@ void S_SetMusicVolume(INT32 digvolume) I_SetMusicVolume(digvolume); } -void -S_SetRestoreMusicFadeInCvar (consvar_t *cv) -{ - music_refade_cv = cv; -} - -int -S_GetRestoreMusicFadeIn (void) -{ - if (music_refade_cv) - return music_refade_cv->value; - else - return 0; -} - -void -S_SetMusicUsage (int type) -{ - music_usage = type; - I_UpdateSongLagConditions(); -} - -int -S_MusicUsage (void) -{ - return music_usage; -} - -/// ------------------------ -/// Music Fading -/// ------------------------ - -void S_SetInternalMusicVolume(INT32 volume) -{ - I_SetInternalMusicVolume(min(max(volume, 0), 100)); -} - -void S_StopFadingMusic(void) -{ - I_StopFadingSong(); -} - -boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms) -{ - if (S_PlaysimMusicDisabled()) - return false; - - if (source_volume < 0) - return I_FadeSong(target_volume, ms, NULL); - else - return I_FadeSongFromVolume(target_volume, source_volume, ms, NULL); -} - -boolean S_FadeOutStopMusic(UINT32 ms) -{ - if (S_PlaysimMusicDisabled()) - return false; - - return I_FadeSong(0, ms, &S_StopMusic); -} - /// ------------------------ /// Init & Others /// ------------------------ -// -// Per level startup code. -// Kills playing sounds at start of level, -// determines music if any, changes music. -// -void S_InitLevelMusic(boolean fromnetsave) -{ - - if (mapmusflags & MUSIC_RELOADRESET) - { - if (!fromnetsave) - { - if (mapheaderinfo[gamemap-1]->musname_size > 1) - mapmusrng = P_RandomKey(PR_MUSICSELECT, mapheaderinfo[gamemap-1]->musname_size); - else - mapmusrng = 0; - } - strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname[mapmusrng], 7); - mapmusname[6] = 0; - mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK); - mapmusposition = mapheaderinfo[gamemap-1]->muspos; - mapmusresume = 0; - } - - S_StopMusic(); - - S_ResetMusicStack(); - music_stack_noposition = false; - music_stack_fadeout = 0; - music_stack_fadein = JINGLEPOSTFADE; -} - static inline void PrintMusicDefField(const char *label, const char *field) { if (field) @@ -3095,7 +2352,7 @@ static void Command_RestartAudio_f(void) if (dedicated) // No point in doing anything if game is a dedicated server. return; - S_StopMusic(); + Music_StopAll(); S_StopSounds(); I_ShutdownMusic(); I_ShutdownSound(); @@ -3223,7 +2480,7 @@ static void Command_MusicDef_f(void) { UINT8 i = 0; - def = S_FindMusicDef(music_name, &i); + def = S_FindMusicDef(Music_CurrentSong(), &i); def->debug_volume = atoi(arg2); I_SetCurrentSongVolume(def->debug_volume); @@ -3298,13 +2555,11 @@ void GameDigiMusic_OnChange(void) digital_disabled = false; I_StartupSound(); // will return early if initialised I_InitMusic(); - - S_AttemptToRestoreMusic(); } else { digital_disabled = true; - S_StopMusic(); + I_UnloadSong(); } } diff --git a/src/s_sound.h b/src/s_sound.h index 753fe28cb..aed057ee5 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -157,22 +157,8 @@ void S_StopSound(void *origin); boolean S_DigMusicDisabled(void); boolean S_MusicDisabled(void); -boolean S_MusicPlaying(void); -boolean S_MusicPaused(void); boolean S_MusicNotInFocus(void); -const char *S_MusicType(void); -const char *S_MusicName(void); -boolean S_MusicExists(const char *mname); -boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping); - - -// -// Music Effects -// - -// Set Speed of Music -boolean S_SpeedMusic(float speed); #define MAXDEFTRACKS 3 @@ -240,8 +226,6 @@ void S_SoundTestTogglePause(void); void S_TickSoundTest(void); #define SOUNDTEST_FADEOUTSECONDS 5 -boolean S_PlaysimMusicDisabled(void); - extern musicdef_t *musicdefstart; void S_LoadMusicDefs(UINT16 wadnum); @@ -250,84 +234,10 @@ musicdef_t *S_FindMusicDef(const char *name, UINT8 *i); void S_ShowMusicCredit(void); void S_StopMusicCredit(void); -// -// Music Seeking -// - -// Get Length of Music -UINT32 S_GetMusicLength(void); - -// Set LoopPoint of Music -boolean S_SetMusicLoopPoint(UINT32 looppoint); - -// Get LoopPoint of Music -UINT32 S_GetMusicLoopPoint(void); - -// Set Position of Music -boolean S_SetMusicPosition(UINT32 position); - -// Get Position of Music -UINT32 S_GetMusicPosition(void); - -// -// Music Stacking (Jingles) -// - -struct musicstack_t -{ - char musname[7]; - UINT16 musflags; - boolean looping; - UINT32 position; - tic_t tic; - UINT16 status; - lumpnum_t mlumpnum; - boolean noposition; // force music stack resuming from zero (like music_stack_noposition) - - musicstack_t *prev; - musicstack_t *next; -}; - -extern char music_stack_nextmusname[7]; -extern boolean music_stack_noposition; -extern UINT32 music_stack_fadeout; -extern UINT32 music_stack_fadein; - -void S_SetStackAdjustmentStart(void); -void S_AdjustMusicStackTics(void); -void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status); -boolean S_RecallMusic(UINT16 status, boolean fromfirst); - // // Music Playback // -enum -{ - MUS_SPECIAL = 1,/* powerups--invincibility, grow */ -}; - -// Start music track, arbitrary, given its name, and set whether looping -// note: music flags 12 bits for tracknum (gme, other formats with more than one track) -// 13-15 aren't used yet -// and the last bit we ignore (internal game flag for resetting music on reload) -void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms); -#define S_ChangeMusicInternal(a,b) S_ChangeMusicEx(a,0,b,0,0,0) -#define S_ChangeMusic(a,b,c) S_ChangeMusicEx(a,b,c,0,0,0) - -void S_ChangeMusicSpecial (const char *mmusic); - -void S_SetRestoreMusicFadeInCvar (consvar_t *cvar); -#define S_ClearRestoreMusicFadeInCvar() \ - S_SetRestoreMusicFadeInCvar(0) -int S_GetRestoreMusicFadeIn (void); - -void S_SetMusicUsage (int type); -int S_MusicUsage (void); - -// Stops the music. -void S_StopMusic(void); - // Stop and resume music, during game PAUSE. void S_PauseAudio(void); void S_ResumeAudio(void); @@ -339,17 +249,6 @@ void S_DisableSound(void); // Attempt to restore music based on gamestate. void S_AttemptToRestoreMusic(void); -// -// Music Fading -// - -void S_SetInternalMusicVolume(INT32 volume); -void S_StopFadingMusic(void); -boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms); -#define S_FadeMusic(a, b) S_FadeMusicFromVolume(a, -1, b) -#define S_FadeInChangeMusic(a,b,c,d) S_ChangeMusicEx(a,b,c,0,0,d) -boolean S_FadeOutStopMusic(UINT32 ms); - // // Updates music & sounds // diff --git a/src/st_stuff.c b/src/st_stuff.c index 2b1abeadc..dc4133089 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -50,6 +50,8 @@ #include "k_grandprix.h" // we need to know grandprix status for titlecards #include "k_boss.h" #include "k_zvote.h" +#include "music.h" +#include "i_sound.h" UINT16 objectsdrawn = 0; @@ -381,38 +383,40 @@ static void ST_pushDebugTimeMS(INT32 *height, const char *label, UINT32 ms) static void ST_drawMusicDebug(INT32 *height) { - char mname[7]; - UINT16 mflags; // unused - boolean looping; + const char *mname = Music_CurrentSong(); + boolean looping = Music_CanLoop(Music_CurrentId()); UINT8 i = 0; const musicdef_t *def; const char *format; - if (!S_MusicInfo(mname, &mflags, &looping)) + ST_pushDebugString(height, va(" Tune: %8s", Music_CurrentId())); + ST_pushRow(height); + + if (!strcmp(mname, "")) { ST_pushDebugString(height, "Song: "); return; } def = S_FindMusicDef(mname, &i); - format = S_MusicType(); + format = I_SongType(); - ST_pushDebugTimeMS(height, " Elapsed: ", S_GetMusicPosition()); + ST_pushDebugTimeMS(height, " Elapsed: ", I_GetSongPosition()); ST_pushDebugTimeMS(height, looping ? " Loop B: " - : "Duration: ", S_GetMusicLength()); + : "Duration: ", I_GetSongLength()); if (looping) { - ST_pushDebugTimeMS(height, " Loop A: ", S_GetMusicLoopPoint()); + ST_pushDebugTimeMS(height, " Loop A: ", I_GetSongLoopPoint()); } ST_pushRow(height); if (format) { - ST_pushDebugString(height, va(" Format: %8s", S_MusicType())); + ST_pushDebugString(height, va(" Format: %8s", I_SongType())); } ST_pushDebugString(height, va(" Song: %8s", mname)); diff --git a/src/y_inter.c b/src/y_inter.c index bfaa71521..391d0861b 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -50,6 +50,7 @@ #include "k_grandprix.h" #include "k_serverstats.h" // SV_BumpMatchStats #include "m_easing.h" +#include "music.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -1823,7 +1824,7 @@ void Y_StartIntermission(void) I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders); if (timer > 1 && musiccountdown == 0) - S_ChangeMusicInternal("racent", true); // loop it + Music_Play("intermission"); S_ShowMusicCredit(); // Always call