1
0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-06-16 09:38:29 +02:00
VVVVVV/desktop_version/src/Music.cpp
Misa 5e557ffd1a Fix regression with playing the same track after it fades out
With commit 48313169b6 (PR #453),
AllyTally added a single-case patch for a regression, instead of fixing
it at its root cause.

In fact, that commit only fixes the music if Presenting VVVVVV is
playing while exiting to the menu, not if you enter a level that plays
Presenting VVVVVV - so it only fixes it going one way, and not going the
other way around; neither fixing also all the other cases this could
happen.

It doesn't, say, fix the case where you are exited to the menu
automatically after collecting the last crewmate in the level (or if the
level calls gamestate 1013 itself), which is what happens in my MIRA-VIU
TAS video at the end, and which I noted in the description of that video
( https://www.youtube.com/watch?v=OYQO4ePbYW4&t=111 ).

So, the problem here is that when musicclass::play() is called, it sees
that currentsong is the same as its input, and decides that since the
music is already playing, it shouldn't play the music again. Thus, the
music fades out, and we get silence instead of the music playing again.

But I said this was a regression. Why didn't this happen in 2.2? Well,
it's because of the fact that 2.2 sets currentsong to -1 (no music
playing at all) immediately when starting a fadeout, and not when the
fadeout completes (commit facb079b35,
PR #316). As you can imagine, this discrepancy could lead to bugs, given
that the game would think that music wasn't playing when in actuality it
was, but fixing this bug could also break code that expected this wrong
behavior. And in this case, it has.

So to properly fix the root cause of this, instead of naïvely
single-case patching out every case that comes up randomly, in
musicclass::play(), the function will now ignore if the input given is
the same as currentsong if the music is currently fading out.
2021-01-04 19:16:35 -05:00

378 lines
8.1 KiB
C++

#define MUSIC_DEFINITION
#include "Music.h"
#include <SDL.h>
#include <stdio.h>
#include "BinaryBlob.h"
#include "Map.h"
#include "UtilityClass.h"
void songend();
musicclass::musicclass()
{
safeToProcessMusic= false;
m_doFadeInVol = false;
musicVolume = MIX_MAX_VOLUME;
FadeVolAmountPerFrame = 0;
currentsong = 0;
nicechange = -1;
nicefade = false;
resumesong = 0;
quick_fade = true;
songStart = 0;
songEnd = 0;
Mix_HookMusicFinished(&songend);
usingmmmmmm = false;
}
void musicclass::init()
{
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();
musicReadBlob.clear();
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" ));
#ifdef VVV_COMPILEMUSIC
binaryBlob musicWriteBlob;
#define FOREACH_TRACK(track_name) musicWriteBlob.AddFileToBinaryBlob(track_name);
TRACK_NAMES
#undef FOREACH_TRACK
musicWriteBlob.writeBinaryBlob("data/BinaryMusic.vvv");
#endif
num_mmmmmm_tracks = 0;
num_pppppp_tracks = 0;
if (!musicReadBlob.unPackBinary("mmmmmm.vvv"))
{
mmmmmm = false;
usingmmmmmm=false;
bool ohCrap = musicReadBlob.unPackBinary("vvvvvvmusic.vvv");
SDL_assert(ohCrap && "Music not found!");
}
else
{
mmmmmm = true;
int index;
SDL_RWops *rw;
#define FOREACH_TRACK(track_name) \
index = musicReadBlob.getIndex(track_name); \
if (index >= 0 && index < musicReadBlob.max_headers) \
{ \
rw = SDL_RWFromMem(musicReadBlob.getAddress(index), musicReadBlob.getSize(index)); \
if (rw == NULL) \
{ \
printf("Unable to read music file header: %s\n", SDL_GetError()); \
} \
else \
{ \
musicTracks.push_back(MusicTrack( rw )); \
} \
}
TRACK_NAMES
num_mmmmmm_tracks += musicTracks.size();
const std::vector<int> extra = musicReadBlob.getExtra();
for (size_t i = 0; i < extra.size(); i++)
{
const int& index_ = extra[i];
rw = SDL_RWFromMem(musicReadBlob.getAddress(index_), musicReadBlob.getSize(index_));
musicTracks.push_back(MusicTrack( rw ));
num_mmmmmm_tracks++;
}
bool ohCrap = musicReadBlob.unPackBinary("vvvvvvmusic.vvv");
SDL_assert(ohCrap && "Music not found!");
}
int index;
SDL_RWops *rw;
TRACK_NAMES
#undef FOREACH_TRACK
num_pppppp_tracks += musicTracks.size() - num_mmmmmm_tracks;
const std::vector<int> extra = musicReadBlob.getExtra();
for (size_t i = 0; i < extra.size(); i++)
{
const int& index_ = extra[i];
rw = SDL_RWFromMem(musicReadBlob.getAddress(index_), musicReadBlob.getSize(index_));
musicTracks.push_back(MusicTrack( rw ));
num_pppppp_tracks++;
}
}
void songend()
{
extern musicclass music;
music.songEnd = SDL_GetPerformanceCounter();
music.currentsong = -1;
}
void musicclass::play(int t, const double position_sec /*= 0.0*/, const int fadein_ms /*= 3000*/)
{
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;
musicVolume = MIX_MAX_VOLUME;
if (currentsong == t && Mix_PlayingMusic() != MIX_FADING_OUT)
{
return;
}
currentsong = t;
if (t == -1)
{
return;
}
if (!INBOUNDS_VEC(t, musicTracks))
{
puts("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_FadeInMusicPos(musicTracks[t].m_music, 0, 0, position_sec) == -1)
{
printf("Mix_FadeInMusicPos: %s\n", Mix_GetError());
}
}
else
{
if (Mix_FadingMusic() == MIX_FADING_OUT)
{
// We're already fading out
nicechange = t;
nicefade = true;
currentsong = -1;
if (quick_fade)
{
Mix_FadeOutMusic(500); // fade out quicker
}
else
{
quick_fade = true;
}
}
else if (Mix_FadeInMusicPos(musicTracks[t].m_music, -1, fadein_ms, position_sec) == -1)
{
printf("Mix_FadeInMusicPos: %s\n", Mix_GetError());
}
}
songStart = SDL_GetPerformanceCounter();
}
void musicclass::resume(const int fadein_ms /*= 0*/)
{
const double offset = static_cast<double>(songEnd - songStart);
const double frequency = static_cast<double>(SDL_GetPerformanceFrequency());
const double position_sec = offset / frequency;
play(resumesong, position_sec, fadein_ms);
}
void musicclass::fadein()
{
resume(3000); // 3000 ms fadein
}
void musicclass::haltdasmusik()
{
Mix_HaltMusic();
resumesong = currentsong;
}
void musicclass::silencedasmusik()
{
musicVolume = 0;
}
void musicclass::fadeMusicVolumeIn(int ms)
{
m_doFadeInVol = true;
FadeVolAmountPerFrame = MIX_MAX_VOLUME / (ms / 33);
}
void musicclass::fadeout(const bool quick_fade_ /*= true*/)
{
Mix_FadeOutMusic(2000);
resumesong = currentsong;
quick_fade = quick_fade_;
}
void musicclass::processmusicfadein()
{
musicVolume += FadeVolAmountPerFrame;
if (musicVolume >= MIX_MAX_VOLUME)
{
m_doFadeInVol = false;
}
}
void musicclass::processmusic()
{
if(!safeToProcessMusic)
{
return;
}
if (nicefade && Mix_PlayingMusic() == 0)
{
play(nicechange);
nicechange = -1;
nicefade = false;
}
if(m_doFadeInVol)
{
processmusicfadein();
}
}
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;
}
}
void musicclass::changemusicarea(int x, int y)
{
switch(musicroom(x, y))
{
case musicroom(11, 4):
niceplay(2);
break;
case musicroom(2, 4):
case musicroom(7, 15):
niceplay(3);
break;
case musicroom(18, 1):
case musicroom(15, 0):
niceplay(12);
break;
case musicroom(0, 0):
case musicroom(0, 16):
case musicroom(2, 11):
case musicroom(7, 9):
case musicroom(8, 11):
case musicroom(13, 2):
case musicroom(17, 12):
case musicroom(14, 19):
case musicroom(17, 17):
niceplay(4);
break;
default:
niceplay(1);
break;
}
}
void musicclass::playef(int t)
{
if (!INBOUNDS_VEC(t, soundTracks))
{
return;
}
int channel;
channel = Mix_PlayChannel(-1, soundTracks[t].sound, 0);
if(channel == -1)
{
fprintf(stderr, "Unable to play WAV file: %s\n", Mix_GetError());
}
}