I can't for the life of me figure out what is wrong with my program. I have a multi threaded sound loader that renders with rtaudio. intermittently I will get a crash in the draw thread whilst deallocating something deep within opengl and glfw when i either play a song for the first time after loading, or on moving focus to another window. I have switched off almost all functionality in the program but it still occurs so it must be to do with my soundengine.
the short version is I keep a multimap of different sounds in a singleton soundplayer class, a seperate thread soundloader will load data, send back to the soundplayer class, and rtaudio will render whatever data in the multimap from the soundplayer class I allow it.
the longer version with code.
soundplayer has a thread and a looping threaded function and a lock. The main thread will pick up a GUI event and call load sounds
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
void SoundPlayer::LoadSound(std::multimap<string, SoundLoadListener*> loadRequests)
{
GetLockBlock("SoundPlayer::LoadSound");
std::multimap<string, SoundLoadListener*>::iterator it = loadRequests.begin();
while(it != loadRequests.end())
{
_loadRequests.insert(*it);
it++;
}
//take out from requests what we don't need loader thread to handle
//and create soundobjects,
//either match up data already loaded and set state to loaded,
//or leave for loader thread and set loadstate to loading
std::multimap<string, SoundLoadListener*>::iterator it1 = _loadRequests.begin();
while(it1 != _loadRequests.end())
{
StreamObject* newStreamObject = new StreamObject;
_streamObjects.insert(std::pair<std::string, StreamObject*>(it1->first, newStreamObject));
newStreamObject->SetLoadState(LoadingState::eLoading);
newStreamObject->SetLoadListener(it1->second);
std::map<string, SoundStore*>::iterator it2 = _dataTable.begin();
if(_dataTable.find(it1->first) != _dataTable.end())
{
//data found, set data, set load state, remove from requests
newStreamObject->GetData().m_fWholeTune = it2->second->_dataf;
newStreamObject->SetLoadState(LoadingState::eLoading); //tricking threadedfunction to pick this up
//newStreamObject->GetLoadListener()->OnLoaded(newStreamObject);
it1 = _loadRequests.erase(it1);
continue;
}
it1++;
}
ReleaseLock("SoundPlayer::LoadSound");
}
|
the threaded function will pick up these requests and pass on the requests to soundloader, another thread.
|
_soundLoader.LoadSounds(_loadRequests);
|
loadsounds simply attains it's own lock, takes a copy of the requsts, stores them, and starts the thread
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
void SoundLoader::LoadSounds(std::multimap<string, SoundLoadListener*> loadRequests)
{
_loadRequests = loadRequests;
//SetPriority(Poco::Thread::Priority::PRIO_HIGHEST);
//SetOSPriority(GetMaxOSPriority());
startThread();
}
void SoundLoader::threadedFunction()
{
//unsigned long long timeFuncStart = ofGetElapsedTimeMillis();
GetLockBlock("SoundLoader::threadedFunction");
//for each entry in _loadRequests, load and call soundplayer->dataloaded
std::multimap<string, SoundLoadListener*>::iterator it = _loadRequests.begin();
while(it != _loadRequests.end())
{
SF_INFO info;
void* datav;
float* dataf;
std::string filename = it->first;
size_t len = filename.find_last_of(".");
size_t filenameLen = filename.length();
char buffer[32];
filename.copy(buffer, filenameLen - len,len);
buffer[filenameLen - len] = '\0';
//for the test, its definitely a .wav
{
SNDFILE* sndfile;
sndfile = sf_open(filename.c_str(), SFM_READ, &info);
if(sndfile == NULL)
{
//failed to open, return false;
return false;
}
sf_count_t numSamples = info.channels * info.frames;
dataf = new float[numSamples];
sf_seek(sndfile, 0, SEEK_SET);
//be clever here
//read in a loop, a few frames at a time? 1024? 2048? test it
//then yield
sf_count_t samplesReadPre = numSamples;
sf_count_t samplesRead = 0;
while(samplesReadPre > 0)
{
//printf("soundload \n");
unsigned int samplestoRead = 0;
if(samplesReadPre - 2048 > 0)
{
samplestoRead = 2048;
}
else
{
samplestoRead = samplesReadPre;
}
samplesReadPre -= samplestoRead;
samplesRead += sf_read_float(sndfile, dataf, samplestoRead);
dataf += samplestoRead;
yield();
}
assert(samplesRead == numSamples);
dataf -= samplesRead;//reset pointer
sf_close(sndfile);
}
SoundPlayer::GetInstance()->DataLoaded(filename, info, dataf);
it = _loadRequests.erase(it);
}
ReleaseLock("SoundLoader::threadedFunction");
}
|
data loaded in soundplayer will take the new float data and put it into it's own soundstore class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void SoundPlayer::DataLoaded(std::string filename, SF_INFO info, float* dataf)
{
//unsigned long long timeDataLoadedStart = ofGetElapsedTimeMicros();
GetLockBlock("SoundPlayer::DataLoaded", true);
SoundStore* newStore = new SoundStore;
newStore->_filename = filename;
newStore->_info = info;
newStore->_dataf = dataf;
_dataTable.insert(std::pair<std::string, SoundStore*>(filename, newStore));
ReleaseLock("SoundPlayer::DataLoaded", true);
}
|
the threaded function of soundplayer, it will keep checking of the data has been loaded, then alert the listeners that allow the GUI to play them
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
if(_streamObjects.size() > 0)
{
//printf("_streamObjects.size() > 0 \n");
std::multimap<std::string, StreamObject*>::iterator streamObjIt = _streamObjects.begin();
while(streamObjIt != _streamObjects.end())
{
StreamObject& streamObject = *(streamObjIt->second);
if(streamObject.GetLoadState() == LoadingState::eLoading)
{
//check data table for load
std::map<string, SoundStore*>::iterator it2 = _dataTable.find(streamObjIt->first);
if(it2 != _dataTable.end())
{
//data found, set data, set load state, remove from requests
streamObject.SetData(it2->second->_dataf);//force copy
streamObject.SetInfo(it2->second->_info);
streamObject.SetName(it2->first);
streamObject.SetLoadState(LoadingState::eLoaded);
streamObject.GetLoadListener()->OnLoaded(&streamObject);
}
}
else if(streamObject.GetLoadState() == LoadingState::eLoaded)
{
//todo what do we do here eh?
}
streamObjIt++;
}
}
|
then when the gui wants to play them, the rtaudio renderer will know this and allow it to be rendered to the audio device