I have been working on a game for a while now and noticed that using your library to stream music created a graphical problem while the screen would scroll. (certain tiles would flicker to be more specific). I found that if I don't play music this problem doesn't occur. Another developer suggested that if I can lower the priority (thread priority) of the music this could fix the problem. Before hearing about this the music was not running off it's own separate thread so the only way I could do this was by creating it's own thread to stream the music, and then the rest of the code runs within the main thread. I realized that with the code being changed to use it's own thread it gets rid of the screen flickering completely. The problem is I can't seem to get it to not crash the game. After looking at this for a while I don't believe it to be a thread stack overflow that occurs. It seems the code is not thread safe and I've tried a bunch of things with mutexes or changes to the code but have not had success with this so far. The crash will either occur in ov_read or at random points within the main thread of the game. (such as doing change animation calls or cases where the game won't crash if music isn't using it's own thread) I'm going to send basically what I hope to be the only relevant code for the music and see if anyone has any suggestions on what I can do to fix this problem. <br>
<br>-Note: I can't exactly send all the music code, but if you need to see more let me know.<br><br>To start it off the main function for the music thread looks like this:<br><br>void ReadStrmData()<br>{<br> // Switch buffer page<br>
char *buf;<br> if(strm.bufPage == 0)<br> {<br> buf = strm_buf;<br> strm.bufPage = 1;<br> }<br> else<br> {<br> buf = strm_buf + /*STRM_BUF_PAGESIZE*/4096;<br>
strm.bufPage = 0;<br> }<br><br> long total_bytes_read = 0;<br> <br> while(total_bytes_read < /*STRM_BUF_PAGESIZE*/4096)<br> {<br> int dummy;<br> long bytes_read = ov_read(<br>
&strm.ovf,<br> &buf[total_bytes_read],<br> /*STRM_BUF_PAGESIZE*/4096 - total_bytes_read,<br> &dummy);<br> <br> if(bytes_read < 0)<br>
{<br> Printf("\nov_read error");<br> }<br> else if(bytes_read == 0)<br> {<br> // End of data<br> if(!music_queue.empty())<br>
{<br> ov_clear(&strm.ovf);<br><br> // Get next song<br> strm.raw_ogg = music_queue[0];<br> music_queue.pop_front();<br> strm.pos = 0;<br>
<br> if(strm.raw_ogg != NULL)<br> {<br> if(ov_open_callbacks(&strm, &strm.ovf, NULL, 0, my_callbacks) < 0)<br> {<br> panic("ov_open_callbacks failed - 2\n");<br>
}<br> }<br> else<br> {<br> // NULL pointer signifies silence<br> StopMusic();<br> return;<br>
}<br> }<br> else<br> {<br> // Loop<br> ov_pcm_seek(&strm.ovf, 0);<br> }<br> }<br> else<br>
{<br> total_bytes_read += bytes_read;<br> }<br> }<br> }<br><br><br>-To explain what each variable is, the first variable in that function is strm. It's a StreamInfo structure, which looks like this:<br>
<br>struct StreamInfo<br>{<br> OggVorbis_File ovf;<br> RawOggData *raw_ogg;<br> int pos;<br> u32 bufPage;<br> bool is_playing;<br>};<br><br>-At the moment strm is just a global variable. I've tried a few other things with this such as having ReadStrmData take a StreamInfo pointer. This has been recommended since the global variable is also referenced in the main thread at points when the first song would load or the same way when a new song is loaded into a level. I am also going to show you the whole process of loading a song as well. The thing is often times I've noticed the crash still occur even when a change from one song to another does not occur. The music thread consists only of ReadStrmData calls so the only time strm would be being used at all while music isn't changing is within the music thread. That was just something I attempted but I don't believe that changed anything in terms of where the crash occurs.<br>
<br>-strm_buf looks like this:<br><br>char strm_buf[STRM_BUF_SIZE * 2] ATTRIBUTE_ALIGN(32);<br><br>-Again it's another global variable. <br><br>-music_queue looks like this:<br><br>std::deque<RawOggData *> music_queue;<br>
<br>-Another global variable.<br><br>I'm now going to show the process of loading a song, storing it in that last mentioned deque and then how it differs when music is it's own thread and not it's own thread.<br>
<br>The load song function looks something like this:<br><br>void LoadSong(std::string name_, std::deque<RawOggData> &raw_oggs)<br>{<br> g_music_fade = 16;<br><br> KillStream();<br> music_queue.clear();<br>
raw_oggs.clear();<br><br> //then something like this<br> //this is really a condition here which depends on<br> //the name variable parameter<br> //the important part is that it loads an intro to a song<br> //and then a looped portion of a song <br>
addAndQueueSegment("song_intro", raw_oggs);<br> addAndQueueSegment("song_theme_loop", raw_oggs);<br><br> PlayStream();<br><br>}<br><br>void addAndQueueSegment(const char *name, std::deque<RawOggData> &raw_oggs)<br>
{<br> std::string filename;<br> filename += name;<br> filename += ".ogg";<br> raw_oggs.resize(raw_oggs.size()+1);<br> RawOggData &last = *(raw_oggs.end()-1);<br> loadFromPakfileIntoVector<char>(filename.c_str(), last);<br>
QueueMusicSegment(last);<br> }<br><br>void QueueMusicSegment(RawOggData &raw_ogg)<br>{<br> music_queue.push_back(&raw_ogg);<br>}<br><br><br>void KillStream()<br> {<br> if(strm.is_playing)<br>
{<br> stopMusicImpl(); //this function just stops the music<br> ov_clear(&strm.ovf);<br> }<br> }<br><br> void PlayStream()<br> {<br> // Stops if during playback<br>
if(strm.is_playing)<br> {<br> KillStream();<br> }<br><br> strm.raw_ogg = music_queue[0];<br> music_queue.pop_front();<br> strm.pos = 0;<br><br> if(ov_open_callbacks(&strm, &strm.ovf, NULL, 0, my_callbacks) < 0)<br>
{<br> Printf("\nov_open_callbacks failed - 1");<br> }<br><br> strm.is_playing = TRUE;<br><br> /* Set parameters */<br> g_timer_value = SND_TIMER_CLOCK / ov_info(&strm.ovf, -1)->rate;<br>
u32 alarmPeriod = g_timer_value * STRM_BUF_PAGESIZE / 32U;<br> alarmPeriod /= sizeof(s16);<br><br> //----- <br> <br> // Reads initial stream data<br> strm.bufPage = 0;<br> ReadStrmData();<br>
ReadStrmData(); // Need to do it twice to fill the buffer<br> <br> //----<br> <br> //code here that sets up an alarm, which calls a function called SoundAlarmHandler<br> <br>
}<br><br><br> void SoundAlarmHandler(void *arg)<br> {<br> //note in the case where a separate thread is not created for the music, ReadStrmData is simply called here<br> //ReadStrmData();<br> //otherwise ReadStrmData is commented out as it is here and instead it sends a message for the thread code.<br>
SendMessage(.....);<br> }<br><br>This function is the function used when the music thread is created. Of course it's not used at all when there is not a separate thread for music.<br><br>static void StrmThread(void * /*arg */ )<br>
{<br> //----<br><br> while (1)<br> {<br> ReceiveMessage(...);<br> (void)ReadStrmData();<br> }<br>}<br><br><br>-Remember when a thread is not being used for the music no crashes occur at all. Whenever music is using it's own thread crashes will either occur inside ov_read called from ReadStrmData or at random points in the main thread where it wouldn't crash otherwise if there is not a separate thread. I've have read some information I found in your documentation about using ov_open_callbacks when it comes to threads. I was not sure how to adjust that so that could potentially be related to the problem. If anyone has any suggestions on what I could change and if maybe something stands out about it not being thread safe let me know. I could also show more code or perhaps go into more detail at certain points if that helps.<br>