changeset 25:46e82b415520

merged
author Eric Wing <ewing . public |-at-| gmail . com>
date Fri, 24 Dec 2010 03:33:31 -0800
parents e085cbc573cf (diff) 9365e714fc4b (current diff)
children 884cce2515eb
files ALmixer.c ALmixer.h
diffstat 3 files changed, 269 insertions(+), 152 deletions(-) [+]
line wrap: on
line diff
--- a/ALmixer.c	Mon Nov 08 22:19:47 2010 -0800
+++ b/ALmixer.c	Fri Dec 24 03:33:31 2010 -0800
@@ -8,19 +8,14 @@
 #include "ALmixer.h"
 
 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
-	#include "ALmixer_rwops.h"
+	#include "ALmixer_RWops.h"
 	#include "SoundDecoder.h"
 #else
 	#include "SDL_sound.h"
 #endif
 
-#ifdef ANDROID_NDK
-	#include <AL/al.h>
-	#include <AL/alc.h>
-#else
-	#include "al.h" /* OpenAL */
-	#include "alc.h" /* For creating OpenAL contexts */
-#endif
+#include "al.h" /* OpenAL */
+#include "alc.h" /* For creating OpenAL contexts */
 
 #ifdef __APPLE__
 	/* For performance things like ALC_CONVERT_DATA_UPON_LOADING */
@@ -29,15 +24,14 @@
 	 * define was moved to a new header file and renamed to
 	 * ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING.
 	 */
+	#include <TargetConditionals.h>
 /*
-	#include <TargetConditionals.h>
 	#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
-
+		#include <AudioToolbox/AudioToolbox.h>
 	#else
 		#include <OpenAL/MacOSX_OALExtensions.h>
 	#endif
 */
-
 #endif
 
 /* For malloc, bsearch, qsort */
@@ -235,6 +229,8 @@
 
 static LinkedList* s_listOfALmixerData = NULL;
 
+/* Special stuff for iOS interruption handling */
+static ALCcontext* s_interruptionContext = NULL;
 
 
 #ifdef __APPLE__
@@ -323,8 +319,6 @@
 			LARGE_INTEGER current_time;
 			QueryPerformanceCounter(&current_time);
 			return (ALuint)((current_time.QuadPart - s_ticksBaseTime.QuadPart) * 1000 * s_hiResSecondsPerTick);
-		#elif ANDROID_NDK
-
 		#else /* assuming POSIX */
 			/* clock_gettime is POSIX.1-2001 */
 			struct timespec current_time;
@@ -346,7 +340,39 @@
 	#define ALmixer_Delay SDL_Delay
 #endif
 
-
+/* On iOS, usleep() of small numbers (say less than 100, very pronounced from 0-50)
+ * seems to be sucking up quite a bit of CPU time and causing performance problems.
+ * Instead of increasing the sleep time, I started changing the thread priority.
+ * This seemed to help the problem.
+ * Experimentally, the default priority seems to be 31. According to the docs,
+ * valid ranges are from 0 to 31. 6 was still giving me some hiccups so setting to
+ * 0 (PTHREAD_MIN_PRIORITY) seems to be the best value so far.
+ * Mac also reports 31 as the default. However, I have not noticed the same
+ * performance problems and cannot get audio to show up as a significant percentage
+ * of the CPU time in Shark/Instruments.
+ */
+#if defined(__APPLE__) && !defined(ALMIXER_COMPILE_WITHOUT_SDL) && ( (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) )
+#include <pthread.h>
+#endif
+static void Internal_LowerThreadPriority(SDL_Thread* simple_thread)
+{
+	/* Might open to other platforms as needed */
+#if defined(__APPLE__) && ( (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) )
+	#ifdef ALMIXER_COMPILE_WITHOUT_SDL
+		SimpleThread_SetThreadPriority(Stream_Thread_global, 0);
+	#else
+		struct sched_param schedule_param;
+		int sched_policy;
+		int ret_val;
+		schedule_param.sched_priority = 0; /* PTHREAD_MIN_PRIORITY, max=31 */
+		/* EVIL! This will break if the SDL_Thread structure layout changes. */
+		pthread_t* native_thread_ptr_hack = (pthread_t*)(((char*)(Stream_Thread_global))+sizeof(unsigned long));
+		ret_val = pthread_setschedparam(*native_thread_ptr_hack, SCHED_OTHER, &schedule_param);
+	#endif
+#else
+	/* No-Op */
+#endif
+}
 
 /* If ENABLE_PARANOID_SIGNEDNESS_CHECK is used,
  * these values will be reset on Init()
@@ -374,7 +400,7 @@
 struct ALmixer_Data
 {
 	ALboolean decoded_all; /* dictates different behaviors */
-	ALint total_time; /* total playing time of sample (msec) */
+	ALint total_time; /* total playing time of sample (msec), obsolete now that we pushed our changes to SDL_sound */
 	
 	ALuint in_use; /* needed to prevent sharing for streams */
 	ALboolean eof; /* flag for eof, only used for streams  */
@@ -609,43 +635,6 @@
 	ALmixer_Channel_List[channel].almixer_data = NULL;
 }
 
-
-#if 0
-/* Not needed anymore because not doing any fileext checks.
- *
- * Unfortunately, strcasecmp isn't portable so here's a
- * reimplementation of it (taken from SDL_sound)
- */
-static int ALmixer_strcasecmp(const char* x, const char* y)
-{
-	int ux, uy;
-	
-	if (x == y)  /* same pointer? Both NULL? */
-		return(0);
-
-	if (x == NULL)
-		return(-1);
-
-	if (y == NULL)
-		return(1);
-	   
-	do
-	{
-		ux = toupper((int) *x);
-		uy = toupper((int) *y);
-		if (ux > uy)
-			return(1);
-		else if (ux < uy)
-			return(-1);
-		x++;
-		y++;
-	} while ((ux) && (uy));
-
-    return(0);
-}
-#endif
-
-					
 /* What shoud this return?
  * 127 for signed, 255 for unsigned
  */
@@ -798,6 +787,7 @@
 } /* End Compute_Total_Time */
 	
 
+#ifdef ALMIXER_DISABLE_PREDECODED_PRECOMPUTE_BUFFER_SIZE_OPTIMIZATION
 static size_t Compute_Total_Bytes_Decomposed(ALuint bytes_per_sample, ALuint frequency, ALubyte channels, ALuint total_msec)
 {
 	double total_sec;
@@ -845,6 +835,7 @@
 	return Compute_Total_Bytes_Decomposed(bytes_per_sample, info->rate, info->channels, total_msec);
 }
 
+
 /* The back-end decoders seem to need to decode in quantized frame sizes.
  * So if I can pad the bytes to the next quanta, things might go more smoothly.
  */
@@ -884,9 +875,8 @@
 	 fprintf(stderr, "remainder_frames=%d, padded_total_bytes=%d\n", remainder_frames, return_bytes);
 	 */
 	 return return_bytes;
-
-}
-
+}
+#endif /* ALMIXER_DISABLE_PREDECODED_PRECOMPUTE_BUFFER_SIZE_OPTIMIZATION */
 
 
 
@@ -1141,21 +1131,30 @@
 /* Converts milliseconds to byte positions.
  * This is needed for seeking on predecoded samples 
  */
-static ALuint Convert_Msec_To_Byte_Pos(Sound_AudioInfo *info, ALuint ms)
-{
-	float frames_per_ms;
-	ALuint frame_offset;
-	ALuint frame_size;
-	if(info == NULL)
+static ALuint Convert_Msec_To_Byte_Pos(Sound_AudioInfo* audio_info, ALuint number_of_milliseconds)
+{
+	ALuint bytes_per_sample;
+	ALuint bytes_per_frame;
+	float bytes_per_millisecond;
+	float byte_position;
+
+	if(audio_info == NULL)
 	{
 		fprintf(stderr, "Error, info is NULL\n");
 	}
 
-	/* "frames" == "sample frames" */
-	frames_per_ms = ((float) info->rate) / 1000.0f;
-	frame_offset = (ALuint) (frames_per_ms * ((float) ms));
-	frame_size = (ALuint) ((info->format & 0xFF) / 8) * info->channels;
-	return frame_offset * frame_size;
+	/* SDL has a mask trick I was not aware of. Mask the upper bits
+	 * of the format, and you get 8 or 16 which is the bits per sample.
+ 	 * Divide by 8bits_per_bytes and you get bytes_per_sample
+	 * I tested this under 32-bit and 64-bit and big and little endian
+	 * to make sure this still works since I have since moved from 
+	 * Uint32 to unspecified size types like ALuint.
+	 */
+	bytes_per_sample = (ALuint) ((audio_info->format & 0xFF) / 8);
+	bytes_per_frame = bytes_per_sample * audio_info->channels;
+	bytes_per_millisecond = (float)bytes_per_frame * (audio_info->rate / 1000.0f);
+	byte_position = ((float)(number_of_milliseconds)) * bytes_per_millisecond;  
+	return (ALuint)(byte_position + 0.5);
 }
 
 static ALint Set_Predecoded_Seek_Position(ALmixer_Data* data, ALuint byte_position)
@@ -3439,7 +3438,7 @@
 	ALfloat value;
 	ALenum error;
 	ALfloat original_value;
-	ALuint current_time = ALmixer_GetTicks();
+/*	ALuint current_time = ALmixer_GetTicks(); */
 	ALint retval;
 
 	
@@ -4805,6 +4804,15 @@
 #endif
 		return 0;
 	}
+
+	/* Bypass if in interruption event */
+	if(NULL == alcGetCurrentContext())
+	{
+#ifdef ENABLE_ALMIXER_THREADS
+		SDL_UnlockMutex(s_simpleLock);
+#endif
+		return 0;
+	}
 	
 	/* Check the quick flag to see if anything needs updating */
 	/* If anything is playing, then we have to do work */
@@ -6208,7 +6216,7 @@
  * the user will need to restart it if they need it after
  * OpenAL shuts down.
  */
-ALboolean ALmixer_Init(ALuint frequency, ALint num_sources, ALuint refresh)
+ALboolean ALmixer_Init(ALuint frequency, ALuint num_sources, ALuint refresh)
 {
 	ALCdevice* dev;
 	ALCcontext* context;
@@ -6576,42 +6584,48 @@
 	 *	This feature is toggled on/off by using the alDisable() & alEnable() APIs. This 
 	 *	setting will be applied to all subsequent 
 	 *	calls to alBufferData().
+	 * 
+	 * Update: Some people keep reporting that they see the enable fail on Mac, but I can't reproduce it myself.
+	 * Rather than deal with it right now, I think I am going to make it an opt-in thing.
 	 */	
-#ifdef __APPLE__
+#if defined(__APPLE__) && defined(ALMIXER_USE_OSX_CONVERT_DATA_UPON_LOADING)
 /*
+ iPhone is getting this enum, but is failing on the enable, so I guess I'll define it out.
+ */
 	#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
-
+	 
 	#else
+		/* iOS reports this enum exists, but loading it always fails, so make it Mac only. */
+		ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
+		 /*
+		 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
+		 */
+		if(0 != convert_data_enum)
+		{
+			alEnable(convert_data_enum);		
+		}
+		if( (AL_NO_ERROR != alGetError()) )
+		{
+			fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
+			ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
+		}
 	#endif
-*/
-	ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
-	/*
-	fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
-	*/
-	if(0 != convert_data_enum)
-	{
-		alEnable(convert_data_enum);		
-	}
-	if( (AL_NO_ERROR != alGetError()) )
-	{
-	fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
-		ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
-	}
-	
-#endif
+	
+#endif /* __APPLE__ */
 	
 
 	
 	
 	ALmixer_Initialized = 1;
 
-	if(num_sources <= 0)
+	if(num_sources == 0)
 	{
 		Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS;
 	}
 	else
 	{
-		Number_of_Channels_global = num_sources;
+		/* probably should make Number_of_Channels_global an ALuint, but need to cast which_channel all the time */
+		Number_of_Channels_global = (ALint)num_sources;
 	}
 	Number_of_Reserve_Channels_global = 0;
 	Is_Playing_global = 0;
@@ -6760,6 +6774,9 @@
 		Number_of_Channels_global = 0;
 		return AL_FALSE;
 	}
+	
+	/* Note: Only a few platforms change the priority. See implementation for notes. */
+	Internal_LowerThreadPriority(Stream_Thread_global);
 		
 /*
 	fprintf(stderr, "Using threads\n");
@@ -7115,34 +7132,38 @@
 	 *	This feature is toggled on/off by using the alDisable() & alEnable() APIs. This 
 	 *	setting will be applied to all subsequent 
 	 *	calls to alBufferData().
+	 * Update: Some people keep reporting that they see the enable fail on Mac, but I can't reproduce it myself.
+	 * Rather than deal with it right now, I think I am going to make it an opt-in thing.
 	 */	
-#ifdef __APPLE__
-	/*
-	 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
-	 
-	 #else
-	 #endif
-	 */
-	ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
+#if defined(__APPLE__) && defined(ALMIXER_USE_OSX_CONVERT_DATA_UPON_LOADING)
 	/*
-	fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
-	*/
-	if(0 != convert_data_enum)
-	{
-		alEnable(convert_data_enum);		
-	}
-	if( (AL_NO_ERROR != alGetError()) )
-	{
-		fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
-		ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
-	}
+	 iPhone is getting this enum, but is failing on the enable, so I guess I'll define it out.
+	 */
+	#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
+	 
+	#else
+		/* iOS reports this enum exists, but loading it always fails, so make it Mac only. */
+		ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
+		 /*
+		 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
+		 */
+		if(0 != convert_data_enum)
+		{
+			alEnable(convert_data_enum);		
+		}
+		if( (AL_NO_ERROR != alGetError()) )
+		{
+			fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
+			ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
+		}
+	#endif
 #endif
 	
 	return AL_TRUE;
 }
 
 	
-ALboolean ALmixer_InitMixer(ALint num_sources)
+ALboolean ALmixer_InitMixer(ALuint num_sources)
 {	
 	ALint i;
 	ALenum error;
@@ -7176,13 +7197,13 @@
 	 */
 #endif
 
-	if(num_sources <= 0)
+	if(num_sources == 0)
 	{
 		Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS;
 	}
 	else
 	{
-		Number_of_Channels_global = num_sources;
+		Number_of_Channels_global = (ALint)num_sources;
 	}
 	Number_of_Reserve_Channels_global = 0;
 	Is_Playing_global = 0;
@@ -7306,7 +7327,10 @@
 		Number_of_Channels_global = 0;
 		return AL_FALSE;
 	}
-		
+
+	/* Note: Only a few platforms change the priority. See implementation for notes. */
+	Internal_LowerThreadPriority(Stream_Thread_global);
+
 	/*
 	fprintf(stderr, "Using threads\n");
 	*/
@@ -7319,7 +7343,54 @@
 	return AL_TRUE;
 }
 
-
+void ALmixer_BeginInterruption()
+{
+#ifdef ENABLE_ALMIXER_THREADS
+	SDL_LockMutex(s_simpleLock);
+#endif
+	s_interruptionContext = alcGetCurrentContext();
+	if(NULL != s_interruptionContext)
+	{
+		/* iOS alcSuspendContext is a no-op */
+		alcSuspendContext(s_interruptionContext);
+		alcMakeContextCurrent(NULL);
+	}
+#ifdef ENABLE_ALMIXER_THREADS
+	SDL_UnlockMutex(s_simpleLock);
+#endif		
+}
+
+void ALmixer_EndInterruption()
+{
+#ifdef ENABLE_ALMIXER_THREADS
+	SDL_LockMutex(s_simpleLock);
+#endif
+
+	/* Note: iOS, you need to set the AudioSession active.
+	 * But if the AudioSession is not initialized, this SetActive 
+	 * call fails.
+	 * So this is probably better if calling app sets this.
+	 */
+/*
+#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
+	OSStatus the_error = AudioSessionSetActive(true);
+	if(noErr != the_error)
+	{
+		fprintf(stderr, "Error setting audio session active! %d\n", the_error);
+	}
+#endif
+ */
+	
+	if(NULL != s_interruptionContext)
+	{
+		alcMakeContextCurrent(s_interruptionContext);
+		alcProcessContext(s_interruptionContext);
+		s_interruptionContext = NULL;
+	}
+#ifdef ENABLE_ALMIXER_THREADS
+	SDL_UnlockMutex(s_simpleLock);
+#endif
+}
 
 /* Keep the return value void to allow easy use with
  * atexit()
@@ -7337,6 +7408,34 @@
 #ifdef ENABLE_ALMIXER_THREADS
 	SDL_LockMutex(s_simpleLock);
 #endif
+	
+	/* Several things we need to do:
+	 First, we need to check if we are in an interruption.
+	 If so, we need to reactivate the alcContext so we can call OpenAL functions.
+	 Next, we should delete the OpenAL sources.
+	 Next, we need to free all the sound data via ALmixer_FreeData().
+	 Finally, we can delete the OpenAL context.
+	 */
+	
+	context = alcGetCurrentContext();
+	if(NULL == context)
+	{
+		/* We might have an interruption event where the current context is NULL */
+		if(NULL == s_interruptionContext)
+		{
+			/* Nothing left to try. I think we're done. */
+			fprintf(stderr, "ALmixer_Quit: Assertion Error. Expecting to find an OpenAL context, but could not find one.\n");
+			return;
+		}
+		else 
+		{
+			context = s_interruptionContext;
+			/* reactivate the context so we can call OpenAL functions */
+			alcMakeContextCurrent(context);
+			s_interruptionContext = NULL;
+		}
+	}	
+	
 	/* Shutdown everything before closing context */
 	Internal_HaltChannel(-1, AL_FALSE);
 	
@@ -7348,7 +7447,7 @@
 
 	SDL_DestroyMutex(s_simpleLock);
 #endif
-
+	
 	/* Delete all the OpenAL sources */
 	for(i=0; i<Number_of_Channels_global; i++)
 	{
@@ -7357,7 +7456,7 @@
 	/* Delete all the channels */
 	free(ALmixer_Channel_List);
 	free(Source_Map_List);
-	
+
 	/* Reset the Number_of_Channels just in case somebody
 	 * tries using a ALmixer function.
 	 * I probably should put "Initialized" checks everywhere,
@@ -7365,21 +7464,7 @@
 	 */
 	Number_of_Channels_global = 0;
 	
-	context = alcGetCurrentContext();
-	if(NULL == context)
-	{
-		return;
-	}
-	/* Need to get the device before I close the context */
-	dev = alcGetContextsDevice(context);
-	alcDestroyContext(context);
-	
-	if(NULL == dev)
-	{
-		return;	
-	}
-	alcCloseDevice(dev);
-	
+
 	/* Delete the list of ALmixerData's before Sound_Quit deletes
 	 * its own underlying information and I potentially have dangling pointers.
 	 */
@@ -7391,7 +7476,20 @@
 	}
 	LinkedList_Free(s_listOfALmixerData);
 	s_listOfALmixerData = NULL;
-
+	
+	
+	/* Need to get the device before I close the context */
+	dev = alcGetContextsDevice(context);
+	
+	alcMakeContextCurrent(NULL);
+	alcDestroyContext(context);
+	
+	if(NULL == dev)
+	{
+		return;	
+	}
+	alcCloseDevice(dev);
+	
 	Sound_Quit();
 
 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
@@ -7841,6 +7939,8 @@
 			 */
 			ret_data->total_bytes = 0;
 
+			ret_data->total_time = Sound_GetDuration(sample);
+
 			/* Create buffers for data
 			 */
 			ret_data->buffer = (ALuint*)malloc( sizeof(ALuint) * max_queue_buffers);
@@ -8369,6 +8469,13 @@
 	{
 		return;
 	}
+
+	/* Bypass if in interruption event */
+	if(NULL == alcGetCurrentContext())
+	{
+		fprintf(stderr, "ALmixer_FreeData: Programmer Error. You cannot delete data when the OpenAL content is currently NULL. You may have already called ALmixer_Quit() or are in an interruption event\n");
+		return;
+	}
 	
 	if(data->decoded_all)
 	{
@@ -8380,12 +8487,10 @@
 			Sound_FreeSample(data->sample);
 		}
 		alDeleteBuffers(1, data->buffer);
-	if((error = alGetError()) != AL_NO_ERROR)
-	{
-		fprintf(stderr, "70Testing error: %s\n",
-			alGetString(error));				
-	}
-
+		if((error = alGetError()) != AL_NO_ERROR)
+		{
+			fprintf(stderr, "ALmixer_FreeData: alDeleteBuffers failed. %s\n", alGetString(error));				
+		}
 	}
 	else
 	{
@@ -8407,11 +8512,10 @@
 			
 		Sound_FreeSample(data->sample);
 		alDeleteBuffers(data->max_queue_buffers, data->buffer);		
-	if((error = alGetError()) != AL_NO_ERROR)
-	{
-		fprintf(stderr, "71Testing error: %s\n",
-			alGetString(error));				
-	}
+		if((error = alGetError()) != AL_NO_ERROR)
+		{
+			fprintf(stderr, "ALmixer_FreeData: alDeleteBuffers failed. %s\n", alGetString(error));				
+		}
 	}
 	free(data->buffer);
 
@@ -8428,6 +8532,7 @@
 	{
 		return -1;
 	}
+
 	return data->total_time;
 }
 
--- a/ALmixer.h	Mon Nov 08 22:19:47 2010 -0800
+++ b/ALmixer.h	Fri Dec 24 03:33:31 2010 -0800
@@ -168,11 +168,7 @@
 
 
 /* Needed for OpenAL types since altypes.h was removed in 1.1 */
-#ifdef ANDROID_NDK
-	#include <AL/al.h>
-#else
-	#include "al.h"
-#endif
+#include "al.h"
 
 /* Set up for C function definitions, even when using C++ */
 #ifdef __cplusplus
@@ -203,7 +199,7 @@
 /* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
  */
 #define ALMIXER_MAJOR_VERSION		0
-#define ALMIXER_MINOR_VERSION		1
+#define ALMIXER_MINOR_VERSION		2
 #define ALMIXER_PATCHLEVEL			0
 
 
@@ -300,7 +296,7 @@
 
 
 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
-	#include "ALmixer_rwops.h"
+	#include "ALmixer_RWops.h"
 #else
 	#include "SDL_rwops.h"
 	/**
@@ -344,7 +340,7 @@
  * Pass in 0 or ALMIXER_DEFAULT_REFRESH to use OpenAL default behaviors.
  * @return Returns AL_FALSE on a failure or AL_TRUE if successfully initialized.
  */
-extern ALMIXER_DECLSPEC ALboolean ALMIXER_CALL ALmixer_Init(ALuint playback_frequency, ALint num_sources, ALuint refresh_rate);
+extern ALMIXER_DECLSPEC ALboolean ALMIXER_CALL ALmixer_Init(ALuint playback_frequency, ALuint num_sources, ALuint refresh_rate);
 
 /** 
  * InitContext will only initialize the OpenAL context (and not the mixer part).
@@ -371,9 +367,24 @@
  * the mixer system separately. I strongly recommend avoiding these two functions
  * and use the normal Init() function.
  */
-extern ALMIXER_DECLSPEC ALboolean ALMIXER_CALL ALmixer_InitMixer(ALint num_sources);
+extern ALMIXER_DECLSPEC ALboolean ALMIXER_CALL ALmixer_InitMixer(ALuint num_sources);
+
+/**
+ * (EXPERIMENTAL) Call to notify ALmixer that your device needs to handle an interruption.
+ * (EXPERIMENTAL) For devices like iOS that need special handling for interruption events like phone calls and alarms,
+ * this function will do the correct platform correct thing to handle the interruption w.r.t. OpenAL.
+ */
+extern ALMIXER_DECLSPEC void ALMIXER_CALL ALmixer_BeginInterruption(void);
 
 /**
+ * (EXPERIMENTAL) Call to notify ALmixer that your device needs to resume from an interruption.
+ * (EXPERIMENTAL) For devices like iOS that need special handling for interruption events like phone calls and alarms,
+ * this function will do the correct platform correct thing to resume from the interruption w.r.t. OpenAL.
+ */
+extern ALMIXER_DECLSPEC void ALMIXER_CALL ALmixer_EndInterruption(void);
+	
+	
+/**
  * This shuts down ALmixer. Please remember to free your ALmixer_Data* instances
  * before calling this method.
  */
--- a/LinkedList.c	Mon Nov 08 22:19:47 2010 -0800
+++ b/LinkedList.c	Fri Dec 24 03:33:31 2010 -0800
@@ -251,13 +251,14 @@
 
 void LinkedList_Clear(LinkedList* linked_list)
 {
+	LinkedListNode* current_node;
+	LinkedListNode* next_node;
 	if(NULL == linked_list)
 	{
 		return;
 	}
 
-	LinkedListNode* current_node = linked_list->headPtr;
-	LinkedListNode* next_node;
+	current_node = linked_list->headPtr;
 	while(NULL != current_node)
 	{
 		next_node = current_node->nextNode;