comparison ALmixer.c @ 42:05e5dc4817a4

Warning: Breaking API/ABI changes. New optimizations to decouple update loop from number of buffers queued in audio streaming to allow more for more and smaller buffers to be processed in a loop. This also has the benefit for access_data callback buffer sizes not to be dictated by the performance tuning. Note that the Load APIs break and add a new parameter to specify the target number of buffers to queue per update pass. Also, I changed the access_data from ALboolean to ALuint for possible future use since I'm already breaking the API. The motivation for this is for the feature request of allowing for data callbacks on buffer queue to modify the input data right before submission.
author Eric Wing <ewing@anscamobile.com>
date Tue, 30 Aug 2011 19:42:31 -0700
parents e10dd3056782
children 56855942fdc6
comparison
equal deleted inserted replaced
41:e10dd3056782 42:05e5dc4817a4
59 /* SDL_sound keeps a private linked list of sounds which get auto-deleted 59 /* SDL_sound keeps a private linked list of sounds which get auto-deleted
60 * on Sound_Quit. This might actually create some leaks for me in certain 60 * on Sound_Quit. This might actually create some leaks for me in certain
61 * usage patterns. To be safe, I should do the same. 61 * usage patterns. To be safe, I should do the same.
62 */ 62 */
63 #include "LinkedList.h" 63 #include "LinkedList.h"
64
65 #ifdef ANDROID_NDK
66 #undef fprintf
67 #include <android/log.h>
68 #define fprintf(stderr, ...) __android_log_print(ANDROID_LOG_INFO, "ALmixer", __VA_ARGS__)
69 #endif
70
64 71
65 #ifdef ENABLE_ALMIXER_THREADS 72 #ifdef ENABLE_ALMIXER_THREADS
66 /* Needed for the Mutex locks (and threads if enabled) */ 73 /* Needed for the Mutex locks (and threads if enabled) */
67 #ifdef ALMIXER_COMPILE_WITHOUT_SDL 74 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
68 #include "SimpleMutex.h" 75 #include "SimpleMutex.h"
5464 * except when there is looping 5471 * except when there is looping
5465 */ 5472 */
5466 5473
5467 /* NEW FEATURE: Try to queue up more buffers per pass, allowing the size of the buffer to be decoupled. */ 5474 /* NEW FEATURE: Try to queue up more buffers per pass, allowing the size of the buffer to be decoupled. */
5468 /* TODO: Optimization: If number of available buffers (max_buffers-buffers_in_use), adjust the number of buffers to queue for this pass. */ 5475 /* TODO: Optimization: If number of available buffers (max_buffers-buffers_in_use), adjust the number of buffers to queue for this pass. */
5476 /* I would benefit from a clearer flag that tells me whether we're in an underrun.
5477 * I think current_buffer only works if data callbacks are enabled.
5478 * So I'll look at for AL_STOPPED which might give me false positives, depending on the code above.
5479 * But I think/hope the code below will deal with it correctly.
5480 * If in an underrun, queue up at least startup_buffers.
5481 */
5482 if(AL_STOPPED == state)
5483 {
5484 number_of_buffers_to_queue_this_pass = ALmixer_Channel_List[i].almixer_data->num_startup_buffers;
5485 // fprintf(stderr, "assuming underrun condition, using num_startup_buffers=%d\n", number_of_buffers_to_queue_this_pass);
5486 }
5487
5488 /* 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. */
5489 /* Logic Hack/Bug: In adding the number_of_buffers_to_queue_this_pass, I discovered the for-loop needs to be more decoupled.
5490 * The loop still needs to be entered because unqueuing and completion callbacks and possibly other state processing are also done.
5491 * So we always need to claim to queue one buffer. (The code already checks for max_queue_buffers.)
5492 */
5493 if(0 == number_of_buffers_to_queue_this_pass)
5494 {
5495 number_of_buffers_to_queue_this_pass = 1;
5496 }
5469 for(current_count_of_buffer_queue_passes=0; current_count_of_buffer_queue_passes<number_of_buffers_to_queue_this_pass; current_count_of_buffer_queue_passes++) 5497 for(current_count_of_buffer_queue_passes=0; current_count_of_buffer_queue_passes<number_of_buffers_to_queue_this_pass; current_count_of_buffer_queue_passes++)
5470 { 5498 {
5471 5499 // fprintf(stderr, "current_count_of_buffer_queue_passes:%d\n", current_count_of_buffer_queue_passes);
5472 /* For this to work, we must rely on EVERYTHING 5500
5473 * else to unset the EOF if there is looping. 5501 /* For this to work, we must rely on EVERYTHING
5474 * Remember, even Play() must do this 5502 * else to unset the EOF if there is looping.
5475 */ 5503 * Remember, even Play() must do this
5476
5477 /* If not EOF, then we are still playing.
5478 * Inside, we might find num_of_buffers < NUM...QUEUE_BUF..
5479 * or buffers_process > 0
5480 * in which case we queue up.
5481 * We also might find no buffers we need to fill,
5482 * in which case we just keep going
5483 */
5484 if( ! ALmixer_Channel_List[i].almixer_data->eof)
5485 {
5486 ALuint bytes_returned;
5487 /* We have a priority. We first must assign
5488 * unused buffers in reserve. If there is nothing
5489 * left, then we may unqueue buffers. We can't
5490 * do it the other way around because we will
5491 * lose the pointer to the unqueued buffer
5492 */ 5504 */
5493 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use 5505
5494 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers) 5506 /* If not EOF, then we are still playing.
5495 { 5507 * Inside, we might find num_of_buffers < NUM...QUEUE_BUF..
5496 #if 0 5508 * or buffers_process > 0
5497 fprintf(stderr, "Getting more data in NOT_EOF and num_buffers_in_use (%d) < max_queue (%d)\n", 5509 * in which case we queue up.
5498 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use, 5510 * We also might find no buffers we need to fill,
5499 ALmixer_Channel_List[i].almixer_data->max_queue_buffers); 5511 * in which case we just keep going
5500 #endif 5512 */
5501 /* Going to add an unused packet. 5513 if( ! ALmixer_Channel_List[i].almixer_data->eof)
5502 * Grab next packet */ 5514 {
5503 bytes_returned = GetMoreData( 5515 ALuint bytes_returned;
5504 ALmixer_Channel_List[i].almixer_data, 5516 /* We have a priority. We first must assign
5505 ALmixer_Channel_List[i].almixer_data->buffer[ 5517 * unused buffers in reserve. If there is nothing
5506 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] 5518 * left, then we may unqueue buffers. We can't
5519 * do it the other way around because we will
5520 * lose the pointer to the unqueued buffer
5521 */
5522 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5523 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5524 {
5525 #if 0
5526 fprintf(stderr, "Getting more data in NOT_EOF and num_buffers_in_use (%d) < max_queue (%d)\n",
5527 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use,
5528 ALmixer_Channel_List[i].almixer_data->max_queue_buffers);
5529 #endif
5530 /* Going to add an unused packet.
5531 * Grab next packet */
5532 bytes_returned = GetMoreData(
5533 ALmixer_Channel_List[i].almixer_data,
5534 ALmixer_Channel_List[i].almixer_data->buffer[
5535 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5536 );
5537 }
5538 /* For processed > 0 */
5539 else if(buffers_processed > 0)
5540 {
5541 /* Unqueue only 1 buffer for now.
5542 * If there are more than one,
5543 * let the next Update pass deal with it
5544 * so we don't stall the program for too long.
5545 */
5546 #if 0
5547 fprintf(stderr, "About to Unqueue, Buffers processed = %d\n", buffers_processed);
5548 fprintf(stderr, "Buffers queued= %d\n", buffers_still_queued);
5549 fprintf(stderr, "Unqueuing a buffer\n");
5550 #endif
5551 alSourceUnqueueBuffers(
5552 ALmixer_Channel_List[i].alsource,
5553 1, &unqueued_buffer_id
5507 ); 5554 );
5508 } 5555 if((error = alGetError()) != AL_NO_ERROR)
5509 /* For processed > 0 */ 5556 {
5510 else if(buffers_processed > 0) 5557 fprintf(stderr, "Error with unqueue: %s, buffer id is %d",
5511 { 5558 alGetString(error), unqueued_buffer_id);
5512 /* Unqueue only 1 buffer for now. 5559 ALmixer_SetError("Unqueue buffer failed: %s",
5513 * If there are more than one, 5560 alGetString(error) );
5514 * let the next Update pass deal with it 5561 error_flag--;
5515 * so we don't stall the program for too long. 5562 }
5563 /*
5564 fprintf(stderr, "Right after unqueue...");
5565 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5566 fprintf(stderr, "Getting more data for NOT_EOF, max_buffers filled\n");
5567 */
5568 /* Grab unqueued packet */
5569 bytes_returned = GetMoreData(
5570 ALmixer_Channel_List[i].almixer_data,
5571 unqueued_buffer_id);
5572 }
5573 /* We are still streaming, but currently
5574 * don't need to fill any buffers */
5575 else
5576 {
5577 /* Might want to check state */
5578 /* In case the playback stopped,
5579 * we need to resume
5580 * a.k.a. buffer underrun
5581 */
5582 #if 1
5583 /* Try not refetching the state here because I'm getting a duplicate
5584 buffer playback (hiccup) */
5585 alGetSourcei(
5586 ALmixer_Channel_List[i].alsource,
5587 AL_SOURCE_STATE, &state
5588 );
5589 if((error = alGetError()) != AL_NO_ERROR)
5590 {
5591 fprintf(stderr, "54bTesting error: %s\n",
5592 alGetString(error));
5593 }
5594 /* Get the number of buffers processed
5595 */
5596 alGetSourcei(
5597 ALmixer_Channel_List[i].alsource,
5598 AL_BUFFERS_PROCESSED,
5599 &buffers_processed
5600 );
5601 if((error = alGetError()) != AL_NO_ERROR)
5602 {
5603 fprintf(stderr, "54cError, Can't get buffers_processed: %s\n",
5604 alGetString(error));
5605 }
5606 #endif
5607 if(AL_STOPPED == state)
5608 {
5609 /* Resuming in not eof, but nothing to buffer */
5610
5611 /* Okay, here's another lately discovered problem:
5612 * I can't find it in the spec, but for at least some of the
5613 * implementations, if I call play on a stopped source that
5614 * has processed buffers, all those buffers get marked as unprocessed
5615 * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers
5616 * processed, on resume, the earlier 24 buffers will get replayed,
5617 * causing a "hiccup" like sound in the playback.
5618 * To avoid this, I must unqueue all processed buffers before
5619 * calling play. But to complicate things, I need to worry about resyncing
5620 * the circular queue with this since I designed this thing
5621 * with some correlation between the two. However, I might
5622 * have already handled this, so I will try writing this code without
5623 * syncing for now.
5624 * There is currently an assumption that a buffer
5625 * was queued above so I actually have something
5626 * to play.
5627 */
5628 ALint temp_count;
5629 #if 0
5630 fprintf(stderr, "STOPPED1, need to clear processed=%d, status is:\n", buffers_processed);
5631 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5632 #endif
5633 for(temp_count=0; temp_count<buffers_processed; temp_count++)
5634 {
5635 alSourceUnqueueBuffers(
5636 ALmixer_Channel_List[i].alsource,
5637 1, &unqueued_buffer_id
5638 );
5639 if((error = alGetError()) != AL_NO_ERROR)
5640 {
5641 fprintf(stderr, "55aTesting error: %s\n",
5642 alGetString(error));
5643 error_flag--;
5644 }
5645 }
5646 #if 0
5647 fprintf(stderr, "After unqueue clear...:\n");
5648 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5649 #endif
5650 /* My assertion: We are STOPPED but not EOF.
5651 * This means we have a buffer underrun.
5652 * We just cleared out the unqueued buffers.
5653 * So we need to reset the mixer_data to reflect we have
5654 * no buffers in queue.
5655 * We need to GetMoreData and then queue up the data.
5656 * Then we need to resume playing.
5657 */
5658 #if 0
5659 int buffers_queued;
5660 alGetSourcei(
5661 ALmixer_Channel_List[i].alsource,
5662 AL_BUFFERS_QUEUED,
5663 &buffers_queued
5664 );
5665
5666 if((error = alGetError()) != AL_NO_ERROR)
5667 {
5668 fprintf(stderr, "Error in PrintQueueStatus, Can't get buffers_queued: %s\n",
5669 alGetString(error));
5670 }
5671 assert(buffers_queued == 0);
5672 fprintf(stderr, "buffer underrun: buffers_queued:%d\n", buffers_queued);
5673 #endif
5674
5675 /* Reset the number of buffers in use to 0 */
5676 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0;
5677
5678 /* Get more data and put it in the first buffer */
5679 bytes_returned = GetMoreData(
5680 ALmixer_Channel_List[i].almixer_data,
5681 ALmixer_Channel_List[i].almixer_data->buffer[0]
5682 );
5683 /* NOTE: We might want to look for EOF and handle it here.
5684 * Currently, I just let the next loop handle it which seems to be working.
5685 */
5686 if(bytes_returned > 0)
5687 {
5688 /* Queue up the new data */
5689 alSourceQueueBuffers(
5690 ALmixer_Channel_List[i].alsource,
5691 1,
5692 &ALmixer_Channel_List[i].almixer_data->buffer[0]
5693 );
5694 if((error = alGetError()) != AL_NO_ERROR)
5695 {
5696 fprintf(stderr, "56e alSourceQueueBuffers error: %s\n",
5697 alGetString(error));
5698 }
5699 /* Increment the number of buffers in use */
5700 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++;
5701
5702
5703 /* We need to empty and update the circular buffer queue if it is in use */
5704 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5705 {
5706 ALuint queue_ret_flag;
5707 CircularQueueUnsignedInt_Clear(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5708 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5709 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5710 ALmixer_Channel_List[i].almixer_data->buffer[0]
5711 );
5712 if(0 == queue_ret_flag)
5713 {
5714 fprintf(stderr, "56fSerious internal error: CircularQueue could not push into queue.\n");
5715 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5716 }
5717 }
5718
5719
5720
5721
5722 /* Resume playback from underrun */
5723 alSourcePlay(ALmixer_Channel_List[i].alsource);
5724 if((error = alGetError()) != AL_NO_ERROR)
5725 {
5726 fprintf(stderr, "55Tbesting error: %s\n",
5727 alGetString(error));
5728 }
5729 }
5730
5731 }
5732 /* Let's escape to the next loop.
5733 * All code below this point is for queuing up
5734 */
5735 /*
5736 fprintf(stderr, "Entry: Nothing to do...continue\n\n");
5737 */
5738 continue;
5739 }
5740 /* We now know we have to fill an available
5741 * buffer.
5516 */ 5742 */
5517 #if 0 5743
5518 fprintf(stderr, "About to Unqueue, Buffers processed = %d\n", buffers_processed); 5744 /* In the previous branch, we just grabbed more data.
5519 fprintf(stderr, "Buffers queued= %d\n", buffers_still_queued); 5745 * Let's check it to make sure it's okay,
5520 fprintf(stderr, "Unqueuing a buffer\n"); 5746 * and then queue it up
5521 #endif 5747 */
5522 alSourceUnqueueBuffers( 5748 /* This check doesn't work anymore because it is now ALuint */
5523 ALmixer_Channel_List[i].alsource, 5749 #if 0
5524 1, &unqueued_buffer_id 5750 if(-1 == bytes_returned)
5525 );
5526 if((error = alGetError()) != AL_NO_ERROR)
5527 { 5751 {
5528 fprintf(stderr, "Error with unqueue: %s", 5752 /* Problem occurred...not sure what to do */
5529 alGetString(error)); 5753 /* Go to next loop? */
5530 ALmixer_SetError("Unqueue buffer failed: %s",
5531 alGetString(error) );
5532 error_flag--; 5754 error_flag--;
5755 /* Set the eof flag to force a quit so
5756 * we don't get stuck in an infinite loop
5757 */
5758 ALmixer_Channel_List[i].almixer_data->eof = 1;
5759 continue;
5533 } 5760 }
5534 /* 5761 #endif
5535 fprintf(stderr, "Right after unqueue..."); 5762 /* This is a special case where we've run
5536 PrintQueueStatus(ALmixer_Channel_List[i].alsource); 5763 * out of data. We should check for loops
5537 fprintf(stderr, "Getting more data for NOT_EOF, max_buffers filled\n"); 5764 * and get more data. If there is no loop,
5538 */ 5765 * then do nothing and wait for future
5539 /* Grab unqueued packet */ 5766 * update passes to handle the EOF.
5540 bytes_returned = GetMoreData( 5767 * The advantage of handling the loop here
5541 ALmixer_Channel_List[i].almixer_data, 5768 * instead of waiting for play to stop is
5542 unqueued_buffer_id); 5769 * that we should be able to keep the buffer
5543 } 5770 * filled.
5544 /* We are still streaming, but currently 5771 */
5545 * don't need to fill any buffers */ 5772 #if 0
5546 else 5773 else if(0 == bytes_returned)
5547 { 5774 #endif
5775 if(0 == bytes_returned)
5776 {
5777 /*
5778 fprintf(stderr, "We got 0 bytes from reading. Checking for loops\n");
5779 */
5780 /* Check for loops */
5781 if( ALmixer_Channel_List[i].loops != 0 )
5782 {
5783 /* We have to loop, so rewind
5784 * and fetch more data
5785 */
5786 /*
5787 fprintf(stderr, "Rewinding data\n");
5788 */
5789 if(0 == Sound_Rewind(
5790 ALmixer_Channel_List[i].almixer_data->sample))
5791 {
5792 fprintf(stderr, "Rewinding failed\n");
5793 ALmixer_SetError( Sound_GetError() );
5794 ALmixer_Channel_List[i].loops = 0;
5795 error_flag--;
5796 /* We'll continue on because we do have some valid data */
5797 continue;
5798 }
5799 /* Remember to reset the data->eof flag */
5800 ALmixer_Channel_List[i].almixer_data->eof = 0;
5801 if(ALmixer_Channel_List[i].loops > 0)
5802 {
5803 ALmixer_Channel_List[i].loops--;
5804 }
5805 /* Try grabbing another packet now.
5806 * Since we may have already unqueued a
5807 * buffer, we don't want to lose it.
5808 */
5809 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5810 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5811 {
5812 /*
5813 fprintf(stderr, "We got %d bytes from reading loop. Filling unused packet\n", bytes_returned);
5814 */
5815 /* Grab next packet */
5816 bytes_returned = GetMoreData(
5817 ALmixer_Channel_List[i].almixer_data,
5818 ALmixer_Channel_List[i].almixer_data->buffer[
5819 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5820 );
5821 /*
5822 fprintf(stderr, "We reread %d bytes into unused packet\n", bytes_returned);
5823 */
5824 }
5825 /* Refilling unqueued packet */
5826 else
5827 {
5828 /*
5829 fprintf(stderr, "We got %d bytes from reading loop. Filling unqueued packet\n", bytes_returned);
5830 */
5831 /* Grab next packet */
5832 bytes_returned = GetMoreData(
5833 ALmixer_Channel_List[i].almixer_data,
5834 unqueued_buffer_id);
5835 /*
5836 fprintf(stderr, "We reread %d bytes into unqueued packet\n", bytes_returned);
5837 */
5838 }
5839 /* Another error check */
5840 /*
5841 if(bytes_returned <= 0)
5842 */
5843 if(0 == bytes_returned)
5844 {
5845 fprintf(stderr, "??????????ERROR\n");
5846 ALmixer_SetError("Could not loop because after rewind, no data could be retrieved");
5847 /* Problem occurred...not sure what to do */
5848 /* Go to next loop? */
5849 error_flag--;
5850 /* Set the eof flag to force a quit so
5851 * we don't get stuck in an infinite loop
5852 */
5853 ALmixer_Channel_List[i].almixer_data->eof = 1;
5854 continue;
5855 }
5856 /* We made it to the end. We still need
5857 * to BufferData, so let this branch
5858 * fall into the next piece of
5859 * code below which will handle that
5860 */
5861
5862
5863 } /* END loop check */
5864 else
5865 {
5866 /* No more loops to do.
5867 * EOF flag should be set.
5868 * Just go to next loop and
5869 * let things be handled correctly
5870 * in future update calls
5871 */
5872 /*
5873 fprintf(stderr, "SHOULD BE EOF\n");
5874
5875 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5876 */
5877 continue;
5878 }
5879 } /* END if bytes_returned == 0 */
5880 /********* Possible trouble point. I might be queueing empty buffers on the mac.
5881 * This check doesn't say if the buffer is valid. Only the EOF assumption is a clue at this point
5882 */
5883 /* Fall here */
5884 /* Everything is normal. We aren't
5885 * at an EOF, but need to simply
5886 * queue more data. The data is already checked for good,
5887 * so queue it up */
5888 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5889 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5890 {
5891 /* Keep count of how many buffers we have
5892 * to queue so we can return the value
5893 */
5894 retval++;
5895 /*
5896 fprintf(stderr, "NOT_EOF???, about to Queue more data for num_buffers (%d) < max_queue (%d)\n",
5897 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use,
5898 ALmixer_Channel_List[i].almixer_data->max_queue_buffers);
5899 */
5900 alSourceQueueBuffers(
5901 ALmixer_Channel_List[i].alsource,
5902 1,
5903 &ALmixer_Channel_List[i].almixer_data->buffer[
5904 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5905 );
5906 if((error = alGetError()) != AL_NO_ERROR)
5907 {
5908 fprintf(stderr, "56Testing error: %s\n",
5909 alGetString(error));
5910 }
5911 /* This is part of the hideous Nvidia workaround. In order to figure out
5912 * which buffer to show during callbacks (for things like
5913 * o-scopes), I must keep a copy of the buffers that are queued in my own
5914 * data structure. This code will be called only if
5915 * "access_data" was set, indicated by whether the queue is NULL.
5916 */
5917 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5918 {
5919 ALuint queue_ret_flag;
5920 // fprintf(stderr, "56d: CircularQueue_PushBack.\n");
5921 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5922 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5923 ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5924 );
5925 if(0 == queue_ret_flag)
5926 {
5927 fprintf(stderr, "56aSerious internal error: CircularQueue could not push into queue.\n");
5928 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5929 }
5930 /*
5931 else
5932 {
5933 CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5934 }
5935 */
5936 }
5937 }
5938 /* for processed > 0 */
5939 else
5940 {
5941 /* Keep count of how many buffers we have
5942 * to queue so we can return the value
5943 */
5944 retval++;
5945 /*
5946 fprintf(stderr, "NOT_EOF, about to Queue more data for filled max_queue (%d)\n",
5947 ALmixer_Channel_List[i].almixer_data->max_queue_buffers);
5948 */
5949 alSourceQueueBuffers(
5950 ALmixer_Channel_List[i].alsource,
5951 1, &unqueued_buffer_id);
5952 if((error = alGetError()) != AL_NO_ERROR)
5953 {
5954 ALmixer_SetError("Could not QueueBuffer: %s",
5955 alGetString(error) );
5956 error_flag--;
5957 continue;
5958 }
5959 /* This is part of the hideous Nvidia workaround. In order to figure out
5960 * which buffer to show during callbacks (for things like
5961 * o-scopes), I must keep a copy of the buffers that are queued in my own
5962 * data structure. This code will be called only if
5963 * "access_data" was set, indicated by whether the queue is NULL.
5964 */
5965 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5966 {
5967 ALuint queue_ret_flag;
5968 // fprintf(stderr, "56e: CircularQueue_PushBack.\n");
5969 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5970 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5971 unqueued_buffer_id
5972 );
5973 if(0 == queue_ret_flag)
5974 {
5975 fprintf(stderr, "56bSerious internal error: CircularQueue could not push into queue.\n");
5976 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5977 }
5978 #if 0
5979 else
5980 {
5981 CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5982 }
5983 #endif
5984 }
5985 }
5986 /* If we used an available buffer queue,
5987 * then we need to update the number of them in use
5988 */
5989 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5990 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5991 {
5992 /* Increment the number of buffers in use */
5993 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++;
5994 }
5548 /* Might want to check state */ 5995 /* Might want to check state */
5549 /* In case the playback stopped, 5996 /* In case the playback stopped,
5550 * we need to resume 5997 * we need to resume */
5551 * a.k.a. buffer underrun
5552 */
5553 #if 1 5998 #if 1
5554 /* Try not refetching the state here because I'm getting a duplicate 5999 /* Try not refetching the state here because I'm getting a duplicate
5555 buffer playback (hiccup) */ 6000 buffer playback (hiccup) */
5556 alGetSourcei( 6001 alGetSourcei(
5557 ALmixer_Channel_List[i].alsource, 6002 ALmixer_Channel_List[i].alsource,
5558 AL_SOURCE_STATE, &state 6003 AL_SOURCE_STATE, &state
5559 ); 6004 );
5560 if((error = alGetError()) != AL_NO_ERROR) 6005 if((error = alGetError()) != AL_NO_ERROR)
5561 { 6006 {
5562 fprintf(stderr, "54bTesting error: %s\n", 6007 fprintf(stderr, "57bTesting error: %s\n",
5563 alGetString(error)); 6008 alGetString(error));
5564 } 6009 }
5565 /* Get the number of buffers processed 6010 /* Get the number of buffers processed
5566 */ 6011 */
5567 alGetSourcei( 6012 alGetSourcei(
5569 AL_BUFFERS_PROCESSED, 6014 AL_BUFFERS_PROCESSED,
5570 &buffers_processed 6015 &buffers_processed
5571 ); 6016 );
5572 if((error = alGetError()) != AL_NO_ERROR) 6017 if((error = alGetError()) != AL_NO_ERROR)
5573 { 6018 {
5574 fprintf(stderr, "54cError, Can't get buffers_processed: %s\n", 6019 fprintf(stderr, "57cError, Can't get buffers_processed: %s\n",
5575 alGetString(error)); 6020 alGetString(error));
5576 } 6021 }
5577 #endif 6022 #endif
5578 if(AL_STOPPED == state) 6023 if(AL_STOPPED == state)
5579 { 6024 {
5580 /* Resuming in not eof, but nothing to buffer */ 6025 /*
5581 6026 fprintf(stderr, "Resuming in not eof\n");
5582 /* Okay, here's another lately discovered problem: 6027 */
5583 * I can't find it in the spec, but for at least some of the 6028 /* Okay, here's another lately discovered problem:
5584 * implementations, if I call play on a stopped source that 6029 * I can't find it in the spec, but for at least some of the
5585 * has processed buffers, all those buffers get marked as unprocessed 6030 * implementations, if I call play on a stopped source that
5586 * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers 6031 * has processed buffers, all those buffers get marked as unprocessed
5587 * processed, on resume, the earlier 24 buffers will get replayed, 6032 * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers
5588 * causing a "hiccup" like sound in the playback. 6033 * processed, on resume, the earlier 24 buffers will get replayed,
5589 * To avoid this, I must unqueue all processed buffers before 6034 * causing a "hiccup" like sound in the playback.
5590 * calling play. But to complicate things, I need to worry about resyncing 6035 * To avoid this, I must unqueue all processed buffers before
5591 * the circular queue with this since I designed this thing 6036 * calling play. But to complicate things, I need to worry about resyncing
5592 * with some correlation between the two. However, I might 6037 * the circular queue with this since I designed this thing
5593 * have already handled this, so I will try writing this code without 6038 * with some correlation between the two. However, I might
5594 * syncing for now. 6039 * have already handled this, so I will try writing this code without
5595 * There is currently an assumption that a buffer 6040 * syncing for now.
5596 * was queued above so I actually have something 6041 * There is currently an assumption that a buffer
5597 * to play. 6042 * was queued above so I actually have something
6043 * to play.
6044 */
6045 ALint temp_count;
6046 /*
6047 fprintf(stderr, "STOPPED2, need to clear processed, status is:\n");
6048 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6049 */
6050
6051 for(temp_count=0; temp_count<buffers_processed; temp_count++)
6052 {
6053 alSourceUnqueueBuffers(
6054 ALmixer_Channel_List[i].alsource,
6055 1, &unqueued_buffer_id
6056 );
6057 if((error = alGetError()) != AL_NO_ERROR)
6058 {
6059 fprintf(stderr, "58aTesting error: %s\n",
6060 alGetString(error));
6061 error_flag--;
6062 }
6063 }
6064 /*
6065 fprintf(stderr, "After unqueue clear...:\n");
6066 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6067 */
6068
6069 alSourcePlay(ALmixer_Channel_List[i].alsource);
6070 if((error = alGetError()) != AL_NO_ERROR)
6071 {
6072 fprintf(stderr, "55Tbesting 8rror: %s\n",
6073 alGetString(error));
6074 }
6075 }
6076 continue;
6077 } /* END if( ! eof) */
6078 /* We have hit EOF in the SDL_Sound sample and there
6079 * are no more loops. However, there may still be
6080 * buffers in the OpenAL queue which still need to
6081 * be played out. The following body of code will
6082 * determine if play is still happening or
6083 * initiate the stop/cleanup sequenece.
6084 */
6085 else
6086 {
6087 /* Let's continue to remove the used up
6088 * buffers as they come in. */
6089 if(buffers_processed > 0)
6090 {
6091 ALint temp_count;
6092 /* Do as a for-loop because I don't want
6093 * to have to create an array for the
6094 * unqueued_buffer_id's
5598 */ 6095 */
5599 ALint temp_count;
5600 #if 0
5601 fprintf(stderr, "STOPPED1, need to clear processed=%d, status is:\n", buffers_processed);
5602 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5603 #endif
5604 for(temp_count=0; temp_count<buffers_processed; temp_count++) 6096 for(temp_count=0; temp_count<buffers_processed; temp_count++)
5605 { 6097 {
6098 /*
6099 fprintf(stderr, "unqueuing remainder, %d\n", temp_count);
6100 */
5606 alSourceUnqueueBuffers( 6101 alSourceUnqueueBuffers(
5607 ALmixer_Channel_List[i].alsource, 6102 ALmixer_Channel_List[i].alsource,
5608 1, &unqueued_buffer_id 6103 1, &unqueued_buffer_id
5609 ); 6104 );
5610 if((error = alGetError()) != AL_NO_ERROR) 6105 if((error = alGetError()) != AL_NO_ERROR)
5611 { 6106 {
5612 fprintf(stderr, "55aTesting error: %s\n", 6107 fprintf(stderr, "59Testing error: %s\n",
5613 alGetString(error)); 6108 alGetString(error));
5614 error_flag--;
5615 } 6109 }
5616 } 6110 }
5617 #if 0 6111 /*
5618 fprintf(stderr, "After unqueue clear...:\n"); 6112 fprintf(stderr, "done unqueuing remainder for this loop, %d\n", temp_count);
5619 PrintQueueStatus(ALmixer_Channel_List[i].alsource); 6113 */
5620 #endif 6114
5621 /* My assertion: We are STOPPED but not EOF. 6115 /* Need to update counts since we removed everything.
5622 * This means we have a buffer underrun. 6116 * If we don't update the counts here, we end up in the
5623 * We just cleared out the unqueued buffers. 6117 * "Shouldn't be here section, but maybe it's okay due to race conditions"
5624 * So we need to reset the mixer_data to reflect we have
5625 * no buffers in queue.
5626 * We need to GetMoreData and then queue up the data.
5627 * Then we need to resume playing.
5628 */ 6118 */
5629 #if 0
5630 int buffers_queued;
5631 alGetSourcei( 6119 alGetSourcei(
5632 ALmixer_Channel_List[i].alsource, 6120 ALmixer_Channel_List[i].alsource,
5633 AL_BUFFERS_QUEUED, 6121 AL_BUFFERS_QUEUED, &buffers_still_queued
5634 &buffers_queued
5635 ); 6122 );
5636
5637 if((error = alGetError()) != AL_NO_ERROR) 6123 if((error = alGetError()) != AL_NO_ERROR)
5638 { 6124 {
5639 fprintf(stderr, "Error in PrintQueueStatus, Can't get buffers_queued: %s\n", 6125 fprintf(stderr, "5100Testing error: %s\n",
5640 alGetString(error)); 6126 alGetString(error));
5641 }
5642 assert(buffers_queued == 0);
5643 fprintf(stderr, "buffer underrun: buffers_queued:%d\n", buffers_queued);
5644 #endif
5645
5646 /* Reset the number of buffers in use to 0 */
5647 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0;
5648
5649 /* Get more data and put it in the first buffer */
5650 bytes_returned = GetMoreData(
5651 ALmixer_Channel_List[i].almixer_data,
5652 ALmixer_Channel_List[i].almixer_data->buffer[0]
5653 );
5654 /* NOTE: We might want to look for EOF and handle it here.
5655 * Currently, I just let the next loop handle it which seems to be working.
5656 */
5657 if(bytes_returned > 0)
5658 {
5659 /* Queue up the new data */
5660 alSourceQueueBuffers(
5661 ALmixer_Channel_List[i].alsource,
5662 1,
5663 &ALmixer_Channel_List[i].almixer_data->buffer[0]
5664 );
5665 if((error = alGetError()) != AL_NO_ERROR)
5666 {
5667 fprintf(stderr, "56e alSourceQueueBuffers error: %s\n",
5668 alGetString(error));
5669 }
5670 /* Increment the number of buffers in use */
5671 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++;
5672
5673
5674 /* We need to empty and update the circular buffer queue if it is in use */
5675 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5676 {
5677 ALuint queue_ret_flag;
5678 CircularQueueUnsignedInt_Clear(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5679 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5680 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5681 ALmixer_Channel_List[i].almixer_data->buffer[0]
5682 );
5683 if(0 == queue_ret_flag)
5684 {
5685 fprintf(stderr, "56fSerious internal error: CircularQueue could not push into queue.\n");
5686 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5687 }
5688 }
5689
5690
5691
5692
5693 /* Resume playback from underrun */
5694 alSourcePlay(ALmixer_Channel_List[i].alsource);
5695 if((error = alGetError()) != AL_NO_ERROR)
5696 {
5697 fprintf(stderr, "55Tbesting error: %s\n",
5698 alGetString(error));
5699 }
5700 } 6127 }
5701 6128 /* Get the number of buffers processed
5702 } 6129 * so we know if we need to refill
5703 /* Let's escape to the next loop. 6130 */
5704 * All code below this point is for queuing up 6131 alGetSourcei(
5705 */ 6132 ALmixer_Channel_List[i].alsource,
5706 /* 6133 AL_BUFFERS_PROCESSED, &buffers_processed
5707 fprintf(stderr, "Entry: Nothing to do...continue\n\n"); 6134 );
5708 */
5709 continue;
5710 }
5711 /* We now know we have to fill an available
5712 * buffer.
5713 */
5714
5715 /* In the previous branch, we just grabbed more data.
5716 * Let's check it to make sure it's okay,
5717 * and then queue it up
5718 */
5719 /* This check doesn't work anymore because it is now ALuint */
5720 #if 0
5721 if(-1 == bytes_returned)
5722 {
5723 /* Problem occurred...not sure what to do */
5724 /* Go to next loop? */
5725 error_flag--;
5726 /* Set the eof flag to force a quit so
5727 * we don't get stuck in an infinite loop
5728 */
5729 ALmixer_Channel_List[i].almixer_data->eof = 1;
5730 continue;
5731 }
5732 #endif
5733 /* This is a special case where we've run
5734 * out of data. We should check for loops
5735 * and get more data. If there is no loop,
5736 * then do nothing and wait for future
5737 * update passes to handle the EOF.
5738 * The advantage of handling the loop here
5739 * instead of waiting for play to stop is
5740 * that we should be able to keep the buffer
5741 * filled.
5742 */
5743 #if 0
5744 else if(0 == bytes_returned)
5745 #endif
5746 if(0 == bytes_returned)
5747 {
5748 /*
5749 fprintf(stderr, "We got 0 bytes from reading. Checking for loops\n");
5750 */
5751 /* Check for loops */
5752 if( ALmixer_Channel_List[i].loops != 0 )
5753 {
5754 /* We have to loop, so rewind
5755 * and fetch more data
5756 */
5757 /*
5758 fprintf(stderr, "Rewinding data\n");
5759 */
5760 if(0 == Sound_Rewind(
5761 ALmixer_Channel_List[i].almixer_data->sample))
5762 {
5763 fprintf(stderr, "Rewinding failed\n");
5764 ALmixer_SetError( Sound_GetError() );
5765 ALmixer_Channel_List[i].loops = 0;
5766 error_flag--;
5767 /* We'll continue on because we do have some valid data */
5768 continue;
5769 }
5770 /* Remember to reset the data->eof flag */
5771 ALmixer_Channel_List[i].almixer_data->eof = 0;
5772 if(ALmixer_Channel_List[i].loops > 0)
5773 {
5774 ALmixer_Channel_List[i].loops--;
5775 }
5776 /* Try grabbing another packet now.
5777 * Since we may have already unqueued a
5778 * buffer, we don't want to lose it.
5779 */
5780 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5781 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5782 {
5783 /*
5784 fprintf(stderr, "We got %d bytes from reading loop. Filling unused packet\n", bytes_returned);
5785 */
5786 /* Grab next packet */
5787 bytes_returned = GetMoreData(
5788 ALmixer_Channel_List[i].almixer_data,
5789 ALmixer_Channel_List[i].almixer_data->buffer[
5790 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5791 );
5792 /*
5793 fprintf(stderr, "We reread %d bytes into unused packet\n", bytes_returned);
5794 */
5795 }
5796 /* Refilling unqueued packet */
5797 else
5798 {
5799 /*
5800 fprintf(stderr, "We got %d bytes from reading loop. Filling unqueued packet\n", bytes_returned);
5801 */
5802 /* Grab next packet */
5803 bytes_returned = GetMoreData(
5804 ALmixer_Channel_List[i].almixer_data,
5805 unqueued_buffer_id);
5806 /*
5807 fprintf(stderr, "We reread %d bytes into unqueued packet\n", bytes_returned);
5808 */
5809 }
5810 /* Another error check */
5811 /*
5812 if(bytes_returned <= 0)
5813 */
5814 if(0 == bytes_returned)
5815 {
5816 fprintf(stderr, "??????????ERROR\n");
5817 ALmixer_SetError("Could not loop because after rewind, no data could be retrieved");
5818 /* Problem occurred...not sure what to do */
5819 /* Go to next loop? */
5820 error_flag--;
5821 /* Set the eof flag to force a quit so
5822 * we don't get stuck in an infinite loop
5823 */
5824 ALmixer_Channel_List[i].almixer_data->eof = 1;
5825 continue;
5826 }
5827 /* We made it to the end. We still need
5828 * to BufferData, so let this branch
5829 * fall into the next piece of
5830 * code below which will handle that
5831 */
5832
5833
5834 } /* END loop check */
5835 else
5836 {
5837 /* No more loops to do.
5838 * EOF flag should be set.
5839 * Just go to next loop and
5840 * let things be handled correctly
5841 * in future update calls
5842 */
5843 /*
5844 fprintf(stderr, "SHOULD BE EOF\n");
5845
5846 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
5847 */
5848 continue;
5849 }
5850 } /* END if bytes_returned == 0 */
5851 /********* Possible trouble point. I might be queueing empty buffers on the mac.
5852 * This check doesn't say if the buffer is valid. Only the EOF assumption is a clue at this point
5853 */
5854 /* Fall here */
5855 /* Everything is normal. We aren't
5856 * at an EOF, but need to simply
5857 * queue more data. The data is already checked for good,
5858 * so queue it up */
5859 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5860 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5861 {
5862 /* Keep count of how many buffers we have
5863 * to queue so we can return the value
5864 */
5865 retval++;
5866 /*
5867 fprintf(stderr, "NOT_EOF???, about to Queue more data for num_buffers (%d) < max_queue (%d)\n",
5868 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use,
5869 ALmixer_Channel_List[i].almixer_data->max_queue_buffers);
5870 */
5871 alSourceQueueBuffers(
5872 ALmixer_Channel_List[i].alsource,
5873 1,
5874 &ALmixer_Channel_List[i].almixer_data->buffer[
5875 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5876 );
5877 if((error = alGetError()) != AL_NO_ERROR)
5878 {
5879 fprintf(stderr, "56Testing error: %s\n",
5880 alGetString(error));
5881 }
5882 /* This is part of the hideous Nvidia workaround. In order to figure out
5883 * which buffer to show during callbacks (for things like
5884 * o-scopes), I must keep a copy of the buffers that are queued in my own
5885 * data structure. This code will be called only if
5886 * "access_data" was set, indicated by whether the queue is NULL.
5887 */
5888 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5889 {
5890 ALuint queue_ret_flag;
5891 // fprintf(stderr, "56d: CircularQueue_PushBack.\n");
5892 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5893 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5894 ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use]
5895 );
5896 if(0 == queue_ret_flag)
5897 {
5898 fprintf(stderr, "56aSerious internal error: CircularQueue could not push into queue.\n");
5899 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5900 }
5901 /*
5902 else
5903 {
5904 CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5905 }
5906 */
5907 }
5908 }
5909 /* for processed > 0 */
5910 else
5911 {
5912 /* Keep count of how many buffers we have
5913 * to queue so we can return the value
5914 */
5915 retval++;
5916 /*
5917 fprintf(stderr, "NOT_EOF, about to Queue more data for filled max_queue (%d)\n",
5918 ALmixer_Channel_List[i].almixer_data->max_queue_buffers);
5919 */
5920 alSourceQueueBuffers(
5921 ALmixer_Channel_List[i].alsource,
5922 1, &unqueued_buffer_id);
5923 if((error = alGetError()) != AL_NO_ERROR)
5924 {
5925 ALmixer_SetError("Could not QueueBuffer: %s",
5926 alGetString(error) );
5927 error_flag--;
5928 continue;
5929 }
5930 /* This is part of the hideous Nvidia workaround. In order to figure out
5931 * which buffer to show during callbacks (for things like
5932 * o-scopes), I must keep a copy of the buffers that are queued in my own
5933 * data structure. This code will be called only if
5934 * "access_data" was set, indicated by whether the queue is NULL.
5935 */
5936 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL)
5937 {
5938 ALuint queue_ret_flag;
5939 // fprintf(stderr, "56e: CircularQueue_PushBack.\n");
5940 queue_ret_flag = CircularQueueUnsignedInt_PushBack(
5941 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue,
5942 unqueued_buffer_id
5943 );
5944 if(0 == queue_ret_flag)
5945 {
5946 fprintf(stderr, "56bSerious internal error: CircularQueue could not push into queue.\n");
5947 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue");
5948 }
5949 #if 0
5950 else
5951 {
5952 CircularQueueUnsignedInt_Print(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue);
5953 }
5954 #endif
5955 }
5956 }
5957 /* If we used an available buffer queue,
5958 * then we need to update the number of them in use
5959 */
5960 if(ALmixer_Channel_List[i].almixer_data->num_buffers_in_use
5961 < ALmixer_Channel_List[i].almixer_data->max_queue_buffers)
5962 {
5963 /* Increment the number of buffers in use */
5964 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use++;
5965 }
5966 /* Might want to check state */
5967 /* In case the playback stopped,
5968 * we need to resume */
5969 #if 1
5970 /* Try not refetching the state here because I'm getting a duplicate
5971 buffer playback (hiccup) */
5972 alGetSourcei(
5973 ALmixer_Channel_List[i].alsource,
5974 AL_SOURCE_STATE, &state
5975 );
5976 if((error = alGetError()) != AL_NO_ERROR)
5977 {
5978 fprintf(stderr, "57bTesting error: %s\n",
5979 alGetString(error));
5980 }
5981 /* Get the number of buffers processed
5982 */
5983 alGetSourcei(
5984 ALmixer_Channel_List[i].alsource,
5985 AL_BUFFERS_PROCESSED,
5986 &buffers_processed
5987 );
5988 if((error = alGetError()) != AL_NO_ERROR)
5989 {
5990 fprintf(stderr, "57cError, Can't get buffers_processed: %s\n",
5991 alGetString(error));
5992 }
5993 #endif
5994 if(AL_STOPPED == state)
5995 {
5996 /*
5997 fprintf(stderr, "Resuming in not eof\n");
5998 */
5999 /* Okay, here's another lately discovered problem:
6000 * I can't find it in the spec, but for at least some of the
6001 * implementations, if I call play on a stopped source that
6002 * has processed buffers, all those buffers get marked as unprocessed
6003 * on alSourcePlay. So if I had a queue of 25 with 24 of the buffers
6004 * processed, on resume, the earlier 24 buffers will get replayed,
6005 * causing a "hiccup" like sound in the playback.
6006 * To avoid this, I must unqueue all processed buffers before
6007 * calling play. But to complicate things, I need to worry about resyncing
6008 * the circular queue with this since I designed this thing
6009 * with some correlation between the two. However, I might
6010 * have already handled this, so I will try writing this code without
6011 * syncing for now.
6012 * There is currently an assumption that a buffer
6013 * was queued above so I actually have something
6014 * to play.
6015 */
6016 ALint temp_count;
6017 /*
6018 fprintf(stderr, "STOPPED2, need to clear processed, status is:\n");
6019 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6020 */
6021
6022 for(temp_count=0; temp_count<buffers_processed; temp_count++)
6023 {
6024 alSourceUnqueueBuffers(
6025 ALmixer_Channel_List[i].alsource,
6026 1, &unqueued_buffer_id
6027 );
6028 if((error = alGetError()) != AL_NO_ERROR)
6029 {
6030 fprintf(stderr, "58aTesting error: %s\n",
6031 alGetString(error));
6032 error_flag--;
6033 }
6034 }
6035 /*
6036 fprintf(stderr, "After unqueue clear...:\n");
6037 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6038 */
6039
6040 alSourcePlay(ALmixer_Channel_List[i].alsource);
6041 if((error = alGetError()) != AL_NO_ERROR) 6135 if((error = alGetError()) != AL_NO_ERROR)
6042 { 6136 {
6043 fprintf(stderr, "55Tbesting 8rror: %s\n", 6137 fprintf(stderr, "5200Testing error: %s\n",
6044 alGetString(error));
6045 }
6046 }
6047 continue;
6048 } /* END if( ! eof) */
6049 /* We have hit EOF in the SDL_Sound sample and there
6050 * are no more loops. However, there may still be
6051 * buffers in the OpenAL queue which still need to
6052 * be played out. The following body of code will
6053 * determine if play is still happening or
6054 * initiate the stop/cleanup sequenece.
6055 */
6056 else
6057 {
6058 /* Let's continue to remove the used up
6059 * buffers as they come in. */
6060 if(buffers_processed > 0)
6061 {
6062 ALint temp_count;
6063 /* Do as a for-loop because I don't want
6064 * to have to create an array for the
6065 * unqueued_buffer_id's
6066 */
6067 for(temp_count=0; temp_count<buffers_processed; temp_count++)
6068 {
6069 /*
6070 fprintf(stderr, "unqueuing remainder, %d\n", temp_count);
6071 */
6072 alSourceUnqueueBuffers(
6073 ALmixer_Channel_List[i].alsource,
6074 1, &unqueued_buffer_id
6075 );
6076 if((error = alGetError()) != AL_NO_ERROR)
6077 {
6078 fprintf(stderr, "59Testing error: %s\n",
6079 alGetString(error)); 6138 alGetString(error));
6080 } 6139 }
6081 } 6140 }
6082 /* 6141
6083 fprintf(stderr, "done unqueuing remainder for this loop, %d\n", temp_count); 6142
6084 */ 6143 /* Else if buffers_processed == 0
6085 6144 * and buffers_still_queued == 0.
6086 /* Need to update counts since we removed everything. 6145 * then we check to see if the source
6087 * If we don't update the counts here, we end up in the 6146 * is still playing. Quit if stopped
6088 * "Shouldn't be here section, but maybe it's okay due to race conditions" 6147 * We shouldn't need to worry about
6148 * looping because that should have
6149 * been handled above.
6089 */ 6150 */
6090 alGetSourcei( 6151 if(0 == buffers_still_queued)
6091 ALmixer_Channel_List[i].alsource,
6092 AL_BUFFERS_QUEUED, &buffers_still_queued
6093 );
6094 if((error = alGetError()) != AL_NO_ERROR)
6095 { 6152 {
6096 fprintf(stderr, "5100Testing error: %s\n", 6153 /* Make sure playback has stopped before
6097 alGetString(error)); 6154 * we shutdown.
6098 } 6155 */
6099 /* Get the number of buffers processed 6156 alGetSourcei(
6100 * so we know if we need to refill 6157 ALmixer_Channel_List[i].alsource,
6101 */ 6158 AL_SOURCE_STATE, &state
6102 alGetSourcei( 6159 );
6103 ALmixer_Channel_List[i].alsource, 6160 if((error = alGetError()) != AL_NO_ERROR)
6104 AL_BUFFERS_PROCESSED, &buffers_processed 6161 {
6105 ); 6162 fprintf(stderr, "60Testing error: %s\n",
6106 if((error = alGetError()) != AL_NO_ERROR) 6163 alGetString(error));
6164 }
6165 if(AL_STOPPED == state)
6166 {
6167 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0;
6168 /* Playback has ended.
6169 * Loop if necessary, or launch callback
6170 * and clear channel (or clear channel and
6171 * then launch callback?)
6172 * Update: Need to do callback first because I reference the mixer_data and source
6173 */
6174
6175 /* Launch callback */
6176 Invoke_Channel_Done_Callback(i, AL_TRUE);
6177
6178 Clean_Channel(i);
6179 /* Subtract counter */
6180 Is_Playing_global--;
6181
6182
6183 /* We're done for this loop.
6184 * Go to next channel
6185 */
6186
6187 /* 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.
6188 * 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.
6189 * We want to make sure we go immediately to the next iteration in the channel loop.
6190 */
6191 break;
6192 }
6193 } /* End end-playback */
6194 else
6107 { 6195 {
6108 fprintf(stderr, "5200Testing error: %s\n", 6196 /* Need to run out buffer */
6109 alGetString(error)); 6197 #if 1
6110 } 6198 /* Might want to check state */
6111 } 6199 /* In case the playback stopped,
6112 6200 * we need to resume */
6113 6201 alGetSourcei(
6114 /* Else if buffers_processed == 0 6202 ALmixer_Channel_List[i].alsource,
6115 * and buffers_still_queued == 0. 6203 AL_SOURCE_STATE, &state
6116 * then we check to see if the source 6204 );
6117 * is still playing. Quit if stopped 6205 if((error = alGetError()) != AL_NO_ERROR)
6118 * We shouldn't need to worry about 6206 {
6119 * looping because that should have 6207 fprintf(stderr, "61Testing error: %s\n",
6120 * been handled above. 6208 alGetString(error));
6121 */ 6209 }
6122 if(0 == buffers_still_queued) 6210 if(AL_STOPPED == state)
6123 { 6211 {
6124 /* Make sure playback has stopped before 6212 /*
6125 * we shutdown. 6213 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);
6126 */ 6214 */
6127 alGetSourcei( 6215 /*
6128 ALmixer_Channel_List[i].alsource, 6216 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6129 AL_SOURCE_STATE, &state 6217 */
6130 ); 6218 /* Rather than force unqueuing the buffer, let's see if
6131 if((error = alGetError()) != AL_NO_ERROR) 6219 * setting the buffer to none works (the OpenAL 1.0
6132 { 6220 * Reference Annotation suggests this should work).
6133 fprintf(stderr, "60Testing error: %s\n", 6221 */
6134 alGetString(error)); 6222 alSourcei(ALmixer_Channel_List[i].alsource,
6135 } 6223 AL_BUFFER, AL_NONE);
6136 if(AL_STOPPED == state) 6224 /*
6137 { 6225 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6138 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0; 6226 */
6139 /* Playback has ended. 6227 /* This doesn't work because in some cases, I think
6140 * Loop if necessary, or launch callback 6228 * it causes the sound to be replayed
6141 * and clear channel (or clear channel and 6229 */
6142 * then launch callback?) 6230 /*
6143 * Update: Need to do callback first because I reference the mixer_data and source 6231 fprintf(stderr, "Resuming in eof (trying to run out buffers\n");
6144 */ 6232 alSourcePlay(ALmixer_Channel_List[i].alsource);
6145 6233 */
6146 /* Launch callback */ 6234 }
6147 Invoke_Channel_Done_Callback(i, AL_TRUE); 6235 #endif
6148 6236 } /* End trap section */
6149 Clean_Channel(i); 6237 } /* End POST-EOF use-up buffer section */
6150 /* Subtract counter */ 6238
6151 Is_Playing_global--; 6239 } /* END NEW number of queue buffers per pass */
6152
6153
6154 /* We're done for this loop.
6155 * Go to next channel
6156 */
6157 continue;
6158 }
6159 } /* End end-playback */
6160 else
6161 {
6162 /* Need to run out buffer */
6163 #if 1
6164 /* Might want to check state */
6165 /* In case the playback stopped,
6166 * we need to resume */
6167 alGetSourcei(
6168 ALmixer_Channel_List[i].alsource,
6169 AL_SOURCE_STATE, &state
6170 );
6171 if((error = alGetError()) != AL_NO_ERROR)
6172 {
6173 fprintf(stderr, "61Testing error: %s\n",
6174 alGetString(error));
6175 }
6176 if(AL_STOPPED == state)
6177 {
6178 /*
6179 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);
6180 */
6181 /*
6182 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6183 */
6184 /* Rather than force unqueuing the buffer, let's see if
6185 * setting the buffer to none works (the OpenAL 1.0
6186 * Reference Annotation suggests this should work).
6187 */
6188 alSourcei(ALmixer_Channel_List[i].alsource,
6189 AL_BUFFER, AL_NONE);
6190 /*
6191 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6192 */
6193 /* This doesn't work because in some cases, I think
6194 * it causes the sound to be replayed
6195 */
6196 /*
6197 fprintf(stderr, "Resuming in eof (trying to run out buffers\n");
6198 alSourcePlay(ALmixer_Channel_List[i].alsource);
6199 */
6200 }
6201 #endif
6202 } /* End trap section */
6203 } /* End POST-EOF use-up buffer section */
6204
6205 } /* END NEW number of queue buffers per pass */
6206 6240
6207 } /* END Streamed section */ 6241 } /* END Streamed section */
6208 } /* END channel in use */ 6242 } /* END channel in use */
6209 } /* END for-loop for each channel */ 6243 } /* END for-loop for each channel */
6210 6244
7827 } 7861 }
7828 7862
7829 7863
7830 7864
7831 7865
7832 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) 7866 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)
7833 { 7867 {
7834 ALuint bytes_decoded; 7868 ALuint bytes_decoded;
7835 ALmixer_Data* ret_data; 7869 ALmixer_Data* ret_data;
7836 ALenum error; 7870 ALenum error;
7837 7871
7880 { 7914 {
7881 num_startup_buffers = max_queue_buffers; 7915 num_startup_buffers = max_queue_buffers;
7882 } 7916 }
7883 ret_data->num_startup_buffers = num_startup_buffers; 7917 ret_data->num_startup_buffers = num_startup_buffers;
7884 7918
7885 /* TODO: Expose value through public API */ 7919 /* Set up the update pass buffers */
7886 ret_data->num_target_buffers_per_pass = 1; 7920 if(0 == suggested_number_of_buffers_to_queue_per_update_pass)
7921 {
7922 suggested_number_of_buffers_to_queue_per_update_pass = ALMIXER_DEFAULT_BUFFERS_TO_QUEUE_PER_UPDATE_PASS;
7923 }
7924 /* Make sure update pass up buffers is less or equal to max_queue_buffers */
7925 if(suggested_number_of_buffers_to_queue_per_update_pass > max_queue_buffers)
7926 {
7927 suggested_number_of_buffers_to_queue_per_update_pass = max_queue_buffers;
7928 }
7929 ret_data->num_target_buffers_per_pass = suggested_number_of_buffers_to_queue_per_update_pass;
7887 7930
7888 ret_data->buffer_map_list = NULL; 7931 ret_data->buffer_map_list = NULL;
7889 ret_data->current_buffer = 0; 7932 ret_data->current_buffer = 0;
7890 7933
7891 ret_data->circular_buffer_queue = NULL; 7934 ret_data->circular_buffer_queue = NULL;
8375 * I don't like the AudioInfo parameter. I removed it once, 8418 * I don't like the AudioInfo parameter. I removed it once,
8376 * but the system will fail on RAW samples because the user 8419 * but the system will fail on RAW samples because the user
8377 * must specify it, so I had to bring it back. 8420 * must specify it, so I had to bring it back.
8378 * Remember I must close the rwops if there is an error before NewSample() 8421 * Remember I must close the rwops if there is an error before NewSample()
8379 */ 8422 */
8380 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) 8423 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)
8381 { 8424 {
8382 Sound_Sample* sample = NULL; 8425 Sound_Sample* sample = NULL;
8383 Sound_AudioInfo target; 8426 Sound_AudioInfo target;
8384 8427
8385 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) ) 8428 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) )
8416 { 8459 {
8417 ALmixer_SetError(Sound_GetError()); 8460 ALmixer_SetError(Sound_GetError());
8418 return NULL; 8461 return NULL;
8419 } 8462 }
8420 8463
8421 return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); 8464 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));
8422 } 8465 }
8423 8466
8424 8467
8425 8468
8426 /* This will load a sample for us from 8469 /* This will load a sample for us from
8427 * a file (instead of RWops). Most of the uglyness is 8470 * a file (instead of RWops). Most of the uglyness is
8428 * error checking and the fact that streamed/predecoded files 8471 * error checking and the fact that streamed/predecoded files
8429 * must be treated differently. 8472 * must be treated differently.
8430 */ 8473 */
8431 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) 8474 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)
8432 { 8475 {
8433 Sound_Sample* sample = NULL; 8476 Sound_Sample* sample = NULL;
8434 Sound_AudioInfo target; 8477 Sound_AudioInfo target;
8435 8478
8436 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) ) 8479 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) )
8526 } 8569 }
8527 8570
8528 /* 8571 /*
8529 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); 8572 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);
8530 */ 8573 */
8531 return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); 8574 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));
8532 } 8575 }
8533 8576
8534 8577
8535 /* This is a back door for RAW samples or if you need the 8578 /* This is a back door for RAW samples or if you need the
8536 * AudioInfo field. Use at your own risk. 8579 * AudioInfo field. Use at your own risk.
8537 */ 8580 */
8538 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) 8581 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)
8539 { 8582 {
8540 Sound_Sample* sample = NULL; 8583 Sound_Sample* sample = NULL;
8541 Sound_AudioInfo sound_desired; 8584 Sound_AudioInfo sound_desired;
8542 8585
8543 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) ) 8586 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) )
8566 if(NULL == sample) 8609 if(NULL == sample)
8567 { 8610 {
8568 ALmixer_SetError(Sound_GetError()); 8611 ALmixer_SetError(Sound_GetError());
8569 return NULL; 8612 return NULL;
8570 } 8613 }
8571 return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); 8614 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));
8572 } 8615 }
8573 8616
8574 8617
8575 8618
8576 8619
8577 /* This is a back door for RAW samples or if you need the 8620 /* This is a back door for RAW samples or if you need the
8578 * AudioInfo field. Use at your own risk. 8621 * AudioInfo field. Use at your own risk.
8579 */ 8622 */
8580 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) 8623 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)
8581 { 8624 {
8582 Sound_Sample* sample = NULL; 8625 Sound_Sample* sample = NULL;
8583 Sound_AudioInfo sound_desired; 8626 Sound_AudioInfo sound_desired;
8584 8627
8585 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) ) 8628 if( (AL_FALSE == ALmixer_Initialized) || (AL_TRUE == g_inInterruption) )
8609 if(NULL == sample) 8652 if(NULL == sample)
8610 { 8653 {
8611 ALmixer_SetError(Sound_GetError()); 8654 ALmixer_SetError(Sound_GetError());
8612 return NULL; 8655 return NULL;
8613 } 8656 }
8614 return( DoLoad(sample, buffersize, decode_mode_is_predecoded, max_queue_buffers, num_startup_buffers, access_data)); 8657 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));
8615 } 8658 }
8616 8659
8617 8660
8618 void ALmixer_FreeData(ALmixer_Data* data) 8661 void ALmixer_FreeData(ALmixer_Data* data)
8619 { 8662 {