Mix_FadeOutMusic(500) causes a delay similar to SDL_Delay(500)

Hello,

I’ve created an Audio object that wraps up some of the SDL2_mixer functions to play background music in my game.

A call to Mix_FadeOutMusic(500); causes a significant delay in my program. It basically locks up the play of the game for 500 ms.

Also note I get nearly the same results by doing the following, but without the actual fade:

MixFadeOutMusic(0);
SDL_Delay(500);

1.) Does the audio system run on its own thread out of the box?
2.) What can I do differently to have my game keep running smoothly but still have the benefits of having the music fade out?

Thanks for your time,
Electrosys

This shouldn’t happen if your game loop is set up correctly, so my guess is that’s where the problem is. Would need to see more code to be sure. I’ve wrapped SDL2_mixer in a class and have faded out music without any problems like this. What platform are you using?

I am building my app in Fedora using VSCode. I’m currently building against SDL2-devel.x86_64 2.0.16-3.fc34 and SDL2_mixer-devel.x86_64 2.0.4-8.fc34. I was hoping to find a threads window in VSCode (much like the threads window in Visual Studio). Does anyone know if there is a threads window in VSCode?

Also I’m wondering, does the audio system run in it’s own thread by default? It is possible that maybe the audio is running in the rendering thread. I need to look at my code a bit and do a little refactoring probably. I don’t think it will be all that useful to just copy and paste blocks of code here. Let me do some refactoring and see where I’m at. I have my project on github so maybe I will create another thread and ask for a code review. This is my first SDL app, its a tetris clone and I’m pretty close to getting it finished up.

The other thing I notice in my program is that it will lock up once in a while for a moment, much like the old nes games did (if your familiar with that). There is certainly a small memory leak somewhere that I need to work out as well. Let me figure out the best way to share my code.

Thanks for your help,
Electrosys

I understand (some of it).

Try this. Make a very basic game loop in main() and add to it until you find the problem. You can draw a moving square and hit any key to play a sound in under 50 lines of code. That’s the best place to start

AntTheAlchemist,

Minimizing any code that could potentially cause a problem but isn’t needed is a great strategy. Somewhat like commenting out chunks of code.

Up to this point I have been plowing through and just adding new features. At this point I’m simply going through and refactoring my code. Some of the code was added a bit haphazardly as I was trying out features and subsystems that I have never worked with before. As I mentioned, this is my first SDL game and your probably correct in that I have done something wrong in creating my game loop. I’m likely blocking the rendering thread somehow. Before I can expect anyone to take a look at my code though I need to refactor it the best I know how.

As I continue to work through this I may create a youtube video or the like to ask for additional help in a code review fashion. But before I can expect any help, I need to refactor my code to the best of my ability.

Thanks,
Electrosys

On a high level I think what is happening is that I am blocking the rendering thread. I’m calling video functions from main as well as calling audio functions from main. As I refactor I’m trying to keep inline with the MVC pattern. I think that moving the Video calls into the View, the Audio calls into the Model as well as input polling into the Controller I may be able to start to resolve the issues I’m having. I still have a lot to learn about threading and concurrency.

I’m making calls to update the screen and then making a call to fade the music in the same main game loop and that is where I think I may be blocking the video.

I’m wondering though in the meantime as I refactor code, does anyone know of some very simple examples of SDL apps or tutorials that use MVC which I can use as a point of reference? I need some tutorials and example code of how to structure code so that I don’t block the Video thread. I’m also looking for some good reads for understanding how the Video rendering thread works and how to interact with Audio as well as how to structure the input poling code so that I don’t block my video thread.

Thanks for the help,
Electrosys

Mix_FadeOutMusic() is a blocking call correct? I’m assuming its a blocking call because it has a return statement and returns a value. So it would make since that if Mix_FadeOutMusic() is called from the main thread in the game loop which also calls SDL_Present then it would certainly block until it returns, correct?

Should I be spawning a new thread and calling Mix_FadeOutMusic() from the new thread? And then if I need the return value I will have to fork it later? Does this all sound correct?

Thanks,
Electrosys.

No, it shouldn’t be a blocking call.

if(hitnext) {
    mainGameModel.playNext(); //Calls Mix_FadeMusic(2000);
    SDL_Log("Hit Next!");
    hitnext = false;
 }

In my code example above I do see the log message at the same moment that I hit the next button, which fades the music out and then the next song in the playlist is played. So I can be certain that Mix_FadeMusic() is a non-blocking call.

Are either of Mix_PlayingMusic() || Mix_FadingMusic() || Mix_PlayMusic() blocking calls? Maybe the blocking is happening in this bit of code where I iterate through the playlist. Also note that the first song that is loaded is for the intro and the second song that is loaded is for the main menu music. So the main playlist while playing the game actually starts with the third song that is loaded.

void ley::Audio::playPlaylist() {

    if(playlistNumber == 0) {
        if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz1, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz1): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }

    if(playlistNumber == 1) {
         if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz2, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz2): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }

    if(playlistNumber == 2) {
         if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz3, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz3): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }


    //restart from the top of the playlist
    if(playlistNumber > (playlistMax-1)) {
        playlistNumber = 0;
    }
}

For brevity here is the entire Audio.cpp file. You can see here how the music is loaded and everything that I’m doing with it.

#include "Audio.h"

ley::Audio::Audio() :
playlistNumber(0), playlistMax(3) {

    SDL_InitSubSystem(SDL_INIT_AUDIO);

    int flags=MIX_INIT_MP3 || MIX_INIT_OGG;
    int initted=Mix_Init(flags);
    
    if(initted & flags != flags) {
        printf("Mix_Init: Failed to init required mp3 support!\n");
        printf("Mix_Init: %s\n", Mix_GetError());
    }

    if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1) {
        printf("Mix_OpenAudio: %s\n", Mix_GetError());
        exit(2);
    }
    
    musIntro = Mix_LoadMUS("./assets/audio/MovieTheaterIntro.mp3");
    if(!musIntro) {
        printf("Mix_LoadMUS(musIntro): %s\n", Mix_GetError());
    }

    musMainMenu = Mix_LoadMUS("./assets/audio/8_bit_ooame_lofi.mp3");
    if(!musMainMenu) {
        printf("Mix_LoadMUS(musMainMenu): %s\n", Mix_GetError());
    }

    musMelJazz1 = Mix_LoadMUS("./assets/audio/wild_jazz.mp3");
    if(!musMelJazz1) {
        printf("Mix_LoadMUS(musMelJazz1): %s\n", Mix_GetError());
    }

    musMelJazz2 = Mix_LoadMUS("./assets/audio/jazz.ogg");
    if(!musMelJazz2) {
        printf("Mix_LoadMUS(musMelJazz2): %s\n", Mix_GetError());
    }

    musMelJazz3 = Mix_LoadMUS("./assets/audio/Shake and Bake.mp3");
    if(!musMelJazz3) {
        printf("Mix_LoadMUS(musMelJazz3): %s\n", Mix_GetError());
    }
}

ley::Audio::~Audio() {
    
    while(Mix_Init(0)) {
        Mix_Quit();
    }

    Mix_CloseAudio();

    SDL_QuitSubSystem(SDL_INIT_AUDIO);
}

void ley::Audio::playIntro() {

    if(Mix_PlayMusic(musIntro, 1) == -1) {
        printf("Mix_PlayMusic(musIntro): %s\n", Mix_GetError());
    }
}

void ley::Audio::playMainMenu() {

    //always start the playlist at 0 when returning to the main menu
    playlistNumber = 0;

    if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMainMenu, -1) == -1) {
                printf("Mix_PlayMusic(musMainMenu): %s\n", Mix_GetError());
            }
    }
    
}
void ley::Audio::playNext() {
    fadeOutMusic();
}

void ley::Audio::playPlaylist() {


    if(playlistNumber == 0) {

        if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz1, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz1): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }

    if(playlistNumber == 1) {
         if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz2, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz2): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }

    if(playlistNumber == 2) {
         if(!Mix_PlayingMusic() || Mix_FadingMusic() == MIX_FADING_OUT) {
            if(Mix_PlayMusic(musMelJazz3, 1) == -1) {
                printf("Mix_PlayMusic(musMelJazz3): %s\n", Mix_GetError());
            } else {
                playlistNumber++;
            }
        }
    }

    //restart from the top of the playlist
    if(playlistNumber > (playlistMax-1)) {
        playlistNumber = 0;
    }
}

void ley::Audio::fadeOutMusic() {

    Mix_FadeOutMusic(2000);
}

I think I may have answered my own question. According to the wiki Mix_PlayMusic() will block until the fade completes.

The previous music will be halted, or if fading out it waits (blocking) for that to finish.

It looks like I should not be calling Mix_PlayMusic() until the music is actually faded out.

1 Like