Is there something thread unsafe about my soundengine

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
Last edited on
and the renderer

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
void SoundPlayer::audioOut( ofSoundBuffer& buffer )
{
    GetLockBlock("SoundPlayer::audioOut", true);
    
    unsigned int frameCount = buffer.getNumFrames();
    float* theBuffer = buffer.getBuffer().data();
    int outChannels = buffer.getNumChannels();
    
    bool ranSome = false;
    //TODO loop through streamobjects (their data)
    std::multimap<string, StreamObject*>::iterator it = _streamObjects.begin();
    while(it != _streamObjects.end())
    {
        if(it->second->IsLoaded() && !it->second->IsPaused() )
        {
            ranSome = true;
            RealCallback(theBuffer, outChannels, frameCount, &it->second->GetData());
        }
        
        it++;
    }
    
    if(!ranSome)
    {
        for(int i = 0; i < (outChannels * frameCount); i++)
        {
            theBuffer[i] = 0.f;
        }
        usleep(2000);
    }
    
    ReleaseLock("SoundPlayer::audioOut", true);
}

void SoundPlayer::RealCallback(float *outputBuffer, int outChannels, unsigned int frameCount, AudioData* audioData)
{
    
    unsigned int inChannels = audioData->GetNumChannels();
    unsigned int startChan = audioData->m_startChannel;
    
    audioData->m_phase = resample_linear_int16xN_reference(audioData->m_phase, audioData->m_phaseStep, audioData->m_fWholeTune,
                                                           audioData->m_tempBuffer, frameCount, inChannels, audioData->m_bForward);
    audioData->m_framePos = audioData->m_phase >> 32;

    for( unsigned int i = 0; i < (frameCount * outChannels); i += inChannels)
    {
        int k = i * inChannels;
        int l = k  + startChan;
        
        for(int j = 0; j < inChannels; j++)
        {
            outputBuffer[l + j] += audioData->m_tempBuffer[k + j];
        }
    }
} 
Topic archived. No new replies allowed.