comparison ALmixer.c @ 48:00b770b0d2aa

Workaround: There is a terrible OpenAL regression bug in iOS 5 dealing with streaming sources. alSourcei(source_id, AL_BUFFER, AL_NONE); fails to clear queued buffer queues on a streaming source. The workaround involves manually dequeuing the individual buffers before calling alSourcei(source_id, AL_BUFFER, AL_NONE);. But there is an additional race condition bug where the unqueue fails to take, so the included workaround keeps looping until the buffers finally report as cleared. The current check is compiled only for iOS and does a runtime version check against CoreFoundation version 674.0. When Apple finally ships a fix, this fix should be amended to not run on fixed versions. Also ironically, it was originally the Apple Mac OpenAL implementation that had a problem with buffer unqueuing which ultimately led to the use of alSourcei(source_id, AL_BUFFER, AL_NONE); This is another reason the workaround is only constrained to iOS. In some cases I've seen, the buffer unqueue also triggers OpenAL errors, however, the current workaround seems to usually avoid those OpenAL errors (even though the buffers fail to unqueue some times). The 20ms sleep seems to avoid the race condition entirely, but when it doesn't, the sleep seems to do something that magically avoids tripping OpenAL errors.
author Eric Wing <ewing@anscamobile.com>
date Fri, 30 Sep 2011 16:44:08 -0700
parents 9b772c81b550
children 4b0268d86298
comparison
equal deleted inserted replaced
47:9b772c81b550 48:00b770b0d2aa
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 ALint buffers_processed;
1699 ALenum error;
1700 /* Here's the situation. My old method of using
1701 * alSourceUnqueueBuffers() seemed to be invalid in light
1702 * of all the problems I suffered through with getting
1703 * the CoreData backend to work with this code.
1704 * As such, I'm changing all the code to set the buffer to
1705 * AL_NONE. Furthermore, the queued vs. non-queued issue
1706 * doesn't need to apply here. For non-queued, Loki,
1707 * Creative Windows, and CoreAudio seem to leave the
1708 * buffer queued (Old Mac didn't.) For queued, we need to
1709 * remove the processed buffers and force remove the
1710 * still-queued buffers.
1711 * Update: This code is was changed agian due to a serious regression bug in iOS 5.0
1712 * Since the 1.1 spec, I think I can make some simplifying assumptions sans the iOS 5.0 bug.
1713 * Before I looked at buffers_still_queued and buffers_processed.
1714 * According to the spec, all buffers get marked processed when alSourceStop is called.
1715 * Also, in addition, alSourcei(source, AL_BUFFER, AL_NONE) is supposed
1716 * to detach buffers for both streamed and non-streamed so all the code
1717 * should just go through that.
1718 * Unfortunately, the iOS 5.0 bug doesn't detach/clear buffers with AL_NONE.
1719 * So I need a special handler for iOS 5.0 which manually unqueues the buffers.
1720 * Ironically, I think the original Mac version did not work reliably
1721 * with unqueuing buffers which is why I moved the code to the AL_NONE
1722 * solution in the first place. This means for safety, I need to
1723 * conditionalize the workaround as not to risk breaking Mac or other * platforms.
1724 */
1725 #ifdef __APPLE__ /* iOS 5.0 workaround */
1726 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
1727 /*
1728 fprintf(stderr, "kCFCoreFoundationVersionNumber: %lf\n", kCFCoreFoundationVersionNumber);
1729 */
1730 /* 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
1731 * for these constants since iOS 4.2, so I don't know if the value is also catching 4.3.
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 */
1734 if(kCFCoreFoundationVersionNumber >= 674.0)
1735 {
1736 if(AL_FALSE == is_predecoded)
1737 {
1738 ALint buffers_processed;
1739 /* Wow this is the bug that just keeps on sucking. There is even a race condition bug where the unqueue may not actually work.
1740 * So I have to keep doing it until it does.
1741 */
1742 do
1743 {
1744 ALint temp_count;
1745
1746 /* Wow this is the bug that just keeps on sucking. There is even a race condition bug where the unqueue may not actually work.
1747 * So I have to keep doing it until it does.
1748 * Sleeping for 20ms seems to help. 10ms was not long enough. (iPad 2)
1749 */
1750 ALmixer_Delay(20);
1751
1752 alGetSourcei(
1753 source_id,
1754 AL_BUFFERS_PROCESSED, &buffers_processed
1755 );
1756 if((error = alGetError()) != AL_NO_ERROR)
1757 {
1758 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",
1759 alGetString(error));
1760 ALmixer_SetError("Failed detecting still processed buffers: %s",
1761 alGetString(error) );
1762 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful.
1763 retval = AL_FALSE;
1764 */
1765 }
1766 /*
1767 fprintf(stderr, "Going to unqueue %d buffers\n", buffers_processed);
1768 */
1769 for(temp_count=0; temp_count<buffers_processed; temp_count++)
1770 {
1771 ALuint unqueued_buffer_id;
1772
1773 alSourceUnqueueBuffers(
1774 source_id,
1775 1, &unqueued_buffer_id
1776 );
1777 if((error = alGetError()) != AL_NO_ERROR)
1778 {
1779 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",
1780 alGetString(error));
1781 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful.
1782 retval = AL_FALSE;
1783 */
1784 }
1785 }
1786
1787 alGetSourcei(
1788 source_id,
1789 AL_BUFFERS_PROCESSED, &buffers_processed
1790 );
1791 if((error = alGetError()) != AL_NO_ERROR)
1792 {
1793 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",
1794 alGetString(error));
1795 ALmixer_SetError("Failed detecting still processed buffers: %s",
1796 alGetString(error) );
1797 /* This whole iOS 5.0 bug is so messed up that returning an error value probably isn't helpful.
1798 retval = AL_FALSE;
1799 */
1800 }
1801 /*
1802 fprintf(stderr, "unqueued buffers should be 0. Actual value is %d\n", buffers_processed);
1803 */
1804 /* Wow this is the bug that just keeps on sucking. There is an additional race condition bug where the unqueue may not actually work.
1805 * So I have to keep doing it until it does.
1806 * I hope this doesn't infinite loop.
1807 */
1808 if(0 != buffers_processed)
1809 {
1810 fprintf(stderr, "Evil Apple OpenAL iOS 5.0 race condition. Buffers didn't actually unqueue. Repeating unqueue loop.\n");
1811 }
1812 } while(0 != buffers_processed);
1813
1814 }
1815 }
1816
1817
1818 #endif
1819 #endif /* iOS 5.0 workaround */
1820
1821 /* According to the spec, this is the best way to clear a source.
1822 * This is supposed to work for both streamed and non-streamed sources.
1823 */
1824 alSourcei(source_id, AL_BUFFER, AL_NONE);
1825 if((error = alGetError()) != AL_NO_ERROR)
1826 {
1827 fprintf(stderr, "17dTesting Error with clearing buffer from source: %s",
1828 alGetString(error));
1829 ALmixer_SetError("Failed to clear buffer from source: %s",
1830 alGetString(error) );
1831 retval = AL_FALSE;
1832 }
1833
1834 return retval;
1835 }
1695 1836
1696 /* Will return the number of channels halted 1837 /* Will return the number of channels halted
1697 * or 0 for error 1838 * or 0 for error
1698 */ 1839 */
1699 static ALint Internal_HaltChannel(ALint channel, ALboolean did_finish_naturally) 1840 static ALint Internal_HaltChannel(ALint channel, ALboolean did_finish_naturally)
1700 { 1841 {
1701 ALint retval = 0; 1842 ALint retval = 0;
1702 ALint counter = 0; 1843 ALint counter = 0;
1703 ALenum error; 1844 ALenum error;
1704 ALint buffers_still_queued; 1845 ALboolean clear_succeeded;
1705 ALint buffers_processed;
1706 1846
1707 if(channel >= Number_of_Channels_global) 1847 if(channel >= Number_of_Channels_global)
1708 { 1848 {
1709 ALmixer_SetError("Cannot halt channel %d because it exceeds maximum number of channels (%d)\n", channel, Number_of_Channels_global); 1849 ALmixer_SetError("Cannot halt channel %d because it exceeds maximum number of channels (%d)\n", channel, Number_of_Channels_global);
1710 return -1; 1850 return -1;
1719 if((error = alGetError()) != AL_NO_ERROR) 1859 if((error = alGetError()) != AL_NO_ERROR)
1720 { 1860 {
1721 fprintf(stderr, "14Testing error: %s\n", 1861 fprintf(stderr, "14Testing error: %s\n",
1722 alGetString(error)); 1862 alGetString(error));
1723 } 1863 }
1724 /* Here's the situation. My old method of using 1864
1725 * alSourceUnqueueBuffers() seemed to be invalid in light 1865 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 1866 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 { 1867 {
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; 1868 retval = -1;
1747 } 1869 }
1748 alGetSourcei( 1870
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 1871
1778 ALmixer_Channel_List[channel].almixer_data->num_buffers_in_use = 0; 1872 ALmixer_Channel_List[channel].almixer_data->num_buffers_in_use = 0;
1779 1873
1780 /* Launch callback for consistency? */ 1874 /* Launch callback for consistency? */
1781 Invoke_Channel_Done_Callback(channel, did_finish_naturally); 1875 Invoke_Channel_Done_Callback(channel, did_finish_naturally);
1799 { 1893 {
1800 fprintf(stderr, "19Testing error: %s\n", 1894 fprintf(stderr, "19Testing error: %s\n",
1801 alGetString(error)); 1895 alGetString(error));
1802 } 1896 }
1803 1897
1804 /* Here's the situation. My old method of using 1898 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 1899 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 { 1900 {
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; 1901 retval = -1;
1827 } 1902 }
1828 alGetSourcei( 1903
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; 1904 ALmixer_Channel_List[i].almixer_data->num_buffers_in_use = 0;
1859 1905
1860 /* Launch callback for consistency? */ 1906 /* Launch callback for consistency? */
1861 Invoke_Channel_Done_Callback(i, did_finish_naturally); 1907 Invoke_Channel_Done_Callback(i, did_finish_naturally);
1862 1908
5171 alGetString(error) ); 5217 alGetString(error) );
5172 error_flag--; 5218 error_flag--;
5173 } 5219 }
5174 if(buffers_still_queued > 0) 5220 if(buffers_still_queued > 0)
5175 { 5221 {
5176 5222 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. */ 5223 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 { 5224 {
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--; 5225 error_flag--;
5204 } 5226 }
5205
5206 } 5227 }
5207 5228
5208 /* Launch callback */ 5229 /* Launch callback */
5209 Invoke_Channel_Done_Callback(i, AL_TRUE); 5230 Invoke_Channel_Done_Callback(i, AL_TRUE);
5210 5231
6236 */ 6257 */
6237 /* Rather than force unqueuing the buffer, let's see if 6258 /* Rather than force unqueuing the buffer, let's see if
6238 * setting the buffer to none works (the OpenAL 1.0 6259 * setting the buffer to none works (the OpenAL 1.0
6239 * Reference Annotation suggests this should work). 6260 * Reference Annotation suggests this should work).
6240 */ 6261 */
6241 alSourcei(ALmixer_Channel_List[i].alsource, 6262 ALboolean clear_succeeded = Internal_DetachBuffersFromSource(ALmixer_Channel_List[i].alsource, ALmixer_Channel_List[i].almixer_data->decoded_all);
6242 AL_BUFFER, AL_NONE); 6263 if(AL_FALSE == clear_succeeded)
6264 {
6265 error_flag--;
6266 }
6243 /* 6267 /*
6244 PrintQueueStatus(ALmixer_Channel_List[i].alsource); 6268 PrintQueueStatus(ALmixer_Channel_List[i].alsource);
6245 */ 6269 */
6246 /* This doesn't work because in some cases, I think 6270 /* This doesn't work because in some cases, I think
6247 * it causes the sound to be replayed 6271 * it causes the sound to be replayed