Subject: Trouble with Mix_LoadMUS_RW()

Member
Posts: 63
Joined: 2005.12
Post: #1
Hello all,

My SDL-based app (which uses SDL, SDL_image, and SDL_mixer) has been going along smoothly, but I've run into one problem that I can't seem to sort out: under certain conditions, using Mix_LoadMUS_RW() causes the program to crash.

I've done a lot of googling and have found some mention of Mix_LoadMUS_RW() being broken in some versions of SDL_mixer. I'm not sure if that applies to the version I'm using though (v1.2.7). Anyway, I'm going to provide a fair amount of detail here in the hopes that someone can spot the problem, but if anyone knows for a fact that Mix_LoadMUS_RW() is broken even in the most up-to-date version of SDL_mixer, that would be useful to know.

To test the problem, I created a new project from the Xcode SDL project template, and added the SDL_mixer framework. Following is the entirety of the project code. There's no error-checking or cleanup, as the crash occurs with or without these, and I wanted to keep the posted code concise. The location and apparent cause of the crash is indicated in the code comments:

Code:
#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 a representative call stack displayed by the debugger after the crash (I think it's the same every time, but I'm not sure):

Code:
#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

Any input on this problem will be greatly appreciated (I've been stuck on it for a few days now and don't seem to be making much progress).

Thanks,

Jesse
Quote this message in a reply
Member
Posts: 63
Joined: 2005.12
Post: #2
It appears the problem is that unlike the other SDL_image and SDL_mixer 'rwops' functions for loading images and (non-music) sound files, you have to keep the original source data around when using Mix_LoadMUS_RW(); that is, as SDL_mixer plays the music it continues to read from the buffer you provide rather than making its own internal copy.

Can anyone confirm this? I'm certainly happy to have a solution, but just for my own edification I'd be interested to know exactly how Mix_LoadMUS_RW() is intended to be used (as far as I can tell it's not a documented feature of SDL_mixer).
Quote this message in a reply
Member
Posts: 63
Joined: 2005.12
Post: #3
Problem solved; I'll just go ahead and post what I found out here in case anyone stumbles on this thread in the future.

Unlike with images in SDL_image and sounds (other than music) in SDL_mixer, when you load a music file via Mix_LoadMUS_RW() you have to keep the original rwops and source data available while the music is playing, as SDL_mixer streams directly from this data rather than making an internal copy.

As a side note, Mix_FreeMusic() frees the associated rwops automatically, so it's not necessary to call SDL_FreeRW() afterwards.
Quote this message in a reply
Post Reply