Mercurial > almixer_isolated
comparison ALmixer.c @ 52:53ee4253c925
merged
author | Eric Wing <ewing@anscamobile.com> |
---|---|
date | Wed, 18 Jan 2012 12:20:54 -0800 |
parents | e2687188aea5 |
children | 8063b19bd40e |
comparison
equal
deleted
inserted
replaced
46:d7da72703681 | 52:53ee4253c925 |
---|---|
1690 /* If we get here, all sources are in use */ | 1690 /* If we get here, all sources are in use */ |
1691 return -1; | 1691 return -1; |
1692 } | 1692 } |
1693 | 1693 |
1694 | 1694 |
1695 static ALboolean Internal_DetachBuffersFromSource(ALuint source_id, ALboolean is_predecoded) | |
1696 { | |
1697 ALboolean retval = AL_TRUE; | |
1698 ALenum error; | |
1699 /* Here's the situation. My old method of using | |
1700 * alSourceUnqueueBuffers() seemed to be invalid in light | |
1701 * of all the problems I suffered through with getting | |
1702 * the CoreData backend to work with this code. | |
1703 * As such, I'm changing all the code to set the buffer to | |
1704 * AL_NONE. Furthermore, the queued vs. non-queued issue | |
1705 * doesn't need to apply here. For non-queued, Loki, | |
1706 * Creative Windows, and CoreAudio seem to leave the | |
1707 * buffer queued (Old Mac didn't.) For queued, we need to | |
1708 * remove the processed buffers and force remove the | |
1709 * still-queued buffers. | |
1710 * Update: This code is was changed agian due to a serious regression bug in iOS 5.0 | |
1711 * Since the 1.1 spec, I think I can make some simplifying assumptions sans the iOS 5.0 bug. | |
1712 * Before I looked at buffers_still_queued and buffers_processed. | |
1713 * According to the spec, all buffers get marked processed when alSourceStop is called. | |
1714 * Also, in addition, alSourcei(source, AL_BUFFER, AL_NONE) is supposed | |
1715 * to detach buffers for both streamed and non-streamed so all the code | |
1716 * should just go through that. | |
1717 * Unfortunately, the iOS 5.0 bug doesn't detach/clear buffers with AL_NONE. | |
1718 * So I need a special handler for iOS 5.0 which manually unqueues the buffers. | |
1719 * Ironically, I think the original Mac version did not work reliably | |
1720 * with unqueuing buffers which is why I moved the code to the AL_NONE | |
1721 * solution in the first place. This means for safety, I need to | |
1722 * conditionalize the workaround as not to risk breaking Mac or other * platforms. | |
1723 */ | |
1724 #ifdef __APPLE__ /* iOS 5.0 workaround */ | |
1725 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) | |
1726 /* | |
1727 fprintf(stderr, "kCFCoreFoundationVersionNumber: %lf\n", kCFCoreFoundationVersionNumber); | |
1728 */ | |
1729 /* I needed a C way to get the iOS version at runtime. This is returning 674.0 iOS 5.0 Beta 7. Apple hasn't updated the headers | |
1730 * for these constants since iOS 4.2, so I don't know if the value is also catching 4.3. | |
1731 * iOS 5.0.1 final is returning 675.0. | |
1732 * TODO: Once I learn which version Apple fixes the bug in, I need to update the range so this check is not run on fixed versions. | |
1733 * iOS 5.1 Beta 1 is returning 690.0. | |
1734 */ | |
1735 if(kCFCoreFoundationVersionNumber >= 674.0 && kCFCoreFoundationVersionNumber < 690.0) | |
1736 { | |
1737 /* For OpenAL experts, this is contrary to what you know, but must be done because the OpenAL implementation is broken. | |
1738 Instead of unqueuing buffers on only streaming sources, it appears that alSourcei(source, AL_BUFFER, AL_NONE) is not reliable at all. | |
1739 In cases where I switch between stream and non-stream on the same source and then stream again, the bug breaks playback on the third playback | |
1740 and only one buffer plays. | |
1741 The workaround seems to be to always unqueue buffers regardless of whether the source is streamed or not. | |
1742 And then avoid calling (source, AL_BUFFER, AL_NONE) | |
1743 From past experience, I know it is a bad idea to try to unqueue buffers from a non-streamed source (which is the contrary to OpenAL part), | |
1744 but this seems to work for this bug. | |
1745 */ | |
1746 ALint buffers_processed; | |
1747 /* Crap. Another iOS 5.0 problem. It seems our loop workaround below can get in cases where the buffer never clears. | |
1748 * It appears that in some cases (probably predecoded, but not always which makes it hard), that the buffer never can be unqueued. | |
1749 * I think it may possibly happen if you never use a source for streaming, and it is also possible it happens after loading or using a certain number of sources. | |
1750 * So to workaround, we need to abort after a certain amount of time to prevent an infinite loop check. | |
1751 * Some testing on iPad 2, 122ms is the highest number I've seen so far. So maybe 200ms is the cap? | |
1752 */ | |
1753 ALuint timeout_counter = ALmixer_GetTicks(); | |
1754 const ALuint MAX_NUMBER_OF_TICKS_TO_WAIT_IN_WORKAROUND = 200; | |
1755 | |
1756 /* Wow this is the bug that just keeps on sucking. There is even a race condition bug where the unqueue may not actually work. | |
1757 * So I have to keep doing it until it does. | |
1758 */ | |
1759 do | |
1760 { | |
1761 ALint temp_count; | |
1762 | |
1763 /* Wow this is the bug that just keeps on sucking. There is even a race condition bug where the unqueue may not actually work. | |
1764 * So I have to keep doing it until it does. | |
1765 * Sleeping for 20ms seems to help. 10ms was not long enough. (iPad 2). | |
1766 * Update: A user is hitting this in a tight loop and calling dozens/hundreds of times. The sleep some times seems to result in a stutter. | |
1767 * My theory is that there is thread contention and sleeps may be queuing up. The workaround seems to be reduce the sleep or eliminate it. | |
1768 * I prefer to reduce it because fundamentally I am waiting for the Core Audio/OpenAL thread to finish processing so yielding seems like a good idea. | |
1769 * The flipside is that I hit a lot of OpenAL errors because the commands keep failing and I'm burning CPU unnecessarily. | |
1770 * So far, the OpenAL errors seem harmless and there are no serious cascading failures I've encountered. | |
1771 */ | |
1772 ALmixer_Delay(0); | |
1773 | |
1774 alGetSourcei( | |
1775 source_id, | |
1776 AL_BUFFERS_PROCESSED, &buffers_processed | |
1777 ); | |
1778 if((error = alGetError()) != AL_NO_ERROR) | |
1779 { | |
1780 fprintf(stderr, "17aTesting Error with buffers_processed on Halt. (You may be seeing this because of a bad Apple OpenAL iOS 5.0 regression bug): %s", | |
1781 alGetString(error)); | |
1782 ALmixer_SetError("Failed detecting still processed buffers: %s", | |
1783 alGetString(error) ); | |
1784 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful. | |
1785 retval = AL_FALSE; | |
1786 */ | |
1787 } | |
1788 /* | |
1789 fprintf(stderr, "Going to unqueue %d buffers\n", buffers_processed); | |
1790 */ | |
1791 for(temp_count=0; temp_count<buffers_processed; temp_count++) | |
1792 { | |
1793 ALuint unqueued_buffer_id; | |
1794 | |
1795 alSourceUnqueueBuffers( | |
1796 source_id, | |
1797 1, &unqueued_buffer_id | |
1798 ); | |
1799 if((error = alGetError()) != AL_NO_ERROR) | |
1800 { | |
1801 /* Disabling this print because we hit it way too much with the usleep(0) */ | |
1802 /* fprintf(stderr, "17bTesting error with unqueuing buffers on Halt (You may be seeing this because of a bad Apple OpenAL iOS 5.0 regression bug): %s\n", alGetString(error)); | |
1803 */ | |
1804 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful. | |
1805 retval = AL_FALSE; | |
1806 */ | |
1807 } | |
1808 } | |
1809 | |
1810 alGetSourcei( | |
1811 source_id, | |
1812 AL_BUFFERS_PROCESSED, &buffers_processed | |
1813 ); | |
1814 if((error = alGetError()) != AL_NO_ERROR) | |
1815 { | |
1816 fprintf(stderr, "17cTesting Error with buffers_processed on Halt. (You may be seeing this because of a bad Apple OpenAL iOS 5.0 regression bug): %s", alGetString(error)); | |
1817 ALmixer_SetError("Failed detecting still processed buffers: %s", alGetString(error) ); | |
1818 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful. | |
1819 retval = AL_FALSE; | |
1820 */ | |
1821 } | |
1822 /* | |
1823 fprintf(stderr, "unqueued buffers should be 0. Actual value is %d\n", buffers_processed); | |
1824 */ | |
1825 /* Wow this is the bug that just keeps on sucking. There is an additional race condition bug where the unqueue may not actually work. | |
1826 * So I have to keep doing it until it does. | |
1827 * I hope this doesn't infinite loop. | |
1828 */ | |
1829 /* Disabling this print because we hit it way too much with the usleep(0) */ | |
1830 /* | |
1831 if(0 != buffers_processed) | |
1832 { | |
1833 fprintf(stderr, "Evil Apple OpenAL iOS 5.0 race condition. Buffers didn't actually unqueue. Repeating unqueue loop. %d, %d %d\n", ALmixer_GetTicks(), timeout_counter, ALmixer_GetTicks()-timeout_counter); | |
1834 } | |
1835 */ | |
1836 } while(0 != buffers_processed && ( (ALmixer_GetTicks()-timeout_counter) < MAX_NUMBER_OF_TICKS_TO_WAIT_IN_WORKAROUND) ); | |
1837 | |
1838 /* Avoid calling the normal cleanup because part of this bug seems to be triggered by alSourcei(source_id, AL_BUFFER, AL_NONE); */ | |
1839 return retval; | |
1840 } | |
1841 | |
1842 #endif | |
1843 #endif /* iOS 5.0 workaround */ | |
1844 | |
1845 /* According to the spec, this is the best way to clear a source. | |
1846 * This is supposed to work for both streamed and non-streamed sources. | |
1847 */ | |
1848 alSourcei(source_id, AL_BUFFER, AL_NONE); | |
1849 if((error = alGetError()) != AL_NO_ERROR) | |
1850 { | |
1851 fprintf(stderr, "17dTesting Error with clearing buffer from source: %s", | |
1852 alGetString(error)); | |
1853 ALmixer_SetError("Failed to clear buffer from source: %s", | |
1854 alGetString(error) ); | |
1855 retval = AL_FALSE; | |
1856 } | |
1857 | |
1858 return retval; | |
1859 } | |
1695 | 1860 |
1696 /* Will return the number of channels halted | 1861 /* Will return the number of channels halted |
1697 * or 0 for error | 1862 * or 0 for error |
1698 */ | 1863 */ |
1699 static ALint Internal_HaltChannel(ALint channel, ALboolean did_finish_naturally) | 1864 static ALint Internal_HaltChannel(ALint channel, ALboolean did_finish_naturally) |
1700 { | 1865 { |
1701 ALint retval = 0; | 1866 ALint retval = 0; |
1702 ALint counter = 0; | 1867 ALint counter = 0; |
1703 ALenum error; | 1868 ALenum error; |
1704 ALint buffers_still_queued; | 1869 ALboolean clear_succeeded; |
1705 ALint buffers_processed; | |
1706 | 1870 |
1707 if(channel >= Number_of_Channels_global) | 1871 if(channel >= Number_of_Channels_global) |
1708 { | 1872 { |
1709 ALmixer_SetError("Cannot halt channel %d because it exceeds maximum number of channels (%d)\n", channel, Number_of_Channels_global); | 1873 ALmixer_SetError("Cannot halt channel %d because it exceeds maximum number of channels (%d)\n", channel, Number_of_Channels_global); |
1710 return -1; | 1874 return -1; |
1719 if((error = alGetError()) != AL_NO_ERROR) | 1883 if((error = alGetError()) != AL_NO_ERROR) |
1720 { | 1884 { |
1721 fprintf(stderr, "14Testing error: %s\n", | 1885 fprintf(stderr, "14Testing error: %s\n", |
1722 alGetString(error)); | 1886 alGetString(error)); |
1723 } | 1887 } |
1724 /* Here's the situation. My old method of using | 1888 |
1725 * alSourceUnqueueBuffers() seemed to be invalid in light | 1889 clear_succeeded = Internal_DetachBuffersFromSource(ALmixer_Channel_List[channel].alsource, ALmixer_Channel_List[channel].almixer_data->decoded_all); |
1726 * of all the problems I suffered through with getting | 1890 if(AL_FALSE == clear_succeeded) |
1727 * the CoreData backend to work with this code. | |
1728 * As such, I'm changing all the code to set the buffer to | |
1729 * AL_NONE. Furthermore, the queued vs. non-queued issue | |
1730 * doesn't need to apply here. For non-queued, Loki, | |
1731 * Creative Windows, and CoreAudio seem to leave the | |
1732 * buffer queued (Old Mac didn't.) For queued, we need to | |
1733 * remove the processed buffers and force remove the | |
1734 * still-queued buffers. | |
1735 */ | |
1736 alGetSourcei( | |
1737 ALmixer_Channel_List[channel].alsource, | |
1738 AL_BUFFERS_QUEUED, &buffers_still_queued | |
1739 ); | |
1740 if((error = alGetError()) != AL_NO_ERROR) | |
1741 { | 1891 { |
1742 fprintf(stderr, "17Testing Error with buffers_still_queued: %s", | |
1743 alGetString(error)); | |
1744 ALmixer_SetError("Failed detecting still queued buffers: %s", | |
1745 alGetString(error) ); | |
1746 retval = -1; | 1892 retval = -1; |
1747 } | 1893 } |
1748 alGetSourcei( | 1894 |
1749 ALmixer_Channel_List[channel].alsource, | |
1750 AL_BUFFERS_PROCESSED, &buffers_processed | |
1751 ); | |
1752 if((error = alGetError()) != AL_NO_ERROR) | |
1753 { | |
1754 fprintf(stderr, "17Testing Error with buffers_processed: %s", | |
1755 alGetString(error)); | |
1756 ALmixer_SetError("Failed detecting still processed buffers: %s", | |
1757 alGetString(error) ); | |
1758 retval = -1; | |
1759 } | |
1760 /* If either of these is greater than 0, it means we need | |
1761 * to clear the source | |
1762 */ | |
1763 if((buffers_still_queued > 0) || (buffers_processed > 0)) | |
1764 { | |
1765 alSourcei(ALmixer_Channel_List[channel].alsource, | |
1766 AL_BUFFER, | |
1767 AL_NONE); | |
1768 if((error = alGetError()) != AL_NO_ERROR) | |
1769 { | |
1770 fprintf(stderr, "17Testing Error with clearing buffer from source: %s", | |
1771 alGetString(error)); | |
1772 ALmixer_SetError("Failed to clear buffer from source: %s", | |
1773 alGetString(error) ); | |
1774 retval = -1; | |
1775 } | |
1776 } | |
1777 | 1895 |
1778 ALmixer_Channel_List[channel].almixer_data->num_buffers_in_use = 0; | 1896 ALmixer_Channel_List[channel].almixer_data->num_buffers_in_use = 0; |
1779 | 1897 |
1780 /* Launch callback for consistency? */ | 1898 /* Launch callback for consistency? */ |
1781 Invoke_Channel_Done_Callback(channel, did_finish_naturally); | 1899 Invoke_Channel_Done_Callback(channel, did_finish_naturally); |
1799 { | 1917 { |
1800 fprintf(stderr, "19Testing error: %s\n", | 1918 fprintf(stderr, "19Testing error: %s\n", |
1801 alGetString(error)); | 1919 alGetString(error)); |
1802 } | 1920 } |
1803 | 1921 |
1804 /* Here's the situation. My old method of using | 1922 clear_succeeded = Internal_DetachBuffersFromSource(ALmixer_Channel_List[i].alsource, ALmixer_Channel_List[i].almixer_data->decoded_all); |
1805 * alSourceUnqueueBuffers() seemed to be invalid in light | 1923 if(AL_FALSE == clear_succeeded) |
1806 * of all the problems I suffered through with getting | |
1807 * the CoreData backend to work with this code. | |
1808 * As such, I'm changing all the code to set the buffer to | |
1809 * AL_NONE. Furthermore, the queued vs. non-queued issue | |
1810 * doesn't need to apply here. For non-queued, Loki, | |
1811 * Creative Windows, and CoreAudio seem to leave the | |
1812 * buffer queued (Old Mac didn't.) For queued, we need to | |
1813 * remove the processed buffers and force remove the | |
1814 * still-queued buffers. | |
1815 */ | |
1816 alGetSourcei( | |
1817 ALmixer_Channel_List[i].alsource, | |
1818 AL_BUFFERS_QUEUED, &buffers_still_queued | |
1819 ); | |
1820 if((error = alGetError()) != AL_NO_ERROR) | |
1821 { | 1924 { |
1822 fprintf(stderr, "17Testing Error with buffers_still_queued: %s", | |
1823 alGetString(error)); | |
1824 ALmixer_SetError("Failed detecting still queued buffers: %s", | |
1825 alGetString(error) ); | |
1826 retval = -1; | 1925 retval = -1; |
1827 } | 1926 } |
1828 alGetSourcei( | 1927 |
1829 ALmixer_Channel_List[i].alsource, | |
1830 AL_BUFFERS_PROCESSED, &buffers_processed | |
1831 ); | |
1832 if((error = alGetError()) != AL_NO_ERROR) | |
1833 { | |
1834 fprintf(stderr, "17Testing Error with buffers_processed: %s", | |
1835 alGetString(error)); | |
1836 ALmixer_SetError("Failed detecting still processed buffers: %s", | |
1837 alGetString(error) ); | |
1838 retval = -1; | |
1839 } | |
1840 /* If either of these is greater than 0, it means we need | |
1841 * to clear the source | |
1842 */ | |
1843 if((buffers_still_queued > 0) || (buffers_processed > 0)) | |
1844 { | |
1845 alSourcei(ALmixer_Channel_List[i].alsource, | |
1846 AL_BUFFER, | |
1847 AL_NONE); | |
1848 if((error = alGetError()) != AL_NO_ERROR) | |
1849 { | |
1850 fprintf(stderr, "17Testing Error with clearing buffer from source: %s", | |
1851 alGetString(error)); | |
1852 ALmixer_SetError("Failed to clear buffer from source: %s", | |
1853 alGetString(error) ); | |
1854 retval = -1; | |
1855 } | |
1856 } | |
1857 | |
1858 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0; | 1928 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0; |
1859 | 1929 |
1860 /* Launch callback for consistency? */ | 1930 /* Launch callback for consistency? */ |
1861 Invoke_Channel_Done_Callback(i, did_finish_naturally); | 1931 Invoke_Channel_Done_Callback(i, did_finish_naturally); |
1862 | 1932 |
2177 if(channel >= 0) | 2247 if(channel >= 0) |
2178 { | 2248 { |
2179 /* only need to process channel if in use */ | 2249 /* only need to process channel if in use */ |
2180 if(ALmixer_Channel_List[channel].channel_in_use) | 2250 if(ALmixer_Channel_List[channel].channel_in_use) |
2181 { | 2251 { |
2182 | 2252 running_count = 1; |
2183 /* What should I do? Do I just rewind the channel | 2253 /* What should I do? Do I just rewind the channel |
2184 * or also rewind the data? Since the data is | 2254 * or also rewind the data? Since the data is |
2185 * shared, let's make it the user's responsibility | 2255 * shared, let's make it the user's responsibility |
2186 * to rewind the data. | 2256 * to rewind the data. |
2187 */ | 2257 */ |
2241 * does no good. Rewinding the data will have an | 2311 * does no good. Rewinding the data will have an |
2242 * effect, but it will be lagged based on how | 2312 * effect, but it will be lagged based on how |
2243 * much data is queued. Recommend users call Halt | 2313 * much data is queued. Recommend users call Halt |
2244 * before rewind if they want immediate results. | 2314 * before rewind if they want immediate results. |
2245 */ | 2315 */ |
2246 retval = Internal_RewindData(ALmixer_Channel_List[channel].almixer_data); | 2316 if(AL_FALSE == Internal_RewindData(ALmixer_Channel_List[channel].almixer_data)) |
2317 { | |
2318 retval = -1; | |
2319 } | |
2247 } | 2320 } |
2248 } | 2321 } |
2249 } | 2322 } |
2250 /* The user wants to rewind all channels */ | 2323 /* The user wants to rewind all channels */ |
2251 else | 2324 else |
2254 for(i=0; i<Number_of_Channels_global; i++) | 2327 for(i=0; i<Number_of_Channels_global; i++) |
2255 { | 2328 { |
2256 /* only need to process channel if in use */ | 2329 /* only need to process channel if in use */ |
2257 if(ALmixer_Channel_List[i].channel_in_use) | 2330 if(ALmixer_Channel_List[i].channel_in_use) |
2258 { | 2331 { |
2332 running_count++; | |
2259 /* What should I do? Do I just rewind the channel | 2333 /* What should I do? Do I just rewind the channel |
2260 * or also rewind the data? Since the data is | 2334 * or also rewind the data? Since the data is |
2261 * shared, let's make it the user's responsibility | 2335 * shared, let's make it the user's responsibility |
2262 * to rewind the data. | 2336 * to rewind the data. |
2263 */ | 2337 */ |
2317 * does no good. Rewinding the data will have an | 2391 * does no good. Rewinding the data will have an |
2318 * effect, but it will be lagged based on how | 2392 * effect, but it will be lagged based on how |
2319 * much data is queued. Recommend users call Halt | 2393 * much data is queued. Recommend users call Halt |
2320 * before rewind if they want immediate results. | 2394 * before rewind if they want immediate results. |
2321 */ | 2395 */ |
2322 running_count += Internal_RewindData(ALmixer_Channel_List[i].almixer_data); | 2396 if(AL_FALSE == Internal_RewindData(ALmixer_Channel_List[i].almixer_data)) |
2397 { | |
2398 retval = -1; | |
2399 } | |
2323 } | 2400 } |
2324 } | 2401 } |
2325 } | 2402 } |
2326 } | 2403 } |
2327 if(-1 == retval) | 2404 if(-1 == retval) |
2687 { | 2764 { |
2688 ALuint k; | 2765 ALuint k; |
2689 ALuint queue_ret_flag; | 2766 ALuint queue_ret_flag; |
2690 for(k=0; k<data->num_buffers_in_use; k++) | 2767 for(k=0; k<data->num_buffers_in_use; k++) |
2691 { | 2768 { |
2692 // fprintf(stderr, "56c: CircularQueue_PushBack.\n"); | 2769 /* fprintf(stderr, "56c: CircularQueue_PushBack.\n"); */ |
2693 queue_ret_flag = CircularQueueUnsignedInt_PushBack(data->circular_buffer_queue, data->buffer[k]); | 2770 queue_ret_flag = CircularQueueUnsignedInt_PushBack(data->circular_buffer_queue, data->buffer[k]); |
2694 if(0 == queue_ret_flag) | 2771 if(0 == queue_ret_flag) |
2695 { | 2772 { |
2696 fprintf(stderr, "Serious internal error: CircularQueue could not push into queue.\n"); | 2773 fprintf(stderr, "Serious internal error: CircularQueue could not push into queue.\n"); |
2697 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); | 2774 ALmixer_SetError("Serious internal error: CircularQueue failed to push into queue"); |
3289 { | 3366 { |
3290 ALmixer_SetError("%s", | 3367 ALmixer_SetError("%s", |
3291 alGetString(error) ); | 3368 alGetString(error) ); |
3292 retval = -1; | 3369 retval = -1; |
3293 } | 3370 } |
3294 /* Need to resume playback if it was originally playing */ | 3371 /* OpenAL 1.1 spec says if this succeeds on a playing source, it will automatically jump */ |
3295 if(AL_PLAYING == state) | 3372 if(AL_PAUSED == state) |
3296 { | |
3297 alSourcePlay(ALmixer_Channel_List[channel].alsource); | |
3298 if((error = alGetError()) != AL_NO_ERROR) | |
3299 { | |
3300 ALmixer_SetError("%s", | |
3301 alGetString(error) ); | |
3302 retval = -1; | |
3303 } | |
3304 } | |
3305 else if(AL_PAUSED == state) | |
3306 { | 3373 { |
3307 /* HACK: The problem is that when paused, after | 3374 /* HACK: The problem is that when paused, after |
3308 * the Rewind, I can't get it off the INITIAL | 3375 * the Rewind, I can't get it off the INITIAL |
3309 * state without restarting | 3376 * state without restarting |
3310 */ | 3377 */ |
3329 * does no good. Rewinding the data will have an | 3396 * does no good. Rewinding the data will have an |
3330 * effect, but it will be lagged based on how | 3397 * effect, but it will be lagged based on how |
3331 * much data is queued. Recommend users call Halt | 3398 * much data is queued. Recommend users call Halt |
3332 * before rewind if they want immediate results. | 3399 * before rewind if they want immediate results. |
3333 */ | 3400 */ |
3334 retval = Internal_SeekData(ALmixer_Channel_List[channel].almixer_data, msec); | 3401 if(AL_FALSE == Internal_SeekData(ALmixer_Channel_List[channel].almixer_data, msec)) |
3402 { | |
3403 retval = -1; | |
3404 } | |
3335 } | 3405 } |
3406 running_count = 1; | |
3336 } | 3407 } |
3337 } | 3408 } |
3338 /* The user wants to rewind all channels */ | 3409 /* The user wants to rewind all channels */ |
3339 else | 3410 else |
3340 { | 3411 { |
3361 { | 3432 { |
3362 fprintf(stderr, "26Testing error: %s\n", | 3433 fprintf(stderr, "26Testing error: %s\n", |
3363 alGetString(error)); | 3434 alGetString(error)); |
3364 } | 3435 } |
3365 | 3436 |
3437 /* OpenAL 1.1 spec says if this succeeds on a playing source, it will automatically jump */ | |
3366 alSourcef(ALmixer_Channel_List[channel].alsource, AL_SEC_OFFSET, sec_offset); | 3438 alSourcef(ALmixer_Channel_List[channel].alsource, AL_SEC_OFFSET, sec_offset); |
3367 if((error = alGetError()) != AL_NO_ERROR) | 3439 if((error = alGetError()) != AL_NO_ERROR) |
3368 { | 3440 { |
3369 ALmixer_SetError("%s", | 3441 ALmixer_SetError("%s", |
3370 alGetString(error) ); | 3442 alGetString(error) ); |
3371 retval = -1; | 3443 retval = -1; |
3372 } | 3444 } |
3373 /* Need to resume playback if it was originally playing */ | 3445 if(AL_PAUSED == state) |
3374 if(AL_PLAYING == state) | |
3375 { | |
3376 alSourcePlay(ALmixer_Channel_List[i].alsource); | |
3377 if((error = alGetError()) != AL_NO_ERROR) | |
3378 { | |
3379 ALmixer_SetError("%s", | |
3380 alGetString(error) ); | |
3381 retval = -1; | |
3382 } | |
3383 } | |
3384 else if(AL_PAUSED == state) | |
3385 { | 3446 { |
3386 /* HACK: The problem is that when paused, after | 3447 /* HACK: The problem is that when paused, after |
3387 * the Rewind, I can't get it off the INITIAL | 3448 * the Rewind, I can't get it off the INITIAL |
3388 * state without restarting | 3449 * state without restarting |
3389 */ | 3450 */ |
3408 * does no good. Rewinding the data will have an | 3469 * does no good. Rewinding the data will have an |
3409 * effect, but it will be lagged based on how | 3470 * effect, but it will be lagged based on how |
3410 * much data is queued. Recommend users call Halt | 3471 * much data is queued. Recommend users call Halt |
3411 * before rewind if they want immediate results. | 3472 * before rewind if they want immediate results. |
3412 */ | 3473 */ |
3413 running_count += Internal_SeekData(ALmixer_Channel_List[i].almixer_data, msec); | 3474 if(AL_FALSE == Internal_SeekData(ALmixer_Channel_List[i].almixer_data, msec)) |
3475 { | |
3476 retval = -1; | |
3477 } | |
3414 } | 3478 } |
3479 running_count++; | |
3415 } | 3480 } |
3416 } | 3481 } |
3417 } | 3482 } |
3418 if(-1 == retval) | 3483 if(-1 == retval) |
3419 { | 3484 { |
5171 alGetString(error) ); | 5236 alGetString(error) ); |
5172 error_flag--; | 5237 error_flag--; |
5173 } | 5238 } |
5174 if(buffers_still_queued > 0) | 5239 if(buffers_still_queued > 0) |
5175 { | 5240 { |
5176 | 5241 ALboolean clear_succeeded = Internal_DetachBuffersFromSource(ALmixer_Channel_List[i].alsource, ALmixer_Channel_List[i].almixer_data->decoded_all); |
5177 #if 0 /* This triggers an error in OS X Core Audio. */ | 5242 if(AL_FALSE == clear_succeeded) |
5178 alSourceUnqueueBuffers( | |
5179 ALmixer_Channel_List[i].alsource, | |
5180 1, | |
5181 ALmixer_Channel_List[i].almixer_data->buffer | |
5182 ); | |
5183 #else | |
5184 /* fprintf(stderr, "In the Bob Aron section...about to clear source\n"); | |
5185 PrintQueueStatus(ALmixer_Channel_List[i].alsource); | |
5186 */ | |
5187 /* Rather than force unqueuing the buffer, let's see if | |
5188 * setting the buffer to none works (the OpenAL 1.0 | |
5189 * Reference Annotation suggests this should work). | |
5190 */ | |
5191 alSourcei(ALmixer_Channel_List[i].alsource, | |
5192 AL_BUFFER, AL_NONE); | |
5193 /* | |
5194 PrintQueueStatus(ALmixer_Channel_List[i].alsource); | |
5195 */ | |
5196 #endif | |
5197 if((error = alGetError()) != AL_NO_ERROR) | |
5198 { | 5243 { |
5199 fprintf(stderr, "Error with unqueue, after alSourceUnqueueBuffers, buffers_still_queued=%d, error is: %s", buffers_still_queued, | |
5200 alGetString(error)); | |
5201 ALmixer_SetError("Predecoded Unqueue buffer failed: %s", | |
5202 alGetString(error) ); | |
5203 error_flag--; | 5244 error_flag--; |
5204 } | 5245 } |
5205 | |
5206 } | 5246 } |
5207 | 5247 |
5208 /* Launch callback */ | 5248 /* Launch callback */ |
5209 Invoke_Channel_Done_Callback(i, AL_TRUE); | 5249 Invoke_Channel_Done_Callback(i, AL_TRUE); |
5210 | 5250 |
5274 * so we know if we need to refill | 5314 * so we know if we need to refill |
5275 */ | 5315 */ |
5276 /* WARNING: It looks like Snow Leopard some times crashes on this call under x86_64 | 5316 /* WARNING: It looks like Snow Leopard some times crashes on this call under x86_64 |
5277 * typically when I suffer a lot of buffer underruns. | 5317 * typically when I suffer a lot of buffer underruns. |
5278 */ | 5318 */ |
5279 // fprintf(stderr, "calling AL_BUFFERS_PROCESSED on source:%d", ALmixer_Channel_List[i].alsource); | 5319 /* fprintf(stderr, "calling AL_BUFFERS_PROCESSED on source:%d", ALmixer_Channel_List[i].alsource); */ |
5280 alGetSourcei( | 5320 alGetSourcei( |
5281 ALmixer_Channel_List[i].alsource, | 5321 ALmixer_Channel_List[i].alsource, |
5282 AL_BUFFERS_PROCESSED, &buffers_processed | 5322 AL_BUFFERS_PROCESSED, &buffers_processed |
5283 ); | 5323 ); |
5284 if((error = alGetError()) != AL_NO_ERROR) | 5324 if((error = alGetError()) != AL_NO_ERROR) |
5285 { | 5325 { |
5286 fprintf(stderr, "52Testing error: %s\n", | 5326 fprintf(stderr, "52Testing error: %s\n", |
5287 alGetString(error)); | 5327 alGetString(error)); |
5288 } | 5328 } |
5289 // fprintf(stderr, "finished AL_BUFFERS_PROCESSED, buffers_processed=%d", buffers_processed); | 5329 /* fprintf(stderr, "finished AL_BUFFERS_PROCESSED, buffers_processed=%d", buffers_processed); */ |
5290 | 5330 |
5291 /* WTF!!! The Nvidia distribution is failing on the alGetSourcei(source, AL_BUFFER, buf_id) call. | 5331 /* WTF!!! The Nvidia distribution is failing on the alGetSourcei(source, AL_BUFFER, buf_id) call. |
5292 * I need this call to figure out which buffer OpenAL is currently playing. | 5332 * I need this call to figure out which buffer OpenAL is currently playing. |
5293 * It keeps returning an "Invalid Enum" error. | 5333 * It keeps returning an "Invalid Enum" error. |
5294 * This is totally inane! It's a basic query. | 5334 * This is totally inane! It's a basic query. |
5481 * If in an underrun, queue up at least startup_buffers. | 5521 * If in an underrun, queue up at least startup_buffers. |
5482 */ | 5522 */ |
5483 if(AL_STOPPED == state) | 5523 if(AL_STOPPED == state) |
5484 { | 5524 { |
5485 number_of_buffers_to_queue_this_pass = ALmixer_Channel_List[i].almixer_data->num_startup_buffers; | 5525 number_of_buffers_to_queue_this_pass = ALmixer_Channel_List[i].almixer_data->num_startup_buffers; |
5486 // fprintf(stderr, "assuming underrun condition, using num_startup_buffers=%d\n", number_of_buffers_to_queue_this_pass); | 5526 /* fprintf(stderr, "assuming underrun condition, using num_startup_buffers=%d\n", number_of_buffers_to_queue_this_pass); */ |
5487 } | 5527 } |
5488 | 5528 |
5489 /* 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. */ | 5529 /* 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. */ |
5490 /* Logic Hack/Bug: In adding the number_of_buffers_to_queue_this_pass, I discovered the for-loop needs to be more decoupled. | 5530 /* Logic Hack/Bug: In adding the number_of_buffers_to_queue_this_pass, I discovered the for-loop needs to be more decoupled. |
5491 * The loop still needs to be entered because unqueuing and completion callbacks and possibly other state processing are also done. | 5531 * The loop still needs to be entered because unqueuing and completion callbacks and possibly other state processing are also done. |
5495 { | 5535 { |
5496 number_of_buffers_to_queue_this_pass = 1; | 5536 number_of_buffers_to_queue_this_pass = 1; |
5497 } | 5537 } |
5498 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++) | 5538 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++) |
5499 { | 5539 { |
5500 // fprintf(stderr, "current_count_of_buffer_queue_passes:%d\n", current_count_of_buffer_queue_passes); | 5540 /* fprintf(stderr, "current_count_of_buffer_queue_passes:%d\n", current_count_of_buffer_queue_passes); */ |
5541 | |
5542 /* Because I introduced this for-loop, I think I need to regrab the number of processed buffers because | |
5543 * the number may now be stale from previous iterations. I suppose I could do it at the end of the loop, | |
5544 * but the logic flow is already too complicated to ensure that the block is being hit. | |
5545 */ | |
5546 /* Get the number of buffers processed | |
5547 */ | |
5548 alGetSourcei( | |
5549 ALmixer_Channel_List[i].alsource, | |
5550 AL_BUFFERS_PROCESSED, | |
5551 &buffers_processed | |
5552 ); | |
5553 if((error = alGetError()) != AL_NO_ERROR) | |
5554 { | |
5555 fprintf(stderr, "59aTestingError, Can't get buffers_processed: %s\n", | |
5556 alGetString(error)); | |
5557 } | |
5558 | |
5501 | 5559 |
5502 /* For this to work, we must rely on EVERYTHING | 5560 /* For this to work, we must rely on EVERYTHING |
5503 * else to unset the EOF if there is looping. | 5561 * else to unset the EOF if there is looping. |
5504 * Remember, even Play() must do this | 5562 * Remember, even Play() must do this |
5505 */ | 5563 */ |
5916 * "access_data" was set, indicated by whether the queue is NULL. | 5974 * "access_data" was set, indicated by whether the queue is NULL. |
5917 */ | 5975 */ |
5918 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) | 5976 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) |
5919 { | 5977 { |
5920 ALuint queue_ret_flag; | 5978 ALuint queue_ret_flag; |
5921 // fprintf(stderr, "56d: CircularQueue_PushBack.\n"); | 5979 /* fprintf(stderr, "56d: CircularQueue_PushBack.\n"); */ |
5922 queue_ret_flag = CircularQueueUnsignedInt_PushBack( | 5980 queue_ret_flag = CircularQueueUnsignedInt_PushBack( |
5923 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, | 5981 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, |
5924 ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] | 5982 ALmixer_Channel_List[i].almixer_data->buffer[ALmixer_Channel_List[i].almixer_data->num_buffers_in_use] |
5925 ); | 5983 ); |
5926 if(0 == queue_ret_flag) | 5984 if(0 == queue_ret_flag) |
5964 * "access_data" was set, indicated by whether the queue is NULL. | 6022 * "access_data" was set, indicated by whether the queue is NULL. |
5965 */ | 6023 */ |
5966 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) | 6024 if(ALmixer_Channel_List[i].almixer_data->circular_buffer_queue != NULL) |
5967 { | 6025 { |
5968 ALuint queue_ret_flag; | 6026 ALuint queue_ret_flag; |
5969 // fprintf(stderr, "56e: CircularQueue_PushBack.\n"); | 6027 /* fprintf(stderr, "56e: CircularQueue_PushBack.\n"); */ |
5970 queue_ret_flag = CircularQueueUnsignedInt_PushBack( | 6028 queue_ret_flag = CircularQueueUnsignedInt_PushBack( |
5971 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, | 6029 ALmixer_Channel_List[i].almixer_data->circular_buffer_queue, |
5972 unqueued_buffer_id | 6030 unqueued_buffer_id |
5973 ); | 6031 ); |
5974 if(0 == queue_ret_flag) | 6032 if(0 == queue_ret_flag) |
6218 */ | 6276 */ |
6219 /* Rather than force unqueuing the buffer, let's see if | 6277 /* Rather than force unqueuing the buffer, let's see if |
6220 * setting the buffer to none works (the OpenAL 1.0 | 6278 * setting the buffer to none works (the OpenAL 1.0 |
6221 * Reference Annotation suggests this should work). | 6279 * Reference Annotation suggests this should work). |
6222 */ | 6280 */ |
6223 alSourcei(ALmixer_Channel_List[i].alsource, | 6281 ALboolean clear_succeeded = Internal_DetachBuffersFromSource(ALmixer_Channel_List[i].alsource, ALmixer_Channel_List[i].almixer_data->decoded_all); |
6224 AL_BUFFER, AL_NONE); | 6282 if(AL_FALSE == clear_succeeded) |
6283 { | |
6284 error_flag--; | |
6285 } | |
6225 /* | 6286 /* |
6226 PrintQueueStatus(ALmixer_Channel_List[i].alsource); | 6287 PrintQueueStatus(ALmixer_Channel_List[i].alsource); |
6227 */ | 6288 */ |
6228 /* This doesn't work because in some cases, I think | 6289 /* This doesn't work because in some cases, I think |
6229 * it causes the sound to be replayed | 6290 * it causes the sound to be replayed |
7735 fprintf(stderr, "Error: You should not call ALmixer_SetError while ALmixer is not initialized\n"); | 7796 fprintf(stderr, "Error: You should not call ALmixer_SetError while ALmixer is not initialized\n"); |
7736 return; | 7797 return; |
7737 } | 7798 } |
7738 va_list argp; | 7799 va_list argp; |
7739 va_start(argp, err_str); | 7800 va_start(argp, err_str); |
7740 // SDL_SetError which I'm emulating has no number parameter. | 7801 /* SDL_SetError which I'm emulating has no number parameter. */ |
7741 TError_SetErrorv(s_ALmixerErrorPool, 1, err_str, argp); | 7802 TError_SetErrorv(s_ALmixerErrorPool, 1, err_str, argp); |
7742 va_end(argp); | 7803 va_end(argp); |
7743 } | 7804 } |
7744 | 7805 |
7745 #endif | 7806 #endif |
8266 while(j>0) | 8327 while(j>0) |
8267 { | 8328 { |
8268 free(ret_data->buffer_map_list[j].data); | 8329 free(ret_data->buffer_map_list[j].data); |
8269 j--; | 8330 j--; |
8270 } | 8331 } |
8271 // Delete for j=0 because the while loop misses the last one | 8332 /* Delete for j=0 because the while loop misses the last one */ |
8272 free(ret_data->buffer_map_list[j].data); | 8333 free(ret_data->buffer_map_list[j].data); |
8273 | 8334 |
8274 free(ret_data->buffer_map_list); | 8335 free(ret_data->buffer_map_list); |
8275 CircularQueueUnsignedInt_FreeQueue(ret_data->circular_buffer_queue); | 8336 CircularQueueUnsignedInt_FreeQueue(ret_data->circular_buffer_queue); |
8276 Sound_FreeSample(sample); | 8337 Sound_FreeSample(sample); |