comparison ALmixer.c @ 22:58f03008ea05

- Added Seek APIs - Added new API for interruption handling like on iOS - Made support for ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING an opt-in compile define because iOS kept reporting it supported this even though it didn't and some people reported that on Mac, this sometimes failed (but I couldn't reproduce). - Made number of sources unsigned - Some minor 64-bit clean ups
author Eric Wing <ewing . public |-at-| gmail . com>
date Fri, 24 Dec 2010 03:23:02 -0800
parents 038baa026db3
children e085cbc573cf
comparison
equal deleted inserted replaced
21:14c22fc3c753 22:58f03008ea05
6 */ 6 */
7 7
8 #include "ALmixer.h" 8 #include "ALmixer.h"
9 9
10 #ifdef ALMIXER_COMPILE_WITHOUT_SDL 10 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
11 #include "ALmixer_rwops.h" 11 #include "ALmixer_RWops.h"
12 #include "SoundDecoder.h" 12 #include "SoundDecoder.h"
13 #else 13 #else
14 #include "SDL_sound.h" 14 #include "SDL_sound.h"
15 #endif 15 #endif
16 16
17 #ifdef ANDROID_NDK 17 #include "al.h" /* OpenAL */
18 #include <AL/al.h> 18 #include "alc.h" /* For creating OpenAL contexts */
19 #include <AL/alc.h>
20 #else
21 #include "al.h" /* OpenAL */
22 #include "alc.h" /* For creating OpenAL contexts */
23 #endif
24 19
25 #ifdef __APPLE__ 20 #ifdef __APPLE__
26 /* For performance things like ALC_CONVERT_DATA_UPON_LOADING */ 21 /* For performance things like ALC_CONVERT_DATA_UPON_LOADING */
27 /* Note: ALC_CONVERT_DATA_UPON_LOADING used to be in the alc.h header. 22 /* Note: ALC_CONVERT_DATA_UPON_LOADING used to be in the alc.h header.
28 * But in the Tiger OpenAL 1.1 release (10.4.7 and Xcode 2.4), the 23 * But in the Tiger OpenAL 1.1 release (10.4.7 and Xcode 2.4), the
29 * define was moved to a new header file and renamed to 24 * define was moved to a new header file and renamed to
30 * ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING. 25 * ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING.
31 */ 26 */
27 #include <TargetConditionals.h>
32 /* 28 /*
33 #include <TargetConditionals.h>
34 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) 29 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
35 30 #include <AudioToolbox/AudioToolbox.h>
36 #else 31 #else
37 #include <OpenAL/MacOSX_OALExtensions.h> 32 #include <OpenAL/MacOSX_OALExtensions.h>
38 #endif 33 #endif
39 */ 34 */
40
41 #endif 35 #endif
42 36
43 /* For malloc, bsearch, qsort */ 37 /* For malloc, bsearch, qsort */
44 #include <stdlib.h> 38 #include <stdlib.h>
45 39
233 static SDL_Thread* Stream_Thread_global = NULL; 227 static SDL_Thread* Stream_Thread_global = NULL;
234 #endif 228 #endif
235 229
236 static LinkedList* s_listOfALmixerData = NULL; 230 static LinkedList* s_listOfALmixerData = NULL;
237 231
232 /* Special stuff for iOS interruption handling */
233 static ALCcontext* s_interruptionContext = NULL;
238 234
239 235
240 #ifdef __APPLE__ 236 #ifdef __APPLE__
241 static ALvoid Internal_alcMacOSXMixerOutputRate(const ALdouble sample_rate) 237 static ALvoid Internal_alcMacOSXMixerOutputRate(const ALdouble sample_rate)
242 { 238 {
321 return (ALuint)((CACurrentMediaTime()-s_ticksBaseTime)*1000.0); 317 return (ALuint)((CACurrentMediaTime()-s_ticksBaseTime)*1000.0);
322 #elif defined(_WIN32) 318 #elif defined(_WIN32)
323 LARGE_INTEGER current_time; 319 LARGE_INTEGER current_time;
324 QueryPerformanceCounter(&current_time); 320 QueryPerformanceCounter(&current_time);
325 return (ALuint)((current_time.QuadPart - s_ticksBaseTime.QuadPart) * 1000 * s_hiResSecondsPerTick); 321 return (ALuint)((current_time.QuadPart - s_ticksBaseTime.QuadPart) * 1000 * s_hiResSecondsPerTick);
326 #elif ANDROID_NDK
327
328 #else /* assuming POSIX */ 322 #else /* assuming POSIX */
329 /* clock_gettime is POSIX.1-2001 */ 323 /* clock_gettime is POSIX.1-2001 */
330 struct timespec current_time; 324 struct timespec current_time;
331 clock_gettime(CLOCK_MONOTONIC, &current_time); 325 clock_gettime(CLOCK_MONOTONIC, &current_time);
332 return (ALuint)((current_time.tv_sec - s_ticksBaseTime.tv_sec)*1000.0 + (current_time.tv_nsec - s_ticksBaseTime.tv_nsec) / 1000000); 326 return (ALuint)((current_time.tv_sec - s_ticksBaseTime.tv_sec)*1000.0 + (current_time.tv_nsec - s_ticksBaseTime.tv_nsec) / 1000000);
372 366
373 367
374 struct ALmixer_Data 368 struct ALmixer_Data
375 { 369 {
376 ALboolean decoded_all; /* dictates different behaviors */ 370 ALboolean decoded_all; /* dictates different behaviors */
377 ALint total_time; /* total playing time of sample (msec) */ 371 ALint total_time; /* total playing time of sample (msec), obsolete now that we pushed our changes to SDL_sound */
378 372
379 ALuint in_use; /* needed to prevent sharing for streams */ 373 ALuint in_use; /* needed to prevent sharing for streams */
380 ALboolean eof; /* flag for eof, only used for streams */ 374 ALboolean eof; /* flag for eof, only used for streams */
381 375
382 ALuint total_bytes; /* For predecoded */ 376 ALuint total_bytes; /* For predecoded */
607 */ 601 */
608 602
609 ALmixer_Channel_List[channel].almixer_data = NULL; 603 ALmixer_Channel_List[channel].almixer_data = NULL;
610 } 604 }
611 605
612
613 #if 0
614 /* Not needed anymore because not doing any fileext checks.
615 *
616 * Unfortunately, strcasecmp isn't portable so here's a
617 * reimplementation of it (taken from SDL_sound)
618 */
619 static int ALmixer_strcasecmp(const char* x, const char* y)
620 {
621 int ux, uy;
622
623 if (x == y) /* same pointer? Both NULL? */
624 return(0);
625
626 if (x == NULL)
627 return(-1);
628
629 if (y == NULL)
630 return(1);
631
632 do
633 {
634 ux = toupper((int) *x);
635 uy = toupper((int) *y);
636 if (ux > uy)
637 return(1);
638 else if (ux < uy)
639 return(-1);
640 x++;
641 y++;
642 } while ((ux) && (uy));
643
644 return(0);
645 }
646 #endif
647
648
649 /* What shoud this return? 606 /* What shoud this return?
650 * 127 for signed, 255 for unsigned 607 * 127 for signed, 255 for unsigned
651 */ 608 */
652 static ALubyte GetSignednessValue(ALushort format) 609 static ALubyte GetSignednessValue(ALushort format)
653 { 610 {
1139 } 1096 }
1140 1097
1141 /* Converts milliseconds to byte positions. 1098 /* Converts milliseconds to byte positions.
1142 * This is needed for seeking on predecoded samples 1099 * This is needed for seeking on predecoded samples
1143 */ 1100 */
1144 static ALuint Convert_Msec_To_Byte_Pos(Sound_AudioInfo *info, ALuint ms) 1101 static ALuint Convert_Msec_To_Byte_Pos(Sound_AudioInfo* audio_info, ALuint number_of_milliseconds)
1145 { 1102 {
1146 float frames_per_ms; 1103 ALuint bytes_per_sample;
1147 ALuint frame_offset; 1104 ALuint bytes_per_frame;
1148 ALuint frame_size; 1105 float bytes_per_millisecond;
1149 if(info == NULL) 1106 float byte_position;
1107
1108 if(audio_info == NULL)
1150 { 1109 {
1151 fprintf(stderr, "Error, info is NULL\n"); 1110 fprintf(stderr, "Error, info is NULL\n");
1152 } 1111 }
1153 1112
1154 /* "frames" == "sample frames" */ 1113 /* SDL has a mask trick I was not aware of. Mask the upper bits
1155 frames_per_ms = ((float) info->rate) / 1000.0f; 1114 * of the format, and you get 8 or 16 which is the bits per sample.
1156 frame_offset = (ALuint) (frames_per_ms * ((float) ms)); 1115 * Divide by 8bits_per_bytes and you get bytes_per_sample
1157 frame_size = (ALuint) ((info->format & 0xFF) / 8) * info->channels; 1116 * I tested this under 32-bit and 64-bit and big and little endian
1158 return frame_offset * frame_size; 1117 * to make sure this still works since I have since moved from
1118 * Uint32 to unspecified size types like ALuint.
1119 */
1120 bytes_per_sample = (ALuint) ((audio_info->format & 0xFF) / 8);
1121 bytes_per_frame = bytes_per_sample * audio_info->channels;
1122 bytes_per_millisecond = (float)bytes_per_frame * (audio_info->rate / 1000.0f);
1123 byte_position = ((float)(number_of_milliseconds)) * bytes_per_millisecond;
1124 return (ALuint)(byte_position + 0.5);
1159 } 1125 }
1160 1126
1161 static ALint Set_Predecoded_Seek_Position(ALmixer_Data* data, ALuint byte_position) 1127 static ALint Set_Predecoded_Seek_Position(ALmixer_Data* data, ALuint byte_position)
1162 { 1128 {
1163 ALenum error; 1129 ALenum error;
2328 static ALint Internal_RewindSource(ALuint source) 2294 static ALint Internal_RewindSource(ALuint source)
2329 { 2295 {
2330 ALint channel; 2296 ALint channel;
2331 if(0 == source) 2297 if(0 == source)
2332 { 2298 {
2333 return Internal_RewindChannel(-1) + 1; 2299 return Internal_RewindChannel(-1);
2334 } 2300 }
2335 2301
2336 channel = Internal_GetChannel(source); 2302 channel = Internal_GetChannel(source);
2337 if(-1 == channel) 2303 if(-1 == channel)
2338 { 2304 {
2339 ALmixer_SetError("Cannot rewind source: %s", ALmixer_GetError()); 2305 ALmixer_SetError("Cannot rewind source: %s", ALmixer_GetError());
2340 return 0; 2306 return 0;
2341 } 2307 }
2342 return Internal_RewindChannel(channel) + 1; 2308 return Internal_RewindChannel(channel);
2343 } 2309 }
2344 2310
2345 2311
2346 2312
2347 2313
3218 3184
3219 return AL_TRUE; 3185 return AL_TRUE;
3220 } 3186 }
3221 3187
3222 3188
3189 static ALint Internal_SeekChannel(ALint channel, ALuint msec)
3190 {
3191 ALint retval = 0;
3192 ALenum error;
3193 ALint state;
3194 ALint running_count = 0;
3195
3196 if(0 == msec)
3197 {
3198 return Internal_RewindChannel(channel);
3199 }
3200
3201 if(channel >= Number_of_Channels_global)
3202 {
3203 ALmixer_SetError("Cannot seek channel %d because it exceeds maximum number of channels (%d)\n", channel, Number_of_Channels_global);
3204 return -1;
3205 }
3206
3207 if((error = alGetError()) != AL_NO_ERROR)
3208 {
3209 fprintf(stderr, "24Testing error: %s\n",
3210 alGetString(error));
3211 }
3212 /* Clear error */
3213 alGetError();
3214
3215 /* If the user specified a specific channel */
3216 if(channel >= 0)
3217 {
3218 /* only need to process channel if in use */
3219 if(ALmixer_Channel_List[channel].channel_in_use)
3220 {
3221
3222 /* What should I do? Do I just rewind the channel
3223 * or also rewind the data? Since the data is
3224 * shared, let's make it the user's responsibility
3225 * to rewind the data.
3226 */
3227 if(ALmixer_Channel_List[channel].almixer_data->decoded_all)
3228 {
3229 /* convert milliseconds to seconds */
3230 ALfloat sec_offset = msec / 1000.0f;
3231
3232 alGetSourcei(
3233 ALmixer_Channel_List[channel].alsource,
3234 AL_SOURCE_STATE, &state
3235 );
3236 if((error = alGetError()) != AL_NO_ERROR)
3237 {
3238 fprintf(stderr, "25Testing error: %s\n",
3239 alGetString(error));
3240 }
3241 /* OpenAL seek */
3242 alSourcef(ALmixer_Channel_List[channel].alsource, AL_SEC_OFFSET, sec_offset);
3243 if((error = alGetError()) != AL_NO_ERROR)
3244 {
3245 ALmixer_SetError("%s",
3246 alGetString(error) );
3247 retval = -1;
3248 }
3249 /* Need to resume playback if it was originally playing */
3250 if(AL_PLAYING == state)
3251 {
3252 alSourcePlay(ALmixer_Channel_List[channel].alsource);
3253 if((error = alGetError()) != AL_NO_ERROR)
3254 {
3255 ALmixer_SetError("%s",
3256 alGetString(error) );
3257 retval = -1;
3258 }
3259 }
3260 else if(AL_PAUSED == state)
3261 {
3262 /* HACK: The problem is that when paused, after
3263 * the Rewind, I can't get it off the INITIAL
3264 * state without restarting
3265 */
3266 alSourcePlay(ALmixer_Channel_List[channel].alsource);
3267 if((error = alGetError()) != AL_NO_ERROR)
3268 {
3269 fprintf(stderr, "25Testing error: %s\n",
3270 alGetString(error));
3271 }
3272 alSourcePause(ALmixer_Channel_List[channel].alsource);
3273 if((error = alGetError()) != AL_NO_ERROR)
3274 {
3275 ALmixer_SetError("%s",
3276 alGetString(error) );
3277 retval = -1;
3278 }
3279 }
3280 }
3281 else
3282 {
3283 /* Streamed data is different. Rewinding the channel
3284 * does no good. Rewinding the data will have an
3285 * effect, but it will be lagged based on how
3286 * much data is queued. Recommend users call Halt
3287 * before rewind if they want immediate results.
3288 */
3289 retval = Internal_SeekData(ALmixer_Channel_List[channel].almixer_data, msec);
3290 }
3291 }
3292 }
3293 /* The user wants to rewind all channels */
3294 else
3295 {
3296 ALint i;
3297 ALfloat sec_offset = msec / 1000.0f;
3298
3299 for(i=0; i<Number_of_Channels_global; i++)
3300 {
3301 /* only need to process channel if in use */
3302 if(ALmixer_Channel_List[i].channel_in_use)
3303 {
3304 /* What should I do? Do I just rewind the channel
3305 * or also rewind the data? Since the data is
3306 * shared, let's make it the user's responsibility
3307 * to rewind the data.
3308 */
3309 if(ALmixer_Channel_List[i].almixer_data->decoded_all)
3310 {
3311 alGetSourcei(
3312 ALmixer_Channel_List[i].alsource,
3313 AL_SOURCE_STATE, &state
3314 );
3315 if((error = alGetError()) != AL_NO_ERROR)
3316 {
3317 fprintf(stderr, "26Testing error: %s\n",
3318 alGetString(error));
3319 }
3320
3321 alSourcef(ALmixer_Channel_List[channel].alsource, AL_SEC_OFFSET, sec_offset);
3322 if((error = alGetError()) != AL_NO_ERROR)
3323 {
3324 ALmixer_SetError("%s",
3325 alGetString(error) );
3326 retval = -1;
3327 }
3328 /* Need to resume playback if it was originally playing */
3329 if(AL_PLAYING == state)
3330 {
3331 alSourcePlay(ALmixer_Channel_List[i].alsource);
3332 if((error = alGetError()) != AL_NO_ERROR)
3333 {
3334 ALmixer_SetError("%s",
3335 alGetString(error) );
3336 retval = -1;
3337 }
3338 }
3339 else if(AL_PAUSED == state)
3340 {
3341 /* HACK: The problem is that when paused, after
3342 * the Rewind, I can't get it off the INITIAL
3343 * state without restarting
3344 */
3345 alSourcePlay(ALmixer_Channel_List[i].alsource);
3346 if((error = alGetError()) != AL_NO_ERROR)
3347 {
3348 fprintf(stderr, "27Testing error: %s\n",
3349 alGetString(error));
3350 }
3351 alSourcePause(ALmixer_Channel_List[i].alsource);
3352 if((error = alGetError()) != AL_NO_ERROR)
3353 {
3354 ALmixer_SetError("%s",
3355 alGetString(error) );
3356 retval = -1;
3357 }
3358 }
3359 }
3360 else
3361 {
3362 /* Streamed data is different. Rewinding the channel
3363 * does no good. Rewinding the data will have an
3364 * effect, but it will be lagged based on how
3365 * much data is queued. Recommend users call Halt
3366 * before rewind if they want immediate results.
3367 */
3368 running_count += Internal_SeekData(ALmixer_Channel_List[i].almixer_data, msec);
3369 }
3370 }
3371 }
3372 }
3373 if(-1 == retval)
3374 {
3375 return -1;
3376 }
3377 else
3378 {
3379 return running_count;
3380 }
3381
3382 }
3383
3384 static ALint Internal_SeekSource(ALuint source, ALuint msec)
3385 {
3386 ALint channel;
3387 if(0 == source)
3388 {
3389 return Internal_SeekChannel(-1, msec);
3390 }
3391
3392 channel = Internal_GetChannel(source);
3393 if(-1 == channel)
3394 {
3395 ALmixer_SetError("Cannot seek source: %s", ALmixer_GetError());
3396 return 0;
3397 }
3398 return Internal_SeekChannel(channel, msec);
3399 }
3400
3401
3223 3402
3224 static ALint Internal_FadeInChannelTimed(ALint channel, ALmixer_Data* data, ALint loops, ALuint fade_ticks, ALint expire_ticks) 3403 static ALint Internal_FadeInChannelTimed(ALint channel, ALmixer_Data* data, ALint loops, ALuint fade_ticks, ALint expire_ticks)
3225 { 3404 {
3226 ALfloat value; 3405 ALfloat value;
3227 ALenum error; 3406 ALenum error;
4584 4763
4585 #ifdef ENABLE_ALMIXER_THREADS 4764 #ifdef ENABLE_ALMIXER_THREADS
4586 SDL_LockMutex(s_simpleLock); 4765 SDL_LockMutex(s_simpleLock);
4587 #endif 4766 #endif
4588 if(0 == ALmixer_Initialized) 4767 if(0 == ALmixer_Initialized)
4768 {
4769 #ifdef ENABLE_ALMIXER_THREADS
4770 SDL_UnlockMutex(s_simpleLock);
4771 #endif
4772 return 0;
4773 }
4774
4775 /* Bypass if in interruption event */
4776 if(NULL == alcGetCurrentContext())
4589 { 4777 {
4590 #ifdef ENABLE_ALMIXER_THREADS 4778 #ifdef ENABLE_ALMIXER_THREADS
4591 SDL_UnlockMutex(s_simpleLock); 4779 SDL_UnlockMutex(s_simpleLock);
4592 #endif 4780 #endif
4593 return 0; 4781 return 0;
4699 */ 4887 */
4700 delta_time = current_time - ALmixer_Channel_List[i].fade_start_time; 4888 delta_time = current_time - ALmixer_Channel_List[i].fade_start_time;
4701 t = (ALfloat) delta_time * ALmixer_Channel_List[i].fade_inv_time; 4889 t = (ALfloat) delta_time * ALmixer_Channel_List[i].fade_inv_time;
4702 current_volume = (1.0f-t) * ALmixer_Channel_List[i].fade_start_volume 4890 current_volume = (1.0f-t) * ALmixer_Channel_List[i].fade_start_volume
4703 + t * ALmixer_Channel_List[i].fade_end_volume; 4891 + t * ALmixer_Channel_List[i].fade_end_volume;
4892 /*
4704 fprintf(stderr, "start_vol=%f, end_vol:%f, current_volume: %f\n", ALmixer_Channel_List[i].fade_start_volume, ALmixer_Channel_List[i].fade_end_volume, current_volume); 4893 fprintf(stderr, "start_vol=%f, end_vol:%f, current_volume: %f\n", ALmixer_Channel_List[i].fade_start_volume, ALmixer_Channel_List[i].fade_end_volume, current_volume);
4705 4894 */
4706 /* Set the volume */ 4895 /* Set the volume */
4707 alSourcef(ALmixer_Channel_List[i].alsource, 4896 alSourcef(ALmixer_Channel_List[i].alsource,
4708 AL_GAIN, current_volume); 4897 AL_GAIN, current_volume);
4709 if((error = alGetError()) != AL_NO_ERROR) 4898 if((error = alGetError()) != AL_NO_ERROR)
4710 { 4899 {
5992 * means the SDL audio system will be disabled. It will not 6181 * means the SDL audio system will be disabled. It will not
5993 * be restored (in case SDL is not actually being used) so 6182 * be restored (in case SDL is not actually being used) so
5994 * the user will need to restart it if they need it after 6183 * the user will need to restart it if they need it after
5995 * OpenAL shuts down. 6184 * OpenAL shuts down.
5996 */ 6185 */
5997 ALboolean ALmixer_Init(ALuint frequency, ALint num_sources, ALuint refresh) 6186 ALboolean ALmixer_Init(ALuint frequency, ALuint num_sources, ALuint refresh)
5998 { 6187 {
5999 ALCdevice* dev; 6188 ALCdevice* dev;
6000 ALCcontext* context; 6189 ALCcontext* context;
6001 ALint i; 6190 ALint i;
6002 ALenum error; 6191 ALenum error;
6360 * (per source) at render timem at the expense of a larger memory footprint. 6549 * (per source) at render timem at the expense of a larger memory footprint.
6361 * 6550 *
6362 * This feature is toggled on/off by using the alDisable() & alEnable() APIs. This 6551 * This feature is toggled on/off by using the alDisable() & alEnable() APIs. This
6363 * setting will be applied to all subsequent 6552 * setting will be applied to all subsequent
6364 * calls to alBufferData(). 6553 * calls to alBufferData().
6554 *
6555 * Update: Some people keep reporting that they see the enable fail on Mac, but I can't reproduce it myself.
6556 * Rather than deal with it right now, I think I am going to make it an opt-in thing.
6365 */ 6557 */
6366 #ifdef __APPLE__ 6558 #if defined(__APPLE__) && defined(ALMIXER_USE_OSX_CONVERT_DATA_UPON_LOADING)
6367 /* 6559 /*
6560 iPhone is getting this enum, but is failing on the enable, so I guess I'll define it out.
6561 */
6368 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) 6562 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
6369 6563
6370 #else 6564 #else
6565 /* iOS reports this enum exists, but loading it always fails, so make it Mac only. */
6566 ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
6567 /*
6568 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
6569 */
6570 if(0 != convert_data_enum)
6571 {
6572 alEnable(convert_data_enum);
6573 }
6574 if( (AL_NO_ERROR != alGetError()) )
6575 {
6576 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6577 ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6578 }
6371 #endif 6579 #endif
6372 */ 6580
6373 ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING"); 6581 #endif /* __APPLE__ */
6374 /*
6375 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
6376 */
6377 if(0 != convert_data_enum)
6378 {
6379 alEnable(convert_data_enum);
6380 }
6381 if( (AL_NO_ERROR != alGetError()) )
6382 {
6383 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6384 ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6385 }
6386
6387 #endif
6388 6582
6389 6583
6390 6584
6391 6585
6392 ALmixer_Initialized = 1; 6586 ALmixer_Initialized = 1;
6393 6587
6394 if(num_sources <= 0) 6588 if(num_sources == 0)
6395 { 6589 {
6396 Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS; 6590 Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS;
6397 } 6591 }
6398 else 6592 else
6399 { 6593 {
6400 Number_of_Channels_global = num_sources; 6594 /* probably should make Number_of_Channels_global an ALuint, but need to cast which_channel all the time */
6595 Number_of_Channels_global = (ALint)num_sources;
6401 } 6596 }
6402 Number_of_Reserve_Channels_global = 0; 6597 Number_of_Reserve_Channels_global = 0;
6403 Is_Playing_global = 0; 6598 Is_Playing_global = 0;
6404 /* Set to Null in case system quit and was reinitialized */ 6599 /* Set to Null in case system quit and was reinitialized */
6405 Channel_Done_Callback = NULL; 6600 Channel_Done_Callback = NULL;
6899 * (per source) at render timem at the expense of a larger memory footprint. 7094 * (per source) at render timem at the expense of a larger memory footprint.
6900 * 7095 *
6901 * This feature is toggled on/off by using the alDisable() & alEnable() APIs. This 7096 * This feature is toggled on/off by using the alDisable() & alEnable() APIs. This
6902 * setting will be applied to all subsequent 7097 * setting will be applied to all subsequent
6903 * calls to alBufferData(). 7098 * calls to alBufferData().
7099 * Update: Some people keep reporting that they see the enable fail on Mac, but I can't reproduce it myself.
7100 * Rather than deal with it right now, I think I am going to make it an opt-in thing.
6904 */ 7101 */
6905 #ifdef __APPLE__ 7102 #if defined(__APPLE__) && defined(ALMIXER_USE_OSX_CONVERT_DATA_UPON_LOADING)
6906 /* 7103 /*
6907 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) 7104 iPhone is getting this enum, but is failing on the enable, so I guess I'll define it out.
7105 */
7106 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
6908 7107
6909 #else 7108 #else
6910 #endif 7109 /* iOS reports this enum exists, but loading it always fails, so make it Mac only. */
6911 */ 7110 ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING");
6912 ALenum convert_data_enum = alcGetEnumValue(dev, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING"); 7111 /*
6913 /* 7112 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum);
6914 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING=0x%x", convert_data_enum); 7113 */
6915 */ 7114 if(0 != convert_data_enum)
6916 if(0 != convert_data_enum) 7115 {
6917 { 7116 alEnable(convert_data_enum);
6918 alEnable(convert_data_enum); 7117 }
6919 } 7118 if( (AL_NO_ERROR != alGetError()) )
6920 if( (AL_NO_ERROR != alGetError()) ) 7119 {
6921 { 7120 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6922 fprintf(stderr, "ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed"); 7121 ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed");
6923 ALmixer_SetError("ALC_MAC_OSX_CONVERT_DATA_UPON_LOADING attempted but failed"); 7122 }
6924 } 7123 #endif
6925 #endif 7124 #endif
6926 7125
6927 return AL_TRUE; 7126 return AL_TRUE;
6928 } 7127 }
6929 7128
6930 7129
6931 ALboolean ALmixer_InitMixer(ALint num_sources) 7130 ALboolean ALmixer_InitMixer(ALuint num_sources)
6932 { 7131 {
6933 ALint i; 7132 ALint i;
6934 ALenum error; 7133 ALenum error;
6935 ALuint* source; 7134 ALuint* source;
6936 7135
6960 fprintf(stderr, "tError Test1: %s\n", ALmixer_GetError()); 7159 fprintf(stderr, "tError Test1: %s\n", ALmixer_GetError());
6961 fprintf(stderr, "tError Test2: %s\n", ALmixer_GetError()); 7160 fprintf(stderr, "tError Test2: %s\n", ALmixer_GetError());
6962 */ 7161 */
6963 #endif 7162 #endif
6964 7163
6965 if(num_sources <= 0) 7164 if(num_sources == 0)
6966 { 7165 {
6967 Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS; 7166 Number_of_Channels_global = ALMIXER_DEFAULT_NUM_CHANNELS;
6968 } 7167 }
6969 else 7168 else
6970 { 7169 {
6971 Number_of_Channels_global = num_sources; 7170 Number_of_Channels_global = (ALint)num_sources;
6972 } 7171 }
6973 Number_of_Reserve_Channels_global = 0; 7172 Number_of_Reserve_Channels_global = 0;
6974 Is_Playing_global = 0; 7173 Is_Playing_global = 0;
6975 /* Set to Null in case system quit and was reinitialized */ 7174 /* Set to Null in case system quit and was reinitialized */
6976 Channel_Done_Callback = NULL; 7175 Channel_Done_Callback = NULL;
7103 */ 7302 */
7104 free(source); 7303 free(source);
7105 return AL_TRUE; 7304 return AL_TRUE;
7106 } 7305 }
7107 7306
7108 7307 void ALmixer_BeginInterruption()
7308 {
7309 #ifdef ENABLE_ALMIXER_THREADS
7310 SDL_LockMutex(s_simpleLock);
7311 #endif
7312 s_interruptionContext = alcGetCurrentContext();
7313 if(NULL != s_interruptionContext)
7314 {
7315 /* iOS alcSuspendContext is a no-op */
7316 alcSuspendContext(s_interruptionContext);
7317 alcMakeContextCurrent(NULL);
7318 }
7319 #ifdef ENABLE_ALMIXER_THREADS
7320 SDL_UnlockMutex(s_simpleLock);
7321 #endif
7322 }
7323
7324 void ALmixer_EndInterruption()
7325 {
7326 #ifdef ENABLE_ALMIXER_THREADS
7327 SDL_LockMutex(s_simpleLock);
7328 #endif
7329
7330 /* Note: iOS, you need to set the AudioSession active.
7331 * But if the AudioSession is not initialized, this SetActive
7332 * call fails.
7333 * So this is probably better if calling app sets this.
7334 */
7335 /*
7336 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
7337 OSStatus the_error = AudioSessionSetActive(true);
7338 if(noErr != the_error)
7339 {
7340 fprintf(stderr, "Error setting audio session active! %d\n", the_error);
7341 }
7342 #endif
7343 */
7344
7345 if(NULL != s_interruptionContext)
7346 {
7347 alcMakeContextCurrent(s_interruptionContext);
7348 alcProcessContext(s_interruptionContext);
7349 s_interruptionContext = NULL;
7350 }
7351 #ifdef ENABLE_ALMIXER_THREADS
7352 SDL_UnlockMutex(s_simpleLock);
7353 #endif
7354 }
7109 7355
7110 /* Keep the return value void to allow easy use with 7356 /* Keep the return value void to allow easy use with
7111 * atexit() 7357 * atexit()
7112 */ 7358 */
7113 void ALmixer_Quit() 7359 void ALmixer_Quit()
7121 return; 7367 return;
7122 } 7368 }
7123 #ifdef ENABLE_ALMIXER_THREADS 7369 #ifdef ENABLE_ALMIXER_THREADS
7124 SDL_LockMutex(s_simpleLock); 7370 SDL_LockMutex(s_simpleLock);
7125 #endif 7371 #endif
7372
7373 /* Several things we need to do:
7374 First, we need to check if we are in an interruption.
7375 If so, we need to reactivate the alcContext so we can call OpenAL functions.
7376 Next, we should delete the OpenAL sources.
7377 Next, we need to free all the sound data via ALmixer_FreeData().
7378 Finally, we can delete the OpenAL context.
7379 */
7380
7381 context = alcGetCurrentContext();
7382 if(NULL == context)
7383 {
7384 /* We might have an interruption event where the current context is NULL */
7385 if(NULL == s_interruptionContext)
7386 {
7387 /* Nothing left to try. I think we're done. */
7388 fprintf(stderr, "ALmixer_Quit: Assertion Error. Expecting to find an OpenAL context, but could not find one.\n");
7389 return;
7390 }
7391 else
7392 {
7393 context = s_interruptionContext;
7394 /* reactivate the context so we can call OpenAL functions */
7395 alcMakeContextCurrent(context);
7396 s_interruptionContext = NULL;
7397 }
7398 }
7399
7126 /* Shutdown everything before closing context */ 7400 /* Shutdown everything before closing context */
7127 Internal_HaltChannel(-1, AL_FALSE); 7401 Internal_HaltChannel(-1, AL_FALSE);
7128 7402
7129 /* This flag will cause the thread to terminate */ 7403 /* This flag will cause the thread to terminate */
7130 ALmixer_Initialized = 0; 7404 ALmixer_Initialized = 0;
7132 SDL_UnlockMutex(s_simpleLock); 7406 SDL_UnlockMutex(s_simpleLock);
7133 SDL_WaitThread(Stream_Thread_global, NULL); 7407 SDL_WaitThread(Stream_Thread_global, NULL);
7134 7408
7135 SDL_DestroyMutex(s_simpleLock); 7409 SDL_DestroyMutex(s_simpleLock);
7136 #endif 7410 #endif
7137 7411
7138 /* Delete all the OpenAL sources */ 7412 /* Delete all the OpenAL sources */
7139 for(i=0; i<Number_of_Channels_global; i++) 7413 for(i=0; i<Number_of_Channels_global; i++)
7140 { 7414 {
7141 alDeleteSources(1, &ALmixer_Channel_List[i].alsource); 7415 alDeleteSources(1, &ALmixer_Channel_List[i].alsource);
7142 } 7416 }
7143 /* Delete all the channels */ 7417 /* Delete all the channels */
7144 free(ALmixer_Channel_List); 7418 free(ALmixer_Channel_List);
7145 free(Source_Map_List); 7419 free(Source_Map_List);
7146 7420
7147 /* Reset the Number_of_Channels just in case somebody 7421 /* Reset the Number_of_Channels just in case somebody
7148 * tries using a ALmixer function. 7422 * tries using a ALmixer function.
7149 * I probably should put "Initialized" checks everywhere, 7423 * I probably should put "Initialized" checks everywhere,
7150 * but I'm too lazy at the moment. 7424 * but I'm too lazy at the moment.
7151 */ 7425 */
7152 Number_of_Channels_global = 0; 7426 Number_of_Channels_global = 0;
7153 7427
7154 context = alcGetCurrentContext(); 7428
7155 if(NULL == context)
7156 {
7157 return;
7158 }
7159 /* Need to get the device before I close the context */
7160 dev = alcGetContextsDevice(context);
7161 alcDestroyContext(context);
7162
7163 if(NULL == dev)
7164 {
7165 return;
7166 }
7167 alcCloseDevice(dev);
7168
7169 /* Delete the list of ALmixerData's before Sound_Quit deletes 7429 /* Delete the list of ALmixerData's before Sound_Quit deletes
7170 * its own underlying information and I potentially have dangling pointers. 7430 * its own underlying information and I potentially have dangling pointers.
7171 */ 7431 */
7172 while(LinkedList_Size(s_listOfALmixerData) > 0) 7432 while(LinkedList_Size(s_listOfALmixerData) > 0)
7173 { 7433 {
7175 ALmixer_Data* almixer_data = LinkedList_Back(s_listOfALmixerData); 7435 ALmixer_Data* almixer_data = LinkedList_Back(s_listOfALmixerData);
7176 ALmixer_FreeData(almixer_data); 7436 ALmixer_FreeData(almixer_data);
7177 } 7437 }
7178 LinkedList_Free(s_listOfALmixerData); 7438 LinkedList_Free(s_listOfALmixerData);
7179 s_listOfALmixerData = NULL; 7439 s_listOfALmixerData = NULL;
7180 7440
7441
7442 /* Need to get the device before I close the context */
7443 dev = alcGetContextsDevice(context);
7444
7445 alcMakeContextCurrent(NULL);
7446 alcDestroyContext(context);
7447
7448 if(NULL == dev)
7449 {
7450 return;
7451 }
7452 alcCloseDevice(dev);
7453
7181 Sound_Quit(); 7454 Sound_Quit();
7182 7455
7183 #ifdef ALMIXER_COMPILE_WITHOUT_SDL 7456 #ifdef ALMIXER_COMPILE_WITHOUT_SDL
7184 /* Remember: ALmixer_SetError/GetError calls will not work while this is gone. */ 7457 /* Remember: ALmixer_SetError/GetError calls will not work while this is gone. */
7185 TError_FreeErrorPool(s_ALmixerErrorPool); 7458 TError_FreeErrorPool(s_ALmixerErrorPool);
7625 /* This information is for predecoded. 7898 /* This information is for predecoded.
7626 * Set to 0, since we don't know. 7899 * Set to 0, since we don't know.
7627 */ 7900 */
7628 ret_data->total_bytes = 0; 7901 ret_data->total_bytes = 0;
7629 7902
7903 ret_data->total_time = Sound_GetDuration(sample);
7904
7630 /* Create buffers for data 7905 /* Create buffers for data
7631 */ 7906 */
7632 ret_data->buffer = (ALuint*)malloc( sizeof(ALuint) * max_queue_buffers); 7907 ret_data->buffer = (ALuint*)malloc( sizeof(ALuint) * max_queue_buffers);
7633 if(NULL == ret_data->buffer) 7908 if(NULL == ret_data->buffer)
7634 { 7909 {
8153 ALenum error; 8428 ALenum error;
8154 if(NULL == data) 8429 if(NULL == data)
8155 { 8430 {
8156 return; 8431 return;
8157 } 8432 }
8433
8434 /* Bypass if in interruption event */
8435 if(NULL == alcGetCurrentContext())
8436 {
8437 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");
8438 return;
8439 }
8158 8440
8159 if(data->decoded_all) 8441 if(data->decoded_all)
8160 { 8442 {
8161 /* If access_data was enabled, then the Sound_Sample* 8443 /* If access_data was enabled, then the Sound_Sample*
8162 * still exists. We need to free it 8444 * still exists. We need to free it
8164 if(data->sample != NULL) 8446 if(data->sample != NULL)
8165 { 8447 {
8166 Sound_FreeSample(data->sample); 8448 Sound_FreeSample(data->sample);
8167 } 8449 }
8168 alDeleteBuffers(1, data->buffer); 8450 alDeleteBuffers(1, data->buffer);
8169 if((error = alGetError()) != AL_NO_ERROR) 8451 if((error = alGetError()) != AL_NO_ERROR)
8170 { 8452 {
8171 fprintf(stderr, "70Testing error: %s\n", 8453 fprintf(stderr, "ALmixer_FreeData: alDeleteBuffers failed. %s\n", alGetString(error));
8172 alGetString(error)); 8454 }
8173 }
8174
8175 } 8455 }
8176 else 8456 else
8177 { 8457 {
8178 ALuint i; 8458 ALuint i;
8179 8459
8191 CircularQueueUnsignedInt_FreeQueue(data->circular_buffer_queue); 8471 CircularQueueUnsignedInt_FreeQueue(data->circular_buffer_queue);
8192 } 8472 }
8193 8473
8194 Sound_FreeSample(data->sample); 8474 Sound_FreeSample(data->sample);
8195 alDeleteBuffers(data->max_queue_buffers, data->buffer); 8475 alDeleteBuffers(data->max_queue_buffers, data->buffer);
8196 if((error = alGetError()) != AL_NO_ERROR) 8476 if((error = alGetError()) != AL_NO_ERROR)
8197 { 8477 {
8198 fprintf(stderr, "71Testing error: %s\n", 8478 fprintf(stderr, "ALmixer_FreeData: alDeleteBuffers failed. %s\n", alGetString(error));
8199 alGetString(error)); 8479 }
8200 }
8201 } 8480 }
8202 free(data->buffer); 8481 free(data->buffer);
8203 8482
8204 LinkedList_Remove(s_listOfALmixerData, 8483 LinkedList_Remove(s_listOfALmixerData,
8205 LinkedList_Find(s_listOfALmixerData, data, NULL) 8484 LinkedList_Find(s_listOfALmixerData, data, NULL)
8212 { 8491 {
8213 if(NULL == data) 8492 if(NULL == data)
8214 { 8493 {
8215 return -1; 8494 return -1;
8216 } 8495 }
8496
8217 return data->total_time; 8497 return data->total_time;
8218 } 8498 }
8219 8499
8220 /* This function will look up the source for the corresponding channel */ 8500 /* This function will look up the source for the corresponding channel */
8221 /* Must return 0 on error instead of -1 because of unsigned int */ 8501 /* Must return 0 on error instead of -1 because of unsigned int */
8500 SDL_UnlockMutex(s_simpleLock); 8780 SDL_UnlockMutex(s_simpleLock);
8501 #endif 8781 #endif
8502 return retval; 8782 return retval;
8503 } 8783 }
8504 8784
8785 ALint ALmixer_SeekChannel(ALint channel, ALuint msec)
8786 {
8787 ALint retval;
8788 #ifdef ENABLE_ALMIXER_THREADS
8789 SDL_LockMutex(s_simpleLock);
8790 #endif
8791 retval = Internal_SeekChannel(channel, msec);
8792 #ifdef ENABLE_ALMIXER_THREADS
8793 SDL_UnlockMutex(s_simpleLock);
8794 #endif
8795 return retval;
8796 }
8797
8798 ALint ALmixer_SeekSource(ALuint source, ALuint msec)
8799 {
8800 ALint retval;
8801 #ifdef ENABLE_ALMIXER_THREADS
8802 SDL_LockMutex(s_simpleLock);
8803 #endif
8804 retval = Internal_SeekSource(source, msec);
8805 #ifdef ENABLE_ALMIXER_THREADS
8806 SDL_UnlockMutex(s_simpleLock);
8807 #endif
8808 return retval;
8809 }
8810
8505 ALint ALmixer_FadeInChannelTimed(ALint channel, ALmixer_Data* data, ALint loops, ALuint fade_ticks, ALint expire_ticks) 8811 ALint ALmixer_FadeInChannelTimed(ALint channel, ALmixer_Data* data, ALint loops, ALuint fade_ticks, ALint expire_ticks)
8506 { 8812 {
8507 ALint retval; 8813 ALint retval;
8508 #ifdef ENABLE_ALMIXER_THREADS 8814 #ifdef ENABLE_ALMIXER_THREADS
8509 SDL_LockMutex(s_simpleLock); 8815 SDL_LockMutex(s_simpleLock);