Is there something thread unsafe about my soundengine

Jul 31, 2016 at 3:44pm
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 Jul 31, 2016 at 3:46pm
Jul 31, 2016 at 3:47pm
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.