Sign in to follow this  
jyk

SDL_mixer: problem with Mix_LoadMUS_RW()

Recommended Posts

Hi all, I posted this on idevgames (since I'm developing in OS X and I suspect this may be a Mac-specific problem), and thought I'd post it here as well in case someone has run into this problem and/or knows of a solution. I'm using SDL_mixer for sound. Everything is working fine except that Mix_LoadMUS_RW() crashes under certain conditions. From googling I've gathered that Mix_LoadMUS_RW() was broken in certain versions of SDL_mixer, but I'm not sure whether this applies to the current version (1.2.7). I'm testing the problem with a clean Xcode project (the SDL project template, with the SDL_mixer framework added). Following is the entirety of the project code (there's no error-checking or cleanup, but the crash occurs with or without these). The location and apparent cause of the crash is indicated in the code comments:
#include <vector>
#include <fstream>
#include <iostream>
#include "SDL.h"
#include "SDL_mixer/SDL_mixer.h"

using namespace std;

Mix_Music* music; // The music, declared as a global variable
void PlayMusic(); // A function that loads and plays the music

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
    Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024);
    SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);

    // When the music is loaded and played via the following block of code,
    // everything works correctly. The music loads, plays while the loop is
    // running, and then the program exits normally.

    std::ifstream file("music.ogg");
    file.seekg(0, std::ios::end);
    size_t size = file.tellg();
    file.seekg(0, std::ios::beg);
    std::vector<char> data(size);
    file.read(&data.front(), data.size());
    SDL_RWops* rwops = SDL_RWFromMem(
        (unsigned char*)&data.front(), data.size()
    );
    Mix_Music* music = Mix_LoadMUS_RW(rwops);
    Mix_PlayMusic(music, -1);
    
    // If instead the above block is commented out and the following function
    // call is used instead, the application crashes. Note that PlayMusic()
    // uses the exact same code as the above block to load and play the
    // music. The crash occurs shortly after PlayMusic() returns, during the
    // 'for' loop that follows.

    //PlayMusic();

    for (size_t i = 0; i < 50000; ++i) { std::cout << i << std::endl; }
    return 0;
}

void PlayMusic()
{
    std::ifstream file("music.ogg");
    file.seekg(0, std::ios::end);
    size_t size = file.tellg();
    file.seekg(0, std::ios::beg);
    std::vector<char> data(size);
    file.read(&data.front(), data.size());
    SDL_RWops* rwops = SDL_RWFromMem(
        (unsigned char*)&data.front(), data.size()
    );
    Mix_Music* music = Mix_LoadMUS_RW(rwops);
    Mix_PlayMusic(music, -1);
    
    for (size_t i = 0; i < 50000; ++i) { std::cout << i << std::endl; }
}

Here is the call stack displayed by the debugger after the crash:
#0	0xffff8984 in objc_msgSend_rtp
#1	0x30009528 in SDL_WriteBE64
#2	0x320364ec in _get_next_page
#3	0x32036b18 in _fetch_and_process_packet
#4	0x32034c44 in ov_read
#5	0x3201bca8 in OGG_getsome
#6	0x3201bdec in OGG_playAudio
#7	0x3201c39c in music_mixer
#8	0x320193c8 in mix_channels
#9	0x30035aac in SDL_SYS_CDQuit
#10	0x700090a0 in DefaultOutputAUEntry
#11	0x700c9774 in dyld_stub__keymgr_get_and_lock_processwide_ptr
#12	0x700c94a4 in dyld_stub__keymgr_get_and_lock_processwide_ptr
#13	0x94159c60 in AudioConverterChain::CallInputProc
#14	0x941598b0 in AudioConverterChain::FillBufferFromInputProc
#15	0x94159078 in BufferedAudioConverter::GetInputBytes
#16	0x94158ed0 in CBRConverter::RenderOutput
#17	0x94158c44 in BufferedAudioConverter::FillBuffer
#18	0x94158dc0 in AudioConverterChain::RenderOutput
#19	0x94158c44 in BufferedAudioConverter::FillBuffer
#20	0x94158ad0 in AudioConverterFillComplexBuffer
#21	0x70008c88 in DefaultOutputAUEntry
#22	0x700c9008 in dyld_stub__keymgr_get_and_lock_processwide_ptr
#23	0x7000ab5c in DefaultOutputAUEntry
#24	0x70008068 in DefaultOutputAUEntry
#25	0x91463cf4 in IOA_Device::CallIOProcs
#26	0x91463a08 in HP_IOThread::PerformIO
#27	0x91461a18 in HP_IOThread::WorkLoop
#28	0x91461580 in HP_IOThread::ThreadEntry
#29	0x914523dc in CAPThread::Entry
#30	0x9002bc28 in _pthread_body

Has anyone successfully used Mix_LoadMUS_RW() in their applications? Any ideas as to what the cause of the crash might be? Thanks for any input, Jesse

Share this post


Link to post
Share on other sites
Quote:
Original post by Jack Sotac
Maybe
std::vector<char> data(size);
is going out of scope? This would cause SDL Mixer to read memory that has been freed.
You are absolutely right! SDL_mixer appears to continue to read from the buffer you provide while playing the music. Keeping data around does in fact keep the app from crashing.

This possibility hadn't occurred to me (obviously), since the other SDL 'rwops' functions for loading images and sound files make an internal copy of the data and don't require that you keep the original data available (or at least as far as I can tell). Perhaps Mix_LoadMUS_RW() works differently though.

Thanks very much for your help :) Meanwhile, if anyone knows exactly how Mix_LoadMUS_RW() is intended to be used, I'd be interested in knowing, just to be sure I'm using it correctly (as far as I can tell it's not a documented feature of SDL_mixer).

Share this post


Link to post
Share on other sites
I've been poking around the SDL_Mixer source(load_ogg.c,music.c and music_ogg.c) and here is what I found.

Mix_LoadWAV/_RW() will make an internal copy of the data. This function can also load OGGs and a few other formats. Therefore you could use this routine instead of LoadMUS_RW in your PlayMusic().

This function isn't good for compressed formats(OGG,MP3) since they need to be uncompressed when making a copy of the data. So when using Mix_LoadWAV() to load a OGG file(~5MB on disk),SDL_Mixer will have to allocate a ~45MB internal buffer to hold the entire uncompressed version.

Mix_LoadMUS() - will stream the data from the disk. So instead of uncompressing all the data at once, SDL_Mixer will allocate small internal buffers(~4k). Then uncompress samples into these buffers as needed, while playing the music.

For OGGs, the Vorbis libs take care of the streaming. So vorbis opens the file and gets the handle(FILE*), then uses it to read in chunks of data directly from the hard drive.

Mix_LoadMUS_RW()- Since SDL doesn't have a function to duplicate (SDL_RWops* )handles, SDL_Mixer has to use the one you give it directly. This means not freeing the (SDL_RWops*)handle or the data that backs it(FILE* handle ,disk file,allocated memory,etc).

In conclusion, if you don't call SDL_RWclose(rw) on the handle or free the memory that the SDL_RWops handle points while the music is playing, you should be fine:)

Disclaimer: I don't use SDL_Mixer that much.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jack Sotac
I've been poking around the SDL_Mixer source(load_ogg.c,music.c and music_ogg.c) and here is what I found.

Mix_LoadWAV/_RW() will make an internal copy of the data. This function can also load OGGs and a few other formats. Therefore you could use this routine instead of LoadMUS_RW in your PlayMusic().

This function isn't good for compressed formats(OGG,MP3) since they need to be uncompressed when making a copy of the data. So when using Mix_LoadWAV() to load a OGG file(~5MB on disk),SDL_Mixer will have to allocate a ~45MB internal buffer to hold the entire uncompressed version.

Mix_LoadMUS() - will stream the data from the disk. So instead of uncompressing all the data at once, SDL_Mixer will allocate small internal buffers(~4k). Then uncompress samples into these buffers as needed, while playing the music.

For OGGs, the Vorbis libs take care of the streaming. So vorbis opens the file and gets the handle(FILE*), then uses it to read in chunks of data directly from the hard drive.

Mix_LoadMUS_RW()- Since SDL doesn't have a function to duplicate (SDL_RWops* )handles, SDL_Mixer has to use the one you give it directly. This means not freeing the (SDL_RWops*)handle or the data that backs it(FILE* handle ,disk file,allocated memory,etc).

In conclusion, if you don't call SDL_RWclose(rw) on the handle or free the memory that the SDL_RWops handle points while the music is playing, you should be fine:)

Disclaimer: I don't use SDL_Mixer that much.
Nice - thanks very much for sharing that information. I looked through the SDL_mixer source today as well, but I didn't get as far with it as you did.

The only thing I'm still uncertain of is why the following bit of clean-up code:
Mix_FreeMusic(music);
SDL_FreeRW(rwops);
Generates a 'double free()' message from the debugger when the rwops is freed. This suggests it's already been freed by Mix_FreeMusic(), but I haven't been able to find any indication of this in the SDL_mixer source.

In any case, thanks for your help - other than the double free, everything seems to be working correctly now.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
The only thing I'm still uncertain of is why the following bit of clean-up code:
Mix_FreeMusic(music);
SDL_FreeRW(rwops);
Generates a 'double free()' message from the debugger when the rwops is freed. This suggests it's already been freed by Mix_FreeMusic(), but I haven't been able to find any indication of this in the SDL_mixer source.


Yeah, Mix_FreeMusic() does free the rwops. This is done in the vorbis library(vorbisfile.c to be exact) so it's not in the mixer source. Always glad to help:)

Share this post


Link to post
Share on other sites
Quote:
Original post by Jack Sotac
Yeah, Mix_FreeMusic() does free the rwops. This is done in the vorbis library(vorbisfile.c to be exact) so it's not in the mixer source.
Ah, I see. Thanks again - you really helped clear things up for me. It is much appreciated :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this