Mercurial > almixer_isolated
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(¤t_time); | 320 QueryPerformanceCounter(¤t_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, ¤t_time); | 325 clock_gettime(CLOCK_MONOTONIC, ¤t_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); |