Subject: Trouble with Mix_LoadMUS_RW()
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:
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):
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
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_bodyAny 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
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).
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).
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.
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.

