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?
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;
channel.player.setLoop(true);
channel.player.setVolume(0.0f);
// Add channel to list
soundChannels.push_back(channel);
// Increase channel count
channelCount++;
}
// Go to next channel and increase channel count
channelIndex++;
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
soundChannels[i].player.loadSound(soundChannels[i].path);
}
}
// Start clock, if needed
if (!clock.isThreadRunning())
{
// Start clock
clock.start(this);
// Run callback to get things started
phraseComplete();
}
}
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
soundChannels[soundIndex].player.unloadSound();
// 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.
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++)
{
...
}