From c60d61a493684bcb90cfd6ac77aa4506be132d05 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 18 Sep 2018 15:10:00 -0400 Subject: [PATCH 01/10] musicplus-jingle 2.2 -> 2.1 backport --- src/dehacked.c | 17 +++ src/p_enemy.c | 7 +- src/p_inter.c | 7 +- src/p_local.h | 40 +++++++ src/p_setup.c | 36 ++++-- src/p_spec.c | 58 ++++++++-- src/p_tick.c | 7 ++ src/p_user.c | 200 ++++++++++++++++++++++++++++----- src/s_sound.c | 261 +++++++++++++++++++++++++++++++++++++++++++ src/s_sound.h | 24 ++++ src/sdl/i_video.c | 5 +- src/win32/win_main.c | 6 +- 12 files changed, 605 insertions(+), 63 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index e2df11142..e145e12aa 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7190,6 +7190,23 @@ struct { {"GT_HIDEANDSEEK",GT_HIDEANDSEEK}, {"GT_CTF",GT_CTF}, + // Jingles (jingletype_t) + {"JT_NONE",JT_NONE}, + {"JT_OTHER",JT_OTHER}, + {"JT_MASTER",JT_MASTER}, + {"JT_1UP",JT_1UP}, + {"JT_SHOES",JT_SHOES}, + {"JT_INV",JT_INV}, + {"JT_MINV",JT_MINV}, + {"JT_DROWN",JT_DROWN}, + {"JT_SUPER",JT_SUPER}, + {"JT_GOVER",JT_GOVER}, + {"JT_NIGHTSTIMEOUT",JT_NIGHTSTIMEOUT}, + {"JT_SSTIMEOUT",JT_SSTIMEOUT}, + // {"JT_LCLEAR",JT_LCLEAR}, + // {"JT_RACENT",JT_RACENT}, + // {"JT_CONTSC",JT_CONTSC}, + // Player state (playerstate_t) {"PST_LIVE",PST_LIVE}, // Playing or camping. {"PST_DEAD",PST_DEAD}, // Dead on the ground, view follows killer. diff --git a/src/p_enemy.c b/src/p_enemy.c index f2e54a583..11e3856c4 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3056,10 +3056,10 @@ void A_Invincibility(mobj_t *actor) if (P_IsLocalPlayer(player) && !player->powers[pw_super]) { - S_StopMusic(); if (mariomode) G_GhostAddColor(GHC_INVINCIBLE); S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false); + P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV); } } @@ -3093,10 +3093,7 @@ void A_SuperSneakers(mobj_t *actor) if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)) S_SpeedMusic(1.4f); else - { - S_StopMusic(); - S_ChangeMusicInternal("shoes", false); - } + P_PlayJingle(player, JT_SHOES); } } diff --git a/src/p_inter.c b/src/p_inter.c index 407e091fa..7a17fbcf6 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2088,10 +2088,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) if (target->player->lives <= 0) // Tails 03-14-2000 { if (P_IsLocalPlayer(target->player)/* && target->player == &players[consoleplayer] */) - { - S_StopMusic(); // Stop the Music! Tails 03-14-2000 - S_ChangeMusicInternal("gmover", false); // Yousa dead now, Okieday? Tails 03-14-2000 - } + P_PlayJingle(target->player, JT_GOVER); // Yousa dead now, Okieday? Tails 03-14-2000 } } target->player->playerstate = PST_DEAD; @@ -2466,7 +2463,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source) && player->nightstime < 10*TICRATE) { //S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH - S_ChangeMusicInternal("drown",false); + P_PlayJingle(player, ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? JT_NIGHTSTIMEOUT : JT_SSTIMEOUT); } } } diff --git a/src/p_local.h b/src/p_local.h index 1fd7ada04..190bd7792 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -184,6 +184,46 @@ void P_PlayLivesJingle(player_t *player); #define P_PlayDeathSound(s) S_StartSound(s, sfx_altdi1 + P_RandomKey(4)); #define P_PlayVictorySound(s) S_StartSound(s, sfx_victr1 + P_RandomKey(4)); +/// ------------------------ +/// Jingle stuff +/// ------------------------ + +typedef enum +{ + JT_NONE, // Null state + JT_OTHER, // Other state + JT_MASTER, // Main level music + JT_1UP, // Extra life + JT_SHOES, // Speed shoes + JT_INV, // Invincibility + JT_MINV, // Mario Invincibility + JT_DROWN, // Drowning + JT_SUPER, // Super Sonic + JT_GOVER, // Game Over + JT_NIGHTSTIMEOUT, // NiGHTS Time Out (10 seconds) + JT_SSTIMEOUT, // NiGHTS Special Stage Time Out (10 seconds) + + // these are not jingles + // JT_LCLEAR, // Level Clear + // JT_RACENT, // Multiplayer Intermission + // JT_CONTSC, // Continue + + NUMJINGLES +} jingletype_t; + +typedef struct +{ + char musname[7]; + boolean looping; +} jingle_t; + +extern jingle_t jingleinfo[NUMJINGLES]; + +#define JINGLEPOSTFADE 1000 + +void P_PlayJingle(player_t *player, jingletype_t jingletype); +boolean P_EvaluateMusicStatus(UINT16 status); +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 6c6b9153d..f504ffb57 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1514,19 +1514,33 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum) { M_Memcpy(process,msd->bottomtexture,8); process[8] = '\0'; - sd->bottomtexture = get_number(process)-1; + sd->bottomtexture = get_number(process); } - M_Memcpy(process,msd->toptexture,8); - process[8] = '\0'; - sd->text = Z_Malloc(7, PU_LEVEL, NULL); - // If they type in O_ or D_ and their music name, just shrug, - // then copy the rest instead. - if ((process[0] == 'O' || process[0] == 'D') && process[7]) - M_Memcpy(sd->text, process+2, 6); - else // Assume it's a proper music name. - M_Memcpy(sd->text, process, 6); - sd->text[6] = 0; + if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0') + { + M_Memcpy(process,msd->midtexture,8); + process[8] = '\0'; + sd->midtexture = get_number(process); + } + + // always process if back sidedef, because we need that - symbol + sd->text = Z_Malloc(7, PU_LEVEL, NULL); + if (i == 1 || msd->toptexture[0] != '-' || msd->toptexture[1] != '\0') + { + M_Memcpy(process,msd->toptexture,8); + process[8] = '\0'; + + // If they type in O_ or D_ and their music name, just shrug, + // then copy the rest instead. + if ((process[0] == 'O' || process[0] == 'D') && process[7]) + M_Memcpy(sd->text, process+2, 6); + else // Assume it's a proper music name. + M_Memcpy(sd->text, process, 6); + sd->text[6] = 0; + } + else + sd->text[0] = 0; break; } case 414: // Play SFX diff --git a/src/p_spec.c b/src/p_spec.c index afc3c0533..7832ff038 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2419,18 +2419,60 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // console player only unless NOCLIMB is set if ((line->flags & ML_NOCLIMB) || (mo && mo->player && P_IsLocalPlayer(mo->player))) { - UINT16 tracknum = (UINT16)sides[line->sidenum[0]].bottomtexture; + boolean musicsame = (!sides[line->sidenum[0]].text[0] || !strnicmp(sides[line->sidenum[0]].text, S_MusicName(), 7)); + UINT16 tracknum = (UINT16)max(sides[line->sidenum[0]].bottomtexture, 0); + INT32 position = (INT32)max(sides[line->sidenum[0]].midtexture, 0); + UINT32 prefadems = (UINT32)max(sides[line->sidenum[0]].textureoffset >> FRACBITS, 0); + UINT32 postfadems = (UINT32)max(sides[line->sidenum[0]].rowoffset >> FRACBITS, 0); + UINT8 fadetarget = (UINT8)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].textureoffset >> FRACBITS : 0, 0); + INT16 fadesource = (INT16)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].rowoffset >> FRACBITS : 0, -1); - strncpy(mapmusname, sides[line->sidenum[0]].text, 7); - mapmusname[6] = 0; + if (line->flags & ML_EFFECT1) + { + // adjust for loop point if subtracting + if (position < 0 && S_GetMusicLength() && + S_GetMusicPosition() > S_GetMusicLoopPoint() && + S_GetMusicPosition() + position < S_GetMusicLoopPoint()) + position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0); + else + position = max(S_GetMusicPosition() + position, 0); + } - mapmusflags = tracknum & MUSIC_TRACKMASK; - if (!(line->flags & ML_BLOCKMONSTERS)) - mapmusflags |= MUSIC_RELOADRESET; + if ((line->flags & ML_EFFECT2) && fadetarget && musicsame) + { + if (!postfadems) + S_SetInternalMusicVolume(fadetarget); + else + S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); - mapmusposition = 0; + if (position) + S_SetMusicPosition(position); + } + else + { + strncpy(mapmusname, sides[line->sidenum[0]].text, 7); + mapmusname[6] = 0; - S_ChangeMusic(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4)); + mapmusflags = tracknum & MUSIC_TRACKMASK; + if (!(line->flags & ML_BLOCKMONSTERS)) + mapmusflags |= MUSIC_RELOADRESET; + if (line->flags & ML_BOUNCY) + mapmusflags |= MUSIC_FORCERESET; + + mapmusposition = position; + + S_ChangeMusicEx(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4), position, + !(line->flags & ML_EFFECT2) ? prefadems : 0, + !(line->flags & ML_EFFECT2) ? postfadems : 0); + + if ((line->flags & ML_EFFECT2) && fadetarget) + { + if (!postfadems) + S_SetInternalMusicVolume(fadetarget); + else + S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); + } + } // Except, you can use the ML_BLOCKMONSTERS flag to change this behavior. // if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn. diff --git a/src/p_tick.c b/src/p_tick.c index f4bc59323..7b1878d93 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -582,13 +582,20 @@ void P_Ticker(boolean run) OP_ObjectplaceMovement(&players[0]); P_MoveChaseCamera(&players[0], &camera, false); P_MapEnd(); + S_SetStackAdjustmentStart(); return; } } // Check for pause or menu up in single player if (paused || P_AutoPause()) + { + S_SetStackAdjustmentStart(); return; + } + + if (!S_MusicPaused()) + S_AdjustMusicStackTics(); postimgtype = postimgtype2 = postimg_none; diff --git a/src/p_user.c b/src/p_user.c index 03b037fed..cb14814a2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -56,6 +56,29 @@ 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 + {"xtlife" , false}, + {"shoes" , true}, + {"invinc" , false}, + {"minvnc" , false}, + {"drown" , false}, + {"supers" , true}, + {"gmover" , false}, + {"drown" , false}, // JT_NIGHTSTIMEOUT + {"drown" , false} // JT_SSTIMEOUT + // {"lclear" , false}, + // {"racent" , true}, + // {"contsc" , true} +}; + // // Movement. // @@ -636,6 +659,7 @@ static void P_DeNightserizePlayer(player_t *player) } // Restore from drowning music + music_stack_fadein = 0; // HACK: Change fade-in for restore music P_RestoreMusic(player); } // @@ -675,6 +699,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->nightstime = player->startedtime = nighttime*TICRATE; player->bonustime = false; + music_stack_fadein = 0; // HACK: Change fade-in for restore music P_RestoreMusic(player); P_SetMobjState(player->mo->tracer, S_SUPERTRANS1); @@ -961,8 +986,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) player->powers[pw_super] = 1; if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player)) { - S_StopMusic(); - S_ChangeMusicInternal("supers", true); + P_PlayJingle(player, JT_SUPER); } S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi @@ -1097,11 +1121,111 @@ void P_PlayLivesJingle(player_t *player) { if (player) player->powers[pw_extralife] = extralifetics + 1; - S_StopMusic(); // otherwise it won't restart if this is done twice in a row - S_ChangeMusicInternal("xtlife", false); + P_PlayJingle(player, JT_1UP); } } +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); +#if defined(HAVE_BLUA) && defined(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 (!P_IsLocalPlayer(player)) + return; + + S_RetainMusic(musname, musflags, looping, 0, status); + S_StopMusic(); + S_ChangeMusicInternal(musname, looping); +} + +boolean P_EvaluateMusicStatus(UINT16 status) +{ + // \todo lua hook + int i; + boolean result = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!P_IsLocalPlayer(&players[i])) + continue; + + switch(status) + { + case JT_1UP: // Extra life + result = (players[i].powers[pw_extralife] > 1); + break; + + case JT_SHOES: // Speed shoes + if (players[i].powers[pw_sneakers] > 1 && !players[i].powers[pw_super]) + { + //strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12); + //S_StartCaption(sfx_None, -1, players[i].powers[pw_sneakers]); + result = true; + } + else + result = false; + break; + + case JT_INV: // Invincibility + case JT_MINV: // Mario Invincibility + if (players[i].powers[pw_invulnerability] > 1) + { + //strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14); + //S_StartCaption(sfx_None, -1, players[i].powers[pw_invulnerability]); + result = true; + } + else + result = false; + break; + + case JT_DROWN: // Drowning + result = (players[i].powers[pw_underwater] && players[i].powers[pw_underwater] <= 11*TICRATE + 1); + break; + + case JT_SUPER: // Super Sonic + result = (players[i].powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC)); + break; + + case JT_GOVER: // Game Over + result = (players[i].lives <= 0); + break; + + case JT_NIGHTSTIMEOUT: // NiGHTS Time Out (10 seconds) + case JT_SSTIMEOUT: + result = (players[i].nightstime && players[i].nightstime <= 10*TICRATE); + break; + + case JT_NONE: // Null state + case JT_OTHER: // Other state + case JT_MASTER: // Main level music + default: + result = true; + } + + if (result) + break; + } + + return result; + } + // // P_RestoreMusic // @@ -1112,25 +1236,46 @@ void P_RestoreMusic(player_t *player) if (!P_IsLocalPlayer(player)) // Only applies to a local player return; + S_SpeedMusic(1.0f); + + // Jingles have a priority in this order, so follow it + // and as a default case, go down the music stack. + + // Extra life if (player->powers[pw_extralife] > 1) return; - S_SpeedMusic(1.0f); - if (player->powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC)) - S_ChangeMusicInternal("supers", true); + + // Super + else if (player->powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) + && !S_RecallMusic(JT_SUPER, false)) + P_PlayJingle(player, JT_SUPER); + + // Invulnerability else if (player->powers[pw_invulnerability] > 1) - S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false); + { + if (!S_RecallMusic(JT_INV, false) && !S_RecallMusic(JT_MINV, false)) + P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV); + } + + // Shoes else if (player->powers[pw_sneakers] > 1 && !player->powers[pw_super]) { if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC) { S_SpeedMusic(1.4f); - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + if (!S_RecallMusic(JT_MASTER, true)) + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); } - else - S_ChangeMusicInternal("shoes", true); + else if (!S_RecallMusic(JT_SHOES, false)) + P_PlayJingle(player, JT_SHOES); } - else - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + + // Default + else if (!S_RecallMusic(JT_NONE, false)) // go down the stack + { + CONS_Debug(DBG_BASIC, "Cannot find any music in resume stack!\n"); + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + } } // @@ -2017,10 +2162,6 @@ static void P_CheckQuicksand(player_t *player) // static void P_CheckSneakerAndLivesTimer(player_t *player) { - if ((player->powers[pw_underwater] <= 11*TICRATE + 1) - && (player->powers[pw_underwater] > 1)) - return; // don't restore music if drowning music is playing - if (player->powers[pw_extralife] == 1) // Extra Life! P_RestoreMusic(player); @@ -2105,15 +2246,18 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) if (!(player->mo->eflags & MFE_UNDERWATER) && player->powers[pw_underwater]) { if (player->powers[pw_underwater] <= 12*TICRATE + 1) + { + player->powers[pw_underwater] = 0; P_RestoreMusic(player); - - player->powers[pw_underwater] = 0; + } + else + player->powers[pw_underwater] = 0; } if (player->powers[pw_spacetime] > 1 && !P_InSpaceSector(player->mo)) { - P_RestoreMusic(player); player->powers[pw_spacetime] = 0; + P_RestoreMusic(player); } // Underwater audio cues @@ -2122,8 +2266,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) if (player->powers[pw_underwater] == 11*TICRATE + 1 && player == &players[consoleplayer]) { - S_StopMusic(); - S_ChangeMusicInternal("drown", false); + P_PlayJingle(player, JT_DROWN); } if (player->powers[pw_underwater] == 25*TICRATE + 1) @@ -2185,10 +2328,6 @@ static void P_CheckInvincibilityTimer(player_t *player) P_SpawnShieldOrb(player); } - if ((player->powers[pw_underwater] <= 11*TICRATE + 1) - && (player->powers[pw_underwater] > 1)) - return; // don't restore music if drowning music is playing - if (!player->powers[pw_super] || (mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC)) P_RestoreMusic(player); } @@ -3410,6 +3549,8 @@ static void P_DoSuperStuff(player_t *player) { player->powers[pw_super] = 0; P_SetPlayerMobjState(player->mo, S_PLAY_STND); + music_stack_noposition = true; // HACK: Do not reposition next music + music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music P_RestoreMusic(player); P_SpawnShieldOrb(player); @@ -3511,6 +3652,8 @@ static void P_DoSuperStuff(player_t *player) } // Resume normal music if you're the console player + music_stack_noposition = true; // HACK: Do not reposition next music + music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music P_RestoreMusic(player); // If you had a shield, restore its visual significance. @@ -5576,13 +5719,14 @@ static void P_NiGHTSMovement(player_t *player) P_DeNightserizePlayer(player); S_StartScreamSound(player->mo, sfx_s3k66); // S_StopSoundByNum(sfx_timeup); // Kill the "out of time" music, if it's playing. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH + music_stack_fadein = 0; // HACK: Change fade-in for restore music P_RestoreMusic(player); // I have my doubts that this is the right place for this... return; } else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE) // S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH - S_ChangeMusicInternal("drown",false); + P_PlayJingle(player, ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? JT_NIGHTSTIMEOUT : JT_SSTIMEOUT); if (player->mo->z < player->mo->floorz) @@ -8754,7 +8898,7 @@ void P_PlayerThink(player_t *player) if (countdown == 11*TICRATE - 1) { if (P_IsLocalPlayer(player)) - S_ChangeMusicInternal("drown", false); + P_PlayJingle(player, JT_DROWN); } // If you've hit the countdown and you haven't made diff --git a/src/s_sound.c b/src/s_sound.c index 3da594fe1..bec6b1ba2 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1229,6 +1229,10 @@ static boolean queue_looping; static UINT32 queue_position; static UINT32 queue_fadeinms; +static musicstack_t music_stack[NUMMUSICSTACKS]; + +static tic_t pause_starttic; + /// ------------------------ /// Music Status /// ------------------------ @@ -1327,6 +1331,254 @@ 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! +/// ------------------------ + +static const musicstack_t empty_music_stack_entry = {{0}}; + +void S_SetStackAdjustmentStart(void) +{ + if (!pause_starttic) + pause_starttic = gametic; +} + +void S_AdjustMusicStackTics(void) +{ + if (pause_starttic) + { + size_t i; + for (i = 0; i < NUMMUSICSTACKS-1; i++) + { + if (!music_stack[i].status) + break; + music_stack[i].tic += gametic - pause_starttic; + } + pause_starttic = 0; + } +} + +static void S_ResetMusicStack() +{ + size_t i; + for (i = 0; i < NUMMUSICSTACKS; i++) + music_stack[i] = empty_music_stack_entry; +} + +static void S_RemoveMusicStackEntry(size_t i) +{ + for (; i < NUMMUSICSTACKS-1; i++) + { + strncpy(music_stack[i].musname, music_stack[i+1].musname, 7); + music_stack[i].musname[6] = 0; + music_stack[i].musflags = music_stack[i+1].musflags; + music_stack[i].looping = music_stack[i+1].looping; + music_stack[i].position = music_stack[i+1].position; + music_stack[i].tic = music_stack[i+1].tic; + music_stack[i].status = music_stack[i+1].status; + + if (!music_stack[i].status) + break; + } + + // clear the last slot + music_stack[NUMMUSICSTACKS-1] = empty_music_stack_entry; +} + +static void S_RemoveMusicStackEntryByStatus(UINT16 status) +{ + int i; + + if (!status) + return; + + for (i = 0; i < NUMMUSICSTACKS-1; i++) + { + if (music_stack[i].status == status) + { + S_RemoveMusicStackEntry(i); + i--; // try this position again + } + else if (!music_stack[i].status) + break; + } +} + +static void S_AddMusicStackEntry(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status) +{ + size_t i; + + // if the first entry is empty, force master onto it + if (!music_stack[0].status && status != JT_MASTER) + S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER); + + // are all slots taken? forget the earliest one (save master) and move down the rest + if (music_stack[NUMMUSICSTACKS-1].status) + S_RemoveMusicStackEntry(1); + + // look for an empty slot to park ourselves + for (i = 0; i < NUMMUSICSTACKS; i++) + { + // entry doesn't exist? park ourselves here! + if (!music_stack[i].status) + { + strncpy(music_stack[i].musname, mname, 7); + music_stack[i].musname[6] = 0; + music_stack[i].musflags = mflags; + music_stack[i].looping = looping; + music_stack[i].position = position; + music_stack[i].tic = gametic; + music_stack[i].status = status; + break; + } + } +} + +static musicstack_t S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 startindex) +{ + size_t i; + + // if the first entry is empty, force master onto it + // fixes a memory corruption bug + if (!music_stack[0].status && status != JT_MASTER) + S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER); + + if (startindex < 0) + startindex = fromfirst ? 0 : NUMMUSICSTACKS-1; + + if (fromfirst) + { + for (i = startindex; i < NUMMUSICSTACKS; i++) + { + if (!music_stack[i].status) // we're counting up, so this must mean we reached the end + break; + else if (!status || music_stack[i].status == status) + { + if (P_EvaluateMusicStatus(music_stack[i].status)) + { + if (!S_MusicExists(music_stack[i].musname, !midi_disabled, !digital_disabled)) // paranoia + S_RemoveMusicStackEntry(i); // then continue + else + return music_stack[i]; + } + else + S_RemoveMusicStackEntry(i); // then continue + } + } + } + else + { + for (i = startindex; i >= 0; i--) + { + if (!music_stack[i].status) // since we're counting down, we have to skip a few... + continue; + else if (!status || music_stack[i].status == status) + { + if (P_EvaluateMusicStatus(music_stack[i].status)) + { + if (!S_MusicExists(music_stack[i].musname, !midi_disabled, !digital_disabled)) // paranoia + S_RemoveMusicStackEntry(i); // then continue + else + return music_stack[i]; + } + else + S_RemoveMusicStackEntry(i); // then continue + } + } + } + + return empty_music_stack_entry; +} + +void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status) +{ + size_t i; + + 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 (i = 0; i < NUMMUSICSTACKS; i++) + { + if (music_stack[i].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 entry; + + if (status) + entry = S_GetMusicStackEntry(status, fromfirst, -1); + else + entry = S_GetMusicStackEntry(JT_NONE, false, -1); + + if (!S_MusicExists(entry.musname, !midi_disabled, !digital_disabled)) + return false; // bad bad bad!! + + // no result, just grab mapmusname + if (!entry.musname[0] || ((status == JT_MASTER || !music_stack[0].status) && !entry.status)) + { + strncpy(entry.musname, mapmusname, 7); + entry.musflags = mapmusflags; + entry.looping = true; + entry.position = mapmusposition; + entry.tic = gametic; + entry.status = JT_MASTER; + } + + if (entry.status == JT_MASTER) + { + mapmuschanged = (boolean)strnicmp(entry.musname, mapmusname, 7); + S_ResetMusicStack(); + } + else + { + if (!entry.status) + return false; + } + + if (!mapmuschanged && 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 (!music_stack_noposition) // HACK: Global boolean to toggle position resuming, e.g., de-superize + newpos = entry.position + (S_GetMusicLength() ? (UINT32)((float)(gametic - entry.tic)/(float)TICRATE*(float)MUSICRATE) : 0); + + if (newpos > 0 && S_MusicPlaying()) + S_SetMusicPosition(newpos); + else + { + S_StopFadingMusic(); + S_SetInternalMusicVolume(100); + } + } + music_stack_noposition = false; + music_stack_fadeout = 0; + music_stack_fadein = JINGLEPOSTFADE; + } + + return true; +} + /// ------------------------ /// Music Playback /// ------------------------ @@ -1529,6 +1781,8 @@ void S_PauseAudio(void) #else I_StopCD(); #endif + + S_SetStackAdjustmentStart(); } void S_ResumeAudio(void) @@ -1538,6 +1792,8 @@ void S_ResumeAudio(void) // resume cd music I_ResumeCD(); + + S_AdjustMusicStackTics(); } void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume) @@ -1623,4 +1879,9 @@ void S_Start(void) if (cv_resetmusic.value) S_StopMusic(); S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + + S_ResetMusicStack(); + music_stack_noposition = false; + music_stack_fadeout = 0; + music_stack_fadein = JINGLEPOSTFADE; } diff --git a/src/s_sound.h b/src/s_sound.h index 538707ffb..241634b12 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -140,6 +140,30 @@ boolean S_SetMusicPosition(UINT32 position); // Get Position of Music UINT32 S_GetMusicPosition(void); +// +// Music Stacking (Jingles) +// + +typedef struct { + char musname[7]; + UINT16 musflags; + boolean looping; + UINT32 position; + tic_t tic; + UINT16 status; +} musicstack_t; + +char music_stack_nextmusname[7]; +boolean music_stack_noposition; +UINT32 music_stack_fadeout; +UINT32 music_stack_fadein; +#define NUMMUSICSTACKS 10 // hahaha wait until someone needs > 10 resumes + +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 // diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 30ef1b27b..2a13c57c8 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -62,7 +62,6 @@ #include "../m_menu.h" #include "../d_main.h" #include "../s_sound.h" -#include "../i_sound.h" // midi pause/unpause #include "../i_joy.h" #include "../st_stuff.h" #include "../g_game.h" @@ -566,7 +565,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) // Tell game we got focus back, resume music if necessary window_notinfocus = false; if (!paused) - I_ResumeSong(); //resume it + S_ResumeAudio(); //resume it if (!firsttimeonmouse) { @@ -578,7 +577,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { // Tell game we lost focus, pause music window_notinfocus = true; - I_PauseSong(); + S_PauseAudio(); if (!disable_mouse) { diff --git a/src/win32/win_main.c b/src/win32/win_main.c index bfe620a43..8a29f7e18 100644 --- a/src/win32/win_main.c +++ b/src/win32/win_main.c @@ -42,7 +42,7 @@ #include "fabdxlib.h" #include "win_main.h" #include "win_dbg.h" -#include "../i_sound.h" // midi pause/unpause +#include "../s_sound.h" // pause sound with handling #include "../g_input.h" // KEY_MOUSEWHEELxxx #include "../screen.h" // for BASEVID* @@ -110,9 +110,9 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR // pause music when alt-tab if (appActive && !paused) - I_ResumeSong(); + S_ResumeAudio(); else if (!paused) - I_PauseSong(); + S_PauseAudio(); { HANDLE ci = GetStdHandle(STD_INPUT_HANDLE); DWORD mode; From be2342799ed673cd767b15af5df7acc49a312cd5 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 18 Sep 2018 15:10:42 -0400 Subject: [PATCH 02/10] Lua P_RestoreMusic else if fix --- src/lua_baselib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 6a8784259..093683660 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -719,7 +719,7 @@ static int lib_pRestoreMusic(lua_State *L) NOHUD if (!player) return LUA_ErrInvalid(L, "player_t"); - else if (P_IsLocalPlayer(player)) + if (P_IsLocalPlayer(player)) P_RestoreMusic(player); return 0; } From e08bdec216b6c8cfa06a0a011fd07785e6aff321 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 19 Sep 2018 18:42:11 -0400 Subject: [PATCH 03/10] MP Jingle: Buildbot fixes --- src/s_sound.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index bec6b1ba2..345ba0449 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1336,8 +1336,18 @@ UINT32 S_GetMusicPosition(void) /// In this section: mazmazz doesn't know how to do dynamic arrays or struct pointers! /// ------------------------ +// make the buildbots happy, because this safely zeroes out a struct +// https://stackoverflow.com/q/1538943 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + static const musicstack_t empty_music_stack_entry = {{0}}; +#pragma clang diagnostic pop +#pragma GCC diagnostic pop + void S_SetStackAdjustmentStart(void) { if (!pause_starttic) @@ -1437,7 +1447,7 @@ static void S_AddMusicStackEntry(const char *mname, UINT16 mflags, boolean loopi static musicstack_t S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 startindex) { - size_t i; + INT16 i; // if the first entry is empty, force master onto it // fixes a memory corruption bug @@ -1469,7 +1479,7 @@ static musicstack_t S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 } else { - for (i = startindex; i >= 0; i--) + for (i = startindex; i >= 0; i--) // this line is why i is signed; otherwise, would wrap around to max value { if (!music_stack[i].status) // since we're counting down, we have to skip a few... continue; From 17c796fe74fb591e6c982ed39b31419faa5b5c27 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 19 Sep 2018 19:24:35 -0400 Subject: [PATCH 04/10] MP Jingle: Buildbot fixes --- src/s_sound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 345ba0449..9a5e7247b 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1369,7 +1369,7 @@ void S_AdjustMusicStackTics(void) } } -static void S_ResetMusicStack() +static void S_ResetMusicStack(void) { size_t i; for (i = 0; i < NUMMUSICSTACKS; i++) @@ -1554,7 +1554,7 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst) if (entry.status == JT_MASTER) { - mapmuschanged = (boolean)strnicmp(entry.musname, mapmusname, 7); + mapmuschanged = strnicmp(entry.musname, mapmusname, 7); S_ResetMusicStack(); } else From af5aecb2460e325bcea316de02669dfd59f1ce4b Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 19 Sep 2018 19:51:04 -0400 Subject: [PATCH 05/10] MP Jingle: Better empty_music_stack_entry initialization (buildbots) --- src/s_sound.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 3708673e3..2f53776bd 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1336,17 +1336,7 @@ UINT32 S_GetMusicPosition(void) /// In this section: mazmazz doesn't know how to do dynamic arrays or struct pointers! /// ------------------------ -// make the buildbots happy, because this safely zeroes out a struct -// https://stackoverflow.com/q/1538943 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-field-initializers" - -static const musicstack_t empty_music_stack_entry = {{0}}; - -#pragma clang diagnostic pop -#pragma GCC diagnostic pop +static const musicstack_t empty_music_stack_entry = {"\0", 0, false, 0, 0, 0}; void S_SetStackAdjustmentStart(void) { From 78c1b99cd4434d6257ec92e3c248eda36830ecf9 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 20 Sep 2018 16:37:14 -0400 Subject: [PATCH 06/10] MP Jingle: Changed music_stacks from fixed array to next/prev chain --- src/s_sound.c | 238 +++++++++++++++++++++++++------------------------- src/s_sound.h | 7 +- 2 files changed, 126 insertions(+), 119 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index f8e0376b4..3fa060340 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1229,8 +1229,6 @@ static boolean queue_looping; static UINT32 queue_position; static UINT32 queue_fadeinms; -static musicstack_t music_stack[NUMMUSICSTACKS]; - static tic_t pause_starttic; /// ------------------------ @@ -1336,7 +1334,8 @@ UINT32 S_GetMusicPosition(void) /// In this section: mazmazz doesn't know how to do dynamic arrays or struct pointers! /// ------------------------ -static const musicstack_t empty_music_stack_entry = {"\0", 0, false, 0, 0, 0}; +static musicstack_t *music_stacks = NULL; +static musicstack_t *last_music_stack = NULL; void S_SetStackAdjustmentStart(void) { @@ -1348,152 +1347,145 @@ void S_AdjustMusicStackTics(void) { if (pause_starttic) { - size_t i; - for (i = 0; i < NUMMUSICSTACKS-1; i++) - { - if (!music_stack[i].status) - break; - music_stack[i].tic += gametic - 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) { - size_t i; - for (i = 0; i < NUMMUSICSTACKS; i++) - music_stack[i] = empty_music_stack_entry; + 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(size_t i) +static void S_RemoveMusicStackEntry(musicstack_t *entry) { - for (; i < NUMMUSICSTACKS-1; i++) + musicstack_t *mst; + for (mst = music_stacks; mst; mst = mst->next) { - strncpy(music_stack[i].musname, music_stack[i+1].musname, 7); - music_stack[i].musname[6] = 0; - music_stack[i].musflags = music_stack[i+1].musflags; - music_stack[i].looping = music_stack[i+1].looping; - music_stack[i].position = music_stack[i+1].position; - music_stack[i].tic = music_stack[i+1].tic; - music_stack[i].status = music_stack[i+1].status; + 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; - if (!music_stack[i].status) break; + } } - - // clear the last slot - music_stack[NUMMUSICSTACKS-1] = empty_music_stack_entry; + Z_Free(entry); } static void S_RemoveMusicStackEntryByStatus(UINT16 status) { - int i; + musicstack_t *mst, *mst_next; if (!status) return; - for (i = 0; i < NUMMUSICSTACKS-1; i++) + for (mst = music_stacks; mst; mst = mst_next) { - if (music_stack[i].status == status) - { - S_RemoveMusicStackEntry(i); - i--; // try this position again - } - else if (!music_stack[i].status) - break; + 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) { - size_t i; + musicstack_t *mst, *new_mst; // if the first entry is empty, force master onto it - if (!music_stack[0].status && status != JT_MASTER) - S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER); + if (!music_stacks) + { + music_stacks = Z_Calloc(sizeof (*mst), PU_MUSIC, NULL); + strncpy(music_stacks->musname, (status == JT_MASTER ? mname : mapmusname), 7); + music_stacks->musflags = (status == JT_MASTER ? mflags : mapmusflags); + music_stacks->looping = (status == JT_MASTER ? looping : true); + music_stacks->position = (status == JT_MASTER ? position : S_GetMusicPosition()); + music_stacks->tic = gametic; + music_stacks->status = JT_MASTER; - // are all slots taken? forget the earliest one (save master) and move down the rest - if (music_stack[NUMMUSICSTACKS-1].status) - S_RemoveMusicStackEntry(1); + if (status == JT_MASTER) + return; // we just added the user's entry here + } // look for an empty slot to park ourselves - for (i = 0; i < NUMMUSICSTACKS; i++) - { - // entry doesn't exist? park ourselves here! - if (!music_stack[i].status) - { - strncpy(music_stack[i].musname, mname, 7); - music_stack[i].musname[6] = 0; - music_stack[i].musflags = mflags; - music_stack[i].looping = looping; - music_stack[i].position = position; - music_stack[i].tic = gametic; - music_stack[i].status = status; - break; - } - } + 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; + + 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) +static musicstack_t *S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 startindex) { - INT16 i; + 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_stack[0].status && status != JT_MASTER) + if (!music_stacks && status != JT_MASTER) S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER); - if (startindex < 0) - startindex = fromfirst ? 0 : NUMMUSICSTACKS-1; - - if (fromfirst) + if (startindex >= 0) { - for (i = startindex; i < NUMMUSICSTACKS; i++) - { - if (!music_stack[i].status) // we're counting up, so this must mean we reached the end - break; - else if (!status || music_stack[i].status == status) - { - if (P_EvaluateMusicStatus(music_stack[i].status)) - { - if (!S_MusicExists(music_stack[i].musname, !midi_disabled, !digital_disabled)) // paranoia - S_RemoveMusicStackEntry(i); // then continue - else - return music_stack[i]; - } - else - S_RemoveMusicStackEntry(i); // then continue - } - } + 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) { - for (i = startindex; i >= 0; i--) // this line is why i is signed; otherwise, would wrap around to max value + mst_next = (fromfirst ? mst->next : mst->prev); + + if (!status || mst->status == status) { - if (!music_stack[i].status) // since we're counting down, we have to skip a few... - continue; - else if (!status || music_stack[i].status == status) + if (P_EvaluateMusicStatus(mst->status)) { - if (P_EvaluateMusicStatus(music_stack[i].status)) - { - if (!S_MusicExists(music_stack[i].musname, !midi_disabled, !digital_disabled)) // paranoia - S_RemoveMusicStackEntry(i); // then continue - else - return music_stack[i]; - } + if (!S_MusicExists(mst->musname, !midi_disabled, !digital_disabled)) // paranoia + S_RemoveMusicStackEntry(mst); // then continue else - S_RemoveMusicStackEntry(i); // then continue + return mst; } + else + S_RemoveMusicStackEntry(mst); // then continue } } - return empty_music_stack_entry; + return NULL; } void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status) { - size_t i; + musicstack_t *mst; if (!status) // we use this as a null indicator, don't push { @@ -1502,9 +1494,9 @@ void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 pos } else if (status == JT_MASTER) // enforce only one JT_MASTER { - for (i = 0; i < NUMMUSICSTACKS; i++) + for (mst = music_stacks; mst; mst = mst->next) { - if (music_stack[i].status == JT_MASTER) + if (mst->status == JT_MASTER) { CONS_Alert(CONS_ERROR, "Music stack can only have one JT_MASTER entry.\n"); return; @@ -1521,47 +1513,58 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst) { UINT32 newpos = 0; boolean mapmuschanged = false; - musicstack_t entry; + musicstack_t *result; + musicstack_t *entry = Z_Calloc(sizeof (*result), PU_MUSIC, NULL); if (status) - entry = S_GetMusicStackEntry(status, fromfirst, -1); + result = S_GetMusicStackEntry(status, fromfirst, -1); else - entry = S_GetMusicStackEntry(JT_NONE, false, -1); + result = S_GetMusicStackEntry(JT_NONE, false, -1); - if (!S_MusicExists(entry.musname, !midi_disabled, !digital_disabled)) - return false; // bad bad bad!! + if (result && !S_MusicExists(result->musname, !midi_disabled, !digital_disabled)) + { + 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 (!entry.musname[0] || ((status == JT_MASTER || !music_stack[0].status) && !entry.status)) + 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; + strncpy(entry->musname, mapmusname, 7); + entry->musflags = mapmusflags; + entry->looping = true; + entry->position = mapmusposition; + entry->tic = gametic; + entry->status = JT_MASTER; } - if (entry.status == JT_MASTER) + if (entry->status == JT_MASTER) { - mapmuschanged = strnicmp(entry.musname, mapmusname, 7); + mapmuschanged = strnicmp(entry->musname, mapmusname, 7); S_ResetMusicStack(); } - else + else if (!entry->status) { - if (!entry.status) - return false; + Z_Free(entry); + return false; } - if (!mapmuschanged && strncmp(entry.musname, S_MusicName(), 7)) // don't restart music if we're already playing it + if (!mapmuschanged && 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); + 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); + S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, 0, music_stack_fadein); if (!music_stack_noposition) // HACK: Global boolean to toggle position resuming, e.g., de-superize - newpos = entry.position + (S_GetMusicLength() ? (UINT32)((float)(gametic - entry.tic)/(float)TICRATE*(float)MUSICRATE) : 0); + newpos = entry->position + (S_GetMusicLength() ? (UINT32)((float)(gametic - entry->tic)/(float)TICRATE*(float)MUSICRATE) : 0); if (newpos > 0 && S_MusicPlaying()) S_SetMusicPosition(newpos); @@ -1576,6 +1579,7 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst) music_stack_fadein = JINGLEPOSTFADE; } + Z_Free(entry); return true; } diff --git a/src/s_sound.h b/src/s_sound.h index 241634b12..dcf851a87 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -144,20 +144,23 @@ UINT32 S_GetMusicPosition(void); // Music Stacking (Jingles) // -typedef struct { +typedef struct musicstack_s +{ char musname[7]; UINT16 musflags; boolean looping; UINT32 position; tic_t tic; UINT16 status; + + struct musicstack_s *prev; + struct musicstack_s *next; } musicstack_t; char music_stack_nextmusname[7]; boolean music_stack_noposition; UINT32 music_stack_fadeout; UINT32 music_stack_fadein; -#define NUMMUSICSTACKS 10 // hahaha wait until someone needs > 10 resumes void S_SetStackAdjustmentStart(void); void S_AdjustMusicStackTics(void); From d8addda298e97f1c566b3da1680379cd59207188 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 20 Sep 2018 18:30:21 -0400 Subject: [PATCH 07/10] MP Jingle: Add fade timing hack after ending a non-looping song --- src/sdl/mixer_sound.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index fc6ccf50c..85c4c23c1 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -84,6 +84,7 @@ static UINT32 fading_timer; static UINT32 fading_duration; static INT32 fading_id; static void (*fading_callback)(void); +static boolean fading_nocleanup; #ifdef HAVE_LIBGME static Music_Emu *gme; @@ -99,7 +100,12 @@ static void var_cleanup(void) songpaused = is_looping =\ is_fading = false; - fading_callback = NULL; + // HACK: See music_loop, where we want the fade timing to proceed after a non-looping + // song has stopped playing + if (!fading_nocleanup) + fading_callback = NULL; + else + fading_nocleanup = false; // use it once, set it back immediately internal_volume = 100; } @@ -125,6 +131,8 @@ void I_StartupSound(void) return; } + fading_nocleanup = false; + var_cleanup(); music = NULL; @@ -537,7 +545,15 @@ static void music_loop(void) music_bytes = loop_point*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition) } else + { + // HACK: Let fade timing proceed beyond the end of a + // non-looping song. This is a specific case where the timing + // should persist after stopping a song, so I don't believe + // this should apply every time the user stops a song. + // This is auto-unset in var_cleanup, called by I_StopSong + fading_nocleanup = true; I_StopSong(); + } } static UINT32 music_fade(UINT32 interval, void *param) @@ -1151,7 +1167,10 @@ boolean I_PlaySong(boolean looping) void I_StopSong(void) { - I_StopFadingSong(); + // HACK: See music_loop on why we want fade timing to proceed + // after end of song + if (!fading_nocleanup) + I_StopFadingSong(); #ifdef HAVE_LIBGME if (gme) @@ -1269,6 +1288,8 @@ void I_StopFadingSong(void) SDL_RemoveTimer(fading_id); is_fading = false; fading_source = fading_target = fading_timer = fading_duration = fading_id = 0; + // don't unset fading_nocleanup here just yet; fading_callback is cleaned up + // in var_cleanup() } boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)) From d14db911331c972db611aa68899f1399cd40192f Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 20 Sep 2018 18:57:37 -0400 Subject: [PATCH 08/10] MP Jingle: Remove P_RestoreMusic from space countdown (no reason to have it) --- src/p_enemy.c | 4 +--- src/p_user.c | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 11e3856c4..ceeb6c97d 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3232,10 +3232,8 @@ void A_WaterShield(mobj_t *actor) player->powers[pw_underwater] = 0; if (player->powers[pw_spacetime] > 1) - { player->powers[pw_spacetime] = 0; - P_RestoreMusic(player); - } + S_StartSound(player->mo, actor->info->seesound); } diff --git a/src/p_user.c b/src/p_user.c index cb14814a2..ea58b7eeb 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2255,10 +2255,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) } if (player->powers[pw_spacetime] > 1 && !P_InSpaceSector(player->mo)) - { player->powers[pw_spacetime] = 0; - P_RestoreMusic(player); - } // Underwater audio cues if (P_IsLocalPlayer(player) && !player->bot) From e0f9cdcfd1a78edea2fbb5c81adc1a9d3587f572 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 21 Sep 2018 15:42:26 -0400 Subject: [PATCH 09/10] MP Jingle: Fix underwater music not restoring on air bubble or shield --- src/p_enemy.c | 7 +++++-- src/p_inter.c | 2 ++ src/p_user.c | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index ceeb6c97d..1f57d1901 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3227,9 +3227,12 @@ void A_WaterShield(mobj_t *actor) } if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1) + { + player->powers[pw_underwater] = 0; P_RestoreMusic(player); - - player->powers[pw_underwater] = 0; + } + else + player->powers[pw_underwater] = 0; if (player->powers[pw_spacetime] > 1) player->powers[pw_spacetime] = 0; diff --git a/src/p_inter.c b/src/p_inter.c index 7a17fbcf6..2c5c85ed3 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1429,7 +1429,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Eaten by player! if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1) + player->powers[pw_underwater] = underwatertics + 1; P_RestoreMusic(player); + } if (player->powers[pw_underwater] < underwatertics + 1) player->powers[pw_underwater] = underwatertics + 1; diff --git a/src/p_user.c b/src/p_user.c index ea58b7eeb..f41459147 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9185,9 +9185,12 @@ void P_PlayerThink(player_t *player) if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)) { if (player->powers[pw_underwater] <= 12*TICRATE+1) + { + player->powers[pw_underwater] = 0; P_RestoreMusic(player); //incase they were about to drown - - player->powers[pw_underwater] = 0; + } + else + player->powers[pw_underwater] = 0; } else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer player->powers[pw_underwater]--; From f2033350fccf8bc512ab69ffcad2294ca55c6f68 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 21 Sep 2018 15:53:03 -0400 Subject: [PATCH 10/10] MP Jingle: A brace (Underwater restore, p_inter.c) --- src/p_inter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_inter.c b/src/p_inter.c index 2c5c85ed3..796759514 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1429,6 +1429,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Eaten by player! if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1) + { player->powers[pw_underwater] = underwatertics + 1; P_RestoreMusic(player); }