1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-16 16:09:42 +01:00
VVVVVV/desktop_version/src/Music.cpp

572 lines
14 KiB
C++
Raw Normal View History

#define MUSIC_DEFINITION
#include "Music.h"
2020-01-01 21:29:24 +01:00
#include <SDL.h>
#include <physfsrwops.h>
2020-01-01 21:29:24 +01:00
#include "BinaryBlob.h"
#include "Game.h"
#include "Graphics.h"
#include "Map.h"
#include "Script.h"
#include "UtilityClass.h"
#include "Vlogging.h"
2020-01-01 21:29:24 +01:00
musicclass::musicclass(void)
{
safeToProcessMusic= false;
m_doFadeInVol = false;
m_doFadeOutVol = false;
musicVolume = 0;
user_music_volume = USER_VOLUME_MAX;
user_sound_volume = USER_VOLUME_MAX;
currentsong = 0;
nicechange = -1;
nicefade = false;
quick_fade = true;
usingmmmmmm = false;
}
void musicclass::init(void)
2020-01-01 21:29:24 +01:00
{
soundTracks.push_back(SoundTrack( "sounds/jump.wav" ));
soundTracks.push_back(SoundTrack( "sounds/jump2.wav" ));
soundTracks.push_back(SoundTrack( "sounds/hurt.wav" ));
soundTracks.push_back(SoundTrack( "sounds/souleyeminijingle.wav" ));
soundTracks.push_back(SoundTrack( "sounds/coin.wav" ));
soundTracks.push_back(SoundTrack( "sounds/save.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crumble.wav" ));
soundTracks.push_back(SoundTrack( "sounds/vanish.wav" ));
soundTracks.push_back(SoundTrack( "sounds/blip.wav" ));
soundTracks.push_back(SoundTrack( "sounds/preteleport.wav" ));
soundTracks.push_back(SoundTrack( "sounds/teleport.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew1.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew2.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew3.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew4.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew5.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crew6.wav" ));
soundTracks.push_back(SoundTrack( "sounds/terminal.wav" ));
soundTracks.push_back(SoundTrack( "sounds/gamesaved.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crashing.wav" ));
soundTracks.push_back(SoundTrack( "sounds/blip2.wav" ));
soundTracks.push_back(SoundTrack( "sounds/countdown.wav" ));
soundTracks.push_back(SoundTrack( "sounds/go.wav" ));
soundTracks.push_back(SoundTrack( "sounds/crash.wav" ));
soundTracks.push_back(SoundTrack( "sounds/combine.wav" ));
soundTracks.push_back(SoundTrack( "sounds/newrecord.wav" ));
soundTracks.push_back(SoundTrack( "sounds/trophy.wav" ));
soundTracks.push_back(SoundTrack( "sounds/rescue.wav" ));
2020-01-01 21:29:24 +01:00
#ifdef VVV_COMPILEMUSIC
binaryBlob musicWriteBlob;
#define FOREACH_TRACK(blob, track_name) blob.AddFileToBinaryBlob("data/" track_name);
TRACK_NAMES(musicWriteBlob)
#undef FOREACH_TRACK
2020-01-01 21:29:24 +01:00
musicWriteBlob.writeBinaryBlob("data/BinaryMusic.vvv");
musicWriteBlob.clear();
2020-01-01 21:29:24 +01:00
#endif
num_mmmmmm_tracks = 0;
num_pppppp_tracks = 0;
if (!mmmmmm_blob.unPackBinary("mmmmmm.vvv"))
{
if (pppppp_blob.unPackBinary("vvvvvvmusic.vvv"))
{
vlog_info("Loading music from PPPPPP blob...");
mmmmmm = false;
usingmmmmmm=false;
int index;
SDL_RWops* rw;
#define FOREACH_TRACK(blob, track_name) \
index = blob.getIndex("data/" track_name); \
rw = SDL_RWFromMem(blob.getAddress(index), blob.getSize(index)); \
musicTracks.push_back(MusicTrack( rw ));
TRACK_NAMES(pppppp_blob)
#undef FOREACH_TRACK
}
else
{
vlog_info("Loading music from loose files...");
SDL_RWops* rw;
#define FOREACH_TRACK(_, track_name) \
rw = PHYSFSRWOPS_openRead(track_name); \
musicTracks.push_back(MusicTrack( rw ));
TRACK_NAMES(_)
#undef FOREACH_TRACK
}
}
else
{
vlog_info("Loading PPPPPP and MMMMMM blobs...");
mmmmmm = true;
int index;
SDL_RWops *rw;
2020-01-01 21:29:24 +01:00
#define FOREACH_TRACK(blob, track_name) \
index = blob.getIndex("data/" track_name); \
if (index >= 0 && index < blob.max_headers) \
{ \
rw = SDL_RWFromConstMem(blob.getAddress(index), blob.getSize(index)); \
if (rw == NULL) \
{ \
vlog_error("Unable to read music file header: %s", SDL_GetError()); \
} \
else \
{ \
musicTracks.push_back(MusicTrack( rw )); \
} \
}
TRACK_NAMES(mmmmmm_blob)
num_mmmmmm_tracks += musicTracks.size();
size_t index_ = 0;
while (mmmmmm_blob.nextExtra(&index_))
{
rw = SDL_RWFromConstMem(mmmmmm_blob.getAddress(index_), mmmmmm_blob.getSize(index_));
musicTracks.push_back(MusicTrack( rw ));
num_mmmmmm_tracks++;
index_++;
}
bool ohCrap = pppppp_blob.unPackBinary("vvvvvvmusic.vvv");
SDL_assert(ohCrap && "Music not found!");
TRACK_NAMES(pppppp_blob)
2020-01-01 21:29:24 +01:00
#undef FOREACH_TRACK
}
2020-01-01 21:29:24 +01:00
num_pppppp_tracks += musicTracks.size() - num_mmmmmm_tracks;
SDL_RWops* rw;
size_t index_ = 0;
while (pppppp_blob.nextExtra(&index_))
{
rw = SDL_RWFromConstMem(pppppp_blob.getAddress(index_), pppppp_blob.getSize(index_));
musicTracks.push_back(MusicTrack( rw ));
num_pppppp_tracks++;
index_++;
}
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
}
void musicclass::destroy(void)
{
for (size_t i = 0; i < soundTracks.size(); ++i)
{
Mix_FreeChunk(soundTracks[i].sound);
}
soundTracks.clear();
// Before we free all the music: stop playing music, else SDL2_mixer
// will call SDL_Delay() if we are fading, resulting in no-draw frames
Mix_HaltMusic();
for (size_t i = 0; i < musicTracks.size(); ++i)
{
Mix_FreeMusic(musicTracks[i].m_music);
}
musicTracks.clear();
pppppp_blob.clear();
mmmmmm_blob.clear();
}
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
void musicclass::play(int t)
2020-01-01 21:29:24 +01:00
{
if (mmmmmm && usingmmmmmm)
{
// Don't conjoin this if-statement with the above one...
if (num_mmmmmm_tracks > 0)
{
t %= num_mmmmmm_tracks;
}
}
else if (num_pppppp_tracks > 0)
{
t %= num_pppppp_tracks;
}
if (mmmmmm && !usingmmmmmm)
{
t += num_mmmmmm_tracks;
}
safeToProcessMusic = true;
if (currentsong == t && !m_doFadeOutVol)
{
return;
}
currentsong = t;
if (t == -1)
{
return;
}
if (!INBOUNDS_VEC(t, musicTracks))
{
vlog_error("play() out-of-bounds!");
currentsong = -1;
return;
}
if (currentsong == 0 || currentsong == 7 || (!map.custommode && (currentsong == 0+num_mmmmmm_tracks || currentsong == 7+num_mmmmmm_tracks)))
{
// Level Complete theme, no fade in or repeat
if (Mix_PlayMusic(musicTracks[t].m_music, 0) == -1)
{
vlog_error("Mix_PlayMusic: %s", Mix_GetError());
}
else
{
m_doFadeInVol = false;
m_doFadeOutVol = false;
musicVolume = MIX_MAX_VOLUME;
Mix_VolumeMusic(musicVolume);
}
}
else
{
if (m_doFadeOutVol)
{
// We're already fading out
nicechange = t;
nicefade = true;
currentsong = -1;
if (quick_fade)
{
fadeMusicVolumeOut(500); // fade out quicker
}
else
{
quick_fade = true;
}
}
else if (Mix_PlayMusic(musicTracks[t].m_music, -1) == -1)
{
vlog_error("Mix_PlayMusic: %s", Mix_GetError());
}
else
{
m_doFadeInVol = false;
m_doFadeOutVol = false;
fadeMusicVolumeIn(3000);
}
}
2020-01-01 21:29:24 +01:00
}
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
void musicclass::resume()
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
{
Mix_ResumeMusic();
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
}
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
void musicclass::resumefade(const int fadein_ms)
{
resume();
fadeMusicVolumeIn(fadein_ms);
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
}
void musicclass::fadein(void)
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
{
resumefade(3000); // 3000 ms fadein
Fix resumemusic/musicfadein not working It seems like they were unfinished. This commit makes them properly work. When a track is stopped with stopmusic() or musicfadeout(), resumemusic() will resume from where the track stopped. musicfadein() does the same but does it with a gradual fade instead of suddenly playing it at full volume. I changed several interfaces around for this. First, setting currentsong to -1 when music is stopped is handled in the hook callback that gets called by SDL_mixer whenever the music stops. Otherwise, it'd be problematic if currentsong was set to -1 when the song starts fading out instead of when the song actually ends. Also, music.play() has a few optional arguments now, to reduce the copying-and-pasting of music code. Lastly, we have to roll our own tracker of music length by using SDL_GetPerformanceCounter(), because there's no way to get the music position if a song fades out. (We could implicitly keep the music position if we abruptly stopped the song using Mix_PauseMusic(), and resume it using Mix_ResumeMusic(), but ignoring the fact that those two functions are also used on the unfocus-pause (which, as it turns out, is basically a non-issue because the unfocus-pause can use some other functions), there's no equivalent for fading out, i.e. there's no "fade out and pause when it fully fades out" function in SDL_mixer.) And then we have to account for the unfocus-pause in our manual tracker. Other than that, these commands are now fully functional.
2020-06-27 10:31:09 +02:00
}
void musicclass::pause(void)
{
Mix_PauseMusic();
}
void musicclass::haltdasmusik(void)
2020-01-01 21:29:24 +01:00
{
/* Just pauses music. This is intended. */
pause();
currentsong = -1;
m_doFadeInVol = false;
m_doFadeOutVol = false;
2020-01-01 21:29:24 +01:00
}
void musicclass::silencedasmusik(void)
2020-01-01 21:29:24 +01:00
{
musicVolume = 0;
m_doFadeInVol = false;
m_doFadeOutVol = false;
2020-01-01 21:29:24 +01:00
}
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
struct FadeState
{
int start_volume;
int end_volume;
int duration_ms;
int step_ms;
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
};
static struct FadeState fade;
enum FadeCode
{
Fade_continue,
Fade_finished
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
};
static enum FadeCode processmusicfade(struct FadeState* state, int* volume)
{
int range;
int new_volume;
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
if (state->duration_ms == 0 /* Fast path. */
|| state->start_volume == state->end_volume /* Fast path. */
|| state->step_ms >= state->duration_ms /* We're finished. */)
{
*volume = state->end_volume;
state->step_ms = 0;
return Fade_finished;
}
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
range = state->end_volume - state->start_volume;
new_volume = range * state->step_ms / state->duration_ms;
new_volume += state->start_volume;
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
*volume = new_volume;
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
state->step_ms += game.get_timestep();
Fix fade volume durations being incorrect The previous fade system used only one variable, the amount of volume to fade per frame. However, this variable was an integer, meaning any decimal portion would be truncated, and would lead to a longer fade duration than intended. The fade per volume is calculated by doing MIX_MAX_VOLUME / (fade_ms / game.get_timestep()). MIX_MAX_VOLUME is 128, and game.get_timestep() is usually 34, so a 3000 millisecond fade would be calculated as 128 / (3000 / 34). 3000 / 34 is 88.235..., but that gets truncated to 88, and then 128 / 88 becomes 1.454545..., which then gets truncated to 1. This essentially means 1 is added to or subtracted from the volume every frame, and given that the max volume is 128, this means that the fade lasts for 128 frames. Now, instead of the fade duration lasting 3 seconds, the fade now lasts for 128 frames, which is 128 * 34 / 1000 = 4.352 seconds long. This could be fixed using floats, but when you introduce floats, you now have 1.9999998 problems. For instance, I'm concerned about floating-point determinism issues. What I've done instead is switch the system to use four different variables instead: the start volume, the end volume, the total duration, and the duration completed so far (called the "step"). For every frame, the game interpolates which value should be used based on the step, the total duration, and the start and end volumes, and then adds the timestep to the step. This way, fades will be correctly timed, and we don't have potential determinism issues. Doing this also fixes inaccuracies with the game timestep changing during the fade, since the timestep is only used in the calculation once at the beginning in the previous system.
2021-04-28 01:37:16 +02:00
return Fade_continue;
}
2020-01-01 21:29:24 +01:00
void musicclass::fadeMusicVolumeIn(int ms)
{
if (halted())
{
return;
}
m_doFadeInVol = true;
m_doFadeOutVol = false;
/* Ensure it starts at 0 */
musicVolume = 0;
/* Fix 1-frame glitch */
Mix_VolumeMusic(0);
fade.step_ms = 0;
fade.duration_ms = ms;
fade.start_volume = 0;
fade.end_volume = MIX_MAX_VOLUME;
2020-01-01 21:29:24 +01:00
}
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
void musicclass::fadeMusicVolumeOut(const int fadeout_ms)
{
if (halted())
{
return;
}
m_doFadeInVol = false;
m_doFadeOutVol = true;
fade.step_ms = 0;
/* Duration is proportional to current volume. */
fade.duration_ms = fadeout_ms * musicVolume / MIX_MAX_VOLUME;
fade.start_volume = musicVolume;
fade.end_volume = 0;
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
}
void musicclass::fadeout(const bool quick_fade_ /*= true*/)
2020-01-01 21:29:24 +01:00
{
fadeMusicVolumeOut(quick_fade_ ? 500 : 2000);
quick_fade = quick_fade_;
2020-01-01 21:29:24 +01:00
}
void musicclass::processmusicfadein(void)
2020-01-01 21:29:24 +01:00
{
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
if (fade_code == Fade_finished)
{
m_doFadeInVol = false;
}
2020-01-01 21:29:24 +01:00
}
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
void musicclass::processmusicfadeout(void)
{
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
if (fade_code == Fade_finished)
{
musicVolume = 0;
m_doFadeOutVol = false;
haltdasmusik();
}
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
}
void musicclass::processmusic(void)
2020-01-01 21:29:24 +01:00
{
if(!safeToProcessMusic)
{
return;
}
if(m_doFadeInVol)
{
processmusicfadein();
}
if (m_doFadeOutVol)
{
processmusicfadeout();
}
/* This needs to come after processing fades */
if (nicefade && halted())
{
play(nicechange);
nicechange = -1;
nicefade = false;
}
2020-01-01 21:29:24 +01:00
}
void musicclass::niceplay(int t)
{
/* important: do nothing if the correct song is playing! */
if ((!mmmmmm && currentsong != t)
|| (mmmmmm && usingmmmmmm && currentsong != t)
|| (mmmmmm && !usingmmmmmm && currentsong != t + num_mmmmmm_tracks))
{
if (currentsong != -1)
{
fadeout(false);
}
nicefade = true;
}
nicechange = t;
2020-01-01 21:29:24 +01:00
}
static const int areamap[] = {
4, 3, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4,12,12,12,12,12,12,12,
4, 3, 3, 3, 3, 3, 3, 4, 4,-2, 4, 4, 4, 4,12,12,12,12,12,12,
4, 4, 4, 4, 3, 4, 4, 4, 4,-2, 4, 4, 4, 4,12,12,12,12,12,12,
4, 4, 4, 4, 3, 4, 4, 4, 4,-2, 4, 4, 1, 1, 1, 1,12,12,12,12,
4, 4, 3, 3, 3, 4, 4, 4, 4,-2,-2,-2, 1, 1, 1, 1, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1,11,11,-1, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1, 1,11,11,11,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 3,
4, 4, 4, 4, 4, 4, 4, 4,-2,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 4,
4, 4,-1,-1,-1, 4, 4, 4, 4,-2, 4, 4, 4, 1, 1, 1, 1, 1, 1, 4,
4, 4,-1,-1,-1, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 4, 1, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4, 1, 1, 1, 1, 1, 1, 4, 1, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4,-2, 4,-1,-3, 4, 4, 4, 4, 4, 1, 4,
4, 4, 4, 4, 4, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 3, 3, 3, 3, 3, 3, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 4, 4, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 4, 4, 3, 4,-2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
};
SDL_COMPILE_TIME_ASSERT(areamap, SDL_arraysize(areamap) == 20 * 20);
2020-01-01 21:29:24 +01:00
void musicclass::changemusicarea(int x, int y)
{
int room;
int track;
if (script.running)
{
return;
}
room = musicroom(x, y);
if (!INBOUNDS_ARR(room, areamap))
{
SDL_assert(0 && "Music map index out-of-bounds!");
return;
}
track = areamap[room];
switch (track)
{
case -1:
/* Don't change music. */
return;
case -2:
/* Special case: Tower music, changes with Flip Mode. */
if (graphics.setflipmode)
{
track = 9; /* ecroF evitisoP */
}
else
{
track = 2; /* Positive Force */
}
break;
case -3:
/* Special case: start of Space Station 2. */
if (game.intimetrial)
{
track = 1; /* Pushing Onwards */
}
else
{
track = 4; /* Passion for Exploring */
}
break;
}
niceplay(track);
2020-01-01 21:29:24 +01:00
}
void musicclass::playef(int t)
2020-01-01 21:29:24 +01:00
{
if (!INBOUNDS_VEC(t, soundTracks))
{
return;
}
int channel;
channel = Mix_PlayChannel(-1, soundTracks[t].sound, 0);
if(channel == -1)
{
vlog_error("Unable to play WAV file: %s", Mix_GetError());
}
2020-01-01 21:29:24 +01:00
}
void musicclass::pauseef(void)
{
Mix_Pause(-1);
}
void musicclass::resumeef(void)
{
Mix_Resume(-1);
}
bool musicclass::halted(void)
{
return Mix_PausedMusic() == 1;
}