# HG changeset patch # User Eric Wing # Date 1314758629 25200 # Node ID a55e1f3d8f10a18b0d29ebcd4bb8ed4a78da3561 # Parent 2b0b55b7f8cf9cb5ec239c3fd857d7b7af1305e1# Parent 05e5dc4817a4724ba6c9cd142d007779d684466f merged diff -r 2b0b55b7f8cf -r a55e1f3d8f10 ALmixer.c --- a/ALmixer.c Fri Aug 19 12:36:50 2011 -0700 +++ b/ALmixer.c Tue Aug 30 19:43:49 2011 -0700 @@ -62,6 +62,13 @@ */ #include "LinkedList.h" +#ifdef ANDROID_NDK + #undef fprintf + #include + #define fprintf(stderr, ...) __android_log_print(ANDROID_LOG_INFO, "ALmixer", __VA_ARGS__) +#endif + + #ifdef ENABLE_ALMIXER_THREADS /* Needed for the Mutex locks (and threads if enabled) */ #ifdef ALMIXER_COMPILE_WITHOUT_SDL @@ -5466,100 +5473,538 @@ /* NEW FEATURE: Try to queue up more buffers per pass, allowing the size of the buffer to be decoupled. */ /* TODO: Optimization: If number of available buffers (max_buffers-buffers_in_use), adjust the number of buffers to queue for this pass. */ + /* I would benefit from a clearer flag that tells me whether we're in an underrun. + * I think current_buffer only works if data callbacks are enabled. + * So I'll look at for AL_STOPPED which might give me false positives, depending on the code above. + * But I think/hope the code below will deal with it correctly. + * If in an underrun, queue up at least startup_buffers. + */ + if(AL_STOPPED == state) + { + number_of_buffers_to_queue_this_pass = ALmixer_Channel_List[i].almixer_data->num_startup_buffers; +// fprintf(stderr, "assuming underrun condition, using num_startup_buffers=%d\n", number_of_buffers_to_queue_this_pass); + } + + /* Don't bother to check to make sure the number_of_buffers_to_queue_this_pass does not exceed the maximum number of buffers because of the logic hack bug. */ + /* Logic Hack/Bug: In adding the number_of_buffers_to_queue_this_pass, I discovered the for-loop needs to be more decoupled. + * The loop still needs to be entered because unqueuing and completion callbacks and possibly other state processing are also done. + * So we always need to claim to queue one buffer. (The code already checks for max_queue_buffers.) + */ + if(0 == number_of_buffers_to_queue_this_pass) + { + number_of_buffers_to_queue_this_pass = 1; + } for(current_count_of_buffer_queue_passes=0; current_count_of_buffer_queue_passes 0 - * in which case we queue up. - * We also might find no buffers we need to fill, - * in which case we just keep going - */ - if( ! ALmixer_Channel_List[i].almixer_data->eof) - { - ALuint bytes_returned; - /* We have a priority. We first must assign - * unused buffers in reserve. If there is nothing - * left, then we may unqueue buffers. We can't - * do it the other way around because we will - * lose the pointer to the unqueued buffer +// fprintf(stderr, "current_count_of_buffer_queue_passes:%d\n", current_count_of_buffer_queue_passes); + + /* For this to work, we must rely on EVERYTHING + * else to unset the EOF if there is looping. + * Remember, even Play() must do this + */ + + /* If not EOF, then we are still playing. + * Inside, we might find num_of_buffers < NUM...QUEUE_BUF.. + * or buffers_process > 0 + * in which case we queue up. + * We also might find no buffers we need to fill, + * in which case we just keep going */ - if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use - < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) - { -#if 0 - fprintf(stderr, "Getting more data in NOT_EOF and num_buffers_in_use (%d) < max_queue (%d)\n", - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use, - ALmixer_Channel_List[i].almixer_data->max_queue_buffers); -#endif - /* Going to add an unused packet. - * Grab next packet */ - bytes_returned = GetMoreData( - ALmixer_Channel_List[i].almixer_data, - ALmixer_Channel_List[i].almixer_data->buffer[ - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] + if( ! ALmixer_Channel_List[i].almixer_data->eof) + { + ALuint bytes_returned; + /* We have a priority. We first must assign + * unused buffers in reserve. If there is nothing + * left, then we may unqueue buffers. We can't + * do it the other way around because we will + * lose the pointer to the unqueued buffer + */ + if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use + < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) + { + #if 0 + fprintf(stderr, "Getting more data in NOT_EOF and num_buffers_in_use (%d) < max_queue (%d)\n", + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use, + ALmixer_Channel_List[i].almixer_data->max_queue_buffers); + #endif + /* Going to add an unused packet. + * Grab next packet */ + bytes_returned = GetMoreData( + ALmixer_Channel_List[i].almixer_data, + ALmixer_Channel_List[i].almixer_data->buffer[ + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] + ); + } + /* For processed > 0 */ + else if(buffers_processed > 0) + { + /* Unqueue only 1 buffer for now. + * If there are more than one, + * let the next Update pass deal with it + * so we don't stall the program for too long. + */ + #if 0 + fprintf(stderr, "About to Unqueue, Buffers processed = %d\n", buffers_processed); + fprintf(stderr, "Buffers queued= %d\n", buffers_still_queued); + fprintf(stderr, "Unqueuing a buffer\n"); + #endif + alSourceUnqueueBuffers( + ALmixer_Channel_List[i].alsource, + 1, &unqueued_buffer_id + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "Error with unqueue: %s, buffer id is %d", + alGetString(error), unqueued_buffer_id); + ALmixer_SetError("Unqueue buffer failed: %s", + alGetString(error) ); + error_flag--; + } + /* + fprintf(stderr, "Right after unqueue..."); + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + fprintf(stderr, "Getting more data for NOT_EOF, max_buffers filled\n"); + */ + /* Grab unqueued packet */ + bytes_returned = GetMoreData( + ALmixer_Channel_List[i].almixer_data, + unqueued_buffer_id); + } + /* We are still streaming, but currently + * don't need to fill any buffers */ + else + { + /* Might want to check state */ + /* In case the playback stopped, + * we need to resume + * a.k.a. buffer underrun + */ + #if 1 + /* Try not refetching the state here because I'm getting a duplicate + buffer playback (hiccup) */ + alGetSourcei( + ALmixer_Channel_List[i].alsource, + AL_SOURCE_STATE, &state + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "54bTesting error: %s\n", + alGetString(error)); + } + /* Get the number of buffers processed + */ + alGetSourcei( + ALmixer_Channel_List[i].alsource, + AL_BUFFERS_PROCESSED, + &buffers_processed ); - } - /* For processed > 0 */ - else if(buffers_processed > 0) - { - /* Unqueue only 1 buffer for now. - * If there are more than one, - * let the next Update pass deal with it - * so we don't stall the program for too long. + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "54cError, Can't get buffers_processed: %s\n", + alGetString(error)); + } + #endif + if(AL_STOPPED == state) + { + /* Resuming in not eof, but nothing to buffer */ + + /* Okay, here's another lately discovered problem: + * I can't find it in the spec, but for at least some of the + * implementations, if I call play on a stopped source that + * has processed buffers, all those buffers get marked as unprocessed + * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers + * processed, on resume, the earlier 24 buffers will get replayed, + * causing a "hiccup" like sound in the playback. + * To avoid this, I must unqueue all processed buffers before + * calling play. But to complicate things, I need to worry about resyncing + * the circular queue with this since I designed this thing + * with some correlation between the two. However, I might + * have already handled this, so I will try writing this code without + * syncing for now. + * There is currently an assumption that a buffer + * was queued above so I actually have something + * to play. + */ + ALint temp_count; + #if 0 + fprintf(stderr, "STOPPED1, need to clear processed=%d, status is:\n", buffers_processed); + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + #endif + for(temp_count=0; temp_countnum_buffers_in_use = 0; + + /* Get more data and put it in the first buffer */ + bytes_returned = GetMoreData( + ALmixer_Channel_List[i].almixer_data, + ALmixer_Channel_List[i].almixer_data->buffer[0] + ); + /* NOTE: We might want to look for EOF and handle it here. + * Currently, I just let the next loop handle it which seems to be working. + */ + if(bytes_returned > 0) + { + /* Queue up the new data */ + alSourceQueueBuffers( + ALmixer_Channel_List[i].alsource, + 1, + &ALmixer_Channel_List[i].almixer_data->buffer[0] + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "56e alSourceQueueBuffers error: %s\n", + alGetString(error)); + } + /* Increment the number of buffers in use */ + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++; + + + /* We need to empty and update the circular buffer queue if it is in use */ + if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) + { + ALuint queue_ret_flag; + CircularQueueUnsignedInt_Clear(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); + queue_ret_flag = CircularQueueUnsignedInt_PushBack( + ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, + ALmixer_Channel_List[i].almixer_data->buffer[0] + ); + if(0 == queue_ret_flag) + { + fprintf(stderr, "56fSerious internal error: CircularQueue could not push into queue.\n"); + ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); + } + } + + + + + /* Resume playback from underrun */ + alSourcePlay(ALmixer_Channel_List[i].alsource); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "55Tbesting error: %s\n", + alGetString(error)); + } + } + + } + /* Let's escape to the next loop. + * All code below this point is for queuing up + */ + /* + fprintf(stderr, "Entry: Nothing to do...continue\n\n"); + */ + continue; + } + /* We now know we have to fill an available + * buffer. */ -#if 0 - fprintf(stderr, "About to Unqueue, Buffers processed = %d\n", buffers_processed); - fprintf(stderr, "Buffers queued= %d\n", buffers_still_queued); - fprintf(stderr, "Unqueuing a buffer\n"); -#endif - alSourceUnqueueBuffers( - ALmixer_Channel_List[i].alsource, - 1, &unqueued_buffer_id - ); - if((error = alGetError()) != AL_NO_ERROR) + + /* In the previous branch, we just grabbed more data. + * Let's check it to make sure it's okay, + * and then queue it up + */ + /* This check doesn't work anymore because it is now ALuint */ + #if 0 + if(-1 == bytes_returned) + { + /* Problem occurred...not sure what to do */ + /* Go to next loop? */ + error_flag--; + /* Set the eof flag to force a quit so + * we don't get stuck in an infinite loop + */ + ALmixer_Channel_List[i].almixer_data->eof = 1; + continue; + } + #endif + /* This is a special case where we've run + * out of data. We should check for loops + * and get more data. If there is no loop, + * then do nothing and wait for future + * update passes to handle the EOF. + * The advantage of handling the loop here + * instead of waiting for play to stop is + * that we should be able to keep the buffer + * filled. + */ + #if 0 + else if(0 == bytes_returned) + #endif + if(0 == bytes_returned) { - fprintf(stderr, "Error with unqueue: %s", - alGetString(error)); - ALmixer_SetError("Unqueue buffer failed: %s", - alGetString(error) ); - error_flag--; + /* + fprintf(stderr, "We got 0 bytes from reading. Checking for loops\n"); + */ + /* Check for loops */ + if( ALmixer_Channel_List[i].loops != 0 ) + { + /* We have to loop, so rewind + * and fetch more data + */ + /* + fprintf(stderr, "Rewinding data\n"); + */ + if(0 == Sound_Rewind( + ALmixer_Channel_List[i].almixer_data->sample)) + { + fprintf(stderr, "Rewinding failed\n"); + ALmixer_SetError( Sound_GetError() ); + ALmixer_Channel_List[i].loops = 0; + error_flag--; + /* We'll continue on because we do have some valid data */ + continue; + } + /* Remember to reset the data->eof flag */ + ALmixer_Channel_List[i].almixer_data->eof = 0; + if(ALmixer_Channel_List[i].loops > 0) + { + ALmixer_Channel_List[i].loops--; + } + /* Try grabbing another packet now. + * Since we may have already unqueued a + * buffer, we don't want to lose it. + */ + if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use + < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) + { + /* + fprintf(stderr, "We got %d bytes from reading loop. Filling unused packet\n", bytes_returned); + */ + /* Grab next packet */ + bytes_returned = GetMoreData( + ALmixer_Channel_List[i].almixer_data, + ALmixer_Channel_List[i].almixer_data->buffer[ + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] + ); + /* + fprintf(stderr, "We reread %d bytes into unused packet\n", bytes_returned); + */ + } + /* Refilling unqueued packet */ + else + { + /* + fprintf(stderr, "We got %d bytes from reading loop. Filling unqueued packet\n", bytes_returned); + */ + /* Grab next packet */ + bytes_returned = GetMoreData( + ALmixer_Channel_List[i].almixer_data, + unqueued_buffer_id); + /* + fprintf(stderr, "We reread %d bytes into unqueued packet\n", bytes_returned); + */ + } + /* Another error check */ + /* + if(bytes_returned <= 0) + */ + if(0 == bytes_returned) + { + fprintf(stderr, "??????????ERROR\n"); + ALmixer_SetError("Could not loop because after rewind, no data could be retrieved"); + /* Problem occurred...not sure what to do */ + /* Go to next loop? */ + error_flag--; + /* Set the eof flag to force a quit so + * we don't get stuck in an infinite loop + */ + ALmixer_Channel_List[i].almixer_data->eof = 1; + continue; + } + /* We made it to the end. We still need + * to BufferData, so let this branch + * fall into the next piece of + * code below which will handle that + */ + + + } /* END loop check */ + else + { + /* No more loops to do. + * EOF flag should be set. + * Just go to next loop and + * let things be handled correctly + * in future update calls + */ + /* + fprintf(stderr, "SHOULD BE EOF\n"); + + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + */ + continue; + } + } /* END if bytes_returned == 0 */ + /********* Possible trouble point. I might be queueing empty buffers on the mac. + * This check doesn't say if the buffer is valid. Only the EOF assumption is a clue at this point + */ + /* Fall here */ + /* Everything is normal. We aren't + * at an EOF, but need to simply + * queue more data. The data is already checked for good, + * so queue it up */ + if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use + < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) + { + /* Keep count of how many buffers we have + * to queue so we can return the value + */ + retval++; + /* + fprintf(stderr, "NOT_EOF???, about to Queue more data for num_buffers (%d) < max_queue (%d)\n", + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use, + ALmixer_Channel_List[i].almixer_data->max_queue_buffers); + */ + alSourceQueueBuffers( + ALmixer_Channel_List[i].alsource, + 1, + &ALmixer_Channel_List[i].almixer_data->buffer[ + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "56Testing error: %s\n", + alGetString(error)); + } + /* This is part of the hideous Nvidia workaround. In order to figure out + * which buffer to show during callbacks (for things like + * o-scopes), I must keep a copy of the buffers that are queued in my own + * data structure. This code will be called only if + * "access_data" was set, indicated by whether the queue is NULL. + */ + if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) + { + ALuint queue_ret_flag; + // fprintf(stderr, "56d: CircularQueue_PushBack.\n"); + queue_ret_flag = CircularQueueUnsignedInt_PushBack( + ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, + ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] + ); + if(0 == queue_ret_flag) + { + fprintf(stderr, "56aSerious internal error: CircularQueue could not push into queue.\n"); + ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); + } + /* + else + { + CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); + } + */ + } } -/* - fprintf(stderr, "Right after unqueue..."); - PrintQueueStatus(ALmixer_Channel_List[i].alsource); - fprintf(stderr, "Getting more data for NOT_EOF, max_buffers filled\n"); -*/ - /* Grab unqueued packet */ - bytes_returned = GetMoreData( - ALmixer_Channel_List[i].almixer_data, - unqueued_buffer_id); - } - /* We are still streaming, but currently - * don't need to fill any buffers */ - else - { + /* for processed > 0 */ + else + { + /* Keep count of how many buffers we have + * to queue so we can return the value + */ + retval++; + /* + fprintf(stderr, "NOT_EOF, about to Queue more data for filled max_queue (%d)\n", + ALmixer_Channel_List[i].almixer_data->max_queue_buffers); + */ + alSourceQueueBuffers( + ALmixer_Channel_List[i].alsource, + 1, &unqueued_buffer_id); + if((error = alGetError()) != AL_NO_ERROR) + { + ALmixer_SetError("Could not QueueBuffer: %s", + alGetString(error) ); + error_flag--; + continue; + } + /* This is part of the hideous Nvidia workaround. In order to figure out + * which buffer to show during callbacks (for things like + * o-scopes), I must keep a copy of the buffers that are queued in my own + * data structure. This code will be called only if + * "access_data" was set, indicated by whether the queue is NULL. + */ + if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) + { + ALuint queue_ret_flag; + // fprintf(stderr, "56e: CircularQueue_PushBack.\n"); + queue_ret_flag = CircularQueueUnsignedInt_PushBack( + ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, + unqueued_buffer_id + ); + if(0 == queue_ret_flag) + { + fprintf(stderr, "56bSerious internal error: CircularQueue could not push into queue.\n"); + ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); + } + #if 0 + else + { + CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); + } + #endif + } + } + /* If we used an available buffer queue, + * then we need to update the number of them in use + */ + if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use + < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) + { + /* Increment the number of buffers in use */ + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++; + } /* Might want to check state */ /* In case the playback stopped, - * we need to resume - * a.k.a. buffer underrun - */ + * we need to resume */ #if 1 /* Try not refetching the state here because I'm getting a duplicate - buffer playback (hiccup) */ + buffer playback (hiccup) */ alGetSourcei( ALmixer_Channel_List[i].alsource, AL_SOURCE_STATE, &state ); if((error = alGetError()) != AL_NO_ERROR) { - fprintf(stderr, "54bTesting error: %s\n", + fprintf(stderr, "57bTesting error: %s\n", alGetString(error)); } /* Get the number of buffers processed @@ -5571,638 +6016,227 @@ ); if((error = alGetError()) != AL_NO_ERROR) { - fprintf(stderr, "54cError, Can't get buffers_processed: %s\n", + fprintf(stderr, "57cError, Can't get buffers_processed: %s\n", alGetString(error)); } -#endif + #endif if(AL_STOPPED == state) { - /* Resuming in not eof, but nothing to buffer */ - - /* Okay, here's another lately discovered problem: - * I can't find it in the spec, but for at least some of the - * implementations, if I call play on a stopped source that - * has processed buffers, all those buffers get marked as unprocessed - * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers - * processed, on resume, the earlier 24 buffers will get replayed, - * causing a "hiccup" like sound in the playback. - * To avoid this, I must unqueue all processed buffers before - * calling play. But to complicate things, I need to worry about resyncing - * the circular queue with this since I designed this thing - * with some correlation between the two. However, I might - * have already handled this, so I will try writing this code without - * syncing for now. - * There is currently an assumption that a buffer - * was queued above so I actually have something - * to play. + /* + fprintf(stderr, "Resuming in not eof\n"); + */ + /* Okay, here's another lately discovered problem: + * I can't find it in the spec, but for at least some of the + * implementations, if I call play on a stopped source that + * has processed buffers, all those buffers get marked as unprocessed + * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers + * processed, on resume, the earlier 24 buffers will get replayed, + * causing a "hiccup" like sound in the playback. + * To avoid this, I must unqueue all processed buffers before + * calling play. But to complicate things, I need to worry about resyncing + * the circular queue with this since I designed this thing + * with some correlation between the two. However, I might + * have already handled this, so I will try writing this code without + * syncing for now. + * There is currently an assumption that a buffer + * was queued above so I actually have something + * to play. + */ + ALint temp_count; + /* + fprintf(stderr, "STOPPED2, need to clear processed, status is:\n"); + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + */ + + for(temp_count=0; temp_count 0) + { + ALint temp_count; + /* Do as a for-loop because I don't want + * to have to create an array for the + * unqueued_buffer_id's */ - ALint temp_count; -#if 0 - fprintf(stderr, "STOPPED1, need to clear processed=%d, status is:\n", buffers_processed); - PrintQueueStatus(ALmixer_Channel_List[i].alsource); -#endif for(temp_count=0; temp_countnum_buffers_in_use = 0; - - /* Get more data and put it in the first buffer */ - bytes_returned = GetMoreData( - ALmixer_Channel_List[i].almixer_data, - ALmixer_Channel_List[i].almixer_data->buffer[0] - ); - /* NOTE: We might want to look for EOF and handle it here. - * Currently, I just let the next loop handle it which seems to be working. - */ - if(bytes_returned > 0) - { - /* Queue up the new data */ - alSourceQueueBuffers( - ALmixer_Channel_List[i].alsource, - 1, - &ALmixer_Channel_List[i].almixer_data->buffer[0] - ); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "56e alSourceQueueBuffers error: %s\n", + fprintf(stderr, "59Testing error: %s\n", alGetString(error)); } - /* Increment the number of buffers in use */ - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++; - - - /* We need to empty and update the circular buffer queue if it is in use */ - if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) - { - ALuint queue_ret_flag; - CircularQueueUnsignedInt_Clear(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); - queue_ret_flag = CircularQueueUnsignedInt_PushBack( - ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, - ALmixer_Channel_List[i].almixer_data->buffer[0] - ); - if(0 == queue_ret_flag) - { - fprintf(stderr, "56fSerious internal error: CircularQueue could not push into queue.\n"); - ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); - } - } - - - - - /* Resume playback from underrun */ - alSourcePlay(ALmixer_Channel_List[i].alsource); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "55Tbesting error: %s\n", - alGetString(error)); - } } - - } - /* Let's escape to the next loop. - * All code below this point is for queuing up - */ - /* - fprintf(stderr, "Entry: Nothing to do...continue\n\n"); - */ - continue; - } - /* We now know we have to fill an available - * buffer. - */ - - /* In the previous branch, we just grabbed more data. - * Let's check it to make sure it's okay, - * and then queue it up - */ - /* This check doesn't work anymore because it is now ALuint */ - #if 0 - if(-1 == bytes_returned) - { - /* Problem occurred...not sure what to do */ - /* Go to next loop? */ - error_flag--; - /* Set the eof flag to force a quit so - * we don't get stuck in an infinite loop - */ - ALmixer_Channel_List[i].almixer_data->eof = 1; - continue; - } - #endif - /* This is a special case where we've run - * out of data. We should check for loops - * and get more data. If there is no loop, - * then do nothing and wait for future - * update passes to handle the EOF. - * The advantage of handling the loop here - * instead of waiting for play to stop is - * that we should be able to keep the buffer - * filled. - */ - #if 0 - else if(0 == bytes_returned) - #endif - if(0 == bytes_returned) - { - /* - fprintf(stderr, "We got 0 bytes from reading. Checking for loops\n"); - */ - /* Check for loops */ - if( ALmixer_Channel_List[i].loops != 0 ) - { - /* We have to loop, so rewind - * and fetch more data - */ /* - fprintf(stderr, "Rewinding data\n"); - */ - if(0 == Sound_Rewind( - ALmixer_Channel_List[i].almixer_data->sample)) - { - fprintf(stderr, "Rewinding failed\n"); - ALmixer_SetError( Sound_GetError() ); - ALmixer_Channel_List[i].loops = 0; - error_flag--; - /* We'll continue on because we do have some valid data */ - continue; - } - /* Remember to reset the data->eof flag */ - ALmixer_Channel_List[i].almixer_data->eof = 0; - if(ALmixer_Channel_List[i].loops > 0) - { - ALmixer_Channel_List[i].loops--; - } - /* Try grabbing another packet now. - * Since we may have already unqueued a - * buffer, we don't want to lose it. - */ - if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use - < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) - { - /* - fprintf(stderr, "We got %d bytes from reading loop. Filling unused packet\n", bytes_returned); - */ - /* Grab next packet */ - bytes_returned = GetMoreData( - ALmixer_Channel_List[i].almixer_data, - ALmixer_Channel_List[i].almixer_data->buffer[ - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] - ); - /* - fprintf(stderr, "We reread %d bytes into unused packet\n", bytes_returned); - */ - } - /* Refilling unqueued packet */ - else - { - /* - fprintf(stderr, "We got %d bytes from reading loop. Filling unqueued packet\n", bytes_returned); - */ - /* Grab next packet */ - bytes_returned = GetMoreData( - ALmixer_Channel_List[i].almixer_data, - unqueued_buffer_id); - /* - fprintf(stderr, "We reread %d bytes into unqueued packet\n", bytes_returned); - */ - } - /* Another error check */ - /* - if(bytes_returned <= 0) + fprintf(stderr, "done unqueuing remainder for this loop, %d\n", temp_count); */ - if(0 == bytes_returned) - { - fprintf(stderr, "??????????ERROR\n"); - ALmixer_SetError("Could not loop because after rewind, no data could be retrieved"); - /* Problem occurred...not sure what to do */ - /* Go to next loop? */ - error_flag--; - /* Set the eof flag to force a quit so - * we don't get stuck in an infinite loop - */ - ALmixer_Channel_List[i].almixer_data->eof = 1; - continue; - } - /* We made it to the end. We still need - * to BufferData, so let this branch - * fall into the next piece of - * code below which will handle that - */ - - - } /* END loop check */ - else - { - /* No more loops to do. - * EOF flag should be set. - * Just go to next loop and - * let things be handled correctly - * in future update calls + + /* Need to update counts since we removed everything. + * If we don't update the counts here, we end up in the + * "Shouldn't be here section, but maybe it's okay due to race conditions" */ -/* - fprintf(stderr, "SHOULD BE EOF\n"); - - PrintQueueStatus(ALmixer_Channel_List[i].alsource); -*/ - continue; - } - } /* END if bytes_returned == 0 */ -/********* Possible trouble point. I might be queueing empty buffers on the mac. - * This check doesn't say if the buffer is valid. Only the EOF assumption is a clue at this point - */ - /* Fall here */ - /* Everything is normal. We aren't - * at an EOF, but need to simply - * queue more data. The data is already checked for good, - * so queue it up */ - if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use - < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) - { - /* Keep count of how many buffers we have - * to queue so we can return the value - */ - retval++; - /* - fprintf(stderr, "NOT_EOF???, about to Queue more data for num_buffers (%d) < max_queue (%d)\n", - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use, - ALmixer_Channel_List[i].almixer_data->max_queue_buffers); - */ - alSourceQueueBuffers( - ALmixer_Channel_List[i].alsource, - 1, - &ALmixer_Channel_List[i].almixer_data->buffer[ - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] - ); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "56Testing error: %s\n", - alGetString(error)); - } - /* This is part of the hideous Nvidia workaround. In order to figure out - * which buffer to show during callbacks (for things like - * o-scopes), I must keep a copy of the buffers that are queued in my own - * data structure. This code will be called only if - * "access_data" was set, indicated by whether the queue is NULL. - */ - if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) - { - ALuint queue_ret_flag; -// fprintf(stderr, "56d: CircularQueue_PushBack.\n"); - queue_ret_flag = CircularQueueUnsignedInt_PushBack( - ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, - ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] - ); - if(0 == queue_ret_flag) - { - fprintf(stderr, "56aSerious internal error: CircularQueue could not push into queue.\n"); - ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); - } - /* - else - { - CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); - } - */ - } - } - /* for processed > 0 */ - else - { - /* Keep count of how many buffers we have - * to queue so we can return the value - */ - retval++; -/* - fprintf(stderr, "NOT_EOF, about to Queue more data for filled max_queue (%d)\n", - ALmixer_Channel_List[i].almixer_data->max_queue_buffers); -*/ - alSourceQueueBuffers( - ALmixer_Channel_List[i].alsource, - 1, &unqueued_buffer_id); - if((error = alGetError()) != AL_NO_ERROR) - { - ALmixer_SetError("Could not QueueBuffer: %s", - alGetString(error) ); - error_flag--; - continue; - } - /* This is part of the hideous Nvidia workaround. In order to figure out - * which buffer to show during callbacks (for things like - * o-scopes), I must keep a copy of the buffers that are queued in my own - * data structure. This code will be called only if - * "access_data" was set, indicated by whether the queue is NULL. - */ - if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) - { - ALuint queue_ret_flag; -// fprintf(stderr, "56e: CircularQueue_PushBack.\n"); - queue_ret_flag = CircularQueueUnsignedInt_PushBack( - ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, - unqueued_buffer_id - ); - if(0 == queue_ret_flag) - { - fprintf(stderr, "56bSerious internal error: CircularQueue could not push into queue.\n"); - ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); - } -#if 0 - else - { - CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue); - } -#endif - } - } - /* If we used an available buffer queue, - * then we need to update the number of them in use - */ - if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use - < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) - { - /* Increment the number of buffers in use */ - ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++; - } - /* Might want to check state */ - /* In case the playback stopped, - * we need to resume */ - #if 1 - /* Try not refetching the state here because I'm getting a duplicate - buffer playback (hiccup) */ - alGetSourcei( - ALmixer_Channel_List[i].alsource, - AL_SOURCE_STATE, &state - ); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "57bTesting error: %s\n", - alGetString(error)); - } - /* Get the number of buffers processed - */ - alGetSourcei( - ALmixer_Channel_List[i].alsource, - AL_BUFFERS_PROCESSED, - &buffers_processed - ); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "57cError, Can't get buffers_processed: %s\n", - alGetString(error)); - } - #endif - if(AL_STOPPED == state) - { - /* - fprintf(stderr, "Resuming in not eof\n"); - */ - /* Okay, here's another lately discovered problem: - * I can't find it in the spec, but for at least some of the - * implementations, if I call play on a stopped source that - * has processed buffers, all those buffers get marked as unprocessed - * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers - * processed, on resume, the earlier 24 buffers will get replayed, - * causing a "hiccup" like sound in the playback. - * To avoid this, I must unqueue all processed buffers before - * calling play. But to complicate things, I need to worry about resyncing - * the circular queue with this since I designed this thing - * with some correlation between the two. However, I might - * have already handled this, so I will try writing this code without - * syncing for now. - * There is currently an assumption that a buffer - * was queued above so I actually have something - * to play. - */ - ALint temp_count; -/* - fprintf(stderr, "STOPPED2, need to clear processed, status is:\n"); - PrintQueueStatus(ALmixer_Channel_List[i].alsource); -*/ - - for(temp_count=0; temp_count 0) - { - ALint temp_count; - /* Do as a for-loop because I don't want - * to have to create an array for the - * unqueued_buffer_id's - */ - for(temp_count=0; temp_countnum_buffers_in_use = 0; - /* Playback has ended. - * Loop if necessary, or launch callback - * and clear channel (or clear channel and - * then launch callback?) - * Update: Need to do callback first because I reference the mixer_data and source - */ - - /* Launch callback */ - Invoke_Channel_Done_Callback(i, AL_TRUE); - - Clean_Channel(i); - /* Subtract counter */ - Is_Playing_global--; - - - /* We're done for this loop. - * Go to next channel + /* Make sure playback has stopped before + * we shutdown. */ - continue; - } - } /* End end-playback */ - else - { - /* Need to run out buffer */ - #if 1 - /* Might want to check state */ - /* In case the playback stopped, - * we need to resume */ - alGetSourcei( - ALmixer_Channel_List[i].alsource, - AL_SOURCE_STATE, &state - ); - if((error = alGetError()) != AL_NO_ERROR) - { - fprintf(stderr, "61Testing error: %s\n", - alGetString(error)); - } - if(AL_STOPPED == state) + alGetSourcei( + ALmixer_Channel_List[i].alsource, + AL_SOURCE_STATE, &state + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "60Testing error: %s\n", + alGetString(error)); + } + if(AL_STOPPED == state) + { + ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0; + /* Playback has ended. + * Loop if necessary, or launch callback + * and clear channel (or clear channel and + * then launch callback?) + * Update: Need to do callback first because I reference the mixer_data and source + */ + + /* Launch callback */ + Invoke_Channel_Done_Callback(i, AL_TRUE); + + Clean_Channel(i); + /* Subtract counter */ + Is_Playing_global--; + + + /* We're done for this loop. + * Go to next channel + */ + + /* I used to call continue here, but continue isn't safe anymore because we are in the number_of_queue_buffers_per_pass loop now. + * We should break, but we need to be careful that there is no code that is run once we break that wasn't run before. + * We want to make sure we go immediately to the next iteration in the channel loop. + */ + break; + } + } /* End end-playback */ + else { - /* - fprintf(stderr, "Shouldn't be here. %d Buffers still in queue, but play stopped. This might be correct though because race conditions could have caused the STOP to happen right after our other tests...Checking queue status...\n", buffers_still_queued); - */ -/* - PrintQueueStatus(ALmixer_Channel_List[i].alsource); -*/ - /* Rather than force unqueuing the buffer, let's see if - * setting the buffer to none works (the OpenAL 1.0 - * Reference Annotation suggests this should work). - */ - alSourcei(ALmixer_Channel_List[i].alsource, - AL_BUFFER, AL_NONE); -/* - PrintQueueStatus(ALmixer_Channel_List[i].alsource); -*/ - /* This doesn't work because in some cases, I think - * it causes the sound to be replayed - */ - /* - fprintf(stderr, "Resuming in eof (trying to run out buffers\n"); - alSourcePlay(ALmixer_Channel_List[i].alsource); - */ - } - #endif - } /* End trap section */ - } /* End POST-EOF use-up buffer section */ - - } /* END NEW number of queue buffers per pass */ + /* Need to run out buffer */ + #if 1 + /* Might want to check state */ + /* In case the playback stopped, + * we need to resume */ + alGetSourcei( + ALmixer_Channel_List[i].alsource, + AL_SOURCE_STATE, &state + ); + if((error = alGetError()) != AL_NO_ERROR) + { + fprintf(stderr, "61Testing error: %s\n", + alGetString(error)); + } + if(AL_STOPPED == state) + { + /* + fprintf(stderr, "Shouldn't be here. %d Buffers still in queue, but play stopped. This might be correct though because race conditions could have caused the STOP to happen right after our other tests...Checking queue status...\n", buffers_still_queued); + */ + /* + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + */ + /* Rather than force unqueuing the buffer, let's see if + * setting the buffer to none works (the OpenAL 1.0 + * Reference Annotation suggests this should work). + */ + alSourcei(ALmixer_Channel_List[i].alsource, + AL_BUFFER, AL_NONE); + /* + PrintQueueStatus(ALmixer_Channel_List[i].alsource); + */ + /* This doesn't work because in some cases, I think + * it causes the sound to be replayed + */ + /* + fprintf(stderr, "Resuming in eof (trying to run out buffers\n"); + alSourcePlay(ALmixer_Channel_List[i].alsource); + */ + } + #endif + } /* End trap section */ + } /* End POST-EOF use-up buffer section */ + + } /* END NEW number of queue buffers per pass */ } /* END Streamed section */ } /* END channel in use */ @@ -7829,7 +7863,7 @@ -static ALmixer_Data* DoLoad(Sound_Sample* sample, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data) +static ALmixer_Data* DoLoad(Sound_Sample* sample, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data) { ALuint bytes_decoded; ALmixer_Data* ret_data; @@ -7882,8 +7916,17 @@ } ret_data->num_startup_buffers = num_startup_buffers; - /* TODO: Expose value through public API */ - ret_data->num_target_buffers_per_pass = 1; + /* Set up the update pass buffers */ + if(0 == suggested_number_of_buffers_to_queue_per_update_pass) + { + suggested_number_of_buffers_to_queue_per_update_pass = ALMIXER_DEFAULT_BUFFERS_TO_QUEUE_PER_UPDATE_PASS; + } + /* Make sure update pass up buffers is less or equal to max_queue_buffers */ + if(suggested_number_of_buffers_to_queue_per_update_pass > max_queue_buffers) + { + suggested_number_of_buffers_to_queue_per_update_pass = max_queue_buffers; + } + ret_data->num_target_buffers_per_pass = suggested_number_of_buffers_to_queue_per_update_pass; ret_data->buffer_map_list = NULL; ret_data->current_buffer = 0; @@ -8377,7 +8420,7 @@ * must specify it, so I had to bring it back. * Remember I must close the rwops if there is an error before NewSample() */ -ALmixer_Data* ALmixer_LoadSample_RW(ALmixer_RWops* rwops, const char* fileext, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data) +ALmixer_Data* ALmixer_LoadSample_RW(ALmixer_RWops* rwops, const char* fileext, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data) { Sound_Sample* sample = NULL; Sound_AudioInfo target; @@ -8418,7 +8461,7 @@ return NULL; } - return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); + return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data)); } @@ -8428,7 +8471,7 @@ * error checking and the fact that streamed/predecoded files * must be treated differently. */ -ALmixer_Data* ALmixer_LoadSample(const char* filename, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data) +ALmixer_Data* ALmixer_LoadSample(const char* filename, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data) { Sound_Sample* sample = NULL; Sound_AudioInfo target; @@ -8528,14 +8571,14 @@ /* fprintf(stderr, "Correction test: Actual rate=%d, desired=%d, actual format=%d, desired format=%d\n", sample->actual.rate, sample->desired.rate, sample->actual.format, sample->desired.format); */ - return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); + return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data)); } /* This is a back door for RAW samples or if you need the * AudioInfo field. Use at your own risk. */ -ALmixer_Data* ALmixer_LoadSample_RAW_RW(ALmixer_RWops* rwops, const char* fileext, ALmixer_AudioInfo* desired, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data) +ALmixer_Data* ALmixer_LoadSample_RAW_RW(ALmixer_RWops* rwops, const char* fileext, ALmixer_AudioInfo* desired, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data) { Sound_Sample* sample = NULL; Sound_AudioInfo sound_desired; @@ -8568,7 +8611,7 @@ ALmixer_SetError(Sound_GetError()); return NULL; } - return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); + return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers,suggested_number_of_buffers_to_queue_per_update_pass, access_data)); } @@ -8577,7 +8620,7 @@ /* This is a back door for RAW samples or if you need the * AudioInfo field. Use at your own risk. */ -ALmixer_Data* ALmixer_LoadSample_RAW(const char* filename, ALmixer_AudioInfo* desired, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data) +ALmixer_Data* ALmixer_LoadSample_RAW(const char* filename, ALmixer_AudioInfo* desired, ALuint buffersize, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data) { Sound_Sample* sample = NULL; Sound_AudioInfo sound_desired; @@ -8611,7 +8654,7 @@ ALmixer_SetError(Sound_GetError()); return NULL; } - return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); + return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data)); } diff -r 2b0b55b7f8cf -r a55e1f3d8f10 ALmixer.h --- a/ALmixer.h Fri Aug 19 12:36:50 2011 -0700 +++ b/ALmixer.h Tue Aug 30 19:43:49 2011 -0700 @@ -479,9 +479,9 @@ /* #define ALMIXER_DEFAULT_BUFFERSIZE 32768 -#define ALMIXER_DEFAULT_BUFFERSIZE 4096 +#define ALMIXER_DEFAULT_BUFFERSIZE 16384 */ -#define ALMIXER_DEFAULT_BUFFERSIZE 16384 +#define ALMIXER_DEFAULT_BUFFERSIZE 4096 /* You probably never need to use these macros directly. */ #ifndef ALMIXER_DISABLE_PREDECODED_PRECOMPUTE_BUFFER_SIZE_OPTIMIZATION @@ -499,11 +499,18 @@ * Specifies the maximum number of queue buffers to use for a sound stream. * Default Queue Buffers must be at least 2. */ -#define ALMIXER_DEFAULT_QUEUE_BUFFERS 12 +/* +#define ALMIXER_DEFAULT_QUEUE_BUFFERS 5 +*/ +#define ALMIXER_DEFAULT_QUEUE_BUFFERS 25 /** * Specifies the number of queue buffers initially filled when first loading a stream. * Default startup buffers should be at least 1. */ +#define ALMIXER_DEFAULT_STARTUP_BUFFERS 8 +/* #define ALMIXER_DEFAULT_STARTUP_BUFFERS 2 +*/ +#define ALMIXER_DEFAULT_BUFFERS_TO_QUEUE_PER_UPDATE_PASS 4 /* #define ALMIXER_DECODE_STREAM 0 @@ -555,6 +562,9 @@ * @param decode_mode_is_predecoded Specifies whether you want to completely preload the data or stream the data in chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -562,7 +572,7 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -extern ALMIXER_DECLSPEC ALmixer_Data* ALMIXER_CALL ALmixer_LoadSample_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +extern ALMIXER_DECLSPEC ALmixer_Data* ALMIXER_CALL ALmixer_LoadSample_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #ifdef DOXYGEN_ONLY /** @@ -574,6 +584,9 @@ * the valid frame sizes of your audio data. If the data is streamed, the data will be read in buffer_size chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -581,9 +594,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadStream_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +ALmixer_Data* ALmixer_LoadStream_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #else -#define ALmixer_LoadStream_RW(rw_ops, file_ext, buffer_size, max_queue_buffers, num_startup_buffers, access_data) ALmixer_LoadSample_RW(rw_ops,file_ext, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, access_data) +#define ALmixer_LoadStream_RW(rw_ops, file_ext, buffer_size, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) ALmixer_LoadSample_RW(rw_ops,file_ext, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) #endif #ifdef DOXYGEN_ONLY @@ -599,9 +612,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadAll_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALboolean access_data); +ALmixer_Data* ALmixer_LoadAll_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALuint access_data); #else -#define ALmixer_LoadAll_RW(rw_ops, file_ext, access_data) ALmixer_LoadSample_RW(rw_ops, fileext, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, access_data) +#define ALmixer_LoadAll_RW(rw_ops, file_ext, access_data) ALmixer_LoadSample_RW(rw_ops, fileext, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, 0, access_data) #endif /** @@ -614,6 +627,9 @@ * @param decode_mode_is_predecoded Specifies whether you want to completely preload the data or stream the data in chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -621,7 +637,7 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample(const char* file_name, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample(const char* file_name, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #ifdef DOXYGEN_ONLY /** @@ -631,6 +647,12 @@ * the valid frame sizes of your audio data. If the data is streamed, the data will be read in buffer_size chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -638,9 +660,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadStream(const char* file_name, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +ALmixer_Data* ALmixer_LoadStream(const char* file_name, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #else -#define ALmixer_LoadStream(file_name, buffer_size, max_queue_buffers, num_startup_buffers,access_data) ALmixer_LoadSample(file_name, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, access_data) +#define ALmixer_LoadStream(file_name, buffer_size, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) ALmixer_LoadSample(file_name, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) #endif #ifdef DOXYGEN_ONLY @@ -654,9 +676,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadAll(const char* file_name, ALboolean access_data); +ALmixer_Data* ALmixer_LoadAll(const char* file_name, ALuint access_data); #else -#define ALmixer_LoadAll(file_name, access_data) ALmixer_LoadSample(file_name, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, access_data) +#define ALmixer_LoadAll(file_name, access_data) ALmixer_LoadSample(file_name, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, 0, access_data) #endif /** @@ -673,6 +695,9 @@ * @param decode_mode_is_predecoded Specifies whether you want to completely preload the data or stream the data in chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -680,7 +705,7 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #ifdef DOXYGEN_ONLY /** @@ -695,6 +720,9 @@ * If the file is to be predecoded, optimizations may occur and this value might be ignored. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -702,9 +730,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadStream_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +ALmixer_Data* ALmixer_LoadStream_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #else -#define ALmixer_LoadStream_RAW_RW(rw_ops, file_ext, desired_format, buffer_size, max_queue_buffers, num_startup_buffers, access_data) ALmixer_LoadSample_RAW_RW(rw_ops, file_ext, desired_format, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, access_data) +#define ALmixer_LoadStream_RAW_RW(rw_ops, file_ext, desired_format, buffer_size, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) ALmixer_LoadSample_RAW_RW(rw_ops, file_ext, desired_format, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) #endif #ifdef DOXYGEN_ONLY @@ -722,9 +750,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadAll_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALboolean access_data); +ALmixer_Data* ALmixer_LoadAll_RAW_RW(ALmixer_RWops* rw_ops, const char* file_ext, ALmixer_AudioInfo* desired_format, ALuint access_data); #else -#define ALmixer_LoadAll_RAW_RW(rw_ops, file_ext, desired_format, access_data) ALmixer_LoadSample_RAW_RW(rw_ops, file_ext, desired_format, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, access_data) +#define ALmixer_LoadAll_RAW_RW(rw_ops, file_ext, desired_format, access_data) ALmixer_LoadSample_RAW_RW(rw_ops, file_ext, desired_format, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, 0, access_data) #endif /** @@ -739,6 +767,9 @@ * @param decode_mode_is_predecoded Specifies whether you want to completely preload the data or stream the data in chunks. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -746,7 +777,7 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +extern ALMIXER_DECLSPEC ALmixer_Data * ALMIXER_CALL ALmixer_LoadSample_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALboolean decode_mode_is_predecoded, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #ifdef DOXYGEN_ONLY /** @@ -759,6 +790,9 @@ * If the file is to be predecoded, optimizations may occur and this value might be ignored. * @param max_queue_buffers For streamed data, specifies the maximum number of buffers that can be queued at any given time. * @param num_startup_buffers For streamed data, specifies the number of buffers to fill before playback starts. + * Buffer underrun conditions will also attempt to restart using this value. + * @param suggested_number_of_buffers_to_queue_per_update_pass For each ALmixer_Update() pass, this is the targeted number + * of buffers that will be queued. * @param access_data A boolean that specifies if you want the data contained in the currently playing buffer to be handed * to you in a callback function. Note that for predecoded data, you get back the entire buffer in one callback when the * audio first starts playing. With streamed data, you get the data in buffer_size chunks. Callbacks are not guarnanteed @@ -766,9 +800,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadStream_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALboolean access_data); +ALmixer_Data* ALmixer_LoadStream_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALuint buffer_size, ALuint max_queue_buffers, ALuint num_startup_buffers, ALuint suggested_number_of_buffers_to_queue_per_update_pass, ALuint access_data); #else -#define ALmixer_LoadStream_RAW(file_name, desired_format, buffer_size, max_queue_buffers, num_startup_buffers, access_data) ALmixer_LoadSample_RAW(file_name, desired_format, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, access_data) +#define ALmixer_LoadStream_RAW(file_name, desired_format, buffer_size, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) ALmixer_LoadSample_RAW(file_name, desired_format, buffer_size, AL_FALSE, max_queue_buffers, num_startup_buffers, suggested_number_of_buffers_to_queue_per_update_pass, access_data) #endif #ifdef DOXYGEN_ONLY @@ -784,9 +818,9 @@ * using this feature, so if you don't need data callbacks, you should pass false to this function. * @return Returns an ALmixer_Data* of the loaded sample or NULL if failed. */ -ALmixer_Data* ALmixer_LoadAll_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALboolean access_data); +ALmixer_Data* ALmixer_LoadAll_RAW(const char* file_name, ALmixer_AudioInfo* desired_format, ALuint access_data); #else -#define ALmixer_LoadAll_RAW(file_name, desired_format, access_data) ALmixer_LoadSample_RAW(file_name, desired_format, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, access_data) +#define ALmixer_LoadAll_RAW(file_name, desired_format, access_data) ALmixer_LoadSample_RAW(file_name, desired_format, ALMIXER_DEFAULT_PREDECODED_BUFFERSIZE, AL_TRUE, 0, 0, 0, access_data) #endif /**