Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FadeIn/FadeOut effects mess up the channel volume after playback is finished #645

Open
Casqade opened this issue Nov 14, 2024 · 1 comment

Comments

@Casqade
Copy link

Casqade commented Nov 14, 2024

If a channel is done playing before fade_length is reached, its volume is never restored to fade_volume_reset. This messes up the volume for the next chunk to be played on the same channel.

Here, the volume is restored upon reaching fade_length. But here, the channel is done playing, its fading is set to MIX_NO_FADING and the volume is never restored. The problem is, in the next call to Mix_PlayChannel() on the same channel, the volume is stuck at whatever value was at the end of previous playback.

I believe there would be the same behaviour for expired channels.

Is this a bug? And what's the best way to deal with this problem when Mix_MasterVolume isn't available and the global volume is controlled by Mix_HaltChannel(-1); Mix_Volume(-1, volume)?

Potentional workaround 1

auto ms_to_fade = min(fade_length, chunk_ticks_left);
Mix_FadeOutChannel(channel, ms_to_fade);

In this case, the condition for restoring the volume will be fulfilled, but I don't know how to get or calculate chunk_ticks_left, as even SDL_Mixer doesn't know or expose how many ticks are left before chunk is done playing.

Potentional workaround 2

if ( Mix_Playing(channel) == true )
{
  auto volume = Mix_Volume(channel, -1);
  Mix_FadeOutChannel(channel, ms_to_fade);
}

// elsewhere
if ( Mix_Playing(channel) == false )
{
  Mix_Volume(channel, volume);
  Mix_PlayChannel(channel, chunk, 0);
}

This is only possible with dedicated channels, and it's a bit more error-prone, as the channel volume must be properly initialized before first playback.

@Casqade
Copy link
Author

Casqade commented Nov 15, 2024

Here's how to reproduce:

Mix_Volume(0, 100);
auto volumeBeforeFadeOut = Mix_Volume(0, -1);

// sample.wav contains 1 second of white noise
Mix_Chunk* sound = Mix_LoadWAV( "sample.wav" );
Mix_PlayChannel(0, sound, 0);

using namespace std::chrono_literals;
std::this_thread::sleep_for(500ms);

Mix_FadeOutChannel(0, 1000);

// we can increase sleep time to 1100ms,
// it won't change anything
std::this_thread::sleep_for(600ms);

auto volumeAfterFadeOut = Mix_Volume(0, -1);

Mix_PlayChannel(0, sound, 0);

auto volumeForNextPlayback = Mix_Volume(0, -1);

std::cout << volumeBeforeFadeOut << "\n";
std::cout << volumeAfterFadeOut << "\n";
std::cout << volumeForNextPlayback << "\n";

// I suppose this should be true
assert(volumeBeforeFadeOut == volumeAfterFadeOut == volumeForNextPlayback);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant