mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 17:49:43 +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.
This commit is contained in:
parent
801ac995e2
commit
1f5835c985
2 changed files with 47 additions and 14 deletions
|
@ -15,7 +15,6 @@ musicclass::musicclass(void)
|
|||
m_doFadeInVol = false;
|
||||
m_doFadeOutVol = false;
|
||||
musicVolume = 0;
|
||||
FadeVolAmountPerFrame = 0;
|
||||
|
||||
user_music_volume = USER_VOLUME_MAX;
|
||||
user_sound_volume = USER_VOLUME_MAX;
|
||||
|
@ -277,14 +276,45 @@ void musicclass::silencedasmusik(void)
|
|||
musicVolume = 0;
|
||||
}
|
||||
|
||||
void musicclass::setfadeamount(const int fade_ms)
|
||||
struct FadeState
|
||||
{
|
||||
if (fade_ms == 0)
|
||||
int start_volume;
|
||||
int end_volume;
|
||||
int duration_ms;
|
||||
int step_ms;
|
||||
};
|
||||
|
||||
static struct FadeState fade;
|
||||
|
||||
enum FadeCode
|
||||
{
|
||||
Fade_continue,
|
||||
Fade_finished
|
||||
};
|
||||
|
||||
static enum FadeCode processmusicfade(struct FadeState* state, int* volume)
|
||||
{
|
||||
int range;
|
||||
int new_volume;
|
||||
|
||||
if (state->duration_ms == 0 /* Fast path. */
|
||||
|| state->start_volume == state->end_volume /* Fast path. */
|
||||
|| state->step_ms >= state->duration_ms /* We're finished. */)
|
||||
{
|
||||
FadeVolAmountPerFrame = MIX_MAX_VOLUME;
|
||||
return;
|
||||
*volume = state->end_volume;
|
||||
state->step_ms = 0;
|
||||
return Fade_finished;
|
||||
}
|
||||
FadeVolAmountPerFrame = MIX_MAX_VOLUME / (fade_ms / game.get_timestep());
|
||||
|
||||
range = state->end_volume - state->start_volume;
|
||||
new_volume = range * state->step_ms / state->duration_ms;
|
||||
new_volume += state->start_volume;
|
||||
|
||||
*volume = new_volume;
|
||||
|
||||
state->step_ms += game.get_timestep();
|
||||
|
||||
return Fade_continue;
|
||||
}
|
||||
|
||||
void musicclass::fadeMusicVolumeIn(int ms)
|
||||
|
@ -298,14 +328,19 @@ void musicclass::fadeMusicVolumeIn(int ms)
|
|||
/* Fix 1-frame glitch */
|
||||
Mix_VolumeMusic(0);
|
||||
|
||||
setfadeamount(ms);
|
||||
fade.duration_ms = ms;
|
||||
fade.start_volume = 0;
|
||||
fade.end_volume = MIX_MAX_VOLUME;
|
||||
}
|
||||
|
||||
void musicclass::fadeMusicVolumeOut(const int fadeout_ms)
|
||||
{
|
||||
m_doFadeInVol = false;
|
||||
m_doFadeOutVol = true;
|
||||
setfadeamount(fadeout_ms);
|
||||
|
||||
fade.duration_ms = fadeout_ms;
|
||||
fade.start_volume = musicVolume;
|
||||
fade.end_volume = 0;
|
||||
}
|
||||
|
||||
void musicclass::fadeout(const bool quick_fade_ /*= true*/)
|
||||
|
@ -316,8 +351,8 @@ void musicclass::fadeout(const bool quick_fade_ /*= true*/)
|
|||
|
||||
void musicclass::processmusicfadein(void)
|
||||
{
|
||||
musicVolume += FadeVolAmountPerFrame;
|
||||
if (musicVolume >= MIX_MAX_VOLUME)
|
||||
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
|
||||
if (fade_code == Fade_finished)
|
||||
{
|
||||
m_doFadeInVol = false;
|
||||
}
|
||||
|
@ -325,8 +360,8 @@ void musicclass::processmusicfadein(void)
|
|||
|
||||
void musicclass::processmusicfadeout(void)
|
||||
{
|
||||
musicVolume -= FadeVolAmountPerFrame;
|
||||
if (musicVolume < 0)
|
||||
enum FadeCode fade_code = processmusicfade(&fade, &musicVolume);
|
||||
if (fade_code == Fade_finished)
|
||||
{
|
||||
musicVolume = 0;
|
||||
m_doFadeOutVol = false;
|
||||
|
|
|
@ -27,7 +27,6 @@ public:
|
|||
void pause(void);
|
||||
void haltdasmusik(void);
|
||||
void silencedasmusik(void);
|
||||
void setfadeamount(const int fade_ms);
|
||||
void fadeMusicVolumeIn(int ms);
|
||||
void fadeMusicVolumeOut(const int fadeout_ms);
|
||||
void fadeout(const bool quick_fade_ = true);
|
||||
|
@ -55,7 +54,6 @@ public:
|
|||
|
||||
bool m_doFadeInVol;
|
||||
bool m_doFadeOutVol;
|
||||
int FadeVolAmountPerFrame;
|
||||
int musicVolume;
|
||||
|
||||
/* 0..USER_VOLUME_MAX */
|
||||
|
|
Loading…
Reference in a new issue