Index: snd_mgr.cpp
===================================================================
--- snd_mgr.cpp	(revision 10040)
+++ snd_mgr.cpp	(working copy)
@@ -409,98 +409,60 @@
 
 
 //-----------------------------------------------------------------------------
-// AL source suballocator: allocate all available sources up-front and
-// pass them out as needed (alGenSources is quite slow, taking 3..5 ms per
-// source returned). also responsible for enforcing user-specified limit
-// on total number of sources (to reduce mixing cost on low-end systems).
+// AL source suballocator
 //-----------------------------------------------------------------------------
 
-// regardless of HW capabilities, we won't use more than this ("enough").
-// necessary in case OpenAL doesn't limit #sources (e.g. if SW mixing).
-static const size_t AL_SRC_MAX = 64;
+// a suballocator used to be necessary because alGenSources took 3-5 ms per
+// source returned on NVidia's native OpenAL. however, OS X CoreAudio can't
+// deal with immediate source reuse (race condition?), and the easiest way
+// to get around this is to generate/delete sources on demand. fortunately,
+// OpenALsoft only takes a few microseconds to do this.
+// this layer is also responsible for enforcing the user-specified limit on
+// the total number of sources (to reduce mixing cost on low-end systems).
 
-// (allow changing at runtime)
-static size_t al_src_maxNumToUse = AL_SRC_MAX;
+// 64 is probably "enough", but the value can be changed at runtime.
+static size_t al_src_maxNumToUse = 64;
 
-static size_t al_src_numPreallocated;
+static volatile intptr_t al_src_inUse;
 
-enum AllocationState
-{
-	kAvailable = 0,	// (must match zero-initialization of al_srcs_allocationStates)
-	kInUse
-};
 
-// note: we want to catch double-free bugs and ensure all sources
-// are released at exit, but OpenAL doesn't specify an always-invalid
-// source name, so we need a separate array of AllocationState.
-static ALuint al_srcs[AL_SRC_MAX];
-static intptr_t al_srcs_allocationStates[AL_SRC_MAX];
-
-/**
- * grab as many sources as possible up to the limit.
- * called from al_init.
- */
 static void al_src_init()
 {
-	// grab as many sources as possible and count how many we get.
-	for(size_t i = 0; i < al_src_maxNumToUse; i++)
-	{
-		ALuint al_src;
-		alGenSources(1, &al_src);
-		// we've reached the limit, no more are available.
-		if(alGetError() != AL_NO_ERROR)
-			break;
-		ENSURE(alIsSource(al_src));
-		al_srcs[i] = al_src;
-		al_src_numPreallocated++;
-	}
-
-	// limit user's cap to what we actually got.
-	// (in case snd_set_max_src was called before this)
-	if(al_src_maxNumToUse > al_src_numPreallocated)
-		al_src_maxNumToUse = al_src_numPreallocated;
-
-	// make sure we got the minimum guaranteed by OpenAL.
-	ENSURE(al_src_numPreallocated >= 16);
 }
 
 
 /**
- * release all sources on free list.
- * all sources must already have been released via al_src_free.
  * called from al_shutdown.
+ * all sources must already have been released via al_src_free.
  */
 static void al_src_shutdown()
 {
-	for(size_t i = 0; i < al_src_numPreallocated; i++)
-		ENSURE(al_srcs_allocationStates[i] == kAvailable);
-
-	AL_CHECK;
-	alDeleteSources((ALsizei)al_src_numPreallocated, al_srcs);
-	AL_CHECK;
-
-	al_src_numPreallocated = 0;
+	// (we no longer bother keeping a list of extant sources,
+	// because they will be freed at exit anyway.)
+	ENSURE(al_src_inUse == 0);
 }
 
 
+TIMER_ADD_CLIENT(tc_OpenAL_source);
+
 /**
  * try to allocate a source.
  *
- * @return whether a source was allocated (see al_srcs_allocationStates).
+ * @return whether a source was allocated.
  * @param al_src receives the new source name iff true is returned.
  */
 static bool al_src_alloc(ALuint& al_src)
 {
-	for(size_t i = 0; i < al_src_numPreallocated; i++)
-	{
-		if(cpu_CAS(&al_srcs_allocationStates[i], kAvailable, kInUse))
-		{
-			al_src = al_srcs[i];
-			return true;
-		}
-	}
+TIMER_ACCRUE(tc_OpenAL_source);
 
-	return false;	// no more to give
+	if((size_t)al_src_inUse >= al_src_maxNumToUse)
+		return false;	// allocated too many already
+	cpu_AtomicAdd(&al_src_inUse, +1);
+
+	AL_CHECK;
+	alGenSources(1, &al_src);
+	AL_CHECK;
+	return true;
 }
 
 
@@ -511,16 +473,14 @@
  */
 static void al_src_free(ALuint al_src)
 {
+TIMER_ACCRUE(tc_OpenAL_source);
 	ENSURE(alIsSource(al_src));
 
-	const ALuint* pos = std::find(al_srcs, al_srcs+al_src_numPreallocated, al_src);
-	if(pos != al_srcs+al_src_numPreallocated)	// found it
-	{
-		const size_t i = pos - al_srcs;
-		ENSURE(cpu_CAS(&al_srcs_allocationStates[i], kInUse, kAvailable));
-	}
-	else
-		DEBUG_WARN_ERR(ERR::LOGIC);	// al_src wasn't in al_srcs
+	cpu_AtomicAdd(&al_src_inUse, -1);
+
+	AL_CHECK;
+	alDeleteSources(1, &al_src);
+	AL_CHECK;
 }
 
 
@@ -528,28 +488,15 @@
  * set maximum number of voices to play simultaneously,
  * to reduce mixing cost on low-end systems.
  * this limit may be ignored if e.g. there's a stricter
- * implementation- defined ceiling anyway.
+ * implementation-defined ceiling anyway.
  *
  * @param limit max. number of sources
  * @return Status
  */
 Status snd_set_max_voices(size_t limit)
 {
-	// valid if cap is legit (less than what we allocated in al_src_init),
-	// or if al_src_init hasn't been called yet. note: we accept anything
-	// in the second case, as al_src_init will sanity-check al_src_cap.
-	if(!al_src_numPreallocated || limit < al_src_numPreallocated)
-	{
-		al_src_maxNumToUse = limit;
-		return INFO::OK;
-	}
-	// user is requesting a cap higher than what we actually allocated.
-	// that's fine (not an error), but we won't set the cap, since it
-	// determines how many sources may be returned.
-	// there's no return value to indicate this because the cap is
-	// precisely that - an upper limit only, we don't care if it can't be met.
-	else
-		return INFO::OK;
+	al_src_maxNumToUse = limit;
+	return INFO::OK;
 }
 
 
