MusicPlus core: Song length, positioning, and fading features with Linedef Exec and Lua support
This is the reason why music-cleanup
was proposed (!278 (merged)). This is also a prerequisite for libopenmpt and non-native MIDI.
This branch adds music seeking and fading to the engine, as well as Linedef Exec and Lua support.
See comments for Linedef Exec and Lua documentation.
Implementation
- The position/seeking methods depend on a new music tag
LENGTHMS=milliseconds
- This tag is necessary because SDL Mixer cannot detect song length.
- Without this tag, the seeking will still happen, with the following side effects:
- Seeking past the end of the song may reset the "listening" position at 0, but the internal position counter will increment from the erroneous seek position. It will never reset to 0 until the music is changed.
- Any future features that depend on reading song position will not work correctly (e.g., jingle resuming) until the music is changed.
- The game spits a warning in DEVMODE 2 (detailed) if this tag is not present.
- OGG, FLAC, and MP3 support this length tag. GME and MOD do not need this tag. MIDI cannot use this tag.
- Auto-tag all your music lumps by running your WAD through musiclengthms.zip (Source).
music.dta
could use this treatment for future releases.
- The fading method implements an "internal music volume", a percentage of the user's configured game volume.
- When an internal volume of less-than 100% is applied, the game adjusts the playback volume to make the song quieter. This is always relative to the user-configured volume. Fading is accomplished by incrementing this internal volume per millisecond.
- I do not use SDL Mixer's
Mix_FadeIn/OutMusic()
methods due to these reasons:- They are thread-blocking, e.g., the game window cannot exit until the fade is completed
- They cannot be interrupted in the middle of the fade
- They do not respect the user's configured volume (at least not easily)
- They do not work on GME
- MIDI cannot fade due to the Win32 volume hack
Test Files
- test_music.wad - Collection of music lumps in different formats
-
test_musicplus-safe.lua - Lua functions for testing; type
musichelp
in console for a list of these- Sample:
musicchange MAP02M 1 0 10000 2000 1000
- Switch to MAP02M music, looping enabled (1), track 0, jump to position 10000, fade out for 2000 ms, fade in for 1000 ms
- Sample:
- test_le-music.wad - Linedef executor test for fading and positioning functions
- Srb2-21-changemusic.cfg - ZoneBuilder config for the updated Change Music linedef exec features
- EXE binaries
Merge request reports
Activity
Line Exec 413: Change Music with fading and positioning features
Linedef Exec Change Music updated to support the new seeking and fading features.
What it already does
-
Front Upper
: Music name -
Front Lower
: Track -
Solid Midtex
: Looping = False -
No Climb
: Music changes for everyone -
Block Enemies
:MUSIC_RELOADRESET
= False
What we added
-
Front Middle
: Position to jump to -
ML_EFFECT1
: Start position from current song position, andFront Middle
adds or subtracts from this value -
Front X Offset
: Fade out milliseconds from old song, 0 for instant change -
Front Y Offset
: Fade in milliseconds to new song -
ML_EFFECT2
: Fade to custom volume target-
Front Y Offset
: Millisecond length of custom fade -
Back X Offset
: Volume to fade to, 0-100 -
Back Y Offset
: Volume to fade from (or 0 for current), 1-100
-
-
ML_BOUNCY
:MUSIC_FORCERELOAD
= True
Edited by mazmazz-
New Lua Functions
See e2e34598 for brand new Lua functions. I'm pretty sure these are safe from local state hackery, but this commit can be reverted otherwise.
-
S_ChangeMusic(string musicname, [boolean looping?, [player_t player, [int tracknum, [int position, [int prefadems, [int fadeinms]]]]]])
-
position
- Millisecond position to start at upon song change -
prefadems
- Milliseconds to fade out of the current song before changing to the new song -
fadeinms
- Milliseconds to fade into the new song
-
-
S_MusicExists(name[, checkMIDI[, checkDigi]])
Check if musicname lump exists. SetcheckMIDI
orcheckDigi
flags to enable checking D_ or O_ lumps, respectively. Both flags default to true.
-
S_SetInternalMusicVolume(int volume, [player_t player])
- Immediately set the internal volume level between 0-100%
-
S_FadeMusic(int target_volume, int ms, [player_t player])
orS_FadeMusic(int target_volume, int ms, [int source_volume, [player_t player]])
- Fade music from source volume to target volume, 0-100%. If
source_volume
is not specified, the source volume is the current internal volume.
- Fade music from source volume to target volume, 0-100%. If
-
S_FadeOutStopMusic(int ms, [player_t player])
- Fade music from current internal volume to 0%, then stop the music.
-
S_StopFadingMusic([player_t player])
- Stop any current fade from running. The music remains playing at the current internal volume.
Edited by mazmazz-
hud.MusicExists
?Edited by toasterPossibly, that's a good idea. I wonder what MI or anyone else would prefer. The solutions are:
- Make Lua functions available as HUD-only
- Make Lua functions available always, regardless of net safety
- Implement in code but disable Lua functions by default; hide behind
#ifdef HAVE_LUA_MUSICPLUS
- Don't implement in code
Included in this MR are what I believe to be net-safe functions:
-
S_ChangeMusic
(fade/position update) S_SetInternalMusicVolume
S_StopFadingMusic
S_FadeMusic
S_FadeOutStopMusic
Excluded for net-unsafety (see https://git.magicalgirl.moe/digiku/SRB2/merge_requests/2 for code):
-
S_MusicType
-- Format of currently music -
S_MusicPlaying
-- Is there any music currently playing? -
S_MusicPaused
-- Is the music paused? -
S_MusicName
-- Name of current music -
S_MusicInfo
-- Get type, name, and statuses of current music? This is a multi-function. -
S_MusicExists
-- Lump checker for O_/D_ music S_GetMusicLength
S_SetMusicLoopPoint
S_GetMusicLoopPoint
S_SetMusicPosition
S_GetMusicPosition
S_PauseMusic
S_ResumeMusic
Already in vanilla:
S_StartSound
S_StartSoundAtVolume
S_StopSound
S_ChangeMusic
S_SpeedMusic
S_StopMusic
S_OriginPlaying
S_IdPlaying
S_SoundPlaying
It's worth noting that some of the current sound functions are already net-unsafe as it is (
S_SoundPlaying
in particular)Edited by mazmazz