ofSoundPlayer.unloadSound() crashes

Maybe it’s just that it’s late and I’m tired, but ofSoundPlayer.unloadSound() is consistently crashing for me. I’ve got sounds loaded and playing fine (they are looping and controlled by a master clock/sequencer). When I want to remove a sound, I’m calling myPlayer.unloadSound();, where it crashes the application.

I looked at the source and it appears to be stopping and releasing the sound (therefore, I’m just unloadSound()ing while it’s still playing), so I’m not sure what’s going on. Any ideas?

Can you post some code with an example that crashes?

Here’s the basic idea. “setup” loads XML. “update” does positioning, tempo, and volume changes (volume is changed according proximity to center of screen). “draw” just draws.

There’s the function that loads the scene (called on a key press right now), which uses a “sceneIndex” integer to grab the sound locations from XML, create sound objects, and load and configure the soundPlayer objects. “clock” is a thread that calls “phraseComplete” every sixteen notes. phraseComplete checks if a sound needs to be played and plays it (keeps everything synced).

When a sound is dragged to the dead center of the screen, it is removed. One minor correction I’ve just found is that sometimes it works! I just tried again and I can sometimes remove one or two sounds and then the next one will crash. Hmmm…

Here are the three functions that have to do with loading, playing, and unloading the sounds:

Loads sounds…

void testApp::loadScene(int sceneIndex)  
    // Get scene  
    TiXmlHandle hDoc(&soundXML);  
    TiXmlHandle hScenes = hDoc.FirstChild("radial").FirstChild("scenes");  
    TiXmlHandle hScene = hScenes.Child("scene", sceneIndex);  
    TiXmlElement* pScene = hScene.ToElement();  
    string sceneName = pScene->Attribute("name");  
    // Get first channel  
    TiXmlElement* pChannel = hScene.FirstChild("channel").ToElement();  
    // Loop through sound channels  
    int channelCount = 0;  
    int channelIndex = 0;  
    while (pChannel != 0)  
        // Iterate through sounds to see if this is already loaded  
        bool soundLoaded = false;  
        int soundCount = soundChannels.size();  
        for (int i = 0; i < soundCount; i++) if (soundChannels[i].sceneIndex == sceneIndex && soundChannels[i].channelIndex == channelIndex) soundLoaded = true;  
        // Sound is not loaded yet  
        if (!soundLoaded)  
            // Create channel  
            soundChannel channel;  
            // Set properties  
            channel.channelIndex = channelIndex;  
            channel.name = pChannel->Attribute("name");  
            channel.path = pChannel->Attribute("src");  
            channel.scene = sceneName;  
            channel.sceneIndex = sceneIndex;  
            channel.tempo = atof(pChannel->Attribute("tempo"));  
            // Add and configure the sound player  
            ofSoundPlayer soundPlayer;  
            channel.player = soundPlayer;  
            // Add channel to list  
            // Increase channel count  
        // Go to next channel and increase channel count  
        pChannel = pChannel->NextSiblingElement();  
    // Channels added  
    if (channelCount > 0)  
        // Get angle between items  
        float angleWidth = ofDegToRad(360.0f / (float)channelIndex);  
        // Iterate through new sounds  
        int soundCount = soundChannels.size();  
        int start = soundCount - channelCount;  
        for (int i = start; i < soundCount; i++)  
            // Set default distance from center  
            soundChannels[i].position.x = (int)(center.x + range * cos(angleWidth * i));  
            soundChannels[i].position.y = (int)(center.y + range * sin(angleWidth * i));  
            // Load sound  
    // Start clock, if needed  
    if (!clock.isThreadRunning())  
        // Start clock  
        // Run callback to get things started  

Plays sounds (called by clock thread)…

void testApp::phraseComplete()  
    // Iterate through sounds  
    int soundCount = soundChannels.size();  
    for (int i = 0; i < soundCount; i++)  
        // Make sure sound is loaded  
        if (soundChannels[i].player.bLoadedOk)  
            // Play sound if complete or not yet started  
            float position = soundChannels[i].player.getPosition();  
            if (position <= 0.0f || position >= 1.0f) soundChannels[i].player.play();  

Unloads sounds (called by update() when sound control object is position over center of screen)…

void testApp::unloadSound(int soundIndex)  
    // Unload the sound  
    // Remove sound from list  
    soundChannels.erase(soundChannels.begin() + soundIndex);  


Your problem could be due to the fact that the soundChannel and ofSoundPlayer objects you create are temporary ones with local scope. Once you leave the function they are defined in they no longer are valid and may be overwritten later. Trying to unload or delete them could result in a crash.

What you will need to do instead is create objects using the new operator, ie.

soundChannel* channel = new soundChannel();  

This means you might want to use an vector of pointers instead of a vector of objects ( because the new operator returns a pointer to the object).

After you unload the sounds you will need to delete the dynamically created objects, i.e.

delete soundChannels[soundIndex];  

Ah, duh… good catch. I’m sure that’s it. Will try it out.

EDIT: That was half of it. The other half was that I was doing my update loop like this:

int soundCount = soundChannels.size();  
for (int i = 0; i < soundCount; i++)  

Which is best practice for performance so it isn’t evaluating “size()” on every loop. However, because the “update()” function deletes a sound from within the loop, the size() does need to be evaluated on every loop. Switching the loop to this fixed it:

for (int i = 0; i < soundChannels.size(); i++)