diff options
author | Sven Gothel <[email protected]> | 2014-06-10 05:30:02 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-06-10 05:30:02 +0200 |
commit | f95bf4457fbc31112fa82dacbc1b7e094b9fd1cf (patch) | |
tree | 965ba5b8e6fc8e6bfe7a981c1dfb1179bb9adcde | |
parent | 7297c3214a4c648aaee81a9877da15b88f798197 (diff) | |
parent | c07fb7b45c1e345dbaa439882250de5b2213026f (diff) |
Merge branch 'UPSTREAM' into UPSTREAM_MERGE
107 files changed, 10857 insertions, 6540 deletions
@@ -1,6 +1,7 @@ build winbuild win64build +include/SLES include/sndio.h include/sys openal-soft.kdev4 @@ -39,6 +39,10 @@ #include "bs2b.h" #include "alu.h" +#include "compat.h" +#include "threads.h" +#include "alstring.h" + #include "backends/base.h" #include "midi/base.h" @@ -79,10 +83,10 @@ static struct BackendInfo BackendList[] = { { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs }, #endif #ifdef HAVE_MMDEVAPI - { "mmdevapi", NULL, alcMMDevApiInit, alcMMDevApiDeinit, alcMMDevApiProbe, EmptyFuncs }, + { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, #endif #ifdef HAVE_DSOUND - { "dsound", NULL, alcDSoundInit, alcDSoundDeinit, alcDSoundProbe, EmptyFuncs }, + { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, #endif #ifdef HAVE_WINMM { "winmm", NULL, alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs }, @@ -445,6 +449,8 @@ static const ALCenums enumeration[] = { DECL(AL_FORMAT_STEREO_DOUBLE_EXT), DECL(AL_FORMAT_MONO_IMA4), DECL(AL_FORMAT_STEREO_IMA4), + DECL(AL_FORMAT_MONO_MSADPCM_SOFT), + DECL(AL_FORMAT_STEREO_MSADPCM_SOFT), DECL(AL_FORMAT_QUAD8_LOKI), DECL(AL_FORMAT_QUAD16_LOKI), DECL(AL_FORMAT_QUAD8), @@ -523,6 +529,8 @@ static const ALCenums enumeration[] = { DECL(AL_BYTE_LENGTH_SOFT), DECL(AL_SAMPLE_LENGTH_SOFT), DECL(AL_SEC_LENGTH_SOFT), + DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), + DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), DECL(AL_UNUSED), DECL(AL_PENDING), @@ -557,14 +565,19 @@ static const ALCenums enumeration[] = { DECL(AL_FILTER_TYPE), DECL(AL_FILTER_NULL), DECL(AL_FILTER_LOWPASS), -#if 0 DECL(AL_FILTER_HIGHPASS), DECL(AL_FILTER_BANDPASS), -#endif DECL(AL_LOWPASS_GAIN), DECL(AL_LOWPASS_GAINHF), + DECL(AL_HIGHPASS_GAIN), + DECL(AL_HIGHPASS_GAINLF), + + DECL(AL_BANDPASS_GAIN), + DECL(AL_BANDPASS_GAINHF), + DECL(AL_BANDPASS_GAINLF), + DECL(AL_EFFECT_TYPE), DECL(AL_EFFECT_NULL), DECL(AL_EFFECT_REVERB), @@ -691,11 +704,9 @@ static const ALCchar alcErrOutOfMemory[] = "Out of Memory"; /* Enumerated device names */ static const ALCchar alcDefaultName[] = "OpenAL Soft\0"; -static ALCchar *alcAllDevicesList; -static ALCchar *alcCaptureDeviceList; -/* Sizes only include the first ending null character, not the second */ -static size_t alcAllDevicesListSize; -static size_t alcCaptureDeviceListSize; + +static al_string alcAllDevicesList; +static al_string alcCaptureDeviceList; /* Default is always the first in the list */ static ALCchar *alcDefaultAllDevicesSpecifier; @@ -706,14 +717,15 @@ static const ALchar alExtList[] = "AL_EXT_ALAW AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 " "AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW " "AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model " - "AL_LOKI_quadriphonic AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data " - "AL_SOFT_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points " - "AL_SOFT_source_latency"; + "AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_buffer_samples " + "AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels " + "AL_SOFT_loop_points AL_SOFTX_MSADPCM AL_SOFT_source_latency " + "AL_SOFTX_source_length"; static volatile ALCenum LastNullDeviceError = ALC_NO_ERROR; /* Thread-local current context */ -static althread_key_t LocalContext; +static altss_t LocalContext; /* Process-wide current context */ static ALCcontext *volatile GlobalContext = NULL; @@ -731,7 +743,7 @@ enum LogLevel LogLevel = LogError; static ALCboolean TrapALCError = ALC_FALSE; /* One-time configuration init control */ -static althread_once_t alc_config_once = ALTHREAD_ONCE_INIT; +static alonce_flag alc_config_once = AL_ONCE_FLAG_INIT; /* Default effect that applies to sources that don't have an effect on send 0 */ static ALeffect DefaultEffect; @@ -760,15 +772,16 @@ static const ALCint alcEFXMinorVersion = 0; ************************************************/ static ALCdevice *volatile DeviceList = NULL; -static CRITICAL_SECTION ListLock; - -static void LockLists(void) +static almtx_t ListLock; +static inline void LockLists(void) { - EnterCriticalSection(&ListLock); + int lockret = almtx_lock(&ListLock); + assert(lockret == althrd_success); } -static void UnlockLists(void) +static inline void UnlockLists(void) { - LeaveCriticalSection(&ListLock); + int unlockret = almtx_unlock(&ListLock); + assert(unlockret == althrd_success); } /************************************************ @@ -779,34 +792,19 @@ static void alc_init(void); static void alc_deinit(void); static void alc_deinit_safe(void); -UIntMap TlsDestructor; - #ifndef AL_LIBTYPE_STATIC -BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) +BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved) { - ALsizei i; - - // Perform actions based on the reason for calling. - switch(ul_reason_for_call) + switch(reason) { case DLL_PROCESS_ATTACH: /* Pin the DLL so we won't get unloaded until the process terminates */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (WCHAR*)hModule, &hModule); - InitUIntMap(&TlsDestructor, ~0); alc_init(); break; case DLL_THREAD_DETACH: - LockUIntMapRead(&TlsDestructor); - for(i = 0;i < TlsDestructor.size;i++) - { - void *ptr = althread_getspecific(TlsDestructor.array[i].key); - void (*callback)(void*) = (void(*)(void*))TlsDestructor.array[i].value; - if(ptr && callback) - callback(ptr); - } - UnlockUIntMapRead(&TlsDestructor); break; case DLL_PROCESS_DETACH: @@ -814,7 +812,6 @@ BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserv alc_deinit(); else alc_deinit_safe(); - ResetUIntMap(&TlsDestructor); break; } return TRUE; @@ -855,9 +852,13 @@ static void ReleaseThreadCtx(void *ptr); static void alc_init(void) { const char *str; + int ret; LogFile = stderr; + AL_STRING_INIT(alcAllDevicesList); + AL_STRING_INIT(alcCaptureDeviceList); + str = getenv("__ALSOFT_HALF_ANGLE_CONES"); if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) ConeScale *= 0.5f; @@ -866,8 +867,12 @@ static void alc_init(void) if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) ZScale *= -1.0f; - althread_key_create(&LocalContext, ReleaseThreadCtx); - InitializeCriticalSection(&ListLock); + ret = altss_create(&LocalContext, ReleaseThreadCtx); + assert(ret == althrd_success); + + ret = almtx_init(&ListLock, almtx_recursive); + assert(ret == althrd_success); + ThunkInit(); } @@ -889,7 +894,7 @@ static void alc_initconfig(void) str = getenv("ALSOFT_LOGFILE"); if(str && str[0]) { - FILE *logfile = fopen(str, "wt"); + FILE *logfile = al_fopen(str, "wt"); if(logfile) LogFile = logfile; else ERR("Failed to open log file '%s'\n", str); } @@ -904,8 +909,12 @@ static void alc_initconfig(void) ReadALConfig(); capfilter = 0; -#ifdef HAVE_SSE +#if defined(HAVE_SSE4_1) + capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE2) capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) + capfilter |= CPU_CAP_SSE; #endif #ifdef HAVE_NEON capfilter |= CPU_CAP_NEON; @@ -935,6 +944,8 @@ static void alc_initconfig(void) capfilter &= ~CPU_CAP_SSE; else if(len == 4 && strncasecmp(str, "sse2", len) == 0) capfilter &= ~CPU_CAP_SSE2; + else if(len == 6 && strncasecmp(str, "sse4.1", len) == 0) + capfilter &= ~CPU_CAP_SSE4_1; else if(len == 4 && strncasecmp(str, "neon", len) == 0) capfilter &= ~CPU_CAP_NEON; else @@ -1140,7 +1151,7 @@ static void alc_initconfig(void) if((str && str[0]) || ConfigValueStr(NULL, "default-reverb", &str)) LoadReverbPreset(str, &DefaultEffect); } -#define DO_INITCONFIG() althread_once(&alc_config_once, alc_initconfig) +#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig) /************************************************ @@ -1150,10 +1161,8 @@ static void alc_cleanup(void) { ALCdevice *dev; - free(alcAllDevicesList); alcAllDevicesList = NULL; - alcAllDevicesListSize = 0; - free(alcCaptureDeviceList); alcCaptureDeviceList = NULL; - alcCaptureDeviceListSize = 0; + AL_STRING_DEINIT(alcAllDevicesList); + AL_STRING_DEINIT(alcCaptureDeviceList); free(alcDefaultAllDevicesSpecifier); alcDefaultAllDevicesSpecifier = NULL; @@ -1180,8 +1189,8 @@ static void alc_deinit_safe(void) FreeALConfig(); ThunkExit(); - DeleteCriticalSection(&ListLock); - althread_key_delete(LocalContext); + almtx_destroy(&ListLock); + altss_delete(LocalContext); if(LogFile != stderr) fclose(LogFile); @@ -1219,14 +1228,12 @@ static void alc_deinit(void) /************************************************ * Device enumeration ************************************************/ -static void ProbeList(ALCchar **list, size_t *listsize, enum DevProbe type) +static void ProbeDevices(al_string *list, enum DevProbe type) { DO_INITCONFIG(); LockLists(); - free(*list); - *list = NULL; - *listsize = 0; + al_string_clear(list); if(type == ALL_DEVICE_PROBE && (PlaybackBackend.Probe || PlaybackBackend.getFactory)) { @@ -1250,42 +1257,24 @@ static void ProbeList(ALCchar **list, size_t *listsize, enum DevProbe type) } UnlockLists(); } - static void ProbeAllDevicesList(void) -{ ProbeList(&alcAllDevicesList, &alcAllDevicesListSize, ALL_DEVICE_PROBE); } +{ ProbeDevices(&alcAllDevicesList, ALL_DEVICE_PROBE); } static void ProbeCaptureDeviceList(void) -{ ProbeList(&alcCaptureDeviceList, &alcCaptureDeviceListSize, CAPTURE_DEVICE_PROBE); } - +{ ProbeDevices(&alcCaptureDeviceList, CAPTURE_DEVICE_PROBE); } -static void AppendList(const ALCchar *name, ALCchar **List, size_t *ListSize) +static void AppendDevice(const ALCchar *name, al_string *devnames) { size_t len = strlen(name); - void *temp; - - if(len == 0) - return; - - temp = realloc(*List, (*ListSize) + len + 2); - if(!temp) + if(len > 0) { - ERR("Realloc failed to add %s!\n", name); - return; + al_string_append_range(devnames, name, name+len); + al_string_append_char(devnames, '\0'); } - *List = temp; - - memcpy((*List)+(*ListSize), name, len+1); - *ListSize += len+1; - (*List)[*ListSize] = 0; } - -#define DECL_APPEND_LIST_FUNC(type) \ -void Append##type##List(const ALCchar *name) \ -{ AppendList(name, &alc##type##List, &alc##type##ListSize); } - -DECL_APPEND_LIST_FUNC(AllDevices) -DECL_APPEND_LIST_FUNC(CaptureDevice) - -#undef DECL_APPEND_LIST_FUNC +void AppendAllDevicesList(const ALCchar *name) +{ AppendDevice(name, &alcAllDevicesList); } +void AppendCaptureDeviceList(const ALCchar *name) +{ AppendDevice(name, &alcCaptureDeviceList); } /************************************************ @@ -1350,8 +1339,8 @@ ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) return 0; } -static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans, - enum DevFmtType *type) +DECL_CONST static ALboolean DecomposeDevFormat(ALenum format, + enum DevFmtChannels *chans, enum DevFmtType *type) { static const struct { ALenum format; @@ -1397,7 +1386,7 @@ static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans, return AL_FALSE; } -static ALCboolean IsValidALCType(ALCenum type) +DECL_CONST static ALCboolean IsValidALCType(ALCenum type) { switch(type) { @@ -1413,7 +1402,7 @@ static ALCboolean IsValidALCType(ALCenum type) return ALC_FALSE; } -static ALCboolean IsValidALCChannels(ALCenum channels) +DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels) { switch(channels) { @@ -1603,7 +1592,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) enum DevFmtType oldType; ALCuint oldFreq; FPUCtl oldMode; - ALuint i; // Check for attributes if(device->Type == Loopback) @@ -1791,16 +1779,25 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->Frequency, device->UpdateSize, device->NumUpdates); + if(device->Type != Loopback) + { + int nohrtf = !(device->Flags&DEVICE_HRTF_REQUEST); + if(GetConfigValueBool(NULL, "hrtf", !nohrtf)) + device->Flags |= DEVICE_HRTF_REQUEST; + else + device->Flags &= ~DEVICE_HRTF_REQUEST; + } if((device->Flags&DEVICE_HRTF_REQUEST)) { enum DevFmtChannels chans; ALCuint freq; - - FindHrtfFormat(device, &chans, &freq); - device->Frequency = freq; - device->FmtChans = chans; - device->Flags |= DEVICE_CHANNELS_REQUEST | - DEVICE_FREQUENCY_REQUEST; + if(FindHrtfFormat(device, &chans, &freq)) + { + device->Frequency = freq; + device->FmtChans = chans; + device->Flags |= DEVICE_CHANNELS_REQUEST | + DEVICE_FREQUENCY_REQUEST; + } } if(V0(device->Backend,reset)() == ALC_FALSE) @@ -1831,22 +1828,9 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) aluInitPanning(device); - for(i = 0;i < MaxChannels;i++) - { - device->ClickRemoval[i] = 0.0f; - device->PendingClicks[i] = 0.0f; - } - V(device->Synth,update)(device); device->Hrtf = NULL; - if(device->Type != Loopback && ConfigValueExists(NULL, "hrtf")) - { - if(GetConfigValueBool(NULL, "hrtf", AL_FALSE)) - device->Flags |= DEVICE_HRTF_REQUEST; - else - device->Flags &= ~DEVICE_HRTF_REQUEST; - } if((device->Flags&DEVICE_HRTF_REQUEST)) { device->Hrtf = GetHrtf(device); @@ -1924,11 +1908,26 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) source->Send[s].GainHF = 1.0f; s++; } - source->NeedsUpdate = AL_FALSE; - ALsource_Update(source, context); + source->NeedsUpdate = AL_TRUE; } UnlockUIntMapRead(&context->SourceMap); + for(pos = 0;pos < context->ActiveSourceCount;pos++) + { + ALactivesource *src = context->ActiveSources[pos]; + ALsource *source = src->Source; + ALuint s = device->NumAuxSends; + while(s < MAX_SENDS) + { + src->Send[s].Moving = AL_FALSE; + src->Send[s].Counter = 0; + s++; + } + + src->Update(src, context); + source->NeedsUpdate = AL_FALSE; + } + context = context->next; } if(device->DefaultSlot) @@ -2029,8 +2028,7 @@ static ALCvoid FreeDevice(ALCdevice *device) free(device->Bs2b); device->Bs2b = NULL; - free(device->DeviceName); - device->DeviceName = NULL; + AL_STRING_DEINIT(device->DeviceName); al_free(device); } @@ -2038,14 +2036,14 @@ static ALCvoid FreeDevice(ALCdevice *device) void ALCdevice_IncRef(ALCdevice *device) { - RefCount ref; + uint ref; ref = IncrementRef(&device->ref); TRACEREF("%p increasing refcount to %u\n", device, ref); } void ALCdevice_DecRef(ALCdevice *device) { - RefCount ref; + uint ref; ref = DecrementRef(&device->ref); TRACEREF("%p decreasing refcount to %u\n", device, ref); if(ref == 0) FreeDevice(device); @@ -2131,6 +2129,8 @@ static ALvoid InitContext(ALCcontext *Context) */ static ALCvoid FreeContext(ALCcontext *context) { + ALsizei i; + TRACE("%p\n", context); if(context->SourceMap.size > 0) @@ -2147,15 +2147,17 @@ static ALCvoid FreeContext(ALCcontext *context) } ResetUIntMap(&context->EffectSlotMap); - context->ActiveSourceCount = 0; + for(i = 0;i < context->MaxActiveSources;i++) + { + al_free(context->ActiveSources[i]); + context->ActiveSources[i] = NULL; + } free(context->ActiveSources); context->ActiveSources = NULL; + context->ActiveSourceCount = 0; context->MaxActiveSources = 0; - context->ActiveEffectSlotCount = 0; - free(context->ActiveEffectSlots); - context->ActiveEffectSlots = NULL; - context->MaxActiveEffectSlots = 0; + VECTOR_DEINIT(context->ActiveAuxSlots); ALCdevice_DecRef(context->Device); context->Device = NULL; @@ -2174,21 +2176,21 @@ static void ReleaseContext(ALCcontext *context, ALCdevice *device) { ALCcontext *volatile*tmp_ctx; - if(althread_getspecific(LocalContext) == context) + if(altss_get(LocalContext) == context) { WARN("%p released while current on thread\n", context); - althread_setspecific(LocalContext, NULL); + altss_set(LocalContext, NULL); ALCcontext_DecRef(context); } - if(CompExchangePtr((XchgPtr*)&GlobalContext, context, NULL)) + if(CompExchangePtr((XchgPtr*)&GlobalContext, context, NULL) == context) ALCcontext_DecRef(context); ALCdevice_Lock(device); tmp_ctx = &device->ContextList; while(*tmp_ctx) { - if(CompExchangePtr((XchgPtr*)tmp_ctx, context, context->next)) + if(CompExchangePtr((XchgPtr*)tmp_ctx, context, context->next) == context) break; tmp_ctx = &(*tmp_ctx)->next; } @@ -2199,14 +2201,14 @@ static void ReleaseContext(ALCcontext *context, ALCdevice *device) void ALCcontext_IncRef(ALCcontext *context) { - RefCount ref; + uint ref; ref = IncrementRef(&context->ref); TRACEREF("%p increasing refcount to %u\n", context, ref); } void ALCcontext_DecRef(ALCcontext *context) { - RefCount ref; + uint ref; ref = DecrementRef(&context->ref); TRACEREF("%p decreasing refcount to %u\n", context, ref); if(ref == 0) FreeContext(context); @@ -2258,7 +2260,7 @@ ALCcontext *GetContextRef(void) { ALCcontext *context; - context = althread_getspecific(LocalContext); + context = altss_get(LocalContext); if(context) ALCcontext_IncRef(context); else @@ -2356,26 +2358,26 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para case ALC_ALL_DEVICES_SPECIFIER: if(VerifyDevice(Device)) { - value = Device->DeviceName; + value = al_string_get_cstr(Device->DeviceName); ALCdevice_DecRef(Device); } else { ProbeAllDevicesList(); - value = alcAllDevicesList; + value = al_string_get_cstr(alcAllDevicesList); } break; case ALC_CAPTURE_DEVICE_SPECIFIER: if(VerifyDevice(Device)) { - value = Device->DeviceName; + value = al_string_get_cstr(Device->DeviceName); ALCdevice_DecRef(Device); } else { ProbeCaptureDeviceList(); - value = alcCaptureDeviceList; + value = al_string_get_cstr(alcCaptureDeviceList); } break; @@ -2385,14 +2387,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para break; case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: - if(!alcAllDevicesList) + if(al_string_empty(alcAllDevicesList)) ProbeAllDevicesList(); Device = VerifyDevice(Device); free(alcDefaultAllDevicesSpecifier); - alcDefaultAllDevicesSpecifier = strdup(alcAllDevicesList ? - alcAllDevicesList : ""); + alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList)); if(!alcDefaultAllDevicesSpecifier) alcSetError(Device, ALC_OUT_OF_MEMORY); @@ -2401,14 +2402,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para break; case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: - if(!alcCaptureDeviceList) + if(al_string_empty(alcCaptureDeviceList)) ProbeCaptureDeviceList(); Device = VerifyDevice(Device); free(alcCaptureDefaultDeviceSpecifier); - alcCaptureDefaultDeviceSpecifier = strdup(alcCaptureDeviceList ? - alcCaptureDeviceList : ""); + alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcAllDevicesList)); if(!alcCaptureDefaultDeviceSpecifier) alcSetError(Device, ALC_OUT_OF_MEMORY); @@ -2864,15 +2864,17 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin return NULL; } - ALContext = calloc(1, sizeof(ALCcontext)+15+sizeof(ALlistener)); + ALContext = calloc(1, sizeof(ALCcontext)+sizeof(ALlistener)); if(ALContext) { - ALContext->ref = 1; - ALContext->Listener = (ALlistener*)(((ALintptrEXT)(ALContext+1)+15)&~15); + InitRef(&ALContext->ref, 1); + ALContext->Listener = (ALlistener*)ALContext->_listener_mem; + + VECTOR_INIT(ALContext->ActiveAuxSlots); ALContext->MaxActiveSources = 256; - ALContext->ActiveSources = malloc(sizeof(ALContext->ActiveSources[0]) * - ALContext->MaxActiveSources); + ALContext->ActiveSources = calloc(ALContext->MaxActiveSources, + sizeof(ALContext->ActiveSources[0])); } if(!ALContext || !ALContext->ActiveSources) { @@ -2883,8 +2885,16 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } UnlockLists(); - free(ALContext); - ALContext = NULL; + if(ALContext) + { + free(ALContext->ActiveSources); + ALContext->ActiveSources = NULL; + + VECTOR_DEINIT(ALContext->ActiveAuxSlots); + + free(ALContext); + ALContext = NULL; + } alcSetError(device, ALC_OUT_OF_MEMORY); ALCdevice_DecRef(device); @@ -2897,7 +2907,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin do { ALContext->next = device->ContextList; - } while(!CompExchangePtr((XchgPtr*)&device->ContextList, ALContext->next, ALContext)); + } while(CompExchangePtr((XchgPtr*)&device->ContextList, ALContext->next, ALContext) != ALContext->next); UnlockLists(); ALCdevice_DecRef(device); @@ -2938,7 +2948,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) { ALCcontext *Context; - Context = althread_getspecific(LocalContext); + Context = altss_get(LocalContext); if(!Context) Context = GlobalContext; return Context; @@ -2951,7 +2961,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) { ALCcontext *Context; - Context = althread_getspecific(LocalContext); + Context = altss_get(LocalContext); return Context; } @@ -2973,9 +2983,9 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) context = ExchangePtr((XchgPtr*)&GlobalContext, context); if(context) ALCcontext_DecRef(context); - if((context=althread_getspecific(LocalContext)) != NULL) + if((context=altss_get(LocalContext)) != NULL) { - althread_setspecific(LocalContext, NULL); + altss_set(LocalContext, NULL); ALCcontext_DecRef(context); } @@ -2997,8 +3007,8 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) return ALC_FALSE; } /* context's reference count is already incremented */ - old = althread_getspecific(LocalContext); - althread_setspecific(LocalContext, context); + old = altss_get(LocalContext); + altss_set(LocalContext, context); if(old) ALCcontext_DecRef(old); return ALC_TRUE; @@ -3046,7 +3056,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0)) deviceName = NULL; - device = al_calloc(16, sizeof(ALCdevice)+15+sizeof(ALeffectslot)); + device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot)); if(!device) { alcSetError(NULL, ALC_OUT_OF_MEMORY); @@ -3054,7 +3064,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) } //Validate device - device->ref = 1; + InitRef(&device->ref, 1); device->Connected = ALC_TRUE; device->Type = Playback; device->LastError = ALC_NO_ERROR; @@ -3062,7 +3072,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) device->Flags = 0; device->Bs2b = NULL; device->Bs2bLevel = 0; - device->DeviceName = NULL; + AL_STRING_INIT(device->DeviceName); device->ContextList = NULL; @@ -3088,10 +3098,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) device->UpdateSize = 1024; if(!PlaybackBackend.getFactory) - { - device->Funcs = &PlaybackBackend.Funcs; - device->Backend = create_backend_wrapper(device, ALCbackend_Playback); - } + device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs, + ALCbackend_Playback); else { ALCbackendFactory *factory = PlaybackBackend.getFactory(); @@ -3261,7 +3269,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) if(DefaultEffect.type != AL_EFFECT_NULL) { - device->DefaultSlot = (ALeffectslot*)(((ALintptrEXT)(device+1)+15)&~15); + device->DefaultSlot = (ALeffectslot*)device->_slot_mem; if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR) { device->DefaultSlot = NULL; @@ -3278,9 +3286,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) do { device->next = DeviceList; - } while(!CompExchangePtr((XchgPtr*)&DeviceList, device->next, device)); + } while(CompExchangePtr((XchgPtr*)&DeviceList, device->next, device) != device->next); - TRACE("Created device %p, \"%s\"\n", device, device->DeviceName); + TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); return device; } @@ -3356,10 +3364,12 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, } //Validate device - device->ref = 1; + InitRef(&device->ref, 1); device->Connected = ALC_TRUE; device->Type = Capture; + AL_STRING_INIT(device->DeviceName); + InitUIntMap(&device->BufferMap, ~0); InitUIntMap(&device->EffectMap, ~0); InitUIntMap(&device->FilterMap, ~0); @@ -3367,13 +3377,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, InitUIntMap(&device->PresetMap, ~0); InitUIntMap(&device->FontsoundMap, ~0); - device->DeviceName = NULL; - if(!CaptureBackend.getFactory) - { - device->Funcs = &CaptureBackend.Funcs; - device->Backend = create_backend_wrapper(device, ALCbackend_Capture); - } + device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs, + ALCbackend_Capture); else { ALCbackendFactory *factory = CaptureBackend.getFactory(); @@ -3409,9 +3415,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, do { device->next = DeviceList; - } while(!CompExchangePtr((XchgPtr*)&DeviceList, device->next, device)); + } while(CompExchangePtr((XchgPtr*)&DeviceList, device->next, device) != device->next); - TRACE("Created device %p, \"%s\"\n", device, device->DeviceName); + TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); return device; } @@ -3524,7 +3530,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN } //Validate device - device->ref = 1; + InitRef(&device->ref, 1); device->Connected = ALC_TRUE; device->Type = Loopback; device->LastError = ALC_NO_ERROR; @@ -3532,7 +3538,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN device->Flags = 0; device->Bs2b = NULL; device->Bs2bLevel = 0; - device->DeviceName = NULL; + AL_STRING_INIT(device->DeviceName); device->ContextList = NULL; @@ -3592,7 +3598,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN V(device->Backend,open)("Loopback"); do { device->next = DeviceList; - } while(!CompExchangePtr((XchgPtr*)&DeviceList, device->next, device)); + } while(CompExchangePtr((XchgPtr*)&DeviceList, device->next, device) != device->next); TRACE("Created device %p\n", device); return device; @@ -3676,17 +3682,18 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) LockLists(); if((device->Flags&DEVICE_PAUSED)) { - if(V0(device->Backend,start)() != ALC_FALSE) + device->Flags &= ~DEVICE_PAUSED; + if(device->ContextList != NULL) { - device->Flags |= DEVICE_RUNNING; - device->Flags &= ~DEVICE_PAUSED; - } - else - { - alcSetError(device, ALC_INVALID_DEVICE); - ALCdevice_Lock(device); - aluHandleDisconnect(device); - ALCdevice_Unlock(device); + if(V0(device->Backend,start)() != ALC_FALSE) + device->Flags |= DEVICE_RUNNING; + else + { + alcSetError(device, ALC_INVALID_DEVICE); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + } } } UnlockLists(); @@ -33,12 +33,17 @@ #include "alAuxEffectSlot.h" #include "alu.h" #include "bs2b.h" +#include "hrtf.h" +#include "static_assert.h" #include "mixer_defs.h" #include "midi/base.h" +static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE, + "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!"); + struct ChanMap { enum Channel channel; ALfloat angle; @@ -86,6 +91,14 @@ static ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment) case PointResampler: return Resample_point32_C; case LinearResampler: +#ifdef HAVE_SSE4_1 + if((CPUCapFlags&CPU_CAP_SSE4_1)) + return Resample_lerp32_SSE41; +#endif +#ifdef HAVE_SSE2 + if((CPUCapFlags&CPU_CAP_SSE2)) + return Resample_lerp32_SSE2; +#endif return Resample_lerp32_C; case CubicResampler: return Resample_cubic32_C; @@ -98,7 +111,7 @@ static ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment) } -static DryMixerFunc SelectHrtfMixer(void) +static HrtfMixerFunc SelectHrtfMixer(void) { #ifdef HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) @@ -226,7 +239,7 @@ static ALvoid CalcListenerParams(ALlistener *Listener) aluMatrixVector(Listener->Params.Velocity, 0.0f, Listener->Params.Matrix); } -ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) +ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) { static const struct ChanMap MonoMap[1] = { { FrontCenter, 0.0f } }; static const struct ChanMap StereoMap[2] = { @@ -276,13 +289,14 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) }; ALCdevice *Device = ALContext->Device; + const ALsource *ALSource = src->Source; ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume; ALbufferlistitem *BufferListItem; enum FmtChannels Channels; - ALfloat (*SrcMatrix)[MaxChannels]; - ALfloat DryGain, DryGainHF; + ALfloat DryGain, DryGainHF, DryGainLF; ALfloat WetGain[MAX_SENDS]; ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; ALint NumSends, Frequency; const struct ChanMap *chans = NULL; enum Resampler Resampler; @@ -290,7 +304,7 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) ALboolean DirectChannels; ALfloat hwidth = 0.0f; ALfloat Pitch; - ALint i, c; + ALint i, j, c; /* Get device properties */ NumSends = Device->NumAuxSends; @@ -307,6 +321,18 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) Resampler = ALSource->Resampler; DirectChannels = ALSource->DirectChannels; + src->Direct.OutBuffer = Device->DryBuffer; + for(i = 0;i < NumSends;i++) + { + ALeffectslot *Slot = ALSource->Send[i].Slot; + if(!Slot && i == 0) + Slot = Device->DefaultSlot; + if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + src->Send[i].OutBuffer = NULL; + else + src->Send[i].OutBuffer = Slot->WetBuffer; + } + /* Calculate the stepping value */ Channels = FmtMono; BufferListItem = ALSource->queue; @@ -316,44 +342,35 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) if((ALBuffer=BufferListItem->buffer) != NULL) { Pitch = Pitch * ALBuffer->Frequency / Frequency; - if(Pitch > 10.0f) - ALSource->Params.Step = 10<<FRACTIONBITS; + if(Pitch > (ALfloat)MAX_PITCH) + src->Step = MAX_PITCH<<FRACTIONBITS; else { - ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE); - if(ALSource->Params.Step == 0) - ALSource->Params.Step = 1; + src->Step = fastf2i(Pitch*FRACTIONONE); + if(src->Step == 0) + src->Step = 1; } - ALSource->Params.Resample = SelectResampler(Resampler, ALSource->Params.Step); + src->Resample = SelectResampler(Resampler, src->Step); Channels = ALBuffer->FmtChannels; break; } BufferListItem = BufferListItem->next; } - if(!DirectChannels && Device->Hrtf) - ALSource->Params.DryMix = SelectHrtfMixer(); - else - ALSource->Params.DryMix = SelectDirectMixer(); - ALSource->Params.WetMix = SelectSendMixer(); /* Calculate gains */ DryGain = clampf(SourceVolume, MinVolume, MaxVolume); - DryGain *= ALSource->DirectGain * ListenerGain; - DryGainHF = ALSource->DirectGainHF; + DryGain *= ALSource->Direct.Gain * ListenerGain; + DryGainHF = ALSource->Direct.GainHF; + DryGainLF = ALSource->Direct.GainLF; for(i = 0;i < NumSends;i++) { - WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); - WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; + WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); + WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; WetGainHF[i] = ALSource->Send[i].GainHF; + WetGainLF[i] = ALSource->Send[i].GainLF; } - SrcMatrix = ALSource->Params.Direct.Gains; - for(i = 0;i < MAX_INPUT_CHANNELS;i++) - { - for(c = 0;c < MaxChannels;c++) - SrcMatrix[i][c] = 0.0f; - } switch(Channels) { case FmtMono: @@ -410,16 +427,64 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) { for(c = 0;c < num_channels;c++) { + ALfloat *restrict Target = src->Direct.Mix.Gains[c].Target; + for(j = 0;j < MaxChannels;j++) + Target[j] = 0.0f; + } + + for(c = 0;c < num_channels;c++) + { + ALfloat *restrict Target = src->Direct.Mix.Gains[c].Target; for(i = 0;i < (ALint)Device->NumChan;i++) { enum Channel chan = Device->Speaker2Chan[i]; if(chan == chans[c].channel) { - SrcMatrix[c][chan] = DryGain; + Target[chan] = DryGain; break; } } } + + if(!src->Direct.Moving) + { + for(i = 0;i < num_channels;i++) + { + ALfloat *restrict Current = src->Direct.Mix.Gains[i].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[i].Step; + ALfloat *restrict Target = src->Direct.Mix.Gains[i].Target; + for(j = 0;j < MaxChannels;j++) + { + Current[j] = Target[j]; + Step[j] = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + for(i = 0;i < num_channels;i++) + { + ALfloat *restrict Current = src->Direct.Mix.Gains[i].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[i].Step; + ALfloat *restrict Target = src->Direct.Mix.Gains[i].Target; + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(Current[j], FLT_EPSILON); + ALfloat trg = maxf(Target[j], FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[j] = powf(trg/cur, 1.0f/64.0f); + else + Step[j] = 1.0f; + Current[j] = cur; + } + } + src->Direct.Counter = 64; + } + + src->IsHrtf = AL_FALSE; + src->Dry.Mix = SelectDirectMixer(); } else if(Device->Hrtf) { @@ -428,12 +493,12 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) if(chans[c].channel == LFE) { /* Skip LFE */ - ALSource->Params.Direct.Hrtf.Params.Delay[c][0] = 0; - ALSource->Params.Direct.Hrtf.Params.Delay[c][1] = 0; + src->Direct.Mix.Hrtf.Params[c].Delay[0] = 0; + src->Direct.Mix.Hrtf.Params[c].Delay[1] = 0; for(i = 0;i < HRIR_LENGTH;i++) { - ALSource->Params.Direct.Hrtf.Params.Coeffs[c][i][0] = 0.0f; - ALSource->Params.Direct.Hrtf.Params.Coeffs[c][i][1] = 0.0f; + src->Direct.Mix.Hrtf.Params[c].Coeffs[i][0] = 0.0f; + src->Direct.Mix.Hrtf.Params[c].Coeffs[i][1] = 0.0f; } } else @@ -441,75 +506,151 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) /* Get the static HRIR coefficients and delays for this * channel. */ GetLerpedHrtfCoeffs(Device->Hrtf, - 0.0f, chans[c].angle, DryGain, - ALSource->Params.Direct.Hrtf.Params.Coeffs[c], - ALSource->Params.Direct.Hrtf.Params.Delay[c]); + 0.0f, chans[c].angle, DryGain, + src->Direct.Mix.Hrtf.Params[c].Coeffs, + src->Direct.Mix.Hrtf.Params[c].Delay); } } - ALSource->Hrtf.Counter = 0; - ALSource->Params.Direct.Hrtf.Params.IrSize = GetHrtfIrSize(Device->Hrtf); + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + src->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf); - ALSource->Params.Direct.Hrtf.State = &ALSource->Hrtf; + src->IsHrtf = AL_TRUE; + src->Dry.HrtfMix = SelectHrtfMixer(); } else { + for(i = 0;i < num_channels;i++) + { + ALfloat *restrict Target = src->Direct.Mix.Gains[i].Target; + for(j = 0;j < MaxChannels;j++) + Target[j] = 0.0f; + } + DryGain *= lerp(1.0f, 1.0f/sqrtf((float)Device->NumChan), hwidth/F_PI); for(c = 0;c < num_channels;c++) { + ALfloat *restrict Target = src->Direct.Mix.Gains[c].Target; /* Special-case LFE */ if(chans[c].channel == LFE) { - SrcMatrix[c][chans[c].channel] = DryGain; + Target[chans[c].channel] = DryGain; continue; } - ComputeAngleGains(Device, chans[c].angle, hwidth, DryGain, - SrcMatrix[c]); + ComputeAngleGains(Device, chans[c].angle, hwidth, DryGain, Target); + } + + if(!src->Direct.Moving) + { + for(i = 0;i < num_channels;i++) + { + ALfloat *restrict Current = src->Direct.Mix.Gains[i].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[i].Step; + ALfloat *restrict Target = src->Direct.Mix.Gains[i].Target; + for(j = 0;j < MaxChannels;j++) + { + Current[j] = Target[j]; + Step[j] = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + for(i = 0;i < num_channels;i++) + { + ALfloat *restrict Current = src->Direct.Mix.Gains[i].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[i].Step; + ALfloat *restrict Target = src->Direct.Mix.Gains[i].Target; + for(j = 0;j < MaxChannels;j++) + { + ALfloat trg = maxf(Target[j], FLT_EPSILON); + ALfloat cur = maxf(Current[j], FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[j] = powf(trg/cur, 1.0f/64.0f); + else + Step[j] = 1.0f; + Current[j] = cur; + } + } + src->Direct.Counter = 64; } - } - ALSource->Params.Direct.OutBuffer = Device->DryBuffer; - ALSource->Params.Direct.ClickRemoval = Device->ClickRemoval; - ALSource->Params.Direct.PendingClicks = Device->PendingClicks; + src->IsHrtf = AL_FALSE; + src->Dry.Mix = SelectDirectMixer(); + } for(i = 0;i < NumSends;i++) { - ALeffectslot *Slot = ALSource->Send[i].Slot; - if(!Slot && i == 0) - Slot = Device->DefaultSlot; - if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + src->Send[i].Gain.Target = WetGain[i]; + if(!src->Send[i].Moving) { - ALSource->Params.Send[i].OutBuffer = NULL; - ALSource->Params.Send[i].ClickRemoval = NULL; - ALSource->Params.Send[i].PendingClicks = NULL; + src->Send[i].Gain.Current = src->Send[i].Gain.Target; + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_TRUE; } else { - ALSource->Params.Send[i].OutBuffer = Slot->WetBuffer; - ALSource->Params.Send[i].ClickRemoval = Slot->ClickRemoval; - ALSource->Params.Send[i].PendingClicks = Slot->PendingClicks; + ALfloat cur = maxf(src->Send[i].Gain.Current, FLT_EPSILON); + ALfloat trg = maxf(src->Send[i].Gain.Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + src->Send[i].Gain.Step = powf(trg/cur, 1.0f/64.0f); + else + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Gain.Current = cur; + src->Send[i].Counter = 64; } - ALSource->Params.Send[i].Gain = WetGain[i]; } + src->WetMix = SelectSendMixer(); { - ALfloat gain = maxf(0.01f, DryGainHF); + ALfloat gainhf = maxf(0.01f, DryGainHF); + ALfloat gainlf = maxf(0.01f, DryGainLF); + ALfloat hfscale = ALSource->Direct.HFReference / Frequency; + ALfloat lfscale = ALSource->Direct.LFReference / Frequency; for(c = 0;c < num_channels;c++) - ALfilterState_setParams(&ALSource->Params.Direct.LpFilter[c], - ALfilterType_HighShelf, gain, - (ALfloat)LOWPASSFREQREF/Frequency, 0.0f); + { + src->Direct.Filters[c].ActiveType = AF_None; + if(gainhf != 1.0f) src->Direct.Filters[c].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Direct.Filters[c].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Direct.Filters[c].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Direct.Filters[c].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } } for(i = 0;i < NumSends;i++) { - ALfloat gain = maxf(0.01f, WetGainHF[i]); + ALfloat gainhf = maxf(0.01f, WetGainHF[i]); + ALfloat gainlf = maxf(0.01f, WetGainLF[i]); + ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; + ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; for(c = 0;c < num_channels;c++) - ALfilterState_setParams(&ALSource->Params.Send[i].LpFilter[c], - ALfilterType_HighShelf, gain, - (ALfloat)LOWPASSFREQREF/Frequency, 0.0f); + { + src->Send[i].Filters[c].ActiveType = AF_None; + if(gainhf != 1.0f) src->Send[i].Filters[c].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Send[i].Filters[c].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Send[i].Filters[c].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Send[i].Filters[c].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } } } -ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) +ALvoid CalcSourceParams(ALactivesource *src, const ALCcontext *ALContext) { ALCdevice *Device = ALContext->Device; + const ALsource *ALSource = src->Source; ALfloat Velocity[3],Direction[3],Position[3],SourceToListener[3]; ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist; ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; @@ -526,9 +667,11 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) ALfloat DecayDistance[MAX_SENDS]; ALfloat DryGain; ALfloat DryGainHF; + ALfloat DryGainLF; ALboolean DryGainHFAuto; ALfloat WetGain[MAX_SENDS]; ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; ALboolean WetGainAuto; ALboolean WetGainHFAuto; enum Resampler Resampler; @@ -538,8 +681,12 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) ALint i, j; DryGainHF = 1.0f; + DryGainLF = 1.0f; for(i = 0;i < MAX_SENDS;i++) + { WetGainHF[i] = 1.0f; + WetGainLF[i] = 1.0f; + } /* Get context/device properties */ DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor; @@ -577,9 +724,7 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) WetGainHFAuto = ALSource->WetGainHFAuto; RoomRolloffBase = ALSource->RoomRolloffFactor; - ALSource->Params.Direct.OutBuffer = Device->DryBuffer; - ALSource->Params.Direct.ClickRemoval = Device->ClickRemoval; - ALSource->Params.Direct.PendingClicks = Device->PendingClicks; + src->Direct.OutBuffer = Device->DryBuffer; for(i = 0;i < NumSends;i++) { ALeffectslot *Slot = ALSource->Send[i].Slot; @@ -619,17 +764,9 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) } if(!Slot || Slot->EffectType == AL_EFFECT_NULL) - { - ALSource->Params.Send[i].OutBuffer = NULL; - ALSource->Params.Send[i].ClickRemoval = NULL; - ALSource->Params.Send[i].PendingClicks = NULL; - } + src->Send[i].OutBuffer = NULL; else - { - ALSource->Params.Send[i].OutBuffer = Slot->WetBuffer; - ALSource->Params.Send[i].ClickRemoval = Slot->ClickRemoval; - ALSource->Params.Send[i].PendingClicks = Slot->PendingClicks; - } + src->Send[i].OutBuffer = Slot->WetBuffer; } /* Transform source to listener space (convert to head relative) */ @@ -792,12 +929,14 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume); /* Apply gain and frequency filters */ - DryGain *= ALSource->DirectGain * ListenerGain; - DryGainHF *= ALSource->DirectGainHF; + DryGain *= ALSource->Direct.Gain * ListenerGain; + DryGainHF *= ALSource->Direct.GainHF; + DryGainLF *= ALSource->Direct.GainLF; for(i = 0;i < NumSends;i++) { WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; WetGainHF[i] *= ALSource->Send[i].GainHF; + WetGainLF[i] *= ALSource->Send[i].GainLF; } /* Calculate velocity-based doppler effect */ @@ -828,25 +967,20 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) /* Calculate fixed-point stepping value, based on the pitch, buffer * frequency, and output frequency. */ Pitch = Pitch * ALBuffer->Frequency / Frequency; - if(Pitch > 10.0f) - ALSource->Params.Step = 10<<FRACTIONBITS; + if(Pitch > (ALfloat)MAX_PITCH) + src->Step = MAX_PITCH<<FRACTIONBITS; else { - ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE); - if(ALSource->Params.Step == 0) - ALSource->Params.Step = 1; + src->Step = fastf2i(Pitch*FRACTIONONE); + if(src->Step == 0) + src->Step = 1; } - ALSource->Params.Resample = SelectResampler(Resampler, ALSource->Params.Step); + src->Resample = SelectResampler(Resampler, src->Step); break; } BufferListItem = BufferListItem->next; } - if(Device->Hrtf) - ALSource->Params.DryMix = SelectHrtfMixer(); - else - ALSource->Params.DryMix = SelectDirectMixer(); - ALSource->Params.WetMix = SelectSendMixer(); if(Device->Hrtf) { @@ -869,56 +1003,55 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) } /* Check to see if the HRIR is already moving. */ - if(ALSource->Hrtf.Moving) + if(src->Direct.Moving) { /* Calculate the normalized HRTF transition factor (delta). */ - delta = CalcHrtfDelta(ALSource->Params.Direct.Hrtf.Params.Gain, DryGain, - ALSource->Params.Direct.Hrtf.Params.Dir, Position); + delta = CalcHrtfDelta(src->Direct.Mix.Hrtf.Gain, DryGain, + src->Direct.Mix.Hrtf.Dir, Position); /* If the delta is large enough, get the moving HRIR target * coefficients, target delays, steppping values, and counter. */ if(delta > 0.001f) { - ALSource->Hrtf.Counter = GetMovingHrtfCoeffs(Device->Hrtf, + ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf, ev, az, DryGain, delta, - ALSource->Hrtf.Counter, - ALSource->Params.Direct.Hrtf.Params.Coeffs[0], - ALSource->Params.Direct.Hrtf.Params.Delay[0], - ALSource->Params.Direct.Hrtf.Params.CoeffStep, - ALSource->Params.Direct.Hrtf.Params.DelayStep); - ALSource->Params.Direct.Hrtf.Params.Gain = DryGain; - ALSource->Params.Direct.Hrtf.Params.Dir[0] = Position[0]; - ALSource->Params.Direct.Hrtf.Params.Dir[1] = Position[1]; - ALSource->Params.Direct.Hrtf.Params.Dir[2] = Position[2]; + src->Direct.Counter, + src->Direct.Mix.Hrtf.Params[0].Coeffs, + src->Direct.Mix.Hrtf.Params[0].Delay, + src->Direct.Mix.Hrtf.Params[0].CoeffStep, + src->Direct.Mix.Hrtf.Params[0].DelayStep); + src->Direct.Counter = counter; + src->Direct.Mix.Hrtf.Gain = DryGain; + src->Direct.Mix.Hrtf.Dir[0] = Position[0]; + src->Direct.Mix.Hrtf.Dir[1] = Position[1]; + src->Direct.Mix.Hrtf.Dir[2] = Position[2]; } } else { /* Get the initial (static) HRIR coefficients and delays. */ GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, DryGain, - ALSource->Params.Direct.Hrtf.Params.Coeffs[0], - ALSource->Params.Direct.Hrtf.Params.Delay[0]); - ALSource->Hrtf.Counter = 0; - ALSource->Hrtf.Moving = AL_TRUE; - ALSource->Params.Direct.Hrtf.Params.Gain = DryGain; - ALSource->Params.Direct.Hrtf.Params.Dir[0] = Position[0]; - ALSource->Params.Direct.Hrtf.Params.Dir[1] = Position[1]; - ALSource->Params.Direct.Hrtf.Params.Dir[2] = Position[2]; + src->Direct.Mix.Hrtf.Params[0].Coeffs, + src->Direct.Mix.Hrtf.Params[0].Delay); + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + src->Direct.Mix.Hrtf.Gain = DryGain; + src->Direct.Mix.Hrtf.Dir[0] = Position[0]; + src->Direct.Mix.Hrtf.Dir[1] = Position[1]; + src->Direct.Mix.Hrtf.Dir[2] = Position[2]; } - ALSource->Params.Direct.Hrtf.Params.IrSize = GetHrtfIrSize(Device->Hrtf); + src->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf); - ALSource->Params.Direct.Hrtf.State = &ALSource->Hrtf; + src->IsHrtf = AL_TRUE; + src->Dry.HrtfMix = SelectHrtfMixer(); } else { - ALfloat (*Matrix)[MaxChannels] = ALSource->Params.Direct.Gains; + ALfloat *restrict Target = src->Direct.Mix.Gains[0].Target; ALfloat DirGain = 0.0f; ALfloat AmbientGain; - for(i = 0;i < MAX_INPUT_CHANNELS;i++) - { - for(j = 0;j < MaxChannels;j++) - Matrix[i][j] = 0.0f; - } + for(j = 0;j < MaxChannels;j++) + Target[j] = 0.0f; /* Normalize the length, and compute panned gains. */ if(Distance > FLT_EPSILON) @@ -930,7 +1063,7 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) DirGain = sqrtf(Position[0]*Position[0] + Position[2]*Position[2]); ComputeAngleGains(Device, atan2f(Position[0], -Position[2]*ZScale), 0.0f, - DryGain*DirGain, Matrix[0]); + DryGain*DirGain, Target); } /* Adjustment for vertical offsets. Not the greatest, but simple @@ -939,25 +1072,99 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) for(i = 0;i < (ALint)Device->NumChan;i++) { enum Channel chan = Device->Speaker2Chan[i]; - Matrix[0][chan] = maxf(Matrix[0][chan], AmbientGain); + Target[chan] = maxf(Target[chan], AmbientGain); } + + if(!src->Direct.Moving) + { + ALfloat *restrict Current = src->Direct.Mix.Gains[0].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[0].Step; + for(j = 0;j < MaxChannels;j++) + { + Current[j] = Target[j]; + Step[j] = 1.0f; + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + ALfloat *restrict Current = src->Direct.Mix.Gains[0].Current; + ALfloat *restrict Step = src->Direct.Mix.Gains[0].Step; + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(Current[j], FLT_EPSILON); + ALfloat trg = maxf(Target[j], FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + Step[j] = powf(trg/cur, 1.0f/64.0f); + else + Step[j] = 1.0f; + Current[j] = cur; + } + src->Direct.Counter = 64; + } + + src->IsHrtf = AL_FALSE; + src->Dry.Mix = SelectDirectMixer(); } for(i = 0;i < NumSends;i++) - ALSource->Params.Send[i].Gain = WetGain[i]; - + { + src->Send[i].Gain.Target = WetGain[i]; + if(!src->Send[i].Moving) + { + src->Send[i].Gain.Current = src->Send[i].Gain.Target; + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_TRUE; + } + else + { + ALfloat cur = maxf(src->Send[i].Gain.Current, FLT_EPSILON); + ALfloat trg = maxf(src->Send[i].Gain.Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + src->Send[i].Gain.Step = powf(trg/cur, 1.0f/64.0f); + else + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Gain.Current = cur; + src->Send[i].Counter = 64; + } + } + src->WetMix = SelectSendMixer(); { - ALfloat gain = maxf(0.01f, DryGainHF); - ALfilterState_setParams(&ALSource->Params.Direct.LpFilter[0], - ALfilterType_HighShelf, gain, - (ALfloat)LOWPASSFREQREF/Frequency, 0.0f); + ALfloat gainhf = maxf(0.01f, DryGainHF); + ALfloat gainlf = maxf(0.01f, DryGainLF); + ALfloat hfscale = ALSource->Direct.HFReference / Frequency; + ALfloat lfscale = ALSource->Direct.LFReference / Frequency; + src->Direct.Filters[0].ActiveType = AF_None; + if(gainhf != 1.0f) src->Direct.Filters[0].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Direct.Filters[0].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Direct.Filters[0].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Direct.Filters[0].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); } for(i = 0;i < NumSends;i++) { - ALfloat gain = maxf(0.01f, WetGainHF[i]); - ALfilterState_setParams(&ALSource->Params.Send[i].LpFilter[0], - ALfilterType_HighShelf, gain, - (ALfloat)LOWPASSFREQREF/Frequency, 0.0f); + ALfloat gainhf = maxf(0.01f, WetGainHF[i]); + ALfloat gainlf = maxf(0.01f, WetGainLF[i]); + ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; + ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; + src->Send[i].Filters[0].ActiveType = AF_None; + if(gainhf != 1.0f) src->Send[i].Filters[0].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Send[i].Filters[0].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Send[i].Filters[0].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Send[i].Filters[0].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); } } @@ -987,11 +1194,10 @@ static inline ALubyte aluF2UB(ALfloat val) { return aluF2B(val)+128; } #define DECL_TEMPLATE(T, func) \ -static int Write_##T(ALCdevice *device, T *restrict buffer, \ - ALuint SamplesToDo) \ +static void Write_##T(ALCdevice *device, ALvoid **buffer, ALuint SamplesToDo) \ { \ ALfloat (*restrict DryBuffer)[BUFFERSIZE] = device->DryBuffer; \ - ALuint numchans = ChannelsFromDevFmt(device->FmtChans); \ + const ALuint numchans = ChannelsFromDevFmt(device->FmtChans); \ const ALuint *offsets = device->ChannelOffsets; \ ALuint i, j; \ \ @@ -1002,11 +1208,11 @@ static int Write_##T(ALCdevice *device, T *restrict buffer, \ if(offsets[j] == INVALID_OFFSET) \ continue; \ \ - out = buffer + offsets[j]; \ + out = (T*)(*buffer) + offsets[j]; \ for(i = 0;i < SamplesToDo;i++) \ out[i*numchans] = func(DryBuffer[j][i]); \ } \ - return SamplesToDo*numchans*sizeof(T); \ + *buffer = (char*)(*buffer) + SamplesToDo*numchans*sizeof(T); \ } DECL_TEMPLATE(ALfloat, aluF2F) @@ -1024,7 +1230,7 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) { ALuint SamplesToDo; ALeffectslot **slot, **slot_end; - ALsource **src, **src_end; + ALactivesource **src, **src_end; ALCcontext *ctx; FPUCtl oldMode; ALuint i, c; @@ -1033,6 +1239,8 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) while(size > 0) { + IncrementRef(&device->MixCount); + SamplesToDo = minu(size, BUFFERSIZE); for(c = 0;c < MaxChannels;c++) memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); @@ -1057,37 +1265,31 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) src_end = src + ctx->ActiveSourceCount; while(src != src_end) { - if((*src)->state != AL_PLAYING) + ALsource *source = (*src)->Source; + + if(source->state != AL_PLAYING && source->state != AL_PAUSED) { + ALactivesource *temp = *(--src_end); + *src_end = *src; + *src = temp; --(ctx->ActiveSourceCount); - *src = *(--src_end); continue; } - if(!DeferUpdates && (ExchangeInt(&(*src)->NeedsUpdate, AL_FALSE) || + if(!DeferUpdates && (ExchangeInt(&source->NeedsUpdate, AL_FALSE) || UpdateSources)) - ALsource_Update(*src, ctx); + (*src)->Update(*src, ctx); - MixSource(*src, device, SamplesToDo); + if(source->state != AL_PAUSED) + MixSource(*src, device, SamplesToDo); src++; } /* effect slot processing */ - slot = ctx->ActiveEffectSlots; - slot_end = slot + ctx->ActiveEffectSlotCount; + slot = VECTOR_ITER_BEGIN(ctx->ActiveAuxSlots); + slot_end = VECTOR_ITER_END(ctx->ActiveAuxSlots); while(slot != slot_end) { - ALfloat offset = (*slot)->ClickRemoval[0]; - if(offset < (1.0f/32768.0f)) - offset = 0.0f; - else for(i = 0;i < SamplesToDo;i++) - { - (*slot)->WetBuffer[0][i] += offset; - offset -= offset * (1.0f/256.0f); - } - (*slot)->ClickRemoval[0] = offset + (*slot)->PendingClicks[0]; - (*slot)->PendingClicks[0] = 0.0f; - if(!DeferUpdates && ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE)) V((*slot)->EffectState,update)(device, *slot); @@ -1106,17 +1308,6 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) slot = &device->DefaultSlot; if(*slot != NULL) { - ALfloat offset = (*slot)->ClickRemoval[0]; - if(offset < (1.0f/32768.0f)) - offset = 0.0f; - else for(i = 0;i < SamplesToDo;i++) - { - (*slot)->WetBuffer[0][i] += offset; - offset -= offset * (1.0f/256.0f); - } - (*slot)->ClickRemoval[0] = offset + (*slot)->PendingClicks[0]; - (*slot)->PendingClicks[0] = 0.0f; - if(ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE)) V((*slot)->EffectState,update)(device, *slot); @@ -1136,87 +1327,50 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) device->SamplesDone %= device->Frequency; ALCdevice_Unlock(device); - /* Click-removal. Could do better; this only really handles immediate - * changes between updates where a predictive sample could be - * generated. Delays caused by effects and HRTF aren't caught. */ - if(device->FmtChans == DevFmtStereo) + if(device->Bs2b) { - /* Assumes the first two channels are FrontLeft and FrontRight */ - for(c = 0;c < 2;c++) - { - ALfloat offset = device->ClickRemoval[c]; - if(offset < (1.0f/32768.0f)) - offset = 0.0f; - else for(i = 0;i < SamplesToDo;i++) - { - device->DryBuffer[c][i] += offset; - offset -= offset * (1.0f/256.0f); - } - device->ClickRemoval[c] = offset + device->PendingClicks[c]; - device->PendingClicks[c] = 0.0f; - } - if(device->Bs2b) + /* Apply binaural/crossfeed filter */ + for(i = 0;i < SamplesToDo;i++) { float samples[2]; - for(i = 0;i < SamplesToDo;i++) - { - samples[0] = device->DryBuffer[FrontLeft][i]; - samples[1] = device->DryBuffer[FrontRight][i]; - bs2b_cross_feed(device->Bs2b, samples); - device->DryBuffer[FrontLeft][i] = samples[0]; - device->DryBuffer[FrontRight][i] = samples[1]; - } - } - } - else - { - for(c = 0;c < MaxChannels;c++) - { - ALfloat offset = device->ClickRemoval[c]; - if(offset < (1.0f/32768.0f)) - offset = 0.0f; - else for(i = 0;i < SamplesToDo;i++) - { - device->DryBuffer[c][i] += offset; - offset -= offset * (1.0f/256.0f); - } - device->ClickRemoval[c] = offset + device->PendingClicks[c]; - device->PendingClicks[c] = 0.0f; + samples[0] = device->DryBuffer[FrontLeft][i]; + samples[1] = device->DryBuffer[FrontRight][i]; + bs2b_cross_feed(device->Bs2b, samples); + device->DryBuffer[FrontLeft][i] = samples[0]; + device->DryBuffer[FrontRight][i] = samples[1]; } } if(buffer) { - int bytes = 0; switch(device->FmtType) { case DevFmtByte: - bytes = Write_ALbyte(device, buffer, SamplesToDo); + Write_ALbyte(device, &buffer, SamplesToDo); break; case DevFmtUByte: - bytes = Write_ALubyte(device, buffer, SamplesToDo); + Write_ALubyte(device, &buffer, SamplesToDo); break; case DevFmtShort: - bytes = Write_ALshort(device, buffer, SamplesToDo); + Write_ALshort(device, &buffer, SamplesToDo); break; case DevFmtUShort: - bytes = Write_ALushort(device, buffer, SamplesToDo); + Write_ALushort(device, &buffer, SamplesToDo); break; case DevFmtInt: - bytes = Write_ALint(device, buffer, SamplesToDo); + Write_ALint(device, &buffer, SamplesToDo); break; case DevFmtUInt: - bytes = Write_ALuint(device, buffer, SamplesToDo); + Write_ALuint(device, &buffer, SamplesToDo); break; case DevFmtFloat: - bytes = Write_ALfloat(device, buffer, SamplesToDo); + Write_ALfloat(device, &buffer, SamplesToDo); break; } - - buffer = (ALubyte*)buffer + bytes; } size -= SamplesToDo; + IncrementRef(&device->MixCount); } RestoreFPUMode(&oldMode); @@ -1232,18 +1386,19 @@ ALvoid aluHandleDisconnect(ALCdevice *device) Context = device->ContextList; while(Context) { - ALsource **src, **src_end; + ALactivesource **src, **src_end; src = Context->ActiveSources; src_end = src + Context->ActiveSourceCount; while(src != src_end) { - if((*src)->state == AL_PLAYING) + ALsource *source = (*src)->Source; + if(source->state == AL_PLAYING) { - (*src)->state = AL_STOPPED; - (*src)->BuffersPlayed = (*src)->BuffersInQueue; - (*src)->position = 0; - (*src)->position_fraction = 0; + source->state = AL_STOPPED; + source->current_buffer = NULL; + source->position = 0; + source->position_fraction = 0; } src++; } diff --git a/Alc/alcConfig.c b/Alc/alcConfig.c index 98c0df32..2c9aef41 100644 --- a/Alc/alcConfig.c +++ b/Alc/alcConfig.c @@ -32,13 +32,14 @@ #include <stdio.h> #include <ctype.h> #include <string.h> - -#include "alMain.h" - #ifdef _WIN32_IE #include <shlobj.h> #endif +#include "alMain.h" +#include "compat.h" + + typedef struct ConfigEntry { char *key; char *value; @@ -48,10 +49,8 @@ typedef struct ConfigBlock { ConfigEntry *entries; unsigned int entryCount; } ConfigBlock; - static ConfigBlock cfgBlock; -static char buffer[1024]; static char *lstrip(char *line) { @@ -69,23 +68,132 @@ static char *rstrip(char *line) return line; } +static int readline(FILE *f, char **output, size_t *maxlen) +{ + size_t len = 0; + int c; + + while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n')) + ; + if(c == EOF) + return 0; + + do { + if(len+1 >= *maxlen) + { + void *temp = NULL; + size_t newmax; + + newmax = (*maxlen ? (*maxlen)<<1 : 32); + if(newmax > *maxlen) + temp = realloc(*output, newmax); + if(!temp) + { + ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen); + return 0; + } + + *output = temp; + *maxlen = newmax; + } + (*output)[len++] = c; + (*output)[len] = '\0'; + } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n'); + + return 1; +} + + +static char *expdup(const char *str) +{ + char *output = NULL; + size_t maxlen = 0; + size_t len = 0; + + while(*str != '\0') + { + const char *addstr; + size_t addstrlen; + size_t i; + + if(str[0] != '$') + { + const char *next = strchr(str, '$'); + addstr = str; + addstrlen = next ? (size_t)(next-str) : strlen(str); + + str += addstrlen; + } + else + { + str++; + if(*str == '$') + { + const char *next = strchr(str+1, '$'); + addstr = str; + addstrlen = next ? (size_t)(next-str) : strlen(str); + + str += addstrlen; + } + else + { + char envname[1024]; + size_t k = 0; + + while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1) + envname[k++] = *(str++); + envname[k++] = '\0'; + + if((addstr=getenv(envname)) == NULL) + continue; + addstrlen = strlen(addstr); + } + } + if(addstrlen == 0) + continue; + + if(addstrlen >= maxlen-len) + { + void *temp = NULL; + size_t newmax; + + newmax = len+addstrlen+1; + if(newmax > maxlen) + temp = realloc(output, newmax); + if(!temp) + { + ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen); + return output; + } + + output = temp; + maxlen = newmax; + } + + for(i = 0;i < addstrlen;i++) + output[len++] = addstr[i]; + output[len] = '\0'; + } + + return output ? output : calloc(1, 1); +} + + static void LoadConfigFromFile(FILE *f) { char curSection[128] = ""; + char *buffer = NULL; + size_t maxlen = 0; ConfigEntry *ent; - while(fgets(buffer, sizeof(buffer), f)) + while(readline(f, &buffer, &maxlen)) { char *line, *comment; char key[256] = ""; char value[256] = ""; comment = strchr(buffer, '#'); - if(comment) - { - *(comment++) = 0; - comment = rstrip(lstrip(comment)); - } + if(comment) *(comment++) = 0; line = rstrip(lstrip(buffer)); if(!line[0]) @@ -123,26 +231,26 @@ static void LoadConfigFromFile(FILE *f) * manually. */ if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0) value[0] = 0; - } - else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2) - { - /* Special case for 'key =' */ - value[0] = 0; - } - else - { - ERR("config parse error: malformed option line: \"%s\"\n\n", line); - continue; - } - rstrip(key); - - if(curSection[0] != 0) - { - size_t len = strlen(curSection); - memmove(&key[len+1], key, sizeof(key)-1-len); - key[len] = '/'; - memcpy(key, curSection, len); - } + } + else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2) + { + /* Special case for 'key =' */ + value[0] = 0; + } + else + { + ERR("config parse error: malformed option line: \"%s\"\n\n", line); + continue; + } + rstrip(key); + + if(curSection[0] != 0) + { + size_t len = strlen(curSection); + memmove(&key[len+1], key, sizeof(key)-1-len); + key[len] = '/'; + memcpy(key, curSection, len); + } /* Check if we already have this option set */ ent = cfgBlock.entries; @@ -171,36 +279,57 @@ static void LoadConfigFromFile(FILE *f) } free(ent->value); - ent->value = strdup(value); + ent->value = expdup(value); TRACE("found '%s' = '%s'\n", ent->key, ent->value); } + + free(buffer); } +#ifdef _WIN32 void ReadALConfig(void) { - const char *str; + WCHAR buffer[PATH_MAX]; + const WCHAR *str; FILE *f; -#ifdef _WIN32 - if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE) + if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE) { - size_t p = strlen(buffer); - snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini"); + size_t p = lstrlenW(buffer); + _snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini"); - TRACE("Loading config %s...\n", buffer); - f = fopen(buffer, "rt"); + TRACE("Loading config %ls...\n", buffer); + f = _wfopen(buffer, L"rt"); if(f) { LoadConfigFromFile(f); fclose(f); } } + + if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str) + { + TRACE("Loading config %ls...\n", str); + f = _wfopen(str, L"rt"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +} #else +void ReadALConfig(void) +{ + char buffer[PATH_MAX]; + const char *str; + FILE *f; + str = "/etc/openal/alsoft.conf"; TRACE("Loading config %s...\n", str); - f = fopen(str, "r"); + f = al_fopen(str, "r"); if(f) { LoadConfigFromFile(f); @@ -231,7 +360,7 @@ void ReadALConfig(void) buffer[sizeof(buffer)-1] = 0; TRACE("Loading config %s...\n", next); - f = fopen(next, "r"); + f = al_fopen(next, "r"); if(f) { LoadConfigFromFile(f); @@ -247,7 +376,7 @@ void ReadALConfig(void) snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str); TRACE("Loading config %s...\n", buffer); - f = fopen(buffer, "r"); + f = al_fopen(buffer, "r"); if(f) { LoadConfigFromFile(f); @@ -266,19 +395,18 @@ void ReadALConfig(void) if(buffer[0] != 0) { TRACE("Loading config %s...\n", buffer); - f = fopen(buffer, "r"); + f = al_fopen(buffer, "r"); if(f) { LoadConfigFromFile(f); fclose(f); } } -#endif if((str=getenv("ALSOFT_CONF")) != NULL && *str) { TRACE("Loading config %s...\n", str); - f = fopen(str, "r"); + f = al_fopen(str, "r"); if(f) { LoadConfigFromFile(f); @@ -286,6 +414,7 @@ void ReadALConfig(void) } } } +#endif void FreeALConfig(void) { diff --git a/Alc/alcRing.c b/Alc/alcRing.c index f831860f..9b5d8214 100644 --- a/Alc/alcRing.c +++ b/Alc/alcRing.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include "alMain.h" +#include "threads.h" #include "compat.h" @@ -35,7 +36,7 @@ struct RingBuffer { ALint read_pos; ALint write_pos; - CRITICAL_SECTION cs; + almtx_t mtx; }; @@ -51,7 +52,7 @@ RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length) ring->read_pos = 0; ring->write_pos = 0; - InitializeCriticalSection(&ring->cs); + almtx_init(&ring->mtx, almtx_plain); } return ring; } @@ -60,7 +61,7 @@ void DestroyRingBuffer(RingBuffer *ring) { if(ring) { - DeleteCriticalSection(&ring->cs); + almtx_destroy(&ring->mtx); free(ring); } } @@ -69,9 +70,9 @@ ALsizei RingBufferSize(RingBuffer *ring) { ALsizei s; - EnterCriticalSection(&ring->cs); + almtx_lock(&ring->mtx); s = (ring->write_pos-ring->read_pos+ring->length) % ring->length; - LeaveCriticalSection(&ring->cs); + almtx_unlock(&ring->mtx); return s; } @@ -80,7 +81,7 @@ void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len) { int remain; - EnterCriticalSection(&ring->cs); + almtx_lock(&ring->mtx); remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length; if(remain < len) len = remain; @@ -103,14 +104,14 @@ void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len) ring->write_pos %= ring->length; } - LeaveCriticalSection(&ring->cs); + almtx_unlock(&ring->mtx); } void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len) { int remain; - EnterCriticalSection(&ring->cs); + almtx_lock(&ring->mtx); remain = ring->length - ring->read_pos; if(remain < len) @@ -124,5 +125,5 @@ void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len) ring->read_pos += len; ring->read_pos %= ring->length; - LeaveCriticalSection(&ring->cs); + almtx_unlock(&ring->mtx); } diff --git a/Alc/alstring.h b/Alc/alstring.h new file mode 100644 index 00000000..5b37a483 --- /dev/null +++ b/Alc/alstring.h @@ -0,0 +1,48 @@ +#ifndef ALSTRING_H +#define ALSTRING_H + +#include <string.h> + +#include "vector.h" + + +typedef char al_string_char_type; +DECL_VECTOR(al_string_char_type) + +typedef vector_al_string_char_type al_string; +typedef const_vector_al_string_char_type const_al_string; + +#define AL_STRING_INIT(_x) VECTOR_INIT(_x) +#define AL_STRING_DEINIT(_x) VECTOR_DEINIT(_x) + +inline ALsizei al_string_length(const_al_string str) +{ return VECTOR_SIZE(str); } + +inline ALboolean al_string_empty(const_al_string str) +{ return al_string_length(str) == 0; } + +inline const al_string_char_type *al_string_get_cstr(const_al_string str) +{ return str ? &VECTOR_FRONT(str) : ""; } + +void al_string_clear(al_string *str); + +int al_string_cmp(const_al_string str1, const_al_string str2); +int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2); + +void al_string_copy(al_string *str, const_al_string from); +void al_string_copy_cstr(al_string *str, const al_string_char_type *from); + +void al_string_append_char(al_string *str, const al_string_char_type c); +void al_string_append_cstr(al_string *str, const al_string_char_type *from); +void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); + +#ifdef _WIN32 +#include <wchar.h> +/* Windows-only methods to deal with WideChar strings. */ +void al_string_copy_wcstr(al_string *str, const wchar_t *from); +#endif + + +DECL_VECTOR(al_string) + +#endif /* ALSTRING_H */ diff --git a/Alc/atomic.h b/Alc/atomic.h deleted file mode 100644 index 1dd8f9dc..00000000 --- a/Alc/atomic.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef AL_ATOMIC_H -#define AL_ATOMIC_H - - -typedef void *volatile XchgPtr; - -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__) -typedef unsigned int RefCount; -inline RefCount IncrementRef(volatile RefCount *ptr) -{ return __sync_add_and_fetch(ptr, 1); } -inline RefCount DecrementRef(volatile RefCount *ptr) -{ return __sync_sub_and_fetch(ptr, 1); } - -inline int ExchangeInt(volatile int *ptr, int newval) -{ - return __sync_lock_test_and_set(ptr, newval); -} -inline void *ExchangePtr(XchgPtr *ptr, void *newval) -{ - return __sync_lock_test_and_set(ptr, newval); -} -inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval) -{ - return __sync_bool_compare_and_swap(ptr, oldval, newval); -} -inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval) -{ - return __sync_bool_compare_and_swap(ptr, oldval, newval); -} - -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -inline unsigned int xaddl(volatile unsigned int *dest, int incr) -{ - unsigned int ret; - __asm__ __volatile__("lock; xaddl %0,(%1)" - : "=r" (ret) - : "r" (dest), "0" (incr) - : "memory"); - return ret; -} - -typedef unsigned int RefCount; -inline RefCount IncrementRef(volatile RefCount *ptr) -{ return xaddl(ptr, 1)+1; } -inline RefCount DecrementRef(volatile RefCount *ptr) -{ return xaddl(ptr, -1)-1; } - -inline int ExchangeInt(volatile int *dest, int newval) -{ - int ret; - __asm__ __volatile__("lock; xchgl %0,(%1)" - : "=r" (ret) - : "r" (dest), "0" (newval) - : "memory"); - return ret; -} - -inline ALboolean CompExchangeInt(volatile int *dest, int oldval, int newval) -{ - int ret; - __asm__ __volatile__("lock; cmpxchgl %2,(%1)" - : "=a" (ret) - : "r" (dest), "r" (newval), "0" (oldval) - : "memory"); - return ret == oldval; -} - -inline void *ExchangePtr(XchgPtr *dest, void *newval) -{ - void *ret; - __asm__ __volatile__( -#ifdef __i386__ - "lock; xchgl %0,(%1)" -#else - "lock; xchgq %0,(%1)" -#endif - : "=r" (ret) - : "r" (dest), "0" (newval) - : "memory" - ); - return ret; -} - -inline ALboolean CompExchangePtr(XchgPtr *dest, void *oldval, void *newval) -{ - void *ret; - __asm__ __volatile__( -#ifdef __i386__ - "lock; cmpxchgl %2,(%1)" -#else - "lock; cmpxchgq %2,(%1)" -#endif - : "=a" (ret) - : "r" (dest), "r" (newval), "0" (oldval) - : "memory" - ); - return ret == oldval; -} - -#elif defined(_WIN32) - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -typedef LONG RefCount; -inline RefCount IncrementRef(volatile RefCount *ptr) -{ return InterlockedIncrement(ptr); } -inline RefCount DecrementRef(volatile RefCount *ptr) -{ return InterlockedDecrement(ptr); } - -extern ALbyte LONG_size_does_not_match_int[(sizeof(LONG)==sizeof(int))?1:-1]; - -inline int ExchangeInt(volatile int *ptr, int newval) -{ - union { - volatile int *i; - volatile LONG *l; - } u = { ptr }; - return InterlockedExchange(u.l, newval); -} -inline void *ExchangePtr(XchgPtr *ptr, void *newval) -{ - return InterlockedExchangePointer(ptr, newval); -} -inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval) -{ - union { - volatile int *i; - volatile LONG *l; - } u = { ptr }; - return InterlockedCompareExchange(u.l, newval, oldval) == oldval; -} -inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval) -{ - return InterlockedCompareExchangePointer(ptr, newval, oldval) == oldval; -} - -#elif defined(__APPLE__) - -#include <libkern/OSAtomic.h> - -typedef int32_t RefCount; -inline RefCount IncrementRef(volatile RefCount *ptr) -{ return OSAtomicIncrement32Barrier(ptr); } -inline RefCount DecrementRef(volatile RefCount *ptr) -{ return OSAtomicDecrement32Barrier(ptr); } - -inline int ExchangeInt(volatile int *ptr, int newval) -{ - /* Really? No regular old atomic swap? */ - int oldval; - do { - oldval = *ptr; - } while(!OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr)); - return oldval; -} -inline void *ExchangePtr(XchgPtr *ptr, void *newval) -{ - void *oldval; - do { - oldval = *ptr; - } while(!OSAtomicCompareAndSwapPtrBarrier(oldval, newval, ptr)); - return oldval; -} -inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval) -{ - return OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr); -} -inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval) -{ - return OSAtomicCompareAndSwapPtrBarrier(oldval, newval, ptr); -} - -#else -#error "No atomic functions available on this platform!" -typedef ALuint RefCount; -#endif - -#endif /* AL_ATOMIC_H */ diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c index 874b31c3..0712a412 100644 --- a/Alc/backends/alsa.c +++ b/Alc/backends/alsa.c @@ -227,14 +227,27 @@ static ALCboolean alsa_load(void) typedef struct { - ALCchar *name; - char *device; + al_string name; + al_string device_name; } DevMap; +DECL_VECTOR(DevMap) -static DevMap *allDevNameMap; -static ALuint numDevNames; -static DevMap *allCaptureDevNameMap; -static ALuint numCaptureDevNames; +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *devlist) +{ + DevMap *iter, *end; + + iter = VECTOR_ITER_BEGIN(*devlist); + end = VECTOR_ITER_END(*devlist); + for(;iter != end;iter++) + { + AL_STRING_DEINIT(iter->name); + AL_STRING_DEINIT(iter->device_name); + } + VECTOR_RESIZE(*devlist, 0); +} static const char *prefix_name(snd_pcm_stream_t stream) @@ -243,23 +256,26 @@ static const char *prefix_name(snd_pcm_stream_t stream) return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; } -static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) +static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList) { const char *main_prefix = "plughw:"; snd_ctl_t *handle; - int card, err, dev, idx; snd_ctl_card_info_t *info; snd_pcm_info_t *pcminfo; - DevMap *DevList; + int card, err, dev; + DevMap entry; + + clear_devlist(DeviceList); snd_ctl_card_info_malloc(&info); snd_pcm_info_malloc(&pcminfo); - DevList = malloc(sizeof(DevMap) * 1); - DevList[0].name = strdup(alsaDevice); - DevList[0].device = strdup(GetConfigValue("alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? - "device" : "capture", "default")); - idx = 1; + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + al_string_copy_cstr(&entry.name, alsaDevice); + al_string_copy_cstr(&entry.device_name, GetConfigValue("alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? + "device" : "capture", "default")); + VECTOR_PUSH_BACK(*DeviceList, entry); card = -1; if((err=snd_card_next(&card)) < 0) @@ -293,8 +309,9 @@ static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) dev = -1; while(1) { + const char *device_prefix = card_prefix; const char *devname; - void *temp; + char device[128]; if(snd_ctl_pcm_next_device(handle, &dev) < 0) ERR("snd_ctl_pcm_next_device failed\n"); @@ -310,28 +327,22 @@ static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) continue; } - temp = realloc(DevList, sizeof(DevMap) * (idx+1)); - if(temp) - { - const char *device_prefix = card_prefix; - char device[128]; - - DevList = temp; - devname = snd_pcm_info_get_name(pcminfo); + devname = snd_pcm_info_get_name(pcminfo); - snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev); - ConfigValueStr("alsa", name, &device_prefix); + snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev); + ConfigValueStr("alsa", name, &device_prefix); - snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", - cardname, devname, cardid, dev); - snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d", - device_prefix, cardid, dev); + snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", + cardname, devname, cardid, dev); + snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d", + device_prefix, cardid, dev); - TRACE("Got device \"%s\", \"%s\"\n", name, device); - DevList[idx].name = strdup(name); - DevList[idx].device = strdup(device); - idx++; - } + TRACE("Got device \"%s\", \"%s\"\n", name, device); + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + al_string_copy_cstr(&entry.name, name); + al_string_copy_cstr(&entry.device_name, device); + VECTOR_PUSH_BACK(*DeviceList, entry); } snd_ctl_close(handle); next_card: @@ -343,9 +354,6 @@ static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) snd_pcm_info_free(pcminfo); snd_ctl_card_info_free(info); - - *count = idx; - return DevList; } @@ -390,12 +398,11 @@ typedef struct ALCplaybackAlsa { ALsizei size; volatile int killNow; - althread_t thread; + althrd_t thread; } ALCplaybackAlsa; -DECLARE_ALCBACKEND_VTABLE(ALCplaybackAlsa); -static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr); -static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr); +static int ALCplaybackAlsa_mixerProc(void *ptr); +static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr); static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device); static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct) @@ -406,8 +413,12 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self); static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self); static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self); static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock) static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa) + +DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa); static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device) @@ -417,7 +428,7 @@ static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device) } -static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr) +static int ALCplaybackAlsa_mixerProc(void *ptr) { ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -429,7 +440,7 @@ static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr) int err; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); update_size = device->UpdateSize; num_updates = device->NumUpdates; @@ -509,7 +520,7 @@ static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr) return 0; } -static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr) +static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr) { ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -519,7 +530,7 @@ static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr) int err; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); update_size = device->UpdateSize; num_updates = device->NumUpdates; @@ -614,20 +625,22 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) if(name) { - size_t idx; + const DevMap *iter, *end; - if(!allDevNameMap) - allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); + if(VECTOR_SIZE(PlaybackDevices) == 0) + probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices); - for(idx = 0;idx < numDevNames;idx++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) { - if(strcmp(name, allDevNameMap[idx].name) == 0) + if(al_string_cmp_cstr(iter->name, name) == 0) { - driver = allDevNameMap[idx].device; + driver = al_string_get_cstr(iter->device_name); break; } } - if(idx == numDevNames) + if(iter == end) return ALC_INVALID_VALUE; } else @@ -636,6 +649,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) driver = GetConfigValue("alsa", "device", "default"); } + TRACE("Opening device \"%s\"\n", driver); err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if(err < 0) { @@ -646,7 +660,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } @@ -663,15 +677,14 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) unsigned int periodLen, bufferLen; snd_pcm_sw_params_t *sp = NULL; snd_pcm_hw_params_t *hp = NULL; + snd_pcm_format_t format = -1; snd_pcm_access_t access; - snd_pcm_format_t format; unsigned int periods; unsigned int rate; const char *funcerr; int allowmmap; int err; - format = -1; switch(device->FmtType) { case DevFmtByte: @@ -809,7 +822,7 @@ error: static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - ALuint (*thread_func)(ALvoid*) = NULL; + int (*thread_func)(void*) = NULL; snd_pcm_hw_params_t *hp = NULL; snd_pcm_access_t access; const char *funcerr; @@ -845,7 +858,8 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) } thread_func = ALCplaybackAlsa_mixerProc; } - if(!StartThread(&self->thread, thread_func, self)) + self->killNow = 0; + if(althrd_create(&self->thread, thread_func, self) != althrd_success) { ERR("Could not create playback thread\n"); free(self->buffer); @@ -863,13 +877,14 @@ error: static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self) { - if(self->thread) - { - self->killNow = 1; - StopThread(self->thread); - self->thread = NULL; - } - self->killNow = 0; + int res; + + if(self->killNow) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); + free(self->buffer); self->buffer = NULL; } @@ -889,14 +904,6 @@ static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self) } -static void ALCplaybackAlsa_Delete(ALCplaybackAlsa *self) -{ - free(self); -} - -DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa); - - typedef struct ALCcaptureAlsa { DERIVE_FROM_TYPE(ALCbackend); @@ -910,7 +917,6 @@ typedef struct ALCcaptureAlsa { snd_pcm_sframes_t last_avail; } ALCcaptureAlsa; -DECLARE_ALCBACKEND_VTABLE(ALCcaptureAlsa); static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device); static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct) @@ -921,8 +927,12 @@ static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self); static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self); static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self); +static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self); static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock) static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa) + +DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa); static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device) @@ -940,26 +950,28 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) snd_pcm_uframes_t bufferSizeInFrames; snd_pcm_uframes_t periodSizeInFrames; ALboolean needring = AL_FALSE; - snd_pcm_format_t format; + snd_pcm_format_t format = -1; const char *funcerr; int err; if(name) { - size_t idx; + const DevMap *iter, *end; - if(!allCaptureDevNameMap) - allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); + if(VECTOR_SIZE(CaptureDevices) == 0) + probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices); - for(idx = 0;idx < numCaptureDevNames;idx++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) { - if(strcmp(name, allCaptureDevNameMap[idx].name) == 0) + if(al_string_cmp_cstr(iter->name, name) == 0) { - driver = allCaptureDevNameMap[idx].device; + driver = al_string_get_cstr(iter->device_name); break; } } - if(idx == numCaptureDevNames) + if(iter == end) return ALC_INVALID_VALUE; } else @@ -968,6 +980,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) driver = GetConfigValue("alsa", "capture", "default"); } + TRACE("Opening device \"%s\"\n", driver); err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if(err < 0) { @@ -978,7 +991,6 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - format = -1; switch(device->FmtType) { case DevFmtByte: @@ -1056,7 +1068,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) } } - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; @@ -1114,11 +1126,12 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self) void *ptr; size = snd_pcm_frames_to_bytes(self->pcmHandle, avail); - ptr = realloc(self->buffer, size); + ptr = malloc(size); if(ptr) { + ALCcaptureAlsa_captureSamples(self, ptr, avail); + free(self->buffer); self->buffer = ptr; - ALCcaptureAlsa_captureSamples(self, self->buffer, avail); self->size = size; } } @@ -1150,7 +1163,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff if((snd_pcm_uframes_t)amt > samples) amt = samples; amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt); - memmove(buffer, self->buffer, amt); + memcpy(buffer, self->buffer, amt); if(self->size > amt) { @@ -1287,14 +1300,11 @@ static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self) return maxi64((ALint64)delay*1000000000/device->Frequency, 0); } -static void ALCcaptureAlsa_Delete(ALCcaptureAlsa *self) -{ - free(self); -} - -DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa); - +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } typedef struct ALCalsaBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); @@ -1303,6 +1313,9 @@ typedef struct ALCalsaBackendFactory { static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self)) { + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + if(!alsa_load()) return ALC_FALSE; return ALC_TRUE; @@ -1310,25 +1323,11 @@ static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self) static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self)) { - ALuint i; - - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); #ifdef HAVE_DYNLOAD if(alsa_handle) @@ -1346,36 +1345,16 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type) { - ALuint i; - switch(type) { case ALL_DEVICE_PROBE: - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device); - } - - free(allDevNameMap); - allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); - - for(i = 0;i < numDevNames;++i) - AppendAllDevicesList(allDevNameMap[i].name); + probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); break; case CAPTURE_DEVICE_PROBE: - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device); - } - - free(allCaptureDevNameMap); - allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); - - for(i = 0;i < numCaptureDevNames;++i) - AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); break; } } @@ -1386,8 +1365,9 @@ static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UN { ALCplaybackAlsa *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCplaybackAlsa_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCplaybackAlsa_Construct(backend, device); @@ -1397,8 +1377,9 @@ static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UN { ALCcaptureAlsa *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCcaptureAlsa_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCcaptureAlsa_Construct(backend, device); diff --git a/Alc/backends/base.c b/Alc/backends/base.c index fe797562..37e4ccc9 100644 --- a/Alc/backends/base.c +++ b/Alc/backends/base.c @@ -11,13 +11,15 @@ /* Base ALCbackend method implementations. */ void ALCbackend_Construct(ALCbackend *self, ALCdevice *device) { + int ret; self->mDevice = device; - InitializeCriticalSection(&self->mMutex); + ret = almtx_init(&self->mMutex, almtx_recursive); + assert(ret == althrd_success); } void ALCbackend_Destruct(ALCbackend *self) { - DeleteCriticalSection(&self->mMutex); + almtx_destroy(&self->mMutex); } ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self)) @@ -42,12 +44,14 @@ ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self)) void ALCbackend_lock(ALCbackend *self) { - EnterCriticalSection(&self->mMutex); + int ret = almtx_lock(&self->mMutex); + assert(ret == althrd_success); } void ALCbackend_unlock(ALCbackend *self) { - LeaveCriticalSection(&self->mMutex); + int ret = almtx_unlock(&self->mMutex); + assert(ret == althrd_success); } @@ -60,9 +64,11 @@ void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self)) /* Wrappers to use an old-style backend with the new interface. */ typedef struct PlaybackWrapper { DERIVE_FROM_TYPE(ALCbackend); + + const BackendFuncs *Funcs; } PlaybackWrapper; -static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device); +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs); static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct) static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name); static void PlaybackWrapper_close(PlaybackWrapper *self); @@ -74,62 +80,61 @@ static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples) static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self); static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock) static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock) -static void PlaybackWrapper_Delete(PlaybackWrapper *self); +DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper) DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper); -static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device) +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(PlaybackWrapper, ALCbackend, self); + + self->Funcs = funcs; } static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->OpenPlayback(device, name); + return self->Funcs->OpenPlayback(device, name); } static void PlaybackWrapper_close(PlaybackWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->Funcs->ClosePlayback(device); + self->Funcs->ClosePlayback(device); } static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->ResetPlayback(device); + return self->Funcs->ResetPlayback(device); } static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->StartPlayback(device); + return self->Funcs->StartPlayback(device); } static void PlaybackWrapper_stop(PlaybackWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->Funcs->StopPlayback(device); + self->Funcs->StopPlayback(device); } static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->GetLatency(device); -} - -static void PlaybackWrapper_Delete(PlaybackWrapper *self) -{ - free(self); + return self->Funcs->GetLatency(device); } typedef struct CaptureWrapper { DERIVE_FROM_TYPE(ALCbackend); + + const BackendFuncs *Funcs; } CaptureWrapper; -static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device); +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs); static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct) static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name); static void CaptureWrapper_close(CaptureWrapper *self); @@ -141,75 +146,72 @@ static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self); static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self); static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock) static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock) -static void CaptureWrapper_Delete(CaptureWrapper *self); +DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper) DEFINE_ALCBACKEND_VTABLE(CaptureWrapper); -static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device) +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(CaptureWrapper, ALCbackend, self); + + self->Funcs = funcs; } static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->OpenCapture(device, name); + return self->Funcs->OpenCapture(device, name); } static void CaptureWrapper_close(CaptureWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->Funcs->CloseCapture(device); + self->Funcs->CloseCapture(device); } static ALCboolean CaptureWrapper_start(CaptureWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->Funcs->StartCapture(device); + self->Funcs->StartCapture(device); return ALC_TRUE; } static void CaptureWrapper_stop(CaptureWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->Funcs->StopCapture(device); + self->Funcs->StopCapture(device); } static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->CaptureSamples(device, buffer, samples); + return self->Funcs->CaptureSamples(device, buffer, samples); } static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->AvailableSamples(device); + return self->Funcs->AvailableSamples(device); } static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return device->Funcs->GetLatency(device); -} - -static void CaptureWrapper_Delete(CaptureWrapper *self) -{ - free(self); + return self->Funcs->GetLatency(device); } -ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type) +ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type) { if(type == ALCbackend_Playback) { PlaybackWrapper *backend; - backend = malloc(sizeof(*backend)); + backend = PlaybackWrapper_New(sizeof(*backend)); if(!backend) return NULL; - PlaybackWrapper_Construct(backend, device); + PlaybackWrapper_Construct(backend, device, funcs); return STATIC_CAST(ALCbackend, backend); } @@ -218,10 +220,10 @@ ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type) { CaptureWrapper *backend; - backend = malloc(sizeof(*backend)); + backend = CaptureWrapper_New(sizeof(*backend)); if(!backend) return NULL; - CaptureWrapper_Construct(backend, device); + CaptureWrapper_Construct(backend, device, funcs); return STATIC_CAST(ALCbackend, backend); } diff --git a/Alc/backends/base.h b/Alc/backends/base.h index 1bbc1fb0..5573c178 100644 --- a/Alc/backends/base.h +++ b/Alc/backends/base.h @@ -2,7 +2,7 @@ #define AL_BACKENDS_BASE_H #include "alMain.h" -#include "compat.h" +#include "threads.h" struct ALCbackendVtable; @@ -12,7 +12,7 @@ typedef struct ALCbackend { ALCdevice *mDevice; - CRITICAL_SECTION mMutex; + almtx_t mMutex; } ALCbackend; void ALCbackend_Construct(ALCbackend *self, ALCdevice *device); @@ -42,12 +42,9 @@ struct ALCbackendVtable { void (*const lock)(ALCbackend*); void (*const unlock)(ALCbackend*); - void (*const Delete)(ALCbackend*); + void (*const Delete)(void*); }; -#define DECLARE_ALCBACKEND_VTABLE(T) \ -static const struct ALCbackendVtable T##_ALCbackend_vtable - #define DEFINE_ALCBACKEND_VTABLE(T) \ DECLARE_THUNK(T, ALCbackend, void, Destruct) \ DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \ @@ -60,9 +57,10 @@ DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \ DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \ DECLARE_THUNK(T, ALCbackend, void, lock) \ DECLARE_THUNK(T, ALCbackend, void, unlock) \ -DECLARE_THUNK(T, ALCbackend, void, Delete) \ +static void T##_ALCbackend_Delete(void *ptr) \ +{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \ \ -DECLARE_ALCBACKEND_VTABLE(T) = { \ +static const struct ALCbackendVtable T##_ALCbackend_vtable = { \ T##_ALCbackend_Destruct, \ \ T##_ALCbackend_open, \ @@ -125,9 +123,11 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \ ALCbackendFactory *ALCpulseBackendFactory_getFactory(void); ALCbackendFactory *ALCalsaBackendFactory_getFactory(void); ALCbackendFactory *ALCossBackendFactory_getFactory(void); +ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void); +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void); ALCbackendFactory *ALCnullBackendFactory_getFactory(void); ALCbackendFactory *ALCloopbackFactory_getFactory(void); -ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type); +ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type); #endif /* AL_BACKENDS_BASE_H */ diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c index 5c9b69c8..b3583ffd 100644 --- a/Alc/backends/coreaudio.c +++ b/Alc/backends/coreaudio.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <alloca.h> #include "alMain.h" #include "alu.h" @@ -180,7 +181,7 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName) return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); device->ExtraData = data; return ALC_NO_ERROR; } @@ -577,6 +578,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) if(data->ring == NULL) goto error; + al_string_copy_cstr(&device->DeviceName, deviceName); + return ALC_NO_ERROR; error: diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c index 6b108fba..3ca398ed 100644 --- a/Alc/backends/dsound.c +++ b/Alc/backends/dsound.c @@ -36,6 +36,9 @@ #include "alu.h" #include "threads.h" #include "compat.h" +#include "alstring.h" + +#include "backends/base.h" #ifndef DSSPEAKER_5POINT1 # define DSSPEAKER_5POINT1 0x00000006 @@ -55,54 +58,23 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +#ifdef HAVE_DYNLOAD static void *ds_handle; -static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); -static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext); -static HRESULT (WINAPI *pDirectSoundCaptureCreate)(LPCGUID pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter); -static HRESULT (WINAPI *pDirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext); +static HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext); +static HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext); #define DirectSoundCreate pDirectSoundCreate -#define DirectSoundEnumerateA pDirectSoundEnumerateA +#define DirectSoundEnumerateW pDirectSoundEnumerateW #define DirectSoundCaptureCreate pDirectSoundCaptureCreate -#define DirectSoundCaptureEnumerateA pDirectSoundCaptureEnumerateA - - -typedef struct { - // DirectSound Playback Device - IDirectSound *DS; - IDirectSoundBuffer *PrimaryBuffer; - IDirectSoundBuffer *Buffer; - IDirectSoundNotify *Notifies; - HANDLE NotifyEvent; - - volatile int killNow; - althread_t thread; -} DSoundPlaybackData; - -typedef struct { - // DirectSound Capture Device - IDirectSoundCapture *DSC; - IDirectSoundCaptureBuffer *DSCbuffer; - DWORD BufferBytes; - DWORD Cursor; - RingBuffer *Ring; -} DSoundCaptureData; - - -typedef struct { - ALCchar *name; - GUID guid; -} DevMap; - -static DevMap *PlaybackDeviceList; -static ALuint NumPlaybackDevices; -static DevMap *CaptureDeviceList; -static ALuint NumCaptureDevices; +#define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW +#endif -#define MAX_UPDATES 128 static ALCboolean DSoundLoad(void) { +#ifdef HAVE_DYNLOAD if(!ds_handle) { ds_handle = LoadLib("dsound.dll"); @@ -121,117 +93,133 @@ static ALCboolean DSoundLoad(void) } \ } while(0) LOAD_FUNC(DirectSoundCreate); - LOAD_FUNC(DirectSoundEnumerateA); + LOAD_FUNC(DirectSoundEnumerateW); LOAD_FUNC(DirectSoundCaptureCreate); - LOAD_FUNC(DirectSoundCaptureEnumerateA); + LOAD_FUNC(DirectSoundCaptureEnumerateW); #undef LOAD_FUNC } +#endif return ALC_TRUE; } -static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data)) +#define MAX_UPDATES 128 + +typedef struct { + al_string name; + GUID guid; +} DevMap; +DECL_VECTOR(DevMap) + +vector_DevMap PlaybackDevices; +vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *list) { - LPOLESTR guidstr = NULL; - char str[1024]; + DevMap *iter, *end; + + iter = VECTOR_ITER_BEGIN(*list); + end = VECTOR_ITER_END(*list); + for(;iter != end;++iter) + AL_STRING_DEINIT(iter->name); + VECTOR_RESIZE(*list, 0); +} + +static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data) +{ + vector_DevMap *devices = data; + OLECHAR *guidstr = NULL; + DevMap *iter, *end; + DevMap entry; HRESULT hr; - void *temp; int count; - ALuint i; if(!guid) return TRUE; + AL_STRING_INIT(entry.name); + count = 0; do { - if(count == 0) - snprintf(str, sizeof(str), "%s", desc); - else - snprintf(str, sizeof(str), "%s #%d", desc, count+1); + al_string_copy_wcstr(&entry.name, desc); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&entry.name, str); + } count++; - for(i = 0;i < NumPlaybackDevices;i++) + iter = VECTOR_ITER_BEGIN(*devices); + end = VECTOR_ITER_END(*devices); + for(;iter != end;++iter) { - if(strcmp(str, PlaybackDeviceList[i].name) == 0) + if(al_string_cmp(entry.name, iter->name) == 0) break; } - } while(i != NumPlaybackDevices); + } while(iter != end); + entry.guid = *guid; hr = StringFromCLSID(guid, &guidstr); if(SUCCEEDED(hr)) { - TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr); + TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr); CoTaskMemFree(guidstr); } - temp = realloc(PlaybackDeviceList, sizeof(DevMap) * (NumPlaybackDevices+1)); - if(temp) - { - PlaybackDeviceList = temp; - PlaybackDeviceList[NumPlaybackDevices].name = strdup(str); - PlaybackDeviceList[NumPlaybackDevices].guid = *guid; - NumPlaybackDevices++; - } + VECTOR_PUSH_BACK(*devices, entry); return TRUE; } -static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data)) -{ - LPOLESTR guidstr = NULL; - char str[1024]; - HRESULT hr; - void *temp; - int count; - ALuint i; +typedef struct ALCdsoundPlayback { + DERIVE_FROM_TYPE(ALCbackend); - if(!guid) - return TRUE; + IDirectSound *DS; + IDirectSoundBuffer *PrimaryBuffer; + IDirectSoundBuffer *Buffer; + IDirectSoundNotify *Notifies; + HANDLE NotifyEvent; - count = 0; - do { - if(count == 0) - snprintf(str, sizeof(str), "%s", desc); - else - snprintf(str, sizeof(str), "%s #%d", desc, count+1); - count++; + volatile int killNow; + althrd_t thread; +} ALCdsoundPlayback; - for(i = 0;i < NumCaptureDevices;i++) - { - if(strcmp(str, CaptureDeviceList[i].name) == 0) - break; - } - } while(i != NumCaptureDevices); +static int ALCdsoundPlayback_mixerProc(void *ptr); - hr = StringFromCLSID(guid, &guidstr); - if(SUCCEEDED(hr)) - { - TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr); - CoTaskMemFree(guidstr); - } +static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device); +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct) +static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name); +static void ALCdsoundPlayback_close(ALCdsoundPlayback *self); +static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self); +static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self); +static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self); +static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback) - temp = realloc(CaptureDeviceList, sizeof(DevMap) * (NumCaptureDevices+1)); - if(temp) - { - CaptureDeviceList = temp; - CaptureDeviceList[NumCaptureDevices].name = strdup(str); - CaptureDeviceList[NumCaptureDevices].guid = *guid; - NumCaptureDevices++; - } +DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback); - return TRUE; + +static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self); } -FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) +FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) { - ALCdevice *Device = (ALCdevice*)ptr; - DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData; + ALCdsoundPlayback *self = ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; DSBCAPS DSBCaps; DWORD LastCursor = 0; DWORD PlayCursor; - VOID *WritePtr1, *WritePtr2; + void *WritePtr1, *WritePtr2; DWORD WriteCnt1, WriteCnt2; BOOL Playing = FALSE; DWORD FrameSize; @@ -240,47 +228,47 @@ FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) HRESULT err; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); memset(&DSBCaps, 0, sizeof(DSBCaps)); DSBCaps.dwSize = sizeof(DSBCaps); - err = IDirectSoundBuffer_GetCaps(data->Buffer, &DSBCaps); + err = IDirectSoundBuffer_GetCaps(self->Buffer, &DSBCaps); if(FAILED(err)) { ERR("Failed to get buffer caps: 0x%lx\n", err); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); return 1; } - FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - FragSize = Device->UpdateSize * FrameSize; + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + FragSize = device->UpdateSize * FrameSize; - IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &LastCursor, NULL); - while(!data->killNow) + IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL); + while(!self->killNow) { // Get current play cursor - IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &PlayCursor, NULL); + IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL); avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; if(avail < FragSize) { if(!Playing) { - err = IDirectSoundBuffer_Play(data->Buffer, 0, 0, DSBPLAY_LOOPING); + err = IDirectSoundBuffer_Play(self->Buffer, 0, 0, DSBPLAY_LOOPING); if(FAILED(err)) { ERR("Failed to play buffer: 0x%lx\n", err); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); return 1; } Playing = TRUE; } - avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE); + avail = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE); if(avail != WAIT_OBJECT_0) ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); continue; @@ -290,18 +278,18 @@ FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) // Lock output buffer WriteCnt1 = 0; WriteCnt2 = 0; - err = IDirectSoundBuffer_Lock(data->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + err = IDirectSoundBuffer_Lock(self->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); // If the buffer is lost, restore it and lock if(err == DSERR_BUFFERLOST) { WARN("Buffer lost, restoring...\n"); - err = IDirectSoundBuffer_Restore(data->Buffer); + err = IDirectSoundBuffer_Restore(self->Buffer); if(SUCCEEDED(err)) { Playing = FALSE; LastCursor = 0; - err = IDirectSoundBuffer_Lock(data->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + err = IDirectSoundBuffer_Lock(self->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); } } @@ -309,18 +297,18 @@ FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) if(SUCCEEDED(err)) { // If we have an active context, mix data directly into output buffer otherwise fill with silence - aluMixData(Device, WritePtr1, WriteCnt1/FrameSize); - aluMixData(Device, WritePtr2, WriteCnt2/FrameSize); + aluMixData(device, WritePtr1, WriteCnt1/FrameSize); + aluMixData(device, WritePtr2, WriteCnt2/FrameSize); // Unlock output buffer only when successfully locked - IDirectSoundBuffer_Unlock(data->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); + IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); } else { ERR("Buffer lock error: %#lx\n", err); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); return 1; } @@ -332,94 +320,95 @@ FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr) return 0; } -static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName) { - DSoundPlaybackData *data = NULL; - LPGUID guid = NULL; - HRESULT hr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + GUID *guid = NULL; + HRESULT hr, hrcom; - if(!PlaybackDeviceList) + if(VECTOR_SIZE(PlaybackDevices) == 0) { - hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL); + /* Initialize COM to prevent name truncation */ + hrcom = CoInitialize(NULL); + hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); + if(SUCCEEDED(hrcom)) + CoUninitialize(); } - if(!deviceName && NumPlaybackDevices > 0) + if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0) { - deviceName = PlaybackDeviceList[0].name; - guid = &PlaybackDeviceList[0].guid; + deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name); + guid = &VECTOR_FRONT(PlaybackDevices).guid; } else { - ALuint i; + DevMap *iter, *end; - for(i = 0;i < NumPlaybackDevices;i++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;++iter) { - if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0) + if(al_string_cmp_cstr(iter->name, deviceName) == 0) { - guid = &PlaybackDeviceList[i].guid; + guid = &iter->guid; break; } } - if(i == NumPlaybackDevices) + if(iter == end) return ALC_INVALID_VALUE; } - //Initialise requested device - data = calloc(1, sizeof(DSoundPlaybackData)); - if(!data) - return ALC_OUT_OF_MEMORY; - hr = DS_OK; - data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->NotifyEvent == NULL) + self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(self->NotifyEvent == NULL) hr = E_FAIL; //DirectSound Init code if(SUCCEEDED(hr)) - hr = DirectSoundCreate(guid, &data->DS, NULL); + hr = DirectSoundCreate(guid, &self->DS, NULL); if(SUCCEEDED(hr)) - hr = IDirectSound_SetCooperativeLevel(data->DS, GetForegroundWindow(), DSSCL_PRIORITY); + hr = IDirectSound_SetCooperativeLevel(self->DS, GetForegroundWindow(), DSSCL_PRIORITY); if(FAILED(hr)) { - if(data->DS) - IDirectSound_Release(data->DS); - if(data->NotifyEvent) - CloseHandle(data->NotifyEvent); - free(data); + if(self->DS) + IDirectSound_Release(self->DS); + self->DS = NULL; + if(self->NotifyEvent) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + ERR("Device init failed: 0x%08lx\n", hr); return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); - device->ExtraData = data; + al_string_copy_cstr(&device->DeviceName, deviceName); + return ALC_NO_ERROR; } -static void DSoundClosePlayback(ALCdevice *device) +static void ALCdsoundPlayback_close(ALCdsoundPlayback *self) { - DSoundPlaybackData *data = device->ExtraData; - - if(data->Notifies) - IDirectSoundNotify_Release(data->Notifies); - data->Notifies = NULL; - if(data->Buffer) - IDirectSoundBuffer_Release(data->Buffer); - data->Buffer = NULL; - if(data->PrimaryBuffer != NULL) - IDirectSoundBuffer_Release(data->PrimaryBuffer); - data->PrimaryBuffer = NULL; - - IDirectSound_Release(data->DS); - CloseHandle(data->NotifyEvent); - free(data); - device->ExtraData = NULL; + if(self->Notifies) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + + IDirectSound_Release(self->DS); + self->DS = NULL; + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; } -static ALCboolean DSoundResetPlayback(ALCdevice *device) +static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) { - DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; DSBUFFERDESC DSBDescription; WAVEFORMATEXTENSIBLE OutputType; DWORD speakers; @@ -427,15 +416,15 @@ static ALCboolean DSoundResetPlayback(ALCdevice *device) memset(&OutputType, 0, sizeof(OutputType)); - if(data->Notifies) - IDirectSoundNotify_Release(data->Notifies); - data->Notifies = NULL; - if(data->Buffer) - IDirectSoundBuffer_Release(data->Buffer); - data->Buffer = NULL; - if(data->PrimaryBuffer != NULL) - IDirectSoundBuffer_Release(data->PrimaryBuffer); - data->PrimaryBuffer = NULL; + if(self->Notifies) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; switch(device->FmtType) { @@ -458,7 +447,7 @@ static ALCboolean DSoundResetPlayback(ALCdevice *device) break; } - hr = IDirectSound_GetSpeakerConfig(data->DS, &speakers); + hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers); if(SUCCEEDED(hr)) { if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) @@ -551,21 +540,21 @@ retry_open: else OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - if(data->PrimaryBuffer) - IDirectSoundBuffer_Release(data->PrimaryBuffer); - data->PrimaryBuffer = NULL; + if(self->PrimaryBuffer) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; } else { - if(SUCCEEDED(hr) && !data->PrimaryBuffer) + if(SUCCEEDED(hr) && !self->PrimaryBuffer) { memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); DSBDescription.dwSize=sizeof(DSBUFFERDESC); DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; - hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->PrimaryBuffer, NULL); + hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->PrimaryBuffer, NULL); } if(SUCCEEDED(hr)) - hr = IDirectSoundBuffer_SetFormat(data->PrimaryBuffer,&OutputType.Format); + hr = IDirectSoundBuffer_SetFormat(self->PrimaryBuffer,&OutputType.Format); } if(SUCCEEDED(hr)) @@ -583,7 +572,7 @@ retry_open: DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * OutputType.Format.nBlockAlign; DSBDescription.lpwfxFormat=&OutputType.Format; - hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->Buffer, NULL); + hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->Buffer, NULL); if(FAILED(hr) && device->FmtType == DevFmtFloat) { device->FmtType = DevFmtShort; @@ -593,7 +582,7 @@ retry_open: if(SUCCEEDED(hr)) { - hr = IDirectSoundBuffer_QueryInterface(data->Buffer, &IID_IDirectSoundNotify, (LPVOID *)&data->Notifies); + hr = IDirectSoundBuffer_QueryInterface(self->Buffer, &IID_IDirectSoundNotify, (void**)&self->Notifies); if(SUCCEEDED(hr)) { DSBPOSITIONNOTIFY notifies[MAX_UPDATES]; @@ -603,97 +592,130 @@ retry_open: { notifies[i].dwOffset = i * device->UpdateSize * OutputType.Format.nBlockAlign; - notifies[i].hEventNotify = data->NotifyEvent; + notifies[i].hEventNotify = self->NotifyEvent; } - if(IDirectSoundNotify_SetNotificationPositions(data->Notifies, device->NumUpdates, notifies) != DS_OK) + if(IDirectSoundNotify_SetNotificationPositions(self->Notifies, device->NumUpdates, notifies) != DS_OK) hr = E_FAIL; } } if(FAILED(hr)) { - if(data->Notifies != NULL) - IDirectSoundNotify_Release(data->Notifies); - data->Notifies = NULL; - if(data->Buffer != NULL) - IDirectSoundBuffer_Release(data->Buffer); - data->Buffer = NULL; - if(data->PrimaryBuffer != NULL) - IDirectSoundBuffer_Release(data->PrimaryBuffer); - data->PrimaryBuffer = NULL; + if(self->Notifies != NULL) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer != NULL) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; return ALC_FALSE; } - ResetEvent(data->NotifyEvent); + ResetEvent(self->NotifyEvent); SetDefaultWFXChannelOrder(device); return ALC_TRUE; } -static ALCboolean DSoundStartPlayback(ALCdevice *device) +static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self) { - DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData; - - if(!StartThread(&data->thread, DSoundPlaybackProc, device)) + self->killNow = 0; + if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; } -static void DSoundStopPlayback(ALCdevice *device) +static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self) { - DSoundPlaybackData *data = device->ExtraData; + int res; - if(!data->thread) + if(self->killNow) return; - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + self->killNow = 1; + althrd_join(self->thread, &res); - data->killNow = 0; - IDirectSoundBuffer_Stop(data->Buffer); + IDirectSoundBuffer_Stop(self->Buffer); } -static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName) + +typedef struct ALCdsoundCapture { + DERIVE_FROM_TYPE(ALCbackend); + + IDirectSoundCapture *DSC; + IDirectSoundCaptureBuffer *DSCbuffer; + DWORD BufferBytes; + DWORD Cursor; + RingBuffer *Ring; +} ALCdsoundCapture; + +static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct) +static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name); +static void ALCdsoundCapture_close(ALCdsoundCapture *self); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset) +static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self); +static void ALCdsoundCapture_stop(ALCdsoundCapture *self); +static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture) + +DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture); + +static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCdsoundCapture, ALCbackend, self); +} + + +static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName) { - DSoundCaptureData *data = NULL; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; WAVEFORMATEXTENSIBLE InputType; DSCBUFFERDESC DSCBDescription; - LPGUID guid = NULL; + GUID *guid = NULL; HRESULT hr, hrcom; ALuint samples; - if(!CaptureDeviceList) + if(VECTOR_SIZE(CaptureDevices) == 0) { /* Initialize COM to prevent name truncation */ hrcom = CoInitialize(NULL); - hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL); + hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); if(SUCCEEDED(hrcom)) CoUninitialize(); } - if(!deviceName && NumCaptureDevices > 0) + if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0) { - deviceName = CaptureDeviceList[0].name; - guid = &CaptureDeviceList[0].guid; + deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name); + guid = &VECTOR_FRONT(CaptureDevices).guid; } else { - ALuint i; + DevMap *iter, *end; - for(i = 0;i < NumCaptureDevices;i++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;++iter) { - if(strcmp(deviceName, CaptureDeviceList[i].name) == 0) + if(al_string_cmp_cstr(iter->name, deviceName) == 0) { - guid = &CaptureDeviceList[i].guid; + guid = &iter->guid; break; } } - if(i == NumCaptureDevices) + if(iter == end) return ALC_INVALID_VALUE; } @@ -712,16 +734,8 @@ static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName) break; } - //Initialise requested device - data = calloc(1, sizeof(DSoundCaptureData)); - if(!data) - return ALC_OUT_OF_MEMORY; - - hr = DS_OK; - //DirectSoundCapture Init code - if(SUCCEEDED(hr)) - hr = DirectSoundCaptureCreate(guid, &data->DSC, NULL); + hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL); if(SUCCEEDED(hr)) { memset(&InputType, 0, sizeof(InputType)); @@ -806,12 +820,12 @@ static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName) DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign; DSCBDescription.lpwfxFormat = &InputType.Format; - hr = IDirectSoundCapture_CreateCaptureBuffer(data->DSC, &DSCBDescription, &data->DSCbuffer, NULL); + hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL); } if(SUCCEEDED(hr)) { - data->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates); - if(data->Ring == NULL) + self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates); + if(self->Ring == NULL) hr = DSERR_OUTOFMEMORY; } @@ -819,218 +833,237 @@ static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName) { ERR("Device init failed: 0x%08lx\n", hr); - DestroyRingBuffer(data->Ring); - data->Ring = NULL; - if(data->DSCbuffer != NULL) - IDirectSoundCaptureBuffer_Release(data->DSCbuffer); - data->DSCbuffer = NULL; - if(data->DSC) - IDirectSoundCapture_Release(data->DSC); - data->DSC = NULL; + DestroyRingBuffer(self->Ring); + self->Ring = NULL; + if(self->DSCbuffer != NULL) + IDirectSoundCaptureBuffer_Release(self->DSCbuffer); + self->DSCbuffer = NULL; + if(self->DSC) + IDirectSoundCapture_Release(self->DSC); + self->DSC = NULL; - free(data); return ALC_INVALID_VALUE; } - data->BufferBytes = DSCBDescription.dwBufferBytes; + self->BufferBytes = DSCBDescription.dwBufferBytes; SetDefaultWFXChannelOrder(device); - device->DeviceName = strdup(deviceName); - device->ExtraData = data; + al_string_copy_cstr(&device->DeviceName, deviceName); return ALC_NO_ERROR; } -static void DSoundCloseCapture(ALCdevice *device) +static void ALCdsoundCapture_close(ALCdsoundCapture *self) { - DSoundCaptureData *data = device->ExtraData; + DestroyRingBuffer(self->Ring); + self->Ring = NULL; - DestroyRingBuffer(data->Ring); - data->Ring = NULL; - - if(data->DSCbuffer != NULL) + if(self->DSCbuffer != NULL) { - IDirectSoundCaptureBuffer_Stop(data->DSCbuffer); - IDirectSoundCaptureBuffer_Release(data->DSCbuffer); - data->DSCbuffer = NULL; + IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); + IDirectSoundCaptureBuffer_Release(self->DSCbuffer); + self->DSCbuffer = NULL; } - IDirectSoundCapture_Release(data->DSC); - data->DSC = NULL; - - free(data); - device->ExtraData = NULL; + IDirectSoundCapture_Release(self->DSC); + self->DSC = NULL; } -static void DSoundStartCapture(ALCdevice *device) +static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self) { - DSoundCaptureData *data = device->ExtraData; HRESULT hr; - hr = IDirectSoundCaptureBuffer_Start(data->DSCbuffer, DSCBSTART_LOOPING); + hr = IDirectSoundCaptureBuffer_Start(self->DSCbuffer, DSCBSTART_LOOPING); if(FAILED(hr)) { ERR("start failed: 0x%08lx\n", hr); - aluHandleDisconnect(device); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_FALSE; } + + return ALC_TRUE; } -static void DSoundStopCapture(ALCdevice *device) +static void ALCdsoundCapture_stop(ALCdsoundCapture *self) { - DSoundCaptureData *data = device->ExtraData; HRESULT hr; - hr = IDirectSoundCaptureBuffer_Stop(data->DSCbuffer); + hr = IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); if(FAILED(hr)) { ERR("stop failed: 0x%08lx\n", hr); - aluHandleDisconnect(device); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); } } -static ALCenum DSoundCaptureSamples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples) +static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples) { - DSoundCaptureData *data = Device->ExtraData; - ReadRingBuffer(data->Ring, pBuffer, lSamples); + ReadRingBuffer(self->Ring, buffer, samples); return ALC_NO_ERROR; } -static ALCuint DSoundAvailableSamples(ALCdevice *Device) +static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) { - DSoundCaptureData *data = Device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; DWORD ReadCursor, LastCursor, BufferBytes, NumBytes; - VOID *ReadPtr1, *ReadPtr2; + void *ReadPtr1, *ReadPtr2; DWORD ReadCnt1, ReadCnt2; DWORD FrameSize; HRESULT hr; - if(!Device->Connected) + if(!device->Connected) goto done; - FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - BufferBytes = data->BufferBytes; - LastCursor = data->Cursor; + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + BufferBytes = self->BufferBytes; + LastCursor = self->Cursor; - hr = IDirectSoundCaptureBuffer_GetCurrentPosition(data->DSCbuffer, NULL, &ReadCursor); + hr = IDirectSoundCaptureBuffer_GetCurrentPosition(self->DSCbuffer, NULL, &ReadCursor); if(SUCCEEDED(hr)) { NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes; if(NumBytes == 0) goto done; - hr = IDirectSoundCaptureBuffer_Lock(data->DSCbuffer, LastCursor, NumBytes, + hr = IDirectSoundCaptureBuffer_Lock(self->DSCbuffer, LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0); } if(SUCCEEDED(hr)) { - WriteRingBuffer(data->Ring, ReadPtr1, ReadCnt1/FrameSize); + WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize); if(ReadPtr2 != NULL) - WriteRingBuffer(data->Ring, ReadPtr2, ReadCnt2/FrameSize); - hr = IDirectSoundCaptureBuffer_Unlock(data->DSCbuffer, + WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize); + hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer, ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2); - data->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes; + self->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes; } if(FAILED(hr)) { ERR("update failed: 0x%08lx\n", hr); - aluHandleDisconnect(Device); + aluHandleDisconnect(device); } done: - return RingBufferSize(data->Ring); + return RingBufferSize(self->Ring); } -static const BackendFuncs DSoundFuncs = { - DSoundOpenPlayback, - DSoundClosePlayback, - DSoundResetPlayback, - DSoundStartPlayback, - DSoundStopPlayback, - DSoundOpenCapture, - DSoundCloseCapture, - DSoundStartCapture, - DSoundStopCapture, - DSoundCaptureSamples, - DSoundAvailableSamples, - ALCdevice_GetLatencyDefault -}; +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } +typedef struct ALCdsoundBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCdsoundBackendFactory; +#define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } } -ALCboolean alcDSoundInit(BackendFuncs *FuncList) +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void); + +static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self); +static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self); +static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type); +static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory); + + +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void) { + static ALCdsoundBackendFactory factory = ALCDSOUNDBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory* UNUSED(self)) +{ + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + if(!DSoundLoad()) return ALC_FALSE; - *FuncList = DSoundFuncs; return ALC_TRUE; } -void alcDSoundDeinit(void) +static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory* UNUSED(self)) { - ALuint i; - - for(i = 0;i < NumPlaybackDevices;++i) - free(PlaybackDeviceList[i].name); - free(PlaybackDeviceList); - PlaybackDeviceList = NULL; - NumPlaybackDevices = 0; + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); - for(i = 0;i < NumCaptureDevices;++i) - free(CaptureDeviceList[i].name); - free(CaptureDeviceList); - CaptureDeviceList = NULL; - NumCaptureDevices = 0; + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); +#ifdef HAVE_DYNLOAD if(ds_handle) CloseLib(ds_handle); ds_handle = NULL; +#endif } -void alcDSoundProbe(enum DevProbe type) +static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type) { HRESULT hr, hrcom; - ALuint i; + /* Initialize COM to prevent name truncation */ + hrcom = CoInitialize(NULL); switch(type) { case ALL_DEVICE_PROBE: - for(i = 0;i < NumPlaybackDevices;++i) - free(PlaybackDeviceList[i].name); - free(PlaybackDeviceList); - PlaybackDeviceList = NULL; - NumPlaybackDevices = 0; - - hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL); + clear_devlist(&PlaybackDevices); + hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr); - else - { - for(i = 0;i < NumPlaybackDevices;i++) - AppendAllDevicesList(PlaybackDeviceList[i].name); - } + ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); break; case CAPTURE_DEVICE_PROBE: - for(i = 0;i < NumCaptureDevices;++i) - free(CaptureDeviceList[i].name); - free(CaptureDeviceList); - CaptureDeviceList = NULL; - NumCaptureDevices = 0; - - /* Initialize COM to prevent name truncation */ - hrcom = CoInitialize(NULL); - hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL); + clear_devlist(&CaptureDevices); + hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr); - else - { - for(i = 0;i < NumCaptureDevices;i++) - AppendCaptureDeviceList(CaptureDeviceList[i].name); - } - if(SUCCEEDED(hrcom)) - CoUninitialize(); + ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); break; } + if(SUCCEEDED(hrcom)) + CoUninitialize(); +} + +static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCdsoundPlayback *backend; + + backend = ALCdsoundPlayback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCdsoundPlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + if(type == ALCbackend_Capture) + { + ALCdsoundCapture *backend; + + backend = ALCdsoundCapture_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCdsoundCapture_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; } diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c index cd5b1a1e..505dfacf 100644 --- a/Alc/backends/loopback.c +++ b/Alc/backends/loopback.c @@ -44,7 +44,7 @@ static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples) static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency) static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock) -static void ALCloopback_Delete(ALCloopback *self); +DECLARE_DEFAULT_ALLOCATORS(ALCloopback) DEFINE_ALCBACKEND_VTABLE(ALCloopback); @@ -59,7 +59,7 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } @@ -83,12 +83,6 @@ static void ALCloopback_stop(ALCloopback* UNUSED(self)) } -static void ALCloopback_Delete(ALCloopback *self) -{ - free(self); -} - - typedef struct ALCloopbackFactory { DERIVE_FROM_TYPE(ALCbackendFactory); } ALCloopbackFactory; @@ -127,14 +121,18 @@ static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevP static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) { - ALCloopback *backend; + if(type == ALCbackend_Loopback) + { + ALCloopback *backend; - assert(type == ALCbackend_Loopback); + backend = ALCloopback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); - backend = calloc(1, sizeof(*backend)); - if(!backend) return NULL; + ALCloopback_Construct(backend, device); - ALCloopback_Construct(backend, device); + return STATIC_CAST(ALCbackend, backend); + } - return STATIC_CAST(ALCbackend, backend); + return NULL; } diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c index b93ff667..d732c3e1 100644 --- a/Alc/backends/mmdevapi.c +++ b/Alc/backends/mmdevapi.c @@ -42,6 +42,9 @@ #include "alu.h" #include "threads.h" #include "compat.h" +#include "alstring.h" + +#include "backends/base.h" DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); @@ -59,31 +62,27 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, typedef struct { + al_string name; WCHAR *devid; +} DevMap; +DECL_VECTOR(DevMap) - IMMDevice *mmdev; - IAudioClient *client; - IAudioRenderClient *render; - HANDLE NotifyEvent; - - HANDLE MsgEvent; - - volatile UINT32 Padding; - - volatile int killNow; - althread_t thread; -} MMDevApiData; - +static void clear_devlist(vector_DevMap *list) +{ + DevMap *iter, *end; -typedef struct { - ALCchar *name; - WCHAR *devid; -} DevMap; + iter = VECTOR_ITER_BEGIN(*list); + end = VECTOR_ITER_END(*list); + for(;iter != end;iter++) + { + AL_STRING_DEINIT(iter->name); + free(iter->devid); + } + VECTOR_RESIZE(*list, 0); +} -static DevMap *PlaybackDeviceList; -static ALuint NumPlaybackDevices; -static DevMap *CaptureDeviceList; -static ALuint NumCaptureDevices; +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; static HANDLE ThreadHdl; @@ -103,6 +102,12 @@ typedef struct { #define WM_USER_Enumerate (WM_USER+5) #define WM_USER_Last (WM_USER+5) +static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res) +{ + req->result = res; + SetEvent(req->FinishedEvt); +} + static HRESULT WaitForResponse(ThreadRequest *req) { if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) @@ -112,45 +117,32 @@ static HRESULT WaitForResponse(ThreadRequest *req) } -static ALCchar *get_device_name(IMMDevice *device) +static void get_device_name(IMMDevice *device, al_string *name) { - ALCchar *name = NULL; IPropertyStore *ps; PROPVARIANT pvname; HRESULT hr; - int len; hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return calloc(1, 1); + return; } PropVariantInit(&pvname); hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname); if(FAILED(hr)) - { WARN("GetValue failed: 0x%08lx\n", hr); - name = calloc(1, 1); - } else - { - if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0) - { - name = calloc(1, len); - WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL); - } - } + al_string_copy_wcstr(name, pvname.pwszVal); PropVariantClear(&pvname); IPropertyStore_Release(ps); - - return name; } -static void add_device(IMMDevice *device, DevMap *devmap) +static void add_device(IMMDevice *device, vector_DevMap *list) { LPWSTR devid; HRESULT hr; @@ -158,48 +150,52 @@ static void add_device(IMMDevice *device, DevMap *devmap) hr = IMMDevice_GetId(device, &devid); if(SUCCEEDED(hr)) { - devmap->devid = strdupW(devid); - devmap->name = get_device_name(device); - TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid); + DevMap entry; + AL_STRING_INIT(entry.name); + + entry.devid = strdupW(devid); + get_device_name(device, &entry.name); + CoTaskMemFree(devid); + + TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid); + VECTOR_PUSH_BACK(*list, entry); } } -static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs) +static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list) { IMMDeviceCollection *coll; IMMDevice *defdev = NULL; - DevMap *devlist = NULL; HRESULT hr; UINT count; - UINT idx; UINT i; hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll); if(FAILED(hr)) { ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); - return NULL; + return hr; } - idx = count = 0; + count = 0; hr = IMMDeviceCollection_GetCount(coll, &count); if(SUCCEEDED(hr) && count > 0) { - devlist = calloc(count, sizeof(*devlist)); - if(!devlist) + clear_devlist(list); + if(!VECTOR_RESERVE(*list, count+1)) { IMMDeviceCollection_Release(coll); - return NULL; + return E_OUTOFMEMORY; } hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir, eMultimedia, &defdev); } if(SUCCEEDED(hr) && defdev != NULL) - add_device(defdev, &devlist[idx++]); + add_device(defdev, list); - for(i = 0;i < count && idx < count;++i) + for(i = 0;i < count;++i) { IMMDevice *device; @@ -207,7 +203,7 @@ static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALu continue; if(device != defdev) - add_device(device, &devlist[idx++]); + add_device(device, list); IMMDevice_Release(device); } @@ -215,15 +211,269 @@ static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALu if(defdev) IMMDevice_Release(defdev); IMMDeviceCollection_Release(coll); - *numdevs = idx; - return devlist; + return S_OK; } -FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr) +/* Proxy interface used by the message handler. */ +struct ALCmmdevProxyVtable; + +typedef struct ALCmmdevProxy { + const struct ALCmmdevProxyVtable *vtbl; +} ALCmmdevProxy; + +struct ALCmmdevProxyVtable { + HRESULT (*const openProxy)(ALCmmdevProxy*); + void (*const closeProxy)(ALCmmdevProxy*); + + HRESULT (*const resetProxy)(ALCmmdevProxy*); + HRESULT (*const startProxy)(ALCmmdevProxy*); + void (*const stopProxy)(ALCmmdevProxy*); +}; + +#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \ + \ +static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \ + T##_ALCmmdevProxy_openProxy, \ + T##_ALCmmdevProxy_closeProxy, \ + T##_ALCmmdevProxy_resetProxy, \ + T##_ALCmmdevProxy_startProxy, \ + T##_ALCmmdevProxy_stopProxy, \ +} + +static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { } +static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { } + +static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) { - ALCdevice *device = ptr; - MMDevApiData *data = device->ExtraData; + ThreadRequest *req = ptr; + IMMDeviceEnumerator *Enumerator; + ALuint deviceCount = 0; + ALCmmdevProxy *proxy; + HRESULT hr, cohr; + MSG msg; + + TRACE("Starting message thread\n"); + + cohr = CoInitialize(NULL); + if(FAILED(cohr)) + { + WARN("Failed to initialize COM: 0x%08lx\n", cohr); + ReturnMsgResponse(req, cohr); + return 0; + } + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(FAILED(hr)) + { + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); + CoUninitialize(); + ReturnMsgResponse(req, hr); + return 0; + } + Enumerator = ptr; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + + CoUninitialize(); + + /* HACK: Force Windows to create a message queue for this thread before + * returning success, otherwise PostThreadMessage may fail if it gets + * called before GetMessage. + */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + TRACE("Message thread initialization complete\n"); + ReturnMsgResponse(req, S_OK); + + TRACE("Starting message loop\n"); + while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last)) + { + TRACE("Got message %u\n", msg.message); + switch(msg.message) + { + case WM_USER_OpenDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = cohr = S_OK; + if(++deviceCount == 1) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) + hr = V0(proxy,openProxy)(); + if(FAILED(hr)) + { + if(--deviceCount == 0 && SUCCEEDED(cohr)) + CoUninitialize(); + } + + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_ResetDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = V0(proxy,resetProxy)(); + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_StartDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = V0(proxy,startProxy)(); + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_StopDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + V0(proxy,stopProxy)(); + ReturnMsgResponse(req, S_OK); + continue; + + case WM_USER_CloseDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + V0(proxy,closeProxy)(); + if(--deviceCount == 0) + CoUninitialize(); + + ReturnMsgResponse(req, S_OK); + continue; + + case WM_USER_Enumerate: + req = (ThreadRequest*)msg.wParam; + + hr = cohr = S_OK; + if(++deviceCount == 1) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + Enumerator = ptr; + + if(msg.lParam == ALL_DEVICE_PROBE) + hr = probe_devices(Enumerator, eRender, &PlaybackDevices); + else if(msg.lParam == CAPTURE_DEVICE_PROBE) + hr = probe_devices(Enumerator, eCapture, &CaptureDevices); + + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + + if(--deviceCount == 0 && SUCCEEDED(cohr)) + CoUninitialize(); + + ReturnMsgResponse(req, hr); + continue; + + default: + ERR("Unexpected message: %u\n", msg.message); + continue; + } + } + TRACE("Message loop finished\n"); + + return 0; +} + + +typedef struct ALCmmdevPlayback { + DERIVE_FROM_TYPE(ALCbackend); + DERIVE_FROM_TYPE(ALCmmdevProxy); + + WCHAR *devid; + + IMMDevice *mmdev; + IAudioClient *client; + IAudioRenderClient *render; + HANDLE NotifyEvent; + + HANDLE MsgEvent; + + volatile UINT32 Padding; + + volatile int killNow; + althrd_t thread; +} ALCmmdevPlayback; + +static int ALCmmdevPlayback_mixerProc(void *arg); + +static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device); +static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self); +static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name); +static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_close(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self); +static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self); +static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self); +static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self); +static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self); +static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self); +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback) + +DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback); +DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback); + + +static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device) +{ + SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self); + SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self); + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self)); + + self->devid = NULL; + + self->mmdev = NULL; + self->client = NULL; + self->render = NULL; + self->NotifyEvent = NULL; + + self->MsgEvent = NULL; + + self->Padding = 0; + + self->killNow = 0; +} + +static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self) +{ + if(self->NotifyEvent != NULL) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + if(self->MsgEvent != NULL) + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + free(self->devid); + self->devid = NULL; + + ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self)); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) +{ + ALCmmdevPlayback *self = arg; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; UINT32 buffer_len, written; ALuint update_size, len; BYTE *buffer; @@ -236,17 +486,17 @@ FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr) ALCdevice_Lock(device); aluHandleDisconnect(device); ALCdevice_Unlock(device); - return 0; + return 1; } SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); update_size = device->UpdateSize; buffer_len = update_size * device->NumUpdates; - while(!data->killNow) + while(!self->killNow) { - hr = IAudioClient_GetCurrentPadding(data->client, &written); + hr = IAudioClient_GetCurrentPadding(self->client, &written); if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); @@ -255,27 +505,27 @@ FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr) ALCdevice_Unlock(device); break; } - data->Padding = written; + self->Padding = written; len = buffer_len - written; if(len < update_size) { DWORD res; - res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE); + res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE); if(res != WAIT_OBJECT_0) ERR("WaitForSingleObjectEx error: 0x%lx\n", res); continue; } len -= len%update_size; - hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer); + hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer); if(SUCCEEDED(hr)) { ALCdevice_Lock(device); aluMixData(device, buffer, len); - data->Padding = written + len; + self->Padding = written + len; ALCdevice_Unlock(device); - hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0); + hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0); } if(FAILED(hr)) { @@ -286,7 +536,7 @@ FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr) break; } } - data->Padding = 0; + self->Padding = 0; CoUninitialize(); return 0; @@ -332,16 +582,178 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX * return ALC_TRUE; } -static HRESULT DoReset(ALCdevice *device) + +static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName) +{ + HRESULT hr = S_OK; + + self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(self->NotifyEvent == NULL || self->MsgEvent == NULL) + { + ERR("Failed to create message events: %lu\n", GetLastError()); + hr = E_FAIL; + } + + if(SUCCEEDED(hr)) + { + if(deviceName) + { + const DevMap *iter, *end; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + { + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE)) + (void)WaitForResponse(&req); + } + + hr = E_FAIL; + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) + { + if(al_string_cmp_cstr(iter->name, deviceName) == 0) + { + self->devid = strdupW(iter->devid); + hr = S_OK; + break; + } + } + if(FAILED(hr)) + WARN("Failed to find device name matching \"%s\"\n", deviceName); + } + } + + if(SUCCEEDED(hr)) + { + ThreadRequest req = { self->MsgEvent, 0 }; + + hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); + else + ERR("Failed to post thread message: %lu\n", GetLastError()); + } + + if(FAILED(hr)) + { + if(self->NotifyEvent != NULL) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + if(self->MsgEvent != NULL) + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + free(self->devid); + self->devid = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_INVALID_VALUE; + } + + return ALC_NO_ERROR; +} + +static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + void *ptr; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + IMMDeviceEnumerator *Enumerator = ptr; + if(!self->devid) + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev); + else + hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev); + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(SUCCEEDED(hr)) + { + self->client = ptr; + get_device_name(self->mmdev, &device->DeviceName); + } + + if(FAILED(hr)) + { + if(self->mmdev) + IMMDevice_Release(self->mmdev); + self->mmdev = NULL; + } + + return hr; +} + + +static void ALCmmdevPlayback_close(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + (void)WaitForResponse(&req); + + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + + free(self->devid); + self->devid = NULL; +} + +static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self) { - MMDevApiData *data = device->ExtraData; + if(self->client) + IAudioClient_Release(self->client); + self->client = NULL; + + if(self->mmdev) + IMMDevice_Release(self->mmdev); + self->mmdev = NULL; +} + + +static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; WAVEFORMATEXTENSIBLE OutputType; WAVEFORMATEX *wfx = NULL; REFERENCE_TIME min_per, buf_time; UINT32 buffer_len, min_len; + void *ptr = NULL; HRESULT hr; - hr = IAudioClient_GetMixFormat(data->client, &wfx); + if(self->client) + IAudioClient_Release(self->client); + self->client = NULL; + + hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(FAILED(hr)) + { + ERR("Failed to reactivate audio client: 0x%08lx\n", hr); + return hr; + } + self->client = ptr; + + hr = IAudioClient_GetMixFormat(self->client, &wfx); if(FAILED(hr)) { ERR("Failed to get mix format: 0x%08lx\n", hr); @@ -451,11 +863,11 @@ static HRESULT DoReset(ALCdevice *device) OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * OutputType.Format.nBlockAlign; - hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); if(FAILED(hr)) { ERR("Failed to check format support: 0x%08lx\n", hr); - hr = IAudioClient_GetMixFormat(data->client, &wfx); + hr = IAudioClient_GetMixFormat(self->client, &wfx); } if(FAILED(hr)) { @@ -527,7 +939,7 @@ static HRESULT DoReset(ALCdevice *device) SetDefaultWFXChannelOrder(device); - hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, + hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time, 0, &OutputType.Format, NULL); if(FAILED(hr)) @@ -536,14 +948,14 @@ static HRESULT DoReset(ALCdevice *device) return hr; } - hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL); + hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL); if(SUCCEEDED(hr)) { min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000); /* Find the nearest multiple of the period size to the update size */ if(min_len < device->UpdateSize) min_len *= (device->UpdateSize + min_len/2)/min_len; - hr = IAudioClient_GetBufferSize(data->client, &buffer_len); + hr = IAudioClient_GetBufferSize(self->client, &buffer_len); } if(FAILED(hr)) { @@ -560,247 +972,106 @@ static HRESULT DoReset(ALCdevice *device) device->UpdateSize = buffer_len / device->NumUpdates; } + hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent); + if(FAILED(hr)) + { + ERR("Failed to set event handle: 0x%08lx\n", hr); + return hr; + } + return hr; } -static DWORD CALLBACK MMDevApiMsgProc(void *ptr) +static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self) { - ThreadRequest *req = ptr; - IMMDeviceEnumerator *Enumerator; - ALuint deviceCount = 0; - MMDevApiData *data; - ALCdevice *device; - HRESULT hr, cohr; - MSG msg; - - TRACE("Starting message thread\n"); - - cohr = CoInitialize(NULL); - if(FAILED(cohr)) - { - WARN("Failed to initialize COM: 0x%08lx\n", cohr); - req->result = cohr; - SetEvent(req->FinishedEvt); - return 0; - } + ThreadRequest req = { self->MsgEvent, 0 }; + HRESULT hr = E_FAIL; - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); - if(FAILED(hr)) - { - WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); - CoUninitialize(); - req->result = hr; - SetEvent(req->FinishedEvt); - return 0; - } - Enumerator = ptr; - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; + if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); - CoUninitialize(); + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} - /* HACK: Force Windows to create a message queue for this thread before - * returning success, otherwise PostThreadMessage may fail if it gets - * called before GetMessage. - */ - PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); +static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self) +{ + HRESULT hr; + void *ptr; - TRACE("Message thread initialization complete\n"); - req->result = S_OK; - SetEvent(req->FinishedEvt); + ResetEvent(self->NotifyEvent); + hr = IAudioClient_Start(self->client); + if(FAILED(hr)) + ERR("Failed to start audio client: 0x%08lx\n", hr); - TRACE("Starting message loop\n"); - while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last)) + if(SUCCEEDED(hr)) + hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr); + if(SUCCEEDED(hr)) { - TRACE("Got message %u\n", msg.message); - switch(msg.message) + self->render = ptr; + self->killNow = 0; + if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success) { - case WM_USER_OpenDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - hr = cohr = S_OK; - if(++deviceCount == 1) - hr = cohr = CoInitialize(NULL); - if(SUCCEEDED(hr)) - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); - if(SUCCEEDED(hr)) - { - Enumerator = ptr; - if(!data->devid) - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); - else - hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev); - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; - } - if(SUCCEEDED(hr)) - hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); - if(SUCCEEDED(hr)) - { - data->client = ptr; - device->DeviceName = get_device_name(data->mmdev); - } - - if(FAILED(hr)) - { - if(data->mmdev) - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; - if(--deviceCount == 0 && SUCCEEDED(cohr)) - CoUninitialize(); - } - - req->result = hr; - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_ResetDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - - req->result = DoReset(device); - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_StartDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - ResetEvent(data->NotifyEvent); - hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent); - if(FAILED(hr)) - ERR("Failed to set event handle: 0x%08lx\n", hr); - else - { - hr = IAudioClient_Start(data->client); - if(FAILED(hr)) - ERR("Failed to start audio client: 0x%08lx\n", hr); - } - - if(SUCCEEDED(hr)) - hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr); - if(SUCCEEDED(hr)) - { - data->render = ptr; - if(!StartThread(&data->thread, MMDevApiProc, device)) - { - if(data->render) - IAudioRenderClient_Release(data->render); - data->render = NULL; - IAudioClient_Stop(data->client); - ERR("Failed to start thread\n"); - hr = E_FAIL; - } - } - - req->result = hr; - SetEvent(req->FinishedEvt); - continue; - - case WM_USER_StopDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - - if(data->thread) - { - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; - - IAudioRenderClient_Release(data->render); - data->render = NULL; - IAudioClient_Stop(data->client); - } + if(self->render) + IAudioRenderClient_Release(self->render); + self->render = NULL; + IAudioClient_Stop(self->client); + ERR("Failed to start thread\n"); + hr = E_FAIL; + } + } - req->result = S_OK; - SetEvent(req->FinishedEvt); - continue; + return hr; +} - case WM_USER_CloseDevice: - req = (ThreadRequest*)msg.wParam; - device = (ALCdevice*)msg.lParam; - data = device->ExtraData; - IAudioClient_Release(data->client); - data->client = NULL; +static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + (void)WaitForResponse(&req); +} - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; +static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self) +{ + int res; - if(--deviceCount == 0) - CoUninitialize(); + if(!self->render) + return; - req->result = S_OK; - SetEvent(req->FinishedEvt); - continue; + self->killNow = 1; + althrd_join(self->thread, &res); - case WM_USER_Enumerate: - req = (ThreadRequest*)msg.wParam; - - hr = cohr = S_OK; - if(++deviceCount == 1) - hr = cohr = CoInitialize(NULL); - if(SUCCEEDED(hr)) - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); - if(SUCCEEDED(hr)) - { - EDataFlow flowdir; - DevMap **devlist; - ALuint *numdevs; - ALuint i; - - Enumerator = ptr; - if(msg.lParam == CAPTURE_DEVICE_PROBE) - { - flowdir = eCapture; - devlist = &CaptureDeviceList; - numdevs = &NumCaptureDevices; - } - else - { - flowdir = eRender; - devlist = &PlaybackDeviceList; - numdevs = &NumPlaybackDevices; - } + IAudioRenderClient_Release(self->render); + self->render = NULL; + IAudioClient_Stop(self->client); +} - for(i = 0;i < *numdevs;i++) - { - free((*devlist)[i].name); - free((*devlist)[i].devid); - } - free(*devlist); - *devlist = NULL; - *numdevs = 0; - *devlist = ProbeDevices(Enumerator, flowdir, numdevs); +static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return (ALint64)self->Padding * 1000000000 / device->Frequency; +} - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; - } - if(--deviceCount == 0 && SUCCEEDED(cohr)) - CoUninitialize(); +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } - req->result = S_OK; - SetEvent(req->FinishedEvt); - continue; +typedef struct ALCmmdevBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCmmdevBackendFactory; +#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } } - default: - ERR("Unexpected message: %u\n", msg.message); - continue; - } - } - TRACE("Message loop finished\n"); +static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self); +static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self); +static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type); +static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type); - return 0; -} +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory); static BOOL MMDevApiLoad(void) @@ -816,7 +1087,7 @@ static BOOL MMDevApiLoad(void) ERR("Failed to create event: %lu\n", GetLastError()); else { - ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID); + ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID); if(ThreadHdl != NULL) InitResult = WaitForResponse(&req); CloseHandle(req.FinishedEvt); @@ -825,196 +1096,23 @@ static BOOL MMDevApiLoad(void) return SUCCEEDED(InitResult); } - -static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) -{ - MMDevApiData *data = NULL; - HRESULT hr; - - //Initialise requested device - data = calloc(1, sizeof(MMDevApiData)); - if(!data) - return ALC_OUT_OF_MEMORY; - device->ExtraData = data; - - hr = S_OK; - data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->NotifyEvent == NULL || data->MsgEvent == NULL) - { - ERR("Failed to create message events: %lu\n", GetLastError()); - hr = E_FAIL; - } - - if(SUCCEEDED(hr)) - { - if(deviceName) - { - ALuint i; - - if(!PlaybackDeviceList) - { - ThreadRequest req = { data->MsgEvent, 0 }; - if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE)) - (void)WaitForResponse(&req); - } - - hr = E_FAIL; - for(i = 0;i < NumPlaybackDevices;i++) - { - if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0) - { - data->devid = strdupW(PlaybackDeviceList[i].devid); - hr = S_OK; - break; - } - } - if(FAILED(hr)) - WARN("Failed to find device name matching \"%s\"\n", deviceName); - } - } - - if(SUCCEEDED(hr)) - { - ThreadRequest req = { data->MsgEvent, 0 }; - - hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) - hr = WaitForResponse(&req); - else - ERR("Failed to post thread message: %lu\n", GetLastError()); - } - - if(FAILED(hr)) - { - if(data->NotifyEvent != NULL) - CloseHandle(data->NotifyEvent); - data->NotifyEvent = NULL; - if(data->MsgEvent != NULL) - CloseHandle(data->MsgEvent); - data->MsgEvent = NULL; - - free(data->devid); - data->devid = NULL; - - free(data); - device->ExtraData = NULL; - - ERR("Device init failed: 0x%08lx\n", hr); - return ALC_INVALID_VALUE; - } - - return ALC_NO_ERROR; -} - -static void MMDevApiClosePlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) - (void)WaitForResponse(&req); - - CloseHandle(data->MsgEvent); - data->MsgEvent = NULL; - - CloseHandle(data->NotifyEvent); - data->NotifyEvent = NULL; - - free(data->devid); - data->devid = NULL; - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean MMDevApiResetPlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - HRESULT hr = E_FAIL; - - if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) - hr = WaitForResponse(&req); - - return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; -} - -static ALCboolean MMDevApiStartPlayback(ALCdevice *device) +static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self)) { - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - HRESULT hr = E_FAIL; - - if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device)) - hr = WaitForResponse(&req); - - return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; -} - -static void MMDevApiStopPlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - ThreadRequest req = { data->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) - (void)WaitForResponse(&req); -} - - -static ALint64 MMDevApiGetLatency(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - - return (ALint64)data->Padding * 1000000000 / device->Frequency; -} - - -static const BackendFuncs MMDevApiFuncs = { - MMDevApiOpenPlayback, - MMDevApiClosePlayback, - MMDevApiResetPlayback, - MMDevApiStartPlayback, - MMDevApiStopPlayback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - MMDevApiGetLatency -}; + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); - -ALCboolean alcMMDevApiInit(BackendFuncs *FuncList) -{ if(!MMDevApiLoad()) return ALC_FALSE; - *FuncList = MMDevApiFuncs; return ALC_TRUE; } -void alcMMDevApiDeinit(void) +static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self)) { - ALuint i; + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); - for(i = 0;i < NumPlaybackDevices;i++) - { - free(PlaybackDeviceList[i].name); - free(PlaybackDeviceList[i].devid); - } - free(PlaybackDeviceList); - PlaybackDeviceList = NULL; - NumPlaybackDevices = 0; - - for(i = 0;i < NumCaptureDevices;i++) - { - free(CaptureDeviceList[i].name); - free(CaptureDeviceList[i].devid); - } - free(CaptureDeviceList); - CaptureDeviceList = NULL; - NumCaptureDevices = 0; + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); if(ThreadHdl) { @@ -1025,34 +1123,61 @@ void alcMMDevApiDeinit(void) } } -void alcMMDevApiProbe(enum DevProbe type) +static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type) { ThreadRequest req = { NULL, 0 }; - HRESULT hr = E_FAIL; - switch(type) + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else { + HRESULT hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type)) + hr = WaitForResponse(&req); + if(SUCCEEDED(hr)) switch(type) + { case ALL_DEVICE_PROBE: - req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); - if(req.FinishedEvt == NULL) - ERR("Failed to create event: %lu\n", GetLastError()); - else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type)) - hr = WaitForResponse(&req); - if(SUCCEEDED(hr)) - { - ALuint i; - for(i = 0;i < NumPlaybackDevices;i++) - { - if(PlaybackDeviceList[i].name) - AppendAllDevicesList(PlaybackDeviceList[i].name); - } - } + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); break; case CAPTURE_DEVICE_PROBE: + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); break; - } - if(req.FinishedEvt != NULL) + } CloseHandle(req.FinishedEvt); - req.FinishedEvt = NULL; + req.FinishedEvt = NULL; + } +} + +static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCmmdevPlayback *backend; + + backend = ALCmmdevPlayback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCmmdevPlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + + +ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void) +{ + static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); } diff --git a/Alc/backends/null.c b/Alc/backends/null.c index a7056369..3f09c5d6 100644 --- a/Alc/backends/null.c +++ b/Alc/backends/null.c @@ -37,11 +37,10 @@ typedef struct ALCnullBackend { DERIVE_FROM_TYPE(ALCbackend); volatile int killNow; - althread_t thread; + althrd_t thread; } ALCnullBackend; -DECLARE_ALCBACKEND_VTABLE(ALCnullBackend); -static ALuint ALCnullBackend_mixerProc(ALvoid *ptr); +static int ALCnullBackend_mixerProc(void *ptr); static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device); static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct) @@ -55,6 +54,10 @@ static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend) + +DEFINE_ALCBACKEND_VTABLE(ALCnullBackend); + static const ALCchar nullDevice[] = "No Output"; @@ -66,42 +69,49 @@ static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device) } -static ALuint ALCnullBackend_mixerProc(ALvoid *ptr) +static int ALCnullBackend_mixerProc(void *ptr) { ALCnullBackend *self = (ALCnullBackend*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - ALuint now, start; + struct timespec now, start; ALuint64 avail, done; + const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / + device->Frequency / 2); SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); done = 0; - start = timeGetTime(); + if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get starting time\n"); + return 1; + } while(!self->killNow && device->Connected) { - now = timeGetTime(); + if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get current time\n"); + return 1; + } - avail = (ALuint64)(now-start) * device->Frequency / 1000; + avail = (now.tv_sec - start.tv_sec) * device->Frequency; + avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; if(avail < done) { - /* Timer wrapped (50 days???). Add the remainder of the cycle to - * the available count and reset the number of samples done */ - avail += (U64(1)<<32)*device->Frequency/1000 - done; - done = 0; + /* Oops, time skipped backwards. Reset the number of samples done + * with one update available since we (likely) just came back from + * sleeping. */ + done = avail - device->UpdateSize; } + if(avail-done < device->UpdateSize) + al_nssleep(0, restTime); + else while(avail-done >= device->UpdateSize) { - ALuint restTime = (ALuint)((device->UpdateSize - (avail-done)) * 1000 / - device->Frequency); - Sleep(restTime); - continue; - } - - do { aluMixData(device, NULL, device->UpdateSize); done += device->UpdateSize; - } while(avail-done >= device->UpdateSize); + } } return 0; @@ -118,7 +128,7 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name) return ALC_INVALID_VALUE; device = STATIC_CAST(ALCbackend, self)->mDevice; - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } @@ -135,31 +145,23 @@ static ALCboolean ALCnullBackend_reset(ALCnullBackend *self) static ALCboolean ALCnullBackend_start(ALCnullBackend *self) { - if(!StartThread(&self->thread, ALCnullBackend_mixerProc, self)) + self->killNow = 0; + if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; } static void ALCnullBackend_stop(ALCnullBackend *self) { - if(!self->thread) + int res; + + if(self->killNow) return; self->killNow = 1; - StopThread(self->thread); - self->thread = NULL; - - self->killNow = 0; -} - - -static void ALCnullBackend_Delete(ALCnullBackend *self) -{ - free(self); + althrd_join(self->thread, &res); } -DEFINE_ALCBACKEND_VTABLE(ALCnullBackend); - typedef struct ALCnullBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); @@ -209,14 +211,18 @@ static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enu static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) { - ALCnullBackend *backend; + if(type == ALCbackend_Playback) + { + ALCnullBackend *backend; - assert(type == ALCbackend_Playback); + backend = ALCnullBackend_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); - backend = calloc(1, sizeof(*backend)); - if(!backend) return NULL; + ALCnullBackend_Construct(backend, device); - ALCnullBackend_Construct(backend, device); + return STATIC_CAST(ALCbackend, backend); + } - return STATIC_CAST(ALCbackend, backend); + return NULL; } diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c index 76cbaf1a..220e6e5c 100644 --- a/Alc/backends/opensl.c +++ b/Alc/backends/opensl.c @@ -28,62 +28,9 @@ #include <SLES/OpenSLES.h> -#if 1 #include <SLES/OpenSLES_Android.h> -#else -extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - -struct SLAndroidSimpleBufferQueueItf_; -typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf; - -typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext); - -typedef struct SLAndroidSimpleBufferQueueState_ { - SLuint32 count; - SLuint32 index; -} SLAndroidSimpleBufferQueueState; - - -struct SLAndroidSimpleBufferQueueItf_ { - SLresult (*Enqueue) ( - SLAndroidSimpleBufferQueueItf self, - const void *pBuffer, - SLuint32 size - ); - SLresult (*Clear) ( - SLAndroidSimpleBufferQueueItf self - ); - SLresult (*GetState) ( - SLAndroidSimpleBufferQueueItf self, - SLAndroidSimpleBufferQueueState *pState - ); - SLresult (*RegisterCallback) ( - SLAndroidSimpleBufferQueueItf self, - slAndroidSimpleBufferQueueCallback callback, - void* pContext - ); -}; - -#define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD) - -typedef struct SLDataLocator_AndroidSimpleBufferQueue { - SLuint32 locatorType; - SLuint32 numBuffers; -} SLDataLocator_AndroidSimpleBufferQueue; - -#endif /* Helper macros */ -#define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b))) -#define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c))) -#define SLObjectItf_Destroy(a) ((*(a))->Destroy((a))) - -#define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e))) -#define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g))) - -#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) - -/* Should start using these generic callers instead of the name-specific ones above. */ #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS @@ -186,7 +133,7 @@ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize; aluMixData(Device, buf, data->bufferSize/data->frameSize); - result = (*bq)->Enqueue(bq, buf, data->bufferSize); + result = VCALL(bq,Enqueue)(buf, data->bufferSize); PRINTERR(result, "bq->Enqueue"); data->curBuffer = (data->curBuffer+1) % Device->NumUpdates; @@ -212,33 +159,33 @@ static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName PRINTERR(result, "slCreateEngine"); if(SL_RESULT_SUCCESS == result) { - result = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE); + result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "engine->Realize"); } if(SL_RESULT_SUCCESS == result) { - result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine); + result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine); PRINTERR(result, "engine->GetInterface"); } if(SL_RESULT_SUCCESS == result) { - result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL); + result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL); PRINTERR(result, "engine->CreateOutputMix"); } if(SL_RESULT_SUCCESS == result) { - result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE); + result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "outputMix->Realize"); } if(SL_RESULT_SUCCESS != result) { if(data->outputMix != NULL) - SLObjectItf_Destroy(data->outputMix); + VCALL0(data->outputMix,Destroy)(); data->outputMix = NULL; if(data->engineObject != NULL) - SLObjectItf_Destroy(data->engineObject); + VCALL0(data->engineObject,Destroy)(); data->engineObject = NULL; data->engine = NULL; @@ -246,7 +193,7 @@ static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName return ALC_INVALID_VALUE; } - Device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&Device->DeviceName, deviceName); Device->ExtraData = data; return ALC_NO_ERROR; @@ -258,13 +205,13 @@ static void opensl_close_playback(ALCdevice *Device) osl_data *data = Device->ExtraData; if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); + VCALL0(data->bufferQueueObject,Destroy)(); data->bufferQueueObject = NULL; - SLObjectItf_Destroy(data->outputMix); + VCALL0(data->outputMix,Destroy)(); data->outputMix = NULL; - SLObjectItf_Destroy(data->engineObject); + VCALL0(data->engineObject,Destroy)(); data->engineObject = NULL; data->engine = NULL; @@ -321,21 +268,21 @@ static ALCboolean opensl_reset_playback(ALCdevice *Device) if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); + VCALL0(data->bufferQueueObject,Destroy)(); data->bufferQueueObject = NULL; - result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); + result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); PRINTERR(result, "engine->CreateAudioPlayer"); if(SL_RESULT_SUCCESS == result) { - result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE); + result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "bufferQueue->Realize"); } if(SL_RESULT_SUCCESS != result) { if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); + VCALL0(data->bufferQueueObject,Destroy)(); data->bufferQueueObject = NULL; return ALC_FALSE; @@ -352,11 +299,11 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) SLresult result; ALuint i; - result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue); + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); PRINTERR(result, "bufferQueue->GetInterface"); if(SL_RESULT_SUCCESS == result) { - result = (*bufferQueue)->RegisterCallback(bufferQueue, opensl_callback, Device); + result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device); PRINTERR(result, "bufferQueue->RegisterCallback"); } if(SL_RESULT_SUCCESS == result) @@ -376,26 +323,26 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) if(SL_RESULT_SUCCESS == result) { ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize; - result = (*bufferQueue)->Enqueue(bufferQueue, buf, data->bufferSize); + result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize); PRINTERR(result, "bufferQueue->Enqueue"); } } data->curBuffer = 0; if(SL_RESULT_SUCCESS == result) { - result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); PRINTERR(result, "bufferQueue->GetInterface"); } if(SL_RESULT_SUCCESS == result) { - result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING); + result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING); PRINTERR(result, "player->SetPlayState"); } if(SL_RESULT_SUCCESS != result) { if(data->bufferQueueObject != NULL) - SLObjectItf_Destroy(data->bufferQueueObject); + VCALL0(data->bufferQueueObject,Destroy)(); data->bufferQueueObject = NULL; free(data->buffer); diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c index c79793c2..93e026e9 100644 --- a/Alc/backends/oss.c +++ b/Alc/backends/oss.c @@ -78,10 +78,10 @@ typedef struct ALCplaybackOSS { int data_size; volatile int killNow; - althread_t thread; + althrd_t thread; } ALCplaybackOSS; -static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr); +static int ALCplaybackOSS_mixerProc(void *ptr); static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device); static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct) @@ -95,11 +95,11 @@ static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock) -static void ALCplaybackOSS_Delete(ALCplaybackOSS *self); +DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS) DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS); -static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr) +static int ALCplaybackOSS_mixerProc(void *ptr) { ALCplaybackOSS *self = (ALCplaybackOSS*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -107,7 +107,7 @@ static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr) ssize_t wrote; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); @@ -131,7 +131,7 @@ static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr) break; } - Sleep(1); + al_nssleep(0, 1000000); continue; } @@ -168,7 +168,7 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) return ALC_INVALID_VALUE; } - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } @@ -275,7 +275,8 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); self->mix_data = calloc(1, self->data_size); - if(!StartThread(&self->thread, ALCplaybackOSS_mixerProc, self)) + self->killNow = 0; + if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success) { free(self->mix_data); self->mix_data = NULL; @@ -287,14 +288,14 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) static void ALCplaybackOSS_stop(ALCplaybackOSS *self) { - if(!self->thread) + int res; + + if(self->killNow) return; self->killNow = 1; - StopThread(self->thread); - self->thread = NULL; + althrd_join(self->thread, &res); - self->killNow = 0; if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0) ERR("Error resetting device: %s\n", strerror(errno)); @@ -302,11 +303,6 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self) self->mix_data = NULL; } -static void ALCplaybackOSS_Delete(ALCplaybackOSS *self) -{ - free(self); -} - typedef struct ALCcaptureOSS { DERIVE_FROM_TYPE(ALCbackend); @@ -320,10 +316,10 @@ typedef struct ALCcaptureOSS { int doCapture; volatile int killNow; - althread_t thread; + althrd_t thread; } ALCcaptureOSS; -static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr); +static int ALCcaptureOSS_recordProc(void *ptr); static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device); static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct) @@ -337,11 +333,11 @@ static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self); static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency) static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock) static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock) -static void ALCcaptureOSS_Delete(ALCcaptureOSS *self); +DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS) DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS); -static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr) +static int ALCcaptureOSS_recordProc(void *ptr) { ALCcaptureOSS *self = (ALCcaptureOSS*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -349,7 +345,7 @@ static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr) int amt; SetRTPriority(); - SetThreadName("alsoft-record"); + althrd_setname(althrd_current(), "alsoft-record"); frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); @@ -366,7 +362,7 @@ static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr) } if(amt == 0) { - Sleep(1); + al_nssleep(0, 1000000); continue; } if(self->doCapture) @@ -488,7 +484,8 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) self->data_size = info.fragsize; self->read_data = calloc(1, self->data_size); - if(!StartThread(&self->thread, ALCcaptureOSS_recordProc, self)) + self->killNow = 0; + if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success) { device->ExtraData = NULL; close(self->fd); @@ -496,16 +493,17 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) return ALC_OUT_OF_MEMORY; } - device->DeviceName = strdup(name); + al_string_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } static void ALCcaptureOSS_close(ALCcaptureOSS *self) { + int res; + self->killNow = 1; - StopThread(self->thread); - self->killNow = 0; + althrd_join(self->thread, &res); close(self->fd); self->fd = -1; @@ -539,12 +537,6 @@ static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self) return RingBufferSize(self->ring); } -void ALCcaptureOSS_Delete(ALCcaptureOSS *self) -{ - free(self); -} - - typedef struct ALCossBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); @@ -615,8 +607,9 @@ ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self { ALCplaybackOSS *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCplaybackOSS_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCplaybackOSS_Construct(backend, device); @@ -626,8 +619,9 @@ ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self { ALCcaptureOSS *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCcaptureOSS_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCcaptureOSS_Construct(backend, device); diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c index 162788fc..0bb6372c 100644 --- a/Alc/backends/portaudio.c +++ b/Alc/backends/portaudio.c @@ -212,7 +212,7 @@ retry_open: } device->ExtraData = data; - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); return ALC_NO_ERROR; } @@ -354,7 +354,7 @@ static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName) goto error; } - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); device->ExtraData = data; return ALC_NO_ERROR; diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c index e2ae52ae..58252240 100644 --- a/Alc/backends/pulseaudio.c +++ b/Alc/backends/pulseaudio.c @@ -306,9 +306,6 @@ static ALCboolean pulse_load(void) static pa_context_flags_t pulse_ctx_flags; static pa_proplist *prop_filter; -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif /* PulseAudio Event Callbacks */ static void context_state_callback(pa_context *context, void *pdata) @@ -458,20 +455,33 @@ static void pulse_close(pa_threaded_mainloop *loop, pa_context *context, typedef struct { - char *name; - char *device_name; + al_string name; + al_string device_name; } DevMap; +DECL_VECTOR(DevMap) + +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *list) +{ + DevMap *iter, *end; -static DevMap *allDevNameMap; -static ALuint numDevNames; -static DevMap *allCaptureDevNameMap; -static ALuint numCaptureDevNames; + iter = VECTOR_ITER_BEGIN(*list); + end = VECTOR_ITER_END(*list); + for(;iter != end;iter++) + { + AL_STRING_DEINIT(iter->name); + AL_STRING_DEINIT(iter->device_name); + } + VECTOR_RESIZE(*list, 0); +} typedef struct ALCpulsePlayback { DERIVE_FROM_TYPE(ALCbackend); - char *device_name; + al_string device_name; pa_buffer_attr attr; pa_sample_spec spec; @@ -482,9 +492,8 @@ typedef struct ALCpulsePlayback { pa_context *context; volatile ALboolean killNow; - althread_t thread; + althrd_t thread; } ALCpulsePlayback; -DECLARE_ALCBACKEND_VTABLE(ALCpulsePlayback); static void ALCpulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); static void ALCpulsePlayback_probeDevices(void); @@ -499,10 +508,10 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_thr pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap); -static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr); +static int ALCpulsePlayback_mixerProc(void *ptr); static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device); -static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, void, Destruct) +static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self); static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name); static void ALCpulsePlayback_close(ALCpulsePlayback *self); static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self); @@ -510,22 +519,34 @@ static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self); static void ALCpulsePlayback_stop(ALCpulsePlayback *self); static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self); static void ALCpulsePlayback_lock(ALCpulsePlayback *self); static void ALCpulsePlayback_unlock(ALCpulsePlayback *self); +DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback) + +DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback); static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCpulsePlayback, ALCbackend, self); + + AL_STRING_INIT(self->device_name); +} + +static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self) +{ + AL_STRING_DEINIT(self->device_name); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) { pa_threaded_mainloop *loop = pdata; - void *temp; - ALuint i; + const DevMap *iter, *end; + DevMap entry; if(eol) { @@ -533,29 +554,31 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p return; } - for(i = 0;i < numDevNames;i++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) { - if(strcmp(info->name, allDevNameMap[i].device_name) == 0) + if(al_string_cmp_cstr(iter->device_name, info->name) == 0) return; } TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); - temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); - if(temp) - { - allDevNameMap = temp; - allDevNameMap[numDevNames].name = strdup(info->description); - allDevNameMap[numDevNames].device_name = strdup(info->name); - numDevNames++; - } + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + + al_string_copy_cstr(&entry.name, info->description); + al_string_copy_cstr(&entry.device_name, info->name); + + VECTOR_PUSH_BACK(PlaybackDevices, entry); } static void ALCpulsePlayback_probeDevices(void) { pa_threaded_mainloop *loop; - allDevNameMap = malloc(sizeof(DevMap) * 1); + clear_devlist(&PlaybackDevices); + if((loop=pa_threaded_mainloop_new()) && pa_threaded_mainloop_start(loop) >= 0) { @@ -697,8 +720,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const return; } - free(device->DeviceName); - device->DeviceName = strdup(info->description); + al_string_copy_cstr(&device->DeviceName, info->description); } @@ -706,10 +728,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata) { ALCpulsePlayback *self = pdata; - free(self->device_name); - self->device_name = strdup(pa_stream_get_device_name(stream)); + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); - TRACE("Stream moved to %s\n", self->device_name); + TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); } @@ -754,7 +775,7 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, } -static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr) +static int ALCpulsePlayback_mixerProc(void *ptr) { ALCpulsePlayback *self = ptr; ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; @@ -764,7 +785,7 @@ static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr) ssize_t len; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); pa_threaded_mainloop_lock(self->loop); frame_size = pa_frame_size(&self->spec); @@ -786,7 +807,7 @@ static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr) if(o) pa_operation_unref(o); } pa_threaded_mainloop_unlock(self->loop); - Sleep(1); + al_nssleep(0, 1000000); pa_threaded_mainloop_lock(self->loop); continue; } @@ -828,20 +849,22 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name if(name) { - ALuint i; + const DevMap *iter, *end; - if(!allDevNameMap) + if(VECTOR_SIZE(PlaybackDevices) == 0) ALCpulsePlayback_probeDevices(); - for(i = 0;i < numDevNames;i++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) { - if(strcmp(name, allDevNameMap[i].name) == 0) + if(al_string_cmp_cstr(iter->name, name) == 0) { - pulse_name = allDevNameMap[i].device_name; + pulse_name = al_string_get_cstr(iter->device_name); break; } } - if(i == numDevNames) + if(iter == end) return ALC_INVALID_VALUE; } @@ -859,6 +882,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name spec.rate = 44100; spec.channels = 2; + TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); self->stream = ALCpulsePlayback_connectStream(pulse_name, self->loop, self->context, flags, NULL, &spec, NULL); if(!self->stream) @@ -871,8 +895,9 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name } pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); - self->device_name = strdup(pa_stream_get_device_name(self->stream)); - o = pa_context_get_sink_info_by_name(self->context, self->device_name, + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + o = pa_context_get_sink_info_by_name(self->context, + al_string_get_cstr(self->device_name), ALCpulsePlayback_sinkNameCallback, self); wait_for_operation(o, self->loop); @@ -888,16 +913,15 @@ static void ALCpulsePlayback_close(ALCpulsePlayback *self) self->context = NULL; self->stream = NULL; - free(self->device_name); - self->device_name = NULL; + al_string_clear(&self->device_name); } static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; pa_stream_flags_t flags = 0; + const char *mapname = NULL; pa_channel_map chanmap; - const char *mapname; ALuint len; pa_threaded_mainloop_lock(self->loop); @@ -917,7 +941,8 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) { pa_operation *o; - o = pa_context_get_sink_info_by_name(self->context, self->device_name, + o = pa_context_get_sink_info_by_name(self->context, + al_string_get_cstr(self->device_name), ALCpulsePlayback_sinkInfoCallback, self); wait_for_operation(o, self->loop); } @@ -964,7 +989,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) return ALC_FALSE; } - mapname = "(invalid)"; switch(device->FmtChans) { case DevFmtMono: @@ -1003,9 +1027,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2); self->attr.maxlength = -1; - self->stream = ALCpulsePlayback_connectStream(self->device_name, self->loop, - self->context, flags, &self->attr, - &self->spec, &chanmap); + self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name), + self->loop, self->context, flags, + &self->attr, &self->spec, &chanmap); if(!self->stream) { pa_threaded_mainloop_unlock(self->loop); @@ -1054,7 +1078,8 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self) { - if(!StartThread(&self->thread, ALCpulsePlayback_mixerProc, self)) + self->killNow = AL_FALSE; + if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; } @@ -1062,17 +1087,13 @@ static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self) static void ALCpulsePlayback_stop(ALCpulsePlayback *self) { pa_operation *o; + int res; if(!self->stream) return; self->killNow = AL_TRUE; - if(self->thread) - { - StopThread(self->thread); - self->thread = NULL; - } - self->killNow = AL_FALSE; + althrd_join(self->thread, &res); pa_threaded_mainloop_lock(self->loop); @@ -1083,25 +1104,19 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self) } -static void ALCpulsePlayback_lock(ALCpulsePlayback *self) -{ - pa_threaded_mainloop_lock(self->loop); -} - -static void ALCpulsePlayback_unlock(ALCpulsePlayback *self) -{ - pa_threaded_mainloop_unlock(self->loop); -} - - static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) { pa_usec_t latency = 0; - int neg; + int neg, err; - if(pa_stream_get_latency(self->stream, &latency, &neg) != 0) + if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0) { - ERR("Failed to get stream latency!\n"); + /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon + * after starting the stream and no timing info has been received from + * the server yet. Should we wait, possibly stalling the app, or give a + * dummy value? Either way, it shouldn't be 0. */ + if(err != -PA_ERR_NODATA) + ERR("Failed to get stream latency: 0x%x\n", err); return 0; } @@ -1110,18 +1125,21 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) } -static void ALCpulsePlayback_Delete(ALCpulsePlayback *self) +static void ALCpulsePlayback_lock(ALCpulsePlayback *self) { - free(self); + pa_threaded_mainloop_lock(self->loop); } -DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback); +static void ALCpulsePlayback_unlock(ALCpulsePlayback *self) +{ + pa_threaded_mainloop_unlock(self->loop); +} typedef struct ALCpulseCapture { DERIVE_FROM_TYPE(ALCbackend); - char *device_name; + al_string device_name; const void *cap_store; size_t cap_len; @@ -1137,7 +1155,6 @@ typedef struct ALCpulseCapture { pa_stream *stream; pa_context *context; } ALCpulseCapture; -DECLARE_ALCBACKEND_VTABLE(ALCpulseCapture); static void ALCpulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); static void ALCpulseCapture_probeDevices(void); @@ -1152,7 +1169,7 @@ static pa_stream *ALCpulseCapture_connectStream(const char *device_name, pa_sample_spec *spec, pa_channel_map *chanmap); static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device); -static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, void, Destruct) +static void ALCpulseCapture_Destruct(ALCpulseCapture *self); static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name); static void ALCpulseCapture_close(ALCpulseCapture *self); static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset) @@ -1160,22 +1177,34 @@ static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self); static void ALCpulseCapture_stop(ALCpulseCapture *self); static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self); +static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self); static void ALCpulseCapture_lock(ALCpulseCapture *self); static void ALCpulseCapture_unlock(ALCpulseCapture *self); +DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture) + +DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture); static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCpulseCapture, ALCbackend, self); + + AL_STRING_INIT(self->device_name); +} + +static void ALCpulseCapture_Destruct(ALCpulseCapture *self) +{ + AL_STRING_DEINIT(self->device_name); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) { pa_threaded_mainloop *loop = pdata; - void *temp; - ALuint i; + const DevMap *iter, *end; + DevMap entry; if(eol) { @@ -1183,29 +1212,31 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa return; } - for(i = 0;i < numCaptureDevNames;i++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) { - if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0) + if(al_string_cmp_cstr(iter->device_name, info->name) == 0) return; } TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); - temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); - if(temp) - { - allCaptureDevNameMap = temp; - allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description); - allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); - numCaptureDevNames++; - } + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + + al_string_copy_cstr(&entry.name, info->description); + al_string_copy_cstr(&entry.device_name, info->name); + + VECTOR_PUSH_BACK(CaptureDevices, entry); } static void ALCpulseCapture_probeDevices(void) { pa_threaded_mainloop *loop; - allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); + clear_devlist(&CaptureDevices); + if((loop=pa_threaded_mainloop_new()) && pa_threaded_mainloop_start(loop) >= 0) { @@ -1288,8 +1319,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons return; } - free(device->DeviceName); - device->DeviceName = strdup(info->description); + al_string_copy_cstr(&device->DeviceName, info->description); } @@ -1297,10 +1327,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata) { ALCpulseCapture *self = pdata; - free(self->device_name); - self->device_name = strdup(pa_stream_get_device_name(stream)); + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); - TRACE("Stream moved to %s\n", self->device_name); + TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); } @@ -1356,20 +1385,22 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) if(name) { - ALuint i; + const DevMap *iter, *end; - if(!allCaptureDevNameMap) + if(VECTOR_SIZE(CaptureDevices) == 0) ALCpulseCapture_probeDevices(); - for(i = 0;i < numCaptureDevNames;i++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) { - if(strcmp(name, allCaptureDevNameMap[i].name) == 0) + if(al_string_cmp_cstr(iter->name, name) == 0) { - pulse_name = allCaptureDevNameMap[i].device_name; + pulse_name = al_string_get_cstr(iter->device_name); break; } } - if(i == numCaptureDevNames) + if(iter == end) return ALC_INVALID_VALUE; } @@ -1431,6 +1462,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; + TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context, flags, &self->attr, &self->spec, &chanmap); @@ -1442,8 +1474,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self); pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self); - self->device_name = strdup(pa_stream_get_device_name(self->stream)); - o = pa_context_get_source_info_by_name(self->context, self->device_name, + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + o = pa_context_get_source_info_by_name(self->context, + al_string_get_cstr(self->device_name), ALCpulseCapture_sourceNameCallback, self); wait_for_operation(o, self->loop); @@ -1466,8 +1499,7 @@ static void ALCpulseCapture_close(ALCpulseCapture *self) self->context = NULL; self->stream = NULL; - free(self->device_name); - self->device_name = NULL; + al_string_clear(&self->device_name); } static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self) @@ -1562,17 +1594,6 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) } -static void ALCpulseCapture_lock(ALCpulseCapture *self) -{ - pa_threaded_mainloop_lock(self->loop); -} - -static void ALCpulseCapture_unlock(ALCpulseCapture *self) -{ - pa_threaded_mainloop_unlock(self->loop); -} - - static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self) { pa_usec_t latency = 0; @@ -1589,23 +1610,43 @@ static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self) } -static void ALCpulseCapture_Delete(ALCpulseCapture *self) +static void ALCpulseCapture_lock(ALCpulseCapture *self) { - free(self); + pa_threaded_mainloop_lock(self->loop); +} + +static void ALCpulseCapture_unlock(ALCpulseCapture *self) +{ + pa_threaded_mainloop_unlock(self->loop); } -DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture); +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } typedef struct ALCpulseBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); } ALCpulseBackendFactory; #define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } } +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self); +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self); +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type); +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type); + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); + + static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self)) { ALCboolean ret = ALC_FALSE; + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + if(pulse_load()) { pa_threaded_mainloop *loop; @@ -1648,25 +1689,11 @@ static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(sel static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self)) { - ALuint i; - - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device_name); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device_name); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); if(prop_filter) pa_proplist_free(prop_filter); @@ -1684,40 +1711,16 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type) { - ALuint i; - switch(type) { case ALL_DEVICE_PROBE: - for(i = 0;i < numDevNames;++i) - { - free(allDevNameMap[i].name); - free(allDevNameMap[i].device_name); - } - free(allDevNameMap); - allDevNameMap = NULL; - numDevNames = 0; - ALCpulsePlayback_probeDevices(); - - for(i = 0;i < numDevNames;i++) - AppendAllDevicesList(allDevNameMap[i].name); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); break; case CAPTURE_DEVICE_PROBE: - for(i = 0;i < numCaptureDevNames;++i) - { - free(allCaptureDevNameMap[i].name); - free(allCaptureDevNameMap[i].device_name); - } - free(allCaptureDevNameMap); - allCaptureDevNameMap = NULL; - numCaptureDevNames = 0; - ALCpulseCapture_probeDevices(); - - for(i = 0;i < numCaptureDevNames;i++) - AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); break; } } @@ -1728,8 +1731,9 @@ static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* { ALCpulsePlayback *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCpulsePlayback_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCpulsePlayback_Construct(backend, device); @@ -1739,8 +1743,9 @@ static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* { ALCpulseCapture *backend; - backend = calloc(1, sizeof(*backend)); + backend = ALCpulseCapture_New(sizeof(*backend)); if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); ALCpulseCapture_Construct(backend, device); @@ -1750,8 +1755,6 @@ static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* return NULL; } -DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); - #else /* PA_API_VERSION == 12 */ diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c index c9762f85..9258e4e5 100644 --- a/Alc/backends/qsa.c +++ b/Alc/backends/qsa.c @@ -34,8 +34,7 @@ #include "threads.h" -typedef struct -{ +typedef struct { snd_pcm_t* pcmHandle; int audio_fd; @@ -46,27 +45,24 @@ typedef struct ALsizei size; volatile int killNow; - althread_t thread; + althrd_t thread; } qsa_data; -typedef struct -{ +typedef struct { ALCchar* name; int card; int dev; } DevMap; -static const ALCchar qsaDevice[]="QSA Default"; +static const ALCchar qsaDevice[] = "QSA Default"; static DevMap* allDevNameMap; static ALuint numDevNames; static DevMap* allCaptureDevNameMap; static ALuint numCaptureDevNames; -static const struct -{ +static const struct { int32_t format; -} formatlist[]= -{ +} formatlist[] = { {SND_PCM_SFMT_FLOAT_LE}, {SND_PCM_SFMT_S32_LE}, {SND_PCM_SFMT_U32_LE}, @@ -77,11 +73,9 @@ static const struct {0}, }; -static const struct -{ +static const struct { int32_t rate; -} ratelist[]= -{ +} ratelist[] = { {192000}, {176400}, {96000}, @@ -98,11 +92,9 @@ static const struct {0}, }; -static const struct -{ +static const struct { int32_t channels; -} channellist[]= -{ +} channellist[] = { {8}, {7}, {6}, @@ -112,7 +104,7 @@ static const struct {0}, }; -static DevMap* deviceList(int type, ALuint* count) +static DevMap *deviceList(int type, ALuint *count) { snd_ctl_t* handle; snd_pcm_info_t pcminfo; @@ -178,7 +170,7 @@ static DevMap* deviceList(int type, ALuint* count) } -FORCE_ALIGN static ALuint qsa_proc_playback(ALvoid* ptr) +FORCE_ALIGN static int qsa_proc_playback(void* ptr) { ALCdevice* device=(ALCdevice*)ptr; qsa_data* data=(qsa_data*)device->ExtraData; @@ -191,7 +183,7 @@ FORCE_ALIGN static ALuint qsa_proc_playback(ALvoid* ptr) struct timeval timeout; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); /* Increase default 10 priority to 11 to avoid jerky sound */ SchedGet(0, 0, ¶m); @@ -343,8 +335,8 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) return ALC_INVALID_DEVICE; } - device->DeviceName=strdup(deviceName); - device->ExtraData=data; + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; return ALC_NO_ERROR; } @@ -616,7 +608,8 @@ static ALCboolean qsa_start_playback(ALCdevice* device) { qsa_data *data = (qsa_data*)device->ExtraData; - if(!StartThread(&data->thread, qsa_proc_playback, device)) + data->killNow = 0; + if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success) return ALC_FALSE; return ALC_TRUE; @@ -624,15 +617,14 @@ static ALCboolean qsa_start_playback(ALCdevice* device) static void qsa_stop_playback(ALCdevice* device) { - qsa_data* data=(qsa_data*)device->ExtraData; + qsa_data *data = (qsa_data*)device->ExtraData; + int res; - if (data->thread) - { - data->killNow=1; - StopThread(data->thread); - data->thread=NULL; - } - data->killNow=0; + if(data->killNow) + return; + + data->killNow = 1; + althrd_join(data->thread, &res); } /***********/ @@ -711,8 +703,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) return ALC_INVALID_DEVICE; } - device->DeviceName=strdup(deviceName); - device->ExtraData=data; + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; switch (device->FmtType) { diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c index 80aebfd1..7152b2d6 100644 --- a/Alc/backends/sndio.c +++ b/Alc/backends/sndio.c @@ -47,11 +47,11 @@ typedef struct { ALsizei data_size; volatile int killNow; - althread_t thread; + althrd_t thread; } sndio_data; -static ALuint sndio_proc(ALvoid *ptr) +static int sndio_proc(void *ptr) { ALCdevice *device = ptr; sndio_data *data = device->ExtraData; @@ -59,7 +59,7 @@ static ALuint sndio_proc(ALvoid *ptr) size_t wrote; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); @@ -111,7 +111,7 @@ static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName) return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); device->ExtraData = data; return ALC_NO_ERROR; @@ -224,7 +224,8 @@ static ALCboolean sndio_start_playback(ALCdevice *device) data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->mix_data = calloc(1, data->data_size); - if(!StartThread(&data->thread, sndio_proc, device)) + data->killNow = 0; + if(althrd_create(&data->thread, sndio_proc, device) != althrd_success) { sio_stop(data->sndHandle); free(data->mix_data); @@ -238,15 +239,14 @@ static ALCboolean sndio_start_playback(ALCdevice *device) static void sndio_stop_playback(ALCdevice *device) { sndio_data *data = device->ExtraData; + int res; - if(!data->thread) + if(data->killNow) return; data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + althrd_join(data->thread, &res); - data->killNow = 0; if(!sio_stop(data->sndHandle)) ERR("Error stopping device\n"); diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c index 700131c8..20d861d2 100644 --- a/Alc/backends/solaris.c +++ b/Alc/backends/solaris.c @@ -50,11 +50,11 @@ typedef struct { int data_size; volatile int killNow; - althread_t thread; + althrd_t thread; } solaris_data; -static ALuint SolarisProc(ALvoid *ptr) +static int SolarisProc(void *ptr) { ALCdevice *Device = (ALCdevice*)ptr; solaris_data *data = (solaris_data*)Device->ExtraData; @@ -62,7 +62,7 @@ static ALuint SolarisProc(ALvoid *ptr) int wrote; SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); @@ -86,7 +86,7 @@ static ALuint SolarisProc(ALvoid *ptr) break; } - Sleep(1); + al_nssleep(0, 1000000); continue; } @@ -119,7 +119,7 @@ static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceNam return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); device->ExtraData = data; return ALC_NO_ERROR; } @@ -211,7 +211,8 @@ static ALCboolean solaris_start_playback(ALCdevice *device) data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->mix_data = calloc(1, data->data_size); - if(!StartThread(&data->thread, SolarisProc, device)) + data->killNow = 0; + if(althrd_create(&data->thread, SolarisProc, device) != althrd_success) { free(data->mix_data); data->mix_data = NULL; @@ -224,15 +225,14 @@ static ALCboolean solaris_start_playback(ALCdevice *device) static void solaris_stop_playback(ALCdevice *device) { solaris_data *data = (solaris_data*)device->ExtraData; + int res; - if(!data->thread) + if(data->killNow) return; data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + althrd_join(data->thread, &res); - data->killNow = 0; if(ioctl(data->fd, AUDIO_DRAIN) < 0) ERR("Error draining device: %s\n", strerror(errno)); diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c index 2fafc4b9..421ca5d7 100644 --- a/Alc/backends/wave.c +++ b/Alc/backends/wave.c @@ -42,7 +42,7 @@ typedef struct { ALuint size; volatile int killNow; - althread_t thread; + althrd_t thread; } wave_data; @@ -85,49 +85,55 @@ static void fwrite32le(ALuint val, FILE *f) } -static ALuint WaveProc(ALvoid *ptr) +static int WaveProc(void *ptr) { - ALCdevice *Device = (ALCdevice*)ptr; - wave_data *data = (wave_data*)Device->ExtraData; + ALCdevice *device = (ALCdevice*)ptr; + wave_data *data = (wave_data*)device->ExtraData; + struct timespec now, start; + ALint64 avail, done; ALuint frameSize; - ALuint now, start; - ALuint64 avail, done; size_t fs; - const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / - Device->Frequency / 2; + const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / + device->Frequency / 2); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); done = 0; - start = timeGetTime(); - while(!data->killNow && Device->Connected) + if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) { - now = timeGetTime(); - - avail = (ALuint64)(now-start) * Device->Frequency / 1000; - if(avail < done) + ERR("Failed to get starting time\n"); + return 1; + } + while(!data->killNow && device->Connected) + { + if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) { - /* Timer wrapped (50 days???). Add the remainder of the cycle to - * the available count and reset the number of samples done */ - avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done; - done = 0; + ERR("Failed to get current time\n"); + return 1; } - if(avail-done < Device->UpdateSize) + + avail = (now.tv_sec - start.tv_sec) * device->Frequency; + avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; + if(avail < done) { - Sleep(restTime); - continue; + /* Oops, time skipped backwards. Reset the number of samples done + * with one update available since we (likely) just came back from + * sleeping. */ + done = avail - device->UpdateSize; } - while(avail-done >= Device->UpdateSize) + if(avail-done < device->UpdateSize) + al_nssleep(0, restTime); + else while(avail-done >= device->UpdateSize) { - aluMixData(Device, data->buffer, Device->UpdateSize); - done += Device->UpdateSize; + aluMixData(device, data->buffer, device->UpdateSize); + done += device->UpdateSize; if(!IS_LITTLE_ENDIAN) { - ALuint bytesize = BytesFromDevFmt(Device->FmtType); + ALuint bytesize = BytesFromDevFmt(device->FmtType); ALubyte *bytes = data->buffer; ALuint i; @@ -149,16 +155,16 @@ static ALuint WaveProc(ALvoid *ptr) } else { - fs = fwrite(data->buffer, frameSize, Device->UpdateSize, + fs = fwrite(data->buffer, frameSize, device->UpdateSize, data->f); (void)fs; } if(ferror(data->f)) { ERR("Error writing to file\n"); - ALCdevice_Lock(Device); - aluHandleDisconnect(Device); - ALCdevice_Unlock(Device); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); break; } } @@ -183,7 +189,7 @@ static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName) data = (wave_data*)calloc(1, sizeof(wave_data)); - data->f = fopen(fname, "wb"); + data->f = al_fopen(fname, "wb"); if(!data->f) { free(data); @@ -191,7 +197,7 @@ static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName) return ALC_INVALID_VALUE; } - device->DeviceName = strdup(deviceName); + al_string_copy_cstr(&device->DeviceName, deviceName); device->ExtraData = data; return ALC_NO_ERROR; } @@ -291,7 +297,8 @@ static ALCboolean wave_start_playback(ALCdevice *device) return ALC_FALSE; } - if(!StartThread(&data->thread, WaveProc, device)) + data->killNow = 0; + if(althrd_create(&data->thread, WaveProc, device) != althrd_success) { free(data->buffer); data->buffer = NULL; @@ -306,15 +313,13 @@ static void wave_stop_playback(ALCdevice *device) wave_data *data = (wave_data*)device->ExtraData; ALuint dataLen; long size; + int res; - if(!data->thread) + if(data->killNow) return; data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; - - data->killNow = 0; + althrd_join(data->thread, &res); free(data->buffer); data->buffer = NULL; diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c index 7082a874..624af37a 100644 --- a/Alc/backends/winmm.c +++ b/Alc/backends/winmm.c @@ -39,10 +39,9 @@ typedef struct { // MMSYSTEM Device volatile ALboolean killNow; - HANDLE WaveThreadEvent; - HANDLE WaveThread; - DWORD WaveThreadID; - volatile LONG WaveBuffersCommitted; + althrd_t thread; + + RefCount WaveBuffersCommitted; WAVEHDR WaveBuffer[4]; union { @@ -56,87 +55,106 @@ typedef struct { } WinMMData; -static ALCchar **PlaybackDeviceList; -static ALuint NumPlaybackDevices; -static ALCchar **CaptureDeviceList; -static ALuint NumCaptureDevices; +static vector_al_string PlaybackDevices; +static vector_al_string CaptureDevices; + +static void clear_devlist(vector_al_string *list) +{ + al_string *iter, *end; + + iter = VECTOR_ITER_BEGIN(*list); + end = VECTOR_ITER_END(*list); + for(;iter != end;iter++) + AL_STRING_DEINIT(*iter); + VECTOR_RESIZE(*list, 0); +} static void ProbePlaybackDevices(void) { + al_string *iter, *end; + ALuint numdevs; ALuint i; - for(i = 0;i < NumPlaybackDevices;i++) - free(PlaybackDeviceList[i]); + clear_devlist(&PlaybackDevices); - NumPlaybackDevices = waveOutGetNumDevs(); - PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); - for(i = 0;i < NumPlaybackDevices;i++) + numdevs = waveOutGetNumDevs(); + VECTOR_RESERVE(PlaybackDevices, numdevs); + for(i = 0;i < numdevs;i++) { - WAVEOUTCAPS WaveCaps; + WAVEOUTCAPSW WaveCaps; + al_string dname; - PlaybackDeviceList[i] = NULL; - if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + AL_STRING_INIT(dname); + if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - char name[1024]; - ALuint count, j; - - count = 0; + ALuint count = 0; do { - if(count == 0) - snprintf(name, sizeof(name), "%s", WaveCaps.szPname); - else - snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1); + al_string_copy_wcstr(&dname, WaveCaps.szPname); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&dname, str); + } count++; - for(j = 0;j < i;j++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) { - if(strcmp(name, PlaybackDeviceList[j]) == 0) + if(al_string_cmp(*iter, dname) == 0) break; } - } while(j != i); + } while(iter != end); - PlaybackDeviceList[i] = strdup(name); + TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); } + VECTOR_PUSH_BACK(PlaybackDevices, dname); } } static void ProbeCaptureDevices(void) { + al_string *iter, *end; + ALuint numdevs; ALuint i; - for(i = 0;i < NumCaptureDevices;i++) - free(CaptureDeviceList[i]); + clear_devlist(&CaptureDevices); - NumCaptureDevices = waveInGetNumDevs(); - CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices); - for(i = 0;i < NumCaptureDevices;i++) + numdevs = waveInGetNumDevs(); + VECTOR_RESERVE(CaptureDevices, numdevs); + for(i = 0;i < numdevs;i++) { - WAVEINCAPS WaveInCaps; + WAVEINCAPSW WaveCaps; + al_string dname; - CaptureDeviceList[i] = NULL; - if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) + AL_STRING_INIT(dname); + if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - char name[1024]; - ALuint count, j; - - count = 0; + ALuint count = 0; do { - if(count == 0) - snprintf(name, sizeof(name), "%s", WaveInCaps.szPname); - else - snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1); + al_string_copy_wcstr(&dname, WaveCaps.szPname); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&dname, str); + } count++; - for(j = 0;j < i;j++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) { - if(strcmp(name, CaptureDeviceList[j]) == 0) + if(al_string_cmp(*iter, dname) == 0) break; } - } while(j != i); + } while(iter != end); - CaptureDeviceList[i] = strdup(name); + TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); } + VECTOR_PUSH_BACK(CaptureDevices, dname); } } @@ -155,28 +173,19 @@ static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR in if(msg != WOM_DONE) return; - InterlockedDecrement(&data->WaveBuffersCommitted); - PostThreadMessage(data->WaveThreadID, msg, 0, param1); + DecrementRef(&data->WaveBuffersCommitted); + PostThreadMessage(data->thread, msg, 0, param1); } -/* - PlaybackThreadProc - - Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its - audio data. -*/ -FORCE_ALIGN static DWORD WINAPI PlaybackThreadProc(LPVOID param) +FORCE_ALIGN static int PlaybackThreadProc(void *arg) { - ALCdevice *Device = (ALCdevice*)param; + ALCdevice *Device = (ALCdevice*)arg; WinMMData *data = Device->ExtraData; - LPWAVEHDR WaveHdr; - ALuint FrameSize; + WAVEHDR *WaveHdr; MSG msg; - FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - SetRTPriority(); - SetThreadName(MIXER_THREAD_NAME); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); while(GetMessage(&msg, NULL, 0, 0)) { @@ -185,24 +194,20 @@ FORCE_ALIGN static DWORD WINAPI PlaybackThreadProc(LPVOID param) if(data->killNow) { - if(data->WaveBuffersCommitted == 0) + if(ReadRef(&data->WaveBuffersCommitted) == 0) break; continue; } - WaveHdr = ((LPWAVEHDR)msg.lParam); - aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength/FrameSize); + WaveHdr = ((WAVEHDR*)msg.lParam); + aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength / + data->Format.nBlockAlign); // Send buffer back to play more data waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR)); - InterlockedIncrement(&data->WaveBuffersCommitted); + IncrementRef(&data->WaveBuffersCommitted); } - // Signal Wave Thread completed event - if(data->WaveThreadEvent) - SetEvent(data->WaveThreadEvent); - - ExitThread(0); return 0; } @@ -220,26 +225,18 @@ static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR inst if(msg != WIM_DATA) return; - InterlockedDecrement(&data->WaveBuffersCommitted); - PostThreadMessage(data->WaveThreadID, msg, 0, param1); + DecrementRef(&data->WaveBuffersCommitted); + PostThreadMessage(data->thread, msg, 0, param1); } -/* - CaptureThreadProc - - Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new - audio data. -*/ -static DWORD WINAPI CaptureThreadProc(LPVOID param) +static int CaptureThreadProc(void *arg) { - ALCdevice *Device = (ALCdevice*)param; + ALCdevice *Device = (ALCdevice*)arg; WinMMData *data = Device->ExtraData; - LPWAVEHDR WaveHdr; - ALuint FrameSize; + WAVEHDR *WaveHdr; MSG msg; - FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - SetThreadName("alsoft-record"); + althrd_setname(althrd_current(), "alsoft-record"); while(GetMessage(&msg, NULL, 0, 0)) { @@ -250,19 +247,15 @@ static DWORD WINAPI CaptureThreadProc(LPVOID param) if(data->killNow) break; - WaveHdr = ((LPWAVEHDR)msg.lParam); - WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData, WaveHdr->dwBytesRecorded/FrameSize); + WaveHdr = ((WAVEHDR*)msg.lParam); + WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData, + WaveHdr->dwBytesRecorded/data->Format.nBlockAlign); // Send buffer back to capture more data waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR)); - InterlockedIncrement(&data->WaveBuffersCommitted); + IncrementRef(&data->WaveBuffersCommitted); } - // Signal Wave Thread completed event - if(data->WaveThreadEvent) - SetEvent(data->WaveThreadEvent); - - ExitThread(0); return 0; } @@ -270,24 +263,26 @@ static DWORD WINAPI CaptureThreadProc(LPVOID param) static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName) { WinMMData *data = NULL; - UINT DeviceID = 0; + const al_string *iter, *end; + UINT DeviceID; MMRESULT res; - ALuint i = 0; - if(!PlaybackDeviceList) + if(VECTOR_SIZE(PlaybackDevices) == 0) ProbePlaybackDevices(); // Find the Device ID matching the deviceName if valid - for(i = 0;i < NumPlaybackDevices;i++) + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) { - if(PlaybackDeviceList[i] && - (!deviceName || strcmp(deviceName, PlaybackDeviceList[i]) == 0)) + if(!al_string_empty(*iter) && + (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0)) { - DeviceID = i; + DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices)); break; } } - if(i == NumPlaybackDevices) + if(iter == end) return ALC_INVALID_VALUE; data = calloc(1, sizeof(*data)); @@ -329,20 +324,10 @@ retry_open: goto failure; } - data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->WaveThreadEvent == NULL) - { - ERR("CreateEvent failed: %lu\n", GetLastError()); - goto failure; - } - - Device->DeviceName = strdup(PlaybackDeviceList[DeviceID]); + al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID)); return ALC_NO_ERROR; failure: - if(data->WaveThreadEvent) - CloseHandle(data->WaveThreadEvent); - if(data->WaveHandle.Out) waveOutClose(data->WaveHandle.Out); @@ -356,9 +341,6 @@ static void WinMMClosePlayback(ALCdevice *device) WinMMData *data = (WinMMData*)device->ExtraData; // Close the Wave device - CloseHandle(data->WaveThreadEvent); - data->WaveThreadEvent = 0; - waveOutClose(data->WaveHandle.Out); data->WaveHandle.Out = 0; @@ -426,11 +408,11 @@ static ALCboolean WinMMStartPlayback(ALCdevice *device) ALint BufferSize; ALuint i; - data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &data->WaveThreadID); - if(data->WaveThread == NULL) + data->killNow = AL_FALSE; + if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success) return ALC_FALSE; - data->WaveBuffersCommitted = 0; + InitRef(&data->WaveBuffersCommitted, 0); // Create 4 Buffers BufferSize = device->UpdateSize*device->NumUpdates / 4; @@ -441,12 +423,12 @@ static ALCboolean WinMMStartPlayback(ALCdevice *device) { memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR)); data->WaveBuffer[i].dwBufferLength = BufferSize; - data->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData : (data->WaveBuffer[i-1].lpData + data->WaveBuffer[i-1].dwBufferLength)); waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR)); waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR)); - InterlockedIncrement(&data->WaveBuffersCommitted); + IncrementRef(&data->WaveBuffersCommitted); } return ALC_TRUE; @@ -458,19 +440,12 @@ static void WinMMStopPlayback(ALCdevice *device) void *buffer = NULL; int i; - if(data->WaveThread == NULL) + if(data->killNow) return; // Set flag to stop processing headers data->killNow = AL_TRUE; - - // Wait for signal that Wave Thread has been destroyed - WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE); - - CloseHandle(data->WaveThread); - data->WaveThread = 0; - - data->killNow = AL_FALSE; + althrd_join(data->thread, &i); // Release the wave buffers for(i = 0;i < 4;i++) @@ -485,28 +460,31 @@ static void WinMMStopPlayback(ALCdevice *device) static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName) { + const al_string *iter, *end; ALbyte *BufferData = NULL; DWORD CapturedDataSize; WinMMData *data = NULL; - UINT DeviceID = 0; ALint BufferSize; + UINT DeviceID; MMRESULT res; ALuint i; - if(!CaptureDeviceList) + if(VECTOR_SIZE(CaptureDevices) == 0) ProbeCaptureDevices(); // Find the Device ID matching the deviceName if valid - for(i = 0;i < NumCaptureDevices;i++) + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) { - if(CaptureDeviceList[i] && - (!deviceName || strcmp(deviceName, CaptureDeviceList[i]) == 0)) + if(!al_string_empty(*iter) && + (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0)) { - DeviceID = i; + DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices)); break; } } - if(i == NumCaptureDevices) + if(iter == end) return ALC_INVALID_VALUE; switch(Device->FmtChans) @@ -560,13 +538,6 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName) goto failure; } - data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->WaveThreadEvent == NULL) - { - ERR("CreateEvent failed: %lu\n", GetLastError()); - goto failure; - } - // Allocate circular memory buffer for the captured audio CapturedDataSize = Device->UpdateSize*Device->NumUpdates; @@ -578,7 +549,7 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName) if(!data->Ring) goto failure; - data->WaveBuffersCommitted = 0; + InitRef(&data->WaveBuffersCommitted, 0); // Create 4 Buffers of 50ms each BufferSize = data->Format.nAvgBytesPerSec / 20; @@ -592,27 +563,23 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName) { memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR)); data->WaveBuffer[i].dwBufferLength = BufferSize; - data->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData : (data->WaveBuffer[i-1].lpData + data->WaveBuffer[i-1].dwBufferLength)); data->WaveBuffer[i].dwFlags = 0; data->WaveBuffer[i].dwLoops = 0; waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); - InterlockedIncrement(&data->WaveBuffersCommitted); + IncrementRef(&data->WaveBuffersCommitted); } - data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)Device, 0, &data->WaveThreadID); - if (data->WaveThread == NULL) + if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success) goto failure; - Device->DeviceName = strdup(CaptureDeviceList[DeviceID]); + al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID)); return ALC_NO_ERROR; failure: - if(data->WaveThread) - CloseHandle(data->WaveThread); - if(BufferData) { for(i = 0;i < 4;i++) @@ -623,9 +590,6 @@ failure: if(data->Ring) DestroyRingBuffer(data->Ring); - if(data->WaveThreadEvent) - CloseHandle(data->WaveThreadEvent); - if(data->WaveHandle.In) waveInClose(data->WaveHandle.In); @@ -642,16 +606,13 @@ static void WinMMCloseCapture(ALCdevice *Device) /* Tell the processing thread to quit and wait for it to do so. */ data->killNow = AL_TRUE; - PostThreadMessage(data->WaveThreadID, WM_QUIT, 0, 0); + PostThreadMessage(data->thread, WM_QUIT, 0, 0); - WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE); + althrd_join(data->thread, &i); /* Make sure capture is stopped and all pending buffers are flushed. */ waveInReset(data->WaveHandle.In); - CloseHandle(data->WaveThread); - data->WaveThread = 0; - // Release the wave buffers for(i = 0;i < 4;i++) { @@ -665,9 +626,6 @@ static void WinMMCloseCapture(ALCdevice *Device) data->Ring = NULL; // Close the Wave device - CloseHandle(data->WaveThreadEvent); - data->WaveThreadEvent = 0; - waveInClose(data->WaveHandle.In); data->WaveHandle.In = 0; @@ -701,6 +659,17 @@ static ALCuint WinMMAvailableSamples(ALCdevice *Device) } +static inline void AppendAllDevicesList2(const al_string *name) +{ + if(!al_string_empty(*name)) + AppendAllDevicesList(al_string_get_cstr(*name)); +} +static inline void AppendCaptureDeviceList2(const al_string *name) +{ + if(!al_string_empty(*name)) + AppendCaptureDeviceList(al_string_get_cstr(*name)); +} + static const BackendFuncs WinMMFuncs = { WinMMOpenPlayback, WinMMClosePlayback, @@ -718,52 +687,34 @@ static const BackendFuncs WinMMFuncs = { ALCboolean alcWinMMInit(BackendFuncs *FuncList) { + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + *FuncList = WinMMFuncs; return ALC_TRUE; } void alcWinMMDeinit() { - ALuint i; + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); - for(i = 0;i < NumPlaybackDevices;i++) - free(PlaybackDeviceList[i]); - free(PlaybackDeviceList); - PlaybackDeviceList = NULL; - - NumPlaybackDevices = 0; - - - for(i = 0;i < NumCaptureDevices;i++) - free(CaptureDeviceList[i]); - free(CaptureDeviceList); - CaptureDeviceList = NULL; - - NumCaptureDevices = 0; + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); } void alcWinMMProbe(enum DevProbe type) { - ALuint i; - switch(type) { case ALL_DEVICE_PROBE: ProbePlaybackDevices(); - for(i = 0;i < NumPlaybackDevices;i++) - { - if(PlaybackDeviceList[i]) - AppendAllDevicesList(PlaybackDeviceList[i]); - } + VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2); break; case CAPTURE_DEVICE_PROBE: ProbeCaptureDevices(); - for(i = 0;i < NumCaptureDevices;i++) - { - if(CaptureDeviceList[i]) - AppendCaptureDeviceList(CaptureDeviceList[i]); - } + VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2); break; } } diff --git a/Alc/compat.h b/Alc/compat.h index dbbcce28..f54ef9ce 100644 --- a/Alc/compat.h +++ b/Alc/compat.h @@ -1,56 +1,23 @@ #ifndef AL_COMPAT_H #define AL_COMPAT_H -#include "AL/al.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> -typedef DWORD althread_key_t; -int althread_key_create(althread_key_t *key, void (*callback)(void*)); -int althread_key_delete(althread_key_t key); -void *althread_getspecific(althread_key_t key); -int althread_setspecific(althread_key_t key, void *val); - -typedef LONG althread_once_t; -#define ALTHREAD_ONCE_INIT 0 -void althread_once(althread_once_t *once, void (*callback)(void)); - -inline int alsched_yield(void) -{ SwitchToThread(); return 0; } - WCHAR *strdupW(const WCHAR *str); +/* Opens a file with standard I/O. The filename is expected to be UTF-8. */ +FILE *al_fopen(const char *fname, const char *mode); + #define HAVE_DYNLOAD 1 #else -#include <pthread.h> - -typedef pthread_mutex_t CRITICAL_SECTION; -void InitializeCriticalSection(CRITICAL_SECTION *cs); -void DeleteCriticalSection(CRITICAL_SECTION *cs); -void EnterCriticalSection(CRITICAL_SECTION *cs); -void LeaveCriticalSection(CRITICAL_SECTION *cs); - -ALuint timeGetTime(void); -void Sleep(ALuint t); - -#define althread_key_t pthread_key_t -#define althread_key_create pthread_key_create -#define althread_key_delete pthread_key_delete -#define althread_getspecific pthread_getspecific -#define althread_setspecific pthread_setspecific - -#define althread_once_t pthread_once_t -#define ALTHREAD_ONCE_INIT PTHREAD_ONCE_INIT -#define althread_once pthread_once - -#define alsched_yield sched_yield +#define al_fopen fopen -#if defined(HAVE_DLFCN_H) +#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER) #define HAVE_DYNLOAD 1 #endif diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c index 527e3be6..c8317c8b 100644 --- a/Alc/effects/autowah.c +++ b/Alc/effects/autowah.c @@ -71,8 +71,8 @@ static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, co attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency; releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency; - state->AttackRate = 1.0f / attackTime; - state->ReleaseRate = 1.0f / releaseTime; + state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime); + state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime); state->PeakGain = slot->EffectProps.Autowah.PeakGain; state->Resonance = slot->EffectProps.Autowah.Resonance; @@ -102,9 +102,9 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, * incoming signal, and attack or release to reach it. */ amplitude = fabsf(smp); if(amplitude > gain) - gain = minf(gain+state->AttackRate, amplitude); + gain = minf(gain*state->AttackRate, amplitude); else if(amplitude < gain) - gain = maxf(gain-state->ReleaseRate, amplitude); + gain = maxf(gain*state->ReleaseRate, amplitude); gain = maxf(gain, GAIN_SILENCE_THRESHOLD); /* FIXME: What range does the filter cover? */ @@ -151,10 +151,7 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, } } -static void ALautowahState_Delete(ALautowahState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALautowahState) DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState); @@ -167,13 +164,13 @@ static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED { ALautowahState *state; - state = malloc(sizeof(*state)); + state = ALautowahState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALautowahState, ALeffectState, state); - state->AttackRate = 0.0f; - state->ReleaseRate = 0.0f; - state->Resonance = 0.0f; + state->AttackRate = 1.0f; + state->ReleaseRate = 1.0f; + state->Resonance = 2.0f; state->PeakGain = 1.0f; state->GainCtrl = 1.0f; diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c index 4945faf2..a6bb199e 100644 --- a/Alc/effects/chorus.c +++ b/Alc/effects/chorus.c @@ -30,6 +30,11 @@ #include "alu.h" +enum ChorusWaveForm { + CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE, + CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID +}; + typedef struct ALchorusState { DERIVE_FROM_TYPE(ALeffectState); @@ -44,7 +49,7 @@ typedef struct ALchorusState { ALfloat Gain[2][MaxChannels]; /* effect parameters */ - ALint waveform; + enum ChorusWaveForm waveform; ALint delay; ALfloat depth; ALfloat feedback; @@ -92,7 +97,15 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons ALfloat rate; ALint phase; - state->waveform = Slot->EffectProps.Chorus.Waveform; + switch(Slot->EffectProps.Chorus.Waveform) + { + case AL_CHORUS_WAVEFORM_TRIANGLE: + state->waveform = CWF_Triangle; + break; + case AL_CHORUS_WAVEFORM_SINUSOID: + state->waveform = CWF_Sinusoid; + break; + } state->depth = Slot->EffectProps.Chorus.Depth; state->feedback = Slot->EffectProps.Chorus.Feedback; state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency); @@ -115,10 +128,10 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons state->lfo_range = fastf2u(frequency/rate + 0.5f); switch(state->waveform) { - case AL_CHORUS_WAVEFORM_TRIANGLE: + case CWF_Triangle: state->lfo_scale = 4.0f / state->lfo_range; break; - case AL_CHORUS_WAVEFORM_SINUSOID: + case CWF_Sinusoid: state->lfo_scale = F_2PI / state->lfo_range; break; } @@ -198,10 +211,15 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, co ALfloat temps[64][2]; ALuint td = minu(SamplesToDo-base, 64); - if(state->waveform == AL_CHORUS_WAVEFORM_TRIANGLE) - ProcessTriangle(state, td, SamplesIn+base, temps); - else if(state->waveform == AL_CHORUS_WAVEFORM_SINUSOID) - ProcessSinusoid(state, td, SamplesIn+base, temps); + switch(state->waveform) + { + case CWF_Triangle: + ProcessTriangle(state, td, SamplesIn+base, temps); + break; + case CWF_Sinusoid: + ProcessSinusoid(state, td, SamplesIn+base, temps); + break; + } for(kt = 0;kt < MaxChannels;kt++) { @@ -224,10 +242,7 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, co } } -static void ALchorusState_Delete(ALchorusState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALchorusState) DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState); @@ -240,7 +255,7 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f { ALchorusState *state; - state = malloc(sizeof(*state)); + state = ALchorusState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALchorusState, ALeffectState, state); @@ -249,6 +264,7 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f state->SampleBuffer[1] = NULL; state->offset = 0; state->lfo_range = 1; + state->waveform = CWF_Triangle; return STATIC_CAST(ALeffectState, state); } diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c index bab155c8..9728cabe 100644 --- a/Alc/effects/compressor.c +++ b/Alc/effects/compressor.c @@ -133,10 +133,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint Samples } } -static void ALcompressorState_Delete(ALcompressorState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALcompressorState) DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState); @@ -149,7 +146,7 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory * { ALcompressorState *state; - state = malloc(sizeof(*state)); + state = ALcompressorState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALcompressorState, ALeffectState, state); diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c index fe57c7d8..89af1c84 100644 --- a/Alc/effects/dedicated.c +++ b/Alc/effects/dedicated.c @@ -76,10 +76,7 @@ static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesTo } } -static void ALdedicatedState_Delete(ALdedicatedState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState) DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState); @@ -93,7 +90,7 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa ALdedicatedState *state; ALsizei s; - state = malloc(sizeof(*state)); + state = ALdedicatedState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALdedicatedState, ALeffectState, state); diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c index e39a8197..b535655b 100644 --- a/Alc/effects/distortion.c +++ b/Alc/effects/distortion.c @@ -170,10 +170,7 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples } } -static void ALdistortionState_Delete(ALdistortionState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); @@ -186,7 +183,7 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory * { ALdistortionState *state; - state = malloc(sizeof(*state)); + state = ALdistortionState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALdistortionState, ALeffectState, state); diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c index 7471c3dd..049ae9c7 100644 --- a/Alc/effects/echo.c +++ b/Alc/effects/echo.c @@ -97,7 +97,7 @@ static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const AL ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf, 1.0f - Slot->EffectProps.Echo.Damping, - (ALfloat)LOWPASSFREQREF/frequency, 0.0f); + LOWPASSFREQREF/frequency, 0.0f); gain = Slot->Gain; dirGain = fabsf(lrpan); @@ -161,10 +161,7 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const state->Offset = offset; } -static void ALechoState_Delete(ALechoState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALechoState) DEFINE_ALEFFECTSTATE_VTABLE(ALechoState); @@ -177,7 +174,7 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory)) { ALechoState *state; - state = malloc(sizeof(*state)); + state = ALechoState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALechoState, ALeffectState, state); diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c index d5bd2caf..cfe7c46c 100644 --- a/Alc/effects/equalizer.c +++ b/Alc/effects/equalizer.c @@ -155,10 +155,7 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesTo } } -static void ALequalizerState_Delete(ALequalizerState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALequalizerState) DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState); @@ -172,7 +169,7 @@ ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(fa ALequalizerState *state; int it; - state = malloc(sizeof(*state)); + state = ALequalizerState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALequalizerState, ALeffectState, state); diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c index a94cd365..117cd1f2 100644 --- a/Alc/effects/flanger.c +++ b/Alc/effects/flanger.c @@ -30,6 +30,11 @@ #include "alu.h" +enum FlangerWaveForm { + FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE, + FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID +}; + typedef struct ALflangerState { DERIVE_FROM_TYPE(ALeffectState); @@ -44,7 +49,7 @@ typedef struct ALflangerState { ALfloat Gain[2][MaxChannels]; /* effect parameters */ - ALint waveform; + enum FlangerWaveForm waveform; ALint delay; ALfloat depth; ALfloat feedback; @@ -92,7 +97,15 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co ALfloat rate; ALint phase; - state->waveform = Slot->EffectProps.Flanger.Waveform; + switch(Slot->EffectProps.Flanger.Waveform) + { + case AL_FLANGER_WAVEFORM_TRIANGLE: + state->waveform = FWF_Triangle; + break; + case AL_FLANGER_WAVEFORM_SINUSOID: + state->waveform = FWF_Sinusoid; + break; + } state->depth = Slot->EffectProps.Flanger.Depth; state->feedback = Slot->EffectProps.Flanger.Feedback; state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency); @@ -115,10 +128,10 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co state->lfo_range = fastf2u(frequency/rate + 0.5f); switch(state->waveform) { - case AL_FLANGER_WAVEFORM_TRIANGLE: + case FWF_Triangle: state->lfo_scale = 4.0f / state->lfo_range; break; - case AL_FLANGER_WAVEFORM_SINUSOID: + case FWF_Sinusoid: state->lfo_scale = F_2PI / state->lfo_range; break; } @@ -198,10 +211,15 @@ static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, ALfloat temps[64][2]; ALuint td = minu(SamplesToDo-base, 64); - if(state->waveform == AL_FLANGER_WAVEFORM_TRIANGLE) - ProcessTriangle(state, td, SamplesIn+base, temps); - else if(state->waveform == AL_FLANGER_WAVEFORM_SINUSOID) - ProcessSinusoid(state, td, SamplesIn+base, temps); + switch(state->waveform) + { + case FWF_Triangle: + ProcessTriangle(state, td, SamplesIn+base, temps); + break; + case FWF_Sinusoid: + ProcessSinusoid(state, td, SamplesIn+base, temps); + break; + } for(kt = 0;kt < MaxChannels;kt++) { @@ -224,10 +242,7 @@ static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, } } -static void ALflangerState_Delete(ALflangerState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALflangerState) DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState); @@ -240,7 +255,7 @@ ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factor { ALflangerState *state; - state = malloc(sizeof(*state)); + state = ALflangerState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALflangerState, ALeffectState, state); @@ -249,6 +264,7 @@ ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factor state->SampleBuffer[1] = NULL; state->offset = 0; state->lfo_range = 1; + state->waveform = FWF_Triangle; return STATIC_CAST(ALeffectState, state); } diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c index 17ca7e17..a5475708 100644 --- a/Alc/effects/modulator.c +++ b/Alc/effects/modulator.c @@ -171,10 +171,7 @@ static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesTo } } -static void ALmodulatorState_Delete(ALmodulatorState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState) DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); @@ -187,7 +184,7 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN { ALmodulatorState *state; - state = malloc(sizeof(*state)); + state = ALmodulatorState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALmodulatorState, ALeffectState, state); diff --git a/Alc/effects/null.c b/Alc/effects/null.c index 816a2525..6dacd021 100644 --- a/Alc/effects/null.c +++ b/Alc/effects/null.c @@ -48,10 +48,20 @@ static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samp (void)samplesOut; } -/* This frees the memory used by the object, after it has been destructed. */ -static void ALnullState_Delete(ALnullState *state) +/* This allocates memory to store the object, before it gets constructed. + * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + */ +static void *ALnullState_New(size_t size) +{ + return malloc(size); +} + +/* This frees the memory used by the object, after it has been destructed. + * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + */ +static void ALnullState_Delete(void *ptr) { - free(state); + free(ptr); } /* Define the forwards and the ALeffectState vtable for this type. */ @@ -67,7 +77,7 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory)) { ALnullState *state; - state = calloc(1, sizeof(*state)); + state = ALnullState_New(sizeof(*state)); if(!state) return NULL; /* Set vtables for inherited types. */ SET_VTABLE2(ALnullState, ALeffectState, state); diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c index 75ec76e3..245aed41 100644 --- a/Alc/effects/reverb.c +++ b/Alc/effects/reverb.c @@ -1096,7 +1096,7 @@ static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, cons } else { - hfscale = (ALfloat)LOWPASSFREQREF / frequency; + hfscale = LOWPASSFREQREF / frequency; ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, Slot->EffectProps.Reverb.GainHF, hfscale, 0.0f); @@ -1170,10 +1170,7 @@ static ALvoid ALreverbState_Destruct(ALreverbState *State) State->SampleBuffer = NULL; } -static void ALreverbState_Delete(ALreverbState *state) -{ - free(state); -} +DECLARE_DEFAULT_ALLOCATORS(ALreverbState) DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState); @@ -1187,7 +1184,7 @@ static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(f ALreverbState *state; ALuint index; - state = malloc(sizeof(ALreverbState)); + state = ALreverbState_New(sizeof(*state)); if(!state) return NULL; SET_VTABLE2(ALreverbState, ALeffectState, state); diff --git a/Alc/helpers.c b/Alc/helpers.c index f8a5f13b..e1220fd4 100644 --- a/Alc/helpers.c +++ b/Alc/helpers.c @@ -18,6 +18,14 @@ * Or go to http://www.gnu.org/copyleft/lgpl.html */ +#ifdef _WIN32 +#ifdef __MINGW32__ +#define _WIN32_IE 0x501 +#else +#define _WIN32_IE 0x400 +#endif +#endif + #include "config.h" #include <stdlib.h> @@ -71,24 +79,20 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, #include <ieeefp.h> #endif +#ifdef _WIN32_IE +#include <shlobj.h> +#endif + #include "alMain.h" +#include "alu.h" #include "atomic.h" #include "uintmap.h" +#include "vector.h" +#include "alstring.h" #include "compat.h" +#include "threads.h" -extern inline RefCount IncrementRef(volatile RefCount *ptr); -extern inline RefCount DecrementRef(volatile RefCount *ptr); -extern inline int ExchangeInt(volatile int *ptr, int newval); -extern inline void *ExchangePtr(XchgPtr *ptr, void *newval); -extern inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval); -extern inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval); - -extern inline void LockUIntMapRead(UIntMap *map); -extern inline void UnlockUIntMapRead(UIntMap *map); -extern inline void LockUIntMapWrite(UIntMap *map); -extern inline void UnlockUIntMapWrite(UIntMap *map); - extern inline ALuint NextPowerOf2(ALuint value); extern inline ALint fastf2i(ALfloat f); extern inline ALuint fastf2u(ALfloat f); @@ -135,7 +139,11 @@ void FillCPUCaps(ALuint capfilter) { caps |= CPU_CAP_SSE; if((cpuinf[0].regs[3]&(1<<26))) + { caps |= CPU_CAP_SSE2; + if((cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } } } } @@ -160,10 +168,13 @@ void FillCPUCaps(ALuint capfilter) caps |= CPU_CAP_NEON; #endif - TRACE("Got caps:%s%s%s%s\n", ((caps&CPU_CAP_SSE)?((capfilter&CPU_CAP_SSE)?" SSE":" (SSE)"):""), - ((caps&CPU_CAP_SSE2)?((capfilter&CPU_CAP_SSE2)?" SSE2":" (SSE2)"):""), - ((caps&CPU_CAP_NEON)?((capfilter&CPU_CAP_NEON)?" Neon":" (Neon)"):""), - ((!caps)?" -none-":"")); + TRACE("Extensions:%s%s%s%s%s\n", + ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), + ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), + ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), + ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""), + ((!capfilter) ? " -none-" : "") + ); CPUCapFlags = caps & capfilter; } @@ -294,46 +305,36 @@ void RestoreFPUMode(const FPUCtl *ctl) #ifdef _WIN32 -extern inline int alsched_yield(void); -void althread_once(althread_once_t *once, void (*callback)(void)) +static WCHAR *FromUTF8(const char *str) { - LONG ret; - while((ret=InterlockedExchange(once, 1)) == 1) - alsched_yield(); - if(ret == 0) - callback(); - InterlockedExchange(once, 2); -} - + WCHAR *out = NULL; + int len; -int althread_key_create(althread_key_t *key, void (*callback)(void*)) -{ - *key = TlsAlloc(); - if(callback) - InsertUIntMapEntry(&TlsDestructor, *key, callback); - return 0; -} - -int althread_key_delete(althread_key_t key) -{ - InsertUIntMapEntry(&TlsDestructor, key, NULL); - TlsFree(key); - return 0; + if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0) + { + out = calloc(sizeof(WCHAR), len); + MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len); + } + return out; } -void *althread_getspecific(althread_key_t key) -{ return TlsGetValue(key); } -int althread_setspecific(althread_key_t key, void *val) +void *LoadLib(const char *name) { - TlsSetValue(key, val); - return 0; -} - + HANDLE hdl = NULL; + WCHAR *wname; -void *LoadLib(const char *name) -{ return LoadLibraryA(name); } + wname = FromUTF8(name); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", name); + else + { + hdl = LoadLibraryW(wname); + free(wname); + } + return hdl; +} void CloseLib(void *handle) { FreeLibrary((HANDLE)handle); } void *GetSymbol(void *handle, const char *name) @@ -362,97 +363,27 @@ WCHAR *strdupW(const WCHAR *str) return ret; } -#else - -#include <pthread.h> -#ifdef HAVE_PTHREAD_NP_H -#include <pthread_np.h> -#endif -#include <sched.h> -#include <time.h> -#include <sys/time.h> - -void InitializeCriticalSection(CRITICAL_SECTION *cs) +FILE *al_fopen(const char *fname, const char *mode) { - pthread_mutexattr_t attrib; - int ret; + WCHAR *wname=NULL, *wmode=NULL; + FILE *file = NULL; - ret = pthread_mutexattr_init(&attrib); - assert(ret == 0); + wname = FromUTF8(fname); + wmode = FromUTF8(mode); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); + else if(!wmode) + ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode); + else + file = _wfopen(wname, wmode); - ret = pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE); -#ifdef HAVE_PTHREAD_NP_H - if(ret != 0) - ret = pthread_mutexattr_setkind_np(&attrib, PTHREAD_MUTEX_RECURSIVE); -#endif - assert(ret == 0); - ret = pthread_mutex_init(cs, &attrib); - assert(ret == 0); + free(wname); + free(wmode); - pthread_mutexattr_destroy(&attrib); -} -void DeleteCriticalSection(CRITICAL_SECTION *cs) -{ - int ret; - ret = pthread_mutex_destroy(cs); - assert(ret == 0); -} -void EnterCriticalSection(CRITICAL_SECTION *cs) -{ - int ret; - ret = pthread_mutex_lock(cs); - assert(ret == 0); -} -void LeaveCriticalSection(CRITICAL_SECTION *cs) -{ - int ret; - ret = pthread_mutex_unlock(cs); - assert(ret == 0); + return file; } -/* NOTE: This wrapper isn't quite accurate as it returns an ALuint, as opposed - * to the expected DWORD. Both are defined as unsigned 32-bit types, however. - * Additionally, Win32 is supposed to measure the time since Windows started, - * as opposed to the actual time. */ -ALuint timeGetTime(void) -{ -#if _POSIX_TIMERS > 0 - struct timespec ts; - int ret = -1; - -#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0) -#if _POSIX_MONOTONIC_CLOCK == 0 - static int hasmono = 0; - if(hasmono > 0 || (hasmono == 0 && - (hasmono=sysconf(_SC_MONOTONIC_CLOCK)) > 0)) -#endif - ret = clock_gettime(CLOCK_MONOTONIC, &ts); -#endif - if(ret != 0) - ret = clock_gettime(CLOCK_REALTIME, &ts); - assert(ret == 0); - - return ts.tv_nsec/1000000 + ts.tv_sec*1000; #else - struct timeval tv; - int ret; - - ret = gettimeofday(&tv, NULL); - assert(ret == 0); - - return tv.tv_usec/1000 + tv.tv_sec*1000; -#endif -} - -void Sleep(ALuint t) -{ - struct timespec tv, rem; - tv.tv_nsec = (t*1000000)%1000000000; - tv.tv_sec = t/1000; - - while(nanosleep(&tv, &rem) == -1 && errno == EINTR) - tv = rem; -} #ifdef HAVE_DLFCN_H @@ -500,6 +431,135 @@ void al_print(const char *type, const char *func, const char *fmt, ...) fflush(LogFile); } +#ifdef _WIN32 +static inline int is_slash(int c) +{ return (c == '\\' || c == '/'); } + +FILE *OpenDataFile(const char *fname, const char *subdir) +{ + static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; + WCHAR *wname=NULL, *wsubdir=NULL; + int i; + + /* If the path is absolute, open it directly. */ + if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2])) + { + FILE *f; + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + return NULL; + } + + wname = FromUTF8(fname); + wsubdir = FromUTF8(subdir); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); + else if(!wsubdir) + ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir); + else for(i = 0;i < 2;i++) + { + WCHAR buffer[PATH_MAX]; + size_t len; + FILE *f; + + if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE) + continue; + + len = lstrlenW(buffer); + if(len > 0 && is_slash(buffer[len-1])) + buffer[--len] = '\0'; + _snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname); + len = lstrlenW(buffer); + while(len > 0) + { + --len; + if(buffer[len] == '/') + buffer[len] = '\\'; + } + + if((f=_wfopen(buffer, L"rb")) != NULL) + { + TRACE("Opened %ls\n", buffer); + return f; + } + WARN("Could not open %ls\n", buffer); + } + free(wname); + free(wsubdir); + + return NULL; +} +#else +FILE *OpenDataFile(const char *fname, const char *subdir) +{ + char buffer[PATH_MAX] = ""; + const char *str, *next; + FILE *f; + + if(fname[0] == '/') + { + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + return NULL; + } + + if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') + snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname); + else if((str=getenv("HOME")) != NULL && str[0] != '\0') + snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname); + if(buffer[0]) + { + if((f=al_fopen(buffer, "rb")) != NULL) + { + TRACE("Opened %s\n", buffer); + return f; + } + WARN("Could not open %s\n", buffer); + } + + if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') + str = "/usr/local/share/:/usr/share/"; + + next = str; + while((str=next) != NULL && str[0] != '\0') + { + size_t len; + next = strchr(str, ':'); + + if(!next) + len = strlen(str); + else + { + len = next - str; + next++; + } + + if(len > sizeof(buffer)-1) + len = sizeof(buffer)-1; + strncpy(buffer, str, len); + buffer[len] = '\0'; + snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname); + + if((f=al_fopen(buffer, "rb")) != NULL) + { + TRACE("Opened %s\n", buffer); + return f; + } + WARN("Could not open %s\n", buffer); + } + + return NULL; +} +#endif + void SetRTPriority(void) { @@ -526,183 +586,172 @@ void SetRTPriority(void) } -static void Lock(volatile ALenum *l) +ALboolean vector_reserve(void *ptr, size_t base_size, size_t obj_count, size_t obj_size, ALboolean exact) { - while(ExchangeInt(l, AL_TRUE) == AL_TRUE) - alsched_yield(); + vector_ *vecptr = ptr; + if((size_t)(*vecptr ? (*vecptr)->Capacity : 0) < obj_count) + { + ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0); + void *temp; + + /* Limit vector sizes to the greatest power-of-two value that an + * ALsizei can hold. */ + if(obj_count > (INT_MAX>>1)+1) + return AL_FALSE; + + /* Use the next power-of-2 size if we don't need to allocate the exact + * amount. This is preferred when regularly increasing the vector since + * it means fewer reallocations. Though it means it also wastes some + * memory. */ + if(exact == AL_FALSE) + obj_count = NextPowerOf2((ALuint)obj_count); + + /* Need to be explicit with the caller type's base size, because it + * could have extra padding before the start of the array (that is, + * sizeof(*vector_) may not equal base_size). */ + temp = realloc(*vecptr, base_size + obj_size*obj_count); + if(temp == NULL) return AL_FALSE; + + *vecptr = temp; + (*vecptr)->Capacity = (ALsizei)obj_count; + (*vecptr)->Size = old_size; + } + return AL_TRUE; } -static void Unlock(volatile ALenum *l) +ALboolean vector_resize(void *ptr, size_t base_size, size_t obj_count, size_t obj_size) { - ExchangeInt(l, AL_FALSE); + vector_ *vecptr = ptr; + if(*vecptr || obj_count > 0) + { + if(!vector_reserve(vecptr, base_size, obj_count, obj_size, AL_TRUE)) + return AL_FALSE; + (*vecptr)->Size = (ALsizei)obj_count; + } + return AL_TRUE; } -void RWLockInit(RWLock *lock) +ALboolean vector_insert(void *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend) { - lock->read_count = 0; - lock->write_count = 0; - lock->read_lock = AL_FALSE; - lock->read_entry_lock = AL_FALSE; - lock->write_lock = AL_FALSE; + vector_ *vecptr = ptr; + if(datstart != datend) + { + ptrdiff_t ins_elem = ((char*)ins_pos - ((char*)(*vecptr) + base_size)) / obj_size; + ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size; + + assert(numins > 0); + if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins || + !vector_reserve(vecptr, base_size, VECTOR_SIZE(*vecptr)+numins, obj_size, AL_TRUE)) + return AL_FALSE; + + /* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */ + if(ins_elem < (*vecptr)->Size) + { + memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size), + (char*)(*vecptr) + base_size + ((ins_elem )*obj_size), + ((*vecptr)->Size-ins_elem)*obj_size); + } + memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size), + datstart, numins*obj_size); + (*vecptr)->Size += (ALsizei)numins; + } + return AL_TRUE; } -void ReadLock(RWLock *lock) + +extern inline ALsizei al_string_length(const_al_string str); +extern inline ALboolean al_string_empty(const_al_string str); +extern inline const al_string_char_type *al_string_get_cstr(const_al_string str); + +void al_string_clear(al_string *str) { - Lock(&lock->read_entry_lock); - Lock(&lock->read_lock); - if(IncrementRef(&lock->read_count) == 1) - Lock(&lock->write_lock); - Unlock(&lock->read_lock); - Unlock(&lock->read_entry_lock); + /* Reserve one more character than the total size of the string. This is to + * ensure we have space to add a null terminator in the string data so it + * can be used as a C-style string. */ + VECTOR_RESERVE(*str, 1); + VECTOR_RESIZE(*str, 0); + *VECTOR_ITER_END(*str) = 0; } -void ReadUnlock(RWLock *lock) +static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len, + const al_string_char_type *str2, ALsizei str2len) { - if(DecrementRef(&lock->read_count) == 0) - Unlock(&lock->write_lock); + ALsizei complen = mini(str1len, str2len); + int ret = memcmp(str1, str2, complen); + if(ret == 0) + { + if(str1len > str2len) return 1; + if(str1len < str2len) return -1; + } + return ret; } - -void WriteLock(RWLock *lock) +int al_string_cmp(const_al_string str1, const_al_string str2) { - if(IncrementRef(&lock->write_count) == 1) - Lock(&lock->read_lock); - Lock(&lock->write_lock); + return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), + &VECTOR_FRONT(str2), al_string_length(str2)); } - -void WriteUnlock(RWLock *lock) +int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2) { - Unlock(&lock->write_lock); - if(DecrementRef(&lock->write_count) == 0) - Unlock(&lock->read_lock); + return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), + str2, (ALsizei)strlen(str2)); } - -void InitUIntMap(UIntMap *map, ALsizei limit) +void al_string_copy(al_string *str, const_al_string from) { - map->array = NULL; - map->size = 0; - map->maxsize = 0; - map->limit = limit; - RWLockInit(&map->lock); + ALsizei len = VECTOR_SIZE(from); + VECTOR_RESERVE(*str, len+1); + VECTOR_RESIZE(*str, 0); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len); + *VECTOR_ITER_END(*str) = 0; } -void ResetUIntMap(UIntMap *map) +void al_string_copy_cstr(al_string *str, const al_string_char_type *from) { - WriteLock(&map->lock); - free(map->array); - map->array = NULL; - map->size = 0; - map->maxsize = 0; - WriteUnlock(&map->lock); + size_t len = strlen(from); + VECTOR_RESERVE(*str, len+1); + VECTOR_RESIZE(*str, 0); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); + *VECTOR_ITER_END(*str) = 0; } -ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) +void al_string_append_char(al_string *str, const al_string_char_type c) { - ALsizei pos = 0; + VECTOR_RESERVE(*str, al_string_length(*str)+2); + VECTOR_PUSH_BACK(*str, c); + *VECTOR_ITER_END(*str) = 0; +} - WriteLock(&map->lock); - if(map->size > 0) +void al_string_append_cstr(al_string *str, const al_string_char_type *from) +{ + size_t len = strlen(from); + if(len != 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->array[mid].key < key) - low = mid + 1; - else - high = mid; - } - if(map->array[low].key < key) - low++; - pos = low; + VECTOR_RESERVE(*str, al_string_length(*str)+len+1); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); + *VECTOR_ITER_END(*str) = 0; } - - if(pos == map->size || map->array[pos].key != key) - { - if(map->size == map->limit) - { - WriteUnlock(&map->lock); - return AL_OUT_OF_MEMORY; - } - - if(map->size == map->maxsize) - { - ALvoid *temp = NULL; - ALsizei newsize; - - newsize = (map->maxsize ? (map->maxsize<<1) : 4); - if(newsize >= map->maxsize) - temp = realloc(map->array, newsize*sizeof(map->array[0])); - if(!temp) - { - WriteUnlock(&map->lock); - return AL_OUT_OF_MEMORY; - } - map->array = temp; - map->maxsize = newsize; - } - - if(pos < map->size) - memmove(&map->array[pos+1], &map->array[pos], - (map->size-pos)*sizeof(map->array[0])); - map->size++; - } - map->array[pos].key = key; - map->array[pos].value = value; - WriteUnlock(&map->lock); - - return AL_NO_ERROR; } -ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) +void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) { - ALvoid *ptr = NULL; - WriteLock(&map->lock); - if(map->size > 0) + if(to != from) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->array[mid].key < key) - low = mid + 1; - else - high = mid; - } - if(map->array[low].key == key) - { - ptr = map->array[low].value; - if(low < map->size-1) - memmove(&map->array[low], &map->array[low+1], - (map->size-1-low)*sizeof(map->array[0])); - map->size--; - } + VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to); + *VECTOR_ITER_END(*str) = 0; } - WriteUnlock(&map->lock); - return ptr; } -ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) +#ifdef _WIN32 +void al_string_copy_wcstr(al_string *str, const wchar_t *from) { - ALvoid *ptr = NULL; - ReadLock(&map->lock); - if(map->size > 0) + int len; + if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->array[mid].key < key) - low = mid + 1; - else - high = mid; - } - if(map->array[low].key == key) - ptr = map->array[low].value; + VECTOR_RESERVE(*str, len); + VECTOR_RESIZE(*str, len-1); + WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL); + *VECTOR_ITER_END(*str) = 0; } - ReadUnlock(&map->lock); - return ptr; } +#endif @@ -28,11 +28,7 @@ #include "alMain.h" #include "alSource.h" #include "alu.h" - - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif +#include "hrtf.h" /* Current data set limits defined by the makehrtf utility. */ @@ -62,16 +58,6 @@ struct Hrtf { static const ALchar magicMarker00[8] = "MinPHR00"; static const ALchar magicMarker01[8] = "MinPHR01"; -/* Define the default HRTF: - * ALubyte defaultAzCount [DefaultHrtf.evCount] - * ALushort defaultEvOffset [DefaultHrtf.evCount] - * ALshort defaultCoeffs [DefaultHrtf.irCount * defaultHrtf.irSize] - * ALubyte defaultDelays [DefaultHrtf.irCount] - * - * struct Hrtf DefaultHrtf - */ -#include "hrtf_tables.inc" - static struct Hrtf *LoadedHrtfs = NULL; /* Calculate the elevation indices given the polar elevation in radians. @@ -664,24 +650,25 @@ static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate) static struct Hrtf *LoadHrtf(ALuint deviceRate) { - const char *fnamelist = NULL; + const char *fnamelist = "default-%r.mhr"; - if(!ConfigValueStr(NULL, "hrtf_tables", &fnamelist)) - return NULL; + ConfigValueStr(NULL, "hrtf_tables", &fnamelist); while(*fnamelist != '\0') { struct Hrtf *Hrtf = NULL; char fname[PATH_MAX]; + const char *next; ALchar magic[8]; ALuint i; FILE *f; + i = 0; while(isspace(*fnamelist) || *fnamelist == ',') fnamelist++; - i = 0; - while(*fnamelist != '\0' && *fnamelist != ',') + next = fnamelist; + while(*(fnamelist=next) != '\0' && *fnamelist != ',') { - const char *next = strpbrk(fnamelist, "%,"); + next = strpbrk(fnamelist, "%,"); while(fnamelist != next && *fnamelist && i < sizeof(fname)) fname[i++] = *(fnamelist++); @@ -704,7 +691,6 @@ static struct Hrtf *LoadHrtf(ALuint deviceRate) } else ERR("Invalid marker '%%%c'\n", *next); - fnamelist = next; } i = minu(i, sizeof(fname)-1); fname[i] = '\0'; @@ -716,7 +702,7 @@ static struct Hrtf *LoadHrtf(ALuint deviceRate) continue; TRACE("Loading %s...\n", fname); - f = fopen(fname, "rb"); + f = OpenDataFile(fname, "openal/hrtf"); if(f == NULL) { ERR("Could not open %s\n", fname); @@ -774,37 +760,31 @@ const struct Hrtf *GetHrtf(ALCdevice *device) Hrtf = LoadHrtf(device->Frequency); if(Hrtf != NULL) return Hrtf; - - if(device->Frequency == DefaultHrtf.sampleRate) - return &DefaultHrtf; } ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(device->FmtChans), device->Frequency); return NULL; } -void FindHrtfFormat(const ALCdevice *device, enum DevFmtChannels *chans, ALCuint *srate) +ALCboolean FindHrtfFormat(const ALCdevice *device, enum DevFmtChannels *chans, ALCuint *srate) { - const struct Hrtf *hrtf = &DefaultHrtf; - - if(device->Frequency != DefaultHrtf.sampleRate) + const struct Hrtf *hrtf = LoadedHrtfs; + while(hrtf != NULL) { - hrtf = LoadedHrtfs; - while(hrtf != NULL) - { - if(device->Frequency == hrtf->sampleRate) - break; - hrtf = hrtf->next; - } + if(device->Frequency == hrtf->sampleRate) + break; + hrtf = hrtf->next; + } - if(hrtf == NULL) - hrtf = LoadHrtf(device->Frequency); - if(hrtf == NULL) - hrtf = &DefaultHrtf; + if(hrtf == NULL) + { + hrtf = LoadHrtf(device->Frequency); + if(hrtf == NULL) return ALC_FALSE; } *chans = DevFmtStereo; *srate = hrtf->sampleRate; + return ALC_TRUE; } void FreeHrtfs(void) diff --git a/Alc/hrtf.h b/Alc/hrtf.h new file mode 100644 index 00000000..d9022d02 --- /dev/null +++ b/Alc/hrtf.h @@ -0,0 +1,28 @@ +#ifndef ALC_HRTF_H +#define ALC_HRTF_H + +#include "AL/al.h" +#include "AL/alc.h" + +enum DevFmtChannels; + +struct Hrtf; + +#define HRIR_BITS (7) +#define HRIR_LENGTH (1<<HRIR_BITS) +#define HRIR_MASK (HRIR_LENGTH-1) +#define HRTFDELAY_BITS (20) +#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS) +#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1) + +const struct Hrtf *GetHrtf(ALCdevice *device); +ALCboolean FindHrtfFormat(const ALCdevice *device, enum DevFmtChannels *chans, ALCuint *srate); + +void FreeHrtfs(void); + +ALuint GetHrtfIrSize(const struct Hrtf *Hrtf); +ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]); +void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays); +ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep); + +#endif /* ALC_HRTF_H */ diff --git a/Alc/hrtf_tables.inc b/Alc/hrtf_tables.inc deleted file mode 100644 index 9c6af6a1..00000000 --- a/Alc/hrtf_tables.inc +++ /dev/null @@ -1,848 +0,0 @@ -/* Elevation metrics */ -static const ALubyte defaultAzCount[19] = { 1, 12, 24, 36, 45, 56, 60, 72, 72, 72, 72, 72, 60, 56, 45, 36, 24, 12, 1, }; -static const ALushort defaultEvOffset[19] = { 0, 1, 13, 37, 73, 118, 174, 234, 306, 378, 450, 522, 594, 654, 710, 755, 791, 815, 827, }; - -/* HRIR Coefficients */ -static const ALshort defaultCoeffs[26496] = -{ - +6117, +8354, +3531, +2903, +1118, +342, +874, -822, -2447, -1309, +579, -133, -617, -1093, -998, -416, -244, -202, -84, -134, -375, -200, -416, -331, -408, -614, -489, -332, -207, -97, -7, -145, - +6958, +8678, +3013, +3274, +1145, +25, +847, -771, -2501, -1666, +534, +86, -597, -917, -967, -916, -367, -80, -198, -215, -475, -312, -442, -372, -399, -554, -517, -249, -103, -75, -5, -176, - +6349, +8083, +2935, +2854, +1109, +344, +851, -891, -2351, -1252, +506, -191, -559, -861, -745, -485, -327, -77, -17, -172, -405, -189, -402, -350, -459, -599, -449, -315, -184, -77, +13, -134, - +5910, +7494, +2698, +2625, +992, +373, +884, -825, -2197, -962, +698, -127, -425, -759, -697, -286, -213, -171, -64, -127, -336, -131, -355, -271, -368, -553, -428, -299, -179, -74, +4, -137, - +5808, +7380, +2660, +2585, +1001, +453, +998, -698, -2078, -854, +773, -100, -470, -857, -730, -241, -185, -179, -63, -114, -325, -112, -322, -220, -300, -507, -404, -297, -216, -122, -35, -168, - +5957, +7591, +2687, +2503, +871, +340, +913, -821, -2282, -1053, +775, -16, -459, -904, -784, -240, -114, -100, -12, -59, -285, -105, -334, -253, -340, -532, -400, -251, -127, -17, +33, -156, - +6515, +8209, +2752, +2639, +892, +326, +895, -1074, -2683, -1313, +854, -53, -682, -1135, -939, -316, -155, -153, -32, -64, -346, -124, -339, -249, -345, -543, -400, -232, -129, -39, +38, -120, - +6887, +8888, +3060, +2770, +926, +146, +833, -1245, -2800, -1317, +584, -231, -619, -1119, -983, -413, -244, -71, -9, -143, -405, -183, -399, -307, -420, -595, -439, -302, -153, -55, +34, -129, - +7340, +9090, +2964, +3130, +1162, +115, +612, -1577, -2702, -692, +458, -1019, -864, -943, -587, -190, -434, -226, +41, -162, -398, -144, -463, -353, -533, -708, -464, -374, -270, -103, +2, -187, - +8348, +9865, +2233, +2503, +594, +154, +1277, -1477, -3002, -433, +1280, -991, -1387, -1385, -643, +20, -514, -498, -16, -150, -545, -145, -486, -329, -539, -846, -509, -326, -250, -110, +9, -211, - +8668, +10241, +2446, +2335, +160, +133, +1408, -1244, -3403, -895, +1742, -269, -1341, -2123, -1100, +207, -94, -571, -238, -198, -607, -211, -581, -326, -492, -792, -539, -293, -202, -119, -28, -265, - +8261, +10052, +3261, +2775, +344, +65, +1000, -1138, -3661, -1615, +1337, +126, -290, -1970, -1899, -55, +143, -202, -175, -248, -492, -207, -591, -284, -495, -901, -578, -409, -197, -51, +38, -151, - +7749, +9628, +3084, +3049, +870, +16, +727, -1159, -2880, -1819, +969, +83, -717, -1031, -1759, -1055, -39, -9, -26, -31, -379, -324, -646, -479, -344, -651, -608, -341, -196, -48, +22, -160, - +7872, +8889, +2424, +3846, +1113, -405, +870, -636, -2562, -2175, +535, +438, -627, -691, -988, -1594, -378, +123, -383, -287, -574, -435, -436, -421, -370, -475, -575, -138, +9, -79, -10, -210, - +7007, +8290, +2692, +3507, +1037, -125, +838, -876, -2265, -1597, +225, -49, -510, -580, -573, -987, -677, +32, +42, -236, -535, -281, -374, -349, -433, -522, -408, -257, -182, -133, +15, -132, - +6475, +7694, +2419, +2913, +1152, +342, +794, -920, -2186, -1265, +352, -196, -457, -569, -494, -665, -413, +113, +33, -239, -428, -173, -379, -384, -517, -559, -408, -308, -159, -59, +31, -118, - +5981, +7105, +2165, +2692, +1134, +347, +767, -876, -2030, -957, +421, -294, -318, -441, -466, -416, -281, -39, -27, -194, -378, -156, -345, -274, -384, -498, -440, -344, -158, -51, +21, -134, - +5468, +6465, +2050, +2460, +952, +446, +858, -776, -1849, -646, +711, -106, -171, -377, -412, -225, -208, -139, -49, -122, -280, -59, -288, -217, -330, -482, -372, -276, -153, -54, +11, -123, - +5247, +6086, +1760, +2189, +842, +506, +1054, -433, -1470, -298, +965, +30, -189, -498, -477, -163, -126, -106, -24, -94, -283, -72, -247, -143, -266, -434, -302, -196, -110, -68, -52, -213, - +5232, +6232, +1996, +2383, +983, +625, +1098, -519, -1614, -432, +848, -66, -286, -585, -461, -110, -157, -165, -46, -92, -261, -20, -217, -109, -190, -394, -333, -281, -239, -151, -66, -182, - +5323, +6297, +1900, +2226, +867, +582, +1114, -514, -1700, -507, +886, -53, -362, -695, -545, -42, +42, -2, +24, -89, -290, -90, -302, -188, -238, -341, -202, -173, -185, -163, -115, -218, - +5574, +6674, +1997, +2183, +707, +407, +936, -776, -2047, -829, +914, +130, -285, -696, -573, -93, +9, -5, +50, +20, -186, -16, -247, -182, -271, -441, -315, -175, -47, +63, +58, -176, - +6045, +7217, +2109, +2333, +722, +394, +912, -1008, -2484, -1201, +940, +97, -533, -929, -717, -112, +69, +18, +107, +96, -204, -31, -262, -175, -230, -397, -254, -106, -72, -21, +17, -112, - +6856, +7935, +1962, +2469, +717, +365, +900, -1353, -2894, -1309, +1170, +26, -784, -1168, -861, -220, -61, -113, +16, +18, -323, -43, -253, -171, -282, -468, -311, -130, -56, +10, +78, -94, - +7514, +8789, +1982, +2483, +712, +262, +907, -1695, -3191, -1325, +1153, -23, -806, -1333, -975, -279, -115, -59, -35, -42, -373, -130, -392, -273, -384, -542, -334, -150, -38, +4, +93, -104, - +7708, +9393, +2471, +2673, +752, -59, +807, -1726, -3115, -1282, +555, -307, -586, -1146, -966, -429, -241, +103, +48, -175, -435, -160, -372, -287, -438, -565, -385, -281, -93, -15, +72, -115, - +7961, +9595, +2700, +3173, +1089, -199, +393, -2045, -2982, -748, +119, -1067, -577, -787, -541, -413, -602, -36, +144, -149, -336, -111, -417, -324, -553, -659, -425, -410, -226, -58, +56, -137, - +8750, +9725, +2189, +3527, +1193, -202, +307, -2424, -2793, +130, +87, -2091, -927, -659, -104, -33, -745, -205, +211, -231, -414, -73, -531, -374, -686, -789, -410, -438, -340, -94, +17, -238, - +9887, +10729, +1149, +2712, +812, -159, +1171, -2374, -3238, +573, +900, -2331, -1559, -881, +10, +282, -825, -483, +230, -178, -567, -74, -569, -393, -734, -962, -410, -384, -393, -161, +71, -197, - +11064, +11221, +279, +2282, +77, +57, +1829, -2384, -3518, +811, +1905, -2262, -2145, -1529, -99, +470, -985, -810, +158, -183, -755, -35, -580, -314, -703, -1105, -476, -311, -308, -116, +33, -294, - +11675, +11698, +209, +1859, -553, +242, +2236, -2105, -4055, +456, +2775, -1639, -2377, -2493, -525, +885, -700, -1033, -74, -269, -767, -30, -730, -335, -644, -1070, -436, -206, -262, -87, -13, -377, - +11801, +11992, +657, +1830, -875, +135, +2141, -1883, -4508, -144, +3105, -728, -2249, -3247, -939, +1040, -70, -1098, -336, -229, -882, -166, -780, -279, -597, -994, -559, -229, -208, -150, -49, -410, - +11441, +12041, +1632, +2283, -957, -39, +1816, -1840, -4880, -918, +2879, +121, -1146, -3647, -1711, +1051, +438, -745, -603, -262, -776, -209, -700, -151, -609, -1078, -625, -402, -194, -201, -9, -262, - +10865, +11676, +2584, +2613, -586, -83, +1224, -1524, -5125, -1724, +2382, +303, +58, -3199, -2831, +663, +561, -315, -300, -369, -601, -190, -808, -177, -617, -1246, -617, -484, -161, +7, +81, -166, - +10191, +11218, +2753, +3225, -135, -314, +1139, -1492, -4741, -2381, +2120, +202, +83, -1978, -3635, -327, +757, +52, -23, -340, -548, -69, -879, -641, -441, -1126, -641, -329, -170, +77, +67, -175, - +9689, +10846, +2307, +3298, +545, -344, +602, -1494, -3324, -2412, +1584, +308, -905, -920, -2772, -1685, +423, +196, -11, +88, -409, -493, -902, -608, -212, -710, -760, -321, -179, +12, +47, -183, - +8953, +9908, +2218, +3607, +920, -373, +599, -1197, -2905, -2314, +1261, +665, -1096, -819, -1580, -2014, -168, +187, -326, -97, -344, -334, -636, -572, -272, -489, -771, -288, -106, -59, +97, -199, - +8864, +8963, +1784, +4657, +969, -956, +982, -415, -2657, -2862, +633, +942, -750, -396, -1083, -2461, -216, +391, -668, -329, -673, -573, -389, -489, -318, -379, -677, +16, +122, -114, -18, -247, - +7756, +8469, +2432, +4304, +831, -834, +867, -757, -2207, -2102, -12, +321, -466, -174, -519, -1837, -871, +255, +23, -250, -677, -423, -326, -339, -330, -413, -490, -195, -73, -162, -2, -152, - +7061, +7757, +2298, +3660, +972, -50, +816, -871, -2023, -1762, +29, +39, -419, -270, -323, -1264, -738, +345, +74, -339, -535, -233, -317, -423, -530, -487, -350, -279, -220, -117, +68, -96, - +6469, +7209, +2035, +3061, +1233, +321, +690, -887, -1952, -1381, +126, -112, -325, -222, -261, -975, -473, +378, +46, -334, -435, -156, -344, -439, -577, -492, -370, -312, -132, -41, +48, -98, - +5924, +6587, +1791, +2840, +1306, +325, +630, -832, -1787, -1021, +168, -250, -169, -97, -250, -687, -289, +202, -37, -279, -361, -130, -348, -361, -448, -399, -386, -361, -133, -16, +50, -129, - +5282, +6011, +1719, +2545, +1130, +445, +720, -784, -1642, -749, +308, -187, +39, -21, -232, -423, -260, +27, -35, -215, -317, -45, -205, -222, -390, -439, -388, -320, -115, -45, +11, -94, - +4741, +5315, +1665, +2368, +986, +543, +781, -655, -1403, -392, +608, -36, +147, +49, -163, -239, -215, -100, -40, -117, -205, +8, -213, -171, -294, -397, -329, -264, -127, -36, +13, -103, - +4473, +4913, +1415, +2018, +802, +585, +981, -314, -1070, -10, +964, +194, +186, -38, -233, -201, -159, -68, +40, -54, -215, -5, -156, -100, -249, -363, -242, -160, -67, -14, -33, -215, - +4287, +4651, +1225, +2028, +1002, +772, +1151, -74, -767, +174, +960, +71, -57, -266, -220, -24, -57, -86, -63, -101, -197, +12, -123, -8, -137, -308, -225, -169, -123, -121, -128, -237, - +4337, +4965, +1624, +2246, +1058, +843, +1155, -266, -1056, -75, +799, +3, -60, -285, -202, -30, -153, -156, -30, -66, -178, +71, -100, -2, -76, -278, -279, -283, -274, -184, -98, -187, - +4443, +5025, +1531, +2119, +997, +856, +1205, -260, -1169, -213, +764, -10, -150, -366, -219, +89, +76, -16, -38, -108, -204, -23, -212, -56, -66, -154, -135, -246, -290, -240, -153, -192, - +4604, +5187, +1421, +1877, +708, +663, +1129, -291, -1278, -255, +968, +170, -109, -537, -478, +41, +175, +171, +207, -3, -219, -11, -242, -189, -190, -256, -119, -13, -31, -100, -174, -309, - +4923, +5652, +1529, +1902, +624, +528, +924, -671, -1743, -664, +996, +326, -100, -476, -372, +25, +128, +85, +100, +106, -73, +61, -152, -121, -199, -342, -237, -104, +32, +141, +70, -204, - +5421, +6220, +1615, +2041, +613, +476, +868, -945, -2209, -1062, +1028, +282, -361, -678, -462, +90, +247, +119, +166, +124, -141, +83, -164, -99, -131, -291, -197, -34, +4, +4, +72, -42, - +6190, +6818, +1507, +2236, +597, +518, +893, -1240, -2699, -1316, +1269, +222, -676, -957, -667, -54, +151, +26, +170, +185, -160, +80, -150, -125, -158, -266, -176, +4, -12, -12, +35, -111, - +7121, +7541, +1201, +2394, +583, +455, +869, -1653, -3075, -1304, +1541, +95, -937, -1178, -764, -132, +36, -84, +64, +109, -308, +44, -155, -103, -220, -385, -224, -28, +7, +54, +114, -67, - +7954, +8408, +983, +2438, +632, +359, +872, -2062, -3411, -1245, +1676, +31, -1048, -1363, -832, -150, -48, -120, +14, +94, -364, -58, -298, -170, -314, -457, -245, -56, +16, +70, +176, -53, - +8426, +9277, +1223, +2430, +532, +133, +944, -2316, -3533, -1292, +1244, -43, -818, -1449, -1029, -259, -53, +135, -32, -56, -402, -136, -404, -282, -407, -503, -267, -114, +46, +41, +136, -109, - +8583, +9860, +1762, +2634, +598, -282, +801, -2272, -3373, -1211, +481, -346, -524, -1183, -947, -470, -228, +324, +74, -228, -460, -132, -337, -273, -462, -518, -331, -270, -26, +19, +110, -100, - +8789, +10123, +2143, +3154, +907, -539, +350, -2551, -3191, -653, -134, -1144, -348, -706, -562, -559, -647, +213, +190, -170, -308, -125, -412, -293, -550, -593, -367, -441, -161, -3, +111, -113, - +9368, +10135, +1939, +3756, +1190, -650, -93, -2909, -2955, +75, -583, -2117, -371, -455, -4, -431, -1040, +118, +327, -275, -298, -31, -480, -340, -761, -730, -381, -524, -283, -44, +42, -215, - +10370, +10210, +1204, +4156, +1175, -623, -28, -3363, -2672, +1145, -623, -3309, -728, -270, +428, +27, -1178, -99, +420, -357, -415, +13, -626, -389, -869, -847, -326, -533, -414, -65, +33, -301, - +11635, +11075, -31, +3531, +952, -550, +724, -3443, -3032, +1791, +130, -3849, -1409, -370, +688, +391, -1323, -332, +504, -391, -598, +96, -680, -457, -950, -1002, -305, -479, -469, -136, +71, -286, - +13207, +11905, -1547, +2772, +301, -274, +1873, -3587, -3556, +2285, +1234, -3973, -2188, -835, +809, +672, -1631, -769, +602, -241, -847, +83, -674, -374, -941, -1208, -342, -448, -473, -101, +134, -318, - +14337, +12287, -2399, +2422, -469, +62, +2531, -3616, -3929, +2464, +2354, -3991, -2785, -1503, +623, +891, -1697, -1088, +457, -261, -1009, +151, -717, -276, -913, -1388, -372, -300, -384, -112, +67, -401, - +15087, +12820, -2744, +1910, -1184, +425, +3120, -3448, -4563, +2187, +3466, -3489, -3173, -2589, +266, +1446, -1532, -1382, +191, -327, -1016, +193, -847, -301, -888, -1367, -296, -177, -336, -114, +20, -474, - +15581, +13124, -2434, +1591, -1676, +521, +3195, -3036, -5227, +1724, +4250, -2658, -3404, -3573, -56, +1861, -986, -1649, +4, -355, -1041, +91, -1052, -239, -767, -1287, -362, -121, -296, -58, -65, -569, - +15601, +13452, -1914, +1563, -2007, +399, +3052, -2823, -5735, +1036, +4617, -1613, -3294, -4431, -451, +2051, -245, -1777, -331, -235, -1215, -48, -1032, -173, -736, -1221, -537, -142, -232, -189, -69, -580, - +15203, +13689, -870, +1955, -2244, +197, +2769, -2807, -6122, +172, +4587, -487, -2272, -5183, -1225, +2226, +446, -1489, -791, -175, -1171, -107, -880, -30, -790, -1309, -587, -320, -186, -316, -13, -442, - +14648, +13594, +409, +2248, -2137, -16, +2270, -2431, -6673, -665, +4167, +189, -766, -5403, -2316, +2230, +741, -1027, -813, -342, -840, -164, -978, +109, -745, -1474, -642, -547, -154, -159, +47, -247, - +13996, +13109, +1461, +2499, -1685, -37, +1524, -2013, -6863, -1546, +3712, +317, +445, -4840, -3714, +1807, +920, -566, -438, -485, -703, -148, -1075, +6, -791, -1647, -584, -567, -97, +72, +120, -190, - +13165, +12534, +1976, +3362, -1471, -314, +1381, -1961, -6484, -2520, +3399, +361, +831, -3679, -5026, +938, +1344, -192, -237, -452, -597, -58, -1183, -507, -582, -1548, -641, -480, -68, +246, +63, -219, - +12626, +12250, +1611, +3690, -681, -941, +1250, -1619, -5566, -2944, +3036, +177, +25, -1933, -5154, -665, +1470, +164, +107, -276, -664, -178, -1249, -762, -296, -1315, -631, -232, -234, +180, +66, -251, - +11980, +11934, +1167, +3735, +109, -727, +510, -1829, -3781, -3108, +2485, +496, -1203, -728, -4093, -2241, +1182, +347, -49, +233, -470, -711, -1173, -711, -7, -812, -944, -259, -161, +83, +61, -212, - +11247, +11010, +1120, +4114, +574, -764, +386, -1687, -3259, -2815, +2128, +825, -1544, -509, -2722, -2986, +467, +444, -350, +190, -276, -570, -1067, -700, +132, -512, -1156, -281, -35, -10, +123, -207, - +10116, +9947, +1402, +4469, +759, -942, +664, -954, -2962, -3136, +1495, +1323, -1375, -529, -1640, -2951, +60, +455, -723, -188, -423, -428, -490, -651, -326, -390, -839, -121, -11, -87, +128, -262, - +9934, +8876, +1128, +5737, +644, -1620, +1223, -109, -2816, -3751, +890, +1596, -1024, +3, -1280, -3531, +194, +690, -1086, -313, -780, -731, -285, -592, -233, -265, -834, +227, +227, -185, -20, -287, - +8502, +8500, +2204, +5212, +430, -1605, +1028, -561, -2155, -2773, -185, +874, -495, +321, -570, -2903, -846, +586, -74, -234, -828, -571, -225, -357, -205, -310, -640, -115, +58, -204, -18, -162, - +7584, +7772, +2171, +4547, +704, -725, +925, -716, -1861, -2401, -267, +505, -407, +183, -244, -2138, -927, +680, +60, -459, -671, -326, -222, -451, -469, -353, -358, -211, -203, -191, +84, -98, - +6877, +7100, +2027, +3726, +1015, +37, +675, -791, -1748, -2003, -196, +244, -318, +111, -88, -1694, -708, +748, +20, -457, -485, -226, -270, -512, -629, -427, -353, -293, -167, -97, +74, -60, - +6094, +6516, +1758, +3125, +1461, +313, +506, -750, -1620, -1508, -142, +48, -126, +224, -68, -1338, -382, +704, -7, -454, -398, -106, -300, -528, -626, -363, -337, -340, -80, +0, +68, -81, - +5474, +5821, +1573, +2970, +1494, +257, +449, -672, -1419, -1123, -157, -119, +81, +343, -71, -994, -247, +423, -94, -356, -314, -117, -307, -403, -455, -271, -389, -398, -91, +12, +63, -119, - +4706, +5219, +1556, +2650, +1342, +394, +527, -623, -1238, -816, -65, -106, +329, +424, -62, -677, -264, +193, -57, -276, -283, -47, -151, -234, -379, -329, -417, -360, -71, -27, +20, -78, - +3969, +4463, +1642, +2395, +1176, +599, +584, -518, -1032, -470, +216, +31, +488, +473, -5, -409, -215, +23, -78, -181, -137, +68, -136, -185, -309, -316, -333, -291, -98, -38, +10, -54, - +3424, +3827, +1536, +2086, +1002, +678, +738, -333, -681, +35, +605, +269, +533, +528, +71, -284, -241, -104, +39, -20, -139, +38, -65, -74, -225, -293, -257, -229, -78, +5, +12, -102, - +3209, +3445, +1231, +1747, +899, +784, +1000, +168, -305, +345, +880, +396, +496, +306, -68, -218, -102, -1, +55, -36, -135, +45, -28, -19, -180, -238, -160, -103, -27, -22, -94, -255, - +2989, +3191, +1074, +1836, +1186, +989, +1156, +381, -8, +502, +823, +223, +193, +66, +7, +5, -16, -56, -80, -81, -98, +68, +12, +88, -46, -191, -160, -138, -110, -149, -188, -250, - +3027, +3528, +1478, +2058, +1276, +1085, +1144, +149, -311, +281, +686, +149, +146, +19, +50, +25, -119, -132, -55, -57, -69, +143, +32, +105, +37, -167, -229, -266, -280, -209, -151, -192, - +3214, +3748, +1601, +2025, +1190, +1116, +1202, +89, -552, -87, +478, +220, +213, +9, +55, +81, -6, -61, -71, -93, -61, +81, -92, +76, +154, -10, -220, -365, -342, -251, -170, -159, - +3266, +3741, +1347, +1791, +1078, +1077, +1224, +91, -623, +51, +737, +114, -29, -162, -63, +235, +299, +125, +20, -78, -150, -17, -140, -29, -21, +31, +66, -141, -296, -306, -239, -221, - +3541, +4062, +1288, +1550, +718, +829, +1077, -44, -837, -142, +945, +448, +111, -311, -297, +101, +259, +279, +274, +70, -83, +51, -163, -131, -124, -170, -40, +66, +53, -44, -202, -351, - +3954, +4589, +1351, +1593, +636, +691, +854, -482, -1382, -587, +1032, +601, +79, -242, -184, +108, +254, +168, +138, +202, +51, +122, -43, -74, -123, -230, -173, -35, +112, +219, +65, -237, - +4535, +5212, +1390, +1740, +598, +607, +776, -814, -1908, -1025, +1099, +545, -214, -426, -239, +239, +413, +193, +207, +182, -47, +183, -57, -40, -30, -175, -148, +40, +73, +37, +118, +7, - +5318, +5785, +1269, +1968, +575, +658, +805, -1127, -2442, -1307, +1372, +466, -591, -725, -443, +119, +395, +164, +270, +354, -56, +110, -69, -55, -25, -139, -51, +109, +11, +15, +19, -56, - +6436, +6460, +882, +2236, +497, +666, +802, -1573, -2941, -1397, +1743, +286, -941, -976, -603, -18, +243, -4, +204, +282, -184, +170, -29, -57, -91, -187, -111, +112, +41, +27, +68, -96, - +7585, +7281, +373, +2458, +491, +542, +790, -2104, -3319, -1286, +2043, +126, -1200, -1227, -664, -50, +85, -70, +93, +172, -322, +133, -69, -86, -185, -297, -168, +53, +66, +96, +196, +7, - +8554, +8204, +13, +2518, +545, +433, +808, -2585, -3651, -1161, +2167, +23, -1301, -1412, -719, -73, +18, -111, +45, +202, -388, -7, -242, -122, -287, -383, -162, +38, +78, +115, +239, -27, - +9212, +9220, +103, +2448, +407, +223, +921, -2981, -3805, -1200, +1719, +26, -1055, -1626, -929, -188, +43, +226, -94, +18, -406, -89, -400, -275, -396, -440, -171, +13, +140, +43, +175, -97, - +9425, +9997, +611, +2554, +412, -263, +952, -2951, -3684, -1187, +857, -167, -621, -1410, -1053, -347, -65, +452, -3, -165, -453, -143, -340, -269, -451, -441, -242, -198, +101, +82, +181, -110, - +9602, +10483, +1219, +2947, +590, -759, +563, -2991, -3446, -797, -98, -815, -218, -921, -768, -633, -444, +563, +104, -246, -350, -155, -369, -240, -521, -488, -298, -401, -10, +50, +144, -115, - +9910, +10681, +1487, +3547, +923, -1024, +61, -3330, -3146, -137, -870, -1590, +8, -449, -298, -772, -939, +485, +312, -243, -240, -78, -399, -324, -669, -567, -332, -547, -156, +25, +160, -93, - +10699, +10561, +1144, +4345, +1148, -1198, -465, -3692, -2804, +756, -1507, -2809, +121, -169, +369, -651, -1433, +435, +473, -418, -220, +23, -526, -346, -932, -715, -318, -648, -289, -5, +58, -250, - +11865, +10458, +321, +5013, +1084, -1187, -415, -4235, -2391, +1999, -1758, -4266, -88, +147, +902, -193, -1705, +234, +619, -537, -297, +92, -743, -384, -1023, -815, -255, -684, -426, -12, +35, -330, - +13346, +11005, -1112, +4810, +949, -1079, +138, -4587, -2456, +3025, -1295, -5300, -712, +202, +1270, +293, -1889, -46, +762, -642, -552, +226, -817, -466, -1180, -995, -171, -630, -552, -62, +74, -399, - +14971, +12208, -3034, +3816, +594, -863, +1619, -4784, -3319, +3684, -109, -5706, -1550, +27, +1400, +652, -2108, -458, +869, -483, -776, +220, -858, -433, -1201, -1254, -117, -550, -640, -140, +204, -318, - +16995, +12596, -4875, +3443, -244, -334, +2668, -5173, -3649, +4366, +1153, -6044, -2514, -559, +1728, +927, -2652, -901, +1093, -410, -1143, +329, -841, -335, -1196, -1458, -195, -525, -564, -33, +194, -462, - +18241, +12894, -5826, +3138, -1130, +187, +3380, -5255, -4143, +4561, +2499, -6195, -3183, -1309, +1509, +1233, -2676, -1266, +894, -425, -1299, +432, -922, -204, -1176, -1686, -183, -313, -482, -86, +105, -535, - +19257, +13367, -6379, +2592, -1984, +792, +4070, -5179, -4919, +4384, +3882, -5784, -3709, -2594, +1250, +1947, -2577, -1645, +617, -476, -1302, +491, -1070, -197, -1169, -1676, -76, -162, -446, -100, +47, -614, - +19926, +13767, -6244, +2039, -2577, +1096, +4300, -4713, -5817, +3943, +5112, -5041, -4213, -3795, +981, +2627, -2147, -2070, +431, -519, -1281, +436, -1355, -148, -989, -1627, -104, -51, -433, -15, -50, -735, - +20240, +14125, -5799, +1784, -3007, +1064, +4265, -4313, -6590, +3386, +5836, -3972, -4474, -4840, +741, +2976, -1373, -2410, +167, -416, -1413, +247, -1435, -24, -966, -1480, -291, -55, -313, -65, -145, -781, - +20015, +14601, -5023, +1828, -3407, +908, +4058, -4144, -7140, +2450, +6274, -2630, -4153, -5964, +216, +3298, -453, -2534, -345, -178, -1641, +157, -1311, +64, -954, -1504, -457, -98, -260, -285, -55, -762, - +19554, +14863, -3770, +2150, -3733, +724, +3709, -4072, -7621, +1495, +6312, -1303, -3056, -6981, -603, +3594, +321, -2274, -920, -58, -1568, +72, -1120, +198, -1037, -1588, -525, -300, -179, -434, +24, -584, - +18917, +14814, -2183, +2351, -3715, +455, +3162, -3571, -8351, +569, +5938, -361, -1346, -7547, -1834, +3783, +700, -1769, -1070, -238, -1132, -62, -1211, +391, -1003, -1782, -585, -567, -111, -272, +77, -349, - +18106, +14727, -945, +2225, -3047, +302, +2302, -2969, -8873, -290, +5489, -62, +243, -7348, -3434, +3665, +911, -1246, -777, -513, -915, -51, -1324, +362, -1049, -1967, -506, -762, +16, -24, +82, -173, - +17254, +13805, +460, +2982, -2958, +154, +1644, -2479, -8779, -1643, +5145, +181, +1378, -6455, -5404, +3105, +1393, -741, -423, -612, -704, -72, -1484, +78, -955, -2125, -465, -626, +16, +278, +142, -257, - +16265, +13474, +690, +3849, -2566, -357, +1746, -2519, -8172, -2680, +5031, +100, +1311, -4691, -6912, +1980, +2040, -358, -216, -622, -649, +103, -1622, -626, -497, -1964, -571, -423, -30, +370, +49, -249, - +15646, +13159, +261, +4311, -1678, -1288, +1621, -1987, -7004, -3335, +4543, -14, +302, -2477, -7170, -48, +2406, +32, +137, -377, -773, -133, -1665, -841, -152, -1691, -589, -158, -270, +328, +50, -306, - +14914, +12999, -328, +4365, -702, -1133, +553, -2016, -4671, -4043, +4026, +360, -1378, -617, -6226, -2166, +2377, +298, -4, +327, -691, -899, -1434, -845, +201, -1106, -1032, -122, -210, +203, +45, -267, - +13958, +12124, -373, +4782, +21, -1129, +272, -2320, -3404, -3627, +3139, +1060, -2160, -3, -4519, -3752, +1820, +541, -476, +570, -311, -1038, -1434, -687, +457, -671, -1457, -167, -11, +62, +118, -218, - +12918, +10973, -67, +5240, +257, -1386, +443, -1503, -3392, -3835, +2924, +1581, -2328, -20, -2956, -4208, +1045, +694, -894, +202, -292, -645, -959, -841, +200, -409, -1380, -60, +24, -53, +228, -310, - +11453, +9857, +558, +5625, +370, -1608, +838, -661, -3107, -4122, +1928, +2028, -1762, -127, -1944, -4054, +595, +740, -1213, -166, -505, -568, -347, -773, -312, -278, -974, +83, +83, -136, +142, -319, - +11087, +8602, +495, +7107, +58, -2371, +1638, +267, -3072, -4860, +1382, +2390, -1510, +558, -1623, -4806, +939, +963, -1665, -194, -913, -908, -115, -747, -101, -144, -1056, +521, +304, -293, -8, -330, - +9334, +8472, +2057, +6324, -243, -2522, +1386, -290, -2159, -3680, -245, +1661, -676, +956, -788, -4263, -533, +965, -282, -154, -1031, -742, -56, -400, -21, -215, -885, +31, +223, -288, -38, -168, - +8202, +7850, +2127, +5711, +161, -1682, +1271, -489, -1744, -3323, -499, +1254, -540, +771, -297, -3344, -963, +1117, -26, -591, -845, -454, -64, -490, -350, -197, -438, -86, -170, -321, +106, -106, - +7291, +7027, +2168, +4730, +564, -540, +844, -580, -1481, -2901, -474, +853, -365, +616, -57, -2666, -805, +1195, -67, -613, -593, -252, -128, -623, -577, -254, -297, -288, -279, -138, +171, -54, - +6561, +6470, +2014, +3851, +1081, +55, +479, -597, -1428, -2456, -448, +615, -253, +562, +94, -2318, -595, +1226, -130, -587, -411, -244, -197, -635, -712, -320, -371, -307, -113, -95, +84, -14, - +5624, +5918, +1762, +3266, +1692, +206, +288, -499, -1267, -1883, -464, +389, +31, +706, +63, -1900, -243, +1098, -142, -584, -335, -87, -244, -648, -669, -209, -339, -372, -24, +17, +83, -55, - +4953, +5180, +1625, +3144, +1769, +106, +214, -394, -1053, -1453, -538, +193, +277, +807, +43, -1499, -93, +748, -239, -450, -232, -115, -285, -525, -481, -91, -398, -447, -39, +45, +83, -112, - +4115, +4607, +1619, +2888, +1682, +161, +244, -329, -812, -1141, -599, +67, +593, +924, +29, -1137, -210, +449, -129, -365, -249, -133, -121, -277, -340, -178, -530, -428, -2, -6, +39, -84, - +3177, +4024, +1786, +2341, +1485, +534, +375, -316, -681, -795, -329, +287, +817, +880, +57, -781, -190, +258, -112, -286, -153, +95, +56, -236, -403, -237, -370, -335, -19, -47, -2, +10, - +2412, +3137, +2012, +2211, +1367, +699, +342, -131, -395, -414, -56, +329, +935, +975, +138, -528, -171, +55, -126, -152, +41, +119, -74, -171, -229, -182, -327, -290, -76, -28, +10, -11, - +1837, +2561, +1897, +1806, +1200, +798, +563, +60, +7, +156, +349, +604, +923, +1022, +216, -431, -239, -64, +75, +24, -47, +67, +76, -40, -176, -179, -231, -224, -40, +27, +0, -75, - +1620, +2181, +1603, +1392, +1074, +916, +849, +620, +322, +470, +694, +763, +907, +764, +43, -376, -76, +71, +101, -3, -39, +86, +110, +0, -148, -121, -113, -69, +18, +9, -117, -268, - +1298, +1691, +1267, +1399, +1307, +1124, +1131, +1104, +891, +667, +567, +575, +628, +507, +105, -108, +52, +24, -45, -25, -34, +55, +160, +131, -29, -57, -79, -54, -16, -156, -247, -286, - +1179, +1848, +1466, +1730, +1760, +1293, +1046, +857, +729, +698, +565, +381, +236, +320, +318, +91, +9, -97, -187, -79, +91, +171, +186, +225, +142, -75, -156, -189, -210, -232, -263, -201, - +1352, +2405, +2052, +1820, +1528, +1334, +1064, +583, +298, +292, +336, +420, +473, +384, +247, -41, -172, -129, -4, +7, +46, +224, +188, +178, +173, -32, -241, -326, -370, -251, -162, -160, - +1579, +2518, +2005, +1719, +1482, +1383, +1139, +573, +88, -86, +163, +548, +481, +335, +281, +88, +49, -30, -101, -73, +80, +110, +12, +172, +343, +170, -228, -430, -393, -298, -216, -120, - +1583, +2450, +1680, +1499, +1447, +1389, +1184, +570, +27, +164, +464, +283, +128, +199, +220, +325, +423, +123, -72, -74, -43, -22, -19, +72, +115, +277, +185, -229, -439, -410, -283, -162, - +1893, +2685, +1508, +1157, +986, +1135, +1120, +583, -124, +57, +765, +643, +287, -137, -232, +275, +511, +407, +350, +8, -96, +73, -79, -165, +36, +100, +98, +172, -6, -229, -385, -374, - +2209, +3190, +1517, +1044, +774, +938, +848, +161, -496, -290, +950, +973, +315, -37, -73, +18, +254, +388, +315, +238, +180, +91, -63, -33, -110, -192, -12, +129, +232, +155, -164, -407, - +2814, +3765, +1508, +1224, +774, +836, +645, -346, -1219, -800, +1084, +985, +184, +33, +8, +229, +466, +169, +87, +354, +189, +192, +160, -56, +4, -65, -184, -15, +177, +366, +142, -203, - +3495, +4412, +1460, +1383, +688, +753, +615, -665, -1708, -1197, +1222, +875, -213, -223, -72, +363, +610, +259, +273, +267, +37, +270, +44, +9, +100, -45, -90, +149, +122, +13, +149, +90, - +4347, +4935, +1271, +1660, +643, +828, +646, -1007, -2278, -1449, +1567, +759, -633, -516, -277, +238, +622, +239, +355, +523, +20, +141, +46, -6, +111, -2, +46, +213, +21, +33, +1, +6, - +5621, +5545, +799, +2031, +504, +892, +652, -1492, -2860, -1584, +2032, +530, -1030, -782, -469, +89, +432, +59, +340, +429, -61, +279, +83, -74, +47, +39, -51, +241, +56, -19, +59, -78, - +6893, +6162, +149, +2382, +396, +828, +623, -2048, -3200, -1408, +2392, +211, -1344, -961, -526, -16, +350, -90, +203, +396, -312, +253, +114, +45, -53, -188, -37, +217, +87, +120, +108, -66, - +8235, +7080, -573, +2693, +417, +618, +659, -2697, -3569, -1230, +2683, +76, -1575, -1280, -539, +24, +87, -57, +104, +215, -351, +233, +5, -119, -156, -194, -147, +124, +129, +137, +322, +117, - +9244, +7992, -1030, +2743, +479, +517, +696, -3209, -3857, -1043, +2739, -57, -1623, -1427, -588, -20, +90, -120, +74, +343, -449, +31, -200, -78, -273, -310, -78, +136, +134, +157, +303, -20, - +10028, +9088, -1078, +2631, +294, +324, +855, -3729, -3984, -1086, +2220, +48, -1346, -1774, -793, -139, +148, +326, -185, +119, -427, -45, -395, -273, -382, -360, -77, +135, +222, +28, +223, -85, - +10294, +10035, -677, +2629, +250, -196, +1099, -3767, -3909, -1145, +1327, -1, -804, -1656, -1142, -169, +106, +562, -120, -57, -462, -169, -331, -270, -431, -351, -156, -128, +239, +131, +252, -134, - +10483, +10682, -44, +2812, +332, -824, +879, -3596, -3635, -1021, +145, -213, -350, -1319, -899, -635, -133, +919, -37, -375, -472, -69, -227, -286, -527, -354, -246, -281, +155, +54, +190, -76, - +10698, +11090, +572, +3361, +554, -1371, +356, -3757, -3360, -322, -956, -1204, +285, -664, -624, -896, -670, +916, +86, -272, -203, -223, -395, -193, -602, -435, -271, -578, +42, +87, +178, -136, - +11106, +11176, +704, +4050, +883, -1630, -180, -4140, -2927, +390, -1871, -1878, +528, -231, -70, -1098, -1213, +889, +383, -352, -139, -56, -382, -365, -792, -502, -300, -681, -116, +59, +212, -78, - +12036, +10896, +310, +5032, +1033, -1870, -823, -4447, -2503, +1349, -2741, -3214, +855, +29, +682, -1055, -1808, +920, +555, -599, -93, +53, -545, -351, -1135, -651, -258, -802, -256, +44, +72, -285, - +13249, +10533, -420, +6055, +876, -1900, -821, -5027, -1957, +2680, -3299, -4856, +928, +484, +1323, -695, -2251, +831, +740, -767, -47, +126, -881, -350, -1161, -698, -223, -889, -359, +46, +17, -320, - +14864, +10460, -1804, +6568, +665, -1731, -635, -5767, -1405, +4093, -3512, -6402, +622, +675, +1688, -26, -2440, +393, +956, -897, -408, +254, -945, -393, -1446, -932, -12, -855, -629, +79, +67, -544, - +16627, +11397, -3842, +5964, +612, -1624, +440, -5965, -2042, +5011, -2485, -7307, -214, +766, +2141, +296, -2808, +260, +1140, -1052, -587, +505, -1093, -517, -1488, -1064, +12, -788, -651, -39, +118, -479, - +18518, +12633, -6334, +4990, +229, -1244, +2367, -6506, -3133, +5859, -1129, -7799, -1129, +600, +2173, +719, -3075, -318, +1316, -740, -910, +425, -1093, -394, -1508, -1432, +145, -700, -804, -94, +327, -401, - +20973, +12619, -8500, +4951, -770, -461, +3392, -7134, -3315, +6719, +196, -8369, -2213, +3, +2743, +961, -3843, -722, +1691, -730, -1369, +644, -1082, -293, -1519, -1634, +52, -689, -686, +59, +271, -608, - +22297, +12836, -9690, +4807, -1655, +104, +4092, -7312, -3771, +7038, +1693, -8910, -3012, -482, +2558, +1188, -3919, -1129, +1548, -693, -1611, +794, -1184, -115, -1465, -1976, +34, -436, -598, -24, +171, -683, - +23980, +12855, -10608, +4407, -2867, +1228, +4914, -7539, -4560, +7253, +3208, -8777, -3558, -1999, +2512, +1964, -3980, -1552, +1290, -765, -1611, +896, -1300, -1, -1603, -2008, +250, -237, -601, -60, +99, -743, - +24531, +13684, -11034, +3704, -3405, +1665, +5463, -7291, -5568, +6806, +4893, -8341, -4325, -3233, +2310, +2880, -3885, -2013, +1022, -727, -1606, +925, -1552, -68, -1433, -1949, +261, -130, -552, -62, +43, -848, - +25399, +13806, -10659, +3086, -4032, +2086, +5508, -6663, -6694, +6473, +6232, -7551, -4944, -4555, +2202, +3628, -3339, -2592, +933, -785, -1566, +810, -1901, +111, -1220, -1934, +196, +16, -580, +86, -113, -983, - +25699, +14205, -10203, +2771, -4453, +2044, +5466, -6220, -7611, +5940, +7067, -6399, -5381, -5665, +2085, +3997, -2469, -3078, +674, -608, -1758, +594, -1930, +265, -1252, -1733, -62, +13, -409, -15, -218, -1005, - +25576, +14678, -9511, +2627, -4889, +1979, +5231, -5940, -8313, +5036, +7731, -5018, -5437, -6902, +1757, +4371, -1482, -3465, +202, -277, -2109, +514, -1803, +302, -1201, -1716, -327, +65, -365, -277, -95, -1022, - +24917, +15555, -8325, +2832, -5406, +1692, +5071, -5894, -8820, +3797, +8250, -3317, -4558, -8452, +986, +4981, -445, -3381, -624, +98, -2239, +474, -1567, +517, -1341, -1880, -275, -194, -234, -524, +73, -946, - +24560, +15279, -6686, +3008, -5680, +1683, +4433, -5659, -9447, +3055, +7994, -2069, -3264, -9450, +188, +5223, +139, -3059, -1138, +123, -1936, +284, -1347, +532, -1421, -1864, -486, -372, -126, -624, +111, -613, - +23630, +15485, -4781, +2918, -5523, +1209, +3879, -4787, -10506, +1989, +7668, -1020, -1165, -10262, -1408, +5686, +432, -2423, -1223, -173, -1294, +57, -1598, +885, -1360, -2222, -434, -735, -7, -301, +148, -390, - +22705, +15510, -3489, +2520, -4580, +974, +2845, -3997, -11138, +1148, +7254, -749, +544, -10123, -3273, +5720, +656, -1873, -901, -539, -1078, +82, -1698, +797, -1433, -2392, -307, -961, +186, -54, +102, -179, - +21833, +14435, -1480, +2989, -4502, +876, +1865, -3114, -11297, -386, +7023, -399, +2038, -9446, -5658, +5465, +1168, -1290, -470, -782, -783, -20, -1877, +659, -1411, -2696, -156, -783, +130, +317, +217, -316, - +20537, +13992, -708, +4279, -4744, +442, +1972, -3237, -10625, -1985, +6923, -199, +2505, -7991, -7936, +4778, +2128, -1048, -448, -664, -657, +84, -2053, -179, -858, -2526, -357, -737, +238, +571, -51, -298, - +19638, +14025, -1030, +4778, -3659, -452, +2274, -3224, -9891, -2808, +7103, -662, +1734, -5223, -9487, +3226, +2912, -583, -44, -906, -673, +388, -2239, -882, -152, -2483, -417, -219, -101, +496, +27, -274, - +19049, +13570, -1555, +5415, -2800, -1809, +2085, -2243, -8328, -3763, +6265, -489, +380, -2622, -9535, +545, +3560, -254, +273, -392, -940, -193, -2156, -847, +103, -2093, -472, -40, -391, +519, -11, -393, - +18168, +13590, -2249, +5443, -1644, -1455, +603, -2301, -5416, -4970, +5891, +62, -1742, -291, -8568, -1959, +3759, +29, -30, +506, -899, -1149, -1711, -913, +543, -1423, -1173, +54, -259, +333, +11, -312, - +17035, +12827, -2281, +5894, -726, -1489, +185, -2933, -3444, -4639, +4541, +1190, -2977, +751, -6689, -4331, +3534, +367, -666, +1029, -430, -1560, -1739, -617, +856, -909, -1806, +43, +2, +135, +104, -229, - +16109, +11603, -1857, +6452, -517, -1810, +245, -2043, -3755, -4481, +4547, +1368, -3096, +727, -4933, -5160, +2492, +733, -1012, +720, -307, -1028, -1535, -868, +957, -652, -2015, +138, +117, -22, +249, -321, - +14557, +10522, -1242, +6803, -315, -2135, +723, -1118, -3548, -5279, +4014, +2629, -3572, +779, -3115, -5769, +2018, +890, -1739, +290, -311, -714, -642, -1091, +163, -204, -1599, +276, +13, -124, +387, -488, - +12812, +9467, -224, +7139, -314, -2365, +1207, -230, -3341, -5355, +2557, +2879, -2315, +419, -2362, -5322, +1454, +982, -1883, -47, -628, -728, -93, -958, -294, -130, -1142, +362, +163, -228, +155, -376, - +13067, +8369, -144, +8730, -2405, -3459, +1450, -1502, -5332, -4279, +6473, +1714, -1900, +3709, -1903, -4707, +1918, +120, -2550, -1093, -1848, -621, -724, -1598, -147, -744, -877, +540, -151, -509, +318, -28, - +10904, +8241, +866, +7747, -891, -4073, +130, -1350, -3923, -2891, +4427, +2115, -885, +2373, -1299, -4343, +153, +77, -1658, -317, -1588, -605, +53, -1825, -85, -335, -1528, -11, -42, -273, +59, -76, - +9279, +7452, +1520, +7064, -405, -3081, +178, -1385, -2926, -2332, +3123, +1898, -776, +1781, -925, -3718, -102, -120, -1233, -82, -1199, -290, +2, -1550, -49, -380, -1428, -264, -245, -270, +11, -162, - +7668, +6712, +2160, +6267, +54, -2125, +134, -1110, -2049, -2036, +2189, +1460, -699, +1339, -688, -3011, -497, -119, -970, -142, -497, +186, +37, -1292, -184, -526, -1267, -503, -546, -437, +10, -9, - +6368, +6134, +2492, +5439, +623, -1515, +73, -449, -1505, -1848, +1544, +1044, -644, +955, -348, -2562, -737, -6, -786, +148, +175, +380, -63, -1068, -366, -637, -1119, -714, -616, -443, -44, +1, - +5309, +5691, +2463, +4700, +1132, -1190, +301, +59, -1194, -1566, +901, +654, -559, +719, -197, -2168, -766, +93, -248, +496, +263, +194, -53, -903, -530, -692, -1037, -585, -576, -463, -126, -99, - +4671, +5240, +2273, +4089, +1224, -820, +599, +183, -902, -1333, +289, +350, -412, +640, -190, -1832, -504, +307, +145, +562, +47, +10, -140, -891, -579, -467, -751, -658, -629, -412, -121, -71, - +4304, +4612, +2145, +3630, +1141, -235, +799, +247, -609, -1181, -104, +233, -152, +634, +32, -1399, -325, +524, +389, +368, -139, -41, -258, -648, -199, -313, -764, -659, -480, -282, -35, +38, - +3883, +4121, +1999, +3085, +1218, +245, +812, +308, -391, -1122, -309, +165, +293, +913, +46, -1198, -224, +714, +439, +55, -298, +56, +12, -400, -192, -282, -674, -589, -276, -72, +113, +77, - +3262, +3880, +1810, +2399, +1483, +674, +763, +273, -288, -1047, -181, +700, +489, +675, +71, -953, -158, +742, +296, -120, -45, +156, +92, -270, -319, -271, -421, -326, -57, +99, -64, -314, - +2385, +3308, +1933, +2191, +1681, +725, +640, +373, +136, -286, -143, +637, +683, +410, +103, -565, -257, +619, +421, -40, +28, +179, +55, -233, -135, -54, -204, -72, -29, -217, -320, -330, - +1874, +3072, +1941, +1419, +1419, +1279, +891, +484, +398, +297, +230, +463, +469, +136, +85, -228, -107, +538, +410, +40, -27, +98, +145, +77, +20, +54, +21, -272, -370, -309, -248, -206, - +1591, +2376, +1629, +1368, +1569, +1437, +1231, +1006, +583, +432, +297, +204, +299, +145, -140, +13, +312, +339, +220, +17, +14, +285, +382, +203, +101, -101, -334, -332, -302, -287, -184, -195, - +1470, +1787, +1502, +1987, +1911, +1545, +1043, +878, +865, +431, +197, +86, +12, +101, +92, +101, +251, +178, +190, +346, +300, +250, +251, +133, -107, -287, -400, -336, -238, -260, -239, -257, - +1914, +2455, +1662, +1958, +1844, +1522, +942, +380, +248, +262, +332, +128, +22, +126, +219, +6, -12, +304, +516, +586, +402, +88, -170, -256, -295, -306, -261, -267, -279, -273, -326, -327, - +1650, +2454, +1992, +2081, +1838, +1445, +722, +274, +244, +203, +344, +372, +259, +83, -81, -61, +209, +336, +515, +666, +466, +145, -229, -453, -374, -308, -238, -216, -280, -231, -312, -373, - +1972, +2726, +1615, +1875, +1444, +1065, +882, +237, +52, +429, +512, +299, +333, -155, -232, +94, +178, +234, +473, +659, +391, +136, -262, -514, -398, -381, -424, -222, -224, -324, -266, -374, - +1944, +2569, +1405, +1599, +1309, +1157, +866, +373, +250, +451, +528, +453, +471, +1, +55, +191, +129, +155, +283, +436, +597, +713, +68, -320, -396, -509, -294, -216, -242, -220, -186, -273, - +2213, +2847, +1259, +1256, +996, +980, +849, +260, -32, +445, +818, +478, +247, +265, +193, +76, +464, +202, +102, +347, +222, +450, +618, +397, -308, -565, -442, -327, -186, -168, -163, -249, - +2697, +3456, +1227, +1213, +572, +517, +850, +102, -387, +192, +924, +490, +415, +251, -28, +334, +487, +237, +455, +306, -44, +361, +407, +349, +369, -96, -484, -382, -224, -207, -148, -176, - +3120, +3716, +1221, +1448, +755, +624, +603, -564, -1136, +136, +1257, +246, +285, +171, -38, +383, +232, +372, +711, +324, +99, +204, -9, +285, +321, +68, +177, -36, -413, -443, -150, -173, - +3790, +4330, +1096, +1569, +784, +588, +463, -1059, -1653, +106, +1501, +73, -126, -239, -105, +460, +435, +368, +402, +389, +91, +309, +250, +37, -30, -24, +199, +248, +66, -210, -381, -373, - +4622, +5180, +929, +1541, +623, +502, +584, -1498, -2233, +215, +1742, -187, -310, -505, -413, +362, +388, +344, +575, +292, -268, +436, +270, +95, +161, -323, -90, +279, +151, -2, +128, -96, - +5681, +5925, +575, +1587, +609, +492, +477, -2071, -2626, +569, +1878, -446, -437, -854, -523, +332, +170, +294, +523, +68, -219, +380, -38, +251, +114, -335, +23, -101, -23, +248, +375, +75, - +6767, +6666, +205, +1705, +662, +498, +302, -2825, -2958, +1150, +2042, -710, -533, -1232, -642, +377, +122, +237, +348, -95, -351, +388, -30, +5, -60, -377, -119, +125, +76, -40, +330, +247, - +7914, +7510, -124, +1761, +488, +420, +174, -3558, -3104, +1640, +2028, -832, -567, -1607, -766, +361, +95, +311, +212, -352, -461, +375, -113, -24, -351, -633, -128, +128, +9, -5, +457, +80, - +8830, +8494, -246, +1750, +304, +130, +113, -4143, -3165, +1945, +1825, -857, -549, -1777, -958, +191, +133, +387, +88, -480, -608, +386, -265, -246, -302, -723, -280, -84, -50, +74, +350, +96, - +9314, +9665, +71, +1635, +346, -372, -34, -4328, -3176, +2030, +1536, -817, -547, -1732, -1016, -133, +66, +591, +24, -506, -733, +116, -40, -327, -495, -730, -460, +25, -137, -166, +374, -5, - +9168, +10764, +1097, +1383, +468, -796, -470, -4274, -3169, +1769, +1273, -725, -691, -1625, -1027, -452, -36, +630, +83, -652, -832, +127, -43, -349, -695, -737, -514, -168, -181, -314, +338, +138, - +8846, +11350, +2560, +1552, +433, -953, -869, -4243, -3192, +1361, +998, -476, -837, -1626, -886, -800, -125, +635, -124, -228, -816, -255, -7, -394, -552, -761, -755, -414, -246, -133, +444, +75, - +8964, +11306, +3527, +2438, +253, -982, -961, -4230, -3082, +755, +442, -71, -869, -1742, -700, -1038, -454, +793, +25, -389, -604, -243, +7, -464, -738, -765, -951, -330, -59, -171, +251, +95, - +9403, +11033, +3733, +3695, +308, -1271, -992, -4163, -2722, +92, -735, +217, -696, -1699, -422, -1673, -402, +984, -229, -198, -530, -373, +28, -463, -956, -681, -906, -518, -289, -162, +497, +102, - +10184, +10713, +3388, +4822, +577, -1450, -980, -3974, -2096, -266, -2455, +170, -229, -2033, +139, -1657, -867, +1176, -218, -154, -435, -585, +36, -392, -1039, -506, -950, -753, -269, -164, +336, -63, - +11469, +10305, +2546, +5881, +499, -1455, -857, -3843, -1101, -509, -4401, -50, +59, -2342, +782, -1732, -924, +1406, -569, -258, -144, -530, -49, -395, -1238, -446, -756, -782, -536, -315, +337, -105, - +12812, +9997, +1333, +7068, +255, -1649, -308, -3676, +7, -196, -6478, -1003, +993, -2770, +1003, -1205, -1230, +1463, -713, -345, +195, -392, -236, -389, -1244, -431, -777, -964, -608, -183, +371, -471, - +14298, +10024, -465, +8212, +114, -2512, +382, -3674, +1378, +663, -8685, -2459, +1859, -2562, +871, -988, -1367, +1466, -684, -580, +254, -110, -241, -347, -1398, -758, -715, -816, -729, -361, +154, -579, - +15791, +10811, -2667, +8435, +624, -3271, +1069, -3748, +2502, +2079, -10027, -4329, +2301, -1901, +666, -578, -1480, +1296, -627, -770, +123, +401, -53, -652, -1473, -822, -551, -788, -1234, -414, +540, -655, - +17964, +11856, -5206, +7310, +1001, -3366, +1912, -4139, +2658, +3650, -10394, -6002, +1938, -1051, +777, -287, -1664, +896, -566, -1001, +87, +651, -41, -918, -1419, -920, -813, -755, -1256, -323, +672, -811, - +20009, +12521, -7253, +6282, +961, -3398, +2583, -4837, +2731, +5771, -10940, -7407, +1508, -472, +1186, -388, -1734, +332, -519, -1043, -71, +553, -29, -952, -1661, -1165, -989, -425, -1171, -377, +657, -839, - +22177, +13864, -9457, +4361, +273, -2050, +3806, -6178, +2512, +7329, -10722, -7725, +990, -611, +1552, -139, -1889, -63, -466, -1163, -297, +596, -38, -1172, -1902, -1306, -955, -76, -1070, -458, +771, -742, - +24345, +14808, -11064, +2917, -761, -281, +4365, -7368, +2909, +8263, -9604, -7789, +243, -1094, +2044, +574, -2131, -536, -396, -1094, -498, +632, -243, -1137, -1962, -1496, -848, +190, -940, -375, +1054, -832, - +26147, +15550, -12303, +1685, -1810, +967, +4288, -7960, +2974, +9042, -8220, -8190, -317, -1893, +2641, +1163, -2716, -789, -273, -1142, -847, +531, -446, -868, -2137, -1852, -734, +364, -774, -243, +1065, -1025, - +27659, +16152, -13062, +755, -2711, +1616, +4052, -7901, +2526, +9678, -6491, -8602, -661, -2830, +3049, +1720, -3198, -833, -91, -1450, -1161, +618, -586, -618, -2344, -2144, -611, +510, -566, -204, +872, -1008, - +28303, +17220, -13451, +40, -3448, +1563, +3863, -7612, +1462, +10219, -4267, -9081, -1078, -3763, +2992, +2435, -3478, -882, -13, -1844, -1413, +823, -875, -471, -2328, -2410, -559, +601, -471, -315, +732, -991, - +29181, +17405, -13114, -304, -4240, +1458, +3452, -7492, +539, +10765, -2404, -8991, -1702, -4595, +2982, +2817, -3292, -1057, +74, -2172, -1478, +872, -1165, -289, -2195, -2469, -548, +634, -663, -287, +578, -1042, - +29321, +17827, -12348, -557, -5122, +1071, +3363, -7877, -467, +11248, -887, -8472, -2373, -5291, +2598, +3058, -2743, -1532, +107, -2142, -1752, +699, -1290, -111, -1997, -2388, -740, +468, -816, -385, +487, -985, - +28805, +18575, -11075, -519, -6256, +621, +3883, -8757, -1416, +11447, +404, -7310, -2751, -5966, +1821, +3238, -1902, -1967, -52, -1838, -2084, +568, -1247, +63, -1562, -2577, -1051, +401, -1056, -280, +613, -1053, - +27710, +19425, -9278, -279, -7398, +89, +4632, -9740, -2488, +11491, +1418, -5692, -2452, -7072, +956, +3436, -1270, -1977, -465, -1644, -2124, +549, -1018, +267, -1537, -2600, -1365, +17, -689, -160, +642, -1022, - +26524, +19729, -7061, +31, -8340, -267, +5003, -10177, -3987, +11429, +2435, -4362, -1321, -8478, +66, +3718, -1132, -1735, -895, -1806, -1690, +886, -1190, +400, -1479, -2883, -1377, -121, -394, -59, +689, -900, - +24731, +20271, -4631, +239, -8724, -1229, +4844, -9185, -6220, +10730, +4243, -3815, +672, -9539, -1506, +4100, -1430, -1335, -947, -2166, -1162, +1047, -1494, +767, -1758, -3042, -837, -474, -154, +165, +571, -941, - +23477, +19815, -2244, +479, -9027, -1898, +3618, -7399, -7878, +9201, +6108, -3552, +2583, -9590, -3747, +4198, -1760, -1193, -323, -2539, -1400, +1397, -1635, +542, -1400, -3228, -438, -316, -265, +162, +498, -845, - +22603, +18971, -668, +1272, -8642, -2638, +2333, -6179, -8347, +7433, +7800, -2940, +3459, -8248, -6239, +4034, -1687, -1398, +219, -2550, -1401, +1181, -1707, +569, -817, -3297, +15, -296, -506, +401, +364, -924, - +21958, +18536, -765, +2387, -7488, -4223, +1716, -5274, -8935, +6110, +9409, -2450, +3540, -6393, -8448, +3758, -1179, -2277, +576, -2418, -1529, +1177, -1880, +420, +14, -3051, -213, -188, -381, +229, +231, -793, - +22176, +17185, -1463, +4151, -7417, -5322, +1894, -4670, -9301, +4733, +10404, -1838, +3744, -5092, -9471, +2688, -467, -2307, -46, -2508, -1717, +1645, -1714, +18, +585, -2841, -290, -132, -583, +271, +215, -863, - +21283, +15947, -1061, +5846, -8289, -5644, +2856, -4640, -8855, +2098, +10641, -385, +3529, -2820, -10460, +1131, +531, -2119, -590, -2947, -2184, +2054, -1090, -387, +1198, -3026, -869, +546, -650, +116, +60, -894, - +19683, +14620, -396, +7001, -7790, -5818, +2880, -3841, -8800, -338, +10153, +959, +3167, -585, -10098, -824, +1447, -2278, -872, -2755, -2996, +1653, -697, +272, +1462, -3849, -1195, +1382, -669, -246, -11, -701, - +19043, +12870, -2025, +8715, -5341, -5810, +2282, -4136, -8408, -1971, +10181, +1931, +1229, +1395, -8274, -2327, +1832, -1907, -1520, -2677, -3203, +831, -255, +370, +1503, -3747, -1696, +1786, -499, -464, -64, -480, - +17772, +11494, -1905, +9490, -4457, -5781, +2323, -3030, -7953, -3525, +9683, +2138, -957, +3838, -5603, -4565, +2549, -1532, -2419, -1808, -2980, +12, -610, -213, +1746, -2508, -2211, +1172, +20, -342, -24, -448, - +16172, +10332, -1197, +9288, -3999, -4949, +1874, -2282, -7098, -4475, +8985, +1667, -1887, +4717, -3907, -5051, +2520, -1200, -2844, -1330, -2611, -280, -1072, -1112, +1423, -1375, -1811, +550, -258, -177, +416, -403, - +14861, +9191, -894, +9322, -3451, -4203, +1793, -2119, -6076, -4508, +7671, +1778, -2043, +4285, -2907, -4648, +2360, -879, -2603, -1241, -2298, -406, -1008, -1513, +521, -984, -1055, +584, -582, -325, +664, -263, - +15154, +9134, -4908, +9650, +694, -5568, -98, -860, -5127, -4048, +7837, -470, -3638, +3508, -972, -2437, +3394, +805, -1417, +31, -1653, -577, -2362, -2979, -580, -1077, -1684, -192, -259, +22, +565, -429, - +12672, +9048, -2546, +8370, +349, -5006, +73, -1080, -4272, -3672, +5900, +444, -2326, +2893, -1066, -2332, +2479, +666, -949, -82, -1657, -457, -1794, -2452, -454, -1165, -1497, -205, -372, -240, +406, -163, - +10640, +8312, -1031, +7498, +500, -3857, +463, -1057, -3977, -3222, +4691, +603, -1571, +2325, -824, -2114, +1780, +754, -897, -192, -1342, -620, -1307, -1779, -405, -857, -1348, -343, -250, -417, +44, -237, - +8848, +7462, +273, +6812, +521, -2799, +1008, -1064, -3777, -2718, +3754, +821, -1252, +1806, -375, -2145, +1225, +906, -817, -265, -1155, -585, -1036, -1174, -21, -762, -1257, -311, -233, -510, -203, -397, - +7576, +6767, +783, +6094, +949, -1930, +961, -934, -3200, -2533, +3115, +1137, -1365, +1382, -102, -2040, +908, +789, -690, -334, -1018, -480, -555, -707, -43, -640, -1047, -255, -386, -684, -317, -445, - +6758, +6214, +818, +5337, +1627, -1135, +432, -710, -2305, -2322, +2540, +1251, -1296, +892, -62, -1566, +663, +551, -577, -337, -682, -77, -179, -659, -184, -409, -797, -413, -574, -679, -216, -506, - +5676, +5613, +1210, +4774, +1859, -559, +347, -565, -1759, -1969, +1960, +1004, -1099, +525, -53, -1205, +350, +369, -435, -6, -319, -23, -94, -656, -285, -373, -741, -563, -562, -531, -260, -514, - +4838, +5040, +1571, +4251, +1972, -105, +267, -310, -1227, -1573, +1267, +789, -798, +205, +14, -944, +147, +508, -9, +9, -221, +150, -128, -742, -391, -376, -652, -447, -470, -469, -201, -405, - +3971, +4513, +1934, +3945, +1836, +92, +581, -63, -938, -1357, +811, +579, -793, +202, +181, -781, +327, +624, -92, +15, +0, +147, -352, -881, -357, -191, -505, -444, -412, -282, -88, -284, - +3487, +4143, +1924, +3589, +1775, +195, +713, +196, -635, -1287, +387, +393, -687, +361, +568, -535, +98, +502, -1, +100, +31, -150, -611, -593, -111, -190, -456, -382, -216, -81, -181, -553, - +3301, +3728, +1795, +3379, +1523, +332, +960, +396, -543, -1253, +191, +235, -129, +880, +351, -652, +117, +445, +130, +39, -216, -238, -343, -293, -61, -146, -363, -182, -162, -325, -424, -636, - +3158, +3429, +1574, +3069, +1415, +510, +1197, +428, -494, -1109, +222, +719, +168, +681, +275, -640, +81, +477, +19, -67, -36, -85, -122, -162, +45, -5, -361, -356, -380, -419, -409, -428, - +2927, +3070, +1345, +2819, +1417, +712, +1293, +444, -262, -476, +280, +510, +400, +568, +118, -618, -112, +442, +241, +165, -34, -72, +77, +111, +94, -334, -675, -432, -303, -274, -306, -497, - +2770, +2981, +1085, +2080, +1492, +1281, +1415, +707, +136, -480, +296, +711, +259, +268, -123, -614, +243, +742, +216, +47, +81, +319, +285, -194, -408, -500, -506, -280, -185, -330, -423, -456, - +2145, +2721, +1165, +1872, +1627, +1355, +2019, +1188, +250, -296, -84, +490, +282, +67, -115, -178, +405, +756, +354, +121, +267, +315, -30, -468, -422, -383, -385, -212, -269, -357, -406, -427, - +1842, +2681, +1515, +1848, +2108, +1608, +1340, +1172, +512, -174, -238, -168, -13, +491, +474, +113, +382, +613, +415, +165, +91, -47, -298, -384, -349, -382, -402, -278, -199, -319, -431, -344, - +1494, +2675, +2068, +1863, +2114, +2048, +1472, +644, +14, -248, -149, +107, +179, +299, +556, +612, +502, +443, +238, -13, -145, -180, -250, -339, -297, -336, -446, -377, -225, -244, -239, -265, - +1925, +2822, +1707, +2248, +2162, +1532, +1572, +666, -432, -363, +210, +212, +263, +498, +470, +699, +813, +253, -219, -217, -141, -158, -176, -306, -384, -385, -364, -200, -281, -326, -188, -200, - +1895, +2793, +1934, +2045, +1846, +1354, +1196, +799, +59, -3, +248, +165, +197, +407, +651, +831, +698, +67, -242, -221, -251, -169, -144, -186, -354, -464, -369, -235, -233, -217, -227, -253, - +2070, +3085, +1525, +1328, +1530, +1219, +1048, +1205, +562, -2, +387, +198, +130, +544, +658, +484, +722, +457, -348, -457, -175, -78, -253, -223, -258, -370, -406, -378, -222, -139, -199, -290, - +2157, +2952, +1156, +902, +1034, +1185, +1499, +1126, +501, +624, +479, -49, +331, +597, +587, +621, +629, +548, +175, -291, -470, -235, -85, -52, -278, -368, -303, -369, -323, -238, -97, -173, - +2202, +2907, +1140, +947, +996, +914, +968, +769, +506, +769, +808, +471, +171, +297, +838, +683, +510, +567, +580, +137, -151, -326, -508, -27, -10, -284, -285, -236, -358, -292, -161, -179, - +2228, +3053, +1096, +862, +1274, +1155, +755, +13, -357, +880, +1376, +402, +198, +406, +514, +671, +748, +486, +514, +663, +288, -200, -289, -329, -436, -55, -28, -272, -239, -283, -290, -193, - +2611, +3293, +974, +955, +1269, +1230, +755, -497, -748, +618, +1056, +494, +365, +41, +465, +810, +420, +494, +736, +416, +453, +551, -173, -405, -427, -456, -169, -36, -153, -282, -273, -363, - +3071, +3880, +1049, +902, +1063, +1099, +721, -721, -1094, +709, +1109, -47, +104, +119, +412, +545, +413, +580, +520, +543, +532, +362, +329, +140, -499, -431, -240, -372, -190, -30, -169, -356, - +3643, +4399, +965, +972, +1042, +1109, +628, -1277, -1336, +964, +1149, -203, -182, -383, +345, +664, +317, +410, +437, +373, +394, +662, +276, +180, +133, -200, -383, -376, -281, -286, -117, -162, - +4308, +4895, +774, +1151, +1170, +1121, +455, -1795, -1513, +1267, +1131, -396, -245, -566, +93, +375, +376, +540, +286, +94, +297, +541, +252, +439, +34, +129, +201, -368, -356, -264, -280, -354, - +5087, +5494, +565, +1262, +1166, +1002, +246, -2296, -1418, +1649, +899, -695, -391, -688, +139, +229, +44, +521, +312, -3, +101, +250, +192, +280, +43, +280, +150, +88, +29, -357, -310, -372, - +5947, +6051, +327, +1497, +1239, +800, -127, -2807, -1134, +2147, +640, -918, -589, -888, +220, +107, +62, +477, +59, -49, +73, +198, -35, +79, -132, +175, +309, +137, +146, +162, -75, -593, - +6826, +6642, +77, +1786, +1321, +527, -620, -3298, -663, +2560, +320, -1067, -693, -1064, +226, -95, -6, +673, -51, -262, -29, +141, -44, -11, -506, +62, +205, +6, +401, +111, +23, -56, - +7619, +7443, -127, +1929, +1356, +155, -1099, -3708, -137, +2846, -83, -1160, -806, -1101, +269, -385, -138, +769, -105, -266, -156, -64, -45, -101, -607, -105, -82, +19, +101, +44, +338, -87, - +8308, +8291, -181, +1996, +1443, -217, -1606, -4093, +424, +3041, -446, -1225, -972, -1051, +348, -589, -310, +757, -179, -230, -151, -213, -150, -169, -724, -105, -113, -531, +38, +251, +144, +14, - +8643, +9273, +110, +1817, +1665, -587, -2048, -4412, +705, +3223, -686, -1244, -1233, -1013, +428, -696, -381, +630, -323, -238, -129, -251, -193, -373, -663, -295, -481, -278, -3, -42, +123, -69, - +8492, +10281, +986, +1409, +1924, -770, -2443, -4558, +552, +3340, -711, -1234, -1461, -1100, +472, -719, -429, +587, -437, -400, -100, -332, -135, -347, -1076, -422, -280, -435, -79, -23, +2, -146, - +8125, +10926, +2345, +1150, +1899, -669, -2844, -4563, +156, +3228, -595, -1223, -1573, -1303, +463, -750, -482, +579, -448, -544, -287, -237, -139, -775, -987, -370, -466, -463, -189, -20, +78, -37, - +7991, +11060, +3529, +1603, +1541, -533, -3046, -4622, -35, +2877, -564, -1180, -1626, -1431, +402, -852, -505, +583, -405, -609, -263, -542, -401, -426, -1162, -560, -503, -504, -197, +50, +140, -226, - +8113, +10987, +4130, +2562, +1381, -747, -2977, -4641, -186, +2488, -599, -1166, -1769, -1438, +351, -1006, -539, +563, -351, -314, -744, -723, -154, -612, -1117, -726, -662, -455, -57, +40, -92, -256, - +8541, +10760, +4178, +3497, +1698, -1029, -2982, -4222, -351, +1963, -668, -970, -2110, -1515, +400, -1246, -614, +645, -262, -603, -476, -696, -449, -571, -1094, -668, -849, -390, -123, -169, +68, -29, - +9463, +10347, +3864, +4361, +1905, -1015, -3375, -3543, -178, +968, -787, -513, -2341, -1829, +501, -1686, -325, +497, -681, +38, -559, -871, -367, -691, -1137, -482, -872, -677, -284, +7, +225, -241, - +10674, +9874, +3106, +5362, +1727, -766, -3609, -3015, +543, -431, -1222, +153, -2336, -2209, +365, -1836, -554, +242, -426, +197, -633, -802, -399, -696, -1174, -325, -1145, -803, -31, -108, -43, -199, - +11979, +9620, +1869, +6561, +1461, -835, -3245, -2641, +1610, -1691, -2279, +974, -2010, -2575, +505, -2638, -686, +670, -1065, +774, -478, -978, -319, -585, -1119, -498, -1130, -708, -19, -296, -40, -290, - +13201, +9744, +157, +7624, +1518, -1581, -2587, -2110, +2716, -2532, -3960, +1424, -1264, -2535, -109, -3054, -615, +404, -1230, +1095, -263, -1158, -337, -435, -1109, -466, -1189, -812, +84, -300, -171, -443, - +14767, +10074, -1860, +8018, +1956, -2430, -2103, -1372, +3819, -2797, -5967, +1234, +173, -2924, -421, -3162, -1160, +752, -1619, +1210, +95, -1349, -306, -444, -1114, -283, -1142, -1053, +276, -314, -302, -597, - +16745, +11407, -4630, +6674, +3018, -2190, -1545, -1323, +4470, -2021, -7769, +629, +1347, -2958, -574, -3466, -1241, +900, -1709, +1083, +268, -1132, -420, -544, -1164, -33, -940, -1138, +189, -205, -362, -753, - +18725, +12859, -7380, +4619, +4068, -1417, -1079, -1554, +4711, -1055, -8876, -550, +1948, -2355, -915, -3587, -1380, +839, -1610, +894, +413, -1080, -618, -661, -1073, +37, -840, -1169, +44, -15, -345, -1019, - +20617, +14278, -9830, +2481, +4741, -181, -623, -1858, +4753, +25, -9421, -1889, +2095, -1425, -1076, -3758, -1263, +565, -1542, +933, +391, -1113, -672, -722, -1015, -8, -886, -996, +4, +105, -420, -1228, - +22971, +14648, -11936, +1010, +4670, +1158, -431, -2361, +5156, +1040, -10169, -3060, +2153, -790, -797, -4032, -1000, +16, -1618, +1257, +163, -1228, -657, -703, -1282, -23, -913, -801, -89, +33, -392, -1315, - +25010, +15029, -13660, +283, +4193, +2237, -295, -3037, +6357, +2181, -11344, -3815, +2283, -582, +157, -4078, -1205, -374, -1568, +1582, -149, -1216, -484, -731, -1677, +102, -841, -808, -116, +31, -256, -1422, - +26769, +15204, -15115, +212, +3256, +2746, -305, -3981, +8274, +2753, -12585, -4021, +1747, -336, +1272, -4084, -1750, -584, -1463, +1491, -429, -1113, -280, -912, -2029, +54, -759, -856, -175, +86, -304, -1461, - +28266, +15342, -16203, +581, +2138, +2848, -458, -4840, +10385, +2925, -13189, -4103, +831, +88, +2199, -3878, -2349, -502, -1510, +1038, -429, -929, -129, -1132, -2297, -63, -611, -925, -177, +40, -335, -1317, - +29815, +15252, -16897, +1083, +956, +2943, -891, -5377, +12108, +2999, -12962, -4416, +24, +360, +3011, -3485, -2819, -229, -1736, +410, -242, -574, -195, -1310, -2449, -114, -453, -1000, -245, +121, -272, -1376, - +30704, +15809, -17499, +1266, -67, +2973, -1441, -5841, +12884, +3441, -12109, -5095, -636, +265, +3716, -2998, -3240, +129, -2153, -287, -24, -180, -492, -1366, -2616, -99, -271, -1307, -188, +298, -393, -1578, - +31618, +16165, -17687, +1138, -966, +3162, -2242, -6103, +13031, +3994, -10855, -5814, -1132, -114, +4440, -2576, -3472, +441, -2571, -925, +179, +66, -731, -1305, -2901, +118, -240, -1556, +32, +256, -573, -1665, - +32352, +16428, -17457, +790, -1672, +3290, -2962, -6399, +12571, +4836, -9536, -6362, -1498, -662, +5052, -2203, -3513, +550, -2849, -1412, +253, +166, -883, -1139, -3109, +166, -209, -1529, +21, +58, -646, -1719, - +32443, +17279, -16999, +233, -2175, +3032, -2969, -7266, +11609, +6215, -8405, -6559, -1896, -1197, +5401, -1763, -3376, +385, -2990, -1689, +180, +133, -974, -846, -3214, -83, +9, -1520, -177, -60, -770, -1566, - +31892, +18478, -16118, -275, -2655, +2351, -2206, -8781, +10331, +8020, -7513, -6312, -2325, -1753, +5424, -1170, -3047, -34, -3062, -1782, +143, -158, -877, -571, -3280, -291, -54, -1441, -346, -233, -674, -1187, - +30789, +19780, -14759, -548, -3198, +1460, -1025, -10799, +8894, +10055, -6732, -5809, -2497, -2429, +5133, -346, -2659, -573, -3168, -1582, +53, -566, -705, -356, -3272, -629, -135, -1412, -494, -112, -434, -955, - +29203, +20906, -12769, -646, -3914, +632, +128, -12834, +7097, +11981, -5813, -5212, -2227, -3391, +4539, +673, -2372, -1121, -3072, -1520, +50, -953, -749, -83, -3467, -767, -412, -1439, -216, +10, -266, -671, - +27369, +21887, -10304, -719, -4689, +134, +1044, -14260, +4701, +13487, -4266, -4865, -1191, -4653, +3796, +1775, -2279, -1199, -3034, -1527, +71, -1028, -1358, +221, -3301, -1300, -376, -1263, +67, +149, +70, -544, - +25668, +22491, -8130, -702, -4924, -592, +1193, -14383, +1825, +13879, -1753, -4990, +70, -5585, +2485, +3061, -2364, -1028, -2910, -1941, +216, -1133, -2116, +371, -3234, -1474, -105, -1358, +438, +510, +66, -745, - +24445, +22594, -6916, -408, -4078, -1908, +266, -13241, -891, +13193, +1079, -5162, +987, -5936, +931, +4120, -2150, -1025, -2529, -2729, -156, -699, -2958, -77, -2635, -1480, +97, -1388, +718, +673, -84, -802, - +24036, +21606, -6204, +627, -3020, -3027, -1363, -11604, -2945, +12413, +3083, -5038, +1938, -6484, +131, +4636, -1658, -795, -2484, -3507, -351, -682, -3613, +80, -2402, -1272, +354, -1476, +898, +717, -205, -856, - +23746, +20456, -5957, +1984, -1938, -4319, -2729, -9544, -5054, +11531, +5123, -5779, +3222, -6434, -1058, +5531, -1261, -866, -2359, -3656, -1163, -839, -3784, +272, -1846, -1591, +565, -1292, +952, +517, -310, -562, - +23960, +19174, -6448, +3094, -1039, -5368, -3542, -7116, -6317, +9363, +7378, -6843, +3456, -4600, -3042, +6646, -852, -1642, -1443, -3885, -1943, -945, -3873, +318, -958, -2243, +620, -863, +528, +698, -367, -469, - +23567, +18274, -6810, +4250, -385, -6084, -3975, -5591, -6825, +6911, +9455, -6771, +1918, -2142, -4373, +6456, +835, -2708, -1024, -3507, -2457, -992, -4100, +133, -369, -2407, +311, -628, +321, +831, -178, -597, - +23143, +16836, -6902, +5727, -531, -6291, -3459, -5161, -7193, +5338, +10494, -6386, +648, -1274, -4124, +6012, +1743, -2864, -1093, -2840, -2832, -626, -4269, -880, -83, -2116, +209, -691, +146, +839, -37, -502, - +22711, +15714, -7208, +6876, -1112, -6813, -1895, -4170, -8096, +3464, +11809, -6438, -833, +596, -4557, +5103, +2952, -2724, -1144, -2460, -2916, -73, -4033, -2509, -346, -1558, +340, -324, -568, +509, +520, -161, - +21883, +15259, -8430, +8064, -235, -8167, -638, -2887, -8285, +800, +12561, -5050, -2930, +2356, -4439, +2991, +4720, -2205, -1490, -1642, -3457, +613, -3421, -4138, -736, -1237, +241, +437, -924, -343, +1118, +203, - +21336, +14054, -8898, +8815, +778, -8299, -1119, -1073, -7651, -2227, +13016, -3460, -4273, +3243, -3776, +1237, +5044, -803, -1471, -1297, -3268, +457, -2964, -4426, -1137, -1555, +51, +1032, -730, -630, +878, +351, - +20364, +12658, -8776, +9749, +928, -8029, -658, -913, -7055, -3196, +11978, -2563, -4312, +3779, -3422, -39, +4857, -93, -856, -1070, -2919, +46, -2987, -3874, -1261, -1841, -597, +1113, -366, -282, +474, -132, - +19013, +11105, -7802, +10307, +543, -7199, -275, -1181, -6482, -3416, +10634, -2108, -4042, +4151, -2777, -1409, +4761, +291, -1107, -73, -2791, -368, -2750, -3871, -721, -1726, -1380, +656, -107, +32, +532, -418, - +17238, +9876, -6359, +10305, +398, -6315, -102, -1089, -5867, -3730, +9349, -1363, -3939, +4046, -1704, -2280, +4126, +804, -1450, +148, -1982, -710, -2585, -3457, -636, -1203, -1796, +107, -115, +106, +736, -435, - +16850, +7680, -7300, +12520, +2285, -8479, -1087, +427, -4800, -1828, +6635, -3369, -2358, +3423, -1655, -892, +2571, -327, -160, +642, -662, +1201, -1048, -1605, +229, -1235, -1428, -365, -1650, -1331, -434, -718, - +14445, +7248, -5479, +10890, +2224, -6676, -847, +20, -3992, -1849, +5544, -2552, -2301, +2914, -1338, -1107, +2267, -66, -274, +451, -432, +1011, -1109, -1445, +210, -1200, -1152, -150, -1229, -1064, -619, -638, - +12262, +7034, -3826, +9064, +2556, -4912, -1000, -85, -3259, -1912, +4712, -1765, -2260, +2378, -779, -1142, +1866, +240, -211, +297, -362, +843, -852, -1395, +26, -936, -987, +115, -841, -918, -540, -612, - +10302, +6772, -2608, +7425, +3047, -3580, -1144, +38, -2705, -1968, +3948, -1167, -2245, +1845, -376, -1032, +1522, +265, -101, +261, -453, +682, -692, -1289, -83, -615, -752, -59, -567, -670, -472, -618, - +8655, +6443, -1690, +6216, +3422, -2584, -975, +289, -2316, -1951, +3369, -755, -2184, +1494, -28, -828, +1147, +375, +103, +99, -381, +549, -584, -1004, +111, -444, -718, -65, -432, -490, -423, -480, - +7221, +6029, -1043, +5350, +3704, -1861, -720, +491, -2015, -1932, +2857, -505, -2070, +1231, +249, -749, +916, +588, -26, +2, -231, +492, -373, -773, +146, -331, -639, -153, -420, -446, -268, -366, - +6149, +5337, -522, +4890, +3705, -1298, -409, +653, -1896, -1811, +2394, -329, -1930, +1021, +457, -665, +862, +483, -128, +133, -8, +453, -352, -564, +224, -355, -685, -251, -418, -327, -123, -232, - +5347, +4865, -12, +4345, +3331, -784, +8, +681, -1713, -1654, +1950, -310, -1698, +1128, +488, -580, +778, +296, +168, +368, -71, +403, -167, -481, +0, -380, -642, -237, -238, -203, -161, -399, - +4513, +4458, +327, +3862, +3291, -212, +50, +538, -1286, -1648, +1413, -6, -1274, +809, +432, -246, +709, +464, +342, +239, -48, +380, -170, -546, -158, -340, -484, -110, -208, -423, -383, -436, - +3737, +4010, +796, +3404, +3194, +416, -10, +338, -902, -1429, +1024, +156, -1024, +609, +727, +264, +595, +355, +422, +205, -29, +258, -264, -566, -23, -135, -442, -273, -489, -465, -296, -300, - +3244, +3768, +1128, +2910, +3135, +692, -253, +385, -416, -1327, +523, +399, -524, +860, +1068, +205, +374, +460, +341, +111, -88, +108, -72, -266, -80, -420, -614, -364, -338, -321, -247, -281, - +2573, +3501, +1542, +2422, +3009, +997, -86, +399, -529, -1166, +615, +927, +3, +728, +933, +166, +369, +430, +133, +29, +88, +270, -37, -461, -485, -479, -438, -286, -266, -299, -341, -359, - +2231, +3444, +1513, +2040, +3101, +1062, -319, +337, -154, -486, +938, +903, +105, +773, +781, +97, +163, +295, +372, +236, +56, -55, -435, -564, -350, -311, -370, -314, -370, -380, -309, -324, - +2099, +3103, +1628, +2260, +2332, +675, +355, +998, +218, -292, +974, +821, +239, +834, +457, -122, +408, +649, +268, -133, -357, -207, -248, -398, -297, -330, -451, -420, -307, -346, -285, -262, - +2122, +2791, +1338, +2074, +2185, +1299, +1098, +1022, +96, -224, +1143, +1002, +34, +360, +626, +337, +351, +217, -313, -387, -92, +1, -292, -456, -393, -443, -464, -373, -291, -249, -256, -321, - +1707, +2474, +1550, +2358, +2684, +1380, +926, +1182, +372, +34, +972, +722, +127, +725, +656, -121, -188, -80, -126, -77, -58, -130, -419, -521, -423, -415, -422, -288, -194, -281, -292, -289, - +2009, +3271, +2261, +2063, +1937, +1369, +1101, +993, +457, +278, +709, +981, +479, -169, -163, -155, -17, +78, -53, -120, -289, -340, -416, -495, -424, -327, -303, -303, -181, -220, -289, -318, - +2008, +3310, +2283, +1883, +1850, +1562, +1336, +1209, +776, +362, +614, +893, +94, -536, -228, +27, +139, +104, -127, -295, -281, -260, -428, -460, -357, -327, -324, -261, -177, -196, -244, -322, - +1917, +2862, +1700, +1658, +1732, +1613, +1699, +1958, +1364, +660, +516, +154, +109, -111, -309, -59, +186, +33, -209, -177, -238, -291, -369, -419, -336, -324, -384, -275, -151, -205, -230, -271, - +2083, +2983, +1506, +1369, +1260, +1246, +1868, +2065, +1340, +757, +550, +543, +648, -21, -250, -120, -176, +2, -56, -272, -389, -249, -240, -367, -275, -212, -264, -298, -255, -207, -278, -316, - +2198, +2902, +1108, +1182, +1319, +951, +937, +1441, +1654, +1337, +1100, +824, +681, +876, +211, -700, -323, -124, -65, -79, -310, -308, -251, -253, -245, -200, -273, -191, -287, -316, -203, -334, - +2056, +2797, +1106, +1111, +1388, +1207, +805, +382, +604, +1281, +1542, +1132, +810, +896, +1118, +443, -493, -486, -254, -141, -40, -152, -378, -280, -153, -282, -256, -141, -244, -319, -313, -273, - +2227, +2815, +713, +806, +1269, +1147, +860, +283, +11, +688, +1122, +928, +1193, +1393, +1389, +1011, +515, +126, -365, -592, -318, -110, -213, -289, -293, -274, -243, -88, -185, -280, -240, -271, - +2487, +3077, +731, +1027, +1384, +982, +654, -128, +18, +996, +561, +73, +591, +932, +1479, +1270, +870, +925, +448, -201, -501, -563, -415, -115, -143, -320, -310, -120, -171, -247, -175, -287, - +2775, +3186, +631, +1392, +1798, +1197, +294, -934, -91, +1380, +530, -194, +56, +369, +981, +652, +864, +1272, +870, +557, +177, -277, -677, -549, -265, -205, -167, -195, -284, -238, -143, -268, - +3354, +3952, +622, +1159, +1616, +1212, +384, -1193, -171, +1340, +276, -317, +73, +202, +505, +358, +412, +812, +941, +759, +457, +303, +14, -361, -589, -473, -252, -41, -106, -338, -312, -210, - +3851, +4360, +508, +1396, +1995, +1083, -118, -1646, +204, +1680, -168, -602, -109, +183, +637, +36, +22, +528, +357, +402, +649, +527, +382, +246, -154, -411, -536, -224, -139, -252, -213, -373, - +4411, +4791, +385, +1660, +2324, +1023, -690, -2047, +660, +1766, -479, -607, -315, +44, +662, -70, -18, +284, +30, +175, +102, +352, +562, +305, +304, +207, -224, -238, -340, -387, -265, -275, - +5196, +5589, +252, +1519, +2294, +877, -1040, -2105, +1073, +1563, -889, -523, -328, -3, +615, -267, -54, +275, -150, -65, -69, +83, +148, +190, +385, +349, +230, +269, -184, -470, -345, -462, - +5718, +5975, +175, +2056, +2782, +433, -1979, -2126, +1813, +1397, -1262, -508, -449, +38, +582, -483, -40, +268, -347, -79, -259, -144, +146, -223, +11, +368, +235, +504, +304, -118, -274, -506, - +6315, +6714, +139, +2135, +3020, +109, -2674, -1982, +2425, +978, -1604, -368, -505, +111, +586, -693, -117, +249, -464, -85, -325, -245, +4, -438, -62, +108, +23, +446, +291, +123, +139, -256, - +6745, +7502, +262, +2112, +3265, -191, -3399, -1845, +3011, +575, -2029, -233, -551, +131, +609, -828, -184, +190, -609, -176, -413, -318, +53, -619, -305, +55, -89, +207, +130, -29, +147, +100, - +7135, +8455, +528, +1753, +3383, -342, -3912, -1715, +3302, +347, -2377, -280, -478, +73, +666, -864, -309, +173, -675, -305, -523, -417, -31, -619, -370, -108, -188, +168, -8, -183, -48, -243, - +7313, +9335, +990, +1537, +3442, -503, -4294, -1670, +3510, +231, -2617, -467, -433, +51, +676, -780, -520, +145, -610, -381, -642, -599, -117, -669, -413, -163, -232, +54, +96, -400, -473, -157, - +7366, +10104, +1617, +1432, +3413, -641, -4433, -1789, +3563, +295, -2857, -581, -471, -47, +733, -715, -694, +95, -548, -428, -666, -773, -215, -762, -547, -237, -183, +203, -336, -503, -328, -397, - +7627, +10884, +2189, +1075, +3085, -556, -4178, -2011, +3118, +500, -2883, -799, -373, -262, +585, -455, -843, -134, -419, -461, -781, -795, -264, -872, -798, -151, -218, -232, -138, -459, -588, -350, - +7805, +11222, +3062, +1025, +2511, +140, -3881, -2575, +2792, +742, -2671, -862, -572, -376, +526, -340, -707, -443, -595, -287, -600, -934, -448, -730, -617, -528, -615, +39, -258, -504, -448, -459, - +8363, +10906, +3309, +2010, +2235, +305, -3931, -2743, +2509, +616, -2212, -1029, -818, -300, +441, -402, -584, -720, -757, -88, -693, -1025, -498, -478, -772, -805, -578, -256, -196, -436, -534, -482, - +9029, +10207, +3227, +3604, +2126, +318, -3990, -2699, +2282, -66, -1506, -870, -1243, -147, +313, -594, -347, -843, -960, -75, -980, -749, -429, -936, -470, -770, -924, -178, -239, -505, -518, -435, - +9921, +9094, +2287, +6032, +2825, -665, -3976, -1942, +1634, -1240, -452, -499, -1858, -63, +175, -661, -141, -1007, -1221, -134, -966, -783, -874, -746, -300, -913, -822, -399, -257, -376, -363, -634, - +11207, +8923, +714, +7384, +3504, -1787, -3331, -478, +667, -3182, +480, +632, -2340, -174, -41, -734, +254, -1209, -1563, +237, -1495, -1119, -374, -1179, -176, -742, -854, -326, -296, -187, -407, -889, - +12500, +9128, -1225, +8120, +4706, -2957, -3096, +1429, +224, -5655, +633, +2334, -2476, -521, -246, -1021, +659, -1250, -1598, -102, -1942, -946, -536, -1243, -56, -809, -795, -9, -245, -516, -388, -591, - +13676, +9715, -3224, +8146, +6300, -3786, -3252, +3385, +497, -7984, -273, +4084, -1795, -1036, -439, -1428, +900, -738, -2169, -270, -1884, -1560, -244, -1157, -225, -702, -726, +327, -337, -781, -147, -433, - +15154, +10710, -5567, +6975, +8141, -3612, -4028, +4575, +1224, -9451, -1863, +5048, -504, -1304, -849, -1887, +1317, -802, -2679, +65, -2243, -1967, -192, -1025, -170, -751, -680, +394, -331, -789, -111, -452, - +16751, +11590, -7670, +5697, +9423, -3002, -4671, +5673, +1938, -10472, -3508, +5316, +1125, -1098, -1562, -2113, +1531, -1067, -2689, -22, -2225, -2260, -408, -719, -88, -730, -829, +416, -248, -657, -207, -430, - +19048, +11932, -9850, +4534, +10090, -1984, -5054, +6461, +2293, -11027, -4690, +4999, +2637, -594, -2197, -2457, +1788, -1314, -2837, +89, -2336, -2296, -497, -623, +34, -680, -1066, +461, -55, -678, -251, -446, - +21615, +12285, -12393, +3218, +10447, -513, -5263, +6448, +2499, -11072, -5490, +4213, +3638, +192, -2622, -3085, +2128, -1593, -3108, +258, -2535, -2078, -721, -584, +167, -802, -1230, +548, +77, -824, -300, -503, - +23814, +13115, -14900, +2298, +10402, +898, -5101, +6133, +3297, -10978, -6160, +3330, +4213, +1402, -2845, -3633, +2406, -1782, -3197, +506, -2620, -1975, -705, -585, +213, -896, -1169, +725, -33, -930, -208, -501, - +26386, +12827, -16969, +2278, +9703, +1341, -4735, +5967, +4345, -11169, -7039, +2751, +4328, +2440, -3053, -3808, +2333, -2300, -2829, +717, -2947, -2030, -466, -759, +120, -952, -975, +828, -420, -898, -159, -497, - +27944, +13492, -18792, +2616, +8679, +1183, -4343, +5611, +6445, -11341, -8610, +2448, +4449, +2910, -2638, -3722, +1665, -2447, -2294, +737, -3191, -2075, -286, -1008, +16, -802, -857, +864, -755, -842, -70, -604, - +29685, +13246, -19821, +3525, +7289, +438, -4363, +5714, +8745, -11618, -10243, +2318, +4494, +2830, -1737, -3623, +798, -2252, -1923, +676, -3494, -1846, -281, -1396, -10, -574, -760, +810, -964, -876, +89, -884, - +30876, +13227, -20231, +4464, +5889, -395, -4997, +6035, +11190, -11731, -11571, +2002, +4359, +2564, -389, -3809, +115, -1644, -2030, +671, -3672, -1473, -429, -1750, -81, -276, -628, +609, -946, -950, +127, -1298, - +31278, +13828, -20217, +5005, +4867, -1003, -6198, +6112, +13378, -11330, -12204, +1134, +4093, +2243, +984, -3911, -507, -785, -2404, +550, -3517, -1127, -734, -1985, -167, -11, -527, +311, -626, -1201, -142, -1426, - +31763, +14606, -20213, +4922, +4300, -1334, -7821, +5934, +15019, -10488, -12296, -244, +3894, +2145, +1899, -3668, -996, -217, -2645, +278, -3186, -960, -1050, -2098, -246, +125, -509, +271, -479, -1663, -297, -1411, - +32059, +15063, -19624, +4534, +3996, -1550, -9300, +5401, +15706, -8935, -11963, -1825, +3576, +2337, +2148, -3207, -1061, -200, -2557, +23, -2815, -964, -1297, -2112, -372, +106, -371, +213, -866, -1642, -555, -1453, - +31594, +16148, -18709, +3873, +3953, -1965, -10020, +3854, +16002, -6585, -11621, -3362, +3109, +2803, +1915, -2572, -712, -591, -2348, +118, -2607, -1103, -1413, -2224, -397, +185, -559, -36, -1026, -1584, -771, -1168, - +30561, +17638, -17451, +3091, +3826, -2310, -9959, +1289, +16030, -3803, -11272, -4403, +2328, +3336, +1431, -1843, -11, -1291, -1874, +345, -2491, -1351, -1564, -2198, -272, -81, -1106, +216, -1299, -1439, -527, -1224, - +29054, +19377, -15810, +1898, +3337, -2040, -9228, -2236, +15567, -882, -10760, -4753, +1128, +3442, +1193, -1335, +949, -1929, -1501, +721, -2539, -1679, -1649, -1976, -762, -420, -1351, +363, -1271, -1184, -487, -1395, - +27602, +19984, -13330, +1384, +2220, -1553, -8967, -5440, +14956, +1794, -10257, -4554, -131, +2570, +2016, -1408, +1844, -1852, -1851, +1473, -2793, -1890, -1630, -2378, -1270, -515, -1540, +663, -952, -1427, -261, -1350, - +25630, +20290, -10249, +1324, +1107, -1114, -8918, -8485, +14115, +4665, -9857, -4277, -756, +606, +3451, -1301, +1865, -565, -2709, +1877, -2367, -2459, -1746, -2677, -1920, -340, -1413, +665, -508, -1675, -66, -1466, - +23903, +20683, -7286, +510, -377, -53, -7840, -11202, +11907, +7374, -9146, -4087, -105, -1982, +4052, -56, +1078, +860, -2851, +1241, -1622, -2794, -2100, -2674, -2293, -321, -1039, +386, -96, -1780, -277, -1191, - +22823, +20723, -6390, +1262, +38, -1708, -6903, -11540, +8654, +9407, -7731, -4726, +1287, -3835, +3356, +1929, +303, +1232, -2157, +96, -909, -2590, -2708, -1999, -2763, -486, -647, +74, -134, -1612, -437, -1074, - +23286, +19705, -6984, +2708, +768, -3874, -6807, -9892, +5577, +9767, -5818, -5461, +2601, -4494, +2384, +2776, +168, +931, -1602, -159, -1262, -1698, -2885, -1466, -2698, -1301, -450, -206, -5, -1620, -620, -969, - +23121, +19255, -7571, +3875, +1563, -5614, -6214, -8564, +2641, +9601, -3111, -6567, +3743, -4320, +945, +3692, -1043, +1590, -1282, -840, -479, -978, -2698, -1087, -2537, -2252, -520, -532, +376, -1513, -1259, -735, - +23895, +17231, -7926, +5603, +1022, -6360, -5559, -7729, +248, +9659, -1110, -7381, +4032, -2981, -753, +3497, -675, +212, -226, -663, -11, -138, -2841, -1009, -2052, -2616, -1508, -419, +202, -1307, -1387, -864, - +24342, +16471, -9991, +7124, +2357, -8386, -5329, -5315, -1440, +7468, +1850, -7622, +3025, -1191, -1868, +2925, -544, -837, +462, +724, -295, +355, -2494, -1379, -1397, -2680, -2066, -975, -223, -1077, -799, -950, - +24670, +15431, -12280, +9290, +3619, -10800, -4710, -2736, -2869, +4919, +4375, -7414, +1744, +298, -2787, +2517, -329, -1185, +786, +1117, +234, +534, -2350, -1334, -1274, -2503, -2002, -1594, -1255, -755, -99, -851, - +25053, +13472, -13783, +11903, +3737, -12350, -3755, -973, -3561, +2410, +5792, -6487, +903, +639, -3500, +2947, +120, -1823, +1427, +635, +328, +1654, -2795, -1081, -1079, -2818, -1321, -1807, -2368, -781, +236, -570, - +25116, +10992, -14060, +14345, +2919, -12891, -2578, +298, -4831, +849, +7344, -6355, -113, +1687, -3785, +2243, +1525, -1852, +673, +891, +28, +1939, -1939, -1636, -642, -2710, -1531, -1027, -2946, -1438, +260, -246, - +23985, +9052, -12782, +15373, +1674, -11878, -1468, -66, -5434, +491, +7676, -6469, -622, +2840, -3942, +1270, +2660, -1561, +29, +815, -200, +1925, -1493, -1644, -301, -2510, -1767, -674, -2584, -1874, -316, -165, - +21931, +7984, -10773, +14942, +1323, -10330, -1496, -41, -5153, -560, +7563, -5510, -1367, +3202, -3067, +472, +2598, -1083, +280, +328, -550, +2171, -1750, -1359, +265, -2505, -1617, -565, -2257, -1696, -689, -545, - +19430, +7714, -9051, +13883, +1898, -9550, -1479, +550, -5119, -1546, +7412, -4342, -2191, +3447, -2123, -382, +2504, -635, +149, +475, -754, +1639, -1244, -1582, +373, -1789, -1679, -351, -2065, -1520, -554, -754, - +15748, +5880, -5425, +14058, +1054, -9150, +6, +395, -3696, +26, +2379, -3599, +1707, +2155, -2493, -381, +1177, -628, -908, +221, -185, +13, -490, +744, +822, +168, +287, -448, -1045, -444, -892, -1472, - +13423, +5793, -3949, +12100, +1504, -7414, -200, +63, -3202, +36, +2192, -2942, +1203, +1913, -1964, -650, +802, -427, -791, -12, -102, +329, -386, +362, +832, +203, -6, -415, -837, -549, -728, -965, - +11547, +5785, -2873, +10302, +2118, -6063, -463, +55, -2706, -44, +2080, -2250, +641, +1766, -1389, -907, +655, -241, -693, -30, +34, +406, -343, +364, +574, +40, +171, -246, -795, -541, -534, -664, - +9992, +5645, -2205, +8952, +2516, -5038, -463, +97, -2247, -98, +1995, -1860, +247, +1695, -1144, -964, +569, -79, -605, +22, +220, +235, -242, +317, +304, +54, +320, -69, -720, -432, -411, -602, - +8782, +5267, -1688, +7925, +2586, -4032, -444, +186, -1797, -191, +1866, -1605, +27, +1498, -942, -933, +555, +32, -524, +199, +141, +118, -129, +149, +324, +142, +325, +78, -560, -385, -344, -545, - +7729, +4882, -1177, +6873, +2650, -3039, -407, +340, -1509, -165, +1693, -1465, -61, +1352, -781, -864, +627, +111, -305, +152, +24, +234, -129, +182, +391, +215, +338, +94, -398, -277, -266, -463, - +6682, +4405, -731, +6123, +2845, -2298, -387, +459, -1188, -176, +1393, -1251, -73, +1145, -660, -560, +620, +101, -161, +95, +165, +308, -117, +224, +453, +233, +340, +124, -276, -83, -284, -652, - +5690, +4114, -370, +5337, +2959, -1597, -302, +540, -969, -272, +1211, -1020, -123, +962, -388, -419, +496, +293, -68, +198, +277, +243, -1, +258, +446, +244, +316, +224, -197, -287, -538, -753, - +4886, +3783, -77, +4739, +3009, -1004, -171, +567, -808, -296, +1071, -732, -150, +762, -143, -243, +583, +495, +70, +295, +285, +282, +109, +300, +419, +303, +318, -41, -410, -429, -543, -696, - +4226, +3500, +214, +4230, +2888, -619, +78, +571, -807, -209, +1096, -674, -362, +872, +202, -45, +765, +519, +142, +420, +299, +326, +177, +298, +271, +19, +43, -216, -432, -470, -647, -770, - +3932, +3487, +332, +3583, +2646, -337, +285, +627, -784, -249, +992, -484, -225, +1185, +515, -8, +833, +683, +354, +372, +256, +357, +26, -114, -52, -13, -48, -275, -631, -669, -560, -678, - +3417, +3033, +572, +3583, +2497, -132, +512, +448, -821, -41, +1094, -215, +63, +1208, +613, +421, +1026, +661, +269, +268, +157, -77, -316, -230, -50, -15, -270, -450, -631, -506, -489, -682, - +3181, +2995, +715, +3281, +2345, -67, +347, +573, -324, +109, +1136, +26, +360, +1541, +983, +492, +841, +521, -21, -138, -249, -144, -314, -277, -62, -239, -335, -379, -509, -456, -497, -624, - +2727, +2772, +1072, +2985, +1995, +392, +618, +698, +14, +104, +1230, +616, +773, +1489, +881, +385, +451, -79, -396, -153, -251, -293, -476, -335, -58, -202, -314, -426, -485, -422, -544, -580, - +2494, +2557, +1043, +2809, +2364, +870, +612, +794, +496, +543, +1308, +960, +905, +1098, +463, -159, -22, -97, -245, -365, -543, -373, -259, -109, -161, -351, -340, -303, -434, -540, -495, -424, - +2244, +2930, +1654, +2496, +2296, +1063, +990, +1335, +682, +721, +1328, +513, +346, +642, +45, -321, -153, -280, -469, -425, -401, -290, -203, -148, -250, -401, -344, -391, -436, -433, -426, -380, - +2037, +3030, +1906, +2584, +3062, +1893, +990, +1134, +771, +385, +555, +134, +140, +441, -13, -448, -353, -397, -487, -345, -327, -238, -229, -306, -339, -347, -307, -358, -440, -470, -365, -304, - +2260, +3620, +2531, +2997, +2841, +1693, +1371, +1050, +54, -313, +437, +310, -186, +172, -15, -693, -347, -145, -477, -380, -271, -292, -232, -289, -368, -327, -359, -335, -337, -420, -362, -300, - +2676, +4646, +3139, +2421, +2169, +1461, +1102, +750, +196, +85, +103, -264, -85, +79, -270, -444, -278, -319, -366, -267, -356, -287, -310, -325, -320, -352, -353, -348, -336, -368, -283, -304, - +2949, +4690, +2841, +1992, +1689, +1410, +1437, +860, +277, +660, +273, -352, -345, -179, +121, -193, -595, -313, -69, -302, -301, -297, -388, -283, -359, -329, -293, -304, -317, -316, -248, -264, - +2663, +4197, +2446, +1659, +1599, +1195, +1150, +1390, +834, +702, +922, +74, -262, -12, -178, -353, -146, -141, -372, -189, -146, -406, -452, -309, -320, -299, -251, -248, -234, -286, -278, -279, - +2398, +3447, +2003, +1730, +1358, +984, +1031, +668, +683, +1466, +1326, +833, +547, +62, +124, -145, -512, -263, -44, -260, -496, -309, -241, -380, -321, -210, -197, -195, -288, -327, -348, -283, - +1987, +2518, +1256, +1700, +1770, +1374, +486, -161, +666, +1239, +1117, +1441, +1412, +1138, +952, +102, -154, -108, -471, -390, -130, -296, -423, -224, -149, -219, -273, -144, -187, -306, -316, -312, - +2277, +2839, +857, +1089, +1531, +1252, +447, -170, +485, +946, +625, +786, +1274, +1596, +1572, +803, +425, +213, -154, -308, -514, -316, -104, -287, -376, -112, -81, -229, -298, -346, -243, -269, - +2475, +3091, +1057, +1202, +1523, +1181, +155, -419, +779, +1083, +35, +243, +607, +800, +1330, +1039, +902, +962, +366, -99, -203, -378, -433, -305, -177, -196, -314, -134, -130, -427, -342, -186, - +2867, +3436, +954, +1406, +1896, +1116, -228, -668, +1005, +1052, -200, +160, +322, +441, +627, +249, +723, +1109, +691, +570, +250, -239, -146, -317, -437, -152, -136, -255, -367, -288, -235, -306, - +3223, +4037, +1059, +1327, +1971, +1123, -496, -724, +1262, +822, -504, +196, +267, +346, +542, -168, +79, +604, +516, +608, +511, +379, +188, -138, -157, -106, -291, -239, -240, -438, -390, -237, - +3604, +4513, +1093, +1460, +2232, +986, -1011, -692, +1601, +582, -689, +154, +137, +264, +549, -231, -112, +308, -5, +126, +288, +386, +454, +334, +190, +65, -9, -169, -440, -448, -309, -360, - +4214, +5204, +1001, +1333, +2363, +811, -1317, -468, +1657, +218, -774, +195, +54, +193, +477, -310, -143, +220, -205, -91, -167, -47, +273, +245, +485, +588, +199, -41, -178, -454, -469, -319, - +4699, +5772, +1037, +1391, +2580, +571, -1811, -137, +1930, -248, -876, +278, -102, +158, +529, -367, -232, +173, -250, -176, -332, -250, -145, -125, +369, +578, +501, +334, -24, -230, -185, -365, - +5288, +6343, +926, +1516, +2837, +116, -2274, +403, +2022, -767, -921, +321, -140, +29, +469, -385, -290, +151, -299, -257, -451, -381, -272, -339, +52, +314, +302, +289, +305, +49, -70, -194, - +5726, +6820, +1096, +1667, +3026, -179, -2850, +907, +2339, -1355, -1017, +450, -163, -66, +415, -378, -412, +166, -245, -345, -541, -415, -353, -452, -62, +81, +100, +97, +117, +54, +153, +24, - +6153, +7423, +1241, +1705, +3192, -342, -3363, +1287, +2593, -1875, -1019, +557, -240, -122, +319, -290, -434, +2, -201, -386, -582, -375, -458, -561, -160, +6, +81, -90, -141, -171, +10, +136, - +6828, +8181, +890, +1848, +3361, -805, -3382, +1527, +2488, -2165, -973, +617, -316, -246, +308, -188, -549, -125, -176, -412, -641, -381, -560, -633, -100, -94, -15, -180, -313, -244, -131, -157, - +7339, +8745, +841, +2086, +3460, -1152, -3508, +1614, +2597, -2420, -1103, +713, -354, -350, +308, -133, -681, -241, -127, -375, -784, -526, -527, -641, -141, -117, -177, -223, -325, -365, -249, -300, - +7574, +9152, +1067, +2490, +3582, -1504, -3603, +1572, +2713, -2475, -1389, +742, -241, -424, +146, -29, -689, -412, -49, -482, -922, -508, -528, -656, -326, -236, +0, -242, -494, -465, -250, -363, - +7771, +9477, +1527, +2680, +3445, -1316, -3581, +1119, +2776, -2355, -1714, +883, -172, -648, +99, -15, -445, -536, -388, -395, -960, -596, -496, -760, -479, -221, +19, -221, -514, -524, -422, -579, - +8021, +9527, +2130, +2911, +3042, -704, -3474, +429, +2652, -2057, -1809, +694, -37, -769, -100, +184, -247, -761, -609, -325, -1000, -680, -746, -736, -332, -408, -31, -128, -468, -729, -608, -586, - +8404, +9096, +2503, +3785, +2807, -578, -3480, +263, +2211, -1959, -1522, +174, -65, -508, -392, +128, +9, -898, -776, -193, -1245, -945, -832, -687, -337, -524, -93, -26, -595, -926, -547, -887, - +8807, +8295, +2417, +5558, +3298, -1393, -3611, +1274, +1418, -2498, -541, -286, -448, +137, -733, -107, +472, -919, -800, -132, -1516, -1020, -876, -819, -258, -573, -75, -222, -470, -870, -816, -827, - +9721, +7750, +1320, +7320, +3992, -2723, -3178, +2422, -20, -3129, +726, -465, -1243, +477, -680, -418, +820, -960, -919, -87, -1553, -1159, -1230, -839, -90, -743, -335, -154, -533, -893, -664, -1012, - +10870, +7707, -280, +8285, +5199, -3799, -2870, +3710, -1420, -4018, +2054, -220, -2214, +514, -188, -855, +1016, -762, -1123, +24, -1500, -1380, -1443, -763, -169, -955, -493, -100, -557, -806, -616, -1159, - +11833, +8327, -2134, +8341, +6953, -4401, -2931, +5077, -2393, -5396, +3185, +626, -3054, -50, +496, -988, +898, -338, -1369, +122, -1461, -1366, -1542, -1049, -218, -1029, -640, -181, -361, -866, -501, -1048, - +12829, +9031, -3811, +7670, +8588, -4509, -3175, +6408, -3118, -7036, +3841, +1817, -3527, -1043, +839, -699, +618, +2, -1465, -51, -1246, -1421, -1712, -1253, -467, -1107, -577, -354, -342, -807, -350, -891, - +14193, +9389, -5197, +6872, +9549, -4273, -3139, +7727, -3898, -8441, +3999, +3031, -3444, -2122, +727, -197, +556, +113, -1327, -318, -1040, -1564, -1813, -1380, -747, -1085, -601, -339, -469, -645, -199, -888, - +15661, +9657, -6466, +6233, +9751, -3871, -2723, +8959, -4623, -9557, +3687, +4006, -2825, -3063, +218, +188, +872, +40, -1085, -476, -1213, -1459, -1966, -1538, -758, -1196, -697, -106, -518, -710, -84, -846, - +17612, +9338, -7642, +6082, +9245, -3958, -1634, +10145, -5734, -10179, +3233, +4461, -2053, -3596, -499, +236, +1471, -53, -824, -783, -1511, -1205, -2322, -1376, -936, -1374, -531, +50, -558, -860, -125, -756, - +19463, +9219, -8893, +6261, +8413, -4281, -363, +11374, -6530, -10787, +2801, +4644, -1342, -3682, -1194, -39, +2157, +139, -767, -1072, -1682, -1101, -2364, -1296, -1186, -1329, -395, +235, -591, -1146, -127, -554, - +21650, +9391, -10922, +6081, +8229, -4220, +202, +11812, -6628, -11097, +2478, +4559, -1182, -3433, -1549, -554, +2784, +461, -1205, -1027, -1704, -1258, -2251, -1431, -1146, -1205, -447, +400, -669, -1391, -60, -370, - +23618, +9863, -13021, +6004, +8047, -4224, +416, +12157, -6101, -11654, +2261, +4560, -1406, -3057, -1488, -1343, +3208, +953, -1820, -705, -1697, -1485, -2065, -1531, -1035, -1118, -452, +413, -760, -1496, +104, -350, - +25507, +10091, -14633, +6037, +7728, -4437, +358, +12536, -5172, -12313, +2012, +4630, -1785, -2622, -1212, -2223, +3337, +1463, -2227, -360, -1728, -1658, -1788, -1625, -1024, -963, -484, +363, -873, -1530, +387, -597, - +26627, +10861, -15853, +5976, +7293, -4687, -371, +13070, -3740, -13126, +1550, +4641, -1970, -2334, -931, -3002, +3015, +1972, -2297, -262, -1622, -1706, -1631, -1718, -1043, -847, -523, +200, -913, -1491, +419, -863, - +27529, +11441, -16338, +5805, +6853, -5062, -1537, +13789, -2342, -13609, +1025, +4352, -1778, -2113, -836, -3461, +2445, +2244, -1905, -447, -1343, -1486, -1806, -1634, -1063, -761, -595, -38, -809, -1509, +121, -852, - +28224, +11700, -16026, +5571, +6480, -5603, -2945, +14554, -1154, -13550, +445, +3703, -1208, -1832, -1081, -3648, +1918, +2132, -1094, -755, -1071, -949, -2221, -1385, -1073, -728, -775, -144, -846, -1640, -17, -888, - +28214, +12317, -15132, +5336, +6093, -6385, -4398, +15063, +169, -13141, -349, +2865, -289, -1493, -1675, -3631, +1627, +1567, -67, -871, -1083, -137, -2567, -1204, -1005, -901, -818, -487, -1098, -1321, -264, -1047, - +27674, +13198, -13952, +5049, +5725, -7019, -5818, +14656, +1734, -12326, -1241, +1915, +642, -1155, -2395, -3373, +1425, +664, +785, -409, -1488, +642, -2474, -1372, -798, -1009, -1376, -899, -968, -1199, -318, -1143, - +26652, +14012, -12267, +5200, +5059, -7687, -7096, +13622, +3659, -11441, -2232, +1136, +1363, -745, -2920, -3117, +1275, -215, +1173, +578, -1926, +1018, -1813, -1727, -552, -1508, -1936, -882, -1218, -880, -117, -1274, - +25476, +14976, -10645, +5041, +4465, -7268, -8563, +11373, +5951, -10470, -2967, +695, +1268, -121, -2916, -3040, +1232, -1049, +1136, +1691, -1847, +783, -939, -1782, -1189, -1393, -2259, -1210, -1263, -527, +246, -1345, - +24418, +15887, -9483, +5106, +3650, -6572, -9308, +8066, +7947, -9122, -3698, +546, +799, +234, -2298, -3040, +979, -1401, +660, +2460, -1414, +581, -594, -1924, -1571, -1185, -2342, -1526, -1293, -276, +512, -1388, - +23795, +16122, -8471, +5677, +2738, -6335, -9272, +4879, +8930, -7283, -4533, +587, +584, -12, -1140, -3045, +596, -1168, -297, +2828, -664, +171, -697, -1636, -1755, -862, -2122, -1839, -1085, -590, +469, -1065, - +23181, +16390, -7741, +6442, +2203, -6740, -8434, +1898, +8933, -4752, -5617, +579, +1155, -849, +75, -2508, -62, -548, -1446, +2589, +243, -427, -620, -1078, -1599, -627, -1538, -1798, -1522, -1117, +586, -608, - +22030, +16813, -6552, +6365, +1905, -6491, -7923, -1054, +8742, -2377, -6611, +561, +1892, -1729, +1053, -1623, -1078, +90, -2143, +1121, +1073, -290, -801, -281, -1302, -213, -1096, -1925, -1798, -1686, +156, -224, - +21251, +16564, -5227, +6101, +1298, -5618, -7958, -3289, +8275, -752, -6864, +468, +2279, -1970, +1851, -1720, -1502, +720, -2997, -87, +1066, +346, -609, +374, -840, -207, -612, -1924, -1832, -2459, -669, -2, - +21059, +15707, -4300, +6487, +399, -4731, -7511, -5182, +7533, +993, -7053, +282, +3472, -2462, +1927, -1518, -1924, +1110, -3285, -499, +296, +615, +989, +279, -802, +498, -387, -1895, -1824, -2715, -1623, -344, - +21308, +14470, -4242, +7387, -252, -4583, -6956, -6093, +6238, +2376, -6555, -258, +4580, -2860, +1045, -858, -2358, +1013, -2617, -1140, +487, +118, +1239, +1477, -1144, +905, +15, -2196, -1670, -2549, -2403, -914, - +21749, +13551, -6010, +9423, +237, -6449, -5831, -5696, +4659, +3069, -5379, -988, +5203, -3101, -229, +148, -2474, +413, -1565, -777, +245, -263, +270, +2911, -521, +46, +1169, -2577, -2021, -1583, -2762, -1790, - +22949, +11670, -8631, +12918, +409, -9740, -4092, -3756, +2385, +3036, -3324, -2593, +5421, -2472, -2044, +991, -1723, -438, -884, +258, -444, -546, -80, +2348, +873, +112, +640, -1839, -2303, -1429, -2266, -2374, - +23912, +9195, -9740, +15651, -753, -10898, -2348, -2548, +736, +2645, -1865, -3420, +5193, -2092, -2914, +1332, -608, -827, -805, +690, -265, -735, -758, +2026, +1239, +685, +402, -1265, -2108, -2026, -1548, -2057, - +24116, +7253, -10354, +17453, -1244, -12053, -838, -452, -1755, +1608, +585, -4398, +4021, -759, -3062, +841, +208, -518, -792, +316, -185, -122, -1534, +1707, +1529, +201, +1161, -889, -2381, -1596, -1536, -1868, - +22899, +6009, -9723, +18156, -1387, -12297, +387, +197, -3319, +1403, +1627, -5089, +3452, +505, -3305, +713, +563, -709, -310, -26, -425, +286, -1418, +1157, +1256, +363, +1114, -493, -2226, -1184, -1308, -2261, - +20564, +5546, -8226, +17550, -828, -11490, +294, +435, -3523, +690, +1926, -4747, +2908, +1152, -2973, +467, +625, -707, -390, +101, -449, +40, -906, +959, +763, +546, +863, -551, -1476, -956, -1217, -2071, - +18013, +5812, -6919, +15896, +403, -10591, +67, +717, -3782, +91, +2336, -4162, +2130, +1919, -2779, -103, +1086, -691, -833, +297, -250, -202, -650, +1014, +731, +166, +703, -484, -1293, -477, -1156, -1837, - +15954, +7279, -4696, +11257, -415, -7760, +15, -732, -924, +300, -779, -1111, +2316, +31, -1667, -359, +362, -476, -1406, +77, +201, +160, +31, +184, +33, +618, +402, -142, -19, -131, -351, -177, - +13494, +6705, -2870, +10505, -334, -6612, +28, -1001, -670, +405, -783, -1035, +2308, +178, -1639, -390, +219, -411, -1192, +78, +48, +72, +135, +215, +200, +619, +296, -179, +63, -49, -474, -174, - +11237, +6344, -1097, +9098, -68, -4982, -506, -1321, -184, +563, -966, -831, +2204, +226, -1398, -487, +210, -400, -1067, +109, +7, -13, +137, +290, +364, +506, +81, -34, +85, -87, -211, -95, - +9435, +6226, -171, +7621, +609, -3889, -842, -1290, +79, +593, -877, -638, +1778, +372, -1115, -612, +133, -304, -919, +84, +28, -9, +49, +403, +518, +117, +63, +315, +9, -189, +148, +127, - +8281, +6134, -52, +6557, +1253, -3449, -792, -804, +3, +410, -398, -491, +1170, +585, -801, -816, +97, -92, -760, -8, +125, +10, +14, +591, +350, +20, +334, +355, -11, +73, +387, +50, - +7625, +5926, -212, +5732, +1440, -2973, -409, -538, -153, +402, +53, -528, +814, +714, -773, -751, +187, -25, -685, +71, +194, -126, +301, +656, +141, +177, +495, +460, +123, +277, +255, -206, - +6929, +5539, -213, +5156, +1489, -2470, -128, -421, -216, +512, +263, -626, +604, +666, -598, -609, +135, +2, -569, +170, +101, +124, +603, +290, +211, +558, +624, +396, +73, +189, -68, -300, - +6185, +4980, +12, +4844, +1476, -2007, +5, -218, -48, +475, +186, -579, +652, +583, -532, -454, +163, -44, -351, +323, +185, +454, +519, +323, +554, +596, +513, +149, -59, +27, -156, -301, - +5578, +4710, +139, +4160, +1511, -1429, +247, -167, -134, +535, +301, -478, +484, +512, -357, -355, +143, +139, +9, +323, +460, +774, +454, +456, +460, +265, +311, +29, -141, -56, -157, -425, - +4834, +4247, +381, +3890, +1667, -1077, +239, -52, +64, +515, +186, -374, +470, +446, -204, -178, +260, +402, +264, +582, +786, +717, +277, +144, +133, +174, +219, +36, -212, -220, -241, -491, - +4111, +3866, +739, +3569, +1573, -633, +462, -99, +98, +570, +132, -353, +509, +570, +1, -45, +535, +857, +514, +702, +653, +127, -67, +94, +33, +142, +132, -176, -297, -218, -361, -612, - +3730, +3751, +749, +2995, +1740, -73, +398, -128, +157, +562, +322, -106, +526, +653, +398, +535, +911, +896, +331, +268, +196, +17, -50, +13, -44, +14, +4, -239, -323, -300, -491, -605, - +2892, +3209, +1362, +3059, +1608, +182, +476, -76, +432, +661, +242, +160, +1034, +1084, +626, +638, +748, +381, -9, +164, +81, +56, -148, -220, -132, -95, -61, -252, -388, -412, -527, -492, - +2752, +3422, +1354, +2301, +1710, +560, +500, +0, +522, +1109, +816, +680, +1165, +1033, +440, -19, +152, +356, +205, +72, -219, -249, -230, -203, -210, -180, -256, -370, -297, -417, -460, -470, - +2213, +3015, +1710, +2495, +1858, +590, +641, +619, +1215, +1482, +1045, +756, +541, +266, +10, +87, +388, +325, -149, -273, -65, -313, -465, -315, -243, -222, -297, -239, -314, -374, -410, -472, - +2378, +3632, +1949, +1796, +1538, +1419, +1931, +1407, +1042, +906, +324, -55, +57, +281, +416, +73, -141, +104, -125, -338, -346, -567, -456, -178, -236, -293, -193, -168, -347, -459, -409, -412, - +2533, +3615, +2312, +2871, +2388, +1873, +1924, +743, -6, +118, +67, +58, +149, +131, +262, -115, -211, -1, -357, -465, -469, -597, -378, -114, -162, -284, -237, -201, -345, -426, -423, -455, - +2942, +4615, +3780, +3595, +1951, +1001, +577, -3, -9, +178, +100, -150, -79, +35, +232, -191, -343, -241, -527, -515, -469, -366, -285, -260, -253, -164, -168, -307, -405, -445, -401, -430, - +3532, +6354, +4764, +2112, +192, +502, +1060, +740, -280, -481, -6, +64, -3, -232, +35, -123, -425, -424, -500, -331, -403, -411, -380, -233, -195, -219, -156, -328, -390, -471, -421, -332, - +3754, +6378, +3302, +1176, +770, +1524, +1808, +405, -520, -9, +89, -401, +38, -134, -211, -179, -439, -279, -348, -439, -354, -411, -499, -252, -186, -273, -220, -306, -347, -364, -422, -391, - +2291, +3472, +1957, +1996, +2277, +2297, +2104, +987, +507, +706, +356, -152, -298, -91, -5, -385, -390, -196, -226, -195, -416, -447, -287, -389, -410, -244, -292, -350, -178, -230, -400, -417, - +1819, +2518, +1138, +1323, +2016, +2067, +1584, +1227, +1540, +1527, +1077, +599, +319, +222, -133, -344, -224, -354, -392, -98, -287, -313, -172, -348, -336, -273, -365, -452, -322, -244, -281, -244, - +1991, +2736, +1265, +1355, +1696, +1270, +711, +599, +1244, +1618, +1273, +1004, +935, +768, +344, -110, -219, -251, -264, -319, -400, -274, -131, -258, -317, -231, -396, -403, -299, -400, -335, -208, - +2251, +2968, +994, +1270, +1801, +1182, +278, +85, +960, +1169, +719, +996, +1351, +1194, +875, +354, +176, -35, -282, -178, -385, -406, -153, -263, -175, -84, -340, -346, -297, -435, -353, -295, - +2479, +3474, +1165, +1200, +1738, +845, +57, +328, +1004, +611, +199, +546, +759, +1002, +1149, +732, +519, +336, +110, +42, -306, -317, -221, -391, -190, -72, -254, -255, -285, -409, -356, -289, - +2648, +3711, +1310, +1382, +2036, +687, -508, +326, +1209, +455, +69, +381, +353, +462, +570, +467, +723, +755, +406, +258, +135, -71, -226, -267, -244, -225, -239, -248, -226, -295, -370, -333, - +2964, +4131, +1490, +1331, +2035, +615, -807, +460, +1251, +53, -30, +484, +328, +176, +292, +67, +31, +459, +630, +520, +346, +231, +64, -123, -179, -163, -253, -368, -325, -244, -286, -364, - +3240, +4589, +1670, +1277, +2158, +577, -1140, +581, +1323, -227, -122, +553, +290, +100, +235, -70, -152, +21, +69, +287, +434, +552, +333, +96, +114, -5, -177, -258, -330, -382, -258, -309, - +3730, +5196, +1638, +1123, +2168, +499, -1274, +755, +1209, -628, -139, +668, +292, -40, +122, -92, -234, -100, -101, -83, -135, +285, +479, +374, +279, +151, +85, -119, -293, -348, -282, -381, - +4119, +5684, +1732, +1126, +2228, +300, -1467, +1077, +1204, -1061, -263, +742, +305, -24, +37, -204, -274, -137, -131, -189, -340, -114, +44, +178, +404, +348, +234, +110, -83, -267, -257, -291, - +4622, +6186, +1693, +1242, +2320, -46, -1538, +1517, +1127, -1483, -304, +841, +294, -67, +5, -203, -308, -200, -102, -217, -386, -222, -141, -99, +23, +201, +346, +265, +65, -52, -11, -200, - +5193, +6638, +1553, +1499, +2388, -594, -1569, +2013, +985, -1862, -401, +847, +342, -127, -49, -265, -391, -279, -24, -258, -446, -237, -373, -107, -35, -174, -57, +100, +163, +130, +55, -58, - +5824, +7125, +1374, +1701, +2443, -1077, -1576, +2426, +806, -2127, -438, +769, +324, -176, +41, -326, -603, -328, -5, -213, -487, -284, -470, -142, -47, -263, -242, -262, -124, +12, +120, +33, - +6335, +7628, +1408, +1800, +2526, -1396, -1736, +2894, +798, -2493, -400, +738, +274, -165, +15, -222, -694, -482, +72, -261, -572, -116, -475, -277, -17, -291, -261, -317, -412, -297, -56, +28, - +6761, +8075, +1459, +2066, +2609, -1964, -1828, +3359, +727, -2782, -456, +715, +208, -232, -18, -148, -747, -627, +81, -302, -698, -121, -406, -325, -107, -219, -294, -466, -521, -391, -176, -288, - +7157, +8606, +1581, +2068, +2784, -2202, -2039, +3602, +767, -2899, -542, +707, +104, -260, -9, -128, -676, -844, +64, -130, -840, -253, -381, -284, -107, -233, -282, -564, -568, -421, -261, -447, - +7410, +9322, +1838, +1630, +3033, -1959, -2478, +3478, +916, -2764, -738, +578, +126, -372, +30, -107, -667, -935, -60, -32, -777, -452, -481, -148, -144, -362, -257, -530, -598, -498, -436, -437, - +7600, +9757, +2318, +1422, +3068, -1530, -2951, +3205, +1134, -2618, -848, +362, +153, -421, -49, -52, -609, -1028, -173, +13, -822, -437, -527, -269, -108, -428, -361, -411, -698, -654, -354, -475, - +8104, +9762, +2543, +1907, +2993, -1492, -3123, +3108, +1007, -2376, -799, +85, +225, -462, -207, +76, -422, -1156, -330, +104, -791, -492, -526, -345, -141, -393, -383, -537, -744, -705, -190, -505, - +8909, +9413, +2318, +2985, +3009, -1948, -3050, +3168, +425, -2046, -543, -270, +167, -483, -337, +79, -154, -1299, -550, +186, -643, -695, -585, -287, -262, -358, -406, -740, -843, -531, -265, -573, - +9426, +9025, +2181, +4009, +3640, -2472, -3407, +3777, -384, -2061, +272, -562, -296, -282, -348, -201, +397, -1463, -821, +375, -577, -753, -723, -199, -322, -387, -483, -747, -845, -590, -326, -571, - +10108, +8385, +1400, +6014, +4224, -3846, -2895, +4634, -1924, -2185, +1409, -691, -736, -445, -160, -496, +690, -1211, -1255, +419, -429, -745, -766, -201, -456, -411, -561, -544, -937, -907, -160, -739, - +10943, +8373, +46, +7110, +5244, -4697, -2467, +5334, -3432, -2574, +2681, -670, -1220, -664, +155, -796, +596, -633, -1639, +276, -169, -751, -786, -162, -512, -556, -561, -560, -872, -1006, -393, -823, - +12145, +8720, -1664, +7093, +6723, -4974, -2355, +5841, -4655, -3092, +3716, -397, -1752, -903, +493, -914, +75, -59, -1625, -196, +169, -746, -800, +30, -700, -607, -584, -846, -650, -980, -876, -718, - +13449, +9173, -3433, +6797, +7856, -4807, -2042, +6051, -5634, -3740, +4384, +233, -2242, -1265, +790, -884, -471, +114, -1201, -709, +233, -517, -817, +265, -855, -714, -702, -983, -688, -985, -936, -805, - +14554, +9843, -4692, +5589, +8876, -3969, -1993, +6452, -6471, -4465, +4658, +1064, -2464, -1808, +1040, -734, -892, -140, -587, -969, -47, -87, -824, +496, -902, -929, -759, -1132, -899, -928, -701, -988, - +15499, +10690, -5588, +4019, +9370, -2825, -1817, +6925, -7237, -5143, +4618, +1819, -2391, -2377, +1112, -527, -1082, -687, -144, -864, -451, +270, -610, +501, -919, -981, -876, -1386, -1039, -897, -490, -928, - +17184, +10743, -6654, +3458, +8941, -2496, -296, +6958, -8428, -5131, +4435, +2201, -2279, -2678, +1041, -465, -1059, -1191, -112, -563, -633, +396, -146, +255, -999, -772, -1178, -1541, -1165, -955, -285, -586, - +18439, +11433, -7884, +3086, +8223, -2330, +1154, +7363, -9381, -5433, +4476, +2299, -2058, -2772, +772, -429, -922, -1487, -412, -319, -459, +489, +179, +4, -920, -666, -1378, -1593, -1381, -989, -70, -199, - +20043, +11571, -8926, +2927, +7480, -2933, +2814, +7900, -10432, -5634, +4629, +2163, -2083, -2501, +322, -605, -660, -1666, -795, -420, +2, +773, +28, -86, -714, -788, -1426, -1661, -1631, -934, -18, +154, - +21100, +12438, -9991, +2658, +6924, -3598, +3640, +9112, -11048, -6263, +4923, +1936, -2083, -2127, -90, -1004, -241, -1755, -1109, -655, +353, +1477, -496, -26, -322, -1103, -1235, -1773, -1804, -950, -17, +340, - +22220, +13016, -10684, +2238, +6616, -4544, +3961, +10464, -11476, -6710, +4989, +1692, -2097, -1740, -471, -1509, +171, -1891, -1172, -1008, +446, +2361, -1007, -80, +206, -1448, -1059, -1719, -2148, -946, -187, +379, - +23160, +13514, -10970, +1648, +6564, -5582, +3717, +11856, -11585, -6851, +4668, +1415, -1888, -1527, -831, -1885, +423, -2020, -993, -1361, +246, +3145, -1090, -388, +815, -1543, -1101, -1488, -2586, -1060, -470, +329, - +23984, +13865, -11140, +1506, +6153, -6455, +3221, +12728, -11274, -6683, +4149, +879, -1350, -1515, -1321, -1966, +480, -2107, -735, -1620, -134, +3538, -695, -707, +1213, -1214, -1434, -1269, -3012, -1293, -736, +72, - +24782, +14120, -11236, +1961, +5253, -7111, +2788, +12880, -10420, -6360, +3471, +290, -601, -1551, -1881, -1800, +635, -2271, -409, -1692, -623, +3484, +102, -645, +1121, -484, -1860, -1402, -3095, -1550, -1113, -154, - +24478, +15178, -10478, +1270, +5126, -7637, +1416, +13108, -9038, -6194, +2475, -43, +4, -1635, -2240, -1773, +998, -2517, -301, -1320, -1403, +2910, +1377, -319, +543, +368, -2377, -1572, -2886, -2071, -1188, -610, - +23336, +16618, -8704, -223, +5298, -7565, -968, +13263, -6982, -6465, +1742, -449, +352, -1376, -2596, -1623, +1240, -2651, -286, -899, -1898, +1936, +2438, +528, -375, +750, -2161, -1941, -2664, -2274, -1304, -958, - +22712, +17229, -7217, -356, +4386, -7099, -2807, +12389, -4621, -6839, +1425, -975, +424, -777, -2826, -1526, +1422, -2734, -246, -516, -2068, +1271, +2387, +1470, -861, +696, -1581, -2206, -2447, -2243, -1214, -1282, - +22892, +17053, -6909, +1550, +2504, -7309, -2981, +10387, -2761, -6478, +847, -1462, +747, -527, -2541, -1653, +1330, -2326, -581, +131, -2018, +687, +1356, +2090, -263, +54, -772, -2199, -2423, -1678, -1280, -1950, - +22958, +16963, -6994, +3862, +804, -8094, -2421, +7964, -1267, -5599, -222, -1546, +1312, -701, -1892, -1941, +925, -1306, -1223, +805, -1822, -507, +577, +2232, +644, +10, -622, -1590, -2129, -1759, -1313, -2053, - +22795, +16811, -6665, +5414, -231, -8963, -2057, +5715, -140, -4502, -1495, -1224, +1967, -1203, -1238, -1944, +128, +141, -1762, +375, -1151, -1442, -293, +2236, +1381, +482, -421, -1050, -2081, -1865, -1013, -2075, - +22156, +16822, -5737, +5914, -531, -9248, -2574, +3914, +921, -3656, -2319, -843, +2476, -1480, -1050, -1438, -455, +614, -1691, -640, -770, -1051, -1218, +1623, +1962, +1863, -702, -1229, -1009, -2137, -1191, -1810, - +21420, +16785, -4799, +6154, -663, -9035, -3372, +2102, +2124, -2833, -3110, -133, +2641, -1601, -638, -1248, -923, +605, -1675, -1249, -658, -53, -1902, +1335, +2038, +2064, +272, -1486, -537, -2097, -1156, -1644, - +21361, +15629, -3750, +6528, -1323, -8215, -4323, +464, +3132, -1840, -3943, +437, +3186, -1861, -582, -1384, -980, +77, -1818, -753, -1149, +490, -779, +520, +1069, +2103, +1599, -1602, -997, -1118, -1205, -1803, - +20330, +15116, -2198, +6125, -1778, -6323, -6028, -1388, +5002, -1330, -4769, +1469, +3125, -2056, -350, -2178, -854, +367, -2722, -22, -540, -99, +456, +351, -11, +1824, +1553, -531, -893, -1195, -631, -1855, - +20062, +13940, -1073, +5901, -2918, -4148, -6485, -3763, +6395, -76, -5571, +1976, +3170, -2469, -500, -2482, -418, +183, -3077, +751, +150, -824, +890, +1089, -685, +923, +962, +419, -69, -1542, -261, -1391, - +21165, +12566, -2644, +7196, -3419, -4374, -5591, -3941, +6004, +491, -4989, +1625, +3132, -2899, -840, -2060, -308, +352, -2926, +725, +461, -401, +854, +946, -597, +286, +687, +245, +522, -714, -834, -655, - +22181, +10753, -4743, +9694, -3820, -6164, -3751, -3237, +4762, +962, -4786, +856, +3566, -3082, -1668, -1292, +74, +282, -2679, +317, +654, +206, +432, +672, -272, -199, +427, +115, +322, +278, -897, -731, - +21833, +9209, -6007, +12315, -3453, -8356, -2210, -1380, +3074, +222, -3684, +199, +3572, -2831, -2010, -495, +19, -33, -2094, +470, +147, +334, +628, +538, -358, +45, -17, -272, +825, +112, -396, -410, - +21222, +7854, -6428, +13904, -3314, -9841, -311, -717, +1107, +600, -2989, -732, +3598, -2074, -2105, -29, +34, -558, -1422, +552, -367, +238, +799, +488, -473, +119, +72, -314, +618, +130, -262, -1, - +19959, +7458, -6254, +13163, -2749, -8575, +168, -1541, +524, +795, -2410, -1167, +3370, -1359, -1981, +131, -37, -600, -1272, +322, -134, +241, +366, +572, -360, -40, +508, -76, -11, +77, -84, -39, - +18263, +7315, -5697, +12058, -1512, -7911, -155, -1097, -277, +440, -1476, -1163, +2794, -682, -1653, -226, +195, -447, -1525, +167, +243, +191, +5, +420, -166, +267, +536, -17, -141, -216, +13, -120, - +14574, +8295, -838, +5767, -2715, -2520, -1691, -1773, +1779, -38, -2463, +993, +2121, -1350, -803, -1187, +196, -325, -1656, +367, -300, -397, +308, +424, +622, +1154, -257, -779, -260, -661, -212, +363, - +12640, +8177, -514, +5232, -1427, -2829, -1332, -1356, +968, +94, -1951, +761, +1867, -950, -840, -1080, +224, -469, -1525, +376, -426, -453, +483, +403, +530, +830, -326, -345, -235, -760, -56, +349, - +11044, +7881, -148, +5139, -839, -2943, -771, -1231, +563, +131, -1472, +726, +1552, -637, -706, -854, +77, -516, -1176, +229, -479, -289, +444, +438, +558, +439, -291, +63, -149, -710, +13, +439, - +9448, +7315, +777, +4791, -766, -2291, -640, -1240, +566, +79, -1184, +670, +1401, -496, -584, -601, -102, -472, -972, +95, -341, -227, +329, +485, +481, +177, -40, +272, -35, -470, -18, +555, - +7784, +6813, +1919, +4053, -580, -1299, -807, -1308, +740, +180, -1143, +616, +1306, -404, -321, -523, -263, -345, -754, -11, -288, -134, +224, +482, +339, +206, +285, +426, +140, -377, +51, +463, - +6153, +6246, +2936, +3514, -265, -522, -963, -1330, +954, +278, -1137, +468, +1183, -27, -199, -727, -188, -39, -737, -145, -161, -167, +296, +467, +191, +475, +664, +574, +50, -370, +118, +236, - +5216, +5838, +3133, +3059, +1, -157, -756, -1165, +778, +326, -848, +372, +928, +178, -109, -801, -20, +104, -796, -199, -47, -58, +362, +449, +343, +811, +845, +385, -220, -311, +33, +113, - +4597, +5368, +3150, +2849, +47, +176, -392, -1164, +650, +523, -570, +155, +748, +467, -67, -777, +130, +87, -748, -20, -18, +143, +685, +510, +516, +926, +646, +76, -295, -314, +101, +201, - +4160, +4800, +2975, +2878, +215, +185, -346, -879, +720, +434, -577, +142, +805, +500, -153, -718, +261, +104, -689, +154, +246, +528, +763, +288, +509, +781, +225, -155, -161, -171, -27, -42, - +4047, +4610, +2554, +2523, +343, +309, -161, -677, +639, +395, -412, +331, +729, +219, -6, -351, +190, +142, -205, +534, +514, +540, +360, +158, +449, +446, +134, -82, -194, -248, -36, -79, - +3681, +4202, +2459, +2522, +468, +335, -124, -483, +720, +451, -371, +292, +718, +292, +137, -143, +474, +547, +124, +441, +403, +249, +3, +182, +383, +387, +48, -193, -191, -244, -51, -202, - +3418, +3958, +2332, +2406, +561, +275, -16, -147, +764, +316, -330, +555, +677, +300, +712, +391, +554, +506, -31, +195, +200, -92, +90, +358, +160, +198, -1, -186, -221, -248, -146, -268, - +3294, +3885, +2237, +2055, +440, +495, +330, -195, +594, +558, -92, +562, +1070, +915, +915, +255, +158, +177, -224, +85, +144, -90, +60, +179, +169, +71, -152, -244, -264, -222, -259, -374, - +3036, +3530, +2299, +2284, +540, +283, +250, +363, +785, +420, +481, +1288, +1245, +678, +546, -172, -123, +52, -133, +151, -76, -120, +87, +168, +37, -127, -184, -303, -217, -273, -383, -412, - +2951, +3684, +2445, +1884, +210, +635, +675, +547, +1250, +1227, +765, +965, +798, +86, +80, -228, +0, +39, -228, +30, -27, -42, +25, +53, -221, -186, -201, -304, -216, -403, -409, -382, - +2948, +3672, +2453, +1863, +191, +765, +1578, +1710, +1229, +774, +415, +256, +396, -103, +36, -65, -268, -80, -64, -3, -34, -116, -164, -71, -281, -296, -229, -301, -271, -378, -434, -341, - +2843, +3566, +2452, +2150, +1119, +1466, +1682, +1795, +928, -254, -261, +372, +403, -274, -165, -161, -184, -111, -88, -48, -200, -201, -180, -289, -413, -275, -295, -256, -268, -441, -348, -288, - +2564, +4101, +3460, +2647, +1410, +1412, +1574, +983, +11, +28, -27, +71, +225, -244, +4, -163, -265, +10, -151, -168, -99, -187, -352, -372, -357, -210, -195, -232, -258, -356, -289, -290, - +3244, +5969, +3950, +1503, +762, +1387, +767, +390, +603, -90, -364, -54, +452, -41, -367, -79, -184, -274, -111, -48, -196, -322, -399, -360, -258, -194, -183, -224, -289, -330, -316, -345, - +3784, +6054, +3526, +1790, +389, +352, +1278, +1092, -88, -197, +260, -59, +139, +65, -157, -256, -333, -279, -121, -80, -211, -244, -442, -312, -216, -247, -254, -250, -268, -326, -359, -335, - +2822, +5085, +3846, +2069, +435, +648, +1401, +1023, +236, -6, +341, +350, +172, -40, +34, -202, -361, -272, -304, -204, -29, -187, -395, -181, -217, -302, -314, -303, -311, -325, -288, -335, - +2707, +4855, +2989, +1364, +947, +1039, +1087, +968, +768, +256, +227, +682, +564, +111, +39, -39, -213, -415, -274, -193, -240, -136, -135, -139, -171, -174, -321, -402, -386, -337, -287, -323, - +2574, +4549, +2481, +1104, +1330, +907, +475, +1038, +988, +424, +619, +680, +540, +614, +433, -71, -59, -64, -397, -356, -134, -141, -286, +11, +52, -193, -195, -312, -434, -438, -356, -279, - +2422, +4172, +2328, +1283, +1472, +647, +125, +1006, +879, +224, +547, +905, +832, +557, +435, +418, +214, -78, -116, -201, -318, -253, -140, -84, -96, +29, -87, -310, -305, -394, -495, -381, - +2457, +4168, +2312, +1258, +1473, +568, -47, +1024, +903, -67, +189, +852, +771, +615, +723, +347, +177, +387, +190, -226, -97, -8, -307, -173, +62, -42, -182, -125, -211, -363, -347, -400, - +2479, +4135, +2339, +1562, +1610, +192, -248, +1300, +909, -337, +81, +690, +508, +388, +636, +433, +219, +291, +303, +164, +7, +21, +31, -36, -110, -67, -84, -238, -296, -215, -282, -331, - +2624, +4417, +2420, +1461, +1611, +190, -334, +1254, +921, -411, -37, +569, +431, +277, +366, +238, +20, +196, +411, +163, +23, +266, +197, +88, +161, +35, -197, -291, -298, -316, -320, -247, - +2857, +4661, +2448, +1599, +1644, -52, -369, +1426, +846, -542, -64, +578, +333, +106, +438, +207, -300, -149, +268, +191, +20, +247, +302, +342, +299, +211, +96, -235, -456, -375, -272, -328, - +3188, +5067, +2423, +1526, +1665, -114, -379, +1420, +746, -615, -22, +514, +244, +36, +349, +284, -291, -398, -114, +35, -95, +71, +386, +395, +370, +407, +245, -45, -298, -398, -392, -348, - +3392, +5464, +2575, +1426, +1705, -141, -510, +1546, +697, -819, -14, +541, +205, -81, +197, +362, -172, -517, -262, -166, -340, -129, +171, +384, +434, +397, +374, +107, -152, -237, -310, -366, - +3778, +5985, +2547, +1224, +1789, -132, -599, +1611, +647, -931, +4, +483, +130, -67, +79, +336, -105, -481, -339, -274, -374, -355, -90, +280, +364, +297, +401, +185, -84, -89, -184, -264, - +4238, +6510, +2468, +1077, +1860, -270, -600, +1756, +466, -1066, +75, +520, -27, -200, +61, +302, -74, -470, -390, -308, -412, -437, -214, +90, +251, +154, +227, +157, -56, -78, -125, -116, - +4791, +7085, +2292, +969, +1933, -473, -530, +1936, +231, -1156, +164, +537, -117, -309, +2, +238, -84, -390, -352, -407, -462, -398, -204, -73, +172, +87, +45, +57, -238, -129, -20, -64, - +5298, +7657, +2183, +875, +2016, -753, -494, +2218, -13, -1312, +267, +578, -182, -441, -93, +196, -122, -393, -302, -435, -513, -440, -113, -135, -14, +190, -70, -156, -337, -293, -128, -66, - +5772, +8287, +2120, +701, +2131, -1043, -540, +2502, -218, -1459, +344, +564, -207, -513, -279, +141, -129, -411, -314, -481, -509, -495, -83, -55, -189, +147, -1, -322, -411, -420, -358, -159, - +6156, +8940, +2162, +466, +2269, -1223, -655, +2753, -350, -1564, +441, +587, -308, -513, -366, -30, -129, -409, -289, -486, -560, -558, -73, +140, -206, -105, +70, -261, -507, -464, -575, -293, - +6742, +9493, +2028, +426, +2287, -1512, -626, +2857, -609, -1493, +499, +542, -410, -555, -338, -271, -242, -367, -235, -522, -637, -614, -88, +199, -77, -219, -130, -145, -572, -543, -605, -493, - +7455, +9892, +1817, +673, +2165, -1782, -526, +2877, -874, -1369, +613, +482, -471, -664, -187, -495, -430, -293, -210, -463, -712, -671, -39, +162, -43, -26, -317, -285, -509, -579, -619, -592, - +8019, +10193, +1826, +947, +2060, -1895, -616, +2838, -973, -1270, +603, +513, -417, -884, -78, -555, -646, -328, -167, -385, -659, -776, -74, +250, -111, +117, -295, -476, -508, -590, -658, -581, - +8488, +10358, +1977, +1265, +2015, -1887, -920, +2788, -1005, -1210, +617, +441, -268, -1020, -166, -487, -785, -574, -79, -286, -605, -796, -270, +464, -155, +23, -164, -502, -600, -692, -661, -560, - +9032, +10338, +2002, +1761, +2191, -2067, -1283, +2872, -1174, -1205, +746, +309, -249, -938, -329, -531, -744, -897, -123, -86, -627, -738, -388, +469, +0, -188, -125, -361, -727, -819, -607, -613, - +9864, +10088, +1523, +2776, +2578, -2733, -1373, +3075, -1563, -1189, +922, +251, -375, -786, -321, -773, -647, -1025, -380, +85, -536, -746, -274, +305, +98, -159, -216, -318, -746, -852, -593, -682, - +10598, +9701, +940, +4274, +3058, -3831, -1125, +3536, -2290, -1265, +1285, +276, -567, -754, -134, -923, -659, -1033, -713, +96, -164, -899, -154, +299, +14, +105, -391, -378, -574, -830, -773, -577, - +10878, +9347, +643, +5807, +3528, -5043, -576, +4279, -3492, -1535, +1951, +350, -873, -858, +153, -941, -755, -1006, -1030, -132, +274, -915, -234, +450, -136, +336, -408, -493, -454, -795, -947, -437, - +11510, +9866, -517, +5809, +4860, -5267, -967, +4693, -4147, -1854, +2440, +298, -1178, -888, +429, -1109, -918, -824, -1173, -606, +385, -531, -456, +537, -109, +345, -322, -566, -435, -822, -1034, -491, - +12635, +10354, -1762, +5073, +6221, -4898, -1391, +4717, -4535, -2049, +2756, +364, -1594, -867, +721, -1229, -1148, -678, -1099, -989, +154, -84, -438, +418, +119, +303, -273, -468, -478, -915, -1048, -631, - +13739, +10931, -3039, +4229, +7036, -3993, -1627, +4425, -4683, -2247, +2950, +515, -2020, -897, +930, -1302, -1368, -646, -958, -1207, -291, +233, -188, +154, +374, +357, -256, -270, -569, -1081, -1002, -747, - +14744, +11698, -4040, +3065, +7514, -2833, -1645, +3985, -4805, -2328, +2928, +769, -2368, -998, +1104, -1337, -1464, -742, -746, -1258, -738, +270, +219, -28, +461, +565, -183, -98, -674, -1208, -1015, -822, - +15661, +12563, -4797, +1712, +7684, -1798, -1303, +3560, -5128, -2181, +2756, +928, -2517, -1206, +1167, -1348, -1484, -913, -576, -1200, -996, -13, +556, +59, +345, +829, -12, -35, -778, -1229, -1099, -956, - +16364, +13610, -5417, +358, +7606, -1081, -694, +3363, -5713, -1853, +2574, +824, -2410, -1460, +1029, -1325, -1426, -1134, -452, -1081, -1063, -425, +647, +304, +234, +1059, +210, -50, -832, -1188, -1210, -1113, - +17301, +14490, -6249, -381, +7171, -884, +239, +3371, -6504, -1381, +2548, +400, -2128, -1591, +701, -1356, -1209, -1399, -313, -902, -1081, -684, +503, +524, +231, +1359, +398, -161, -776, -1179, -1300, -1239, - +19090, +14422, -7537, +119, +6088, -1186, +1568, +3110, -7218, -739, +2538, -295, -1756, -1656, +234, -1424, -1032, -1537, -233, -694, -1129, -779, +292, +571, +268, +1771, +559, -457, -623, -1262, -1420, -1267, - +20805, +14336, -8736, +920, +4704, -1494, +2649, +2925, -7561, -328, +2504, -973, -1224, -1901, -205, -1369, -1023, -1413, -248, -538, -1010, -940, +175, +531, +207, +2290, +770, -842, -494, -1328, -1610, -1150, - +22109, +14485, -9555, +1595, +3405, -1875, +3399, +2878, -7467, -159, +2320, -1375, -756, -2191, -602, -1235, -972, -1286, -262, -418, -788, -1088, +82, +594, -8, +2708, +1095, -1168, -535, -1285, -1751, -974, - +22911, +14859, -9889, +1889, +2263, -2415, +3753, +2933, -7127, -227, +1986, -1630, -415, -2450, -1051, -1037, -827, -1434, -165, -366, -647, -1068, -154, +882, -368, +2616, +1524, -1241, -905, -1145, -1703, -979, - +23245, +15363, -9604, +1856, +1322, -3040, +3776, +3098, -6633, -350, +1568, -1819, -103, -2618, -1435, -794, -655, -1757, +41, -318, -654, -746, -459, +1267, -738, +1911, +2006, -928, -1339, -994, -1484, -1026, - +23048, +16128, -8809, +1596, +486, -3594, +3405, +3364, -5994, -577, +1227, -2110, +338, -2650, -1697, -633, -515, -2058, +200, -158, -867, -14, -786, +1297, -792, +869, +2115, -163, -1403, -1180, -994, -927, - +22809, +16473, -7468, +1279, -304, -4094, +2691, +3769, -5530, -656, +858, -2440, +917, -2540, -1937, -596, -384, -2335, +377, -58, -950, +797, -1345, +1069, -434, -289, +1797, +898, -1234, -1327, -340, -1167, - +22287, +16759, -6019, +907, -957, -4847, +1807, +4137, -5333, -671, +434, -2757, +1592, -2484, -2177, -772, -331, -2403, +180, +397, -1148, +952, -1488, +457, +9, -900, +941, +1571, -432, -1370, -331, -1254, - +21687, +17120, -4653, +1201, -1531, -5605, +1233, +4224, -4907, -618, +334, -2824, +2339, -2255, -2444, -777, -345, -2119, +138, +690, -1476, +1035, -1282, -255, +720, -854, -197, +2118, +560, -1484, -235, -840, - +20812, +17571, -3517, +1604, -1925, -6559, +655, +3983, -4426, -559, +260, -2689, +2858, -2122, -2599, -1007, -244, -1381, -668, +771, -1403, +492, -756, -404, +502, +93, -695, +1167, +1322, -839, -247, -943, - +20139, +17539, -2371, +1932, -2101, -7422, -134, +3611, -3812, -412, +185, -2382, +3168, -1970, -3046, -567, -229, -1405, -1029, +419, -1307, +375, -467, -375, +380, +1214, -1001, -411, +2033, -274, -430, -799, - +19753, +17050, -1457, +2572, -2382, -8049, -965, +2983, -2716, -235, -326, -1321, +2916, -2171, -2296, -826, -571, -1104, -1712, +62, -637, +159, -684, +668, +232, +865, +69, -1322, +1025, +254, +67, -588, - +19313, +16611, -863, +3251, -2596, -8557, -2035, +2815, -1745, -84, -572, -839, +2943, -1918, -2229, -1014, -802, -1378, -1915, +318, -814, +322, -33, +499, +177, +995, +794, -1733, -313, +324, +698, -311, - +19432, +15373, -280, +4094, -3296, -8664, -2599, +2170, -225, -30, -1542, +594, +2691, -2281, -1544, -1686, -1307, -903, -1915, -153, -126, +698, -360, +340, +825, +1064, +273, -993, -998, -689, +1129, +142, - +19161, +14270, +164, +4902, -4023, -8420, -3080, +1712, +839, +173, -2097, +1066, +2771, -2396, -1584, -2406, -841, -783, -2457, +724, +24, +6, +115, +460, +603, +1331, -24, -638, -844, -1849, +898, +543, - +18185, +13294, +1396, +5056, -4975, -6537, -4217, +256, +3477, -142, -3597, +2330, +2565, -2744, -1485, -2544, -786, -568, -2073, +767, -278, -87, +712, +47, +286, +1855, +49, -975, -401, -2019, -110, +720, - +18313, +12559, +1145, +3915, -4788, -4611, -4800, -248, +4385, -385, -3737, +2444, +2059, -2542, -1236, -3085, -90, -60, -2227, +677, -342, +170, +590, -167, +444, +1928, +89, -943, -588, -1828, -345, +588, - +19047, +11703, -955, +4365, -4893, -3853, -3267, -1533, +4299, +134, -4009, +2112, +2030, -2635, -1305, -2227, -55, -224, -2038, +767, -148, -368, +345, +393, +586, +1413, +142, -763, -846, -1547, -218, +199, - +19014, +10464, -2335, +5732, -4481, -4151, -2526, -849, +3281, -472, -3208, +1847, +1902, -2824, -779, -1622, -183, -396, -1924, +1079, -378, -665, +394, +566, +672, +1279, -35, -701, -812, -1354, +31, +44, - +18162, +9492, -3044, +7090, -3964, -4906, -1033, -851, +1796, -66, -2783, +1424, +1900, -2369, -665, -1270, -198, -516, -1532, +747, -522, -404, +331, +314, +897, +1359, -338, -718, -601, -1136, +5, +342, - +16598, +8667, -2195, +6902, -3696, -3649, -980, -1771, +1921, -11, -2768, +1203, +2093, -1882, -722, -1121, -151, -296, -1570, +398, -363, -287, +171, +326, +918, +1214, -295, -849, -425, -762, -169, +362, - +14574, +8452, +258, +4430, -4365, -2407, +102, -220, -223, -832, -1852, +1615, +1202, -2022, +253, -1216, -444, -63, -1455, +368, -312, -727, +373, +217, -412, +849, +339, -186, +129, -916, -296, -195, - +12278, +8141, +1428, +3486, -3243, -1249, -493, -560, +165, -592, -1924, +1327, +1447, -1595, +92, -1011, -282, -108, -1214, +238, -313, -593, +289, +150, -236, +853, +317, -36, -85, -842, +19, -3, - +9818, +8134, +2487, +2440, -2082, -590, -823, -829, +309, -432, -1752, +1141, +1209, -1118, +27, -830, -297, -203, -900, +148, -415, -552, +316, +48, -77, +666, +249, +139, -158, -658, +206, +196, - +8218, +7700, +2637, +2474, -1307, -764, -546, -580, +192, -485, -1350, +993, +1042, -888, +8, -569, -372, -127, -680, -31, -355, -306, +131, -53, +119, +550, +383, +249, -130, -272, +357, +9, - +7260, +6952, +2528, +2729, -947, -957, -177, -207, -119, -455, -915, +762, +893, -798, +106, -330, -495, -95, -429, -94, -346, -203, -44, +24, +289, +567, +633, +250, -96, -40, +210, -228, - +6314, +6251, +2666, +2794, -629, -891, -34, +102, -183, -316, -716, +491, +887, -525, +110, -285, -414, +113, -262, -316, -185, -38, -118, +319, +459, +666, +731, +112, -104, -55, +52, -322, - +5417, +5613, +2827, +2816, -327, -838, +28, +455, -189, -286, -591, +343, +896, -378, +146, -174, -275, +223, -276, -286, +110, +144, +58, +429, +461, +747, +496, -173, -50, +14, -42, -390, - +4737, +5252, +2774, +2553, +28, -656, +97, +596, -174, -128, -477, +136, +904, -37, +86, -133, -68, +253, -71, +76, +414, +107, +44, +523, +339, +397, +292, -77, +23, -24, -184, -339, - +3981, +4689, +2974, +2705, +128, -673, +191, +862, -76, -188, -396, +170, +926, +95, +102, -7, +257, +567, +232, +227, +237, +46, +2, +205, +229, +329, +244, -63, +38, -31, -270, -262, - +3639, +4461, +2924, +2330, +166, -438, +386, +823, -29, -56, -316, +227, +932, +175, +339, +566, +570, +436, +183, +186, -95, -222, +17, +187, +153, +207, +145, +26, +72, -187, -282, -293, - +3259, +4171, +2982, +2255, +67, -366, +661, +923, -74, +111, -202, +162, +1330, +741, +513, +485, +402, +353, -62, -283, -157, +6, -120, +24, +146, +186, +148, -35, -33, -267, -322, -309, - +3122, +4118, +2900, +1944, +0, -98, +882, +1021, -181, +159, +515, +745, +1264, +473, +425, +351, -23, -30, -118, -265, -166, -97, -186, +192, +98, -114, +91, +11, -210, -322, -297, -412, - +3235, +4303, +2623, +1571, +77, +285, +881, +1185, +547, +613, +652, +402, +925, +370, -45, -8, +19, -59, -264, -364, +11, +81, -230, -1, -128, -117, +119, -34, -321, -411, -265, -385, - +3389, +4725, +2245, +1203, +58, +908, +2020, +1399, +334, +357, +538, +113, +387, +96, +39, -34, -209, -107, -173, -4, +62, -200, -339, -98, -124, -103, +123, -133, -427, -315, -196, -296, - +3432, +4882, +1947, +1109, +1266, +1747, +1687, +1007, +236, +86, +163, -84, +293, +0, -42, -68, -271, +122, +155, -210, -251, -156, -291, -288, -163, +38, -58, -180, -248, -250, -248, -317, - +3101, +4702, +2964, +1975, +929, +1541, +1764, +744, -57, -59, +188, -44, +87, -46, -2, +54, +100, +125, -120, -181, -166, -232, -402, -239, -149, -71, +43, -36, -164, -289, -356, -319, - +3896, +5904, +2447, +896, +1159, +1573, +1310, +595, -175, -62, +179, +2, +162, -166, +142, +495, +86, -291, -23, -10, -305, -361, -351, -199, -209, +43, +163, +6, -270, -382, -312, -377, - +4111, +6381, +2053, +273, +1154, +1396, +1209, +828, +5, -261, +331, +111, +75, +99, +333, +466, -15, -97, -25, -78, -294, -279, -352, -332, -105, +81, +175, -17, -195, -388, -359, -345, - +3095, +5772, +3360, +570, +281, +922, +1379, +1273, +83, -225, +436, +376, -8, +55, +499, +520, +210, -84, -45, +41, -164, -351, -289, -251, -244, +17, +144, +52, -153, -277, -319, -362, - +2581, +5083, +3747, +1205, +166, +554, +1050, +1250, +302, -212, +397, +598, +127, +67, +436, +383, +436, +172, +39, +53, -182, -180, -149, -287, -291, -5, +118, -12, -207, -177, -214, -333, - +2552, +4951, +3531, +1372, +349, +359, +972, +1271, +183, -443, +321, +679, +210, +58, +442, +429, +340, +218, +211, +180, -63, -124, -120, -161, -112, -103, +24, +72, -222, -300, -275, -209, - +2665, +4956, +3357, +1460, +482, +248, +879, +1296, +212, -508, +150, +505, +100, +12, +412, +341, +306, +406, +139, +62, +128, -3, -44, -39, -12, -31, +66, +41, -175, -338, -364, -294, - +2910, +5172, +3202, +1418, +526, +219, +908, +1323, +135, -544, +153, +494, +74, -268, +195, +282, +179, +422, +266, +64, +20, -95, +137, +155, -18, +190, +178, +50, -99, -282, -367, -378, - +3106, +5510, +3157, +1272, +654, +172, +826, +1393, +101, -594, +108, +460, +164, -335, -22, +133, -125, +219, +461, +52, -105, -110, +31, +176, +54, +285, +298, +145, +61, -223, -389, -316, - +3368, +5932, +3118, +1145, +758, +78, +765, +1468, +37, -717, +138, +457, +120, -290, -144, +62, -257, -129, +404, +63, -206, -153, -100, +152, +60, +155, +386, +264, +71, -31, -197, -377, - +3733, +6456, +3058, +942, +889, +25, +681, +1552, -15, -820, +148, +459, +69, -211, -190, -47, -265, -323, +274, +169, -372, -347, -72, +87, +21, +34, +374, +281, +11, +98, -68, -219, - +4188, +7066, +2967, +691, +995, -34, +635, +1651, -188, -887, +235, +437, -43, -227, -100, -139, -354, -381, +96, +266, -304, -584, -157, +39, -36, +7, +204, +218, +59, +6, -48, -108, - +4746, +7725, +2804, +457, +1072, -163, +664, +1759, -458, -963, +395, +430, -203, -287, -43, -200, -465, -426, -10, +200, -148, -659, -283, +63, -164, -51, +108, +49, +5, -21, -57, -176, - +5293, +8424, +2691, +200, +1075, -352, +722, +1883, -811, -1091, +593, +430, -407, -391, +9, -266, -633, -524, -73, +107, -103, -596, -428, +55, -134, -199, +69, -190, -167, +13, -143, -226, - +5863, +9141, +2667, +40, +1052, -543, +868, +2094, -1172, -1177, +878, +497, -546, -487, +104, -257, -732, -597, -153, +94, -40, -473, -388, -65, +22, -141, -52, -184, -372, -50, -90, -264, - +6256, +9830, +2894, -163, +970, -781, +879, +2372, -1514, -1408, +1131, +611, -714, -628, +124, -250, -817, -728, -318, +48, +3, -481, -237, -140, -63, +59, -165, -299, -349, -283, -158, -207, - +6594, +10538, +3188, -331, +878, -910, +803, +2530, -1667, -1658, +1306, +784, -813, -792, +152, -249, -880, -762, -546, -74, +192, -563, -200, +88, -247, +128, -7, -470, -416, -264, -343, -125, - +6837, +11211, +3624, -582, +873, -887, +585, +2486, -1630, -1804, +1272, +974, -837, -900, +104, -254, -971, -753, -648, -416, +302, -345, -395, +287, -167, -70, +229, -446, -659, -259, -391, -167, - +7304, +11778, +3694, -565, +932, -974, +429, +2281, -1610, -1825, +1167, +1061, -833, -962, +48, -273, -1134, -738, -667, -706, +131, -22, -389, +150, +154, -245, +198, -184, -827, -453, -344, -269, - +8235, +12147, +3165, -179, +1250, -1280, +244, +2247, -1802, -1646, +1035, +1095, -763, -1091, +127, -372, -1161, -866, -623, -801, -185, +121, -30, -44, +248, -32, -42, -16, -686, -726, -403, -227, - +9297, +12266, +2226, +787, +1787, -2113, +102, +2510, -2139, -1611, +994, +1052, -665, -1269, +199, -511, -1125, -991, -710, -778, -421, -49, +341, +106, +24, +220, -125, -69, -467, -801, -641, -218, - +10147, +12083, +1353, +2463, +2227, -3535, +293, +3115, -2724, -1863, +1270, +952, -736, -1330, +259, -696, -1131, -995, -849, -797, -533, -301, +461, +439, -103, +301, -72, -178, -379, -650, -774, -450, - +10811, +11594, +849, +4553, +2308, -5080, +959, +3783, -3596, -2129, +1832, +791, -1018, -1161, +326, -938, -1147, -869, -944, -907, -502, -461, +355, +652, +38, +401, -29, -244, -390, -373, -749, -645, - +12468, +11803, -955, +4782, +3329, -5109, +523, +3592, -3808, -2001, +2054, +456, -1328, -944, +309, -1271, -1148, -913, -864, -1037, -475, -486, +86, +849, +201, +318, +27, -164, -495, -300, -684, -554, - +14105, +12174, -2678, +4502, +4313, -4564, -152, +3309, -3818, -1885, +2202, +220, -1631, -822, +354, -1654, -1062, -1037, -689, -1076, -553, -275, -262, +847, +479, +306, -29, -31, -462, -290, -612, -344, - +15495, +12899, -4263, +3779, +5080, -3531, -900, +2885, -3629, -1828, +2258, +22, -1763, -951, +442, -1975, -1049, -1113, -557, -940, -769, +0, -446, +597, +677, +483, -199, +94, -302, -403, -398, -267, - +17119, +13318, -5617, +3044, +5263, -2373, -1299, +2236, -3473, -1545, +2114, -220, -1690, -1270, +432, -2146, -1156, -1068, -496, -761, -890, +68, -296, +183, +744, +728, -384, +229, -162, -523, -141, -307, - +18484, +13996, -6861, +2361, +5043, -1327, -1316, +1482, -3224, -1275, +1889, -488, -1425, -1754, +345, -2172, -1331, -893, -513, -608, -886, +7, -57, -56, +527, +1062, -513, +203, +21, -599, -22, -277, - +19664, +14808, -7930, +1852, +4472, -629, -944, +743, -2898, -1130, +1684, -714, -1190, -2160, +165, -2109, -1400, -723, -585, -442, -817, -87, +159, -22, +135, +1403, -577, -74, +264, -623, -30, -70, - +21117, +15043, -8651, +1665, +3562, -302, -433, +306, -2732, -998, +1479, -897, -1082, -2373, -85, -2001, -1270, -757, -612, -253, -784, -73, +231, +244, -130, +1411, -530, -488, +338, -544, +88, +69, - +22154, +15465, -8959, +1402, +2531, -346, +86, +155, -2719, -964, +1298, -1197, -837, -2538, -368, -1715, -1223, -911, -611, -54, -781, -33, +268, +653, -384, +1073, -318, -957, +90, -192, +241, +104, - +22859, +15907, -8818, +1234, +1297, -584, +637, +95, -2746, -892, +980, -1473, -344, -2665, -593, -1404, -1232, -1113, -586, +219, -920, +190, +386, +763, -501, +647, -217, -1200, -157, -60, +514, +428, - +23324, +16162, -8220, +1233, -271, -830, +1044, +4, -2761, -848, +545, -1644, +393, -2819, -805, -1116, -1381, -1232, -481, +245, -798, +446, +127, +832, -496, +109, -143, -1012, -512, -218, +1023, +636, - +23496, +16326, -7279, +1262, -1985, -1155, +1282, -135, -2863, -819, +135, -1617, +1191, -3031, -953, -1076, -1474, -1177, -641, +567, -852, +321, -9, +770, -595, -103, +21, -1077, -316, -427, +637, +1058, - +23095, +16685, -5961, +1382, -3468, -1694, +1418, -258, -2996, -670, +47, -1596, +2016, -3064, -1350, -839, -1582, -979, -545, +371, -989, +403, -350, +690, -257, -300, +78, -651, -207, -824, +183, +1360, - +23064, +16194, -4345, +1405, -4772, -2458, +1309, -309, -3246, -87, -265, -1329, +2641, -3203, -1669, -828, -1062, -1045, -1017, +329, -1074, +156, -379, +809, -266, -110, +348, -721, -327, -460, -318, +476, - +22166, +16307, -2720, +1513, -5503, -3602, +868, +57, -3167, +189, -262, -1102, +3165, -3419, -1892, +19, -1444, -1300, -1087, -50, -1052, +281, -440, +743, +297, +10, -121, -326, +62, -941, -205, -296, - +21370, +16040, -1198, +1538, -5866, -4987, +180, +1090, -3147, +120, +48, -770, +2709, -2688, -1726, -511, -1299, -1588, -1418, -44, -787, -13, -172, +1224, -173, +86, +190, -419, +152, -1134, +61, -580, - +20588, +15763, -447, +2263, -6362, -6502, +294, +1839, -3312, +732, -314, -780, +3610, -2936, -1826, -471, -1919, -1347, -1286, -339, -634, +489, -63, +635, +27, +550, -117, -722, +816, -1169, -430, +0, - +20517, +14351, +399, +2893, -7361, -6720, +201, +2186, -2610, +382, -514, +112, +3193, -2866, -1734, -1281, -1564, -810, -1919, -87, +325, -151, -301, +1086, -74, +249, +48, -598, +812, -904, -810, +141, - +19676, +13347, +1269, +2990, -7493, -6742, +83, +2235, -1954, +838, -1855, +980, +3383, -3609, -1607, -1296, -1241, -963, -1499, +306, -188, -169, +134, +680, -311, +474, +114, -498, +603, -657, -632, -512, - +18294, +12994, +1651, +2477, -6776, -5594, -1193, +2361, -74, -729, -2110, +2060, +2280, -3521, -925, -1755, -1040, +4, -1831, -24, +217, -146, -194, +523, -89, +412, +148, -368, +547, -585, -520, -748, - +18558, +12123, -346, +2881, -6362, -4645, -59, +1181, -164, -263, -2409, +2048, +1834, -3453, -616, -1525, -435, -206, -2101, +563, +264, -775, -79, +639, -65, +370, -42, -65, +678, -907, -609, -386, - +17685, +10480, -40, +3473, -6519, -2739, -508, +476, +794, -1230, -2572, +2593, +1476, -3576, +141, -1207, -497, -277, -1926, +798, -90, -892, +128, +624, -265, +467, +194, -122, +569, -901, -567, -322, - +16309, +9199, +186, +4082, -5385, -2804, -279, +710, -5, -1375, -2021, +2397, +953, -2773, +531, -1321, -456, -212, -1629, +612, -269, -841, +361, +372, -412, +797, +264, -230, +451, -838, -585, -226, - +15571, +11264, +2379, +478, -10208, -3446, +2999, +3275, -500, -1930, -3348, +266, +1652, -1984, +1186, -362, -580, +611, -665, +483, -71, -639, -236, -683, -414, +551, -551, -99, +973, -315, +229, -109, - +13108, +11000, +3186, +1280, -8696, -4695, +2112, +2705, +415, -824, -3163, -516, +1162, -1069, +990, -408, -449, +725, -557, +276, +252, -440, -211, -595, -598, +266, -697, +52, +1118, -108, +414, -97, - +11346, +10103, +3442, +1763, -7242, -4319, +1504, +1898, +542, -443, -2642, -793, +1114, -726, +553, -274, -403, +642, -397, +111, +268, -218, -281, -579, -486, +53, -591, +203, +993, +104, +363, -159, - +9333, +9181, +4026, +1891, -5737, -3665, +628, +1442, +686, -179, -2080, -924, +1011, -560, +271, -128, -344, +546, -210, +21, +313, -35, -349, -508, -402, +17, -265, +291, +786, +199, +233, -195, - +7824, +8565, +4450, +1794, -4594, -3174, +140, +1252, +671, +30, -1593, -927, +798, -461, +248, -40, -339, +534, -12, -10, +342, +33, -332, -281, -185, +20, -144, +273, +558, +171, +139, -239, - +6337, +7830, +4715, +1908, -3553, -2897, -163, +1081, +702, +211, -1249, -839, +616, -437, +196, +111, -267, +479, +135, +12, +326, +212, -47, -34, -177, -218, -105, +167, +266, +120, +84, -158, - +5342, +7158, +4878, +2188, -2903, -2714, -263, +1026, +804, +276, -973, -621, +425, -432, +323, +229, -177, +523, +156, +236, +666, +314, -37, +11, -270, -439, -68, +145, +155, +196, +77, -226, - +4764, +6717, +4705, +2043, -2555, -2349, -213, +866, +792, +353, -756, -574, +302, -362, +407, +259, -158, +679, +475, +400, +570, +170, -131, -157, -526, -408, +47, -28, +159, +57, -96, +32, - +4181, +6120, +4609, +2151, -2225, -2162, -181, +900, +848, +365, -518, -436, +242, -269, +393, +570, +370, +732, +393, +271, +373, -75, -324, -138, -536, -326, +93, -221, -12, +223, +50, -24, - +4015, +5982, +4273, +1823, -2038, -1791, +35, +933, +871, +389, -235, -337, +148, +75, +824, +806, +274, +455, +265, +150, +178, -130, -231, -188, -435, -270, -151, -8, +202, +32, -30, +38, - +3935, +5782, +3945, +1507, -1917, -1335, +323, +927, +964, +503, -86, -55, +602, +326, +626, +524, +61, +238, +130, +138, +142, -108, -297, -233, -424, -97, +144, -211, +2, +11, +59, +102, - +3902, +5745, +3557, +1311, -1656, -1075, +626, +1068, +893, +711, +517, +29, +271, +228, +356, +317, +28, +184, +167, +138, +52, -157, -344, -161, +13, -100, -239, -186, +14, +16, +22, +148, - +3628, +5656, +3399, +1258, -1213, -703, +858, +1220, +1301, +913, +184, -182, +3, -30, +266, +240, +91, +140, +96, +72, +8, -109, -120, +190, -242, -389, -174, -137, -134, -75, +225, -41, - +3635, +5627, +3156, +1153, -667, -305, +1152, +1542, +949, +463, +84, -274, -67, -129, +266, +232, +17, +66, +88, +199, -87, +174, +189, -310, -329, -218, -244, -286, -93, -9, -62, +33, - +3750, +5696, +2978, +1126, -254, +215, +1221, +1141, +606, +286, +8, -220, -48, -224, +233, +223, -143, +120, +323, +31, +156, +458, -282, -324, -203, -336, -370, -173, -80, -307, +26, +219, - +3708, +6007, +3252, +1084, -197, +131, +973, +976, +487, +259, +136, -370, -69, -106, +43, +210, -33, +247, +171, +273, +554, -86, -293, -37, -371, -530, -251, -143, -337, -184, +155, +321, - +3943, +6405, +3364, +855, -445, -300, +746, +1177, +555, +245, +158, -422, -186, -48, +127, +143, +129, +192, +83, +689, +384, -278, -109, -102, -392, -521, -225, -363, -322, +86, +221, +118, - +3963, +6788, +3553, +597, -677, -627, +588, +1136, +500, +391, +206, -514, -153, +22, +70, +197, +285, -39, +123, +985, +227, -286, +1, -155, -441, -352, -267, -542, -311, +209, +364, -112, - +3772, +6899, +3947, +501, -937, -727, +636, +1168, +340, +109, +160, -312, -140, -4, +244, +180, +122, +45, +73, +884, +451, -250, -58, -111, -413, -290, -294, -523, -289, +122, +331, -74, - +3462, +6882, +4502, +695, -1202, -904, +656, +1374, +518, -169, -142, -383, -288, +151, +492, +188, +134, +104, -153, +774, +761, -195, -124, +19, -277, -262, -247, -438, -285, +127, +319, -81, - +3402, +6922, +4775, +801, -1378, -1083, +663, +1537, +602, -245, -249, -453, -540, -124, +493, +404, +124, +134, -71, +426, +838, +18, -287, -73, -153, -110, -193, -451, -296, +175, +269, -23, - +3560, +7157, +4846, +842, -1601, -1246, +801, +1630, +679, -335, -317, -376, -611, -299, +272, +320, +96, +184, +134, +295, +750, +284, -376, -324, -110, -14, -46, -352, -273, +168, +253, +68, - +3781, +7518, +5064, +780, -1878, -1426, +861, +1887, +678, -490, -323, -360, -585, -337, +124, +225, -92, -87, +272, +304, +648, +580, -207, -590, -353, -33, +88, -214, -276, +256, +259, +93, - +4023, +8013, +5260, +520, -2126, -1537, +904, +2110, +621, -684, -367, -338, -545, -334, +31, +151, -90, -326, +98, +316, +484, +602, +136, -551, -624, -186, +88, -177, -281, +258, +226, +119, - +4437, +8672, +5470, +189, -2426, -1777, +1004, +2534, +471, -984, -417, -281, -536, -313, +40, +69, -119, -424, -33, +239, +301, +445, +325, -371, -639, -347, -13, -119, -335, +161, +141, +68, - +4952, +9349, +5539, -179, -2758, -2011, +1219, +2888, +279, -1276, -463, -211, -592, -300, +102, -4, -169, -480, -76, +194, +168, +301, +339, -242, -691, -395, -6, -107, -357, +112, +26, -204, - +5239, +10282, +5947, -647, -3196, -2226, +1311, +3315, +161, -1681, -491, -173, -597, -373, +170, -45, -294, -440, -175, +126, +127, +78, +331, -124, -648, -478, -149, -83, -176, +24, -84, -362, - +5839, +11149, +6005, -1123, -3665, -2327, +1613, +3627, -71, -2065, -429, -99, -639, -410, +251, -61, -421, -436, -166, +117, +55, -75, +317, -59, -558, -473, -198, -174, -157, +173, -146, -406, - +6264, +12031, +6260, -1503, -4165, -2511, +1902, +3881, -220, -2443, -428, +51, -706, -495, +320, -9, -559, -492, -144, +113, +44, -260, +228, +46, -480, -381, -268, -203, -217, +218, -114, -429, - +6813, +12945, +6569, -1945, -4848, -2783, +2188, +4127, -523, -2951, -443, +204, -809, -654, +274, +56, -695, -599, -243, +84, +37, -469, +12, +64, -398, -293, -252, -328, -254, +98, -55, -527, - +7209, +13717, +6921, -1967, -5316, -3090, +2544, +4308, -606, -3304, -485, +501, -832, -729, +300, +116, -725, -556, -280, +71, +96, -513, -156, +86, -225, -237, -54, -372, -184, +51, -70, -428, - +7996, +14426, +6885, -1822, -5825, -3511, +3030, +4353, -912, -3620, -593, +764, -830, -921, +358, +119, -837, -534, -312, +44, +63, -481, -430, -53, +49, -227, +88, -308, -219, +114, -168, -425, - +8882, +15071, +6412, -1835, -5966, -3673, +3408, +4119, -1227, -3662, -763, +899, -746, -1111, +359, +151, -942, -516, -330, +17, -16, -403, -535, -393, +280, -94, +75, -119, -291, +140, -109, -538, - +10567, +15752, +4909, -1935, -5684, -3567, +3668, +3636, -1744, -3453, -930, +847, -522, -1336, +279, +135, -969, -534, -297, -7, -169, -364, -497, -748, +299, +223, -60, +142, -209, -44, +74, -643, - +11943, +15859, +3693, -1168, -5342, -4152, +3859, +3672, -2239, -3456, -946, +824, -414, -1484, +211, +82, -987, -499, -174, -100, -272, -362, -415, -898, +70, +599, -127, +237, +12, -204, +166, -634, - +13286, +15772, +2438, -128, -4984, -4823, +4009, +3750, -2786, -3502, -799, +696, -411, -1556, +156, -56, -1055, -395, +29, -275, -377, -305, -450, -921, -205, +832, -22, +179, +243, -241, +88, -561, - +15203, +16245, +66, +61, -3930, -4645, +3668, +3392, -3154, -3516, -581, +453, -338, -1692, +34, -193, -1068, -347, +256, -295, -574, -274, -592, -759, -402, +757, +277, +182, +289, -131, -87, -538, - +17246, +16423, -2122, -11, -3063, -3961, +3236, +2949, -3315, -3590, -360, +310, -300, -1854, +21, -379, -1001, -238, +408, -257, -762, -185, -742, -584, -408, +586, +464, +330, +189, -58, -105, -673, - +19117, +17194, -4525, -594, -2040, -2928, +2541, +2436, -3174, -3955, -153, +154, -238, -2084, -35, -538, -893, -103, +477, -124, -1066, -60, -862, -458, -281, +408, +389, +584, +66, -198, +49, -844, - +20721, +17777, -6299, -1368, -1442, -1849, +1965, +2056, -2976, -4357, -43, +20, -202, -2215, -94, -626, -745, -20, +462, +11, -1311, +50, -912, -421, -24, +317, +54, +773, +39, -439, +197, -803, - +22510, +18390, -7885, -2507, -1141, -610, +1490, +1800, -2821, -4825, -36, -196, -92, -2264, -98, -656, -568, +0, +406, +174, -1496, +186, -1014, -271, +276, +136, -341, +713, +119, -602, +263, -543, - +23764, +19077, -8920, -3523, -1276, +188, +1429, +1689, -2771, -5201, -193, -427, +128, -2191, -88, -582, -532, -60, +345, +366, -1555, +193, -996, -85, +266, +23, -631, +431, +299, -553, +232, -344, - +24818, +19733, -9528, -4328, -1918, +770, +1688, +1610, -2757, -5522, -511, -645, +585, -2065, -55, -440, -631, -162, +378, +612, -1616, +251, -901, -119, +151, -32, -905, +195, +543, -403, +171, -190, - +25942, +20257, -9828, -5006, -3337, +1229, +2251, +1491, -2816, -5884, -1016, -737, +1357, -2026, +75, -408, -894, -120, +477, +717, -1428, +314, -1223, -176, -2, -219, -932, +91, +634, -169, +260, -194, - +26806, +20370, -9612, -5429, -4920, +1516, +2696, +1428, -3018, -6052, -1361, -633, +1984, -1981, +130, -617, -894, -5, +427, +963, -1258, -100, -1423, -216, -233, -165, -732, -55, +587, +188, +254, -445, - +27209, +20584, -8793, -5848, -6861, +1389, +3245, +1326, -3300, -5756, -1708, -571, +2761, -2008, -124, -572, -811, +83, +640, +854, -1485, -319, -1727, -286, -130, -52, -610, -82, +598, +192, +317, -394, - +26893, +21151, -7832, -5916, -8592, +716, +3831, +1043, -3056, -5275, -2081, -436, +3209, -2095, -313, -337, -686, +446, +148, +578, -1361, -677, -1772, -79, -85, -62, -326, -96, +288, +256, +729, -628, - +26953, +20650, -6193, -5839, -10706, -128, +4025, +1350, -2673, -4655, -2334, -397, +3476, -2270, -280, +333, -640, -63, -135, +292, -1261, -713, -1609, +24, -62, +134, -367, -195, +342, +253, +839, -600, - +26223, +20702, -4716, -5564, -12304, -1451, +4411, +2099, -2421, -4083, -2432, -598, +3439, -2035, +238, +317, -1138, -105, -496, +286, -908, -750, -1436, +173, -78, -80, -177, -33, +173, +164, +1062, -575, - +25928, +19969, -3485, -5141, -13936, -2549, +5094, +2717, -2372, -3260, -2726, -1115, +3984, -1704, +8, +193, -1565, -173, -413, +254, -689, -651, -1111, -74, -302, +129, -24, -206, +176, +212, +1048, -582, - +25176, +19122, -2331, -4479, -15530, -3096, +5493, +3261, -1783, -2862, -3340, -611, +4094, -2064, +150, -498, -1520, +306, -690, +346, -212, -559, -1329, -185, -92, +42, -36, -161, +240, +170, +921, -542, - +24482, +17970, -1385, -4296, -15673, -3520, +5482, +4107, -1928, -2384, -3245, -840, +4078, -2209, -307, -382, -1158, +232, -617, +670, -229, -809, -1085, -111, -314, +49, +102, -188, +256, +76, +933, -522, - +23018, +16730, -558, -3503, -15157, -4048, +5380, +4222, -1077, -2064, -4052, -288, +3518, -2565, -42, -296, -1035, +526, -341, +255, -181, -591, -933, -333, -446, +264, +149, -398, +317, +312, +625, -550, - +21612, +15388, -80, -2338, -14307, -4401, +4806, +4911, -877, -2366, -3925, -404, +3244, -2585, +155, -320, -674, +881, -905, +291, +164, -706, -883, -428, -426, +443, -108, -467, +714, +169, +330, -367, - +19859, +13489, +594, -510, -13595, -4524, +5021, +4279, -755, -2023, -4414, +179, +2843, -2732, +498, -73, -330, +471, -963, +610, +149, -898, -638, -432, -427, +380, -223, -282, +814, +29, +168, -248, - +17742, +12321, +1537, +213, -12421, -3676, +4012, +3612, -294, -2291, -3920, +331, +2249, -2515, +892, +37, -643, +510, -767, +581, +11, -855, -358, -539, -531, +491, -270, -331, +960, -123, +115, -173, - +16821, +11926, +463, -1706, -11380, -169, +5057, +1524, -1492, -1439, -2644, -359, +2193, -2391, +635, +125, -644, +317, -422, +472, -172, -610, -637, -211, -252, -7, -26, -80, -218, -217, +829, -147, - +13656, +11995, +3238, -883, -11164, -2746, +3338, +2611, +688, -1041, -2888, -1083, +1213, -1539, +1161, +189, -818, +634, -355, +123, +274, -342, -515, +152, -873, -436, -4, -321, +327, +309, +783, -46, - +11207, +10925, +3942, -83, -8816, -2936, +2399, +2271, +597, -709, -2174, -1110, +823, -992, +937, +143, -694, +584, -237, +52, +375, -217, -303, +162, -792, -361, +105, -252, +214, +381, +632, -19, - +9243, +9941, +4245, +657, -6939, -2976, +1760, +1792, +578, -304, -1653, -1098, +643, -655, +689, +159, -542, +528, -131, +56, +430, -13, -221, +210, -469, -319, +52, -319, +110, +335, +494, +43, - +7775, +9004, +4364, +1074, -5566, -2771, +1288, +1356, +632, -19, -1266, -996, +487, -456, +486, +214, -418, +456, -8, +91, +463, +193, -8, +266, -378, -438, -88, -360, -112, +272, +544, +30, - +6749, +8329, +4226, +1199, -4454, -2378, +954, +1109, +723, +141, -897, -819, +428, -339, +428, +308, -281, +463, +96, +331, +672, +327, +10, +166, -357, -510, -211, -393, -151, +319, +576, +31, - +5988, +7653, +4116, +1168, -3765, -2038, +743, +915, +737, +259, -623, -668, +285, -262, +442, +295, -206, +564, +342, +402, +543, +169, -105, +70, -443, -594, -251, -433, -272, +334, +502, -77, - +5467, +7250, +3847, +1019, -3110, -1692, +669, +871, +822, +369, -365, -484, +245, -133, +474, +473, +20, +567, +337, +288, +369, +117, -150, +13, -324, -594, -356, -414, -226, +271, +602, +138, - +5122, +6973, +3542, +803, -2565, -1389, +713, +947, +893, +463, -170, -291, +235, +82, +633, +446, -49, +345, +308, +240, +201, +133, -77, -9, -324, -573, -352, -476, -45, +494, +363, +16, - +4855, +6836, +3247, +531, -2098, -1103, +840, +1094, +882, +577, -75, -121, +401, +26, +454, +321, -151, +130, +276, +269, +118, +152, -61, -115, -199, -507, -538, -83, +76, +24, +184, +126, - +4686, +6807, +3020, +381, -1698, -797, +985, +1300, +880, +654, +193, -260, +327, -18, +295, +312, -217, +5, +244, +427, +89, +60, +69, +22, -305, -328, -97, -385, -172, +69, +91, +154, - +4632, +6769, +2880, +360, -1379, -630, +1032, +1346, +1008, +601, +155, -416, +54, +91, +208, +254, -266, -94, +300, +314, +94, +118, +154, -112, -59, +83, -557, -570, -137, +42, +28, +65, - +4540, +6825, +2991, +442, -1145, -666, +903, +1315, +922, +656, +103, -490, -32, -70, +234, +239, -193, -130, +219, +288, +5, +292, +9, +89, +428, -439, -684, -356, -314, -42, +168, -24, - +4565, +7043, +3133, +398, -1092, -628, +698, +1105, +781, +505, +302, -393, -208, -57, +80, +210, -27, -27, +131, +97, +164, +95, +10, +715, +96, -727, -439, -390, -341, +7, +168, -278, - +4628, +7376, +3330, +175, -1131, -632, +658, +1096, +468, +306, +276, -396, -10, -64, +41, +226, -104, +83, +212, +65, +61, -40, +256, +815, -181, -624, -368, -423, -196, +109, -11, -411, - +4617, +7799, +3588, -63, -1282, -806, +761, +1267, +342, +103, +9, -566, -109, +89, +288, +207, -102, +37, +103, +149, +142, -173, +222, +878, -329, -612, -244, -446, -47, +197, -164, -416, - +4660, +8175, +3965, -277, -1581, -952, +885, +1487, +358, +16, -136, -742, -411, +6, +351, +359, +99, +41, +67, +54, +185, -145, +158, +875, -393, -637, -194, -414, +89, +273, -162, -421, - +4721, +8663, +4296, -438, -1910, -1208, +1075, +1666, +396, -64, -205, -776, -575, -199, +234, +288, +60, +190, +260, +39, +105, -43, -107, +796, -78, -840, -343, -310, +149, +286, -89, -348, - +4872, +9194, +4729, -774, -2386, -1225, +1197, +1899, +429, -345, -152, -738, -728, -229, +85, +167, -114, +11, +383, +94, +153, +194, -391, +530, +269, -869, -518, -280, +55, +314, -13, -367, - +5066, +9823, +5271, -1107, -2955, -1313, +1335, +2152, +396, -624, -133, -598, -828, -241, +24, +42, -162, -203, +299, +184, -39, +336, -145, +68, +454, -442, -819, -503, +69, +300, -63, -319, - +5153, +10511, +6095, -1484, -3588, -1456, +1423, +2504, +356, -975, -219, -396, -876, -284, +78, -92, -239, -325, +171, +296, -163, +80, +60, -117, +343, +144, -711, -771, -136, +164, +7, -305, - +5279, +11273, +7005, -1898, -4346, -1576, +1570, +2874, +319, -1413, -325, -192, -896, -360, +145, -111, -363, -399, +61, +261, +19, -193, -117, -56, -9, +423, -34, -872, -482, +162, -138, -388, - +6056, +12428, +7017, -2743, -5015, -1595, +2092, +3133, -74, -1835, -369, -82, -887, -433, +159, -124, -505, -444, +1, +176, +57, -207, -371, -191, -116, +297, +399, -455, -738, -65, -85, -546, - +6550, +13417, +7566, -3351, -5854, -1576, +2466, +3359, -262, -2321, -480, +199, -992, -483, +233, -211, -561, -459, -82, +172, +17, -196, -387, -532, -129, +223, +460, +107, -612, -458, -73, -511, - +7012, +14309, +8271, -3829, -6828, -1561, +2860, +3523, -456, -2805, -697, +575, -1040, -702, +326, -213, -692, -488, -58, +49, +67, -318, -318, -681, -441, +323, +421, +317, -109, -624, -463, -340, - +8016, +15374, +8120, -4423, -7514, -1497, +3500, +3557, -967, -3143, -754, +778, -1014, -873, +371, -263, -697, -565, +8, +40, -65, -294, -389, -676, -607, +208, +557, +311, +173, -239, -863, -535, - +8871, +16239, +8063, -4588, -8132, -1638, +4115, +3491, -1344, -3391, -963, +994, -922, -1121, +393, -248, -762, -536, +34, +55, -184, -324, -431, -662, -593, -42, +629, +427, +134, +214, -755, -1041, - +9741, +17020, +7674, -4576, -8426, -2000, +4611, +3393, -1757, -3551, -1213, +1115, -851, -1378, +299, -217, -824, -499, +46, +51, -282, -489, -415, -741, -447, -171, +355, +641, +79, +272, -284, -1349, - +11312, +17790, +6362, -4799, -8173, -2096, +4973, +3237, -2301, -3658, -1190, +1112, -886, -1482, +185, -250, -754, -451, +117, +102, -439, -579, -427, -793, -304, +10, -28, +635, +235, +100, +114, -1241, - +13177, +18485, +4498, -4942, -7590, -1947, +5032, +3048, -2673, -3843, -991, +968, -867, -1596, +107, -286, -639, -361, +165, +232, -672, -529, -499, -772, -195, +286, -298, +389, +407, -34, +287, -927, - +15061, +19221, +2298, -5197, -6823, -1696, +4782, +2846, -2873, -4171, -778, +660, -800, -1706, -5, -268, -588, -254, +120, +358, -929, -400, -614, -747, -72, +413, -441, -8, +458, -76, +301, -594, - +17417, +19661, -218, -5533, -6027, -1097, +4358, +2666, -3090, -4449, -601, +308, -686, -1718, -14, -284, -463, -238, +124, +408, -1064, -243, -713, -651, +62, +367, -575, -268, +316, -31, +381, -321, - +19867, +19942, -2698, -5917, -5419, -356, +3943, +2529, -3315, -4768, -533, -59, -432, -1576, -30, -272, -359, -356, +203, +452, -1089, -118, -778, -495, +49, +184, -640, -440, +103, +121, +489, -151, - +22000, +20274, -4928, -6244, -5183, +422, +3657, +2387, -3535, -5191, -499, -408, +124, -1486, -39, -209, -489, -410, +327, +558, -1091, -6, -842, -510, -62, +53, -686, -527, +27, +247, +412, +120, - +23867, +20630, -6764, -6611, -5435, +1219, +3584, +2132, -3790, -5664, -426, -579, +849, -1544, +115, -346, -693, -253, +402, +737, -1086, +70, -1208, -570, -77, -89, -681, -321, -84, +142, +497, +304, - +25427, +20921, -8048, -7062, -6069, +1884, +3693, +1754, -4141, -5893, -210, -678, +1594, -1604, +126, -548, -678, -23, +392, +982, -1152, -266, -1531, -447, -207, -3, -478, -227, -288, +201, +484, +187, - +26670, +20956, -8821, -7361, -6968, +2286, +3959, +1161, -4414, -5482, -257, -597, +2261, -1827, +18, -485, -493, +96, +432, +895, -1493, -446, -1665, -450, +33, +175, -509, -150, -228, -15, +349, +351, - +27247, +21029, -9279, -7221, -8186, +2443, +4316, +433, -4111, -5058, -277, -461, +2624, -2135, +8, -68, -470, +172, -50, +659, -1495, -578, -1725, -94, +295, -26, -297, -74, -411, -173, +430, +303, - +27900, +20315, -8903, -7184, -9921, +2431, +4704, +385, -3980, -4367, -496, -493, +2857, -2265, +375, +207, -681, -337, -225, +556, -1409, -535, -1534, +66, +228, +14, -233, -136, -400, -267, +410, +268, - +27607, +19995, -8273, -6960, -11863, +2111, +5727, +206, -3669, -3625, -950, -750, +3372, -2159, +511, +126, -1428, -249, -334, +460, -1027, -385, -1566, +110, +211, -88, -105, -186, -370, -401, +501, +177, - +27622, +18453, -7307, -6733, -13561, +2585, +6218, +129, -3285, -2980, -1630, -393, +3761, -2468, +529, -441, -1518, +13, -480, +602, -588, -563, -1536, +165, +105, -89, -91, -163, -357, -425, +580, -7, - +26712, +17271, -6277, -6285, -14362, +2609, +6500, +415, -3110, -2459, -1751, -325, +3747, -2769, +192, -404, -1346, +67, -259, +700, -500, -685, -1381, +215, -122, -46, -21, -142, -291, -476, +628, -31, - +24769, +16230, -5111, -5257, -14397, +2104, +6670, +474, -2600, -1910, -2144, -355, +3491, -3153, +319, -229, -1416, +584, -304, +502, -383, -651, -1149, -88, -115, +8, -18, -89, -321, -449, +713, -173, - +22531, +14663, -3328, -4140, -13801, +1449, +6057, +1180, -2109, -1891, -2239, -499, +2901, -2827, +365, -277, -887, +650, -524, +392, -96, -669, -1178, -67, -149, -9, +23, -125, -229, -396, +626, -144, - +19895, +13154, -1419, -2898, -12707, +438, +5719, +1680, -2070, -1480, -2520, -641, +2754, -2677, +403, +34, -653, +479, -618, +548, -55, -828, -839, -119, -236, +37, -10, -61, -202, -414, +734, -50, - +15813, +10164, -3306, -1259, -4293, +3357, +1973, -2111, -1944, +200, +219, -308, +1352, -2193, +64, -117, -1126, +153, -436, +60, -137, -521, -663, +805, -86, +19, +120, -674, -64, -483, -254, +28, - +13540, +9962, -1922, -1329, -4062, +2676, +2356, -1429, -1777, +3, -53, -302, +1192, -1816, -26, +30, -863, +65, -256, +68, -137, -483, -700, +634, +28, +18, +255, -495, -126, -392, -252, -65, - +11634, +9561, -699, -1265, -3629, +2134, +2474, -898, -1471, -172, -210, -167, +1001, -1414, -33, +173, -616, +55, -78, +97, -92, -391, -629, +503, +182, +60, +264, -359, -148, -296, -278, -94, - +10116, +9195, +85, -1190, -3168, +1756, +2451, -457, -1159, -296, -215, -72, +824, -1068, +57, +267, -418, +80, +67, +164, -62, -236, -495, +399, +241, +62, +228, -255, -153, -255, -222, -97, - +8972, +8865, +505, -1212, -2723, +1474, +2315, -138, -916, -335, -184, -65, +679, -767, +131, +280, -267, +100, +140, +232, -22, -124, -428, +222, +224, +45, +106, -204, -143, -199, -220, -223, - +8143, +8550, +717, -1226, -2288, +1267, +2150, +132, -720, -286, -144, -49, +640, -554, +197, +300, -154, +119, +220, +280, -45, -61, -392, +43, +178, +16, +16, -111, -123, -218, -232, -196, - +7519, +8353, +812, -1237, -1913, +1117, +2025, +376, -587, -178, -1, -84, +685, -393, +217, +358, -60, +126, +213, +332, -84, -72, -329, -119, +124, +96, -34, -68, -117, -163, -197, -307, - +7138, +8193, +814, -1279, -1576, +1064, +1902, +541, -519, -85, +237, -175, +678, -205, +145, +407, -22, +33, +193, +347, -106, -168, -290, -132, +40, +147, -63, -81, -15, -226, -137, +56, - +6765, +8188, +868, -1293, -1277, +1051, +1806, +584, -405, -124, +486, -108, +449, +28, +127, +224, +67, +7, +73, +339, -51, -243, -320, +0, -67, +74, +55, -80, -93, +50, +86, -210, - +6619, +8107, +942, -1282, -1022, +1117, +1715, +528, -435, -63, +479, +47, +396, -99, +275, +103, -92, +116, +36, +186, -20, -104, -395, -86, -10, +55, +1, -128, +248, +155, -185, -146, - +6506, +8197, +996, -1294, -821, +1178, +1651, +459, -560, -146, +509, -44, +460, -45, +85, +200, -171, -75, +154, +145, -147, -20, -290, -290, -60, +165, -210, +91, +457, -145, -98, -174, - +6582, +8378, +972, -1387, -629, +1360, +1678, +371, -630, -289, +430, -31, +347, +59, +235, +84, -170, +9, -19, +188, -18, -105, -290, -206, -28, -83, -58, +442, +120, -133, +100, -328, - +6729, +8662, +933, -1675, -529, +1529, +1750, +352, -860, -415, +386, -244, +217, +58, +206, +210, -193, -125, +73, +64, -149, +23, -388, -231, +36, -212, +36, +433, -79, -64, +4, -312, - +6891, +9095, +950, -1986, -500, +1682, +1851, +373, -983, -585, +394, -308, -13, -44, +240, +173, -175, +14, +14, +51, -151, -125, -432, -103, +46, -279, +169, +405, -280, -72, +3, -309, - +7140, +9633, +936, -2394, -433, +1844, +1940, +403, -1165, -606, +441, -410, -140, -123, +140, +125, -224, -22, +162, +113, -243, -123, -493, -210, +171, -184, +103, +464, -305, -188, -101, -278, - +7579, +10153, +808, -2790, -377, +2061, +1958, +319, -1378, -620, +599, -484, -299, -248, +140, +31, -407, -65, +163, +117, -205, -114, -553, -215, +158, -225, +53, +587, -241, -398, -127, -310, - +8098, +10746, +572, -3255, -297, +2282, +1951, +145, -1630, -671, +779, -545, -492, -334, +56, -74, -517, -231, +85, +61, -318, -68, -487, -259, +155, -163, -275, +532, +118, -570, -295, -288, - +8856, +11416, +228, -3688, -131, +2625, +1937, -65, -1888, -582, +1023, -544, -642, -393, +130, -159, -647, -205, +40, -68, -349, -125, -337, -110, +114, -18, -391, +244, +381, -387, -478, -229, - +9699, +12034, -166, -4109, -4, +2965, +1853, -341, -2231, -485, +1256, -561, -825, -509, +157, -201, -806, -277, +86, -284, -419, -202, -354, +141, +75, -8, -343, -105, +372, -52, -654, -293, - +10543, +12775, -545, -4510, +87, +3328, +1796, -636, -2510, -451, +1566, -593, -960, -599, +156, -239, -879, -366, +153, -404, -550, -174, -435, +353, +148, -50, -279, -255, +87, +310, -515, -582, - +11200, +13601, -758, -4805, +51, +3618, +1817, -936, -2667, -529, +1865, -619, -1077, -658, +152, -290, -948, -411, +137, -381, -707, -102, -513, +487, +276, -91, -256, -301, -183, +365, -56, -808, - +11970, +14337, -1048, -4982, -71, +3819, +1800, -1224, -2823, -587, +2067, -657, -1227, -698, +129, -342, -1036, -477, +85, -363, -801, -49, -525, +479, +403, -110, -295, -362, -353, +170, +366, -705, - +12952, +14790, -1411, -4961, -192, +3874, +1746, -1433, -2954, -613, +2172, -717, -1346, -744, +115, -409, -1087, -567, +16, -356, -787, -59, -428, +439, +419, -85, -317, -480, -441, -15, +471, -306, - +13912, +15283, -1898, -4796, -288, +3733, +1713, -1566, -3031, -703, +2247, -785, -1394, -782, +55, -432, -1125, -661, -51, -311, -721, -96, -300, +422, +338, -39, -314, -612, -519, -71, +296, +100, - +14855, +15823, -2738, -4516, -305, +3389, +1721, -1674, -3103, -885, +2277, -894, -1325, -878, -28, -456, -1189, -726, -166, -184, -719, -109, -219, +375, +189, +34, -370, -698, -563, -131, +49, +307, - +16338, +16003, -4097, -3971, -183, +3015, +1641, -1812, -3218, -959, +2300, -1062, -1053, -1052, -10, -553, -1252, -711, -213, -61, -766, +9, -305, +359, +93, +64, -446, -677, -520, -329, -54, +380, - +18251, +15738, -5808, -3185, -135, +2789, +1385, -1959, -3407, -901, +2346, -1243, -701, -1229, +42, -723, -1269, -588, -226, -66, -699, +25, -533, +532, -57, +38, -378, -632, -576, -478, +14, +227, - +20108, +15232, -7463, -2359, -182, +2680, +993, -2135, -3628, -757, +2465, -1466, -282, -1397, +30, -889, -1150, -413, -456, +70, -740, -222, -483, +617, -261, +207, -289, -809, -541, -427, -149, +45, - +21818, +14565, -8920, -1528, -323, +2633, +590, -2360, -3741, -408, +2485, -1626, +257, -1681, +37, -834, -979, -443, -529, +77, -986, -154, -547, +693, -157, +334, -410, -839, -327, -606, -328, +75, - +23103, +13939, -10069, -743, -746, +2659, +301, -2677, -3587, -100, +2394, -1612, +709, -2069, +297, -672, -1108, -278, -883, +30, -908, -351, -497, +1045, -177, +252, -316, -804, -379, -726, -359, +57, - +23945, +13086, -10548, -173, -1474, +2865, +166, -2907, -3401, +227, +2202, -1525, +1022, -2203, +552, -677, -1107, -549, -996, +157, -1089, -304, -261, +1165, -310, +418, -308, -941, -344, -785, -396, +51, - +24255, +12118, -10245, -13, -2426, +3265, +310, -3204, -3183, +647, +1733, -1352, +1565, -2480, +734, -599, -1582, -399, -1019, +24, -846, -257, -242, +1202, -199, +374, -346, -1003, -311, -777, -466, +127, - +23709, +11520, -9531, -151, -3388, +3855, +550, -3483, -2789, +731, +1250, -841, +1752, -2633, +801, -862, -1583, -318, -1087, +206, -657, -373, -274, +1353, -271, +302, -334, -1032, -204, -865, -432, +235, - +22428, +11143, -8405, -496, -4041, +4230, +889, -3448, -2615, +728, +1070, -660, +1947, -2748, +486, -621, -1697, -171, -896, +282, -583, -459, -269, +1324, -316, +161, -148, -1048, -191, -833, -297, +216, - +20761, +10556, -6721, -940, -4366, +4259, +1328, -3270, -2442, +888, +519, -254, +1808, -2851, +481, -564, -1534, -32, -625, +157, -458, -456, -349, +1175, -358, +206, -68, -1025, -144, -651, -305, +204, - +18483, +10322, -5051, -1232, -4488, +3998, +1699, -2851, -2083, +521, +346, -174, +1484, -2533, +230, -316, -1365, +115, -496, +12, -245, -474, -558, +987, -219, +124, +13, -912, -46, -573, -264, +159, - +15151, +10407, -3905, -1083, -1883, +2118, +367, -2185, -1952, +542, +790, -175, +1158, -1491, +303, +11, -942, -141, -277, +14, -417, -305, -403, +619, -260, -27, +128, -617, -289, -452, -406, -109, - +12885, +10154, -2446, -1435, -1886, +1994, +819, -1625, -1685, +115, +676, -116, +950, -1122, +70, +152, -656, +7, -100, +33, -249, -339, -518, +467, -154, -64, +221, -437, -240, -376, -360, -167, - +11026, +9815, -1373, -1604, -1732, +1727, +1082, -1042, -1489, -153, +594, -120, +851, -783, -35, +259, -348, +25, +24, +163, -205, -275, -535, +286, -60, -104, +181, -299, -203, -314, -276, -258, - +9638, +9479, -718, -1704, -1525, +1463, +1205, -627, -1291, -289, +532, -105, +728, -476, -29, +265, -147, +9, +102, +238, -168, -228, -548, +99, -70, -181, +55, -160, -166, -274, -267, -318, - +8752, +9179, -333, -1745, -1266, +1296, +1255, -331, -1103, -308, +517, +6, +654, -256, +108, +218, -57, +66, +139, +248, -132, -162, -550, -73, -107, -216, +35, -13, -164, -279, -126, -282, - +8140, +9001, -85, -1784, -995, +1204, +1247, -134, -1072, -291, +536, +70, +685, -204, +206, +280, -165, +40, +195, +187, -211, -72, -529, -292, -102, -127, -36, -76, -58, -138, -270, -89, - +7821, +8936, +29, -1810, -714, +1265, +1138, -40, -1096, -420, +649, +5, +642, -11, +138, +306, -111, -183, +147, +285, -330, -213, -387, -248, -250, -102, -97, -86, +22, -265, +130, +187, - +7807, +9028, -50, -1866, -441, +1451, +1138, -195, -1118, -490, +518, +2, +517, +35, +307, +254, -153, -103, +7, +126, -244, -245, -496, -140, -99, -332, -144, +49, -205, +61, +387, -266, - +7883, +9354, -84, -2058, -175, +1671, +1191, -268, -1313, -554, +542, -204, +353, +81, +268, +401, -95, -139, +119, +41, -495, -190, -362, -215, -155, -154, -74, -308, -29, +393, -17, -331, - +8309, +9731, -294, -2293, +42, +1934, +1203, -363, -1445, -664, +574, -270, +131, -85, +298, +391, -163, +29, +228, -28, -529, -302, -516, -77, -71, -224, -65, -236, +37, +234, -157, -305, - +8910, +10272, -662, -2606, +320, +2118, +1145, -521, -1640, -652, +647, -370, -95, -197, +260, +249, -276, +1, +311, +7, -540, -340, -597, -115, -162, -109, -41, -330, +23, +288, -224, -561, - +9705, +10883, -1159, -2876, +618, +2308, +939, -735, -1827, -569, +847, -551, -252, -350, +282, +190, -540, -53, +297, -89, -533, -247, -636, +1, -229, -304, +0, -239, -164, +169, +30, -627, - +10729, +11542, -1791, -3053, +931, +2475, +682, -1055, -1976, -419, +1145, -742, -457, -399, +293, +130, -657, -195, +215, -136, -668, -129, -569, +112, -117, -431, -109, -273, -261, +33, +231, -480, - +11880, +12255, -2434, -3089, +1163, +2572, +450, -1357, -2085, -232, +1467, -892, -621, -438, +381, +30, -732, -214, +65, -162, -709, -100, -437, +212, -28, -368, -165, -380, -411, -90, +291, -64, - +13138, +12910, -3157, -3037, +1198, +2492, +221, -1627, -2212, -103, +1693, -1094, -727, -513, +408, -69, -857, -306, -36, -187, -773, -78, -367, +223, -33, -259, -244, -476, -574, -270, +164, +260, - +14397, +13579, -3910, -2827, +1090, +2265, +96, -1784, -2287, -52, +1872, -1249, -736, -567, +401, -118, -956, -410, -92, -132, -795, +4, -347, +213, -84, -122, -283, -544, -622, -487, +74, +387, - +15830, +13982, -4898, -2472, +856, +1874, -58, -1916, -2412, -69, +1931, -1394, -672, -679, +358, -219, -1064, -512, -131, -146, -801, +55, -460, +223, -217, -78, -273, -623, -663, -737, +48, +327, - +17467, +14082, -6145, -1897, +641, +1483, -284, -1985, -2544, -62, +2082, -1574, -443, -758, +315, -348, -1052, -474, -316, -34, -792, -77, -456, +264, -381, +42, -169, -787, -651, -765, -87, +204, - +19204, +13692, -7428, -1120, +469, +1144, -634, -1991, -2737, +205, +2197, -1785, +24, -916, +275, -357, -852, -637, -399, +114, -1061, -13, -468, +245, -296, +208, -219, -909, -451, -819, -365, +209, - +20434, +13223, -8548, -342, +182, +828, -924, -2160, -2725, +468, +2161, -1789, +445, -1242, +435, -205, -999, -575, -649, -4, -1029, -158, -482, +542, -293, +181, -169, -874, -514, -927, -442, +127, - +21071, +12403, -8865, +245, -318, +823, -1130, -2243, -2635, +724, +2101, -1684, +725, -1293, +626, -269, -883, -830, -829, +105, -1203, -105, -238, +653, -371, +371, -143, -1016, -532, -886, -493, +33, - +20789, +11662, -8363, +332, -911, +1138, -984, -2430, -2493, +1026, +1715, -1379, +1228, -1581, +803, -170, -1256, -633, -926, +9, -905, -125, -208, +782, -256, +313, -208, -993, -501, -825, -526, -5, - +19596, +11016, -7150, -54, -1442, +1677, -696, -2554, -2277, +940, +1405, -817, +1246, -1536, +683, -288, -1142, -645, -762, +96, -739, -256, -171, +923, -379, +209, -146, -915, -439, -791, -471, +64, - +17572, +10705, -5607, -659, -1779, +2017, -199, -2475, -2231, +834, +1110, -554, +1413, -1693, +462, -69, -1229, -319, -565, +61, -598, -344, -187, +729, -361, +89, -38, -790, -417, -617, -384, -60, - +13984, +10256, -4374, -387, +520, +1170, -465, -1651, -1737, +620, +1299, -598, +593, -692, +507, -106, -759, -500, -601, +13, -482, -134, -166, +603, -169, +64, -79, -688, -462, -543, -415, -231, - +12758, +10312, -3687, -1357, +269, +1735, +98, -1444, -1892, +26, +1171, -327, +678, -523, +452, +210, -645, -277, -231, -18, -408, -233, -377, +567, -155, -104, +72, -516, -401, -434, -355, -174, - +11716, +10426, -3021, -2050, -38, +1840, +528, -1068, -1835, -336, +989, -214, +710, -348, +420, +365, -440, -231, -16, +39, -429, -185, -574, +332, -35, -237, +26, -307, -370, -373, -221, -235, - +11134, +10529, -2662, -2372, -80, +1872, +687, -929, -1758, -466, +868, -263, +670, -278, +447, +463, -382, -147, +37, -28, -515, -135, -648, +44, +50, -206, -64, -271, -287, -346, -196, -161, - +11076, +10636, -2726, -2397, +319, +2006, +654, -1075, -1802, -427, +898, -371, +393, -170, +406, +444, -274, -100, +119, -97, -686, -259, -554, +17, -61, -105, -68, -278, -240, -444, -157, +31, - +11612, +10554, -3129, -2035, +925, +2135, +343, -1322, -1834, -253, +981, -508, +127, -339, +524, +263, -428, +146, +113, -216, -691, -403, -615, +161, -3, -171, -98, -195, -334, -500, -145, +70, - +12375, +10683, -3543, -1496, +1375, +1878, -63, -1524, -1717, +100, +1163, -755, -161, -335, +443, +21, -544, +0, +88, -275, -693, -316, -565, +159, -85, -8, -150, -333, -390, -555, -137, +51, - +13451, +11029, -4018, -1046, +1272, +1286, -363, -1499, -1496, +592, +1316, -1108, -273, -352, +296, -275, -635, -344, -118, -175, -740, -127, -424, +105, -164, +113, -179, -568, -399, -627, -271, +78, - +14734, +11620, -4801, -879, +695, +632, -389, -1350, -1318, +1007, +1424, -1500, -74, -505, -4, -282, -945, -711, -166, -124, -792, +39, -391, +77, -83, +118, -326, -662, -381, -867, -393, +164, - +15985, +11630, -5516, -532, +203, +208, -354, -1214, -1189, +1341, +1442, -1615, +268, -744, -62, -254, -1183, -868, -259, -11, -770, +72, -261, +154, +24, +109, -377, -617, -514, -967, -389, +157, - +16167, +11377, -5735, -169, +103, +84, -542, -1299, -1254, +1362, +1544, -1556, +422, -670, -103, -248, -1015, -1157, -377, +88, -926, +197, -106, +201, +13, +190, -354, -777, -517, -923, -458, +58, - +15468, +10670, -5248, +0, +346, +442, -693, -1493, -1502, +1200, +1468, -1209, +625, -745, +218, -158, -964, -895, -623, +11, -688, +93, -44, +335, -4, +183, -327, -757, -508, -710, -494, -108, - +11864, +10338, -3078, -621, +1333, +599, -268, -1143, -1003, +1077, +1244, -764, -17, -195, +147, -60, -510, -758, -332, -70, -719, -16, -15, +316, +287, +250, -355, -886, -747, -549, -54, +215, -}; - -/* HRIR Delays */ -static const ALubyte defaultDelays[828] = -{ - 12, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, -}; - -/* Default HRTF Definition */ -static const struct Hrtf DefaultHrtf = { - 44100, 32, 19, defaultAzCount, defaultEvOffset, - defaultCoeffs, defaultDelays, NULL -}; diff --git a/Alc/midi/base.h b/Alc/midi/base.h index 4d13a054..42a4c279 100644 --- a/Alc/midi/base.h +++ b/Alc/midi/base.h @@ -80,7 +80,6 @@ struct MidiSynthVtable { ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids); void (*const setGain)(MidiSynth *self, ALfloat gain); - void (*const setState)(MidiSynth *self, ALenum state); void (*const stop)(MidiSynth *self); void (*const reset)(MidiSynth *self); @@ -88,26 +87,25 @@ struct MidiSynthVtable { void (*const update)(MidiSynth *self, ALCdevice *device); void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); - void (*const Delete)(MidiSynth *self); + void (*const Delete)(void *ptr); }; #define DEFINE_MIDISYNTH_VTABLE(T) \ DECLARE_THUNK(T, MidiSynth, void, Destruct) \ DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \ DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \ -DECLARE_THUNK1(T, MidiSynth, void, setState, ALenum) \ DECLARE_THUNK(T, MidiSynth, void, stop) \ DECLARE_THUNK(T, MidiSynth, void, reset) \ DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \ DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \ -DECLARE_THUNK(T, MidiSynth, void, Delete) \ +static void T##_MidiSynth_Delete(void *ptr) \ +{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \ \ static const struct MidiSynthVtable T##_MidiSynth_vtable = { \ T##_MidiSynth_Destruct, \ \ T##_MidiSynth_selectSoundfonts, \ T##_MidiSynth_setGain, \ - T##_MidiSynth_setState, \ T##_MidiSynth_stop, \ T##_MidiSynth_reset, \ T##_MidiSynth_update, \ @@ -117,6 +115,7 @@ static const struct MidiSynthVtable T##_MidiSynth_vtable = { \ } +MidiSynth *SSynth_create(ALCdevice *device); MidiSynth *FSynth_create(ALCdevice *device); MidiSynth *DSynth_create(ALCdevice *device); diff --git a/Alc/midi/dummy.c b/Alc/midi/dummy.c index 79f82b87..d50b8fef 100644 --- a/Alc/midi/dummy.c +++ b/Alc/midi/dummy.c @@ -22,12 +22,11 @@ static void DSynth_Construct(DSynth *self, ALCdevice *device); static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct) static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat) -static DECLARE_FORWARD1(DSynth, MidiSynth, void, setState, ALenum) static DECLARE_FORWARD(DSynth, MidiSynth, void, stop) static DECLARE_FORWARD(DSynth, MidiSynth, void, reset) static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*) static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); -static void DSynth_Delete(DSynth *self); +DECLARE_DEFAULT_ALLOCATORS(DSynth) DEFINE_MIDISYNTH_VTABLE(DSynth); @@ -63,20 +62,15 @@ static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*r } -static void DSynth_Delete(DSynth *self) -{ - free(self); -} - - MidiSynth *DSynth_create(ALCdevice *device) { - DSynth *synth = calloc(1, sizeof(*synth)); + DSynth *synth = DSynth_New(sizeof(*synth)); if(!synth) { ERR("Failed to allocate DSynth\n"); return NULL; } + memset(synth, 0, sizeof(*synth)); DSynth_Construct(synth, device); return STATIC_CAST(MidiSynth, synth); } diff --git a/Alc/midi/fluidsynth.c b/Alc/midi/fluidsynth.c index d4e594e6..b4788635 100644 --- a/Alc/midi/fluidsynth.c +++ b/Alc/midi/fluidsynth.c @@ -470,13 +470,12 @@ static void FSynth_Destruct(FSynth *self); static ALboolean FSynth_init(FSynth *self, ALCdevice *device); static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids); static void FSynth_setGain(FSynth *self, ALfloat gain); -static void FSynth_setState(FSynth *self, ALenum state); static void FSynth_stop(FSynth *self); static void FSynth_reset(FSynth *self); static void FSynth_update(FSynth *self, ALCdevice *device); static void FSynth_processQueue(FSynth *self, ALuint64 time); static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); -static void FSynth_Delete(FSynth *self); +DECLARE_DEFAULT_ALLOCATORS(FSynth) DEFINE_MIDISYNTH_VTABLE(FSynth); static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename); @@ -632,11 +631,6 @@ static void FSynth_setGain(FSynth *self, ALfloat gain) } -static void FSynth_setState(FSynth *self, ALenum state) -{ - MidiSynth_setState(STATIC_CAST(MidiSynth, self), state); -} - static void FSynth_stop(FSynth *self) { MidiSynth *synth = STATIC_CAST(MidiSynth, self); @@ -798,20 +792,15 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict } -static void FSynth_Delete(FSynth *self) -{ - free(self); -} - - MidiSynth *FSynth_create(ALCdevice *device) { - FSynth *synth = calloc(1, sizeof(*synth)); + FSynth *synth = FSynth_New(sizeof(*synth)); if(!synth) { ERR("Failed to allocate FSynth\n"); return NULL; } + memset(synth, 0, sizeof(*synth)); FSynth_Construct(synth, device); if(FSynth_init(synth, device) == AL_FALSE) diff --git a/Alc/midi/sf2load.c b/Alc/midi/sf2load.c index 169a5189..2bc94133 100644 --- a/Alc/midi/sf2load.c +++ b/Alc/midi/sf2load.c @@ -50,9 +50,10 @@ static void skip(Reader *stream, ALuint amt) size_t got; got = READ(stream, buf, minu(sizeof(buf), amt)); - if(got == 0) READERR(stream) = 1; + if(got == 0 || got > amt) + READERR(stream) = 1; - amt -= got; + amt -= (ALuint)got; } } @@ -324,61 +325,46 @@ static void RiffHdr_read(RiffHdr *self, Reader *stream) } -typedef struct GenModList { - Generator *gens; - ALsizei gens_size; - ALsizei gens_max; +DECL_VECTOR(Generator) +DECL_VECTOR(Modulator) - Modulator *mods; - ALsizei mods_size; - ALsizei mods_max; +typedef struct GenModList { + vector_Generator gens; + vector_Modulator mods; } GenModList; static void GenModList_Construct(GenModList *self) { - self->gens = NULL; - self->gens_size = 0; - self->gens_max = 0; - - self->mods = NULL; - self->mods_size = 0; - self->mods_max = 0; + VECTOR_INIT(self->gens); + VECTOR_INIT(self->mods); } static void GenModList_Destruct(GenModList *self) { - free(self->gens); - self->gens = NULL; - self->gens_size = 0; - self->gens_max = 0; - - free(self->mods); - self->mods = NULL; - self->mods_size = 0; - self->mods_max = 0; + VECTOR_DEINIT(self->mods); + VECTOR_DEINIT(self->gens); } static GenModList GenModList_clone(const GenModList *self) { GenModList ret; - ret.gens = malloc(self->gens_max * sizeof(ret.gens[0])); - memcpy(ret.gens, self->gens, self->gens_size * sizeof(ret.gens[0])); - ret.gens_size = self->gens_size; - ret.gens_max = self->gens_max; + GenModList_Construct(&ret); - ret.mods = malloc(self->mods_max * sizeof(ret.mods[0])); - memcpy(ret.mods, self->mods, self->mods_size * sizeof(ret.mods[0])); - ret.mods_size = self->mods_size; - ret.mods_max = self->mods_max; + VECTOR_INSERT(ret.gens, VECTOR_ITER_END(ret.gens), + VECTOR_ITER_BEGIN(self->gens), VECTOR_ITER_END(self->gens) + ); + VECTOR_INSERT(ret.mods, VECTOR_ITER_END(ret.mods), + VECTOR_ITER_BEGIN(self->mods), VECTOR_ITER_END(self->mods) + ); return ret; } static void GenModList_insertGen(GenModList *self, const Generator *gen, ALboolean ispreset) { - Generator *i = self->gens; - Generator *end = i + self->gens_size; + Generator *i = VECTOR_ITER_BEGIN(self->gens); + Generator *end = VECTOR_ITER_END(self->gens); for(;i != end;i++) { if(i->mGenerator == gen->mGenerator) @@ -396,32 +382,16 @@ static void GenModList_insertGen(GenModList *self, const Generator *gen, ALboole gen->mGenerator == 58)) return; - if(self->gens_size == self->gens_max) + if(VECTOR_PUSH_BACK(self->gens, *gen) == AL_FALSE) { - void *temp = NULL; - ALsizei newsize; - - newsize = (self->gens_max ? self->gens_max<<1 : 1); - if(newsize > self->gens_max) - temp = realloc(self->gens, newsize * sizeof(self->gens[0])); - if(!temp) - { - ERR("Failed to increase generator storage to %d elements (from %d)\n", - newsize, self->gens_max); - return; - } - - self->gens = temp; - self->gens_max = newsize; + ERR("Failed to insert generator (from %d elements)\n", VECTOR_SIZE(self->gens)); + return; } - - self->gens[self->gens_size] = *gen; - self->gens_size++; } static void GenModList_accumGen(GenModList *self, const Generator *gen) { - Generator *i = self->gens; - Generator *end = i + self->gens_size; + Generator *i = VECTOR_ITER_BEGIN(self->gens); + Generator *end = VECTOR_ITER_END(self->gens); for(;i != end;i++) { if(i->mGenerator == gen->mGenerator) @@ -441,35 +411,19 @@ static void GenModList_accumGen(GenModList *self, const Generator *gen) } } - if(self->gens_size == self->gens_max) + if(VECTOR_PUSH_BACK(self->gens, *gen) == AL_FALSE) { - void *temp = NULL; - ALsizei newsize; - - newsize = (self->gens_max ? self->gens_max<<1 : 1); - if(newsize > self->gens_max) - temp = realloc(self->gens, newsize * sizeof(self->gens[0])); - if(!temp) - { - ERR("Failed to increase generator storage to %d elements (from %d)\n", - newsize, self->gens_max); - return; - } - - self->gens = temp; - self->gens_max = newsize; + ERR("Failed to insert generator (from %d elements)\n", VECTOR_SIZE(self->gens)); + return; } - - self->gens[self->gens_size] = *gen; if(gen->mGenerator < 60) - self->gens[self->gens_size].mAmount += DefaultGenValue[gen->mGenerator]; - self->gens_size++; + VECTOR_BACK(self->gens).mAmount += DefaultGenValue[gen->mGenerator]; } static void GenModList_insertMod(GenModList *self, const Modulator *mod) { - Modulator *i = self->mods; - Modulator *end = i + self->mods_size; + Modulator *i = VECTOR_ITER_BEGIN(self->mods); + Modulator *end = VECTOR_ITER_END(self->mods); for(;i != end;i++) { if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp && @@ -480,32 +434,16 @@ static void GenModList_insertMod(GenModList *self, const Modulator *mod) } } - if(self->mods_size == self->mods_max) + if(VECTOR_PUSH_BACK(self->mods, *mod) == AL_FALSE) { - void *temp = NULL; - ALsizei newsize; - - newsize = (self->mods_max ? self->mods_max<<1 : 1); - if(newsize > self->mods_max) - temp = realloc(self->mods, newsize * sizeof(self->mods[0])); - if(!temp) - { - ERR("Failed to increase modulator storage to %d elements (from %d)\n", - newsize, self->mods_max); - return; - } - - self->mods = temp; - self->mods_max = newsize; + ERR("Failed to insert modulator (from %d elements)\n", VECTOR_SIZE(self->mods)); + return; } - - self->mods[self->mods_size] = *mod; - self->mods_size++; } static void GenModList_accumMod(GenModList *self, const Modulator *mod) { - Modulator *i = self->mods; - Modulator *end = i + self->mods_size; + Modulator *i = VECTOR_ITER_BEGIN(self->mods); + Modulator *end = VECTOR_ITER_END(self->mods); for(;i != end;i++) { if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp && @@ -516,47 +454,32 @@ static void GenModList_accumMod(GenModList *self, const Modulator *mod) } } - if(self->mods_size == self->mods_max) + if(VECTOR_PUSH_BACK(self->mods, *mod) == AL_FALSE) { - void *temp = NULL; - ALsizei newsize; - - newsize = (self->mods_max ? self->mods_max<<1 : 1); - if(newsize > self->mods_max) - temp = realloc(self->mods, newsize * sizeof(self->mods[0])); - if(!temp) - { - ERR("Failed to increase modulator storage to %d elements (from %d)\n", - newsize, self->mods_max); - return; - } - - self->mods = temp; - self->mods_max = newsize; + ERR("Failed to insert modulator (from %d elements)\n", VECTOR_SIZE(self->mods)); + return; } - self->mods[self->mods_size] = *mod; if(mod->mSrcOp == 0x0502 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 960; + VECTOR_BACK(self->mods).mAmount += 960; else if(mod->mSrcOp == 0x0102 && mod->mDstOp == 8 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += -2400; + VECTOR_BACK(self->mods).mAmount += -2400; else if(mod->mSrcOp == 0x000D && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 50; + VECTOR_BACK(self->mods).mAmount += 50; else if(mod->mSrcOp == 0x0081 && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 50; + VECTOR_BACK(self->mods).mAmount += 50; else if(mod->mSrcOp == 0x0582 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 960; + VECTOR_BACK(self->mods).mAmount += 960; else if(mod->mSrcOp == 0x028A && mod->mDstOp == 17 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 1000; + VECTOR_BACK(self->mods).mAmount += 1000; else if(mod->mSrcOp == 0x058B && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 960; + VECTOR_BACK(self->mods).mAmount += 960; else if(mod->mSrcOp == 0x00DB && mod->mDstOp == 16 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 200; + VECTOR_BACK(self->mods).mAmount += 200; else if(mod->mSrcOp == 0x00DD && mod->mDstOp == 15 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 200; + VECTOR_BACK(self->mods).mAmount += 200; /*else if(mod->mSrcOp == 0x020E && mod->mDstOp == ?initialpitch? && mod->mAmtSrcOp == 0x0010 && mod->mTransOp == 0) - self->mods[self->mods_size].mAmount += 12700;*/ - self->mods_size++; + VECTOR_BACK(self->mods).mAmount += 12700;*/ } @@ -710,21 +633,21 @@ static ALboolean ensureFontSanity(const Soundfont *sfont) static ALboolean checkZone(const GenModList *zone, const PresetHeader *preset, const InstrumentHeader *inst, const SampleHeader *samp) { - ALsizei i; - - for(i = 0;i < zone->gens_size;i++) + Generator *gen = VECTOR_ITER_BEGIN(zone->gens); + Generator *gen_end = VECTOR_ITER_END(zone->gens); + for(;gen != gen_end;gen++) { - if(zone->gens[i].mGenerator == 43 || zone->gens[i].mGenerator == 44) + if(gen->mGenerator == 43 || gen->mGenerator == 44) { - int high = zone->gens[i].mAmount>>8; - int low = zone->gens[i].mAmount&0xff; + int high = gen->mAmount>>8; + int low = gen->mAmount&0xff; if(!(low >= 0 && high <= 127 && high >= low)) { TRACE("Preset \"%s\", inst \"%s\", sample \"%s\": invalid %s range %d...%d\n", preset->mName, inst->mName, samp->mName, - (zone->gens[i].mGenerator == 43) ? "key" : - (zone->gens[i].mGenerator == 44) ? "velocity" : "(unknown)", + (gen->mGenerator == 43) ? "key" : + (gen->mGenerator == 44) ? "velocity" : "(unknown)", low, high); return AL_FALSE; } @@ -871,43 +794,43 @@ static void fillZone(ALfontsound *sound, ALCcontext *context, const GenModList * 0, /* 59 - */ }; const Generator *gen, *gen_end; + const Modulator *mod, *mod_end; - if(zone->mods) + mod = VECTOR_ITER_BEGIN(zone->mods); + mod_end = VECTOR_ITER_END(zone->mods); + for(;mod != mod_end;mod++) { - ALsizei i; - for(i = 0;i < zone->mods_size;i++) + ALenum src0in = getModSrcInput(mod->mSrcOp&0xFF); + ALenum src0type = getModSrcType(mod->mSrcOp&0x0300); + ALenum src0form = getModSrcForm(mod->mSrcOp&0xFC00); + ALenum src1in = getModSrcInput(mod->mAmtSrcOp&0xFF); + ALenum src1type = getModSrcType(mod->mAmtSrcOp&0x0300); + ALenum src1form = getModSrcForm(mod->mAmtSrcOp&0xFC00); + ALenum trans = getModTransOp(mod->mTransOp); + ALenum dst = (mod->mDstOp < 60) ? Gen2Param[mod->mDstOp] : 0; + if(!dst || dst == AL_KEY_RANGE_SOFT || dst == AL_VELOCITY_RANGE_SOFT || + dst == AL_LOOP_MODE_SOFT || dst == AL_EXCLUSIVE_CLASS_SOFT || + dst == AL_BASE_KEY_SOFT) + ERR("Unhandled modulator destination: %d\n", mod->mDstOp); + else if(src0in != AL_INVALID && src0form != AL_INVALID && src0type != AL_INVALID && + src1in != AL_INVALID && src1form != AL_INVALID && src0type != AL_INVALID && + trans != AL_INVALID) { - ALenum src0in = getModSrcInput(zone->mods[i].mSrcOp&0xFF); - ALenum src0type = getModSrcType(zone->mods[i].mSrcOp&0x0300); - ALenum src0form = getModSrcForm(zone->mods[i].mSrcOp&0xFC00); - ALenum src1in = getModSrcInput(zone->mods[i].mAmtSrcOp&0xFF); - ALenum src1type = getModSrcType(zone->mods[i].mAmtSrcOp&0x0300); - ALenum src1form = getModSrcForm(zone->mods[i].mAmtSrcOp&0xFC00); - ALenum trans = getModTransOp(zone->mods[i].mTransOp); - ALenum dst = (zone->mods[i].mDstOp < 60) ? Gen2Param[zone->mods[i].mDstOp] : 0; - if(!dst || dst == AL_KEY_RANGE_SOFT || dst == AL_VELOCITY_RANGE_SOFT || - dst == AL_LOOP_MODE_SOFT || dst == AL_EXCLUSIVE_CLASS_SOFT || - dst == AL_BASE_KEY_SOFT) - ERR("Unhandled modulator destination: %d\n", zone->mods[i].mDstOp); - else if(src0in != AL_INVALID && src0form != AL_INVALID && src0type != AL_INVALID && - src1in != AL_INVALID && src1form != AL_INVALID && src0type != AL_INVALID && - trans != AL_INVALID) - { - ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_INPUT_SOFT, src0in); - ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_TYPE_SOFT, src0type); - ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_FORM_SOFT, src0form); - ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_INPUT_SOFT, src1in); - ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_TYPE_SOFT, src1type); - ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_FORM_SOFT, src1form); - ALfontsound_setModStagei(sound, context, i, AL_AMOUNT_SOFT, zone->mods[i].mAmount); - ALfontsound_setModStagei(sound, context, i, AL_TRANSFORM_OP_SOFT, trans); - ALfontsound_setModStagei(sound, context, i, AL_DESTINATION_SOFT, dst); - } + ALsizei idx = (ALsizei)(mod - VECTOR_ITER_BEGIN(zone->mods)); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_INPUT_SOFT, src0in); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_TYPE_SOFT, src0type); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_FORM_SOFT, src0form); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_INPUT_SOFT, src1in); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_TYPE_SOFT, src1type); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_FORM_SOFT, src1form); + ALfontsound_setModStagei(sound, context, idx, AL_AMOUNT_SOFT, mod->mAmount); + ALfontsound_setModStagei(sound, context, idx, AL_TRANSFORM_OP_SOFT, trans); + ALfontsound_setModStagei(sound, context, idx, AL_DESTINATION_SOFT, dst); } } - gen = zone->gens; - gen_end = gen + zone->gens_size; + gen = VECTOR_ITER_BEGIN(zone->gens); + gen_end = VECTOR_ITER_END(zone->gens); for(;gen != gen_end;gen++) { ALint value = (ALshort)gen->mAmount; @@ -1019,8 +942,8 @@ static void processInstrument(ALfontsound ***sounds, ALsizei *sounds_size, ALCco temp = realloc(*sounds, (zone_end-zone + *sounds_size)*sizeof((*sounds)[0])); if(!temp) { - ERR("Failed reallocating fontsound storage to %ld elements (from %d)\n", - (zone_end-zone + *sounds_size), *sounds_size); + ERR("Failed reallocating fontsound storage to %d elements (from %d)\n", + (ALsizei)(zone_end-zone) + *sounds_size, *sounds_size); return; } *sounds = temp; @@ -1049,13 +972,13 @@ static void processInstrument(ALfontsound ***sounds, ALsizei *sounds_size, ALCco } samp = &sfont->shdr[gen->mAmount]; - gen = pzone->gens; - gen_end = gen + pzone->gens_size; + gen = VECTOR_ITER_BEGIN(pzone->gens); + gen_end = VECTOR_ITER_END(pzone->gens); for(;gen != gen_end;gen++) GenModList_accumGen(&lzone, gen); - mod = pzone->mods; - mod_end = mod + pzone->mods_size; + mod = VECTOR_ITER_BEGIN(pzone->mods); + mod_end = VECTOR_ITER_END(pzone->mods); for(;mod != mod_end;mod++) GenModList_accumMod(&lzone, mod); @@ -1156,6 +1079,8 @@ ALboolean loadSf2(Reader *stream, ALsoundfont *soundfont, ALCcontext *context) TRACE("SF2 ROM ID: %s\n", sfont.irom); } } + else + TRACE("Skipping INFO sub-chunk '%c%c%c%c' (%u bytes)\n", FOURCCARGS(info.mCode), info.mSize); list.mSize -= info.mSize; skip(stream, info.mSize); } diff --git a/Alc/midi/soft.c b/Alc/midi/soft.c new file mode 100644 index 00000000..7102ff7a --- /dev/null +++ b/Alc/midi/soft.c @@ -0,0 +1,141 @@ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "alMain.h" +#include "alError.h" +#include "evtqueue.h" +#include "alu.h" + +#include "midi/base.h" + + +typedef struct SSynth { + DERIVE_FROM_TYPE(MidiSynth); +} SSynth; + +static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); + +static void SSynth_Construct(SSynth *self, ALCdevice *device); +static void SSynth_Destruct(SSynth *self); +static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) +static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat) +static DECLARE_FORWARD(SSynth, MidiSynth, void, stop) +static DECLARE_FORWARD(SSynth, MidiSynth, void, reset) +static void SSynth_update(SSynth *self, ALCdevice *device); +static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); +DECLARE_DEFAULT_ALLOCATORS(SSynth) +DEFINE_MIDISYNTH_VTABLE(SSynth); + + +static void SSynth_Construct(SSynth *self, ALCdevice *device) +{ + MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device); + SET_VTABLE2(SSynth, MidiSynth, self); +} + +static void SSynth_Destruct(SSynth* UNUSED(self)) +{ +} + + +static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device)) +{ +} + + +static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + (void)DryBuffer; +} + + +static void SSynth_processQueue(SSynth *self, ALuint64 time) +{ + EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue; + + while(queue->pos < queue->size && queue->events[queue->pos].time <= time) + queue->pos++; +} + +static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALenum state = synth->State; + ALuint64 curtime; + ALuint total = 0; + + if(state == AL_INITIAL) + return; + if(state != AL_PLAYING) + { + SSynth_mixSamples(self, SamplesToDo, DryBuffer); + return; + } + + curtime = MidiSynth_getTime(synth); + while(total < SamplesToDo) + { + ALuint64 time, diff; + ALint tonext; + + time = MidiSynth_getNextEvtTime(synth); + diff = maxu64(time, curtime) - curtime; + if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX) + { + /* If there's no pending event, or if it's more than 1 second + * away, do as many samples as we can. */ + tonext = INT_MAX; + } + else + { + /* Figure out how many samples until the next event. */ + tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES); + tonext -= total; + /* For efficiency reasons, try to mix a multiple of 64 samples + * (~1ms @ 44.1khz) before processing the next event. */ + tonext = (tonext+63) & ~63; + } + + if(tonext > 0) + { + ALuint todo = mini(tonext, SamplesToDo-total); + SSynth_mixSamples(self, todo, DryBuffer); + total += todo; + tonext -= todo; + } + if(total < SamplesToDo && tonext <= 0) + SSynth_processQueue(self, time); + } + + synth->SamplesDone += SamplesToDo; + synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES; + synth->SamplesDone %= synth->SampleRate; +} + + +MidiSynth *SSynth_create(ALCdevice *device) +{ + SSynth *synth; + + /* This option is temporary. Once this synth is in a more usable state, a + * more generic selector should be used. */ + if(!GetConfigValueBool("midi", "internal-synth", 0)) + { + TRACE("Not using internal MIDI synth\n"); + return NULL; + } + + synth = SSynth_New(sizeof(*synth)); + if(!synth) + { + ERR("Failed to allocate SSynth\n"); + return NULL; + } + SSynth_Construct(synth, device); + return STATIC_CAST(MidiSynth, synth); +} diff --git a/Alc/mixer.c b/Alc/mixer.c index acd6c610..6a4abfc6 100644 --- a/Alc/mixer.c +++ b/Alc/mixer.c @@ -37,6 +37,9 @@ #include "bs2b.h" +extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size); + + static inline ALfloat Sample_ALbyte(ALbyte val) { return val * (1.0f/127.0f); } @@ -84,21 +87,44 @@ static void SilenceData(ALfloat *dst, ALuint samples) } -static void DoFilter(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, - ALuint numsamples) +static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter, + ALfloat *restrict dst, const ALfloat *restrict src, + ALuint numsamples, enum ActiveFilters type) { ALuint i; - for(i = 0;i < numsamples;i++) - dst[i] = ALfilterState_processSingle(filter, src[i]); - dst[i] = ALfilterState_processSingleC(filter, src[i]); + switch(type) + { + case AF_None: + break; + + case AF_LowPass: + ALfilterState_process(lpfilter, dst, src, numsamples); + return dst; + case AF_HighPass: + ALfilterState_process(hpfilter, dst, src, numsamples); + return dst; + + case AF_BandPass: + for(i = 0;i < numsamples;) + { + ALfloat temp[64]; + ALuint todo = minu(64, numsamples-i); + + ALfilterState_process(lpfilter, temp, src+i, todo); + ALfilterState_process(hpfilter, dst+i, temp, todo); + i += todo; + } + return dst; + } + return src; } -ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) +ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo) { + ALsource *Source = src->Source; ALbufferlistitem *BufferListItem; ALuint DataPosInt, DataPosFrac; - ALuint BuffersPlayed; ALboolean Looping; ALuint increment; enum Resampler Resampler; @@ -110,20 +136,17 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) ALuint chan, j; /* Get source info */ - State = Source->state; - BuffersPlayed = Source->BuffersPlayed; - DataPosInt = Source->position; - DataPosFrac = Source->position_fraction; - Looping = Source->Looping; - increment = Source->Params.Step; - Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler; - NumChannels = Source->NumChannels; - SampleSize = Source->SampleSize; + State = Source->state; + BufferListItem = Source->current_buffer; + DataPosInt = Source->position; + DataPosFrac = Source->position_fraction; + Looping = Source->Looping; + increment = src->Step; + Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler; + NumChannels = Source->NumChannels; + SampleSize = Source->SampleSize; /* Get current buffer queue item */ - BufferListItem = Source->queue; - for(j = 0;j < BuffersPlayed;j++) - BufferListItem = BufferListItem->next; OutPos = 0; do { @@ -132,7 +155,7 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) ALuint SrcBufferSize, DstBufferSize; /* Figure out how many buffer samples will be needed */ - DataSize64 = SamplesToDo-OutPos+1; + DataSize64 = SamplesToDo-OutPos; DataSize64 *= increment; DataSize64 += DataPosFrac+FRACTIONMASK; DataSize64 >>= FRACTIONBITS; @@ -144,7 +167,6 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) DataSize64 = SrcBufferSize; DataSize64 -= BufferPadding+BufferPrePadding; DataSize64 <<= FRACTIONBITS; - DataSize64 -= increment; DataSize64 -= DataPosFrac; DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment); @@ -157,13 +179,13 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) for(chan = 0;chan < NumChannels;chan++) { - ALfloat *SrcData = Device->SampleData1; - ALfloat *ResampledData = Device->SampleData2; + const ALfloat *ResampledData; + ALfloat *SrcData = Device->SourceData; ALuint SrcDataSize = 0; if(Source->SourceType == AL_STATIC) { - const ALbuffer *ALBuffer = Source->queue->buffer; + const ALbuffer *ALBuffer = BufferListItem->buffer; const ALubyte *Data = ALBuffer->data; ALuint DataSize; ALuint pos; @@ -256,7 +278,15 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) pos = BufferPrePadding - DataPosInt; while(pos > 0) { - if(!tmpiter->prev && !Looping) + ALbufferlistitem *prev; + if((prev=tmpiter->prev) != NULL) + tmpiter = prev; + else if(Looping) + { + while(tmpiter->next) + tmpiter = tmpiter->next; + } + else { ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos); @@ -267,14 +297,6 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) break; } - if(tmpiter->prev) - tmpiter = tmpiter->prev; - else - { - while(tmpiter->next) - tmpiter = tmpiter->next; - } - if(tmpiter->buffer) { if((ALuint)tmpiter->buffer->SampleLen > pos) @@ -322,38 +344,57 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) } /* Now resample, then filter and mix to the appropriate outputs. */ - Source->Params.Resample(&SrcData[BufferPrePadding], DataPosFrac, - increment, ResampledData, DstBufferSize); - + ResampledData = src->Resample( + &SrcData[BufferPrePadding], DataPosFrac, increment, + Device->ResampledData, DstBufferSize + ); { - DirectParams *directparms = &Source->Params.Direct; - - DoFilter(&directparms->LpFilter[chan], SrcData, ResampledData, - DstBufferSize); - Source->Params.DryMix(directparms, SrcData, chan, OutPos, - SamplesToDo, DstBufferSize); + DirectParams *parms = &src->Direct; + const ALfloat *samples; + + samples = DoFilters( + &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, + Device->FilteredData, ResampledData, DstBufferSize, + parms->Filters[chan].ActiveType + ); + if(!src->IsHrtf) + src->Dry.Mix(parms->OutBuffer, samples, &parms->Mix.Gains[chan], + parms->Counter, OutPos, DstBufferSize); + else + src->Dry.HrtfMix( + parms->OutBuffer, samples, parms->Counter, src->Offset, + OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan], + &parms->Mix.Hrtf.State[chan], DstBufferSize + ); } for(j = 0;j < Device->NumAuxSends;j++) { - SendParams *sendparms = &Source->Params.Send[j]; - if(!sendparms->OutBuffer) + SendParams *parms = &src->Send[j]; + const ALfloat *samples; + + if(!parms->OutBuffer) continue; - DoFilter(&sendparms->LpFilter[chan], SrcData, ResampledData, - DstBufferSize); - Source->Params.WetMix(sendparms, SrcData, OutPos, - SamplesToDo, DstBufferSize); + samples = DoFilters( + &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, + Device->FilteredData, ResampledData, DstBufferSize, + parms->Filters[chan].ActiveType + ); + src->WetMix(parms->OutBuffer, samples, &parms->Gain, + parms->Counter, OutPos, DstBufferSize); } } /* Update positions */ - for(j = 0;j < DstBufferSize;j++) - { - DataPosFrac += increment; - DataPosInt += DataPosFrac>>FRACTIONBITS; - DataPosFrac &= FRACTIONMASK; - } + DataPosFrac += increment*DstBufferSize; + DataPosInt += DataPosFrac>>FRACTIONBITS; + DataPosFrac &= FRACTIONMASK; + OutPos += DstBufferSize; + src->Offset += DstBufferSize; + src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize; + for(j = 0;j < Device->NumAuxSends;j++) + src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize; /* Handle looping sources */ while(1) @@ -374,6 +415,7 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) if(Looping && Source->SourceType == AL_STATIC) { + assert(LoopEnd > LoopStart); DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; break; } @@ -382,20 +424,13 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) break; if(BufferListItem->next) - { BufferListItem = BufferListItem->next; - BuffersPlayed++; - } else if(Looping) - { BufferListItem = Source->queue; - BuffersPlayed = 0; - } else { State = AL_STOPPED; - BufferListItem = Source->queue; - BuffersPlayed = Source->BuffersInQueue; + BufferListItem = NULL; DataPosInt = 0; DataPosFrac = 0; break; @@ -407,15 +442,7 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) /* Update source info */ Source->state = State; - Source->BuffersPlayed = BuffersPlayed; + Source->current_buffer = BufferListItem; Source->position = DataPosInt; Source->position_fraction = DataPosFrac; - Source->Hrtf.Offset += OutPos; - if(State == AL_PLAYING) - Source->Hrtf.Counter = maxu(Source->Hrtf.Counter, OutPos) - OutPos; - else - { - Source->Hrtf.Counter = 0; - Source->Hrtf.Moving = AL_FALSE; - } } diff --git a/Alc/mixer_c.c b/Alc/mixer_c.c index 36d8bf5a..6dd01e7d 100644 --- a/Alc/mixer_c.c +++ b/Alc/mixer_c.c @@ -15,28 +15,33 @@ static inline ALfloat lerp32(const ALfloat *vals, ALuint frac) static inline ALfloat cubic32(const ALfloat *vals, ALuint frac) { return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); } -void Resample_copy32_C(const ALfloat *data, ALuint UNUSED(frac), - ALuint increment, ALfloat *restrict OutBuffer, ALuint BufferSize) +const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac), + ALuint increment, ALfloat *restrict dst, ALuint numsamples) { assert(increment==FRACTIONONE); - memcpy(OutBuffer, data, (BufferSize+1)*sizeof(ALfloat)); +#if defined(HAVE_SSE) || defined(HAVE_NEON) + /* Avoid copying the source data if it's aligned like the destination. */ + if((((intptr_t)src)&15) == (((intptr_t)dst)&15)) + return src; +#endif + memcpy(dst, src, numsamples*sizeof(ALfloat)); + return dst; } #define DECL_TEMPLATE(Sampler) \ -void Resample_##Sampler##_C(const ALfloat *data, ALuint frac, \ - ALuint increment, ALfloat *restrict OutBuffer, ALuint BufferSize) \ +const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \ + ALuint increment, ALfloat *restrict dst, ALuint numsamples) \ { \ - ALuint pos = 0; \ ALuint i; \ - \ - for(i = 0;i < BufferSize+1;i++) \ + for(i = 0;i < numsamples;i++) \ { \ - OutBuffer[i] = Sampler(data + pos, frac); \ + dst[i] = Sampler(src, frac); \ \ frac += increment; \ - pos += frac>>FRACTIONBITS; \ + src += frac>>FRACTIONBITS; \ frac &= FRACTIONMASK; \ } \ + return dst; \ } DECL_TEMPLATE(point32) @@ -46,13 +51,26 @@ DECL_TEMPLATE(cubic32) #undef DECL_TEMPLATE -static inline void ApplyCoeffsStep(const ALuint IrSize, +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples) +{ + ALuint i; + for(i = 0;i < numsamples;i++) + *(dst++) = ALfilterState_processSingle(filter, *(src++)); +} + + +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2]) + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) { ALuint c; for(c = 0;c < IrSize;c++) { + const ALuint off = (Offset+c)&HRIR_MASK; + Values[off][0] += Coeffs[c][0] * left; + Values[off][1] += Coeffs[c][1] * right; Coeffs[c][0] += CoeffStep[c][0]; Coeffs[c][1] += CoeffStep[c][1]; } @@ -77,49 +95,61 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], #undef SUFFIX -void MixDirect_C(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixDirect_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; - ALuint pos; + ALfloat DrySend, Step; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Gains[srcchan][c]; + ALuint pos = 0; + DrySend = Gains->Current[c]; + Step = Gains->Step[c]; + if(Step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + if(pos == Counter) + DrySend = Gains->Target[c]; + Gains->Current[c] = DrySend; + } + if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - for(pos = 0;pos < BufferSize;pos++) + for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } -void MixSend_C(const SendParams *params, const ALfloat *restrict data, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixSend_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGainMono *Gain, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat WetSend; - ALuint pos; - - WetSend = params->Gain; - if(!(WetSend > GAIN_SILENCE_THRESHOLD)) - return; - - if(OutPos == 0) - ClickRemoval[0] -= data[0] * WetSend; - for(pos = 0;pos < BufferSize;pos++) - OutBuffer[0][OutPos+pos] += data[pos] * WetSend; - if(OutPos+pos == SamplesToDo) - PendingClicks[0] += data[pos] * WetSend; + ALfloat WetSend, Step; + + { + ALuint pos = 0; + WetSend = Gain->Current; + Step = Gain->Step; + if(Step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[0][OutPos+pos] += data[pos]*WetSend; + WetSend *= Step; + } + if(pos == Counter) + WetSend = Gain->Target; + Gain->Current = WetSend; + } + + if(!(WetSend > GAIN_SILENCE_THRESHOLD)) + return; + for(;pos < BufferSize;pos++) + OutBuffer[0][OutPos+pos] += data[pos] * WetSend; + } } diff --git a/Alc/mixer_defs.h b/Alc/mixer_defs.h index f8968a0a..caa06c25 100644 --- a/Alc/mixer_defs.h +++ b/Alc/mixer_defs.h @@ -4,30 +4,75 @@ #include "AL/alc.h" #include "AL/al.h" #include "alMain.h" +#include "alu.h" -struct DirectParams; -struct SendParams; +struct MixGains; +struct MixGainMono; + +struct HrtfParams; +struct HrtfState; /* C resamplers */ -void Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -void Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -void Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -void Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); /* C mixers */ -void MixDirect_Hrtf_C(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixDirect_C(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixSend_C(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint); +void MixDirect_Hrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void MixDirect_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGains *Gains, ALuint Counter, ALuint OutPos, + ALuint BufferSize); +void MixSend_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGainMono *Gain, ALuint Counter, ALuint OutPos, + ALuint BufferSize); /* SSE mixers */ -void MixDirect_Hrtf_SSE(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixDirect_SSE(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixSend_SSE(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint); +void MixDirect_Hrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void MixDirect_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGains *Gains, ALuint Counter, ALuint OutPos, + ALuint BufferSize); +void MixSend_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGainMono *Gain, ALuint Counter, ALuint OutPos, + ALuint BufferSize); + +/* SSE resamplers */ +inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size) +{ + ALuint i; + + pos_arr[0] = 0; + frac_arr[0] = frac; + for(i = 1;i < size;i++) + { + ALuint frac_tmp = frac_arr[i-1] + increment; + pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS); + frac_arr[i] = frac_tmp&FRACTIONMASK; + } +} + +const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples); +const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples); /* Neon mixers */ -void MixDirect_Hrtf_Neon(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixDirect_Neon(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint); -void MixSend_Neon(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint); +void MixDirect_Hrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void MixDirect_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGains *Gains, ALuint Counter, ALuint OutPos, + ALuint BufferSize); +void MixSend_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + struct MixGainMono *Gain, ALuint Counter, ALuint OutPos, + ALuint BufferSize); #endif /* MIXER_DEFS_H */ diff --git a/Alc/mixer_inc.c b/Alc/mixer_inc.c index 17be5cde..7c90ae9c 100644 --- a/Alc/mixer_inc.c +++ b/Alc/mixer_inc.c @@ -2,15 +2,11 @@ #include "alMain.h" #include "alSource.h" + +#include "hrtf.h" #include "mixer_defs.h" +#include "align.h" -#ifdef __GNUC__ -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif #define REAL_MERGE2(a,b) a##b #define MERGE2(a,b) REAL_MERGE2(a,b) @@ -18,116 +14,76 @@ #define MixDirect_Hrtf MERGE2(MixDirect_Hrtf_,SUFFIX) -static inline void ApplyCoeffsStep(const ALuint irSize, +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint irSize, ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2]); + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right); static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], const ALuint irSize, ALfloat (*restrict Coeffs)[2], ALfloat left, ALfloat right); -void MixDirect_Hrtf(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixDirect_Hrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize) { - ALfloat (*restrict DryBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - const ALuint IrSize = params->Hrtf.Params.IrSize; - const ALint *restrict DelayStep = params->Hrtf.Params.DelayStep; - const ALfloat (*restrict CoeffStep)[2] = params->Hrtf.Params.CoeffStep; - const ALfloat (*restrict TargetCoeffs)[2] = params->Hrtf.Params.Coeffs[srcchan]; - const ALuint *restrict TargetDelay = params->Hrtf.Params.Delay[srcchan]; - ALfloat *restrict History = params->Hrtf.State->History[srcchan]; - ALfloat (*restrict Values)[2] = params->Hrtf.State->Values[srcchan]; - ALint Counter = maxu(params->Hrtf.State->Counter, OutPos) - OutPos; - ALuint Offset = params->Hrtf.State->Offset + OutPos; - ALIGN(16) ALfloat Coeffs[HRIR_LENGTH][2]; + alignas(16) ALfloat Coeffs[HRIR_LENGTH][2]; ALuint Delay[2]; ALfloat left, right; ALuint pos; ALuint c; - pos = 0; for(c = 0;c < IrSize;c++) { - Coeffs[c][0] = TargetCoeffs[c][0] - (CoeffStep[c][0]*Counter); - Coeffs[c][1] = TargetCoeffs[c][1] - (CoeffStep[c][1]*Counter); + Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter); + Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter); } + Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter); + Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter); - Delay[0] = TargetDelay[0] - (DelayStep[0]*Counter); - Delay[1] = TargetDelay[1] - (DelayStep[1]*Counter); - - if(LIKELY(OutPos == 0)) + for(pos = 0;pos < BufferSize && pos < Counter;pos++) { - History[Offset&SRC_HISTORY_MASK] = data[pos]; - left = lerp(History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], - History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], + hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos]; + left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], + hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); - right = lerp(History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], - History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], + right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], + hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); - ClickRemoval[FrontLeft] -= Values[(Offset+1)&HRIR_MASK][0] + - Coeffs[0][0] * left; - ClickRemoval[FrontRight] -= Values[(Offset+1)&HRIR_MASK][1] + - Coeffs[0][1] * right; - } - for(pos = 0;pos < BufferSize && Counter > 0;pos++) - { - History[Offset&SRC_HISTORY_MASK] = data[pos]; - left = lerp(History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], - History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], - (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); - right = lerp(History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], - History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], - (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); + Delay[0] += hrtfparams->DelayStep[0]; + Delay[1] += hrtfparams->DelayStep[1]; - Delay[0] += DelayStep[0]; - Delay[1] += DelayStep[1]; - - Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; Offset++; - ApplyCoeffs(Offset, Values, IrSize, Coeffs, left, right); - DryBuffer[FrontLeft][OutPos] += Values[Offset&HRIR_MASK][0]; - DryBuffer[FrontRight][OutPos] += Values[Offset&HRIR_MASK][1]; - ApplyCoeffsStep(IrSize, Coeffs, CoeffStep); - + ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right); + OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; + OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; OutPos++; - Counter--; } Delay[0] >>= HRTFDELAY_BITS; Delay[1] >>= HRTFDELAY_BITS; for(;pos < BufferSize;pos++) { - History[Offset&SRC_HISTORY_MASK] = data[pos]; - left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; - right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; + hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos]; + left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK]; + right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK]; - Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; Offset++; - ApplyCoeffs(Offset, Values, IrSize, Coeffs, left, right); - DryBuffer[FrontLeft][OutPos] += Values[Offset&HRIR_MASK][0]; - DryBuffer[FrontRight][OutPos] += Values[Offset&HRIR_MASK][1]; + ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right); + OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; + OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; OutPos++; } - if(LIKELY(OutPos == SamplesToDo)) - { - History[Offset&SRC_HISTORY_MASK] = data[pos]; - left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; - right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; - - PendingClicks[FrontLeft] += Values[(Offset+1)&HRIR_MASK][0] + - Coeffs[0][0] * left; - PendingClicks[FrontRight] += Values[(Offset+1)&HRIR_MASK][1] + - Coeffs[0][1] * right; - } } @@ -135,6 +91,3 @@ void MixDirect_Hrtf(const DirectParams *params, const ALfloat *restrict data, AL #undef MERGE2 #undef REAL_MERGE2 - -#undef UNLIKELY -#undef LIKELY diff --git a/Alc/mixer_neon.c b/Alc/mixer_neon.c index 0aa450ad..6a0421a5 100644 --- a/Alc/mixer_neon.c +++ b/Alc/mixer_neon.c @@ -1,28 +1,43 @@ #include "config.h" -#ifdef HAVE_ARM_NEON_H #include <arm_neon.h> -#endif #include "AL/al.h" #include "AL/alc.h" #include "alMain.h" #include "alu.h" +#include "hrtf.h" -static inline void ApplyCoeffsStep(const ALuint IrSize, +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2]) + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) { - float32x4_t coeffs, deltas; ALuint c; - + float32x4_t leftright4; + { + float32x2_t leftright2 = vdup_n_f32(0.0); + leftright2 = vset_lane_f32(left, leftright2, 0); + leftright2 = vset_lane_f32(right, leftright2, 1); + leftright4 = vcombine_f32(leftright2, leftright2); + } for(c = 0;c < IrSize;c += 2) { - coeffs = vld1q_f32(&Coeffs[c][0]); - deltas = vld1q_f32(&CoeffStep[c][0]); - coeffs = vaddq_f32(coeffs, deltas); - vst1q_f32(&Coeffs[c][0], coeffs); + const ALuint o0 = (Offset+c)&HRIR_MASK; + const ALuint o1 = (o0+1)&HRIR_MASK; + float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), + vld1_f32((float32_t*)&Values[o1][0])); + float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); + float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]); + + vals = vmlaq_f32(vals, coefs, leftright4); + coefs = vaddq_f32(coefs, deltas); + + vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); + vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); + vst1q_f32(&Coeffs[c][0], coefs); } } @@ -60,28 +75,37 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], #undef SUFFIX -void MixDirect_Neon(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixDirect_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; + ALfloat DrySend, Step; float32x4_t gain; - ALuint pos; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Gains[srcchan][c]; + ALuint pos = 0; + DrySend = Gains->Current[c]; + Step = Gains->Step[c]; + if(Step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + if(pos == Counter) + DrySend = Gains->Target[c]; + Gains->Current[c] = DrySend; + /* Mix until pos is aligned with 4 or the mix is done. */ + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + } + if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - gain = vdupq_n_f32(DrySend); - for(pos = 0;BufferSize-pos > 3;pos += 4) + for(;BufferSize-pos > 3;pos += 4) { const float32x4_t val4 = vld1q_f32(&data[pos]); float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); @@ -90,41 +114,45 @@ void MixDirect_Neon(const DirectParams *params, const ALfloat *restrict data, AL } for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } -void MixSend_Neon(const SendParams *params, const ALfloat *restrict data, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixSend_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGainMono *Gain, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat WetGain; + ALfloat WetGain, Step; float32x4_t gain; - ALuint pos; - - WetGain = params->Gain; - if(!(WetGain > GAIN_SILENCE_THRESHOLD)) - return; - - if(OutPos == 0) - ClickRemoval[0] -= data[0] * WetGain; - gain = vdupq_n_f32(WetGain); - for(pos = 0;BufferSize-pos > 3;pos += 4) { - const float32x4_t val4 = vld1q_f32(&data[pos]); - float32x4_t wet4 = vld1q_f32(&OutBuffer[0][OutPos+pos]); - wet4 = vaddq_f32(wet4, vmulq_f32(val4, gain)); - vst1q_f32(&OutBuffer[0][OutPos+pos], wet4); - } - for(;pos < BufferSize;pos++) - OutBuffer[0][OutPos+pos] += data[pos] * WetGain; + ALuint pos = 0; + WetGain = Gain->Current; + Step = Gain->Step; + if(Step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[0][OutPos+pos] += data[pos]*WetGain; + WetGain *= Step; + } + if(pos == Counter) + WetGain = Gain->Target; + Gain->Current = WetGain; + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[0][OutPos+pos] += data[pos]*WetGain; + } - if(OutPos+pos == SamplesToDo) - PendingClicks[0] += data[pos] * WetGain; + if(!(WetGain > GAIN_SILENCE_THRESHOLD)) + return; + gain = vdupq_n_f32(WetGain); + for(;BufferSize-pos > 3;pos += 4) + { + const float32x4_t val4 = vld1q_f32(&data[pos]); + float32x4_t wet4 = vld1q_f32(&OutBuffer[0][OutPos+pos]); + wet4 = vaddq_f32(wet4, vmulq_f32(val4, gain)); + vst1q_f32(&OutBuffer[0][OutPos+pos], wet4); + } + for(;pos < BufferSize;pos++) + OutBuffer[0][OutPos+pos] += data[pos] * WetGain; + } } diff --git a/Alc/mixer_sse.c b/Alc/mixer_sse.c index 719ebd23..c4e1fdf5 100644 --- a/Alc/mixer_sse.c +++ b/Alc/mixer_sse.c @@ -1,6 +1,5 @@ #include "config.h" -#ifdef HAVE_XMMINTRIN_H #ifdef IN_IDE_PARSER /* KDevelop's parser won't recognize these defines that get added by the -msse * switch used to compile this source. Without them, xmmintrin.h fails to @@ -9,7 +8,6 @@ #define __SSE__ #endif #include <xmmintrin.h> -#endif #include "AL/al.h" #include "AL/alc.h" @@ -21,19 +19,65 @@ #include "mixer_defs.h" -static inline void ApplyCoeffsStep(const ALuint IrSize, +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2]) + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) { - __m128 coeffs, deltas; + const __m128 lrlr = _mm_setr_ps(left, right, left, right); + __m128 coeffs, deltas, imp0, imp1; + __m128 vals = _mm_setzero_ps(); ALuint i; - for(i = 0;i < IrSize;i += 2) + if((Offset&1)) { - coeffs = _mm_load_ps(&Coeffs[i][0]); - deltas = _mm_load_ps(&CoeffStep[i][0]); + const ALuint o0 = Offset&HRIR_MASK; + const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[0][0]); + deltas = _mm_load_ps(&CoeffStep[0][0]); + vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]); + imp0 = _mm_mul_ps(lrlr, coeffs); coeffs = _mm_add_ps(coeffs, deltas); - _mm_store_ps(&Coeffs[i][0], coeffs); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[0][0], coeffs); + _mm_storel_pi((__m64*)&Values[o0][0], vals); + for(i = 1;i < IrSize-1;i += 2) + { + const ALuint o2 = (Offset+i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i+1][0]); + deltas = _mm_load_ps(&CoeffStep[i+1][0]); + vals = _mm_load_ps(&Values[o2][0]); + imp1 = _mm_mul_ps(lrlr, coeffs); + coeffs = _mm_add_ps(coeffs, deltas); + imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[i+1][0], coeffs); + _mm_store_ps(&Values[o2][0], vals); + imp0 = imp1; + } + vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]); + imp0 = _mm_movehl_ps(imp0, imp0); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi((__m64*)&Values[o1][0], vals); + } + else + { + for(i = 0;i < IrSize;i += 2) + { + const ALuint o = (Offset + i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i][0]); + deltas = _mm_load_ps(&CoeffStep[i][0]); + vals = _mm_load_ps(&Values[o][0]); + imp0 = _mm_mul_ps(lrlr, coeffs); + coeffs = _mm_add_ps(coeffs, deltas); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[i][0], coeffs); + _mm_store_ps(&Values[o][0], vals); + } } } @@ -42,7 +86,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], ALfloat (*restrict Coeffs)[2], ALfloat left, ALfloat right) { - const __m128 lrlr = { left, right, left, right }; + const __m128 lrlr = _mm_setr_ps(left, right, left, right); __m128 vals = _mm_setzero_ps(); __m128 coeffs; ALuint i; @@ -94,28 +138,58 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], #undef SUFFIX -void MixDirect_SSE(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixDirect_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat DrySend; - __m128 gain; - ALuint pos; + ALfloat DrySend, Step; + __m128 gain, step; ALuint c; for(c = 0;c < MaxChannels;c++) { - DrySend = params->Gains[srcchan][c]; + ALuint pos = 0; + DrySend = Gains->Current[c]; + Step = Gains->Step[c]; + if(Step != 1.0f && Counter > 0) + { + /* Mix with applying gain steps in aligned multiples of 4. */ + if(BufferSize-pos > 3 && Counter-pos > 3) + { + gain = _mm_setr_ps( + DrySend, + DrySend * Step, + DrySend * Step * Step, + DrySend * Step * Step * Step + ); + step = _mm_set1_ps(Step * Step * Step * Step); + do { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain)); + gain = _mm_mul_ps(gain, step); + _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4); + pos += 4; + } while(BufferSize-pos > 3 && Counter-pos > 3); + DrySend = _mm_cvtss_f32(gain); + } + /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + DrySend *= Step; + } + if(pos == Counter) + DrySend = Gains->Target[c]; + Gains->Current[c] = DrySend; + /* Mix until pos is aligned with 4 or the mix is done. */ + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*DrySend; + } + if(!(DrySend > GAIN_SILENCE_THRESHOLD)) continue; - - if(OutPos == 0) - ClickRemoval[c] -= data[0]*DrySend; - gain = _mm_set1_ps(DrySend); - for(pos = 0;BufferSize-pos > 3;pos += 4) + for(;BufferSize-pos > 3;pos += 4) { const __m128 val4 = _mm_load_ps(&data[pos]); __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); @@ -124,41 +198,64 @@ void MixDirect_SSE(const DirectParams *params, const ALfloat *restrict data, ALu } for(;pos < BufferSize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*DrySend; - - if(OutPos+pos == SamplesToDo) - PendingClicks[c] += data[pos]*DrySend; } } -void MixSend_SSE(const SendParams *params, const ALfloat *restrict data, - ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) +void MixSend_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGainMono *Gain, ALuint Counter, ALuint OutPos, ALuint BufferSize) { - ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer; - ALfloat *restrict ClickRemoval = params->ClickRemoval; - ALfloat *restrict PendingClicks = params->PendingClicks; - ALfloat WetGain; - __m128 gain; - ALuint pos; - - WetGain = params->Gain; - if(!(WetGain > GAIN_SILENCE_THRESHOLD)) - return; - - if(OutPos == 0) - ClickRemoval[0] -= data[0] * WetGain; - - gain = _mm_set1_ps(WetGain); - for(pos = 0;BufferSize-pos > 3;pos += 4) + ALfloat WetGain, Step; + __m128 gain, step; + { - const __m128 val4 = _mm_load_ps(&data[pos]); - __m128 wet4 = _mm_load_ps(&OutBuffer[0][OutPos+pos]); - wet4 = _mm_add_ps(wet4, _mm_mul_ps(val4, gain)); - _mm_store_ps(&OutBuffer[0][OutPos+pos], wet4); - } - for(;pos < BufferSize;pos++) - OutBuffer[0][OutPos+pos] += data[pos] * WetGain; + ALuint pos = 0; + WetGain = Gain->Current; + Step = Gain->Step; + if(Step != 1.0f && Counter > 0) + { + if(BufferSize-pos > 3 && Counter-pos > 3) + { + gain = _mm_setr_ps( + WetGain, + WetGain * Step, + WetGain * Step * Step, + WetGain * Step * Step * Step + ); + step = _mm_set1_ps(Step * Step * Step * Step); + do { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[0][OutPos+pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain)); + gain = _mm_mul_ps(gain, step); + _mm_store_ps(&OutBuffer[0][OutPos+pos], dry4); + pos += 4; + } while(BufferSize-pos > 3 && Counter-pos > 3); + WetGain = _mm_cvtss_f32(gain); + } + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[0][OutPos+pos] += data[pos]*WetGain; + WetGain *= Step; + } + if(pos == Counter) + WetGain = Gain->Target; + Gain->Current = WetGain; + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[0][OutPos+pos] += data[pos]*WetGain; + } - if(OutPos+pos == SamplesToDo) - PendingClicks[0] += data[pos] * WetGain; + if(!(WetGain > GAIN_SILENCE_THRESHOLD)) + return; + gain = _mm_set1_ps(WetGain); + for(;BufferSize-pos > 3;pos += 4) + { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 wet4 = _mm_load_ps(&OutBuffer[0][OutPos+pos]); + wet4 = _mm_add_ps(wet4, _mm_mul_ps(val4, gain)); + _mm_store_ps(&OutBuffer[0][OutPos+pos], wet4); + } + for(;pos < BufferSize;pos++) + OutBuffer[0][OutPos+pos] += data[pos] * WetGain; + } } diff --git a/Alc/mixer_sse2.c b/Alc/mixer_sse2.c new file mode 100644 index 00000000..7670add3 --- /dev/null +++ b/Alc/mixer_sse2.c @@ -0,0 +1,78 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri <[email protected]>. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <xmmintrin.h> +#include <emmintrin.h> + +#include "alu.h" +#include "mixer_defs.h" + + +const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples) +{ + const __m128i increment4 = _mm_set1_epi32(increment*4); + const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); + const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); + alignas(16) union { ALuint i[4]; float f[4]; } pos_; + alignas(16) union { ALuint i[4]; float f[4]; } frac_; + __m128i frac4, pos4; + ALuint pos; + ALuint i; + + InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); + + frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); + pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); + + for(i = 0;numsamples-i > 3;i += 4) + { + const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); + const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); + + /* val1 + (val2-val1)*mu */ + const __m128 r0 = _mm_sub_ps(val2, val1); + const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); + const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); + + _mm_store_ps(&dst[i], out); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); + frac4 = _mm_and_si128(frac4, fracMask4); + + _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4)); + } + + pos = pos_.i[0]; + frac = _mm_cvtsi128_si32(frac4); + + for(;i < numsamples;i++) + { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} diff --git a/Alc/mixer_sse41.c b/Alc/mixer_sse41.c new file mode 100644 index 00000000..8ce8cd90 --- /dev/null +++ b/Alc/mixer_sse41.c @@ -0,0 +1,82 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri <[email protected]>. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <xmmintrin.h> +#include <emmintrin.h> +#include <smmintrin.h> + +#include "alu.h" +#include "mixer_defs.h" + + +const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples) +{ + const __m128i increment4 = _mm_set1_epi32(increment*4); + const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); + const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); + alignas(16) union { ALuint i[4]; float f[4]; } pos_; + alignas(16) union { ALuint i[4]; float f[4]; } frac_; + __m128i frac4, pos4; + ALuint pos; + ALuint i; + + InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); + + frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); + pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); + + for(i = 0;numsamples-i > 3;i += 4) + { + const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); + const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); + + /* val1 + (val2-val1)*mu */ + const __m128 r0 = _mm_sub_ps(val2, val1); + const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); + const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); + + _mm_store_ps(&dst[i], out); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); + frac4 = _mm_and_si128(frac4, fracMask4); + + pos_.i[0] = _mm_extract_epi32(pos4, 0); + pos_.i[1] = _mm_extract_epi32(pos4, 1); + pos_.i[2] = _mm_extract_epi32(pos4, 2); + pos_.i[3] = _mm_extract_epi32(pos4, 3); + } + + pos = pos_.i[0]; + frac = _mm_cvtsi128_si32(frac4); + + for(;i < numsamples;i++) + { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} diff --git a/Alc/rwlock.h b/Alc/rwlock.h deleted file mode 100644 index efbab4e8..00000000 --- a/Alc/rwlock.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef AL_RWLOCK_H -#define AL_RWLOCK_H - -#include "AL/al.h" -#include "atomic.h" - -typedef struct { - volatile RefCount read_count; - volatile RefCount write_count; - volatile ALenum read_lock; - volatile ALenum read_entry_lock; - volatile ALenum write_lock; -} RWLock; - -void RWLockInit(RWLock *lock); -void ReadLock(RWLock *lock); -void ReadUnlock(RWLock *lock); -void WriteLock(RWLock *lock); -void WriteUnlock(RWLock *lock); - -#endif /* AL_RWLOCK_H */ diff --git a/Alc/threads.c b/Alc/threads.c deleted file mode 100644 index 64586ae9..00000000 --- a/Alc/threads.c +++ /dev/null @@ -1,200 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 1999-2007 by authors. - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#include "config.h" - -#include "threads.h" - -#include <stdlib.h> -#include <errno.h> - -#include "alMain.h" -#include "alThunk.h" - -#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */ - -#ifdef _WIN32 - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -typedef struct althread_info { - ALuint (*func)(ALvoid*); - ALvoid *ptr; - HANDLE hdl; -} althread_info; - -static DWORD CALLBACK StarterFunc(void *ptr) -{ - althread_info *inf = (althread_info*)ptr; - ALuint ret; - - ret = inf->func(inf->ptr); - ExitThread((DWORD)ret); - - return (DWORD)ret; -} - - -ALboolean StartThread(althread_t *thread, ALuint (*func)(ALvoid*), ALvoid *ptr) -{ - althread_info *info; - DWORD dummy; - - info = malloc(sizeof(*info)); - if(!info) return AL_FALSE; - - info->func = func; - info->ptr = ptr; - - info->hdl = CreateThread(NULL, THREAD_STACK_SIZE, StarterFunc, info, 0, &dummy); - if(!info->hdl) - { - free(info); - return AL_FALSE; - } - - *thread = info; - return AL_TRUE; -} - -ALuint StopThread(althread_t thread) -{ - DWORD ret = 0; - - WaitForSingleObject(thread->hdl, INFINITE); - GetExitCodeThread(thread->hdl, &ret); - CloseHandle(thread->hdl); - - free(thread); - - return (ALuint)ret; -} - - -void SetThreadName(const char *name) -{ -#if defined(_MSC_VER) -#define MS_VC_EXCEPTION 0x406D1388 - struct { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = -1; - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info); - } - __except(EXCEPTION_CONTINUE_EXECUTION) { - } -#undef MS_VC_EXCEPTION -#else - TRACE("Can't set thread %04lx name to \"%s\"\n", GetCurrentThreadId(), name); -#endif -} - -#else - -#include <pthread.h> - -typedef struct althread_info { - ALuint (*func)(ALvoid*); - ALvoid *ptr; - ALuint ret; - pthread_t hdl; -} althread_info; - -static void *StarterFunc(void *ptr) -{ - althread_info *inf = (althread_info*)ptr; - inf->ret = inf->func(inf->ptr); - return NULL; -} - - -ALboolean StartThread(althread_t *thread, ALuint (*func)(ALvoid*), ALvoid *ptr) -{ - pthread_attr_t attr; - althread_info *info; - - info = malloc(sizeof(*info)); - if(!info) return AL_FALSE; - - if(pthread_attr_init(&attr) != 0) - { - free(info); - return AL_FALSE; - } - if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0) - { - pthread_attr_destroy(&attr); - free(info); - return AL_FALSE; - } - - info->func = func; - info->ptr = ptr; - if(pthread_create(&info->hdl, &attr, StarterFunc, info) != 0) - { - pthread_attr_destroy(&attr); - free(info); - return AL_FALSE; - } - pthread_attr_destroy(&attr); - - *thread = info; - return AL_TRUE; -} - -ALuint StopThread(althread_t thread) -{ - ALuint ret; - - pthread_join(thread->hdl, NULL); - ret = thread->ret; - - free(thread); - - return ret; -} - - -void SetThreadName(const char *name) -{ -#if defined(HAVE_PTHREAD_SETNAME_NP) -#if defined(__GNUC__) - if(pthread_setname_np(pthread_self(), name) != 0) -#elif defined(__APPLE__) - if(pthread_setname_np(name) != 0) -#endif - WARN("Failed to set thread name to \"%s\": %s\n", name, strerror(errno)); -#elif defined(HAVE_PTHREAD_SET_NAME_NP) - pthread_set_name_np(pthread_self(), name); -#else - TRACE("Can't set thread name to \"%s\"\n", name); -#endif -} - -#endif diff --git a/Alc/vector.h b/Alc/vector.h new file mode 100644 index 00000000..5c094bee --- /dev/null +++ b/Alc/vector.h @@ -0,0 +1,69 @@ +#ifndef AL_VECTOR_H +#define AL_VECTOR_H + +#include <stdlib.h> + +#include <AL/al.h> + +/* "Base" vector type, designed to alias with the actual vector types. */ +typedef struct vector__s { + ALsizei Capacity; + ALsizei Size; +} *vector_; + +#define DECL_VECTOR(T) typedef struct vector_##T##_s { \ + ALsizei Capacity; \ + ALsizei Size; \ + T Data[]; \ +} *vector_##T; \ +typedef const struct vector_##T##_s *const_vector_##T; + +#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0) +#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0) + +/* Helper to increase a vector's reserve. Do not call directly. */ +ALboolean vector_reserve(void *ptr, size_t base_size, size_t obj_count, size_t obj_size, ALboolean exact); +#define VECTOR_RESERVE(_x, _c) (vector_reserve(&(_x), sizeof(*(_x)), (_c), sizeof((_x)->Data[0]), AL_TRUE)) + +/* Helper to change a vector's size. Do not call directly. */ +ALboolean vector_resize(void *ptr, size_t base_size, size_t obj_count, size_t obj_size); +#define VECTOR_RESIZE(_x, _c) (vector_resize(&(_x), sizeof(*(_x)), (_c), sizeof((_x)->Data[0]))) + +#define VECTOR_CAPACITY(_x) ((const ALsizei)((_x) ? (_x)->Capacity : 0)) +#define VECTOR_SIZE(_x) ((const ALsizei)((_x) ? (_x)->Size : 0)) + +#define VECTOR_ITER_BEGIN(_x) ((_x)->Data + 0) +#define VECTOR_ITER_END(_x) ((_x)->Data + VECTOR_SIZE(_x)) + +ALboolean vector_insert(void *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend); +#ifdef __GNUC__ +#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2) +#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \ + ALboolean _r; \ + static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \ + static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \ + static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \ + _r = vector_insert(&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \ + _r; \ +}) +#else +#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert(&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e))) +#endif + +#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve(&(_x), sizeof(*(_x)), VECTOR_SIZE(_x)+1, sizeof((_x)->Data[0]), AL_FALSE) && \ + (((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE)) +#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--)) + +#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1]) +#define VECTOR_FRONT(_x) ((_x)->Data[0]) + +#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)]) + +#define VECTOR_FOR_EACH(_t, _x, _f) do { \ + _t *_iter = VECTOR_ITER_BEGIN((_x)); \ + _t *_end = VECTOR_ITER_END((_x)); \ + for(;_iter != _end;++_iter) \ + (_f)(_iter); \ +} while(0) + +#endif /* AL_VECTOR_H */ diff --git a/CMakeLists.txt b/CMakeLists.txt index cec2df9f..217c3d7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,51 +25,18 @@ INCLUDE(CheckTypeSize) SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) -OPTION(ALSOFT_CPUEXT_SSE "Check for SSE/SSE2 support" ON) -OPTION(ALSOFT_CPUEXT_NEON "Check for ARM Neon support" ON) - -OPTION(ALSOFT_REQUIRE_SSE "Require SSE/SSE2 support" OFF) -OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) - IF(WIN32) # This option is mainly for static linking OpenAL Soft into another project # that already defines the IDs. It is up to that project to ensure all # required IDs are defined. OPTION(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF) +ELSE() + # These are needed on non-Windows systems for extra features + ADD_DEFINITIONS(-D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700") ENDIF() -OPTION(ALSOFT_BACKEND_ALSA "Check for ALSA backend" ON) -OPTION(ALSOFT_BACKEND_OSS "Check for OSS backend" ON) -OPTION(ALSOFT_BACKEND_SOLARIS "Check for Solaris backend" ON) -OPTION(ALSOFT_BACKEND_SNDIO "Check for SndIO backend" ON) -OPTION(ALSOFT_BACKEND_QSA "Check for QSA backend" ON) -OPTION(ALSOFT_BACKEND_MMDEVAPI "Check for MMDevApi backend" ON) -OPTION(ALSOFT_BACKEND_DSOUND "Check for DirectSound backend" ON) -OPTION(ALSOFT_BACKEND_WINMM "Check for Windows Multimedia backend" ON) -OPTION(ALSOFT_BACKEND_PORTAUDIO "Check for PortAudio backend" ON) -OPTION(ALSOFT_BACKEND_PULSEAUDIO "Check for PulseAudio backend" ON) -OPTION(ALSOFT_BACKEND_COREAUDIO "Check for CoreAudio backend" ON) -OPTION(ALSOFT_BACKEND_OPENSL "Check for OpenSL backend" ON) -OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) - -OPTION(ALSOFT_MIDI_FLUIDSYNTH "Check for FluidSynth MIDI" ON) - -OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) -OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) -OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) -OPTION(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) -OPTION(ALSOFT_REQUIRE_QSA "Require QSA backend" OFF) -OPTION(ALSOFT_REQUIRE_MMDEVAPI "Require MMDevApi" OFF) -OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) -OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) -OPTION(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) -OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) -OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) -OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) - -OPTION(ALSOFT_REQUIRE_FLUIDSYNTH "Require FluidSynth MIDI" OFF) - OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF) @@ -80,11 +47,12 @@ OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON) OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) +OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) IF(WIN32) SET(LIBNAME OpenAL32) - ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0501") + ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502") ELSE() SET(LIBNAME openal) ENDIF() @@ -124,19 +92,8 @@ IF(MSVC) CHECK_C_SOURCE_COMPILES("int *restrict foo; int main() {return 0;}" HAVE_RESTRICT) IF(NOT HAVE_RESTRICT) - # Slightly convoluted way to do this, because MSVC may barf if restrict is - # defined to __restrict. - CHECK_C_SOURCE_COMPILES("#define restrict __restrict - #include <stdlib.h> - int *restrict foo; - int main() {return 0;}" HAVE___RESTRICT) - IF(HAVE___RESTRICT) - ADD_DEFINITIONS(-Drestrict=__restrict) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=__restrict") - ELSE() - ADD_DEFINITIONS("-Drestrict=") - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=") - ENDIF() + ADD_DEFINITIONS("-Drestrict=") + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=") ENDIF() CHECK_C_SOURCE_COMPILES("inline void foo(void) { } @@ -169,6 +126,44 @@ IF(CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") ENDIF() +# Check if we have C99 variable length arrays +CHECK_C_SOURCE_COMPILES( +"int main(int argc, char *argv[]) + { + volatile int tmp[argc]; + tmp[0] = argv[0][0]; + return tmp[0]; + }" +HAVE_C99_VLA) + +# Check if we have C99 bool +CHECK_C_SOURCE_COMPILES( +"int main(int argc, char *argv[]) + { + volatile _Bool ret; + ret = (argc > 1) ? 1 : 0; + return ret ? -1 : 0; + }" +HAVE_C99_BOOL) + +# Check if we have C11 static_assert +CHECK_C_SOURCE_COMPILES( +"int main() + { + _Static_assert(sizeof(int) == sizeof(int), \"What\"); + return 0; + }" +HAVE_C11_STATIC_ASSERT) + +# Check if we have C11 alignas +CHECK_C_SOURCE_COMPILES( +"_Alignas(16) int foo; + int main() + { + return 0; + }" +HAVE_C11_ALIGNAS) + # Add definitions, compiler switches, etc. INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_BINARY_DIR}") IF(CMAKE_VERSION VERSION_LESS "2.8.8") @@ -232,6 +227,15 @@ ELSE() ADD_DEFINITIONS(-Werror) ENDIF() + # Force enable PIC if available. The static common library will be linked + # into the dynamic openal library, which requires all its code to be + # position-independent, and CMake doesn't automatically enable PIC for + # static library targets (Windows code is always position-independent). + CHECK_C_COMPILER_FLAG(-fPIC HAVE_FPIC_SWITCH) + IF(HAVE_FPIC_SWITCH AND NOT WIN32) + ADD_DEFINITIONS(-fPIC) + ENDIF() + SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O2 -D_DEBUG" CACHE STRING "Flags used by the compiler during Release with Debug Info builds." FORCE) @@ -296,23 +300,31 @@ ELSE() ENDIF() SET(SSE_SWITCH "") -IF(MSVC) - CHECK_C_COMPILER_FLAG(/arch:SSE HAVE_ARCHSSE_SWITCH) - IF(HAVE_ARCHSSE_SWITCH) - SET(SSE_SWITCH "/arch:SSE") - ENDIF() -ENDIF() -IF(NOT SSE_SWITCH) +SET(SSE2_SWITCH "") +SET(SSE4_1_SWITCH "") +IF(NOT MSVC) CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH) IF(HAVE_MSSE_SWITCH) SET(SSE_SWITCH "-msse") ENDIF() + CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH) + IF(HAVE_MSSE2_SWITCH) + SET(SSE2_SWITCH "-msse2") + ENDIF() + CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH) + IF(HAVE_MSSE4_1_SWITCH) + SET(SSE4_1_SWITCH "-msse4.1") + ENDIF() ENDIF() CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2))); int main() {return 0;}" HAVE_GCC_FORMAT) +CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H) +CHECK_INCLUDE_FILE(stdalign.h HAVE_STDALIGN_H) CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) +CHECK_INCLUDE_FILE(ftw.h HAVE_FTW_H) +CHECK_INCLUDE_FILE(io.h HAVE_IO_H) CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H) CHECK_INCLUDE_FILE(sys/sysconf.h HAVE_SYS_SYSCONF_H) @@ -336,11 +348,23 @@ CHECK_SYMBOL_EXISTS(aligned_alloc stdlib.h HAVE_ALIGNED_ALLOC) CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF) +IF(NOT HAVE_C99_VLA) + CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA) + IF(NOT HAVE_ALLOCA) + MESSAGE(FATAL_ERROR "No alloca function found, please report!") + ENDIF() +ENDIF() IF(HAVE_FLOAT_H) CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP) CHECK_SYMBOL_EXISTS(__control87_2 float.h HAVE___CONTROL87_2) ENDIF() +IF(HAVE_FTW_H) + CHECK_SYMBOL_EXISTS(ftw ftw.h HAVE_FTW) +ENDIF() +IF(HAVE_IO_H) + CHECK_SYMBOL_EXISTS(_wfindfirst io.h HAVE__WFINDFIRST) +ENDIF() CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF) CHECK_FUNCTION_EXISTS(stat HAVE_STAT) @@ -414,12 +438,8 @@ ENDIF() CHECK_INCLUDE_FILE(android/api-level.h HAVE_ANDROID_H) # Check if we have Windows headers -CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0501) +CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0502) IF(NOT HAVE_WINDOWS_H) - # These are needed on some systems for extra features - ADD_DEFINITIONS(-D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700") - CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) IF(NOT HAVE_GETTIMEOFDAY) MESSAGE(FATAL_ERROR "No timing function found!") @@ -461,6 +481,11 @@ IF(NOT HAVE_WINDOWS_H) IF(NOT HAVE_PTHREAD_SETNAME_NP) CHECK_SYMBOL_EXISTS(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP) ENDIF() + IF(HAVE_PTHREAD_NP_H) + CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) + ENDIF() + + CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK) CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_LIBRT) IF(HAVE_LIBRT) @@ -472,7 +497,7 @@ ENDIF() CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) IF(NOT HAVE_STDINT_H) IF(HAVE_WINDOWS_H) - CHECK_C_SOURCE_COMPILES("#define _WIN32_WINNT 0x0501 + CHECK_C_SOURCE_COMPILES("#define _WIN32_WINNT 0x0502 #include <windows.h> __int64 foo; int main() {return 0;}" HAVE___INT64) @@ -486,14 +511,12 @@ IF(NOT HAVE_STDINT_H) ENDIF() ENDIF() -# Windows needs winmm for timeGetTime, even if the backend is disabled -CHECK_SHARED_FUNCTION_EXISTS(timeGetTime "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) -IF(HAVE_LIBWINMM) - SET(EXTRA_LIBS winmm ${EXTRA_LIBS}) - SET(PKG_CONFIG_LIBS ${PKG_CONFIG_LIBS} -lwinmm) -ENDIF() - +SET(COMMON_OBJS common/atomic.c + common/rwlock.c + common/threads.c + common/uintmap.c +) SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c OpenAL32/alBuffer.c OpenAL32/alEffect.c @@ -508,6 +531,7 @@ SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c OpenAL32/alSource.c OpenAL32/alState.c OpenAL32/alThunk.c + OpenAL32/sample_cvt.c ) SET(ALC_OBJS Alc/ALc.c Alc/ALu.c @@ -528,37 +552,84 @@ SET(ALC_OBJS Alc/ALc.c Alc/helpers.c Alc/hrtf.c Alc/panning.c - Alc/threads.c Alc/mixer.c Alc/mixer_c.c ) SET(CPU_EXTS "Default") -SET(HAVE_SSE 0) +SET(HAVE_SSE 0) +SET(HAVE_SSE2 0) +SET(HAVE_SSE4_1 0) SET(HAVE_NEON 0) # Check for SSE support -IF(ALSOFT_CPUEXT_SSE AND ALIGN_DECL) - CHECK_INCLUDE_FILE(xmmintrin.h HAVE_XMMINTRIN_H "${SSE_SWITCH}") - IF(HAVE_XMMINTRIN_H) - SET(HAVE_SSE 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse.c) - IF(SSE_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse.c PROPERTIES - COMPILE_FLAGS "${SSE_SWITCH}") +OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) +CHECK_INCLUDE_FILE(xmmintrin.h HAVE_XMMINTRIN_H "${SSE_SWITCH}") +IF(HAVE_XMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) + IF(ALSOFT_CPUEXT_SSE) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse.c) + IF(SSE_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse.c PROPERTIES + COMPILE_FLAGS "${SSE_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE") ENDIF() - SET(CPU_EXTS "${CPU_EXTS}, SSE") ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) MESSAGE(FATAL_ERROR "Failed to enabled required SSE CPU extensions") ENDIF() +OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) +CHECK_INCLUDE_FILE(emmintrin.h HAVE_EMMINTRIN_H "${SSE2_SWITCH}") +IF(HAVE_EMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) + IF(HAVE_SSE AND ALSOFT_CPUEXT_SSE2) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE2 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse2.c) + IF(SSE2_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse2.c PROPERTIES + COMPILE_FLAGS "${SSE2_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE2") + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) + MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") +ENDIF() + +OPTION(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) +CHECK_INCLUDE_FILE(smmintrin.h HAVE_SMMINTRIN_H "${SSE4_1_SWITCH}") +IF(HAVE_SMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) + IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE4_1) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE4_1 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse41.c) + IF(SSE4_1_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse41.c PROPERTIES + COMPILE_FLAGS "${SSE4_1_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE4.1") + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) + MESSAGE(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") +ENDIF() + # Check for ARM Neon support -IF(ALSOFT_CPUEXT_NEON) - CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) - IF(HAVE_ARM_NEON_H) +OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) +CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) +IF(HAVE_ARM_NEON_H) + OPTION(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) + IF(ALSOFT_CPUEXT_NEON) SET(HAVE_NEON 1) SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_neon.c) SET(CPU_EXTS "${CPU_EXTS}, Neon") @@ -574,14 +645,17 @@ SET(ALC_OBJS ${ALC_OBJS} Alc/midi/sf2load.c Alc/midi/dummy.c Alc/midi/fluidsynth.c + Alc/midi/soft.c ) SET(HAVE_FLUIDSYNTH 0) # Check for FluidSynth support -IF(ALSOFT_MIDI_FLUIDSYNTH) - FIND_PACKAGE(FluidSynth) - IF(FLUIDSYNTH_FOUND) +OPTION(ALSOFT_REQUIRE_FLUIDSYNTH "Require FluidSynth MIDI" OFF) +FIND_PACKAGE(FluidSynth) +IF(FLUIDSYNTH_FOUND) + OPTION(ALSOFT_MIDI_FLUIDSYNTH "Enable FluidSynth MIDI" ON) + IF(ALSOFT_MIDI_FLUIDSYNTH) SET(HAVE_FLUIDSYNTH 1) IF(CMAKE_VERSION VERSION_LESS "2.8.8") INCLUDE_DIRECTORIES(${FLUIDSYNTH_INCLUDE_DIR}) @@ -615,21 +689,29 @@ SET(HAVE_PULSEAUDIO 0) SET(HAVE_COREAUDIO 0) SET(HAVE_OPENSL 0) SET(HAVE_WAVE 0) +IF(WIN32 OR HAVE_DLFCN_H) + SET(IS_LINKED "") + MACRO(ADD_BACKEND_LIBS _LIBS) + ENDMACRO() +ELSE() + SET(IS_LINKED " (linked)") + MACRO(ADD_BACKEND_LIBS _LIBS) + SET(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS}) + ENDMACRO() +ENDIF() # Check ALSA backend -IF(ALSOFT_BACKEND_ALSA) - CHECK_INCLUDE_FILE(alsa/asoundlib.h HAVE_ALSA_ASOUNDLIB_H) - IF(HAVE_ALSA_ASOUNDLIB_H) - CHECK_SHARED_FUNCTION_EXISTS(snd_pcm_open "alsa/asoundlib.h" asound "" HAVE_LIBASOUND) - IF(HAVE_LIBASOUND OR HAVE_DLFCN_H OR WIN32) - SET(HAVE_ALSA 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) - IF(HAVE_DLFCN_H OR WIN32) - SET(BACKENDS "${BACKENDS} ALSA,") - ELSE() - SET(BACKENDS "${BACKENDS} ALSA \(linked\),") - SET(EXTRA_LIBS asound ${EXTRA_LIBS}) - ENDIF() +OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) +FIND_PACKAGE(ALSA) +IF(ALSA_FOUND) + OPTION(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) + IF(ALSOFT_BACKEND_ALSA) + SET(HAVE_ALSA 1) + SET(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) + ADD_BACKEND_LIBS(${ALSA_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${ALSA_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -638,12 +720,17 @@ IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) ENDIF() # Check OSS backend -IF(ALSOFT_BACKEND_OSS) - CHECK_INCLUDE_FILE(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) - IF(HAVE_SYS_SOUNDCARD_H) +OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) +FIND_PACKAGE(OSS) +IF(OSS_FOUND) + OPTION(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) + IF(ALSOFT_BACKEND_OSS) SET(HAVE_OSS 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) SET(BACKENDS "${BACKENDS} OSS,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${OSS_INCLUDE_DIRS}) + ENDIF() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) @@ -651,12 +738,17 @@ IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) ENDIF() # Check Solaris backend -IF(ALSOFT_BACKEND_SOLARIS) - CHECK_INCLUDE_FILE(sys/audioio.h HAVE_SYS_AUDIOIO_H) - IF(HAVE_SYS_AUDIOIO_H) +OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) +FIND_PACKAGE(AudioIO) +IF(AUDIOIO_FOUND) + OPTION(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON) + IF(ALSOFT_BACKEND_SOLARIS) SET(HAVE_SOLARIS 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) SET(BACKENDS "${BACKENDS} Solaris,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${AUDIOIO_INCLUDE_DIRS}) + ENDIF() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) @@ -664,15 +756,17 @@ IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) ENDIF() # Check SndIO backend -IF(ALSOFT_BACKEND_SNDIO) - CHECK_INCLUDE_FILE(sndio.h HAVE_SNDIO_H) - IF(HAVE_SNDIO_H) - CHECK_SHARED_FUNCTION_EXISTS(sio_open "sndio.h" sndio "" HAVE_LIBSNDIO) - IF(HAVE_LIBSNDIO) - SET(HAVE_SNDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) - SET(BACKENDS "${BACKENDS} SndIO \(linked\),") - SET(EXTRA_LIBS sndio ${EXTRA_LIBS}) +OPTION(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) +FIND_PACKAGE(SoundIO) +IF(SOUNDIO_FOUND) + OPTION(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + IF(ALSOFT_BACKEND_SNDIO) + SET(HAVE_SNDIO 1) + SET(BACKENDS "${BACKENDS} SndIO (linked),") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) + SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${SOUNDIO_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -681,15 +775,17 @@ IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) ENDIF() # Check QSA backend -IF (ALSOFT_BACKEND_QSA AND "${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") - CHECK_INCLUDE_FILE(sys/asoundlib.h HAVE_SYS_ASOUNDLIB_H) - IF(HAVE_SYS_ASOUNDLIB_H) - CHECK_SHARED_FUNCTION_EXISTS(snd_pcm_open "sys/asoundlib.h" asound "" HAVE_LIBASOUND) - IF(HAVE_LIBASOUND) - SET(HAVE_QSA 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/qsa.c) - SET(EXTRA_LIBS asound ${EXTRA_LIBS}) - SET(BACKENDS "${BACKENDS} QSA \(linked\),") +OPTION(ALSOFT_REQUIRE_QSA "Require QSA backend" OFF) +FIND_PACKAGE(QSA) +IF(QSA_FOUND) + OPTION(ALSOFT_BACKEND_QSA "Enable QSA backend" ON) + IF(ALSOFT_BACKEND_QSA) + SET(HAVE_QSA 1) + SET(BACKENDS "${BACKENDS} QSA (linked),") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/qsa.c) + SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${QSA_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -697,15 +793,35 @@ IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA) MESSAGE(FATAL_ERROR "Failed to enabled required QSA backend") ENDIF() +# Check DSound backend +OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) +FIND_PACKAGE(DSound) +IF(DSOUND_FOUND) + OPTION(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + IF(ALSOFT_BACKEND_DSOUND) + SET(HAVE_DSOUND 1) + SET(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) + ADD_BACKEND_LIBS(${DSOUND_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${DSOUND_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) + MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") +ENDIF() + # Check for MMDevApi backend +OPTION(ALSOFT_REQUIRE_MMDEVAPI "Require MMDevApi backend" OFF) IF(HAVE_WINDOWS_H) - IF(ALSOFT_BACKEND_MMDEVAPI) - CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) - IF(HAVE_MMDEVICEAPI_H) + CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + IF(HAVE_MMDEVICEAPI_H) + OPTION(ALSOFT_BACKEND_MMDEVAPI "Enable MMDevApi backend" ON) + IF(ALSOFT_BACKEND_MMDEVAPI) SET(HAVE_MMDEVAPI 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) - SET(BACKENDS "${BACKENDS} MMDevApi,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) ENDIF() ENDIF() ENDIF() @@ -713,35 +829,20 @@ IF(ALSOFT_REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI) MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend") ENDIF() -# Check DSound/MMSystem backend -IF(ALSOFT_BACKEND_DSOUND) - CHECK_INCLUDE_FILE(dsound.h HAVE_DSOUND_H) - IF(HAVE_DSOUND_H) - CHECK_SHARED_FUNCTION_EXISTS(DirectSoundCreate "dsound.h" dsound "" HAVE_LIBDSOUND) - IF(HAVE_LIBDSOUND OR HAVE_DLFCN_H OR WIN32) - SET(HAVE_DSOUND 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) - - IF(HAVE_DLFCN_H OR WIN32) - SET(BACKENDS "${BACKENDS} DirectSound,") - ELSE() - SET(BACKENDS "${BACKENDS} DirectSound \(linked\),") - SET(EXTRA_LIBS dsound ${EXTRA_LIBS}) - ENDIF() - ENDIF() - ENDIF() -ENDIF() -IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) - MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") -ENDIF() - +# Check MMSystem backend +OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) IF(HAVE_WINDOWS_H) - IF(ALSOFT_BACKEND_WINMM) - CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0501) - IF(HAVE_MMSYSTEM_H AND HAVE_LIBWINMM) - SET(HAVE_WINMM 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) - SET(BACKENDS "${BACKENDS} WinMM,") + CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0502) + IF(HAVE_MMSYSTEM_H) + CHECK_SHARED_FUNCTION_EXISTS(waveOutOpen "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) + IF(HAVE_LIBWINMM) + OPTION(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + IF(ALSOFT_BACKEND_WINMM) + SET(HAVE_WINMM 1) + SET(BACKENDS "${BACKENDS} WinMM,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) + SET(EXTRA_LIBS winmm ${EXTRA_LIBS}) + ENDIF() ENDIF() ENDIF() ENDIF() @@ -750,19 +851,17 @@ IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) ENDIF() # Check PortAudio backend -IF(ALSOFT_BACKEND_PORTAUDIO) - CHECK_INCLUDE_FILE(portaudio.h HAVE_PORTAUDIO_H) - IF(HAVE_PORTAUDIO_H) - CHECK_SHARED_FUNCTION_EXISTS(Pa_Initialize "portaudio.h" portaudio "" HAVE_LIBPORTAUDIO) - IF(HAVE_LIBPORTAUDIO OR HAVE_DLFCN_H OR WIN32) - SET(HAVE_PORTAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) - IF(HAVE_DLFCN_H OR WIN32) - SET(BACKENDS "${BACKENDS} PortAudio,") - ELSE() - SET(BACKENDS "${BACKENDS} PortAudio \(linked\),") - SET(EXTRA_LIBS portaudio ${EXTRA_LIBS}) - ENDIF() +OPTION(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +FIND_PACKAGE(PortAudio) +IF(PORTAUDIO_FOUND) + OPTION(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) + IF(ALSOFT_BACKEND_PORTAUDIO) + SET(HAVE_PORTAUDIO 1) + SET(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) + ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${PORTAUDIO_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -771,19 +870,17 @@ IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) ENDIF() # Check PulseAudio backend -IF(ALSOFT_BACKEND_PULSEAUDIO) - CHECK_INCLUDE_FILE(pulse/pulseaudio.h HAVE_PULSE_PULSEAUDIO_H) - IF(HAVE_PULSE_PULSEAUDIO_H) - CHECK_SHARED_FUNCTION_EXISTS(pa_context_new "pulse/pulseaudio.h" pulse "" HAVE_LIBPULSE) - IF(HAVE_LIBPULSE OR HAVE_DLFCN_H OR WIN32) - SET(HAVE_PULSEAUDIO 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) - IF(HAVE_DLFCN_H OR WIN32) - SET(BACKENDS "${BACKENDS} PulseAudio,") - ELSE() - SET(BACKENDS "${BACKENDS} PulseAudio \(linked\),") - SET(EXTRA_LIBS pulse ${EXTRA_LIBS}) - ENDIF() +OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +FIND_PACKAGE(PulseAudio) +IF(PULSEAUDIO_FOUND) + OPTION(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) + IF(ALSOFT_BACKEND_PULSEAUDIO) + SET(HAVE_PULSEAUDIO 1) + SET(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) + ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${PULSEAUDIO_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -792,13 +889,18 @@ IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) ENDIF() # Check CoreAudio backend -IF(ALSOFT_BACKEND_COREAUDIO) - CHECK_INCLUDE_FILE(/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h HAVE_COREAUDIO_FRAMEWORK) - IF(HAVE_COREAUDIO_FRAMEWORK) +OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) +FIND_LIBRARY(COREAUDIO_FRAMEWORK + NAMES CoreAudio + PATHS /System/Library/Frameworks +) +IF(COREAUDIO_FRAMEWORK) + OPTION(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) + IF(ALSOFT_BACKEND_COREAUDIO) SET(HAVE_COREAUDIO 1) SET(ALC_OBJS ${ALC_OBJS} Alc/backends/coreaudio.c) SET(BACKENDS "${BACKENDS} CoreAudio,") - SET(EXTRA_LIBS /System/Library/Frameworks/CoreAudio.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${EXTRA_LIBS}) SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) @@ -820,11 +922,13 @@ IF(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) ENDIF() # Check for OpenSL (Android) backend -IF(ALSOFT_BACKEND_OPENSL) - CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h" HAVE_SLES_OPENSLES_ANDROID_H) - IF(HAVE_SLES_OPENSLES_ANDROID_H) - CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h" OpenSLES "" HAVE_LIBOPENSLES) - IF(HAVE_LIBOPENSLES) +OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) +CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h" HAVE_SLES_OPENSLES_ANDROID_H) +IF(HAVE_SLES_OPENSLES_ANDROID_H) + CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h" OpenSLES "" HAVE_LIBOPENSLES) + IF(HAVE_LIBOPENSLES) + OPTION(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) + IF(ALSOFT_BACKEND_OPENSL) SET(HAVE_OPENSL 1) SET(ALC_OBJS ${ALC_OBJS} Alc/backends/opensl.c) SET(BACKENDS "${BACKENDS} OpenSL,") @@ -837,6 +941,7 @@ IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) ENDIF() # Optionally enable the Wave Writer backend +OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) IF(ALSOFT_BACKEND_WAVE) SET(HAVE_WAVE 1) SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wave.c) @@ -850,11 +955,15 @@ IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) ENDIF() IF(ALSOFT_EXAMPLES) - FIND_PACKAGE(SDL) - IF(SDL_FOUND) + FIND_PACKAGE(SDL2) + IF(SDL2_FOUND) FIND_PACKAGE(SDL_sound) IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + ENDIF() + FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) + IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS}) ENDIF() ENDIF() ENDIF() @@ -881,7 +990,10 @@ CONFIGURE_FILE( "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -# Build a library +# Build a common library with reusable helpers +ADD_LIBRARY(common STATIC ${COMMON_OBJS}) + +# Build main library ADD_LIBRARY(${LIBNAME} ${LIBTYPE} ${OPENAL_OBJS} ${ALC_OBJS}) SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES) IF(WIN32 AND ALSOFT_NO_UID_DEFS) @@ -891,13 +1003,37 @@ SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOU IF(FLUIDSYNTH_FOUND) SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${FLUIDSYNTH_INCLUDE_DIR}) ENDIF() +IF(HAVE_ALSA) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_OSS) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_SOLARIS) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_SNDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_QSA) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_DSOUND) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_PORTAUDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_PULSEAUDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS}) +ENDIF() SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION} SOVERSION ${LIB_MAJOR_VERSION}) IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC") SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "") ENDIF() -TARGET_LINK_LIBRARIES(${LIBNAME} ${EXTRA_LIBS}) +TARGET_LINK_LIBRARIES(${LIBNAME} common ${EXTRA_LIBS}) # Add an install target here INSTALL(TARGETS ${LIBNAME} @@ -946,6 +1082,15 @@ IF(ALSOFT_CONFIG) MESSAGE(STATUS "") ENDIF() +# Install HRTF definitions +IF(ALSOFT_HRTF_DEFS) + INSTALL(FILES hrtf/default-44100.mhr + DESTINATION share/openal/hrtf + ) + MESSAGE(STATUS "Installing HRTF definitions") + MESSAGE(STATUS "") +ENDIF() + IF(ALSOFT_UTILS) ADD_EXECUTABLE(openal-info utils/openal-info.c) TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) @@ -969,26 +1114,30 @@ IF(ALSOFT_UTILS) ENDIF() IF(ALSOFT_EXAMPLES) - IF(SDL_FOUND AND SDL_SOUND_FOUND) + IF(SDL2_FOUND AND SDL_SOUND_FOUND) ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c examples/common/sdl_sound.c) - SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL_SOUND_INCLUDE_DIR}) + SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) ADD_EXECUTABLE(alstream examples/alstream.c) - TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${LIBNAME}) - SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL_SOUND_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) ADD_EXECUTABLE(alreverb examples/alreverb.c) - TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${LIBNAME}) - SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL_SOUND_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) ADD_EXECUTABLE(allatency examples/allatency.c) - TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${LIBNAME}) - SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL_SOUND_INCLUDE_DIR}) + TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) ADD_EXECUTABLE(alloopback examples/alloopback.c) - TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL_LIBRARY} ${LIBNAME}) - SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL_INCLUDE_DIR} + TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) INSTALL(TARGETS alstream alreverb allatency alloopback @@ -997,7 +1146,45 @@ IF(ALSOFT_EXAMPLES) ARCHIVE DESTINATION "lib${LIB_SUFFIX}" ) - MESSAGE(STATUS "Building example programs") + SET(FFVER_OK FALSE) + IF(FFMPEG_FOUND) + SET(FFVER_OK TRUE) + IF(AVFORMAT_VERSION VERSION_LESS "55.33.100") + MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 55.33.100)") + SET(FFVER_OK FALSE) + ENDIF() + IF(AVCODEC_VERSION VERSION_LESS "55.52.102") + MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 55.52.102)") + SET(FFVER_OK FALSE) + ENDIF() + IF(AVUTIL_VERSION VERSION_LESS "52.66.100") + MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 52.66.100)") + SET(FFVER_OK FALSE) + ENDIF() + IF(SWSCALE_VERSION VERSION_LESS "2.5.102") + MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 2.5.102)") + SET(FFVER_OK FALSE) + ENDIF() + IF(SWRESAMPLE_VERSION VERSION_LESS "0.18.100") + MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 0.18.100)") + SET(FFVER_OK FALSE) + ENDIF() + ENDIF() + IF(FFVER_OK AND NOT MSVC) + ADD_EXECUTABLE(alffplay examples/alffplay.c) + TARGET_LINK_LIBRARIES(alffplay common ex-common ${SDL2_LIBRARY} ${LIBNAME} ${FFMPEG_LIBRARIES}) + SET_PROPERTY(TARGET alffplay APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${FFMPEG_INCLUDE_DIRS}) + + INSTALL(TARGETS alffplay + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + MESSAGE(STATUS "Building SDL and FFmpeg example programs") + ELSE() + MESSAGE(STATUS "Building SDL example programs") + ENDIF() MESSAGE(STATUS "") ENDIF() ENDIF() diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h index c7182d9b..bc871111 100644 --- a/OpenAL32/Include/alAuxEffectSlot.h +++ b/OpenAL32/Include/alAuxEffectSlot.h @@ -4,6 +4,8 @@ #include "alMain.h" #include "alEffect.h" +#include "align.h" + #ifdef __cplusplus extern "C" { #endif @@ -22,7 +24,7 @@ struct ALeffectStateVtable { void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot); void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE]); - void (*const Delete)(struct ALeffectState *state); + void (*const Delete)(void *ptr); }; #define DEFINE_ALEFFECTSTATE_VTABLE(T) \ @@ -30,7 +32,8 @@ DECLARE_THUNK(T, ALeffectState, void, Destruct) \ DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \ DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \ DECLARE_THUNK3(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict) \ -DECLARE_THUNK(T, ALeffectState, void, Delete) \ +static void T##_ALeffectState_Delete(void *ptr) \ +{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \ \ static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \ T##_ALeffectState_Destruct, \ @@ -71,10 +74,7 @@ typedef struct ALeffectslot { volatile ALenum NeedsUpdate; ALeffectState *EffectState; - ALIGN(16) ALfloat WetBuffer[1][BUFFERSIZE]; - - ALfloat ClickRemoval[1]; - ALfloat PendingClicks[1]; + alignas(16) ALfloat WetBuffer[1][BUFFERSIZE]; RefCount ref; diff --git a/OpenAL32/Include/alBuffer.h b/OpenAL32/Include/alBuffer.h index d22b3b9b..12a682f4 100644 --- a/OpenAL32/Include/alBuffer.h +++ b/OpenAL32/Include/alBuffer.h @@ -22,6 +22,7 @@ enum UserFmtType { UserFmtMulaw, UserFmtAlaw, UserFmtIMA4, + UserFmtMSADPCM, }; enum UserFmtChannels { UserFmtMono = AL_MONO_SOFT, @@ -33,8 +34,8 @@ enum UserFmtChannels { UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */ }; -ALuint BytesFromUserFmt(enum UserFmtType type); -ALuint ChannelsFromUserFmt(enum UserFmtChannels chans); +ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST; +ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST; inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type) { return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type); @@ -58,8 +59,8 @@ enum FmtChannels { }; #define MAX_INPUT_CHANNELS (8) -ALuint BytesFromFmt(enum FmtType type); -ALuint ChannelsFromFmt(enum FmtChannels chans); +ALuint BytesFromFmt(enum FmtType type) DECL_CONST; +ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST; inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) { return ChannelsFromFmt(chans) * BytesFromFmt(type); @@ -79,10 +80,14 @@ typedef struct ALbuffer { enum UserFmtChannels OriginalChannels; enum UserFmtType OriginalType; ALsizei OriginalSize; + ALsizei OriginalAlign; ALsizei LoopStart; ALsizei LoopEnd; + ALsizei UnpackAlign; + ALsizei PackAlign; + /* Number of times buffer was attached to a source (deletion can only occur when 0) */ RefCount ref; diff --git a/OpenAL32/Include/alFilter.h b/OpenAL32/Include/alFilter.h index 5167c125..62b5fda8 100644 --- a/OpenAL32/Include/alFilter.h +++ b/OpenAL32/Include/alFilter.h @@ -7,7 +7,8 @@ extern "C" { #endif -#define LOWPASSFREQREF (5000) +#define LOWPASSFREQREF (5000.0f) +#define HIGHPASSFREQREF (250.0f) /* Filters implementation is based on the "Cookbook formulae for audio * @@ -15,12 +16,18 @@ extern "C" { * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */ typedef enum ALfilterType { + /** EFX-style low-pass filter, specifying a gain and reference frequency. */ ALfilterType_HighShelf, + /** EFX-style high-pass filter, specifying a gain and reference frequency. */ ALfilterType_LowShelf, + /** Peaking filter, specifying a gain, reference frequency, and bandwidth. */ ALfilterType_Peaking, + /** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */ ALfilterType_LowPass, + /** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */ ALfilterType_HighPass, + /** Band-pass filter, specifying a center frequency and bandwidth. */ ALfilterType_BandPass, } ALfilterType; @@ -29,10 +36,13 @@ typedef struct ALfilterState { ALfloat y[2]; /* History of two last output samples */ ALfloat a[3]; /* Transfer function coefficients "a" */ ALfloat b[3]; /* Transfer function coefficients "b" */ + + void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); } ALfilterState; +#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__)) void ALfilterState_clear(ALfilterState *filter); -void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_scale, ALfloat bandwidth); +void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth); inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample) { @@ -51,14 +61,7 @@ inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample return outsmp; } -inline ALfloat ALfilterState_processSingleC(const ALfilterState *filter, ALfloat sample) -{ - return filter->b[0] * sample + - filter->b[1] * filter->x[0] + - filter->b[2] * filter->x[1] - - filter->a[1] * filter->y[0] - - filter->a[2] * filter->y[1]; -} +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); typedef struct ALfilter { @@ -67,6 +70,9 @@ typedef struct ALfilter { ALfloat Gain; ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val); void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals); diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 934a4e0f..232c438c 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -6,6 +6,7 @@ #include <stdarg.h> #include <assert.h> #include <math.h> +#include <limits.h> #ifdef HAVE_STRINGS_H #include <strings.h> @@ -19,8 +20,22 @@ #include "AL/alc.h" #include "AL/alext.h" + +#if defined(_WIN64) +#define SZFMT "%I64u" +#elif defined(_WIN32) +#define SZFMT "%u" +#else +#define SZFMT "%zu" +#endif + + +#include "static_assert.h" +#include "align.h" #include "atomic.h" #include "uintmap.h" +#include "vector.h" +#include "alstring.h" #ifndef ALC_SOFT_HRTF #define ALC_SOFT_HRTF 1 @@ -239,16 +254,17 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, #endif #endif +#ifndef AL_SOFT_MSADPCM +#define AL_SOFT_MSADPCM 1 +#define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302 +#define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303 +#endif + #ifdef IN_IDE_PARSER /* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but * recent versions (at least 4.5.1) do recognize GCC's __restrict. */ #define restrict __restrict -/* KDevelop won't see the ALIGN macro from config.h when viewing files that - * don't include it directly (e.g. headers). */ -#ifndef ALIGN -#define ALIGN(x) -#endif #endif @@ -284,10 +300,12 @@ typedef ptrdiff_t ALsizeiptrEXT; #endif #endif -#ifdef HAVE_GCC_FORMAT -#define PRINTF_STYLE(x, y) __attribute__((format(printf, (x), (y)))) +#ifdef __GNUC__ +#define DECL_CONST __attribute__((const)) +#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z)))) #else -#define PRINTF_STYLE(x, y) +#define DECL_CONST +#define DECL_FORMAT(x, y, z) #endif #if defined(__GNUC__) && defined(__i386__) @@ -300,6 +318,20 @@ typedef ptrdiff_t ALsizeiptrEXT; #define FORCE_ALIGN #endif +#ifdef HAVE_C99_VLA +#define DECL_VLA(T, _name, _size) T _name[(_size)] +#else +#define DECL_VLA(T, _name, _size) T *_name = alloca((_size) * sizeof(T)) +#endif + +#ifndef PATH_MAX +#ifdef MAX_PATH +#define PATH_MAX MAX_PATH +#else +#define PATH_MAX 4096 +#endif +#endif + static const union { ALuint u; @@ -312,8 +344,15 @@ static const union { #define DERIVE_FROM_TYPE(t) t t##_parent #define STATIC_CAST(to, obj) (&(obj)->to##_parent) +#ifdef __GNUC__ +#define STATIC_UPCAST(to, from, obj) __extension__({ \ + static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \ + "Invalid upcast object from type"); \ + (to*)((char*)(obj) - offsetof(to, from##_parent)); \ +}) +#else #define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent))) - +#endif #define DECLARE_FORWARD(T1, T2, rettype, func) \ rettype T1##_##func(T1 *obj) \ @@ -354,6 +393,9 @@ static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \ static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \ { return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); } +#define DECLARE_DEFAULT_ALLOCATORS(T) \ +static void* T##_New(size_t size) { return malloc(size); } \ +static void T##_Delete(void *ptr) { free(ptr); } /* Helper to extract an argument list for VCALL. Not used directly. */ #define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) @@ -448,12 +490,6 @@ void alc_solaris_probe(enum DevProbe type); ALCboolean alc_sndio_init(BackendFuncs *func_list); void alc_sndio_deinit(void); void alc_sndio_probe(enum DevProbe type); -ALCboolean alcMMDevApiInit(BackendFuncs *func_list); -void alcMMDevApiDeinit(void); -void alcMMDevApiProbe(enum DevProbe type); -ALCboolean alcDSoundInit(BackendFuncs *func_list); -void alcDSoundDeinit(void); -void alcDSoundProbe(enum DevProbe type); ALCboolean alcWinMMInit(BackendFuncs *FuncList); void alcWinMMDeinit(void); void alcWinMMProbe(enum DevProbe type); @@ -537,8 +573,8 @@ enum DevFmtChannels { DevFmtChannelsDefault = DevFmtStereo }; -ALuint BytesFromDevFmt(enum DevFmtType type); -ALuint ChannelsFromDevFmt(enum DevFmtChannels chans); +ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST; +ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST; inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type) { return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type); @@ -570,7 +606,7 @@ enum DeviceType { struct ALCdevice_struct { - volatile RefCount ref; + RefCount ref; ALCboolean Connected; enum DeviceType Type; @@ -581,7 +617,7 @@ struct ALCdevice_struct enum DevFmtChannels FmtChans; enum DevFmtType FmtType; - ALCchar *DeviceName; + al_string DeviceName; volatile ALCenum LastError; @@ -637,15 +673,20 @@ struct ALCdevice_struct ALuint64 ClockBase; ALuint SamplesDone; - /* Temp storage used for mixing. +1 for the predictive sample. */ - ALIGN(16) ALfloat SampleData1[BUFFERSIZE+1]; - ALIGN(16) ALfloat SampleData2[BUFFERSIZE+1]; + /* Temp storage used for each source when mixing. */ + alignas(16) ALfloat SourceData[BUFFERSIZE]; + alignas(16) ALfloat ResampledData[BUFFERSIZE]; + alignas(16) ALfloat FilteredData[BUFFERSIZE]; // Dry path buffer mix - ALIGN(16) ALfloat DryBuffer[MaxChannels][BUFFERSIZE]; + alignas(16) ALfloat DryBuffer[MaxChannels][BUFFERSIZE]; - ALIGN(16) ALfloat ClickRemoval[MaxChannels]; - ALIGN(16) ALfloat PendingClicks[MaxChannels]; + /* Running count of the mixer invocations, in 31.1 fixed point. This + * actually increments *twice* when mixing, first at the start and then at + * the end, so the bottom bit indicates if the device is currently mixing + * and the upper bits indicates how many mixes have been done. + */ + RefCount MixCount; /* Default effect slot */ struct ALeffectslot *DefaultSlot; @@ -655,10 +696,12 @@ struct ALCdevice_struct struct ALCbackend *Backend; - BackendFuncs *Funcs; void *ExtraData; // For the backend's use ALCdevice *volatile next; + + /* Memory space used by the default slot (Playback devices only) */ + alignas(16) ALCbyte _slot_mem[]; }; // Frequency was requested by the app or config file @@ -692,9 +735,13 @@ struct ALCdevice_struct #define MIXER_THREAD_NAME "alsoft-mixer" +typedef struct ALeffectslot *ALeffectslotPtr; +DECL_VECTOR(ALeffectslotPtr) + + struct ALCcontext_struct { - volatile RefCount ref; + RefCount ref; struct ALlistener *Listener; @@ -713,18 +760,19 @@ struct ALCcontext_struct volatile ALfloat SpeedOfSound; volatile ALenum DeferUpdates; - struct ALsource **ActiveSources; - ALsizei ActiveSourceCount; - ALsizei MaxActiveSources; + struct ALactivesource **ActiveSources; + ALsizei ActiveSourceCount; + ALsizei MaxActiveSources; - struct ALeffectslot **ActiveEffectSlots; - ALsizei ActiveEffectSlotCount; - ALsizei MaxActiveEffectSlots; + vector_ALeffectslotPtr ActiveAuxSlots; ALCdevice *Device; const ALCchar *ExtensionList; ALCcontext *volatile next; + + /* Memory space used by the listener */ + alignas(16) ALCbyte _listener_mem[]; }; ALCcontext *GetContextRef(void); @@ -789,31 +837,17 @@ void SetRTPriority(void); void SetDefaultChannelOrder(ALCdevice *device); void SetDefaultWFXChannelOrder(ALCdevice *device); -const ALCchar *DevFmtTypeString(enum DevFmtType type); -const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans); - -#define HRIR_BITS (7) -#define HRIR_LENGTH (1<<HRIR_BITS) -#define HRIR_MASK (HRIR_LENGTH-1) -#define HRTFDELAY_BITS (20) -#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS) -#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1) -const struct Hrtf *GetHrtf(ALCdevice *device); -void FindHrtfFormat(const ALCdevice *device, enum DevFmtChannels *chans, ALCuint *srate); -void FreeHrtfs(void); -ALuint GetHrtfIrSize(const struct Hrtf *Hrtf); -ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]); -void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays); -ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep); +const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST; +const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST; extern FILE *LogFile; -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(IN_IDE_PARSER) #define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__) #else -void al_print(const char *type, const char *func, const char *fmt, ...) PRINTF_STYLE(3,4); -#define AL_PRINT(T, MSG, ...) al_print((T), __FUNCTION__, MSG, __VA_ARGS__) +void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4); +#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__) #endif enum LogLevel { @@ -853,11 +887,13 @@ extern ALuint CPUCapFlags; enum { CPU_CAP_SSE = 1<<0, CPU_CAP_SSE2 = 1<<1, - CPU_CAP_NEON = 1<<2, + CPU_CAP_SSE4_1 = 1<<2, + CPU_CAP_NEON = 1<<3, }; void FillCPUCaps(ALuint capfilter); +FILE *OpenDataFile(const char *fname, const char *subdir); /* Small hack to use a pointer-to-array type as a normal argument type. * Shouldn't be used directly. */ diff --git a/OpenAL32/Include/alMidi.h b/OpenAL32/Include/alMidi.h index 24e6675f..d18c165e 100644 --- a/OpenAL32/Include/alMidi.h +++ b/OpenAL32/Include/alMidi.h @@ -32,7 +32,7 @@ typedef struct ALenvelope { typedef struct ALfontsound { - volatile RefCount ref; + RefCount ref; ALint MinKey, MaxKey; ALint MinVelocity, MaxVelocity; @@ -110,7 +110,7 @@ void ReleaseALFontsounds(ALCdevice *device); typedef struct ALsfpreset { - volatile RefCount ref; + RefCount ref; ALint Preset; /* a.k.a. MIDI program number */ ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */ @@ -133,7 +133,7 @@ void ReleaseALPresets(ALCdevice *device); typedef struct ALsoundfont { - volatile RefCount ref; + RefCount ref; ALsfpreset **Presets; ALsizei NumPresets; diff --git a/OpenAL32/Include/alSource.h b/OpenAL32/Include/alSource.h index 31a0d229..f87f1672 100644 --- a/OpenAL32/Include/alSource.h +++ b/OpenAL32/Include/alSource.h @@ -5,17 +5,12 @@ #include "alMain.h" #include "alu.h" -#include "alFilter.h" -#include "alBuffer.h" +#include "hrtf.h" #ifdef __cplusplus extern "C" { #endif -#define SRC_HISTORY_BITS (6) -#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS) -#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1) - extern enum Resampler DefaultResampler; extern const ALsizei ResamplerPadding[ResamplerMax]; @@ -28,57 +23,32 @@ typedef struct ALbufferlistitem { struct ALbufferlistitem *prev; } ALbufferlistitem; -typedef struct HrtfState { - ALboolean Moving; - ALuint Counter; - ALIGN(16) ALfloat History[MAX_INPUT_CHANNELS][SRC_HISTORY_LENGTH]; - ALIGN(16) ALfloat Values[MAX_INPUT_CHANNELS][HRIR_LENGTH][2]; - ALuint Offset; -} HrtfState; - -typedef struct HrtfParams { - ALfloat Gain; - ALfloat Dir[3]; - ALIGN(16) ALfloat Coeffs[MAX_INPUT_CHANNELS][HRIR_LENGTH][2]; - ALIGN(16) ALfloat CoeffStep[HRIR_LENGTH][2]; - ALuint Delay[MAX_INPUT_CHANNELS][2]; - ALint DelayStep[2]; - ALuint IrSize; -} HrtfParams; - -typedef struct DirectParams { - ALfloat (*OutBuffer)[BUFFERSIZE]; - ALfloat *ClickRemoval; - ALfloat *PendingClicks; - struct { - HrtfParams Params; - HrtfState *State; - } Hrtf; +typedef struct ALactivesource { + struct ALsource *Source; - /* A mixing matrix. First subscript is the channel number of the input data - * (regardless of channel configuration) and the second is the channel - * target (eg. FrontLeft). Not used with HRTF. */ - ALfloat Gains[MAX_INPUT_CHANNELS][MaxChannels]; + /** Method to update mixing parameters. */ + ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context); - ALfilterState LpFilter[MAX_INPUT_CHANNELS]; -} DirectParams; + /** Current target parameters used for mixing. */ + ResamplerFunc Resample; + union { + DryMixerFunc Mix; + HrtfMixerFunc HrtfMix; + } Dry; + WetMixerFunc WetMix; -typedef struct SendParams { - ALfloat (*OutBuffer)[BUFFERSIZE]; - ALfloat *ClickRemoval; - ALfloat *PendingClicks; + ALboolean IsHrtf; + ALint Step; - /* Gain control, which applies to all input channels to a single (mono) - * output buffer. */ - ALfloat Gain; + ALuint Offset; /* Number of output samples mixed since starting. */ - ALfilterState LpFilter[MAX_INPUT_CHANNELS]; -} SendParams; + DirectParams Direct; + SendParams Send[MAX_SENDS]; +} ALactivesource; -typedef struct ALsource -{ +typedef struct ALsource { /** Source properties. */ volatile ALfloat Pitch; volatile ALfloat Gain; @@ -132,49 +102,37 @@ typedef struct ALsource ALuint position_fraction; /** Source Buffer Queue info. */ - ALbufferlistitem *queue; - ALuint BuffersInQueue; - ALuint BuffersPlayed; + ALbufferlistitem *volatile queue; + ALbufferlistitem *volatile current_buffer; + RWLock queue_lock; /** Current buffer sample info. */ ALuint NumChannels; ALuint SampleSize; /** Direct filter and auxiliary send info. */ - ALfloat DirectGain; - ALfloat DirectGainHF; - + struct { + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + } Direct; struct { struct ALeffectslot *Slot; ALfloat Gain; ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; } Send[MAX_SENDS]; - /** HRTF info. */ - HrtfState Hrtf; - - /** Current target parameters used for mixing. */ - struct { - ResamplerFunc Resample; - DryMixerFunc DryMix; - WetMixerFunc WetMix; - - ALint Step; - - DirectParams Direct; - - SendParams Send[MAX_SENDS]; - } Params; /** Source needs to update its mixing parameters. */ volatile ALenum NeedsUpdate; - /** Method to update mixing parameters. */ - ALvoid (*Update)(struct ALsource *self, const ALCcontext *context); - /** Self ID */ ALuint id; } ALsource; -#define ALsource_Update(s,a) ((s)->Update(s,a)) inline struct ALsource *LookupSource(ALCcontext *context, ALuint id) { return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); } diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h index d88e860c..c4b4d674 100644 --- a/OpenAL32/Include/alu.h +++ b/OpenAL32/Include/alu.h @@ -1,8 +1,6 @@ #ifndef _ALU_H_ #define _ALU_H_ -#include "alMain.h" - #include <limits.h> #include <math.h> #ifdef HAVE_FLOAT_H @@ -12,6 +10,13 @@ #include <ieeefp.h> #endif +#include "alMain.h" +#include "alBuffer.h" +#include "alFilter.h" + +#include "hrtf.h" +#include "align.h" + #define F_PI (3.14159265358979323846f) #define F_PI_2 (1.57079632679489661923f) @@ -25,29 +30,112 @@ #define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI)) -#ifdef __cplusplus -extern "C" { -#endif - -struct ALsource; -struct ALbuffer; -struct DirectParams; -struct SendParams; +#define SRC_HISTORY_BITS (6) +#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS) +#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1) -typedef void (*ResamplerFunc)(const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint dstlen); +#define MAX_PITCH (10) -typedef ALvoid (*DryMixerFunc)(const struct DirectParams *params, - const ALfloat *restrict data, ALuint srcchan, - ALuint OutPos, ALuint SamplesToDo, - ALuint BufferSize); -typedef ALvoid (*WetMixerFunc)(const struct SendParams *params, - const ALfloat *restrict data, - ALuint OutPos, ALuint SamplesToDo, - ALuint BufferSize); +#ifdef __cplusplus +extern "C" { +#endif -#define GAIN_SILENCE_THRESHOLD (0.00001f) +enum ActiveFilters { + AF_None = 0, + AF_LowPass = 1, + AF_HighPass = 2, + AF_BandPass = AF_LowPass | AF_HighPass +}; + + +typedef struct HrtfState { + alignas(16) ALfloat History[SRC_HISTORY_LENGTH]; + alignas(16) ALfloat Values[HRIR_LENGTH][2]; +} HrtfState; + +typedef struct HrtfParams { + alignas(16) ALfloat Coeffs[HRIR_LENGTH][2]; + alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2]; + ALuint Delay[2]; + ALint DelayStep[2]; +} HrtfParams; + + +typedef struct MixGains { + ALfloat Current[MaxChannels]; + ALfloat Step[MaxChannels]; + ALfloat Target[MaxChannels]; +} MixGains; + +typedef struct MixGainMono { + ALfloat Current; + ALfloat Step; + ALfloat Target; +} MixGainMono; + + +typedef struct DirectParams { + ALfloat (*OutBuffer)[BUFFERSIZE]; + + /* If not 'moving', gain/coefficients are set directly without fading. */ + ALboolean Moving; + /* Stepping counter for gain/coefficient fading. */ + ALuint Counter; + + struct { + enum ActiveFilters ActiveType; + ALfilterState LowPass; + ALfilterState HighPass; + } Filters[MAX_INPUT_CHANNELS]; + + union { + struct { + HrtfParams Params[MAX_INPUT_CHANNELS]; + HrtfState State[MAX_INPUT_CHANNELS]; + ALuint IrSize; + ALfloat Gain; + ALfloat Dir[3]; + } Hrtf; + + MixGains Gains[MAX_INPUT_CHANNELS]; + } Mix; +} DirectParams; + +typedef struct SendParams { + ALfloat (*OutBuffer)[BUFFERSIZE]; + + ALboolean Moving; + ALuint Counter; + + struct { + enum ActiveFilters ActiveType; + ALfilterState LowPass; + ALfilterState HighPass; + } Filters[MAX_INPUT_CHANNELS]; + + /* Gain control, which applies to all input channels to a single (mono) + * output buffer. */ + MixGainMono Gain; +} SendParams; + + +typedef const ALfloat* (*ResamplerFunc)(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint dstlen); + +typedef void (*DryMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGains *Gains, ALuint Counter, ALuint OutPos, + ALuint BufferSize); +typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, + const ALuint IrSize, const HrtfParams *hrtfparams, + HrtfState *hrtfstate, ALuint BufferSize); +typedef void (*WetMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + MixGainMono *Gain, ALuint Counter, ALuint OutPos, + ALuint BufferSize); + + +#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */ #define SPEEDOFSOUNDMETRESPERSEC (343.3f) #define AIRABSORBGAINHF (0.99426f) /* -0.05dB */ @@ -137,10 +225,10 @@ inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxC } -ALvoid CalcSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext); -ALvoid CalcNonAttnSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext); +ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext); +ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext); -ALvoid MixSource(struct ALsource *Source, ALCdevice *Device, ALuint SamplesToDo); +ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo); ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size); /* Caller must lock the device. */ diff --git a/OpenAL32/Include/sample_cvt.h b/OpenAL32/Include/sample_cvt.h new file mode 100644 index 00000000..12bb1fa6 --- /dev/null +++ b/OpenAL32/Include/sample_cvt.h @@ -0,0 +1,9 @@ +#ifndef SAMPLE_CVT_H +#define SAMPLE_CVT_H + +#include "AL/al.h" +#include "alBuffer.h" + +void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align); + +#endif /* SAMPLE_CVT_H */ diff --git a/OpenAL32/Include/threads.h b/OpenAL32/Include/threads.h deleted file mode 100644 index 26493101..00000000 --- a/OpenAL32/Include/threads.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef AL_THREADS_H -#define AL_THREADS_H - -#include "alMain.h" - -struct althread_info; -typedef struct althread_info* althread_t; - -ALboolean StartThread(althread_t *out, ALuint (*func)(ALvoid*), ALvoid *ptr); -ALuint StopThread(althread_t thread); - -void SetThreadName(const char *name); - -#endif /* AL_THREADS_H */ diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c index aa071de2..9f554cb0 100644 --- a/OpenAL32/alAuxEffectSlot.c +++ b/OpenAL32/alAuxEffectSlot.c @@ -35,8 +35,8 @@ extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id); extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id); -static ALenum AddEffectSlotArray(ALCcontext *Context, ALsizei count, const ALuint *slots); -static ALvoid RemoveEffectSlotArray(ALCcontext *Context, ALeffectslot *slot); +static ALenum AddEffectSlotArray(ALCcontext *Context, const_vector_ALeffectslotPtr slots); +static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot); static UIntMap EffectStateFactoryMap; @@ -52,14 +52,20 @@ static inline ALeffectStateFactory *getFactoryByType(ALenum type) AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) { ALCcontext *context; + vector_ALeffectslotPtr slotvec; ALsizei cur; ALenum err; context = GetContextRef(); if(!context) return; + VECTOR_INIT(slotvec); + if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!VECTOR_RESERVE(slotvec, n)) + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + for(cur = 0;cur < n;cur++) { ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot)); @@ -84,9 +90,11 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo SET_ERROR_AND_GOTO(context, err, done); } + VECTOR_PUSH_BACK(slotvec, slot); + effectslots[cur] = slot->id; } - err = AddEffectSlotArray(context, n, effectslots); + err = AddEffectSlotArray(context, slotvec); if(err != AL_NO_ERROR) { alDeleteAuxiliaryEffectSlots(cur, effectslots); @@ -94,6 +102,8 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo } done: + VECTOR_DEINIT(slotvec); + ALCcontext_DecRef(context); } @@ -112,7 +122,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * { if((slot=LookupEffectSlot(context, effectslots[i])) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(slot->ref != 0) + if(ReadRef(&slot->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -375,56 +385,37 @@ done: } -static ALvoid RemoveEffectSlotArray(ALCcontext *context, ALeffectslot *slot) +static ALenum AddEffectSlotArray(ALCcontext *context, const_vector_ALeffectslotPtr slots) { - ALeffectslot **slotlist, **slotlistend; + ALenum err = AL_NO_ERROR; LockContext(context); - slotlist = context->ActiveEffectSlots; - slotlistend = slotlist + context->ActiveEffectSlotCount; - while(slotlist != slotlistend) - { - if(*slotlist == slot) - { - *slotlist = *(--slotlistend); - context->ActiveEffectSlotCount--; - break; - } - slotlist++; - } + if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), + VECTOR_ITER_BEGIN(slots), VECTOR_ITER_END(slots))) + err = AL_OUT_OF_MEMORY; UnlockContext(context); + + return err; } -static ALenum AddEffectSlotArray(ALCcontext *context, ALsizei count, const ALuint *slots) +static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot) { - ALsizei i; + ALeffectslot **slotlist, **slotlistend; LockContext(context); - if(count > context->MaxActiveEffectSlots-context->ActiveEffectSlotCount) + slotlist = VECTOR_ITER_BEGIN(context->ActiveAuxSlots); + slotlistend = VECTOR_ITER_END(context->ActiveAuxSlots); + while(slotlist != slotlistend) { - ALsizei newcount; - void *temp = NULL; - - newcount = context->MaxActiveEffectSlots ? (context->MaxActiveEffectSlots<<1) : 1; - if(newcount > context->MaxActiveEffectSlots) - temp = realloc(context->ActiveEffectSlots, - newcount * sizeof(*context->ActiveEffectSlots)); - if(!temp) + if(*slotlist == slot) { - UnlockContext(context); - return AL_OUT_OF_MEMORY; + *slotlist = VECTOR_BACK(context->ActiveAuxSlots); + VECTOR_POP_BACK(context->ActiveAuxSlots); + break; } - context->ActiveEffectSlots = temp; - context->MaxActiveEffectSlots = newcount; - } - for(i = 0;i < count;i++) - { - ALeffectslot *slot = LookupEffectSlot(context, slots[i]); - assert(slot != NULL); - context->ActiveEffectSlots[context->ActiveEffectSlotCount++] = slot; + slotlist++; } UnlockContext(context); - return AL_NO_ERROR; } @@ -541,10 +532,8 @@ ALenum InitEffectSlot(ALeffectslot *slot) { for(i = 0;i < BUFFERSIZE;i++) slot->WetBuffer[c][i] = 0.0f; - slot->ClickRemoval[c] = 0.0f; - slot->PendingClicks[c] = 0.0f; } - slot->ref = 0; + InitRef(&slot->ref, 0); return AL_NO_ERROR; } diff --git a/OpenAL32/alBuffer.c b/OpenAL32/alBuffer.c index d9c91c2f..2b2528f6 100644 --- a/OpenAL32/alBuffer.c +++ b/OpenAL32/alBuffer.c @@ -24,12 +24,16 @@ #include <stdio.h> #include <assert.h> #include <limits.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif #include "alMain.h" #include "alu.h" #include "alError.h" #include "alBuffer.h" #include "alThunk.h" +#include "sample_cvt.h" extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id); @@ -37,152 +41,12 @@ extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id); extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); -static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels chans, enum UserFmtType type, const ALvoid *data, ALboolean storesrc); -static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len); -static ALboolean IsValidType(ALenum type); -static ALboolean IsValidChannels(ALenum channels); -static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type); -static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type); - - -/* - * Global Variables - */ - -/* IMA ADPCM Stepsize table */ -static const int IMAStep_size[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, - 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, - 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, - 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, - 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, - 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, - 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, - 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, - 32767 -}; - -/* IMA4 ADPCM Codeword decode table */ -static const int IMA4Codeword[16] = { - 1, 3, 5, 7, 9, 11, 13, 15, - -1,-3,-5,-7,-9,-11,-13,-15, -}; - -/* IMA4 ADPCM Step index adjust decode table */ -static const int IMA4Index_adjust[16] = { - -1,-1,-1,-1, 2, 4, 6, 8, - -1,-1,-1,-1, 2, 4, 6, 8 -}; - -/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a - * signed 16-bit sample */ -static const ALshort muLawDecompressionTable[256] = { - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -/* Values used when encoding a muLaw sample */ -static const int muLawBias = 0x84; -static const int muLawClip = 32635; -static const char muLawCompressTable[256] = { - 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; - - -/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a - * signed 16-bit sample */ -static const ALshort aLawDecompressionTable[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -/* Values used when encoding an aLaw sample */ -static const int aLawClip = 32635; -static const char aLawCompressTable[128] = { - 1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; +static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels chans, enum UserFmtType type, const ALvoid *data, ALsizei align, ALboolean storesrc); +static ALboolean IsValidType(ALenum type) DECL_CONST; +static ALboolean IsValidChannels(ALenum channels) DECL_CONST; +static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type) DECL_CONST; +static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) DECL_CONST; +static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align); AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) @@ -251,7 +115,7 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) /* Check for valid Buffer ID */ if((ALBuf=LookupBuffer(device, buffers[i])) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(ALBuf->ref != 0) + if(ReadRef(&ALBuf->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -294,9 +158,10 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi enum UserFmtType srctype; ALCdevice *device; ALCcontext *context; - ALuint framesize; - ALenum newformat; ALbuffer *albuf; + ALenum newformat = AL_NONE; + ALuint framesize; + ALsizei align; ALenum err; context = GetContextRef(); @@ -309,6 +174,10 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + align = albuf->UnpackAlign; + if(SanitizeAlignment(srctype, &align) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); switch(srctype) { case UserFmtByte: @@ -316,12 +185,12 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtShort: case UserFmtUShort: case UserFmtFloat: - framesize = FrameSizeFromUserFmt(srcchannels, srctype); - if(!((size%framesize) == 0)) + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - err = LoadData(albuf, freq, format, size/framesize, - srcchannels, srctype, data, AL_TRUE); + err = LoadData(albuf, freq, format, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -331,11 +200,10 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtByte3: case UserFmtUByte3: case UserFmtDouble: - framesize = FrameSizeFromUserFmt(srcchannels, srctype); - if(!((size%framesize) == 0)) + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - newformat = AL_FORMAT_MONO_FLOAT32; switch(srcchannels) { case UserFmtMono: newformat = AL_FORMAT_MONO_FLOAT32; break; @@ -346,19 +214,18 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtX61: newformat = AL_FORMAT_61CHN32; break; case UserFmtX71: newformat = AL_FORMAT_71CHN32; break; } - err = LoadData(albuf, freq, newformat, size/framesize, - srcchannels, srctype, data, AL_TRUE); + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; case UserFmtMulaw: case UserFmtAlaw: - framesize = FrameSizeFromUserFmt(srcchannels, srctype); - if(!((size%framesize) == 0)) + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - newformat = AL_FORMAT_MONO16; switch(srcchannels) { case UserFmtMono: newformat = AL_FORMAT_MONO16; break; @@ -369,22 +236,18 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; } - err = LoadData(albuf, freq, newformat, size/framesize, - srcchannels, srctype, data, AL_TRUE); + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; case UserFmtIMA4: - /* Here is where things vary: - * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel - * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel - */ - framesize = ChannelsFromUserFmt(srcchannels) * 36; - if(!((size%framesize) == 0)) + framesize = (align-1)/2 + 4; + framesize *= ChannelsFromUserFmt(srcchannels); + if((size%framesize) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - newformat = AL_FORMAT_MONO16; switch(srcchannels) { case UserFmtMono: newformat = AL_FORMAT_MONO16; break; @@ -395,8 +258,30 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; } - err = LoadData(albuf, freq, newformat, size/framesize*65, - srcchannels, srctype, data, AL_TRUE); + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + + case UserFmtMSADPCM: + framesize = (align-2)/2 + 7; + framesize *= ChannelsFromUserFmt(srcchannels); + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + switch(srcchannels) + { + case UserFmtMono: newformat = AL_FORMAT_MONO16; break; + case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break; + case UserFmtRear: newformat = AL_FORMAT_REAR16; break; + case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break; + case UserFmtX51: newformat = AL_FORMAT_51CHN16; break; + case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; + case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; + } + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -413,9 +298,10 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons ALCdevice *device; ALCcontext *context; ALbuffer *albuf; - ALuint original_align; + ALuint byte_align; ALuint channels; ALuint bytes; + ALsizei align; context = GetContextRef(); if(!context) return; @@ -429,39 +315,55 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); WriteLock(&albuf->lock); - original_align = ((albuf->OriginalType == UserFmtIMA4) ? - (ChannelsFromUserFmt(albuf->OriginalChannels)*36) : - FrameSizeFromUserFmt(albuf->OriginalChannels, - albuf->OriginalType)); - + align = albuf->UnpackAlign; + if(SanitizeAlignment(srctype, &align) == AL_FALSE) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } if(srcchannels != albuf->OriginalChannels || srctype != albuf->OriginalType) { WriteUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } - if(offset > albuf->OriginalSize || length > albuf->OriginalSize-offset || - (offset%original_align) != 0 || (length%original_align) != 0) + if(align != albuf->OriginalAlign) { WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } - channels = ChannelsFromFmt(albuf->FmtChannels); - bytes = BytesFromFmt(albuf->FmtType); - /* offset -> byte offset, length -> sample count */ - if(srctype == UserFmtIMA4) + if(albuf->OriginalType == UserFmtIMA4) { - offset = offset/36*65 * bytes; - length = length/original_align * 65; + byte_align = (albuf->OriginalAlign-1)/2 + 4; + byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); + } + else if(albuf->OriginalType == UserFmtMSADPCM) + { + byte_align = (albuf->OriginalAlign-2)/2 + 7; + byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); } else { - ALuint OldBytes = BytesFromUserFmt(srctype); - offset = offset/OldBytes * bytes; - length = length/OldBytes/channels; + byte_align = albuf->OriginalAlign; + byte_align *= FrameSizeFromUserFmt(albuf->OriginalChannels, + albuf->OriginalType); } + + if(offset > albuf->OriginalSize || length > albuf->OriginalSize-offset || + (offset%byte_align) != 0 || (length%byte_align) != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + channels = ChannelsFromFmt(albuf->FmtChannels); + bytes = BytesFromFmt(albuf->FmtType); + /* offset -> byte offset, length -> sample count */ + offset = offset/byte_align * channels*bytes; + length = length/byte_align * albuf->OriginalAlign; + ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - data, srctype, channels, length); + data, srctype, channels, length, align); WriteUnlock(&albuf->lock); done: @@ -476,6 +378,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALCdevice *device; ALCcontext *context; ALbuffer *albuf; + ALsizei align; ALenum err; context = GetContextRef(); @@ -489,8 +392,14 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + align = albuf->UnpackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if((samples%align) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + err = LoadData(albuf, samplerate, internalformat, samples, - channels, type, data, AL_FALSE); + channels, type, data, align, AL_FALSE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); @@ -504,8 +413,8 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, { ALCdevice *device; ALCcontext *context; - ALuint framesize; ALbuffer *albuf; + ALsizei align; context = GetContextRef(); if(!context) return; @@ -519,22 +428,32 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); WriteLock(&albuf->lock); - framesize = FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); + align = albuf->UnpackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } if(channels != (ALenum)albuf->FmtChannels) { WriteUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } - else if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) + if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if((samples%align) != 0) { WriteUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); } /* offset -> byte offset */ - offset *= framesize; + offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - data, type, ChannelsFromFmt(albuf->FmtChannels), samples); + data, type, ChannelsFromFmt(albuf->FmtChannels), samples, align); WriteUnlock(&albuf->lock); done: @@ -547,8 +466,8 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, { ALCdevice *device; ALCcontext *context; - ALuint framesize; ALbuffer *albuf; + ALsizei align; context = GetContextRef(); if(!context) return; @@ -562,7 +481,12 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); ReadLock(&albuf->lock); - framesize = FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); + align = albuf->PackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + { + ReadUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } if(channels != (ALenum)albuf->FmtChannels) { ReadUnlock(&albuf->lock); @@ -573,16 +497,16 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ReadUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); } - if(type == UserFmtIMA4 && (samples%65) != 0) + if((samples%align) != 0) { ReadUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); } /* offset -> byte offset */ - offset *= framesize; + offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); ConvertData(data, type, (char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - ChannelsFromFmt(albuf->FmtChannels), samples); + ChannelsFromFmt(albuf->FmtChannels), samples, align); ReadUnlock(&albuf->lock); done: @@ -678,20 +602,33 @@ done: } -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint UNUSED(value)) +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) { ALCdevice *device; ALCcontext *context; + ALbuffer *albuf; context = GetContextRef(); if(!context) return; device = context->Device; - if(LookupBuffer(device, buffer) == NULL) + if((albuf=LookupBuffer(device, buffer)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) { + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + ExchangeInt(&albuf->UnpackAlign, value); + break; + + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + ExchangeInt(&albuf->PackAlign, value); + break; + default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } @@ -730,6 +667,17 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val ALCcontext *context; ALbuffer *albuf; + if(values) + { + switch(param) + { + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + alBufferi(buffer, param, values[0]); + return; + } + } + context = GetContextRef(); if(!context) return; @@ -743,7 +691,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val { case AL_LOOP_POINTS_SOFT: WriteLock(&albuf->lock); - if(albuf->ref != 0) + if(ReadRef(&albuf->ref) != 0) { WriteUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); @@ -909,6 +857,14 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value *value = albuf->SampleLen; break; + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + *value = albuf->UnpackAlign; + break; + + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + *value = albuf->PackAlign; + break; + default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } @@ -958,6 +914,8 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values case AL_INTERNAL_FORMAT_SOFT: case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + case AL_PACK_BLOCK_ALIGNMENT_SOFT: alGetBufferi(buffer, param, values); return; } @@ -989,916 +947,6 @@ done: } -typedef ALubyte ALmulaw; -typedef ALubyte ALalaw; -typedef ALubyte ALima4; -typedef struct { - ALbyte b[3]; -} ALbyte3; -extern ALbyte ALbyte3_size_is_not_3[(sizeof(ALbyte3)==sizeof(ALbyte[3]))?1:-1]; -typedef struct { - ALubyte b[3]; -} ALubyte3; -extern ALbyte ALubyte3_size_is_not_3[(sizeof(ALubyte3)==sizeof(ALubyte[3]))?1:-1]; - -static inline ALshort DecodeMuLaw(ALmulaw val) -{ return muLawDecompressionTable[val]; } - -static ALmulaw EncodeMuLaw(ALshort val) -{ - ALint mant, exp, sign; - - sign = (val>>8) & 0x80; - if(sign) - { - /* -32768 doesn't properly negate on a short; it results in itself. - * So clamp to -32767 */ - val = maxi(val, -32767); - val = -val; - } - - val = mini(val, muLawClip); - val += muLawBias; - - exp = muLawCompressTable[(val>>7) & 0xff]; - mant = (val >> (exp+3)) & 0x0f; - - return ~(sign | (exp<<4) | mant); -} - -static inline ALshort DecodeALaw(ALalaw val) -{ return aLawDecompressionTable[val]; } - -static ALalaw EncodeALaw(ALshort val) -{ - ALint mant, exp, sign; - - sign = ((~val) >> 8) & 0x80; - if(!sign) - { - val = maxi(val, -32767); - val = -val; - } - val = mini(val, aLawClip); - - if(val >= 256) - { - exp = aLawCompressTable[(val>>8) & 0x7f]; - mant = (val >> (exp+3)) & 0x0f; - } - else - { - exp = 0; - mant = val >> 4; - } - - return ((exp<<4) | mant) ^ (sign^0x55); -} - -static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans) -{ - ALint sample[MAX_INPUT_CHANNELS], index[MAX_INPUT_CHANNELS]; - ALuint code[MAX_INPUT_CHANNELS]; - ALsizei j,k,c; - - for(c = 0;c < numchans;c++) - { - sample[c] = *(src++); - sample[c] |= *(src++) << 8; - sample[c] = (sample[c]^0x8000) - 32768; - index[c] = *(src++); - index[c] |= *(src++) << 8; - index[c] = (index[c]^0x8000) - 32768; - - index[c] = clampi(index[c], 0, 88); - - dst[c] = sample[c]; - } - - j = 1; - while(j < 65) - { - for(c = 0;c < numchans;c++) - { - code[c] = *(src++); - code[c] |= *(src++) << 8; - code[c] |= *(src++) << 16; - code[c] |= *(src++) << 24; - } - - for(k = 0;k < 8;k++,j++) - { - for(c = 0;c < numchans;c++) - { - int nibble = code[c]&0xf; - code[c] >>= 4; - - sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - dst[j*numchans + c] = sample[c]; - } - } - } -} - -static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans) -{ - ALsizei j,k,c; - - for(c = 0;c < numchans;c++) - { - int diff = src[c] - sample[c]; - int step = IMAStep_size[index[c]]; - int nibble; - - nibble = 0; - if(diff < 0) - { - nibble = 0x8; - diff = -diff; - } - - diff = mini(step*2, diff); - nibble |= (diff*8/step - 1) / 2; - - sample[c] += IMA4Codeword[nibble] * step / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - *(dst++) = sample[c] & 0xff; - *(dst++) = (sample[c]>>8) & 0xff; - *(dst++) = index[c] & 0xff; - *(dst++) = (index[c]>>8) & 0xff; - } - - j = 1; - while(j < 65) - { - for(c = 0;c < numchans;c++) - { - for(k = 0;k < 8;k++) - { - int diff = src[(j+k)*numchans + c] - sample[c]; - int step = IMAStep_size[index[c]]; - int nibble; - - nibble = 0; - if(diff < 0) - { - nibble = 0x8; - diff = -diff; - } - - diff = mini(step*2, diff); - nibble |= (diff*8/step - 1) / 2; - - sample[c] += IMA4Codeword[nibble] * step / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - if(!(k&1)) *dst = nibble; - else *(dst++) |= nibble<<4; - } - } - j += 8; - } -} - - -static inline ALint DecodeByte3(ALbyte3 val) -{ - if(IS_LITTLE_ENDIAN) - return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]); - return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]); -} - -static inline ALbyte3 EncodeByte3(ALint val) -{ - if(IS_LITTLE_ENDIAN) - { - ALbyte3 ret = {{ val, val>>8, val>>16 }}; - return ret; - } - else - { - ALbyte3 ret = {{ val>>16, val>>8, val }}; - return ret; - } -} - -static inline ALint DecodeUByte3(ALubyte3 val) -{ - if(IS_LITTLE_ENDIAN) - return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]); - return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2]; -} - -static inline ALubyte3 EncodeUByte3(ALint val) -{ - if(IS_LITTLE_ENDIAN) - { - ALubyte3 ret = {{ val, val>>8, val>>16 }}; - return ret; - } - else - { - ALubyte3 ret = {{ val>>16, val>>8, val }}; - return ret; - } -} - - -static inline ALbyte Conv_ALbyte_ALbyte(ALbyte val) -{ return val; } -static inline ALbyte Conv_ALbyte_ALubyte(ALubyte val) -{ return val-128; } -static inline ALbyte Conv_ALbyte_ALshort(ALshort val) -{ return val>>8; } -static inline ALbyte Conv_ALbyte_ALushort(ALushort val) -{ return (val>>8)-128; } -static inline ALbyte Conv_ALbyte_ALint(ALint val) -{ return val>>24; } -static inline ALbyte Conv_ALbyte_ALuint(ALuint val) -{ return (val>>24)-128; } -static inline ALbyte Conv_ALbyte_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 127; - if(val < -1.0f) return -128; - return (ALint)(val * 127.0f); -} -static inline ALbyte Conv_ALbyte_ALdouble(ALdouble val) -{ - if(val > 1.0) return 127; - if(val < -1.0) return -128; - return (ALint)(val * 127.0); -} -static inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val) -{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); } -static inline ALbyte Conv_ALbyte_ALalaw(ALalaw val) -{ return Conv_ALbyte_ALshort(DecodeALaw(val)); } -static inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)>>16; } -static inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)>>16)-128; } - -static inline ALubyte Conv_ALubyte_ALbyte(ALbyte val) -{ return val+128; } -static inline ALubyte Conv_ALubyte_ALubyte(ALubyte val) -{ return val; } -static inline ALubyte Conv_ALubyte_ALshort(ALshort val) -{ return (val>>8)+128; } -static inline ALubyte Conv_ALubyte_ALushort(ALushort val) -{ return val>>8; } -static inline ALubyte Conv_ALubyte_ALint(ALint val) -{ return (val>>24)+128; } -static inline ALubyte Conv_ALubyte_ALuint(ALuint val) -{ return val>>24; } -static inline ALubyte Conv_ALubyte_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 255; - if(val < -1.0f) return 0; - return (ALint)(val * 127.0f) + 128; -} -static inline ALubyte Conv_ALubyte_ALdouble(ALdouble val) -{ - if(val > 1.0) return 255; - if(val < -1.0) return 0; - return (ALint)(val * 127.0) + 128; -} -static inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val) -{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); } -static inline ALubyte Conv_ALubyte_ALalaw(ALalaw val) -{ return Conv_ALubyte_ALshort(DecodeALaw(val)); } -static inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)>>16)+128; } -static inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)>>16; } - -static inline ALshort Conv_ALshort_ALbyte(ALbyte val) -{ return val<<8; } -static inline ALshort Conv_ALshort_ALubyte(ALubyte val) -{ return (val-128)<<8; } -static inline ALshort Conv_ALshort_ALshort(ALshort val) -{ return val; } -static inline ALshort Conv_ALshort_ALushort(ALushort val) -{ return val-32768; } -static inline ALshort Conv_ALshort_ALint(ALint val) -{ return val>>16; } -static inline ALshort Conv_ALshort_ALuint(ALuint val) -{ return (val>>16)-32768; } -static inline ALshort Conv_ALshort_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 32767; - if(val < -1.0f) return -32768; - return (ALint)(val * 32767.0f); -} -static inline ALshort Conv_ALshort_ALdouble(ALdouble val) -{ - if(val > 1.0) return 32767; - if(val < -1.0) return -32768; - return (ALint)(val * 32767.0); -} -static inline ALshort Conv_ALshort_ALmulaw(ALmulaw val) -{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); } -static inline ALshort Conv_ALshort_ALalaw(ALalaw val) -{ return Conv_ALshort_ALshort(DecodeALaw(val)); } -static inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)>>8; } -static inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)>>8)-32768; } - -static inline ALushort Conv_ALushort_ALbyte(ALbyte val) -{ return (val+128)<<8; } -static inline ALushort Conv_ALushort_ALubyte(ALubyte val) -{ return val<<8; } -static inline ALushort Conv_ALushort_ALshort(ALshort val) -{ return val+32768; } -static inline ALushort Conv_ALushort_ALushort(ALushort val) -{ return val; } -static inline ALushort Conv_ALushort_ALint(ALint val) -{ return (val>>16)+32768; } -static inline ALushort Conv_ALushort_ALuint(ALuint val) -{ return val>>16; } -static inline ALushort Conv_ALushort_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 65535; - if(val < -1.0f) return 0; - return (ALint)(val * 32767.0f) + 32768; -} -static inline ALushort Conv_ALushort_ALdouble(ALdouble val) -{ - if(val > 1.0) return 65535; - if(val < -1.0) return 0; - return (ALint)(val * 32767.0) + 32768; -} -static inline ALushort Conv_ALushort_ALmulaw(ALmulaw val) -{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); } -static inline ALushort Conv_ALushort_ALalaw(ALalaw val) -{ return Conv_ALushort_ALshort(DecodeALaw(val)); } -static inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)>>8)+32768; } -static inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)>>8; } - -static inline ALint Conv_ALint_ALbyte(ALbyte val) -{ return val<<24; } -static inline ALint Conv_ALint_ALubyte(ALubyte val) -{ return (val-128)<<24; } -static inline ALint Conv_ALint_ALshort(ALshort val) -{ return val<<16; } -static inline ALint Conv_ALint_ALushort(ALushort val) -{ return (val-32768)<<16; } -static inline ALint Conv_ALint_ALint(ALint val) -{ return val; } -static inline ALint Conv_ALint_ALuint(ALuint val) -{ return val-2147483648u; } -static inline ALint Conv_ALint_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 2147483647; - if(val < -1.0f) return -2147483647-1; - return (ALint)(val*16777215.0f) << 7; -} -static inline ALint Conv_ALint_ALdouble(ALdouble val) -{ - if(val > 1.0) return 2147483647; - if(val < -1.0) return -2147483647-1; - return (ALint)(val * 2147483647.0); -} -static inline ALint Conv_ALint_ALmulaw(ALmulaw val) -{ return Conv_ALint_ALshort(DecodeMuLaw(val)); } -static inline ALint Conv_ALint_ALalaw(ALalaw val) -{ return Conv_ALint_ALshort(DecodeALaw(val)); } -static inline ALint Conv_ALint_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)<<8; } -static inline ALint Conv_ALint_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)-8388608)<<8; } - -static inline ALuint Conv_ALuint_ALbyte(ALbyte val) -{ return (val+128)<<24; } -static inline ALuint Conv_ALuint_ALubyte(ALubyte val) -{ return val<<24; } -static inline ALuint Conv_ALuint_ALshort(ALshort val) -{ return (val+32768)<<16; } -static inline ALuint Conv_ALuint_ALushort(ALushort val) -{ return val<<16; } -static inline ALuint Conv_ALuint_ALint(ALint val) -{ return val+2147483648u; } -static inline ALuint Conv_ALuint_ALuint(ALuint val) -{ return val; } -static inline ALuint Conv_ALuint_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 4294967295u; - if(val < -1.0f) return 0; - return ((ALint)(val*16777215.0f)<<7) + 2147483648u; -} -static inline ALuint Conv_ALuint_ALdouble(ALdouble val) -{ - if(val > 1.0) return 4294967295u; - if(val < -1.0) return 0; - return (ALint)(val * 2147483647.0) + 2147483648u; -} -static inline ALuint Conv_ALuint_ALmulaw(ALmulaw val) -{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); } -static inline ALuint Conv_ALuint_ALalaw(ALalaw val) -{ return Conv_ALuint_ALshort(DecodeALaw(val)); } -static inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)+8388608)<<8; } -static inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)<<8; } - -static inline ALfloat Conv_ALfloat_ALbyte(ALbyte val) -{ return val * (1.0f/127.0f); } -static inline ALfloat Conv_ALfloat_ALubyte(ALubyte val) -{ return (val-128) * (1.0f/127.0f); } -static inline ALfloat Conv_ALfloat_ALshort(ALshort val) -{ return val * (1.0f/32767.0f); } -static inline ALfloat Conv_ALfloat_ALushort(ALushort val) -{ return (val-32768) * (1.0f/32767.0f); } -static inline ALfloat Conv_ALfloat_ALint(ALint val) -{ return (ALfloat)(val * (1.0/2147483647.0)); } -static inline ALfloat Conv_ALfloat_ALuint(ALuint val) -{ return (ALfloat)((ALint)(val-2147483648u) * (1.0/2147483647.0)); } -static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val) -{ return (val==val) ? val : 0.0f; } -static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val) -{ return (val==val) ? (ALfloat)val : 0.0f; } -static inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val) -{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); } -static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val) -{ return Conv_ALfloat_ALshort(DecodeALaw(val)); } -static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val) -{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); } -static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val) -{ return (ALfloat)((DecodeUByte3(val)-8388608) * (1.0/8388607.0)); } - -static inline ALdouble Conv_ALdouble_ALbyte(ALbyte val) -{ return val * (1.0/127.0); } -static inline ALdouble Conv_ALdouble_ALubyte(ALubyte val) -{ return (val-128) * (1.0/127.0); } -static inline ALdouble Conv_ALdouble_ALshort(ALshort val) -{ return val * (1.0/32767.0); } -static inline ALdouble Conv_ALdouble_ALushort(ALushort val) -{ return (val-32768) * (1.0/32767.0); } -static inline ALdouble Conv_ALdouble_ALint(ALint val) -{ return val * (1.0/2147483647.0); } -static inline ALdouble Conv_ALdouble_ALuint(ALuint val) -{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); } -static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val) -{ return (val==val) ? val : 0.0f; } -static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val) -{ return (val==val) ? val : 0.0; } -static inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val) -{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); } -static inline ALdouble Conv_ALdouble_ALalaw(ALalaw val) -{ return Conv_ALdouble_ALshort(DecodeALaw(val)); } -static inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val) * (1.0/8388607.0); } -static inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); } - -#define DECL_TEMPLATE(T) \ -static inline ALmulaw Conv_ALmulaw_##T(T val) \ -{ return EncodeMuLaw(Conv_ALshort_##T(val)); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val) -{ return val; } -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static inline ALalaw Conv_ALalaw_##T(T val) \ -{ return EncodeALaw(Conv_ALshort_##T(val)); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -static inline ALalaw Conv_ALalaw_ALalaw(ALalaw val) -{ return val; } -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static inline ALbyte3 Conv_ALbyte3_##T(T val) \ -{ return EncodeByte3(Conv_ALint_##T(val)>>8); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val) -{ return val; } -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static inline ALubyte3 Conv_ALubyte3_##T(T val) \ -{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -static inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val) -{ return val; } - -#undef DECL_TEMPLATE - - -#define DECL_TEMPLATE(T1, T2) \ -static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans, \ - ALuint len) \ -{ \ - ALuint i, j; \ - for(i = 0;i < len;i++) \ - { \ - for(j = 0;j < numchans;j++) \ - *(dst++) = Conv_##T1##_##T2(*(src++)); \ - } \ -} - -DECL_TEMPLATE(ALbyte, ALbyte) -DECL_TEMPLATE(ALbyte, ALubyte) -DECL_TEMPLATE(ALbyte, ALshort) -DECL_TEMPLATE(ALbyte, ALushort) -DECL_TEMPLATE(ALbyte, ALint) -DECL_TEMPLATE(ALbyte, ALuint) -DECL_TEMPLATE(ALbyte, ALfloat) -DECL_TEMPLATE(ALbyte, ALdouble) -DECL_TEMPLATE(ALbyte, ALmulaw) -DECL_TEMPLATE(ALbyte, ALalaw) -DECL_TEMPLATE(ALbyte, ALbyte3) -DECL_TEMPLATE(ALbyte, ALubyte3) - -DECL_TEMPLATE(ALubyte, ALbyte) -DECL_TEMPLATE(ALubyte, ALubyte) -DECL_TEMPLATE(ALubyte, ALshort) -DECL_TEMPLATE(ALubyte, ALushort) -DECL_TEMPLATE(ALubyte, ALint) -DECL_TEMPLATE(ALubyte, ALuint) -DECL_TEMPLATE(ALubyte, ALfloat) -DECL_TEMPLATE(ALubyte, ALdouble) -DECL_TEMPLATE(ALubyte, ALmulaw) -DECL_TEMPLATE(ALubyte, ALalaw) -DECL_TEMPLATE(ALubyte, ALbyte3) -DECL_TEMPLATE(ALubyte, ALubyte3) - -DECL_TEMPLATE(ALshort, ALbyte) -DECL_TEMPLATE(ALshort, ALubyte) -DECL_TEMPLATE(ALshort, ALshort) -DECL_TEMPLATE(ALshort, ALushort) -DECL_TEMPLATE(ALshort, ALint) -DECL_TEMPLATE(ALshort, ALuint) -DECL_TEMPLATE(ALshort, ALfloat) -DECL_TEMPLATE(ALshort, ALdouble) -DECL_TEMPLATE(ALshort, ALmulaw) -DECL_TEMPLATE(ALshort, ALalaw) -DECL_TEMPLATE(ALshort, ALbyte3) -DECL_TEMPLATE(ALshort, ALubyte3) - -DECL_TEMPLATE(ALushort, ALbyte) -DECL_TEMPLATE(ALushort, ALubyte) -DECL_TEMPLATE(ALushort, ALshort) -DECL_TEMPLATE(ALushort, ALushort) -DECL_TEMPLATE(ALushort, ALint) -DECL_TEMPLATE(ALushort, ALuint) -DECL_TEMPLATE(ALushort, ALfloat) -DECL_TEMPLATE(ALushort, ALdouble) -DECL_TEMPLATE(ALushort, ALmulaw) -DECL_TEMPLATE(ALushort, ALalaw) -DECL_TEMPLATE(ALushort, ALbyte3) -DECL_TEMPLATE(ALushort, ALubyte3) - -DECL_TEMPLATE(ALint, ALbyte) -DECL_TEMPLATE(ALint, ALubyte) -DECL_TEMPLATE(ALint, ALshort) -DECL_TEMPLATE(ALint, ALushort) -DECL_TEMPLATE(ALint, ALint) -DECL_TEMPLATE(ALint, ALuint) -DECL_TEMPLATE(ALint, ALfloat) -DECL_TEMPLATE(ALint, ALdouble) -DECL_TEMPLATE(ALint, ALmulaw) -DECL_TEMPLATE(ALint, ALalaw) -DECL_TEMPLATE(ALint, ALbyte3) -DECL_TEMPLATE(ALint, ALubyte3) - -DECL_TEMPLATE(ALuint, ALbyte) -DECL_TEMPLATE(ALuint, ALubyte) -DECL_TEMPLATE(ALuint, ALshort) -DECL_TEMPLATE(ALuint, ALushort) -DECL_TEMPLATE(ALuint, ALint) -DECL_TEMPLATE(ALuint, ALuint) -DECL_TEMPLATE(ALuint, ALfloat) -DECL_TEMPLATE(ALuint, ALdouble) -DECL_TEMPLATE(ALuint, ALmulaw) -DECL_TEMPLATE(ALuint, ALalaw) -DECL_TEMPLATE(ALuint, ALbyte3) -DECL_TEMPLATE(ALuint, ALubyte3) - -DECL_TEMPLATE(ALfloat, ALbyte) -DECL_TEMPLATE(ALfloat, ALubyte) -DECL_TEMPLATE(ALfloat, ALshort) -DECL_TEMPLATE(ALfloat, ALushort) -DECL_TEMPLATE(ALfloat, ALint) -DECL_TEMPLATE(ALfloat, ALuint) -DECL_TEMPLATE(ALfloat, ALfloat) -DECL_TEMPLATE(ALfloat, ALdouble) -DECL_TEMPLATE(ALfloat, ALmulaw) -DECL_TEMPLATE(ALfloat, ALalaw) -DECL_TEMPLATE(ALfloat, ALbyte3) -DECL_TEMPLATE(ALfloat, ALubyte3) - -DECL_TEMPLATE(ALdouble, ALbyte) -DECL_TEMPLATE(ALdouble, ALubyte) -DECL_TEMPLATE(ALdouble, ALshort) -DECL_TEMPLATE(ALdouble, ALushort) -DECL_TEMPLATE(ALdouble, ALint) -DECL_TEMPLATE(ALdouble, ALuint) -DECL_TEMPLATE(ALdouble, ALfloat) -DECL_TEMPLATE(ALdouble, ALdouble) -DECL_TEMPLATE(ALdouble, ALmulaw) -DECL_TEMPLATE(ALdouble, ALalaw) -DECL_TEMPLATE(ALdouble, ALbyte3) -DECL_TEMPLATE(ALdouble, ALubyte3) - -DECL_TEMPLATE(ALmulaw, ALbyte) -DECL_TEMPLATE(ALmulaw, ALubyte) -DECL_TEMPLATE(ALmulaw, ALshort) -DECL_TEMPLATE(ALmulaw, ALushort) -DECL_TEMPLATE(ALmulaw, ALint) -DECL_TEMPLATE(ALmulaw, ALuint) -DECL_TEMPLATE(ALmulaw, ALfloat) -DECL_TEMPLATE(ALmulaw, ALdouble) -DECL_TEMPLATE(ALmulaw, ALmulaw) -DECL_TEMPLATE(ALmulaw, ALalaw) -DECL_TEMPLATE(ALmulaw, ALbyte3) -DECL_TEMPLATE(ALmulaw, ALubyte3) - -DECL_TEMPLATE(ALalaw, ALbyte) -DECL_TEMPLATE(ALalaw, ALubyte) -DECL_TEMPLATE(ALalaw, ALshort) -DECL_TEMPLATE(ALalaw, ALushort) -DECL_TEMPLATE(ALalaw, ALint) -DECL_TEMPLATE(ALalaw, ALuint) -DECL_TEMPLATE(ALalaw, ALfloat) -DECL_TEMPLATE(ALalaw, ALdouble) -DECL_TEMPLATE(ALalaw, ALmulaw) -DECL_TEMPLATE(ALalaw, ALalaw) -DECL_TEMPLATE(ALalaw, ALbyte3) -DECL_TEMPLATE(ALalaw, ALubyte3) - -DECL_TEMPLATE(ALbyte3, ALbyte) -DECL_TEMPLATE(ALbyte3, ALubyte) -DECL_TEMPLATE(ALbyte3, ALshort) -DECL_TEMPLATE(ALbyte3, ALushort) -DECL_TEMPLATE(ALbyte3, ALint) -DECL_TEMPLATE(ALbyte3, ALuint) -DECL_TEMPLATE(ALbyte3, ALfloat) -DECL_TEMPLATE(ALbyte3, ALdouble) -DECL_TEMPLATE(ALbyte3, ALmulaw) -DECL_TEMPLATE(ALbyte3, ALalaw) -DECL_TEMPLATE(ALbyte3, ALbyte3) -DECL_TEMPLATE(ALbyte3, ALubyte3) - -DECL_TEMPLATE(ALubyte3, ALbyte) -DECL_TEMPLATE(ALubyte3, ALubyte) -DECL_TEMPLATE(ALubyte3, ALshort) -DECL_TEMPLATE(ALubyte3, ALushort) -DECL_TEMPLATE(ALubyte3, ALint) -DECL_TEMPLATE(ALubyte3, ALuint) -DECL_TEMPLATE(ALubyte3, ALfloat) -DECL_TEMPLATE(ALubyte3, ALdouble) -DECL_TEMPLATE(ALubyte3, ALmulaw) -DECL_TEMPLATE(ALubyte3, ALalaw) -DECL_TEMPLATE(ALubyte3, ALbyte3) -DECL_TEMPLATE(ALubyte3, ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \ - ALuint len) \ -{ \ - ALshort tmp[65*MAX_INPUT_CHANNELS]; /* Max samples an IMA4 frame can be */\ - ALuint i, j, k; \ - \ - i = 0; \ - while(i < len) \ - { \ - DecodeIMA4Block(tmp, src, numchans); \ - src += 36*numchans; \ - \ - for(j = 0;j < 65 && i < len;j++,i++) \ - { \ - for(k = 0;k < numchans;k++) \ - *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ - } \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \ - ALuint len) \ -{ \ - ALshort tmp[65*MAX_INPUT_CHANNELS]; /* Max samples an IMA4 frame can be */\ - ALint sample[MaxChannels] = {0,0,0,0,0,0,0,0}; \ - ALint index[MaxChannels] = {0,0,0,0,0,0,0,0}; \ - ALuint i, j; \ - \ - for(i = 0;i < len;i += 65) \ - { \ - for(j = 0;j < 65*numchans;j++) \ - tmp[j] = Conv_ALshort_##T(*(src++)); \ - EncodeIMA4Block(dst, tmp, sample, index, numchans); \ - dst += 36*numchans; \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -static void Convert_ALima4_ALima4(ALima4 *dst, const ALima4 *src, - ALuint numchans, ALuint numblocks) -{ memcpy(dst, src, numblocks*36*numchans); } -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ - ALsizei numchans, ALsizei len) \ -{ \ - switch(srcType) \ - { \ - case UserFmtByte: \ - Convert_##T##_ALbyte(dst, src, numchans, len); \ - break; \ - case UserFmtUByte: \ - Convert_##T##_ALubyte(dst, src, numchans, len); \ - break; \ - case UserFmtShort: \ - Convert_##T##_ALshort(dst, src, numchans, len); \ - break; \ - case UserFmtUShort: \ - Convert_##T##_ALushort(dst, src, numchans, len); \ - break; \ - case UserFmtInt: \ - Convert_##T##_ALint(dst, src, numchans, len); \ - break; \ - case UserFmtUInt: \ - Convert_##T##_ALuint(dst, src, numchans, len); \ - break; \ - case UserFmtFloat: \ - Convert_##T##_ALfloat(dst, src, numchans, len); \ - break; \ - case UserFmtDouble: \ - Convert_##T##_ALdouble(dst, src, numchans, len); \ - break; \ - case UserFmtMulaw: \ - Convert_##T##_ALmulaw(dst, src, numchans, len); \ - break; \ - case UserFmtAlaw: \ - Convert_##T##_ALalaw(dst, src, numchans, len); \ - break; \ - case UserFmtIMA4: \ - Convert_##T##_ALima4(dst, src, numchans, len); \ - break; \ - case UserFmtByte3: \ - Convert_##T##_ALbyte3(dst, src, numchans, len); \ - break; \ - case UserFmtUByte3: \ - Convert_##T##_ALubyte3(dst, src, numchans, len); \ - break; \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALima4) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - - -static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len) -{ - switch(dstType) - { - case UserFmtByte: - Convert_ALbyte(dst, src, srcType, numchans, len); - break; - case UserFmtUByte: - Convert_ALubyte(dst, src, srcType, numchans, len); - break; - case UserFmtShort: - Convert_ALshort(dst, src, srcType, numchans, len); - break; - case UserFmtUShort: - Convert_ALushort(dst, src, srcType, numchans, len); - break; - case UserFmtInt: - Convert_ALint(dst, src, srcType, numchans, len); - break; - case UserFmtUInt: - Convert_ALuint(dst, src, srcType, numchans, len); - break; - case UserFmtFloat: - Convert_ALfloat(dst, src, srcType, numchans, len); - break; - case UserFmtDouble: - Convert_ALdouble(dst, src, srcType, numchans, len); - break; - case UserFmtMulaw: - Convert_ALmulaw(dst, src, srcType, numchans, len); - break; - case UserFmtAlaw: - Convert_ALalaw(dst, src, srcType, numchans, len); - break; - case UserFmtIMA4: - Convert_ALima4(dst, src, srcType, numchans, len); - break; - case UserFmtByte3: - Convert_ALbyte3(dst, src, srcType, numchans, len); - break; - case UserFmtUByte3: - Convert_ALubyte3(dst, src, srcType, numchans, len); - break; - } -} - - /* * LoadData * @@ -1906,7 +954,7 @@ static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src * Currently, the new format must have the same channel configuration as the * original format. */ -static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALboolean storesrc) +static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc) { ALuint NewChannels, NewBytes; enum FmtChannels DstChannels; @@ -1928,7 +976,7 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f return AL_OUT_OF_MEMORY; WriteLock(&ALBuf->lock); - if(ALBuf->ref != 0) + if(ReadRef(&ALBuf->ref) != 0) { WriteUnlock(&ALBuf->lock); return AL_INVALID_OPERATION; @@ -1943,22 +991,36 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f ALBuf->data = temp; if(data != NULL) - ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames); + ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align); if(storesrc) { ALBuf->OriginalChannels = SrcChannels; ALBuf->OriginalType = SrcType; if(SrcType == UserFmtIMA4) - ALBuf->OriginalSize = frames / 65 * 36 * ChannelsFromUserFmt(SrcChannels); + { + ALsizei byte_align = ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels); + ALBuf->OriginalSize = frames / align * byte_align; + ALBuf->OriginalAlign = align; + } + else if(SrcType == UserFmtMSADPCM) + { + ALsizei byte_align = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels); + ALBuf->OriginalSize = frames / align * byte_align; + ALBuf->OriginalAlign = align; + } else - ALBuf->OriginalSize = frames * FrameSizeFromUserFmt(SrcChannels, SrcType); + { + ALBuf->OriginalSize = frames * FrameSizeFromUserFmt(SrcChannels, SrcType); + ALBuf->OriginalAlign = 1; + } } else { ALBuf->OriginalChannels = (enum UserFmtChannels)DstChannels; ALBuf->OriginalType = (enum UserFmtType)DstType; ALBuf->OriginalSize = frames * NewBytes * NewChannels; + ALBuf->OriginalAlign = 1; } ALBuf->Frequency = freq; @@ -1987,11 +1049,12 @@ ALuint BytesFromUserFmt(enum UserFmtType type) case UserFmtUInt: return sizeof(ALuint); case UserFmtFloat: return sizeof(ALfloat); case UserFmtDouble: return sizeof(ALdouble); - case UserFmtByte3: return sizeof(ALbyte3); - case UserFmtUByte3: return sizeof(ALubyte3); + case UserFmtByte3: return sizeof(ALbyte[3]); + case UserFmtUByte3: return sizeof(ALubyte[3]); case UserFmtMulaw: return sizeof(ALubyte); case UserFmtAlaw: return sizeof(ALubyte); case UserFmtIMA4: break; /* not handled here */ + case UserFmtMSADPCM: break; /* not handled here */ } return 0; } @@ -2017,21 +1080,23 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtChannels channels; enum UserFmtType type; } list[] = { - { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte }, - { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort }, - { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat }, - { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble }, - { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 }, - { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw }, - { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw }, - - { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte }, - { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort }, - { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat }, - { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble }, - { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 }, - { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw }, - { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw }, + { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte }, + { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort }, + { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat }, + { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble }, + { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 }, + { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM }, + { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw }, + { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw }, + + { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte }, + { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort }, + { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat }, + { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble }, + { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 }, + { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM }, + { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw }, + { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw }, { AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte }, { AL_FORMAT_REAR16, UserFmtRear, UserFmtShort }, @@ -2153,6 +1218,44 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm return AL_FALSE; } +static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align) +{ + if(*align < 0) + return AL_FALSE; + + if(*align == 0) + { + if(type == UserFmtIMA4) + { + /* Here is where things vary: + * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel + * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel + */ + *align = 65; + } + else if(type == UserFmtMSADPCM) + *align = 64; + else + *align = 1; + return AL_TRUE; + } + + if(type == UserFmtIMA4) + { + /* IMA4 block alignment must be a multiple of 8, plus 1. */ + return ((*align)&7) == 1; + } + if(type == UserFmtMSADPCM) + { + /* MSADPCM block alignment must be a multiple of 2. */ + /* FIXME: Too strict? Might only require align*channels to be a + * multiple of 2. */ + return ((*align)&1) == 0; + } + + return AL_TRUE; +} + static ALboolean IsValidType(ALenum type) { diff --git a/OpenAL32/alFilter.c b/OpenAL32/alFilter.c index 692109f5..acfae8a6 100644 --- a/OpenAL32/alFilter.c +++ b/OpenAL32/alFilter.c @@ -32,7 +32,6 @@ extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id); extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id); extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample); -extern inline ALfloat ALfilterState_processSingleC(const ALfilterState *filter, ALfloat sample); static void InitFilterParams(ALfilter *filter, ALenum type); @@ -146,7 +145,8 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) { if(param == AL_FILTER_TYPE) { - if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS) + if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS || + value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) InitFilterParams(ALFilter, value); else alSetError(Context, AL_INVALID_VALUE); @@ -336,7 +336,7 @@ void ALfilterState_clear(ALfilterState *filter) filter->y[1] = 0.0f; } -void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_scale, ALfloat bandwidth) +void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth) { ALfloat alpha; ALfloat w0; @@ -344,50 +344,28 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g // Limit gain to -100dB gain = maxf(gain, 0.00001f); - w0 = F_2PI * freq_scale; + w0 = F_2PI * freq_mult; /* Calculate filter coefficients depending on filter type */ switch(type) { case ALfilterType_HighShelf: - alpha = sinf(w0) / 2.0f * sqrtf((gain + 1.0f/gain) * - (1.0f/0.75f - 1.0f) + 2.0f); - filter->b[0] = gain * ((gain + 1.0f) + - (gain - 1.0f) * cosf(w0) + - 2.0f * sqrtf(gain) * alpha); - filter->b[1] = -2.0f * gain * ((gain - 1.0f) + - (gain + 1.0f) * cosf(w0)); - filter->b[2] = gain * ((gain + 1.0f) + - (gain - 1.0f) * cosf(w0) - - 2.0f * sqrtf(gain) * alpha); - filter->a[0] = (gain + 1.0f) - - (gain - 1.0f) * cosf(w0) + - 2.0f * sqrtf(gain) * alpha; - filter->a[1] = 2.0f * ((gain - 1.0f) - - (gain + 1.0f) * cosf(w0)); - filter->a[2] = (gain + 1.0f) - - (gain - 1.0f) * cosf(w0) - - 2.0f * sqrtf(gain) * alpha; + alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); + filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); + filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) ); + filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); + filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; + filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) ); + filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; break; case ALfilterType_LowShelf: - alpha = sinf(w0) / 2.0f * sqrtf((gain + 1.0f / gain) * - (1.0f / 0.75f - 1.0f) + 2.0f); - filter->b[0] = gain * ((gain + 1.0f) - - (gain - 1.0f) * cosf(w0) + - 2.0f * sqrtf(gain) * alpha); - filter->b[1] = 2.0f * gain * ((gain - 1.0f) - - (gain + 1.0f) * cosf(w0)); - filter->b[2] = gain * ((gain + 1.0f) - - (gain - 1.0f) * cosf(w0) - - 2.0f * sqrtf(gain) * alpha); - filter->a[0] = (gain + 1.0f) + - (gain - 1.0f) * cosf(w0) + - 2.0f * sqrtf(gain) * alpha; - filter->a[1] = -2.0f * ((gain - 1.0f) + - (gain + 1.0f) * cosf(w0)); - filter->a[2] = (gain + 1.0f) + - (gain - 1.0f) * cosf(w0) - - 2.0f * sqrtf(gain) * alpha; + alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); + filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); + filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) ); + filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); + filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; + filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) ); + filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; break; case ALfilterType_Peaking: alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); @@ -434,6 +412,8 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g filter->a[2] /= filter->a[0]; filter->a[1] /= filter->a[0]; filter->a[0] /= filter->a[0]; + + filter->process = ALfilterState_processC; } @@ -492,6 +472,126 @@ static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, A } +static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_HIGHPASS_GAIN: + if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->Gain = val; + break; + + case AL_HIGHPASS_GAINLF: + if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainLF = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + hp_SetParamf(filter, context, param, vals[0]); +} + +static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_HIGHPASS_GAIN: + *val = filter->Gain; + break; + + case AL_HIGHPASS_GAINLF: + *val = filter->GainLF; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ + hp_GetParamf(filter, context, param, vals); +} + + +static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_BANDPASS_GAIN: + if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->Gain = val; + break; + + case AL_BANDPASS_GAINHF: + if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainHF = val; + break; + + case AL_BANDPASS_GAINLF: + if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainLF = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + bp_SetParamf(filter, context, param, vals[0]); +} + +static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_BANDPASS_GAIN: + *val = filter->Gain; + break; + + case AL_BANDPASS_GAINHF: + *val = filter->GainHF; + break; + + case AL_BANDPASS_GAINLF: + *val = filter->GainLF; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ + bp_GetParamf(filter, context, param, vals); +} + + static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) @@ -533,6 +633,9 @@ static void InitFilterParams(ALfilter *filter, ALenum type) { filter->Gain = AL_LOWPASS_DEFAULT_GAIN; filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; filter->SetParami = lp_SetParami; filter->SetParamiv = lp_SetParamiv; @@ -543,8 +646,48 @@ static void InitFilterParams(ALfilter *filter, ALenum type) filter->GetParamf = lp_GetParamf; filter->GetParamfv = lp_GetParamfv; } + else if(type == AL_FILTER_HIGHPASS) + { + filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = hp_SetParami; + filter->SetParamiv = hp_SetParamiv; + filter->SetParamf = hp_SetParamf; + filter->SetParamfv = hp_SetParamfv; + filter->GetParami = hp_GetParami; + filter->GetParamiv = hp_GetParamiv; + filter->GetParamf = hp_GetParamf; + filter->GetParamfv = hp_GetParamfv; + } + else if(type == AL_FILTER_BANDPASS) + { + filter->Gain = AL_BANDPASS_DEFAULT_GAIN; + filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = bp_SetParami; + filter->SetParamiv = bp_SetParamiv; + filter->SetParamf = bp_SetParamf; + filter->SetParamfv = bp_SetParamfv; + filter->GetParami = bp_GetParami; + filter->GetParamiv = bp_GetParamiv; + filter->GetParamf = bp_GetParamf; + filter->GetParamfv = bp_GetParamfv; + } else { + filter->Gain = 1.0f; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; + filter->SetParami = null_SetParami; filter->SetParamiv = null_SetParamiv; filter->SetParamf = null_SetParamf; diff --git a/OpenAL32/alFontsound.c b/OpenAL32/alFontsound.c index 94cf3064..c4d49e92 100644 --- a/OpenAL32/alFontsound.c +++ b/OpenAL32/alFontsound.c @@ -69,7 +69,7 @@ AL_API ALvoid AL_APIENTRY alDeleteFontsoundsSOFT(ALsizei n, const ALuint *ids) /* Check for valid ID */ if((inst=LookupFontsound(device, ids[i])) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(inst->ref != 0) + if(ReadRef(&inst->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -115,7 +115,7 @@ AL_API void AL_APIENTRY alFontsoundiSOFT(ALuint id, ALenum param, ALint value) device = context->Device; if(!(sound=LookupFontsound(device, id))) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(sound->ref != 0) + if(ReadRef(&sound->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); ALfontsound_setPropi(sound, context, param, value); @@ -136,7 +136,7 @@ AL_API void AL_APIENTRY alFontsound2iSOFT(ALuint id, ALenum param, ALint value1, device = context->Device; if(!(sound=LookupFontsound(device, id))) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(sound->ref != 0) + if(ReadRef(&sound->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); switch(param) { @@ -231,7 +231,7 @@ AL_API void AL_APIENTRY alFontsoundivSOFT(ALuint id, ALenum param, const ALint * device = context->Device; if(!(sound=LookupFontsound(device, id))) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(sound->ref != 0) + if(ReadRef(&sound->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); switch(param) { @@ -508,7 +508,7 @@ ALfontsound *NewFontsound(ALCcontext *context) static void ALfontsound_Construct(ALfontsound *self) { - self->ref = 0; + InitRef(&self->ref, 0); self->MinKey = 0; self->MaxKey = 127; @@ -829,7 +829,7 @@ void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei st { ALint srcidx = 0; - if(self->ref != 0) + if(ReadRef(&self->ref) != 0) SET_ERROR_AND_RETURN(context, AL_INVALID_OPERATION); switch(param) { diff --git a/OpenAL32/alMidi.c b/OpenAL32/alMidi.c index 0679b64c..6313523e 100644 --- a/OpenAL32/alMidi.c +++ b/OpenAL32/alMidi.c @@ -19,7 +19,9 @@ MidiSynth *SynthCreate(ALCdevice *device) { - MidiSynth *synth = FSynth_create(device); + MidiSynth *synth = NULL; + if(!synth) synth = SSynth_create(device); + if(!synth) synth = FSynth_create(device); if(!synth) synth = DSynth_create(device); return synth; } @@ -133,7 +135,7 @@ AL_API void AL_APIENTRY alMidiPlaySOFT(void) synth = context->Device->Synth; WriteLock(&synth->Lock); - V(synth,setState)(AL_PLAYING); + MidiSynth_setState(synth, AL_PLAYING); WriteUnlock(&synth->Lock); ALCcontext_DecRef(context); @@ -149,7 +151,7 @@ AL_API void AL_APIENTRY alMidiPauseSOFT(void) synth = context->Device->Synth; WriteLock(&synth->Lock); - V(synth,setState)(AL_PAUSED); + MidiSynth_setState(synth, AL_PAUSED); WriteUnlock(&synth->Lock); ALCcontext_DecRef(context); @@ -168,7 +170,7 @@ AL_API void AL_APIENTRY alMidiStopSOFT(void) synth = device->Synth; WriteLock(&synth->Lock); - V(synth,setState)(AL_STOPPED); + MidiSynth_setState(synth, AL_STOPPED); ALCdevice_Lock(device); V0(synth,stop)(); @@ -191,7 +193,7 @@ AL_API void AL_APIENTRY alMidiResetSOFT(void) synth = device->Synth; WriteLock(&synth->Lock); - V(synth,setState)(AL_INITIAL); + MidiSynth_setState(synth, AL_INITIAL); ALCdevice_Lock(device); V0(synth,reset)(); diff --git a/OpenAL32/alPreset.c b/OpenAL32/alPreset.c index d34772a4..c7167d6b 100644 --- a/OpenAL32/alPreset.c +++ b/OpenAL32/alPreset.c @@ -65,7 +65,7 @@ AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids) /* Check for valid ID */ if((preset=LookupPreset(device, ids[i])) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(preset->ref != 0) + if(ReadRef(&preset->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -107,7 +107,7 @@ AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value) device = context->Device; if((preset=LookupPreset(device, id)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(preset->ref != 0) + if(ReadRef(&preset->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); switch(param) { @@ -151,7 +151,7 @@ AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *val device = context->Device; if((preset=LookupPreset(device, id)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(preset->ref != 0) + if(ReadRef(&preset->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); switch(param) { @@ -220,7 +220,7 @@ AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const A if(count < 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(preset->ref != 0) + if(ReadRef(&preset->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); if(count == 0) @@ -294,7 +294,7 @@ void DeletePreset(ALsfpreset *preset, ALCdevice *device) static void ALsfpreset_Construct(ALsfpreset *self) { - self->ref = 0; + InitRef(&self->ref, 0); self->Preset = 0; self->Bank = 0; diff --git a/OpenAL32/alSoundfont.c b/OpenAL32/alSoundfont.c index 355c5b41..357e13e7 100644 --- a/OpenAL32/alSoundfont.c +++ b/OpenAL32/alSoundfont.c @@ -91,7 +91,7 @@ AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids) } else if((sfont=LookupSfont(device, ids[i])) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(sfont->Mapped != AL_FALSE || sfont->ref != 0) + if(sfont->Mapped != AL_FALSE || ReadRef(&sfont->ref) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -157,7 +157,7 @@ AL_API ALvoid AL_APIENTRY alSoundfontSamplesSOFT(ALuint id, ALenum type, ALsizei SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); WriteLock(&sfont->Lock); - if(sfont->ref != 0) + if(ReadRef(&sfont->ref) != 0) alSetError(context, AL_INVALID_OPERATION); else if(sfont->Mapped) alSetError(context, AL_INVALID_OPERATION); @@ -232,7 +232,7 @@ AL_API ALvoid* AL_APIENTRY alSoundfontMapSamplesSOFT(ALuint id, ALsizei offset, SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); ReadLock(&sfont->Lock); - if(sfont->ref != 0) + if(ReadRef(&sfont->ref) != 0) alSetError(context, AL_INVALID_OPERATION); else if(ExchangeInt(&sfont->Mapped, AL_TRUE) == AL_TRUE) alSetError(context, AL_INVALID_OPERATION); @@ -329,7 +329,7 @@ AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const A SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); WriteLock(&sfont->Lock); - if(sfont->ref != 0) + if(ReadRef(&sfont->ref) != 0) { WriteUnlock(&sfont->Lock); SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); @@ -350,6 +350,7 @@ AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const A { if(!(presets[i]=LookupPreset(device, pids[i]))) { + free(presets); WriteUnlock(&sfont->Lock); SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); } @@ -389,7 +390,7 @@ AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_ SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); WriteLock(&sfont->Lock); - if(sfont->ref != 0) + if(ReadRef(&sfont->ref) != 0) { WriteUnlock(&sfont->Lock); SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); @@ -418,7 +419,7 @@ done: void ALsoundfont_Construct(ALsoundfont *self) { - self->ref = 0; + InitRef(&self->ref, 0); self->Presets = NULL; self->NumPresets = 0; @@ -469,7 +470,7 @@ ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context) { FILE *f; - f = fopen(fname, "rb"); + f = OpenDataFile(fname, "openal/soundfonts"); if(f == NULL) ERR("Failed to open %s\n", fname); else @@ -519,7 +520,7 @@ void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device) deleting = AL_FALSE; for(j = 0;j < num_sounds;j++) { - if(sounds[j] && sounds[j]->ref == 0) + if(sounds[j] && ReadRef(&sounds[j]->ref) == 0) { deleting = AL_TRUE; RemoveFontsound(device, sounds[j]->id); diff --git a/OpenAL32/alSource.c b/OpenAL32/alSource.c index 543910db..a580c45c 100644 --- a/OpenAL32/alSource.c +++ b/OpenAL32/alSource.c @@ -33,6 +33,8 @@ #include "alThunk.h" #include "alAuxEffectSlot.h" +#include "threads.h" + enum Resampler DefaultResampler = LinearResampler; const ALsizei ResamplerPadding[ResamplerMax] = { @@ -98,6 +100,8 @@ typedef enum SrcFloatProp { /* AL_EXT_source_distance_model */ sfDistanceModel = AL_DISTANCE_MODEL, + sfSecLength = AL_SEC_LENGTH_SOFT, + /* AL_SOFT_buffer_sub_data / AL_SOFT_buffer_samples */ sfSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, sfByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, @@ -140,6 +144,9 @@ typedef enum SrcIntProp { /* AL_EXT_source_distance_model */ siDistanceModel = AL_DISTANCE_MODEL, + siByteLength = AL_BYTE_LENGTH_SOFT, + siSampleLength = AL_SAMPLE_LENGTH_SOFT, + /* AL_SOFT_buffer_sub_data / AL_SOFT_buffer_samples */ siSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, siByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, @@ -152,9 +159,9 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SrcFloatProp static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint *values); static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint64SOFT *values); -static ALboolean GetSourcedv(const ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values); -static ALboolean GetSourceiv(const ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values); -static ALboolean GetSourcei64v(const ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values); +static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values); +static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values); +static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values); static ALint FloatValsByProp(ALenum prop) { @@ -191,6 +198,7 @@ static ALint FloatValsByProp(ALenum prop) case sfBuffersQueued: case sfBuffersProcessed: case sfSourceType: + case sfSecLength: return 1; case sfSampleRWOffsetsSOFT: @@ -242,6 +250,7 @@ static ALint DoubleValsByProp(ALenum prop) case sfBuffersQueued: case sfBuffersProcessed: case sfSourceType: + case sfSecLength: return 1; case sfSampleRWOffsetsSOFT: @@ -285,6 +294,8 @@ static ALint IntValsByProp(ALenum prop) case siDirectFilter: case siDirectChannelsSOFT: case siDistanceModel: + case siByteLength: + case siSampleLength: return 1; case siSampleRWOffsetsSOFT: @@ -330,6 +341,8 @@ static ALint Int64ValsByProp(ALenum prop) case siDirectFilter: case siDirectChannelsSOFT: case siDistanceModel: + case siByteLength: + case siSampleLength: return 1; case siSampleRWOffsetsSOFT: @@ -478,6 +491,7 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SrcFloatProp return AL_TRUE; + case sfSecLength: case AL_SEC_OFFSET_LATENCY_SOFT: /* Query only */ SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); @@ -572,23 +586,17 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p case AL_BUFFER: CHECKVAL(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL); - LockContext(Context); + WriteLock(&Source->queue_lock); if(!(Source->state == AL_STOPPED || Source->state == AL_INITIAL)) { - UnlockContext(Context); + WriteUnlock(&Source->queue_lock); SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); } - Source->BuffersInQueue = 0; - Source->BuffersPlayed = 0; - if(buffer != NULL) { ALbufferlistitem *BufferListItem; - /* Source is now Static */ - Source->SourceType = AL_STATIC; - /* Add the selected buffer to a one-item queue */ BufferListItem = malloc(sizeof(ALbufferlistitem)); BufferListItem->buffer = buffer; @@ -596,18 +604,14 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p BufferListItem->prev = NULL; IncrementRef(&buffer->ref); + /* Source is now Static */ + Source->SourceType = AL_STATIC; oldlist = ExchangePtr((XchgPtr*)&Source->queue, BufferListItem); - Source->BuffersInQueue = 1; ReadLock(&buffer->lock); Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); Source->SampleSize = BytesFromFmt(buffer->FmtType); ReadUnlock(&buffer->lock); - if(buffer->FmtChannels == FmtMono) - Source->Update = CalcSourceParams; - else - Source->Update = CalcNonAttnSourceParams; - Source->NeedsUpdate = AL_TRUE; } else { @@ -615,6 +619,8 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p Source->SourceType = AL_UNDETERMINED; oldlist = ExchangePtr((XchgPtr*)&Source->queue, NULL); } + Source->current_buffer = Source->queue; + WriteUnlock(&Source->queue_lock); /* Delete all elements in the previous queue */ while(oldlist != NULL) @@ -626,7 +632,6 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p DecrementRef(&temp->buffer->ref); free(temp); } - UnlockContext(Context); return AL_TRUE; case siSourceState: @@ -658,6 +663,8 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p return AL_TRUE; + case siByteLength: + case siSampleLength: case siSampleRWOffsetsSOFT: case siByteRWOffsetsSOFT: /* Query only */ @@ -670,13 +677,19 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p LockContext(Context); if(!filter) { - Source->DirectGain = 1.0f; - Source->DirectGainHF = 1.0f; + Source->Direct.Gain = 1.0f; + Source->Direct.GainHF = 1.0f; + Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.GainLF = 1.0f; + Source->Direct.LFReference = HIGHPASSFREQREF; } else { - Source->DirectGain = filter->Gain; - Source->DirectGainHF = filter->GainHF; + Source->Direct.Gain = filter->Gain; + Source->Direct.GainHF = filter->GainHF; + Source->Direct.HFReference = filter->HFReference; + Source->Direct.GainLF = filter->GainLF; + Source->Direct.LFReference = filter->LFReference; } UnlockContext(Context); Source->NeedsUpdate = AL_TRUE; @@ -745,11 +758,17 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp p /* Disable filter */ Source->Send[values[1]].Gain = 1.0f; Source->Send[values[1]].GainHF = 1.0f; + Source->Send[values[1]].HFReference = LOWPASSFREQREF; + Source->Send[values[1]].GainLF = 1.0f; + Source->Send[values[1]].LFReference = HIGHPASSFREQREF; } else { Source->Send[values[1]].Gain = filter->Gain; Source->Send[values[1]].GainHF = filter->GainHF; + Source->Send[values[1]].HFReference = filter->HFReference; + Source->Send[values[1]].GainLF = filter->GainLF; + Source->Send[values[1]].LFReference = filter->LFReference; } Source->NeedsUpdate = AL_TRUE; UnlockContext(Context); @@ -802,6 +821,8 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp case AL_SOURCE_STATE: case AL_BYTE_OFFSET: case AL_SAMPLE_OFFSET: + case siByteLength: + case siSampleLength: case siSourceType: case siBuffersQueued: case siBuffersProcessed: @@ -862,8 +883,9 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp #undef CHECKVAL -static ALboolean GetSourcedv(const ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values) +static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values) { + ALbufferlistitem *BufferList; ALdouble offsets[2]; ALdouble updateLen; ALint ivals[3]; @@ -914,11 +936,11 @@ static ALboolean GetSourcedv(const ALsource *Source, ALCcontext *Context, SrcFlo case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: + ReadLock(&Source->queue_lock); LockContext(Context); - updateLen = (ALdouble)Context->Device->UpdateSize / - Context->Device->Frequency; - GetSourceOffsets(Source, prop, offsets, updateLen); + GetSourceOffsets(Source, prop, offsets, 0.0); UnlockContext(Context); + ReadUnlock(&Source->queue_lock); *values = offsets[0]; return AL_TRUE; @@ -938,21 +960,46 @@ static ALboolean GetSourcedv(const ALsource *Source, ALCcontext *Context, SrcFlo *values = Source->DopplerFactor; return AL_TRUE; + case sfSecLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=Source->queue)) + *values = 0; + else + { + ALint length = 0; + ALsizei freq = 1; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer && buffer->SampleLen > 0) + { + freq = buffer->Frequency; + length += buffer->SampleLen; + } + } while((BufferList=BufferList->next) != NULL); + *values = (ALdouble)length / (ALdouble)freq; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + case AL_SAMPLE_RW_OFFSETS_SOFT: case AL_BYTE_RW_OFFSETS_SOFT: + ReadLock(&Source->queue_lock); LockContext(Context); updateLen = (ALdouble)Context->Device->UpdateSize / Context->Device->Frequency; GetSourceOffsets(Source, prop, values, updateLen); UnlockContext(Context); + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_SEC_OFFSET_LATENCY_SOFT: + ReadLock(&Source->queue_lock); LockContext(Context); values[0] = GetSourceSecOffset(Source); values[1] = (ALdouble)ALCdevice_GetLatency(Context->Device) / 1000000000.0; UnlockContext(Context); + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_POSITION: @@ -1000,7 +1047,7 @@ static ALboolean GetSourcedv(const ALsource *Source, ALCcontext *Context, SrcFlo SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); } -static ALboolean GetSourceiv(const ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values) +static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values) { ALbufferlistitem *BufferList; ALdouble dvals[3]; @@ -1017,32 +1064,89 @@ static ALboolean GetSourceiv(const ALsource *Source, ALCcontext *Context, SrcInt return AL_TRUE; case AL_BUFFER: - LockContext(Context); - BufferList = Source->queue; - if(Source->SourceType != AL_STATIC) - { - ALuint i = Source->BuffersPlayed; - while(i > 0) - { - BufferList = BufferList->next; - i--; - } - } - *values = ((BufferList && BufferList->buffer) ? - BufferList->buffer->id : 0); - UnlockContext(Context); + ReadLock(&Source->queue_lock); + BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : + Source->current_buffer; + *values = (BufferList && BufferList->buffer) ? BufferList->buffer->id : 0; + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_SOURCE_STATE: *values = Source->state; return AL_TRUE; + case siByteLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=Source->queue)) + *values = 0; + else + { + ALint length = 0; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer && buffer->SampleLen > 0) + { + ALuint byte_align, sample_align; + if(buffer->OriginalType == UserFmtIMA4) + { + ALsizei align = (buffer->OriginalAlign-1)/2 + 4; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + else if(buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (buffer->OriginalAlign-2)/2 + 7; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + else + { + ALsizei align = buffer->OriginalAlign; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + + length += buffer->SampleLen / sample_align * byte_align; + } + } while((BufferList=BufferList->next) != NULL); + *values = length; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case siSampleLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=Source->queue)) + *values = 0; + else + { + ALint length = 0; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer) length += buffer->SampleLen; + } while((BufferList=BufferList->next) != NULL); + *values = length; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + case AL_BUFFERS_QUEUED: - *values = Source->BuffersInQueue; + ReadLock(&Source->queue_lock); + if(!(BufferList=Source->queue)) + *values = 0; + else + { + ALsizei count = 0; + do { + ++count; + } while((BufferList=BufferList->next) != NULL); + *values = count; + } + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_BUFFERS_PROCESSED: - LockContext(Context); + ReadLock(&Source->queue_lock); if(Source->Looping || Source->SourceType != AL_STREAMING) { /* Buffers on a looping source are in a perpetual state of @@ -1050,8 +1154,17 @@ static ALboolean GetSourceiv(const ALsource *Source, ALCcontext *Context, SrcInt *values = 0; } else - *values = Source->BuffersPlayed; - UnlockContext(Context); + { + const ALbufferlistitem *BufferList = Source->queue; + ALsizei played = 0; + while(BufferList && BufferList != Source->current_buffer) + { + played++; + BufferList = BufferList->next; + } + *values = played; + } + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_SOURCE_TYPE: @@ -1125,7 +1238,7 @@ static ALboolean GetSourceiv(const ALsource *Source, ALCcontext *Context, SrcInt SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); } -static ALboolean GetSourcei64v(const ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values) +static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values) { ALdouble dvals[3]; ALint ivals[3]; @@ -1134,10 +1247,12 @@ static ALboolean GetSourcei64v(const ALsource *Source, ALCcontext *Context, SrcI switch(prop) { case AL_SAMPLE_OFFSET_LATENCY_SOFT: + ReadLock(&Source->queue_lock); LockContext(Context); values[0] = GetSourceOffset(Source); values[1] = ALCdevice_GetLatency(Context->Device); UnlockContext(Context); + ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_MAX_DISTANCE: @@ -1178,6 +1293,8 @@ static ALboolean GetSourcei64v(const ALsource *Source, ALCcontext *Context, SrcI case AL_SOURCE_STATE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: + case siByteLength: + case siSampleLength: case AL_SOURCE_TYPE: case AL_DIRECT_FILTER_GAINHF_AUTO: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: @@ -1272,7 +1389,7 @@ AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) } for(i = 0;i < n;i++) { - ALsource **srclist, **srclistend; + ALactivesource **srclist, **srclistend; if((Source=RemoveSource(context, sources[i])) == NULL) continue; @@ -1283,10 +1400,12 @@ AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) srclistend = srclist + context->ActiveSourceCount; while(srclist != srclistend) { - if(*srclist == Source) + if((*srclist)->Source == Source) { - context->ActiveSourceCount--; - *srclist = *(--srclistend); + ALactivesource *temp = *(--srclistend); + *srclistend = *srclist; + *srclist = temp; + --(context->ActiveSourceCount); break; } srclist++; @@ -1903,18 +2022,20 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) LockContext(context); while(n > context->MaxActiveSources-context->ActiveSourceCount) { - void *temp = NULL; + ALactivesource **temp = NULL; ALsizei newcount; newcount = context->MaxActiveSources << 1; if(newcount > 0) temp = realloc(context->ActiveSources, - sizeof(*context->ActiveSources) * newcount); + newcount * sizeof(context->ActiveSources[0])); if(!temp) { UnlockContext(context); SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); } + for(i = context->MaxActiveSources;i < newcount;i++) + temp[i] = NULL; context->ActiveSources = temp; context->MaxActiveSources = newcount; @@ -2041,7 +2162,7 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu ALCcontext *context; ALsource *source; ALsizei i; - ALbufferlistitem *BufferListStart = NULL; + ALbufferlistitem *BufferListStart; ALbufferlistitem *BufferList; ALbuffer *BufferFmt = NULL; @@ -2058,10 +2179,10 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu if((source=LookupSource(context, src)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - LockContext(context); + WriteLock(&source->queue_lock); if(source->SourceType == AL_STATIC) { - UnlockContext(context); + WriteUnlock(&source->queue_lock); /* Can't queue on a Static Source */ SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); } @@ -2078,13 +2199,15 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu BufferList = BufferList->next; } + BufferListStart = NULL; + BufferList = NULL; for(i = 0;i < nb;i++) { ALbuffer *buffer = NULL; if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL) { - UnlockContext(context); - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + WriteUnlock(&source->queue_lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, buffer_error); } if(!BufferListStart) @@ -2104,65 +2227,68 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu BufferList = BufferList->next; } if(!buffer) continue; - IncrementRef(&buffer->ref); + /* Hold a read lock on each buffer being queued while checking all + * provided buffers. This is done so other threads don't see an extra + * reference on some buffers if this operation ends up failing. */ ReadLock(&buffer->lock); + IncrementRef(&buffer->ref); + if(BufferFmt == NULL) { BufferFmt = buffer; source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); source->SampleSize = BytesFromFmt(buffer->FmtType); - if(buffer->FmtChannels == FmtMono) - source->Update = CalcSourceParams; - else - source->Update = CalcNonAttnSourceParams; - - source->NeedsUpdate = AL_TRUE; } else if(BufferFmt->Frequency != buffer->Frequency || BufferFmt->OriginalChannels != buffer->OriginalChannels || BufferFmt->OriginalType != buffer->OriginalType) { - ReadUnlock(&buffer->lock); - UnlockContext(context); - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + WriteUnlock(&source->queue_lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, buffer_error); + + buffer_error: + /* A buffer failed (invalid ID or format), so unlock and release + * each buffer we had. */ + while(BufferList != NULL) + { + ALbufferlistitem *prev = BufferList->prev; + if((buffer=BufferList->buffer) != NULL) + { + DecrementRef(&buffer->ref); + ReadUnlock(&buffer->lock); + } + free(BufferList); + BufferList = prev; + } + goto done; } - ReadUnlock(&buffer->lock); + } + /* All buffers good, unlock them now. */ + while(BufferList != NULL) + { + ALbuffer *buffer = BufferList->buffer; + if(buffer) ReadUnlock(&buffer->lock); + BufferList = BufferList->prev; } /* Source is now streaming */ source->SourceType = AL_STREAMING; - if(source->queue == NULL) - source->queue = BufferListStart; - else + if((BufferList=CompExchangePtr((XchgPtr*)&source->queue, NULL, BufferListStart)) != NULL) { - /* Append to the end of the queue */ - BufferList = source->queue; + /* Queue head is not NULL, append to the end of the queue */ while(BufferList->next != NULL) BufferList = BufferList->next; BufferListStart->prev = BufferList; BufferList->next = BufferListStart; } - BufferListStart = NULL; - - source->BuffersInQueue += nb; - - UnlockContext(context); + CompExchangePtr((XchgPtr*)&source->current_buffer, NULL, BufferListStart); + WriteUnlock(&source->queue_lock); done: - while(BufferListStart) - { - BufferList = BufferListStart; - BufferListStart = BufferList->next; - - if(BufferList->buffer) - DecrementRef(&BufferList->buffer->ref); - free(BufferList); - } - ALCcontext_DecRef(context); } @@ -2170,8 +2296,9 @@ AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint { ALCcontext *context; ALsource *source; - ALsizei i; ALbufferlistitem *BufferList; + ALbufferlistitem *OldHead; + ALsizei i; if(nb == 0) return; @@ -2185,35 +2312,59 @@ AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint if((source=LookupSource(context, src)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - LockContext(context); - if(source->Looping || source->SourceType != AL_STREAMING || - (ALuint)nb > source->BuffersPlayed) + WriteLock(&source->queue_lock); + /* Find the new buffer queue head */ + BufferList = source->queue; + for(i = 0;i < nb && BufferList;i++) { - UnlockContext(context); + if(BufferList == source->current_buffer) + break; + BufferList = BufferList->next; + } + if(source->Looping || source->SourceType != AL_STREAMING || i != nb) + { + WriteUnlock(&source->queue_lock); /* Trying to unqueue pending buffers, or a buffer that wasn't queued. */ SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); } - for(i = 0;i < nb;i++) + /* Swap it, and cut the new head from the old. */ + OldHead = ExchangePtr((XchgPtr*)&source->queue, BufferList); + if(BufferList) { - BufferList = source->queue; - source->queue = BufferList->next; - source->BuffersInQueue--; - source->BuffersPlayed--; - - if(BufferList->buffer) + ALCdevice *device = context->Device; + uint count; + + /* Cut the new head's link back to the old body. The mixer is robust + * enough to handle the link back going away. Once the active mix (if + * any) is complete, it's safe to finish cutting the old tail from the + * new head. */ + BufferList = ExchangePtr((XchgPtr*)&BufferList->prev, NULL); + if(((count=ReadRef(&device->MixCount))&1) != 0) { - buffers[i] = BufferList->buffer->id; - DecrementRef(&BufferList->buffer->ref); + while(count == ReadRef(&device->MixCount)) + althrd_yield(); } + BufferList->next = NULL; + } + WriteUnlock(&source->queue_lock); + + while(OldHead != NULL) + { + ALbufferlistitem *next = OldHead->next; + ALbuffer *buffer = OldHead->buffer; + + if(!buffer) + *(buffers++) = 0; else - buffers[i] = 0; + { + *(buffers++) = buffer->id; + DecrementRef(&buffer->ref); + } - free(BufferList); + free(OldHead); + OldHead = next; } - if(source->queue) - source->queue->prev = NULL; - UnlockContext(context); done: ALCcontext_DecRef(context); @@ -2224,6 +2375,8 @@ static ALvoid InitSourceParams(ALsource *Source) { ALuint i; + RWLockInit(&Source->queue_lock); + Source->InnerAngle = 360.0f; Source->OuterAngle = 360.0f; Source->Pitch = 1.0f; @@ -2263,18 +2416,24 @@ static ALvoid InitSourceParams(ALsource *Source) Source->SourceType = AL_UNDETERMINED; Source->Offset = -1.0; - Source->DirectGain = 1.0f; - Source->DirectGainHF = 1.0f; + Source->queue = NULL; + Source->current_buffer = NULL; + + Source->Direct.Gain = 1.0f; + Source->Direct.GainHF = 1.0f; + Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.GainLF = 1.0f; + Source->Direct.LFReference = HIGHPASSFREQREF; for(i = 0;i < MAX_SENDS;i++) { Source->Send[i].Gain = 1.0f; Source->Send[i].GainHF = 1.0f; + Source->Send[i].HFReference = LOWPASSFREQREF; + Source->Send[i].GainLF = 1.0f; + Source->Send[i].LFReference = HIGHPASSFREQREF; } Source->NeedsUpdate = AL_TRUE; - - Source->Hrtf.Moving = AL_FALSE; - Source->Hrtf.Counter = 0; } @@ -2284,9 +2443,12 @@ static ALvoid InitSourceParams(ALsource *Source) */ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) { + ReadLock(&Source->queue_lock); if(state == AL_PLAYING) { + ALCdevice *device = Context->Device; ALbufferlistitem *BufferList; + ALactivesource *src = NULL; ALsizei j, k; /* Check that there is a queue containing at least one valid, non zero @@ -2294,31 +2456,18 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) BufferList = Source->queue; while(BufferList) { - if(BufferList->buffer != NULL && BufferList->buffer->SampleLen) + ALbuffer *buffer; + if((buffer=BufferList->buffer) != NULL && buffer->SampleLen > 0) break; BufferList = BufferList->next; } - if(Source->state != AL_PLAYING) - { - for(j = 0;j < MAX_INPUT_CHANNELS;j++) - { - for(k = 0;k < SRC_HISTORY_LENGTH;k++) - Source->Hrtf.History[j][k] = 0.0f; - for(k = 0;k < HRIR_LENGTH;k++) - { - Source->Hrtf.Values[j][k][0] = 0.0f; - Source->Hrtf.Values[j][k][1] = 0.0f; - } - } - } - if(Source->state != AL_PAUSED) { Source->state = AL_PLAYING; Source->position = 0; Source->position_fraction = 0; - Source->BuffersPlayed = 0; + Source->current_buffer = BufferList; } else Source->state = AL_PLAYING; @@ -2329,37 +2478,70 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) /* If there's nothing to play, or device is disconnected, go right to * stopped */ - if(!BufferList || !Context->Device->Connected) - { - SetSourceState(Source, Context, AL_STOPPED); - return; - } + if(!BufferList || !device->Connected) + goto do_stop; for(j = 0;j < Context->ActiveSourceCount;j++) { - if(Context->ActiveSources[j] == Source) + if(Context->ActiveSources[j]->Source == Source) + { + src = Context->ActiveSources[j]; break; + } + } + if(src == NULL) + { + src = Context->ActiveSources[Context->ActiveSourceCount]; + if(src == NULL) + { + src = al_malloc(16, sizeof(src[0])); + Context->ActiveSources[Context->ActiveSourceCount] = src; + } + memset(src, 0, sizeof(*src)); + + src->Source = Source; + if(BufferList->buffer->FmtChannels == FmtMono) + src->Update = CalcSourceParams; + else + src->Update = CalcNonAttnSourceParams; + Context->ActiveSourceCount++; + } + else + { + ALuint i; + + src->Direct.Moving = AL_FALSE; + src->Direct.Counter = 0; + for(j = 0;j < MAX_INPUT_CHANNELS;j++) + { + for(k = 0;k < SRC_HISTORY_LENGTH;k++) + src->Direct.Mix.Hrtf.State[j].History[k] = 0.0f; + for(k = 0;k < HRIR_LENGTH;k++) + { + src->Direct.Mix.Hrtf.State[j].Values[k][0] = 0.0f; + src->Direct.Mix.Hrtf.State[j].Values[k][1] = 0.0f; + } + } + for(i = 0;i < device->NumAuxSends;i++) + { + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_FALSE; + } } - if(j == Context->ActiveSourceCount) - Context->ActiveSources[Context->ActiveSourceCount++] = Source; + Source->NeedsUpdate = AL_TRUE; } else if(state == AL_PAUSED) { if(Source->state == AL_PLAYING) - { Source->state = AL_PAUSED; - Source->Hrtf.Moving = AL_FALSE; - Source->Hrtf.Counter = 0; - } } else if(state == AL_STOPPED) { + do_stop: if(Source->state != AL_INITIAL) { Source->state = AL_STOPPED; - Source->BuffersPlayed = Source->BuffersInQueue; - Source->Hrtf.Moving = AL_FALSE; - Source->Hrtf.Counter = 0; + Source->current_buffer = NULL; } Source->Offset = -1.0; } @@ -2370,12 +2552,11 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) Source->state = AL_INITIAL; Source->position = 0; Source->position_fraction = 0; - Source->BuffersPlayed = 0; - Source->Hrtf.Moving = AL_FALSE; - Source->Hrtf.Counter = 0; + Source->current_buffer = Source->queue; } Source->Offset = -1.0; } + ReadUnlock(&Source->queue_lock); } /* GetSourceOffset @@ -2388,7 +2569,6 @@ static ALint64 GetSourceOffset(const ALsource *Source) { const ALbufferlistitem *BufferList; ALuint64 readPos; - ALuint i; if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) return 0; @@ -2398,7 +2578,7 @@ static ALint64 GetSourceOffset(const ALsource *Source) readPos = (ALuint64)Source->position << 32; readPos |= (ALuint64)Source->position_fraction << (32-FRACTIONBITS); BufferList = Source->queue; - for(i = 0;i < Source->BuffersPlayed && BufferList;i++) + while(BufferList && BufferList != Source->current_buffer) { if(BufferList->buffer) readPos += (ALuint64)BufferList->buffer->SampleLen << 32; @@ -2418,20 +2598,8 @@ static ALdouble GetSourceSecOffset(const ALsource *Source) const ALbufferlistitem *BufferList; const ALbuffer *Buffer = NULL; ALuint64 readPos; - ALuint i; - - BufferList = Source->queue; - while(BufferList) - { - if(BufferList->buffer) - { - Buffer = BufferList->buffer; - break; - } - BufferList = BufferList->next; - } - if((Source->state != AL_PLAYING && Source->state != AL_PAUSED) || !Buffer) + if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) return 0.0; /* NOTE: This is the offset into the *current* buffer, so add the length of @@ -2439,13 +2607,24 @@ static ALdouble GetSourceSecOffset(const ALsource *Source) readPos = (ALuint64)Source->position << FRACTIONBITS; readPos |= (ALuint64)Source->position_fraction; BufferList = Source->queue; - for(i = 0;i < Source->BuffersPlayed && BufferList;i++) + while(BufferList && BufferList != Source->current_buffer) { - if(BufferList->buffer) - readPos += (ALuint64)BufferList->buffer->SampleLen << FRACTIONBITS; + const ALbuffer *buffer = BufferList->buffer; + if(buffer != NULL) + { + if(!Buffer) Buffer = buffer; + readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS; + } BufferList = BufferList->next; } + while(BufferList && !Buffer) + { + Buffer = BufferList->buffer; + BufferList = BufferList->next; + } + assert(Buffer != NULL); + return (ALdouble)readPos / (ALdouble)FRACTIONONE / (ALdouble)Buffer->Frequency; } @@ -2458,24 +2637,12 @@ static ALdouble GetSourceSecOffset(const ALsource *Source) static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *offset, ALdouble updateLen) { const ALbufferlistitem *BufferList; - const ALbuffer *Buffer = NULL; + const ALbuffer *Buffer = NULL; + ALboolean readFin = AL_FALSE; ALuint readPos, writePos; ALuint totalBufferLen; - ALuint i; - // Find the first valid Buffer in the Queue - BufferList = Source->queue; - while(BufferList) - { - if(BufferList->buffer) - { - Buffer = BufferList->buffer; - break; - } - BufferList = BufferList->next; - } - - if((Source->state != AL_PLAYING && Source->state != AL_PAUSED) || !Buffer) + if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) { offset[0] = 0.0; offset[1] = 0.0; @@ -2487,19 +2654,23 @@ static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *of /* NOTE: This is the offset into the *current* buffer, so add the length of * any played buffers */ - readPos = Source->position; totalBufferLen = 0; + readPos = Source->position; BufferList = Source->queue; - for(i = 0;BufferList;i++) + while(BufferList != NULL) { - if(BufferList->buffer) + const ALbuffer *buffer; + readFin = readFin || (BufferList == Source->current_buffer); + if((buffer=BufferList->buffer) != NULL) { - if(i < Source->BuffersPlayed) - readPos += BufferList->buffer->SampleLen; - totalBufferLen += BufferList->buffer->SampleLen; + if(!Buffer) Buffer = buffer; + totalBufferLen += buffer->SampleLen; + if(!readFin) readPos += buffer->SampleLen; } BufferList = BufferList->next; } + assert(Buffer != NULL); + if(Source->state == AL_PLAYING) writePos = readPos + (ALuint)(updateLen*Buffer->Frequency); else @@ -2536,8 +2707,26 @@ static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *of case AL_BYTE_RW_OFFSETS_SOFT: if(Buffer->OriginalType == UserFmtIMA4) { - ALuint BlockSize = 36 * ChannelsFromFmt(Buffer->FmtChannels); - ALuint FrameBlockSize = 65; + ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; + ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); + ALuint FrameBlockSize = Buffer->OriginalAlign; + + /* Round down to nearest ADPCM block */ + offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); + if(Source->state != AL_PLAYING) + offset[1] = offset[0]; + else + { + /* Round up to nearest ADPCM block */ + offset[1] = (ALdouble)((writePos+FrameBlockSize-1) / + FrameBlockSize * BlockSize); + } + } + else if(Buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; + ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); + ALuint FrameBlockSize = Buffer->OriginalAlign; /* Round down to nearest ADPCM block */ offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); @@ -2568,10 +2757,9 @@ static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *of */ ALboolean ApplyOffset(ALsource *Source) { - const ALbufferlistitem *BufferList; - const ALbuffer *Buffer; + ALbufferlistitem *BufferList; + const ALbuffer *Buffer; ALint bufferLen, totalBufferLen; - ALint buffersPlayed; ALint offset; /* Get sample frame offset */ @@ -2579,24 +2767,17 @@ ALboolean ApplyOffset(ALsource *Source) if(offset == -1) return AL_FALSE; - buffersPlayed = 0; totalBufferLen = 0; - BufferList = Source->queue; - while(BufferList) + while(BufferList && totalBufferLen <= offset) { Buffer = BufferList->buffer; bufferLen = Buffer ? Buffer->SampleLen : 0; - if(bufferLen <= offset-totalBufferLen) - { - /* Offset is past this buffer so increment to the next buffer */ - buffersPlayed++; - } - else if(totalBufferLen <= offset) + if(bufferLen > offset-totalBufferLen) { /* Offset is in this buffer */ - Source->BuffersPlayed = buffersPlayed; + Source->current_buffer = BufferList; Source->position = offset - totalBufferLen; Source->position_fraction = 0; @@ -2650,8 +2831,15 @@ static ALint GetSampleOffset(ALsource *Source) Offset = (ALint)Source->Offset; if(Buffer->OriginalType == UserFmtIMA4) { - Offset /= 36 * ChannelsFromUserFmt(Buffer->OriginalChannels); - Offset *= 65; + ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; + Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); + Offset *= Buffer->OriginalAlign; + } + else if(Buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; + Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); + Offset *= Buffer->OriginalAlign; } else Offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); diff --git a/OpenAL32/alState.c b/OpenAL32/alState.c index 280dd896..d499fcd1 100644 --- a/OpenAL32/alState.c +++ b/OpenAL32/alState.c @@ -715,7 +715,7 @@ AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) if(!context->DeferUpdates) { ALboolean UpdateSources; - ALsource **src, **src_end; + ALactivesource **src, **src_end; ALeffectslot **slot, **slot_end; FPUCtl oldMode; @@ -731,21 +731,25 @@ AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) src_end = src + context->ActiveSourceCount; while(src != src_end) { - if((*src)->state != AL_PLAYING) + ALsource *source = (*src)->Source; + + if(source->state != AL_PLAYING && source->state != AL_PAUSED) { - context->ActiveSourceCount--; - *src = *(--src_end); + ALactivesource *temp = *(--src_end); + *src_end = *src; + *src = temp; + --(context->ActiveSourceCount); continue; } - if(ExchangeInt(&(*src)->NeedsUpdate, AL_FALSE) || UpdateSources) - ALsource_Update(*src, context); + if(ExchangeInt(&source->NeedsUpdate, AL_FALSE) || UpdateSources) + (*src)->Update(*src, context); src++; } - slot = context->ActiveEffectSlots; - slot_end = slot + context->ActiveEffectSlotCount; + slot = VECTOR_ITER_BEGIN(context->ActiveAuxSlots); + slot_end = VECTOR_ITER_END(context->ActiveAuxSlots); while(slot != slot_end) { if(ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE)) @@ -780,7 +784,11 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void) if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && Source->Offset >= 0.0) + { + ReadLock(&Source->queue_lock); ApplyOffset(Source); + ReadUnlock(&Source->queue_lock); + } new_state = ExchangeInt(&Source->new_state, AL_NONE); if(new_state) diff --git a/OpenAL32/sample_cvt.c b/OpenAL32/sample_cvt.c new file mode 100644 index 00000000..a02b217e --- /dev/null +++ b/OpenAL32/sample_cvt.c @@ -0,0 +1,1269 @@ + +#include "config.h" + +#include "sample_cvt.h" + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +#include "AL/al.h" +#include "alu.h" +#include "alBuffer.h" + + +/* IMA ADPCM Stepsize table */ +static const int IMAStep_size[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, + 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, + 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, + 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, + 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, + 32767 +}; + +/* IMA4 ADPCM Codeword decode table */ +static const int IMA4Codeword[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1,-3,-5,-7,-9,-11,-13,-15, +}; + +/* IMA4 ADPCM Step index adjust decode table */ +static const int IMA4Index_adjust[16] = { + -1,-1,-1,-1, 2, 4, 6, 8, + -1,-1,-1,-1, 2, 4, 6, 8 +}; + + +/* MSADPCM Adaption table */ +static const int MSADPCMAdaption[16] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +/* MSADPCM Adaption Coefficient tables */ +static const int MSADPCMAdaptionCoeff[7][2] = { + { 256, 0 }, + { 512, -256 }, + { 0, 0 }, + { 192, 64 }, + { 240, 0 }, + { 460, -208 }, + { 392, -232 } +}; + + +/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a + * signed 16-bit sample */ +static const ALshort muLawDecompressionTable[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + +/* Values used when encoding a muLaw sample */ +static const int muLawBias = 0x84; +static const int muLawClip = 32635; +static const char muLawCompressTable[256] = { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + + +/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a + * signed 16-bit sample */ +static const ALshort aLawDecompressionTable[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +}; + +/* Values used when encoding an aLaw sample */ +static const int aLawClip = 32635; +static const char aLawCompressTable[128] = { + 1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + + +typedef ALubyte ALmulaw; +typedef ALubyte ALalaw; +typedef ALubyte ALima4; +typedef ALubyte ALmsadpcm; +typedef struct { + ALbyte b[3]; +} ALbyte3; +static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3"); +typedef struct { + ALubyte b[3]; +} ALubyte3; +static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3"); + +static inline ALshort DecodeMuLaw(ALmulaw val) +{ return muLawDecompressionTable[val]; } + +static ALmulaw EncodeMuLaw(ALshort val) +{ + ALint mant, exp, sign; + + sign = (val>>8) & 0x80; + if(sign) + { + /* -32768 doesn't properly negate on a short; it results in itself. + * So clamp to -32767 */ + val = maxi(val, -32767); + val = -val; + } + + val = mini(val, muLawClip); + val += muLawBias; + + exp = muLawCompressTable[(val>>7) & 0xff]; + mant = (val >> (exp+3)) & 0x0f; + + return ~(sign | (exp<<4) | mant); +} + +static inline ALshort DecodeALaw(ALalaw val) +{ return aLawDecompressionTable[val]; } + +static ALalaw EncodeALaw(ALshort val) +{ + ALint mant, exp, sign; + + sign = ((~val) >> 8) & 0x80; + if(!sign) + { + val = maxi(val, -32767); + val = -val; + } + val = mini(val, aLawClip); + + if(val >= 256) + { + exp = aLawCompressTable[(val>>8) & 0x7f]; + mant = (val >> (exp+3)) & 0x0f; + } + else + { + exp = 0; + mant = val >> 4; + } + + return ((exp<<4) | mant) ^ (sign^0x55); +} + +static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans, ALsizei align) +{ + ALint sample[MAX_INPUT_CHANNELS], index[MAX_INPUT_CHANNELS]; + ALuint code[MAX_INPUT_CHANNELS]; + ALsizei j,k,c; + + for(c = 0;c < numchans;c++) + { + sample[c] = *(src++); + sample[c] |= *(src++) << 8; + sample[c] = (sample[c]^0x8000) - 32768; + index[c] = *(src++); + index[c] |= *(src++) << 8; + index[c] = (index[c]^0x8000) - 32768; + + index[c] = clampi(index[c], 0, 88); + + dst[c] = sample[c]; + } + + for(j = 1;j < align;j += 8) + { + for(c = 0;c < numchans;c++) + { + code[c] = *(src++); + code[c] |= *(src++) << 8; + code[c] |= *(src++) << 16; + code[c] |= *(src++) << 24; + } + + for(k = 0;k < 8;k++) + { + for(c = 0;c < numchans;c++) + { + int nibble = code[c]&0xf; + code[c] >>= 4; + + sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + dst[(j+k)*numchans + c] = sample[c]; + } + } + } +} + +static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans, ALsizei align) +{ + ALsizei j,k,c; + + for(c = 0;c < numchans;c++) + { + int diff = src[c] - sample[c]; + int step = IMAStep_size[index[c]]; + int nibble; + + nibble = 0; + if(diff < 0) + { + nibble = 0x8; + diff = -diff; + } + + diff = mini(step*2, diff); + nibble |= (diff*8/step - 1) / 2; + + sample[c] += IMA4Codeword[nibble] * step / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + *(dst++) = sample[c] & 0xff; + *(dst++) = (sample[c]>>8) & 0xff; + *(dst++) = index[c] & 0xff; + *(dst++) = (index[c]>>8) & 0xff; + } + + for(j = 1;j < align;j += 8) + { + for(c = 0;c < numchans;c++) + { + for(k = 0;k < 8;k++) + { + int diff = src[(j+k)*numchans + c] - sample[c]; + int step = IMAStep_size[index[c]]; + int nibble; + + nibble = 0; + if(diff < 0) + { + nibble = 0x8; + diff = -diff; + } + + diff = mini(step*2, diff); + nibble |= (diff*8/step - 1) / 2; + + sample[c] += IMA4Codeword[nibble] * step / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + if(!(k&1)) *dst = nibble; + else *(dst++) |= nibble<<4; + } + } + } +} + + +static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchans, ALsizei align) +{ + ALubyte blockpred[MAX_INPUT_CHANNELS]; + ALint delta[MAX_INPUT_CHANNELS]; + ALshort samples[MAX_INPUT_CHANNELS][2]; + ALint i, j; + + for(i = 0;i < numchans;i++) + { + blockpred[i] = *(src++); + blockpred[i] = minu(blockpred[i], 6); + } + for(i = 0;i < numchans;i++) + { + delta[i] = *(src++); + delta[i] |= *(src++) << 8; + delta[i] = (delta[i]^0x8000) - 0x8000; + } + for(i = 0;i < numchans;i++) + { + samples[i][0] = *(src++); + samples[i][0] |= *(src++) << 8; + samples[i][0] = (samples[i][0]^0x8000) - 0x8000; + } + for(i = 0;i < numchans;i++) + { + samples[i][1] = *(src++); + samples[i][1] |= *(src++) << 8; + samples[i][1] = (samples[i][1]^0x8000) - 0x8000; + } + + /* Second sample is written first. */ + for(i = 0;i < numchans;i++) + *(dst++) = samples[i][1]; + for(i = 0;i < numchans;i++) + *(dst++) = samples[i][0]; + + for(j = 2;j < align;j++) + { + for(i = 0;i < numchans;i++) + { + const ALint num = (j*numchans) + i; + ALint nibble, pred; + + /* Read the nibble (first is in the upper bits). */ + if(!(num&1)) + nibble = (*src>>4)&0x0f; + else + nibble = (*(src++))&0x0f; + + pred = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + + samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; + pred += ((nibble^0x08) - 0x08) * delta[i]; + pred = clampi(pred, -32768, 32767); + + samples[i][1] = samples[i][0]; + samples[i][0] = pred; + + delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; + delta[i] = maxi(16, delta[i]); + + *(dst++) = pred; + } + } +} + +/* NOTE: This encoder is pretty dumb/simplistic. Some kind of pre-processing + * that tries to find the optimal block predictors would be nice, at least. A + * multi-pass method that can generate better deltas would be good, too. */ +static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample, ALint numchans, ALsizei align) +{ + ALubyte blockpred[MAX_INPUT_CHANNELS]; + ALint delta[MAX_INPUT_CHANNELS]; + ALshort samples[MAX_INPUT_CHANNELS][2]; + ALint i, j; + + /* Block predictor */ + for(i = 0;i < numchans;i++) + { + /* FIXME: Calculate something better. */ + blockpred[i] = 0; + *(dst++) = blockpred[i]; + } + /* Initial delta */ + for(i = 0;i < numchans;i++) + { + delta[i] = 16; + *(dst++) = (delta[i] ) & 0xff; + *(dst++) = (delta[i]>>8) & 0xff; + } + /* Initial sample 1 */ + for(i = 0;i < numchans;i++) + { + samples[i][0] = src[1*numchans + i]; + *(dst++) = (samples[i][0] ) & 0xff; + *(dst++) = (samples[i][0]>>8) & 0xff; + } + /* Initial sample 2 */ + for(i = 0;i < numchans;i++) + { + samples[i][1] = src[i]; + *(dst++) = (samples[i][1] ) & 0xff; + *(dst++) = (samples[i][1]>>8) & 0xff; + } + + for(j = 2;j < align;j++) + { + for(i = 0;i < numchans;i++) + { + const ALint num = (j*numchans) + i; + ALint nibble = 0; + ALint bias; + + sample[i] = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + + samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; + + nibble = src[num] - sample[i]; + if(nibble >= 0) + bias = delta[i] / 2; + else + bias = -delta[i] / 2; + + nibble = (nibble + bias) / delta[i]; + nibble = clampi(nibble, -8, 7)&0x0f; + + sample[i] += ((nibble^0x08)-0x08) * delta[i]; + sample[i] = clampi(sample[i], -32768, 32767); + + samples[i][1] = samples[i][0]; + samples[i][0] = sample[i]; + + delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; + delta[i] = maxi(16, delta[i]); + + if(!(num&1)) + *dst = nibble << 4; + else + { + *dst |= nibble; + dst++; + } + } + } +} + + +static inline ALint DecodeByte3(ALbyte3 val) +{ + if(IS_LITTLE_ENDIAN) + return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]); + return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]); +} + +static inline ALbyte3 EncodeByte3(ALint val) +{ + if(IS_LITTLE_ENDIAN) + { + ALbyte3 ret = {{ val, val>>8, val>>16 }}; + return ret; + } + else + { + ALbyte3 ret = {{ val>>16, val>>8, val }}; + return ret; + } +} + +static inline ALint DecodeUByte3(ALubyte3 val) +{ + if(IS_LITTLE_ENDIAN) + return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]); + return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2]; +} + +static inline ALubyte3 EncodeUByte3(ALint val) +{ + if(IS_LITTLE_ENDIAN) + { + ALubyte3 ret = {{ val, val>>8, val>>16 }}; + return ret; + } + else + { + ALubyte3 ret = {{ val>>16, val>>8, val }}; + return ret; + } +} + + +static inline ALbyte Conv_ALbyte_ALbyte(ALbyte val) +{ return val; } +static inline ALbyte Conv_ALbyte_ALubyte(ALubyte val) +{ return val-128; } +static inline ALbyte Conv_ALbyte_ALshort(ALshort val) +{ return val>>8; } +static inline ALbyte Conv_ALbyte_ALushort(ALushort val) +{ return (val>>8)-128; } +static inline ALbyte Conv_ALbyte_ALint(ALint val) +{ return val>>24; } +static inline ALbyte Conv_ALbyte_ALuint(ALuint val) +{ return (val>>24)-128; } +static inline ALbyte Conv_ALbyte_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 127; + if(val < -1.0f) return -128; + return (ALint)(val * 127.0f); +} +static inline ALbyte Conv_ALbyte_ALdouble(ALdouble val) +{ + if(val > 1.0) return 127; + if(val < -1.0) return -128; + return (ALint)(val * 127.0); +} +static inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val) +{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); } +static inline ALbyte Conv_ALbyte_ALalaw(ALalaw val) +{ return Conv_ALbyte_ALshort(DecodeALaw(val)); } +static inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)>>16; } +static inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)>>16)-128; } + +static inline ALubyte Conv_ALubyte_ALbyte(ALbyte val) +{ return val+128; } +static inline ALubyte Conv_ALubyte_ALubyte(ALubyte val) +{ return val; } +static inline ALubyte Conv_ALubyte_ALshort(ALshort val) +{ return (val>>8)+128; } +static inline ALubyte Conv_ALubyte_ALushort(ALushort val) +{ return val>>8; } +static inline ALubyte Conv_ALubyte_ALint(ALint val) +{ return (val>>24)+128; } +static inline ALubyte Conv_ALubyte_ALuint(ALuint val) +{ return val>>24; } +static inline ALubyte Conv_ALubyte_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 255; + if(val < -1.0f) return 0; + return (ALint)(val * 127.0f) + 128; +} +static inline ALubyte Conv_ALubyte_ALdouble(ALdouble val) +{ + if(val > 1.0) return 255; + if(val < -1.0) return 0; + return (ALint)(val * 127.0) + 128; +} +static inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val) +{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); } +static inline ALubyte Conv_ALubyte_ALalaw(ALalaw val) +{ return Conv_ALubyte_ALshort(DecodeALaw(val)); } +static inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)>>16)+128; } +static inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)>>16; } + +static inline ALshort Conv_ALshort_ALbyte(ALbyte val) +{ return val<<8; } +static inline ALshort Conv_ALshort_ALubyte(ALubyte val) +{ return (val-128)<<8; } +static inline ALshort Conv_ALshort_ALshort(ALshort val) +{ return val; } +static inline ALshort Conv_ALshort_ALushort(ALushort val) +{ return val-32768; } +static inline ALshort Conv_ALshort_ALint(ALint val) +{ return val>>16; } +static inline ALshort Conv_ALshort_ALuint(ALuint val) +{ return (val>>16)-32768; } +static inline ALshort Conv_ALshort_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 32767; + if(val < -1.0f) return -32768; + return (ALint)(val * 32767.0f); +} +static inline ALshort Conv_ALshort_ALdouble(ALdouble val) +{ + if(val > 1.0) return 32767; + if(val < -1.0) return -32768; + return (ALint)(val * 32767.0); +} +static inline ALshort Conv_ALshort_ALmulaw(ALmulaw val) +{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); } +static inline ALshort Conv_ALshort_ALalaw(ALalaw val) +{ return Conv_ALshort_ALshort(DecodeALaw(val)); } +static inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)>>8; } +static inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)>>8)-32768; } + +static inline ALushort Conv_ALushort_ALbyte(ALbyte val) +{ return (val+128)<<8; } +static inline ALushort Conv_ALushort_ALubyte(ALubyte val) +{ return val<<8; } +static inline ALushort Conv_ALushort_ALshort(ALshort val) +{ return val+32768; } +static inline ALushort Conv_ALushort_ALushort(ALushort val) +{ return val; } +static inline ALushort Conv_ALushort_ALint(ALint val) +{ return (val>>16)+32768; } +static inline ALushort Conv_ALushort_ALuint(ALuint val) +{ return val>>16; } +static inline ALushort Conv_ALushort_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 65535; + if(val < -1.0f) return 0; + return (ALint)(val * 32767.0f) + 32768; +} +static inline ALushort Conv_ALushort_ALdouble(ALdouble val) +{ + if(val > 1.0) return 65535; + if(val < -1.0) return 0; + return (ALint)(val * 32767.0) + 32768; +} +static inline ALushort Conv_ALushort_ALmulaw(ALmulaw val) +{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); } +static inline ALushort Conv_ALushort_ALalaw(ALalaw val) +{ return Conv_ALushort_ALshort(DecodeALaw(val)); } +static inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)>>8)+32768; } +static inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)>>8; } + +static inline ALint Conv_ALint_ALbyte(ALbyte val) +{ return val<<24; } +static inline ALint Conv_ALint_ALubyte(ALubyte val) +{ return (val-128)<<24; } +static inline ALint Conv_ALint_ALshort(ALshort val) +{ return val<<16; } +static inline ALint Conv_ALint_ALushort(ALushort val) +{ return (val-32768)<<16; } +static inline ALint Conv_ALint_ALint(ALint val) +{ return val; } +static inline ALint Conv_ALint_ALuint(ALuint val) +{ return val-2147483648u; } +static inline ALint Conv_ALint_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 2147483647; + if(val < -1.0f) return -2147483647-1; + return (ALint)(val*16777215.0f) << 7; +} +static inline ALint Conv_ALint_ALdouble(ALdouble val) +{ + if(val > 1.0) return 2147483647; + if(val < -1.0) return -2147483647-1; + return (ALint)(val * 2147483647.0); +} +static inline ALint Conv_ALint_ALmulaw(ALmulaw val) +{ return Conv_ALint_ALshort(DecodeMuLaw(val)); } +static inline ALint Conv_ALint_ALalaw(ALalaw val) +{ return Conv_ALint_ALshort(DecodeALaw(val)); } +static inline ALint Conv_ALint_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)<<8; } +static inline ALint Conv_ALint_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)-8388608)<<8; } + +static inline ALuint Conv_ALuint_ALbyte(ALbyte val) +{ return (val+128)<<24; } +static inline ALuint Conv_ALuint_ALubyte(ALubyte val) +{ return val<<24; } +static inline ALuint Conv_ALuint_ALshort(ALshort val) +{ return (val+32768)<<16; } +static inline ALuint Conv_ALuint_ALushort(ALushort val) +{ return val<<16; } +static inline ALuint Conv_ALuint_ALint(ALint val) +{ return val+2147483648u; } +static inline ALuint Conv_ALuint_ALuint(ALuint val) +{ return val; } +static inline ALuint Conv_ALuint_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 4294967295u; + if(val < -1.0f) return 0; + return ((ALint)(val*16777215.0f)<<7) + 2147483648u; +} +static inline ALuint Conv_ALuint_ALdouble(ALdouble val) +{ + if(val > 1.0) return 4294967295u; + if(val < -1.0) return 0; + return (ALint)(val * 2147483647.0) + 2147483648u; +} +static inline ALuint Conv_ALuint_ALmulaw(ALmulaw val) +{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); } +static inline ALuint Conv_ALuint_ALalaw(ALalaw val) +{ return Conv_ALuint_ALshort(DecodeALaw(val)); } +static inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)+8388608)<<8; } +static inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)<<8; } + +static inline ALfloat Conv_ALfloat_ALbyte(ALbyte val) +{ return val * (1.0f/127.0f); } +static inline ALfloat Conv_ALfloat_ALubyte(ALubyte val) +{ return (val-128) * (1.0f/127.0f); } +static inline ALfloat Conv_ALfloat_ALshort(ALshort val) +{ return val * (1.0f/32767.0f); } +static inline ALfloat Conv_ALfloat_ALushort(ALushort val) +{ return (val-32768) * (1.0f/32767.0f); } +static inline ALfloat Conv_ALfloat_ALint(ALint val) +{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); } +static inline ALfloat Conv_ALfloat_ALuint(ALuint val) +{ return (ALfloat)((ALint)(val>>7)-16777216) * (1.0f/16777215.0f); } +static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val) +{ return (val==val) ? val : 0.0f; } +static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val) +{ return (val==val) ? (ALfloat)val : 0.0f; } +static inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val) +{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); } +static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val) +{ return Conv_ALfloat_ALshort(DecodeALaw(val)); } +static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val) +{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); } +static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val) +{ return (ALfloat)((DecodeUByte3(val)-8388608) * (1.0/8388607.0)); } + +static inline ALdouble Conv_ALdouble_ALbyte(ALbyte val) +{ return val * (1.0/127.0); } +static inline ALdouble Conv_ALdouble_ALubyte(ALubyte val) +{ return (val-128) * (1.0/127.0); } +static inline ALdouble Conv_ALdouble_ALshort(ALshort val) +{ return val * (1.0/32767.0); } +static inline ALdouble Conv_ALdouble_ALushort(ALushort val) +{ return (val-32768) * (1.0/32767.0); } +static inline ALdouble Conv_ALdouble_ALint(ALint val) +{ return val * (1.0/2147483647.0); } +static inline ALdouble Conv_ALdouble_ALuint(ALuint val) +{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); } +static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val) +{ return (val==val) ? val : 0.0f; } +static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val) +{ return (val==val) ? val : 0.0; } +static inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val) +{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); } +static inline ALdouble Conv_ALdouble_ALalaw(ALalaw val) +{ return Conv_ALdouble_ALshort(DecodeALaw(val)); } +static inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val) * (1.0/8388607.0); } +static inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); } + +#define DECL_TEMPLATE(T) \ +static inline ALmulaw Conv_ALmulaw_##T(T val) \ +{ return EncodeMuLaw(Conv_ALshort_##T(val)); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val) +{ return val; } +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static inline ALalaw Conv_ALalaw_##T(T val) \ +{ return EncodeALaw(Conv_ALshort_##T(val)); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +static inline ALalaw Conv_ALalaw_ALalaw(ALalaw val) +{ return val; } +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static inline ALbyte3 Conv_ALbyte3_##T(T val) \ +{ return EncodeByte3(Conv_ALint_##T(val)>>8); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val) +{ return val; } +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static inline ALubyte3 Conv_ALubyte3_##T(T val) \ +{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +static inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val) +{ return val; } + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(T1, T2) \ +static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans, \ + ALuint len, ALsizei UNUSED(align)) \ +{ \ + ALuint i, j; \ + for(i = 0;i < len;i++) \ + { \ + for(j = 0;j < numchans;j++) \ + *(dst++) = Conv_##T1##_##T2(*(src++)); \ + } \ +} + +#define DECL_TEMPLATE2(T) \ +DECL_TEMPLATE(T, ALbyte) \ +DECL_TEMPLATE(T, ALubyte) \ +DECL_TEMPLATE(T, ALshort) \ +DECL_TEMPLATE(T, ALushort) \ +DECL_TEMPLATE(T, ALint) \ +DECL_TEMPLATE(T, ALuint) \ +DECL_TEMPLATE(T, ALfloat) \ +DECL_TEMPLATE(T, ALdouble) \ +DECL_TEMPLATE(T, ALmulaw) \ +DECL_TEMPLATE(T, ALalaw) \ +DECL_TEMPLATE(T, ALbyte3) \ +DECL_TEMPLATE(T, ALubyte3) + +DECL_TEMPLATE2(ALbyte) +DECL_TEMPLATE2(ALubyte) +DECL_TEMPLATE2(ALshort) +DECL_TEMPLATE2(ALushort) +DECL_TEMPLATE2(ALint) +DECL_TEMPLATE2(ALuint) +DECL_TEMPLATE2(ALfloat) +DECL_TEMPLATE2(ALdouble) +DECL_TEMPLATE2(ALmulaw) +DECL_TEMPLATE2(ALalaw) +DECL_TEMPLATE2(ALbyte3) +DECL_TEMPLATE2(ALubyte3) + +#undef DECL_TEMPLATE2 +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \ + ALuint len, ALuint align) \ +{ \ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 0 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + DecodeIMA4Block(tmp, src, numchans, align); \ + src += byte_align; \ + \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ + } \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALshort_ALima4(ALshort *dst, const ALima4 *src, ALuint numchans, + ALuint len, ALuint align) +{ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; + ALuint i; + + assert(align > 0 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + DecodeIMA4Block(dst, src, numchans, align); + src += byte_align; + dst += align*numchans; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \ + ALuint len, ALuint align) \ +{ \ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 0 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ + } \ + EncodeIMA4Block(dst, tmp, sample, index, numchans, align); \ + dst += byte_align; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALima4_ALshort(ALima4 *dst, const ALshort *src, + ALuint numchans, ALuint len, ALuint align) +{ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALsizei byte_align = ((align-1)/2 + 4) * numchans; + ALuint i; + + assert(align > 0 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + EncodeIMA4Block(dst, src, sample, index, numchans, align); + src += align*numchans; + dst += byte_align; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(T) \ +static void Convert_##T##_ALmsadpcm(T *dst, const ALmsadpcm *src, \ + ALuint numchans, ALuint len, \ + ALuint align) \ +{ \ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 1 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + DecodeMSADPCMBlock(tmp, src, numchans, align); \ + src += byte_align; \ + \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ + } \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALmsadpcm *src, + ALuint numchans, ALuint len, + ALuint align) +{ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; + ALuint i; + + assert(align > 1 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + DecodeMSADPCMBlock(dst, src, numchans, align); + src += byte_align; + dst += align*numchans; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_ALmsadpcm_##T(ALmsadpcm *dst, const T *src, \ + ALuint numchans, ALuint len, ALuint align) \ +{ \ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 1 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ + } \ + EncodeMSADPCMBlock(dst, tmp, sample, numchans, align); \ + dst += byte_align; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALmsadpcm_ALshort(ALmsadpcm *dst, const ALshort *src, + ALuint numchans, ALuint len, ALuint align) +{ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALsizei byte_align = ((align-2)/2 + 7) * numchans; + ALuint i; + + assert(align > 1 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + EncodeMSADPCMBlock(dst, src, sample, numchans, align); + src += align*numchans; + dst += byte_align; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +/* NOTE: We don't store compressed samples internally, so these conversions + * should never happen. */ +static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected IMA4-to-IMA4 conversion!\n"); +} + +static void Convert_ALmsadpcm_ALmsadpcm(ALmsadpcm* UNUSED(dst), const ALmsadpcm* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected MSADPCM-to-MSADPCM conversion!\n"); +} + +static void Convert_ALmsadpcm_ALima4(ALmsadpcm* UNUSED(dst), const ALima4* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected IMA4-to-MSADPCM conversion!\n"); +} + +static void Convert_ALima4_ALmsadpcm(ALima4* UNUSED(dst), const ALmsadpcm* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected MSADPCM-to-IMA4 conversion!\n"); +} + + +#define DECL_TEMPLATE(T) \ +static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ + ALsizei numchans, ALsizei len, ALsizei align) \ +{ \ + switch(srcType) \ + { \ + case UserFmtByte: \ + Convert_##T##_ALbyte(dst, src, numchans, len, align); \ + break; \ + case UserFmtUByte: \ + Convert_##T##_ALubyte(dst, src, numchans, len, align); \ + break; \ + case UserFmtShort: \ + Convert_##T##_ALshort(dst, src, numchans, len, align); \ + break; \ + case UserFmtUShort: \ + Convert_##T##_ALushort(dst, src, numchans, len, align); \ + break; \ + case UserFmtInt: \ + Convert_##T##_ALint(dst, src, numchans, len, align); \ + break; \ + case UserFmtUInt: \ + Convert_##T##_ALuint(dst, src, numchans, len, align); \ + break; \ + case UserFmtFloat: \ + Convert_##T##_ALfloat(dst, src, numchans, len, align); \ + break; \ + case UserFmtDouble: \ + Convert_##T##_ALdouble(dst, src, numchans, len, align); \ + break; \ + case UserFmtMulaw: \ + Convert_##T##_ALmulaw(dst, src, numchans, len, align); \ + break; \ + case UserFmtAlaw: \ + Convert_##T##_ALalaw(dst, src, numchans, len, align); \ + break; \ + case UserFmtIMA4: \ + Convert_##T##_ALima4(dst, src, numchans, len, align); \ + break; \ + case UserFmtMSADPCM: \ + Convert_##T##_ALmsadpcm(dst, src, numchans, len, align); \ + break; \ + case UserFmtByte3: \ + Convert_##T##_ALbyte3(dst, src, numchans, len, align); \ + break; \ + case UserFmtUByte3: \ + Convert_##T##_ALubyte3(dst, src, numchans, len, align); \ + break; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALima4) +DECL_TEMPLATE(ALmsadpcm) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + + +void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align) +{ + switch(dstType) + { + case UserFmtByte: + Convert_ALbyte(dst, src, srcType, numchans, len, align); + break; + case UserFmtUByte: + Convert_ALubyte(dst, src, srcType, numchans, len, align); + break; + case UserFmtShort: + Convert_ALshort(dst, src, srcType, numchans, len, align); + break; + case UserFmtUShort: + Convert_ALushort(dst, src, srcType, numchans, len, align); + break; + case UserFmtInt: + Convert_ALint(dst, src, srcType, numchans, len, align); + break; + case UserFmtUInt: + Convert_ALuint(dst, src, srcType, numchans, len, align); + break; + case UserFmtFloat: + Convert_ALfloat(dst, src, srcType, numchans, len, align); + break; + case UserFmtDouble: + Convert_ALdouble(dst, src, srcType, numchans, len, align); + break; + case UserFmtMulaw: + Convert_ALmulaw(dst, src, srcType, numchans, len, align); + break; + case UserFmtAlaw: + Convert_ALalaw(dst, src, srcType, numchans, len, align); + break; + case UserFmtIMA4: + Convert_ALima4(dst, src, srcType, numchans, len, align); + break; + case UserFmtMSADPCM: + Convert_ALmsadpcm(dst, src, srcType, numchans, len, align); + break; + case UserFmtByte3: + Convert_ALbyte3(dst, src, srcType, numchans, len, align); + break; + case UserFmtUByte3: + Convert_ALubyte3(dst, src, srcType, numchans, len, align); + break; + } +} diff --git a/alsoftrc.sample b/alsoftrc.sample index 64590357..2e6dd92a 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -1,28 +1,40 @@ -# OpenAL config file. Options that are not under a block or are under the -# [general] block are for general, non-backend-specific options. Blocks may -# appear multiple times, and duplicated options will take the last value -# specified. +# OpenAL config file. +# +# Option blocks may appear multiple times, and duplicated options will take the +# last value specified. Environment variables may be specified within option +# values, and are automatically substituted when the config file is loaded. +# Environment variable names may only contain alpha-numeric characters (a-z, +# A-Z, 0-9) and underscores (_), and are prefixed with $. For example, +# specifying "$HOME/file.ext" would typically result in something like +# "/home/user/file.ext". To specify an actual "$" character, use "$$". +# # The system-wide settings can be put in /etc/openal/alsoft.conf and user- -# specific override settings in ~/.alsoftrc. -# For Windows, these settings should go into %AppData%\alsoft.ini - +# specific override settings in $HOME/.alsoftrc. +# For Windows, these settings should go into $AppData\alsoft.ini +# # Option and block names are case-insenstive. The supplied values are only # hints and may not be honored (though generally it'll try to get as close as # possible). Note: options that are left unset may default to app- or system- # specified values. These are the current available settings: +## +## General stuff +## +[general] + ## disable-cpu-exts: -# Disables use of the listed CPU extensions. Certain methods may utilize CPU -# extensions when detected, and this option is useful for preventing those -# extensions from being used. The available extensions are: sse, sse2, neon. -# Specifying 'all' disables use of all extensions. +# Disables use of specialized methods that use specific CPU intrinsics. +# Certain methods may utilize CPU extensions for improved performance, and +# this option is useful for preventing some or all of those methods from being +# used. The available extensions are: sse, sse2, sse4.1, and neon. Specifying +# 'all' disables use of all such specialized methods. #disable-cpu-exts = ## channels: # Sets the output channel configuration. If left unspecified, one will try to # be detected from the system, and defaulting to stereo. The available values # are: mono, stereo, quad, surround51, surround61, surround71 -#channels = stereo +#channels = ## sample-type: # Sets the output sample type. Currently, all mixing is done with 32-bit float @@ -45,15 +57,19 @@ ## hrtf_tables # Specifies a comma-separated list of files containing HRTF data sets. The -# listed data sets can be used in place of or in addiiton to the the built-in -# set. The format of the files are described in hrtf.txt. The filenames may -# contain these markers, which will be replaced as needed: +# format of the files are described in hrtf.txt. The filenames may contain +# these markers, which will be replaced as needed: # %r - Device sampling rate # %% - Percent sign (%) -# So if this is set to "kemar-%r-diffuse.mhr", it will try to open -# "kemar-44100-diffuse.mhr" if the device is using 44100hz output, or -# "kemar-48000-diffuse.mhr" if the device is using 48000hz output, etc. -#hrtf_tables = +# The listed files are relative to system-dependant data directories. On +# Windows this is: +# $AppData\openal\hrtf +# And on other systems, it's (in order): +# $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf) +# $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and +# /usr/share/openal/hrtf) +# An absolute path may also be specified, if the given file is elsewhere. +#hrtf_tables = default-%r.mhr ## cf_level: # Sets the crossfeed level for stereo output. Valid values are: @@ -77,8 +93,9 @@ #wide-stereo = false ## frequency: -# Sets the output frequency. -#frequency = 44100 +# Sets the output frequency. If left unspecified it will try to detect a +# default from the system, otherwise it will default to 44100. +#frequency = ## resampler: # Selects the resampler used when mixing sources. Valid values are: @@ -117,11 +134,11 @@ ## drivers: # Sets the backend driver list order, comma-seperated. Unknown backends and # duplicated names are ignored. Unlisted backends won't be considered for use -# unless the list is ended with a comma (eg. 'oss,' will list OSS first -# followed by all other available backends, while 'oss' will list OSS only). -# Backends prepended with - won't be available for use (eg. '-oss,' will allow -# all available backends except OSS). An empty list means the default. -#drivers = pulse,alsa,core,oss,solaris,sndio,qsa,mmdevapi,dsound,winmm,port,opensl,null,wave +# unless the list is ended with a comma (e.g. 'oss,' will try OSS first before +# other backends, while 'oss' will try OSS only). Backends prepended with - +# won't be considered for use (e.g. '-oss,' will try all available backends +# except OSS). An empty list means to try all backends. +#drivers = ## excludefx: # Sets which effects to exclude, preventing apps from using them. This can @@ -190,7 +207,13 @@ ## soundfont: # A default soundfont (sf2 format). Used when an app requests the system -# default. +# default. The listed file is relative to system-dependant data directories. +# On Windows this is: +# $AppData\openal\soundfonts +# And on other systems, it's (in order): +# $XDG_DATA_HOME/openal/soundfonts +# $XDG_DATA_DIRS/openal/soundfonts +# An absolute path may also be specified, if the given file is elsewhere. #soundfont = ## volume: diff --git a/cmake/FindALSA.cmake b/cmake/FindALSA.cmake new file mode 100644 index 00000000..519304d6 --- /dev/null +++ b/cmake/FindALSA.cmake @@ -0,0 +1,73 @@ +# - Find alsa +# Find the alsa libraries (asound) +# +# This module defines the following variables: +# ALSA_FOUND - True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found +# ALSA_LIBRARIES - Set when ALSA_LIBRARY is found +# ALSA_INCLUDE_DIRS - Set when ALSA_INCLUDE_DIR is found +# +# ALSA_INCLUDE_DIR - where to find asoundlib.h, etc. +# ALSA_LIBRARY - the asound library +# ALSA_VERSION_STRING - the version of alsa found (since CMake 2.8.8) +# + +#============================================================================= +# Copyright 2009-2011 Kitware, Inc. +# Copyright 2009-2011 Philip Lowman <[email protected]> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * The names of Kitware, Inc., the Insight Consortium, or the names of +# any consortium members, or of any contributors, may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h + DOC "The ALSA (asound) include directory" +) + +find_library(ALSA_LIBRARY NAMES asound + DOC "The ALSA (asound) library" +) + +if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h") + file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"") + + string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}") + unset(alsa_version_str) +endif() + +# handle the QUIETLY and REQUIRED arguments and set ALSA_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ALSA + REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR + VERSION_VAR ALSA_VERSION_STRING) + +if(ALSA_FOUND) + set( ALSA_LIBRARIES ${ALSA_LIBRARY} ) + set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} ) +endif() + +mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY) diff --git a/cmake/FindAudioIO.cmake b/cmake/FindAudioIO.cmake new file mode 100644 index 00000000..f0f8b2a5 --- /dev/null +++ b/cmake/FindAudioIO.cmake @@ -0,0 +1,21 @@ +# - Find AudioIO includes and libraries +# +# AUDIOIO_FOUND - True if AUDIOIO_INCLUDE_DIR is found +# AUDIOIO_INCLUDE_DIRS - Set when AUDIOIO_INCLUDE_DIR is found +# +# AUDIOIO_INCLUDE_DIR - where to find sys/audioio.h, etc. +# + +find_path(AUDIOIO_INCLUDE_DIR + NAMES sys/audioio.h + DOC "The AudioIO include directory" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AudioIO REQUIRED_VARS AUDIOIO_INCLUDE_DIR) + +if(AUDIOIO_FOUND) + set(AUDIOIO_INCLUDE_DIRS ${AUDIOIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(AUDIOIO_INCLUDE_DIR) diff --git a/cmake/FindDSound.cmake b/cmake/FindDSound.cmake new file mode 100644 index 00000000..36cdf4b5 --- /dev/null +++ b/cmake/FindDSound.cmake @@ -0,0 +1,33 @@ +# - Find DirectSound includes and libraries +# +# DSOUND_FOUND - True if DSOUND_INCLUDE_DIR & DSOUND_LIBRARY are found +# DSOUND_LIBRARIES - Set when DSOUND_LIBRARY is found +# DSOUND_INCLUDE_DIRS - Set when DSOUND_INCLUDE_DIR is found +# +# DSOUND_INCLUDE_DIR - where to find dsound.h, etc. +# DSOUND_LIBRARY - the dsound library +# + +find_path(DSOUND_INCLUDE_DIR + PATHS "${DXSDK_DIR}/include" + NAMES dsound.h + DOC "The DirectSound include directory" +) + +find_library(DSOUND_LIBRARY + PATHS "${DXSDK_DIR}/lib" + NAMES dsound + DOC "The DirectSound library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DSound + REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR +) + +if(DSOUND_FOUND) + set(DSOUND_LIBRARIES ${DSOUND_LIBRARY}) + set(DSOUND_INCLUDE_DIRS ${DSOUND_INCLUDE_DIR}) +endif() + +mark_as_advanced(DSOUND_INCLUDE_DIR DSOUND_LIBRARY) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 00000000..96cbb6ed --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,173 @@ +# vim: ts=2 sw=2 +# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) +# +# Once done this will define +# FFMPEG_FOUND - System has the all required components. +# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. +# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. +# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. +# +# For each of the components it will additionaly set. +# - AVCODEC +# - AVDEVICE +# - AVFORMAT +# - AVUTIL +# - POSTPROC +# - SWSCALE +# - SWRESAMPLE +# the following variables will be defined +# <component>_FOUND - System has <component> +# <component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers +# <component>_LIBRARIES - Link these to use <component> +# <component>_DEFINITIONS - Compiler switches required for using <component> +# <component>_VERSION - The components version +# +# Copyright (c) 2006, Matthias Kretz, <[email protected]> +# Copyright (c) 2008, Alexander Neundorf, <[email protected]> +# Copyright (c) 2011, Michael Jansen, <[email protected]> +# +# Redistribution and use is allowed according to the terms of the BSD license. + +include(FindPackageHandleStandardArgs) + +if(NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVFORMAT AVCODEC AVUTIL) +endif() + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component) + if(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) + # message(STATUS " - ${_component} found.") + set(${_component}_FOUND TRUE) + else() + # message(STATUS " - ${_component} not found.") + endif() +endmacro() + +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + if(NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + endif() + endif() + + find_path(${_component}_INCLUDE_DIRS ${_header} + HINTS + ${FFMPEGSDK_INC} + ${PC_LIB${_component}_INCLUDEDIR} + ${PC_LIB${_component}_INCLUDE_DIRS} + PATH_SUFFIXES + ffmpeg + ) + + find_library(${_component}_LIBRARIES NAMES ${_library} + HINTS + ${FFMPEGSDK_LIB} + ${PC_LIB${_component}_LIBDIR} + ${PC_LIB${_component}_LIBRARY_DIRS} + ) + + STRING(REGEX REPLACE "/.*" "/version.h" _ver_header ${_header}) + if(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_ver_header}" version_str REGEX "^#define[\t ]+LIB${_component}_VERSION_M.*") + + foreach(_str "${version_str}") + if(NOT version_maj) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MAJOR[\t ]+([0-9]*).*$" "\\1" version_maj "${_str}") + endif() + if(NOT version_min) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MINOR[\t ]+([0-9]*).*$" "\\1" version_min "${_str}") + endif() + if(NOT version_mic) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MICRO[\t ]+([0-9]*).*$" "\\1" version_mic "${_str}") + endif() + endforeach() + unset(version_str) + + set(${_component}_VERSION "${version_maj}.${version_min}.${version_mic}" CACHE STRING "The ${_component} version number.") + unset(version_maj) + unset(version_min) + unset(version_mic) + endif(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + + set_component_found(${_component}) + + mark_as_advanced( + ${_component}_INCLUDE_DIRS + ${_component}_LIBRARIES + ${_component}_DEFINITIONS + ${_component}_VERSION) +endmacro() + + +set(FFMPEGSDK $ENV{FFMPEG_HOME}) +if(FFMPEGSDK) + set(FFMPEGSDK_INC "${FFMPEGSDK}/include") + set(FFMPEGSDK_LIB "${FFMPEGSDK}/lib") +endif() + +# Check for all possible components. +find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) +find_component(AVFORMAT libavformat avformat libavformat/avformat.h) +find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) +find_component(AVUTIL libavutil avutil libavutil/avutil.h) +find_component(SWSCALE libswscale swscale libswscale/swscale.h) +find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h) +find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) + +# Check if the required components were found and add their stuff to the FFMPEG_* vars. +foreach(_component ${FFmpeg_FIND_COMPONENTS}) + if(${_component}_FOUND) + # message(STATUS "Required component ${_component} present.") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) + else() + # message(STATUS "Required component ${_component} missing.") + endif() +endforeach() + +# Build the include path and library list with duplicates removed. +if(FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) +endif() + +if(FFMPEG_LIBRARIES) + list(REMOVE_DUPLICATES FFMPEG_LIBRARIES) +endif() + +# cache the vars. +set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) +set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) +set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) + +mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS) + +# Now set the noncached _FOUND vars for the components. +foreach(_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE) + set_component_found(${_component}) +endforeach () + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach(_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) +endforeach() + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) diff --git a/cmake/FindFluidSynth.cmake b/cmake/FindFluidSynth.cmake index 7d5cb6a8..fe96b225 100644 --- a/cmake/FindFluidSynth.cmake +++ b/cmake/FindFluidSynth.cmake @@ -6,11 +6,6 @@ # FLUIDSYNTH_FOUND - True if fluidsynth found. -IF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) - # Already in cache, be silent - SET(FluidSynth_FIND_QUIETLY TRUE) -ENDIF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) - FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth ) @@ -19,5 +14,6 @@ MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR ) # handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth + REQUIRED_VARS FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) diff --git a/cmake/FindOSS.cmake b/cmake/FindOSS.cmake new file mode 100644 index 00000000..88ee66ad --- /dev/null +++ b/cmake/FindOSS.cmake @@ -0,0 +1,21 @@ +# - Find OSS includes +# +# OSS_FOUND - True if OSS_INCLUDE_DIR is found +# OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found +# +# OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc. +# + +find_path(OSS_INCLUDE_DIR + NAMES sys/soundcard.h + DOC "The OSS include directory" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OSS REQUIRED_VARS OSS_INCLUDE_DIR) + +if(OSS_FOUND) + set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR}) +endif() + +mark_as_advanced(OSS_INCLUDE_DIR) diff --git a/cmake/FindPortAudio.cmake b/cmake/FindPortAudio.cmake new file mode 100644 index 00000000..fad2313d --- /dev/null +++ b/cmake/FindPortAudio.cmake @@ -0,0 +1,32 @@ +# - Find PortAudio includes and libraries +# +# PORTAUDIO_FOUND - True if PORTAUDIO_INCLUDE_DIR & PORTAUDIO_LIBRARY +# are found +# PORTAUDIO_LIBRARIES - Set when PORTAUDIO_LIBRARY is found +# PORTAUDIO_INCLUDE_DIRS - Set when PORTAUDIO_INCLUDE_DIR is found +# +# PORTAUDIO_INCLUDE_DIR - where to find portaudio.h, etc. +# PORTAUDIO_LIBRARY - the portaudio library +# + +find_path(PORTAUDIO_INCLUDE_DIR + NAMES portaudio.h + DOC "The PortAudio include directory" +) + +find_library(PORTAUDIO_LIBRARY + NAMES portaudio + DOC "The PortAudio library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PortAudio + REQUIRED_VARS PORTAUDIO_LIBRARY PORTAUDIO_INCLUDE_DIR +) + +if(PORTAUDIO_FOUND) + set(PORTAUDIO_LIBRARIES ${PORTAUDIO_LIBRARY}) + set(PORTAUDIO_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(PORTAUDIO_INCLUDE_DIR PORTAUDIO_LIBRARY) diff --git a/cmake/FindPulseAudio.cmake b/cmake/FindPulseAudio.cmake new file mode 100644 index 00000000..1f6f843a --- /dev/null +++ b/cmake/FindPulseAudio.cmake @@ -0,0 +1,43 @@ +# - Find PulseAudio includes and libraries +# +# PULSEAUDIO_FOUND - True if PULSEAUDIO_INCLUDE_DIR & +# PULSEAUDIO_LIBRARY are found +# PULSEAUDIO_LIBRARIES - Set when PULSEAUDIO_LIBRARY is found +# PULSEAUDIO_INCLUDE_DIRS - Set when PULSEAUDIO_INCLUDE_DIR is found +# +# PULSEAUDIO_INCLUDE_DIR - where to find pulse/pulseaudio.h, etc. +# PULSEAUDIO_LIBRARY - the pulse library +# PULSEAUDIO_VERSION_STRING - the version of PulseAudio found +# + +find_path(PULSEAUDIO_INCLUDE_DIR + NAMES pulse/pulseaudio.h + DOC "The PulseAudio include directory" +) + +find_library(PULSEAUDIO_LIBRARY + NAMES pulse + DOC "The PulseAudio library" +) + +if(PULSEAUDIO_INCLUDE_DIR AND EXISTS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h") + file(STRINGS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h" pulse_version_str + REGEX "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\)") + + string(REGEX REPLACE "^.*pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]*)\"\\).*$" "\\1" + PULSEAUDIO_VERSION_STRING "${pulse_version_str}") + unset(pulse_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PulseAudio + REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR + VERSION_VAR PULSEAUDIO_VERSION_STRING +) + +if(PULSEAUDIO_FOUND) + set(PULSEAUDIO_LIBRARIES ${PULSEAUDIO_LIBRARY}) + set(PULSEAUDIO_INCLUDE_DIRS ${PULSEAUDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY) diff --git a/cmake/FindQSA.cmake b/cmake/FindQSA.cmake new file mode 100644 index 00000000..0ad1fd43 --- /dev/null +++ b/cmake/FindQSA.cmake @@ -0,0 +1,34 @@ +# - Find QSA includes and libraries +# +# QSA_FOUND - True if QSA_INCLUDE_DIR & QSA_LIBRARY are found +# QSA_LIBRARIES - Set when QSA_LIBRARY is found +# QSA_INCLUDE_DIRS - Set when QSA_INCLUDE_DIR is found +# +# QSA_INCLUDE_DIR - where to find sys/asoundlib.h, etc. +# QSA_LIBRARY - the asound library +# + +# Only check for QSA on QNX, because it conflicts with ALSA. +if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") + find_path(QSA_INCLUDE_DIR + NAMES sys/asoundlib.h + DOC "The QSA include directory" + ) + + find_library(QSA_LIBRARY + NAMES asound + DOC "The QSA library" + ) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QSA + REQUIRED_VARS QSA_LIBRARY QSA_INCLUDE_DIR +) + +if(QSA_FOUND) + set(QSA_LIBRARIES ${QSA_LIBRARY}) + set(QSA_INCLUDE_DIRS ${QSA_INCLUDE_DIR}) +endif() + +mark_as_advanced(QSA_INCLUDE_DIR QSA_LIBRARY) diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 00000000..70e607a8 --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,193 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + IF(WIN32) + SET(SDL2_LIBRARY_TEMP winmm imm32 version msimg32 ${SDL2_LIBRARY_TEMP}) + ENDIF(WIN32) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + +IF(SDL2_STATIC) + if (UNIX AND NOT APPLE) + EXECUTE_PROCESS(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_LINK_FLAGS) + STRING(REGEX REPLACE "(\r?\n)+$" "" SDL2_LINK_FLAGS "${SDL2_LINK_FLAGS}") + SET(SDL2_LIBRARY ${SDL2_LINK_FLAGS}) + ENDIF() +ENDIF(SDL2_STATIC) diff --git a/cmake/FindSDL_sound.cmake b/cmake/FindSDL_sound.cmake index 97d2a84c..2dab1a1c 100644 --- a/cmake/FindSDL_sound.cmake +++ b/cmake/FindSDL_sound.cmake @@ -1,7 +1,7 @@ # - Locates the SDL_sound library # # This module depends on SDL being found and -# must be called AFTER FindSDL.cmake is called. +# must be called AFTER FindSDL.cmake or FindSDL2.cmake is called. # # This module defines # SDL_SOUND_INCLUDE_DIR, where to find SDL_sound.h @@ -52,8 +52,8 @@ # # On OSX, this will prefer the Framework version (if found) over others. # People will have to manually change the cache values of -# SDL_LIBRARY to override this selectionor set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. +# SDL_LIBRARY or SDL2_LIBRARY to override this selection or set the CMake +# environment CMAKE_INCLUDE_PATH to modify the search paths. #============================================================================= # Copyright 2005-2009 Kitware, Inc. @@ -74,289 +74,288 @@ mark_as_advanced(SDL_SOUND_EXTRAS) # Find SDL_sound.h find_path(SDL_SOUND_INCLUDE_DIR SDL_sound.h - HINTS - ENV SDLSOUNDDIR - ENV SDLDIR - PATH_SUFFIXES SDL SDL12 SDL11 - ) + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR + PATH_SUFFIXES SDL SDL12 SDL11 +) find_library(SDL_SOUND_LIBRARY - NAMES SDL_sound - HINTS - ENV SDLSOUNDDIR - ENV SDLDIR - ) - -if(SDL_FOUND AND SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY) - - # CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS - # for the :STRING syntax if I have multiple values contained in a - # single variable. This is a problem for the SDL_LIBRARY variable - # because it does just that. When I feed this variable to the command, - # only the first value gets the appropriate modifier (e.g. -I) and - # the rest get dropped. - # To get multiple single variables to work, I must separate them with a "\;" - # I could go back and modify the FindSDL.cmake module, but that's kind of painful. - # The solution would be to try something like: - # set(SDL_TRY_COMPILE_LIBRARY_LIST "${SDL_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}") - # Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt - # with a temporary test project and invoke that with TRY_COMPILE. - # See message thread "Figuring out dependencies for a library in order to build" - # 2005-07-16 - # try_compile( - # MY_RESULT - # ${CMAKE_BINARY_DIR} - # ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c - # CMAKE_FLAGS - # -DINCLUDE_DIRECTORIES:STRING=${SDL_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR} - # -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL_LIBRARY} - # OUTPUT_VARIABLE MY_OUTPUT - # ) - - # To minimize external dependencies, create a sdlsound test program - # which will be used to figure out if additional link dependencies are - # required for the link phase. - file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c - "#include \"SDL_sound.h\" - #include \"SDL.h\" - int main(int argc, char* argv[]) - { - Sound_AudioInfo desired; - Sound_Sample* sample; - - SDL_Init(0); - Sound_Init(); - - /* This doesn't actually have to work, but Init() is a no-op - * for some of the decoders, so this should force more symbols - * to be pulled in. - */ - sample = Sound_NewSampleFromFile(argv[1], &desired, 4096); - - Sound_Quit(); - SDL_Quit(); - return 0; - }" - ) - - # Calling - # target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) - # causes problems when SDL_LIBRARY looks like - # /Library/Frameworks/SDL.framework;-framework Cocoa - # The ;-framework Cocoa seems to be confusing CMake once the OS X - # framework support was added. I was told that breaking up the list - # would fix the problem. - set(TMP_TRY_LIBS) - foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) - set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") - endforeach() - - # message("TMP_TRY_LIBS ${TMP_TRY_LIBS}") - - # Write the CMakeLists.txt and test project - # Weird, this is still sketchy. If I don't quote the variables - # in the TARGET_LINK_LIBRARIES, I seem to loose everything - # in the SDL_LIBRARY string after the "-framework". - # But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work. - file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt - "cmake_minimum_required(VERSION 2.8) - project(DetermineSoundLibs C) - include_directories(${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - add_executable(DetermineSoundLibs DetermineSoundLibs.c) - target_link_libraries(DetermineSoundLibs ${TMP_TRY_LIBS})" - ) - - try_compile( - MY_RESULT - ${PROJECT_BINARY_DIR}/CMakeTmp - ${PROJECT_BINARY_DIR}/CMakeTmp - DetermineSoundLibs - OUTPUT_VARIABLE MY_OUTPUT - ) - - # message("${MY_RESULT}") - # message(${MY_OUTPUT}) - - if(NOT MY_RESULT) - - # I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically. - # I think Timidity is also compiled in statically. - # I've never had to explcitly link against Quicktime, so I'll skip that for now. - - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY}) - - # Find MikMod - if("${MY_OUTPUT}" MATCHES "MikMod_") - find_library(MIKMOD_LIBRARY - NAMES libmikmod-coreaudio mikmod - PATHS - ENV MIKMODDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(MIKMOD_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY}) - endif(MIKMOD_LIBRARY) - endif("${MY_OUTPUT}" MATCHES "MikMod_") - - # Find ModPlug - if("${MY_OUTPUT}" MATCHES "MODPLUG_") - find_library(MODPLUG_LIBRARY - NAMES modplug - PATHS - ENV MODPLUGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(MODPLUG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY}) - endif() - endif() - - - # Find Ogg and Vorbis - if("${MY_OUTPUT}" MATCHES "ov_") - find_library(VORBIS_LIBRARY - NAMES vorbis Vorbis VORBIS - PATHS - ENV VORBISDIR - ENV OGGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(VORBIS_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY}) - endif() - - find_library(OGG_LIBRARY - NAMES ogg Ogg OGG - PATHS - ENV OGGDIR - ENV VORBISDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(OGG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) - endif() - endif() - - - # Find SMPEG - if("${MY_OUTPUT}" MATCHES "SMPEG_") - find_library(SMPEG_LIBRARY - NAMES smpeg SMPEG Smpeg SMpeg - PATHS - ENV SMPEGDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(SMPEG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY}) - endif() - endif() - - - # Find FLAC - if("${MY_OUTPUT}" MATCHES "FLAC_") - find_library(FLAC_LIBRARY - NAMES flac FLAC - PATHS - ENV FLACDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(FLAC_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY}) - endif() - endif() - - - # Hmmm...Speex seems to depend on Ogg. This might be a problem if - # the TRY_COMPILE attempt gets blocked at SPEEX before it can pull - # in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff - # above for here or if two ogg entries will screw up things. - if("${MY_OUTPUT}" MATCHES "speex_") - find_library(SPEEX_LIBRARY - NAMES speex SPEEX - PATHS - ENV SPEEXDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES - lib - ) - if(SPEEX_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY}) - endif() - - # Find OGG (needed for Speex) - # We might have already found Ogg for Vorbis, so skip it if so. - if(NOT OGG_LIBRARY) - find_library(OGG_LIBRARY - NAMES ogg Ogg OGG - PATHS - ENV OGGDIR - ENV VORBISDIR - ENV SPEEXDIR - ENV SDLSOUNDDIR - ENV SDLDIR - /sw - /opt/local - /opt/csw - /opt - PATH_SUFFIXES lib - ) - if(OGG_LIBRARY) - set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) - endif() - endif() - endif() - - set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries") - else() - set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARY} CACHE INTERNAL "SDL_sound and dependent libraries") - endif() + NAMES SDL_sound + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR +) + +if(SDL2_FOUND OR SDL_FOUND) + if(SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY) + # CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS + # for the :STRING syntax if I have multiple values contained in a + # single variable. This is a problem for the SDL2_LIBRARY variable + # because it does just that. When I feed this variable to the command, + # only the first value gets the appropriate modifier (e.g. -I) and + # the rest get dropped. + # To get multiple single variables to work, I must separate them with a "\;" + # I could go back and modify the FindSDL2.cmake module, but that's kind of painful. + # The solution would be to try something like: + # set(SDL2_TRY_COMPILE_LIBRARY_LIST "${SDL2_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}") + # Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt + # with a temporary test project and invoke that with TRY_COMPILE. + # See message thread "Figuring out dependencies for a library in order to build" + # 2005-07-16 + # try_compile( + # MY_RESULT + # ${CMAKE_BINARY_DIR} + # ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c + # CMAKE_FLAGS + # -DINCLUDE_DIRECTORIES:STRING=${SDL2_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR} + # -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL2_LIBRARY} + # OUTPUT_VARIABLE MY_OUTPUT + # ) + + # To minimize external dependencies, create a sdlsound test program + # which will be used to figure out if additional link dependencies are + # required for the link phase. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c + "#include \"SDL_sound.h\" + #include \"SDL.h\" + int main(int argc, char* argv[]) + { + Sound_AudioInfo desired; + Sound_Sample* sample; + + SDL_Init(0); + Sound_Init(); + + /* This doesn't actually have to work, but Init() is a no-op + * for some of the decoders, so this should force more symbols + * to be pulled in. + */ + sample = Sound_NewSampleFromFile(argv[1], &desired, 4096); + + Sound_Quit(); + SDL_Quit(); + return 0; + }" + ) + + # Calling + # target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) + # causes problems when SDL2_LIBRARY looks like + # /Library/Frameworks/SDL2.framework;-framework Cocoa + # The ;-framework Cocoa seems to be confusing CMake once the OS X + # framework support was added. I was told that breaking up the list + # would fix the problem. + set(TMP_TRY_LIBS) + if(SDL2_FOUND) + foreach(lib ${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) + set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") + endforeach() + set(TMP_INCLUDE_DIRS ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + else() + foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) + set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") + endforeach() + set(TMP_INCLUDE_DIRS ${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + endif() + + # message("TMP_TRY_LIBS ${TMP_TRY_LIBS}") + + # Write the CMakeLists.txt and test project + # Weird, this is still sketchy. If I don't quote the variables + # in the TARGET_LINK_LIBRARIES, I seem to loose everything + # in the SDL2_LIBRARY string after the "-framework". + # But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt + "cmake_minimum_required(VERSION 2.8) + project(DetermineSoundLibs C) + include_directories(${TMP_INCLUDE_DIRS}) + add_executable(DetermineSoundLibs DetermineSoundLibs.c) + target_link_libraries(DetermineSoundLibs ${TMP_TRY_LIBS})" + ) + unset(TMP_INCLUDE_DIRS) + unset(TMP_TRY_LIBS) + + try_compile( + MY_RESULT + ${PROJECT_BINARY_DIR}/CMakeTmp + ${PROJECT_BINARY_DIR}/CMakeTmp + DetermineSoundLibs + OUTPUT_VARIABLE MY_OUTPUT + ) + # message("${MY_RESULT}") + # message(${MY_OUTPUT}) + + if(NOT MY_RESULT) + # I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically. + # I think Timidity is also compiled in statically. + # I've never had to explcitly link against Quicktime, so I'll skip that for now. + + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY}) + + # Find MikMod + if("${MY_OUTPUT}" MATCHES "MikMod_") + find_library(MIKMOD_LIBRARY + NAMES libmikmod-coreaudio mikmod + PATHS + ENV MIKMODDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(MIKMOD_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY}) + endif(MIKMOD_LIBRARY) + endif("${MY_OUTPUT}" MATCHES "MikMod_") + + # Find ModPlug + if("${MY_OUTPUT}" MATCHES "MODPLUG_") + find_library(MODPLUG_LIBRARY + NAMES modplug + PATHS + ENV MODPLUGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(MODPLUG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY}) + endif() + endif() + + # Find Ogg and Vorbis + if("${MY_OUTPUT}" MATCHES "ov_") + find_library(VORBIS_LIBRARY + NAMES vorbis Vorbis VORBIS + PATHS + ENV VORBISDIR + ENV OGGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(VORBIS_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY}) + endif() + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + + # Find SMPEG + if("${MY_OUTPUT}" MATCHES "SMPEG_") + find_library(SMPEG_LIBRARY + NAMES smpeg SMPEG Smpeg SMpeg + PATHS + ENV SMPEGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(SMPEG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY}) + endif() + endif() + + + # Find FLAC + if("${MY_OUTPUT}" MATCHES "FLAC_") + find_library(FLAC_LIBRARY + NAMES flac FLAC + PATHS + ENV FLACDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(FLAC_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY}) + endif() + endif() + + + # Hmmm...Speex seems to depend on Ogg. This might be a problem if + # the TRY_COMPILE attempt gets blocked at SPEEX before it can pull + # in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff + # above for here or if two ogg entries will screw up things. + if("${MY_OUTPUT}" MATCHES "speex_") + find_library(SPEEX_LIBRARY + NAMES speex SPEEX + PATHS + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(SPEEX_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY}) + endif() + + # Find OGG (needed for Speex) + # We might have already found Ogg for Vorbis, so skip it if so. + if(NOT OGG_LIBRARY) + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + endif() + + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries") + else() + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARY} CACHE INTERNAL "SDL_sound and dependent libraries") + endif() + endif() endif() if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h") @@ -376,7 +375,6 @@ if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h") endif() include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound - REQUIRED_VARS SDL_SOUND_LIBRARY SDL_SOUND_INCLUDE_DIR + REQUIRED_VARS SDL_SOUND_LIBRARIES SDL_SOUND_INCLUDE_DIR VERSION_VAR SDL_SOUND_VERSION_STRING) diff --git a/cmake/FindSoundIO.cmake b/cmake/FindSoundIO.cmake new file mode 100644 index 00000000..10450254 --- /dev/null +++ b/cmake/FindSoundIO.cmake @@ -0,0 +1,32 @@ +# - Find SoundIO (sndio) includes and libraries +# +# SOUNDIO_FOUND - True if SOUNDIO_INCLUDE_DIR & SOUNDIO_LIBRARY are +# found +# SOUNDIO_LIBRARIES - Set when SOUNDIO_LIBRARY is found +# SOUNDIO_INCLUDE_DIRS - Set when SOUNDIO_INCLUDE_DIR is found +# +# SOUNDIO_INCLUDE_DIR - where to find sndio.h, etc. +# SOUNDIO_LIBRARY - the sndio library +# + +find_path(SOUNDIO_INCLUDE_DIR + NAMES sndio.h + DOC "The SoundIO include directory" +) + +find_library(SOUNDIO_LIBRARY + NAMES sndio + DOC "The SoundIO library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SoundIO + REQUIRED_VARS SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR +) + +if(SOUNDIO_FOUND) + set(SOUNDIO_LIBRARIES ${SOUNDIO_LIBRARY}) + set(SOUNDIO_INCLUDE_DIRS ${SOUNDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) diff --git a/common/atomic.c b/common/atomic.c new file mode 100644 index 00000000..634587e4 --- /dev/null +++ b/common/atomic.c @@ -0,0 +1,16 @@ + +#include "config.h" + +#include "atomic.h" + + +extern inline void InitRef(volatile RefCount *ptr, uint value); +extern inline uint ReadRef(volatile RefCount *ptr); +extern inline uint IncrementRef(volatile RefCount *ptr); +extern inline uint DecrementRef(volatile RefCount *ptr); +extern inline uint ExchangeRef(volatile RefCount *ptr, uint newval); +extern inline uint CompExchangeRef(volatile RefCount *ptr, uint oldval, uint newval); +extern inline int ExchangeInt(volatile int *ptr, int newval); +extern inline void *ExchangePtr(XchgPtr *ptr, void *newval); +extern inline int CompExchangeInt(volatile int *ptr, int oldval, int newval); +extern inline void *CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval); diff --git a/common/rwlock.c b/common/rwlock.c new file mode 100644 index 00000000..ff7aa418 --- /dev/null +++ b/common/rwlock.c @@ -0,0 +1,62 @@ + +#include "config.h" + +#include "rwlock.h" + +#include "bool.h" +#include "atomic.h" +#include "threads.h" + + +/* A simple spinlock. Yield the thread while the given integer is set by + * another. Could probably be improved... */ +static void Lock(volatile int *l) +{ + while(ExchangeInt(l, true) == true) + althrd_yield(); +} + +static void Unlock(volatile int *l) +{ + ExchangeInt(l, false); +} + + +void RWLockInit(RWLock *lock) +{ + InitRef(&lock->read_count, 0); + InitRef(&lock->write_count, 0); + lock->read_lock = false; + lock->read_entry_lock = false; + lock->write_lock = false; +} + +void ReadLock(RWLock *lock) +{ + Lock(&lock->read_entry_lock); + Lock(&lock->read_lock); + if(IncrementRef(&lock->read_count) == 1) + Lock(&lock->write_lock); + Unlock(&lock->read_lock); + Unlock(&lock->read_entry_lock); +} + +void ReadUnlock(RWLock *lock) +{ + if(DecrementRef(&lock->read_count) == 0) + Unlock(&lock->write_lock); +} + +void WriteLock(RWLock *lock) +{ + if(IncrementRef(&lock->write_count) == 1) + Lock(&lock->read_lock); + Lock(&lock->write_lock); +} + +void WriteUnlock(RWLock *lock) +{ + Unlock(&lock->write_lock); + if(DecrementRef(&lock->write_count) == 0) + Unlock(&lock->read_lock); +} diff --git a/common/threads.c b/common/threads.c new file mode 100644 index 00000000..cd9f6755 --- /dev/null +++ b/common/threads.c @@ -0,0 +1,744 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "threads.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "uintmap.h" + + +extern inline althrd_t althrd_current(void); +extern inline int althrd_equal(althrd_t thr0, althrd_t thr1); +extern inline void althrd_exit(int res); +extern inline void althrd_yield(void); + +extern inline int almtx_lock(almtx_t *mtx); +extern inline int almtx_unlock(almtx_t *mtx); +extern inline int almtx_trylock(almtx_t *mtx); + +extern inline void *altss_get(altss_t tss_id); +extern inline int altss_set(altss_t tss_id, void *val); + + +#ifndef UNUSED +#if defined(__cplusplus) +#define UNUSED(x) +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif +#endif + + +#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */ + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mmsystem.h> + + +void althrd_setname(althrd_t thr, const char *name) +{ +#if defined(_MSC_VER) +#define MS_VC_EXCEPTION 0x406D1388 +#pragma pack(push,8) + struct { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } info; +#pragma pack(pop) + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = thr; + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#undef MS_VC_EXCEPTION +#else + (void)thr; + (void)name; +#endif +} + + +static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE; + +static void NTAPI althrd_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +{ + if(reason == DLL_PROCESS_DETACH) + ResetUIntMap(&ThrdIdHandle); +} +#ifdef _MSC_VER +#pragma section(".CRT$XLC",read) +__declspec(allocate(".CRT$XLC")) PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; +#elif defined(__GNUC__) +PIMAGE_TLS_CALLBACK althrd_callback_ __attribute__((section(".CRT$XLC"))) = althrd_callback; +#else +PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; +#endif + + +typedef struct thread_cntr { + althrd_start_t func; + void *arg; +} thread_cntr; + +static DWORD WINAPI althrd_starter(void *arg) +{ + thread_cntr cntr; + memcpy(&cntr, arg, sizeof(cntr)); + free(arg); + + return (DWORD)((*cntr.func)(cntr.arg)); +} + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) +{ + thread_cntr *cntr; + DWORD thrid; + HANDLE hdl; + + cntr = malloc(sizeof(*cntr)); + if(!cntr) return althrd_nomem; + + cntr->func = func; + cntr->arg = arg; + + hdl = CreateThread(NULL, THREAD_STACK_SIZE, althrd_starter, cntr, 0, &thrid); + if(!hdl) + { + free(cntr); + return althrd_error; + } + InsertUIntMapEntry(&ThrdIdHandle, thrid, hdl); + + *thr = thrid; + return althrd_success; +} + +int althrd_detach(althrd_t thr) +{ + HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr); + if(!hdl) return althrd_error; + + CloseHandle(hdl); + return althrd_success; +} + +int althrd_join(althrd_t thr, int *res) +{ + DWORD code; + + HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr); + if(!hdl) return althrd_error; + + WaitForSingleObject(hdl, INFINITE); + GetExitCodeThread(hdl, &code); + CloseHandle(hdl); + + if(res != NULL) + *res = (int)code; + return althrd_success; +} + +int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem)) +{ + DWORD msec; + + if(ts->tv_sec < 0 || ts->tv_sec >= (0x7fffffff / 1000) || + ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) + return -2; + + msec = (DWORD)(ts->tv_sec * 1000); + msec += (DWORD)((ts->tv_nsec+999999) / 1000000); + Sleep(msec); + + return 0; +} + + +int almtx_init(almtx_t *mtx, int type) +{ + if(!mtx) return althrd_error; + type &= ~(almtx_recursive|almtx_timed); + if(type != almtx_plain) + return althrd_error; + + InitializeCriticalSection(mtx); + return althrd_success; +} + +void almtx_destroy(almtx_t *mtx) +{ + DeleteCriticalSection(mtx); +} + +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) +{ + int ret; + + if(!mtx || !ts) + return althrd_error; + + while((ret=almtx_trylock(mtx)) == althrd_busy) + { + struct timespec now; + + if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || + altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) + return althrd_timedout; + + althrd_yield(); + } + + return ret; +} + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +int alcnd_init(alcnd_t *cond) +{ + InitializeConditionVariable(cond); + return althrd_success; +} + +int alcnd_signal(alcnd_t *cond) +{ + WakeConditionVariable(cond); + return althrd_success; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + WakeAllConditionVariable(cond); + return althrd_success; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + if(SleepConditionVariableCS(cond, mtx, INFINITE) != 0) + return althrd_success; + return althrd_error; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + struct timespec curtime; + DWORD sleeptime; + + if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + + sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; + sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; + if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0) + return althrd_success; + return (GetLastError()==ERROR_TIMEOUT) ? althrd_timedout : althrd_error; +} + +void alcnd_destroy(alcnd_t* UNUSED(cond)) +{ + /* Nothing to delete? */ +} + +#else + +/* WARNING: This is a rather poor implementation of condition variables, with + * known problems. However, it's simple, efficient, and good enough for now to + * not require Vista. Based on "Strategies for Implementing POSIX Condition + * Variables" by Douglas C. Schmidt and Irfan Pyarali: + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + */ +/* A better solution may be using Wine's implementation. It requires internals + * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from + * ntdll, and implemention of exchange and compare-exchange for RefCounts. + */ + +typedef struct { + RefCount wait_count; + + HANDLE events[2]; +} _int_alcnd_t; +enum { + SIGNAL = 0, + BROADCAST = 1 +}; + +int alcnd_init(alcnd_t *cond) +{ + _int_alcnd_t *icond = calloc(1, sizeof(*icond)); + if(!icond) return althrd_nomem; + + InitRef(&icond->wait_count, 0); + + icond->events[SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL); + icond->events[BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!icond->events[SIGNAL] || !icond->events[BROADCAST]) + { + if(icond->events[SIGNAL]) + CloseHandle(icond->events[SIGNAL]); + if(icond->events[BROADCAST]) + CloseHandle(icond->events[BROADCAST]); + free(icond); + return althrd_error; + } + + cond->Ptr = icond; + return althrd_success; +} + +int alcnd_signal(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + if(ReadRef(&icond->wait_count) > 0) + SetEvent(icond->events[SIGNAL]); + return althrd_success; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + if(ReadRef(&icond->wait_count) > 0) + SetEvent(icond->events[BROADCAST]); + return althrd_success; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + _int_alcnd_t *icond = cond->Ptr; + int res; + + IncrementRef(&icond->wait_count); + LeaveCriticalSection(mtx); + + res = WaitForMultipleObjects(2, icond->events, FALSE, INFINITE); + + if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST) + ResetEvent(icond->events[BROADCAST]); + EnterCriticalSection(mtx); + + return althrd_success; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + _int_alcnd_t *icond = cond->Ptr; + struct timespec curtime; + DWORD sleeptime; + int res; + + if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; + sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; + + IncrementRef(&icond->wait_count); + LeaveCriticalSection(mtx); + + res = WaitForMultipleObjects(2, icond->events, FALSE, sleeptime); + + if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST) + ResetEvent(icond->events[BROADCAST]); + EnterCriticalSection(mtx); + + return (res == WAIT_TIMEOUT) ? althrd_timedout : althrd_success; +} + +void alcnd_destroy(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + CloseHandle(icond->events[SIGNAL]); + CloseHandle(icond->events[BROADCAST]); + free(icond); +} +#endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */ + + +/* An associative map of uint:void* pairs. The key is the TLS index (given by + * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits, + * we iterate over the TLS indices for their thread-local value and call the + * destructor function with it if they're both not NULL. To avoid using + * DllMain, a PIMAGE_TLS_CALLBACK function pointer is placed in a ".CRT$XLx" + * section (where x is a character A to Z) which will be called by the CRT. + */ +static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE; + +static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +{ + ALsizei i; + + if(reason == DLL_PROCESS_DETACH) + { + ResetUIntMap(&TlsDestructors); + return; + } + if(reason != DLL_THREAD_DETACH) + return; + + LockUIntMapRead(&TlsDestructors); + for(i = 0;i < TlsDestructors.size;i++) + { + void *ptr = altss_get(TlsDestructors.array[i].key); + altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value; + if(ptr && callback) + callback(ptr); + } + UnlockUIntMapRead(&TlsDestructors); +} +#ifdef _MSC_VER +#pragma section(".CRT$XLB",read) +__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#elif defined(__GNUC__) +PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback; +#else +#warning "No TLS callback support, thread-local contexts may leak references on poorly written applications." +PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#endif + +int altss_create(altss_t *tss_id, altss_dtor_t callback) +{ + DWORD key = TlsAlloc(); + if(key == TLS_OUT_OF_INDEXES) + return althrd_error; + + *tss_id = key; + if(callback != NULL) + InsertUIntMapEntry(&TlsDestructors, key, callback); + return althrd_success; +} + +void altss_delete(altss_t tss_id) +{ + RemoveUIntMapKey(&TlsDestructors, tss_id); + TlsFree(tss_id); +} + + +int altimespec_get(struct timespec *ts, int base) +{ + static_assert(sizeof(FILETIME) == sizeof(ULARGE_INTEGER), + "Size of FILETIME does not match ULARGE_INTEGER"); + if(base == AL_TIME_UTC) + { + union { + FILETIME ftime; + ULARGE_INTEGER ulint; + } systime; + GetSystemTimeAsFileTime(&systime.ftime); + /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */ + ts->tv_sec = systime.ulint.QuadPart/10000000; + ts->tv_nsec = (systime.ulint.QuadPart%10000000) * 100; + return base; + } + + return 0; +} + + +void alcall_once(alonce_flag *once, void (*callback)(void)) +{ + LONG ret; + while((ret=InterlockedExchange(once, 1)) == 1) + althrd_yield(); + if(ret == 0) + (*callback)(); + InterlockedExchange(once, 2); +} + +#else + +#include <sys/time.h> +#include <unistd.h> +#include <pthread.h> +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif + + +extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem); +extern inline void alcall_once(alonce_flag *once, void (*callback)(void)); + + +void althrd_setname(althrd_t thr, const char *name) +{ +#if defined(HAVE_PTHREAD_SETNAME_NP) +#if defined(__GNUC__) + pthread_setname_np(thr, name); +#elif defined(__APPLE__) + if(althrd_equal(thr, althrd_current()) + pthread_setname_np(name); +#endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(thr, name); +#else + (void)thr; + (void)name; +#endif +} + + +typedef struct thread_cntr { + althrd_start_t func; + void *arg; +} thread_cntr; + +static void *althrd_starter(void *arg) +{ + thread_cntr cntr; + memcpy(&cntr, arg, sizeof(cntr)); + free(arg); + + return (void*)(intptr_t)((*cntr.func)(cntr.arg)); +} + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) +{ + thread_cntr *cntr; + pthread_attr_t attr; + + cntr = malloc(sizeof(*cntr)); + if(!cntr) return althrd_nomem; + + if(pthread_attr_init(&attr) != 0) + { + free(cntr); + return althrd_error; + } + if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0) + { + pthread_attr_destroy(&attr); + free(cntr); + return althrd_error; + } + + cntr->func = func; + cntr->arg = arg; + if(pthread_create(thr, &attr, althrd_starter, cntr) != 0) + { + pthread_attr_destroy(&attr); + free(cntr); + return althrd_error; + } + pthread_attr_destroy(&attr); + + return althrd_success; +} + +int althrd_detach(althrd_t thr) +{ + if(pthread_detach(thr) != 0) + return althrd_error; + return althrd_success; +} + +int althrd_join(althrd_t thr, int *res) +{ + void *code; + + if(pthread_join(thr, &code) != 0) + return althrd_error; + if(res != NULL) + *res = (int)(intptr_t)code; + return althrd_success; +} + + +int almtx_init(almtx_t *mtx, int type) +{ + int ret; + + if(!mtx) return althrd_error; + if((type&~(almtx_recursive|almtx_timed)) != 0) + return althrd_error; + + type &= ~almtx_timed; + if(type == almtx_plain) + ret = pthread_mutex_init(mtx, NULL); + else + { + pthread_mutexattr_t attr; + + ret = pthread_mutexattr_init(&attr); + if(ret) return althrd_error; + + if(type == almtx_recursive) + { + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP + if(ret != 0) + ret = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + else + ret = 1; + if(ret == 0) + ret = pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + } + return ret ? althrd_error : althrd_success; +} + +void almtx_destroy(almtx_t *mtx) +{ + pthread_mutex_destroy(mtx); +} + +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) +{ + int ret; + +#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK + ret = pthread_mutex_timedlock(mtx, ts); + switch(ret) + { + case 0: return althrd_success; + case ETIMEDOUT: return althrd_timedout; + case EBUSY: return althrd_busy; + } + return althrd_error; +#else + if(!mtx || !ts) + return althrd_error; + + while((ret=almtx_trylock(mtx)) == althrd_busy) + { + struct timespec now; + + if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || + altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) + return althrd_timedout; + + althrd_yield(); + } + + return ret; +#endif +} + +int alcnd_init(alcnd_t *cond) +{ + if(pthread_cond_init(cond, NULL) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_signal(alcnd_t *cond) +{ + if(pthread_cond_signal(cond) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + if(pthread_cond_broadcast(cond) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + if(pthread_cond_wait(cond, mtx) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + if(pthread_cond_timedwait(cond, mtx, time_point) == 0) + return althrd_success; + return althrd_error; +} + +void alcnd_destroy(alcnd_t *cond) +{ + pthread_cond_destroy(cond); +} + + +int altss_create(altss_t *tss_id, altss_dtor_t callback) +{ + if(pthread_key_create(tss_id, callback) != 0) + return althrd_error; + return althrd_success; +} + +void altss_delete(altss_t tss_id) +{ + pthread_key_delete(tss_id); +} + + +int altimespec_get(struct timespec *ts, int base) +{ + if(base == AL_TIME_UTC) + { + int ret; +#if _POSIX_TIMERS > 0 + ret = clock_gettime(CLOCK_REALTIME, ts); + if(ret == 0) return base; +#else /* _POSIX_TIMERS > 0 */ + struct timeval tv; + ret = gettimeofday(&tv, NULL); + if(ret == 0) + { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return base; + } +#endif + } + + return 0; +} + +#endif + + +void al_nssleep(time_t sec, long nsec) +{ + struct timespec ts, rem; + ts.tv_sec = sec; + ts.tv_nsec = nsec; + + while(althrd_sleep(&ts, &rem) == -1) + ts = rem; +} diff --git a/common/uintmap.c b/common/uintmap.c new file mode 100644 index 00000000..b7a9a29c --- /dev/null +++ b/common/uintmap.c @@ -0,0 +1,144 @@ + +#include "config.h" + +#include "uintmap.h" + +#include <stdlib.h> +#include <string.h> + + +extern inline void LockUIntMapRead(UIntMap *map); +extern inline void UnlockUIntMapRead(UIntMap *map); +extern inline void LockUIntMapWrite(UIntMap *map); +extern inline void UnlockUIntMapWrite(UIntMap *map); + + +void InitUIntMap(UIntMap *map, ALsizei limit) +{ + map->array = NULL; + map->size = 0; + map->maxsize = 0; + map->limit = limit; + RWLockInit(&map->lock); +} + +void ResetUIntMap(UIntMap *map) +{ + WriteLock(&map->lock); + free(map->array); + map->array = NULL; + map->size = 0; + map->maxsize = 0; + WriteUnlock(&map->lock); +} + +ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) +{ + ALsizei pos = 0; + + WriteLock(&map->lock); + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key < key) + low++; + pos = low; + } + + if(pos == map->size || map->array[pos].key != key) + { + if(map->size == map->limit) + { + WriteUnlock(&map->lock); + return AL_OUT_OF_MEMORY; + } + + if(map->size == map->maxsize) + { + ALvoid *temp = NULL; + ALsizei newsize; + + newsize = (map->maxsize ? (map->maxsize<<1) : 4); + if(newsize >= map->maxsize) + temp = realloc(map->array, newsize*sizeof(map->array[0])); + if(!temp) + { + WriteUnlock(&map->lock); + return AL_OUT_OF_MEMORY; + } + map->array = temp; + map->maxsize = newsize; + } + + if(pos < map->size) + memmove(&map->array[pos+1], &map->array[pos], + (map->size-pos)*sizeof(map->array[0])); + map->size++; + } + map->array[pos].key = key; + map->array[pos].value = value; + WriteUnlock(&map->lock); + + return AL_NO_ERROR; +} + +ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) +{ + ALvoid *ptr = NULL; + WriteLock(&map->lock); + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key == key) + { + ptr = map->array[low].value; + if(low < map->size-1) + memmove(&map->array[low], &map->array[low+1], + (map->size-1-low)*sizeof(map->array[0])); + map->size--; + } + } + WriteUnlock(&map->lock); + return ptr; +} + +ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) +{ + ALvoid *ptr = NULL; + ReadLock(&map->lock); + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key == key) + ptr = map->array[low].value; + } + ReadUnlock(&map->lock); + return ptr; +} diff --git a/config.h.in b/config.h.in index d9582e74..3fdc0c7e 100644 --- a/config.h.in +++ b/config.h.in @@ -25,6 +25,8 @@ /* Define if we have SSE CPU extensions */ #cmakedefine HAVE_SSE +#cmakedefine HAVE_SSE2 +#cmakedefine HAVE_SSE4_1 /* Define if we have ARM Neon CPU extensions */ #cmakedefine HAVE_NEON @@ -89,6 +91,18 @@ /* Define to the size of a long long int type */ #cmakedefine SIZEOF_LONG_LONG ${SIZEOF_LONG_LONG} +/* Define if we have C99 variable-length array support */ +#cmakedefine HAVE_C99_VLA + +/* Define if we have C99 _Bool support */ +#cmakedefine HAVE_C99_BOOL + +/* Define if we have C11 _Static_assert support */ +#cmakedefine HAVE_C11_STATIC_ASSERT + +/* Define if we have C11 _Alignas support */ +#cmakedefine HAVE_C11_ALIGNAS + /* Define if we have GCC's destructor attribute */ #cmakedefine HAVE_GCC_DESTRUCTOR @@ -98,6 +112,12 @@ /* Define if we have stdint.h */ #cmakedefine HAVE_STDINT_H +/* Define if we have stdbool.h */ +#cmakedefine HAVE_STDBOOL_H + +/* Define if we have stdalign.h */ +#cmakedefine HAVE_STDALIGN_H + /* Define if we have windows.h */ #cmakedefine HAVE_WINDOWS_H @@ -113,9 +133,18 @@ /* Define if we have arm_neon.h */ #cmakedefine HAVE_ARM_NEON_H +/* Define if we have alloca.h */ +#cmakedefine HAVE_ALLOCA_H + /* Define if we have malloc.h */ #cmakedefine HAVE_MALLOC_H +/* Define if we have ftw.h */ +#cmakedefine HAVE_FTW_H + +/* Define if we have io.h */ +#cmakedefine HAVE_IO_H + /* Define if we have strings.h */ #cmakedefine HAVE_STRINGS_H @@ -146,6 +175,12 @@ /* Define if we have __control87_2() */ #cmakedefine HAVE___CONTROL87_2 +/* Define if we have ftw() */ +#cmakedefine HAVE_FTW + +/* Define if we have _wfindfirst() */ +#cmakedefine HAVE__WFINDFIRST + /* Define if we have pthread_setschedparam() */ #cmakedefine HAVE_PTHREAD_SETSCHEDPARAM @@ -154,3 +189,9 @@ /* Define if we have pthread_set_name_np() */ #cmakedefine HAVE_PTHREAD_SET_NAME_NP + +/* Define if we have pthread_mutexattr_setkind_np() */ +#cmakedefine HAVE_PTHREAD_MUTEXATTR_SETKIND_NP + +/* Define if we have pthread_mutex_timedlock() */ +#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK diff --git a/examples/alffplay.c b/examples/alffplay.c new file mode 100644 index 00000000..762582c4 --- /dev/null +++ b/examples/alffplay.c @@ -0,0 +1,1478 @@ +/* + * alffplay.c + * + * A pedagogical video player that really works! Now with seeking features. + * + * Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, and a tutorial by + * Martin Bohme <[email protected]>. + * + * Requires C99. + */ + +#include <stdio.h> +#include <math.h> + +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +#include <libavutil/time.h> +#include <libavutil/avstring.h> +#include <libswscale/swscale.h> +#include <libswresample/swresample.h> + +#include <SDL.h> +#include <SDL_thread.h> +#include <SDL_video.h> + +#include "threads.h" +#include "bool.h" + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + + +static bool has_latency_check = false; +static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; + +#define AUDIO_BUFFER_TIME 100 /* In milliseconds, per-buffer */ +#define AUDIO_BUFFER_QUEUE_SIZE 8 /* Number of buffers to queue */ +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) /* Bytes of compressed audio data to keep queued */ +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) /* Bytes of compressed video data to keep queued */ +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_MAX_DIFF 0.1 +#define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 16 + +enum { + FF_UPDATE_EVENT = SDL_USEREVENT, + FF_REFRESH_EVENT, + FF_QUIT_EVENT +}; + + +typedef struct PacketQueue { + AVPacketList *first_pkt, *last_pkt; + volatile int nb_packets; + volatile int size; + volatile bool flushing; + almtx_t mutex; + alcnd_t cond; +} PacketQueue; + +typedef struct VideoPicture { + SDL_Texture *bmp; + int width, height; /* Logical image size (actual size may be larger) */ + volatile bool updated; + double pts; +} VideoPicture; + +typedef struct AudioState { + AVStream *st; + + PacketQueue q; + AVPacket pkt; + + /* Used for clock difference average computation */ + double diff_accum; + double diff_avg_coef; + double diff_threshold; + + /* Time (in seconds) of the next sample to be buffered */ + double current_pts; + + /* Decompressed sample frame, and swresample context for conversion */ + AVFrame *decoded_aframe; + struct SwrContext *swres_ctx; + + /* Conversion format, for what gets fed to OpenAL */ + int dst_ch_layout; + enum AVSampleFormat dst_sample_fmt; + + /* Storage of converted samples */ + uint8_t *samples; + ssize_t samples_len; /* In samples */ + ssize_t samples_pos; + int samples_max; + + /* OpenAL format */ + ALenum format; + ALint frame_size; + + ALuint source; + ALuint buffer[AUDIO_BUFFER_QUEUE_SIZE]; + ALuint buffer_idx; + almtx_t src_mutex; + + althrd_t thread; +} AudioState; + +typedef struct VideoState { + AVStream *st; + + PacketQueue q; + + double clock; + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double current_pts; + /* time (av_gettime) at which we updated current_pts - used to have running video pts */ + int64_t current_pts_time; + + /* Decompressed video frame, and swscale context for conversion */ + AVFrame *decoded_vframe; + struct SwsContext *swscale_ctx; + + VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; + int pictq_size, pictq_rindex, pictq_windex; + almtx_t pictq_mutex; + alcnd_t pictq_cond; + + althrd_t thread; +} VideoState; + +typedef struct MovieState { + AVFormatContext *pFormatCtx; + int videoStream, audioStream; + + volatile bool seek_req; + int64_t seek_pos; + + int av_sync_type; + + int64_t external_clock_base; + + AudioState audio; + VideoState video; + + althrd_t parse_thread; + + char filename[1024]; + + volatile bool quit; +} MovieState; + +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, + + DEFAULT_AV_SYNC_TYPE = AV_SYNC_EXTERNAL_MASTER +}; + +static AVPacket flush_pkt = { .data = (uint8_t*)"FLUSH" }; + +static void packet_queue_init(PacketQueue *q) +{ + memset(q, 0, sizeof(PacketQueue)); + almtx_init(&q->mutex, almtx_plain); + alcnd_init(&q->cond); +} +static int packet_queue_put(PacketQueue *q, AVPacket *pkt) +{ + AVPacketList *pkt1; + if(pkt != &flush_pkt && !pkt->buf && av_dup_packet(pkt) < 0) + return -1; + + pkt1 = av_malloc(sizeof(AVPacketList)); + if(!pkt1) return -1; + pkt1->pkt = *pkt; + pkt1->next = NULL; + + almtx_lock(&q->mutex); + if(!q->last_pkt) + q->first_pkt = pkt1; + else + q->last_pkt->next = pkt1; + q->last_pkt = pkt1; + q->nb_packets++; + q->size += pkt1->pkt.size; + almtx_unlock(&q->mutex); + + alcnd_signal(&q->cond); + return 0; +} +static int packet_queue_get(PacketQueue *q, AVPacket *pkt, MovieState *state) +{ + AVPacketList *pkt1; + int ret = -1; + + almtx_lock(&q->mutex); + while(!state->quit) + { + pkt1 = q->first_pkt; + if(pkt1) + { + q->first_pkt = pkt1->next; + if(!q->first_pkt) + q->last_pkt = NULL; + q->nb_packets--; + q->size -= pkt1->pkt.size; + *pkt = pkt1->pkt; + av_free(pkt1); + ret = 1; + break; + } + + if(q->flushing) + { + ret = 0; + break; + } + alcnd_wait(&q->cond, &q->mutex); + } + almtx_unlock(&q->mutex); + return ret; +} +static void packet_queue_clear(PacketQueue *q) +{ + AVPacketList *pkt, *pkt1; + + almtx_lock(&q->mutex); + for(pkt = q->first_pkt;pkt != NULL;pkt = pkt1) + { + pkt1 = pkt->next; + if(pkt->pkt.data != flush_pkt.data) + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + q->last_pkt = NULL; + q->first_pkt = NULL; + q->nb_packets = 0; + q->size = 0; + almtx_unlock(&q->mutex); +} +static void packet_queue_flush(PacketQueue *q) +{ + almtx_lock(&q->mutex); + q->flushing = true; + almtx_unlock(&q->mutex); + alcnd_signal(&q->cond); +} +static void packet_queue_deinit(PacketQueue *q) +{ + packet_queue_clear(q); + alcnd_destroy(&q->cond); + almtx_destroy(&q->mutex); +} + + +static double get_audio_clock(AudioState *state) +{ + double pts; + + almtx_lock(&state->src_mutex); + /* The audio clock is the timestamp of the sample currently being heard. + * It's based on 4 components: + * 1 - The timestamp of the next sample to buffer (state->current_pts) + * 2 - The length of the source's buffer queue (AL_SEC_LENGTH_SOFT) + * 3 - The offset OpenAL is currently at in the source (the first value + * from AL_SEC_OFFSET_LATENCY_SOFT) + * 4 - The latency between OpenAL and the DAC (the second value from + * AL_SEC_OFFSET_LATENCY_SOFT) + * + * Subtracting the length of the source queue from the next sample's + * timestamp gives the timestamp of the sample at start of the source + * queue. Adding the source offset to that results in the timestamp for + * OpenAL's current position, and subtracting the source latency from that + * gives the timestamp of the sample currently at the DAC. + */ + pts = state->current_pts; + if(state->source) + { + ALdouble offset[2] = { 0.0, 0.0 }; + ALdouble queue_len = 0.0; + ALint status; + + /* NOTE: The source state must be checked last, in case an underrun + * occurs and the source stops between retrieving the offset+latency + * and getting the state. */ + if(has_latency_check) + { + alGetSourcedvSOFT(state->source, AL_SEC_OFFSET_LATENCY_SOFT, offset); + alGetSourcedvSOFT(state->source, AL_SEC_LENGTH_SOFT, &queue_len); + } + else + { + ALint ioffset, ilen; + alGetSourcei(state->source, AL_SAMPLE_OFFSET, &ioffset); + alGetSourcei(state->source, AL_SAMPLE_LENGTH_SOFT, &ilen); + offset[0] = (double)ioffset / state->st->codec->sample_rate; + queue_len = (double)ilen / state->st->codec->sample_rate; + } + alGetSourcei(state->source, AL_SOURCE_STATE, &status); + + /* If the source is AL_STOPPED, then there was an underrun and all + * buffers are processed, so ignore the source queue. The audio thread + * will put the source into an AL_INITIAL state and clear the queue + * when it starts recovery. */ + if(status != AL_STOPPED) + pts = pts - queue_len + offset[0]; + if(status == AL_PLAYING) + pts = pts - offset[1]; + } + almtx_unlock(&state->src_mutex); + + return (pts >= 0.0) ? pts : 0.0; +} +static double get_video_clock(VideoState *state) +{ + double delta = (av_gettime() - state->current_pts_time) / 1000000.0; + return state->current_pts + delta; +} +static double get_external_clock(MovieState *movState) +{ + return (av_gettime()-movState->external_clock_base) / 1000000.0; +} + +double get_master_clock(MovieState *movState) +{ + if(movState->av_sync_type == AV_SYNC_VIDEO_MASTER) + return get_video_clock(&movState->video); + if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) + return get_audio_clock(&movState->audio); + return get_external_clock(movState); +} + +/* Return how many samples to skip to maintain sync (negative means to + * duplicate samples). */ +static int synchronize_audio(MovieState *movState) +{ + double diff, avg_diff; + double ref_clock; + + if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) + return 0; + + ref_clock = get_master_clock(movState); + diff = ref_clock - get_audio_clock(&movState->audio); + + if(!(diff < AV_NOSYNC_THRESHOLD)) + { + /* Difference is TOO big; reset diff stuff */ + movState->audio.diff_accum = 0.0; + return 0; + } + + /* Accumulate the diffs */ + movState->audio.diff_accum = movState->audio.diff_accum*movState->audio.diff_avg_coef + diff; + avg_diff = movState->audio.diff_accum*(1.0 - movState->audio.diff_avg_coef); + if(fabs(avg_diff) < movState->audio.diff_threshold) + return 0; + + /* Constrain the per-update difference to avoid exceedingly large skips */ + if(!(diff <= SAMPLE_CORRECTION_MAX_DIFF)) + diff = SAMPLE_CORRECTION_MAX_DIFF; + else if(!(diff >= -SAMPLE_CORRECTION_MAX_DIFF)) + diff = -SAMPLE_CORRECTION_MAX_DIFF; + return (int)(diff*movState->audio.st->codec->sample_rate); +} + +static int audio_decode_frame(MovieState *movState) +{ + AVPacket *pkt = &movState->audio.pkt; + + while(!movState->quit) + { + while(!movState->quit && pkt->size == 0) + { + av_free_packet(pkt); + + /* Get the next packet */ + int err; + if((err=packet_queue_get(&movState->audio.q, pkt, movState)) <= 0) + { + if(err == 0) + break; + return err; + } + if(pkt->data == flush_pkt.data) + { + avcodec_flush_buffers(movState->audio.st->codec); + movState->audio.diff_accum = 0.0; + movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; + + alSourceRewind(movState->audio.source); + alSourcei(movState->audio.source, AL_BUFFER, 0); + + av_new_packet(pkt, 0); + + return -1; + } + + /* If provided, update w/ pts */ + if(pkt->pts != AV_NOPTS_VALUE) + movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; + } + + AVFrame *frame = movState->audio.decoded_aframe; + int got_frame = 0; + int len1 = avcodec_decode_audio4(movState->audio.st->codec, frame, + &got_frame, pkt); + if(len1 < 0) break; + + if(len1 <= pkt->size) + { + /* Move the unread data to the front and clear the end bits */ + int remaining = pkt->size - len1; + memmove(pkt->data, &pkt->data[len1], remaining); + av_shrink_packet(pkt, remaining); + } + + if(!got_frame || frame->nb_samples <= 0) + { + av_frame_unref(frame); + continue; + } + + if(frame->nb_samples > movState->audio.samples_max) + { + av_freep(&movState->audio.samples); + av_samples_alloc( + &movState->audio.samples, NULL, movState->audio.st->codec->channels, + frame->nb_samples, movState->audio.dst_sample_fmt, 0 + ); + movState->audio.samples_max = frame->nb_samples; + } + /* Return the amount of sample frames converted */ + int data_size = swr_convert(movState->audio.swres_ctx, + &movState->audio.samples, frame->nb_samples, + (const uint8_t**)frame->data, frame->nb_samples + ); + + av_frame_unref(frame); + return data_size; + } + + return -1; +} + +static int read_audio(MovieState *movState, uint8_t *samples, int length) +{ + int sample_skip = synchronize_audio(movState); + int audio_size = 0; + + /* Read the next chunk of data, refill the buffer, and queue it + * on the source */ + length /= movState->audio.frame_size; + while(audio_size < length) + { + if(movState->audio.samples_len <= 0 || movState->audio.samples_pos >= movState->audio.samples_len) + { + int frame_len = audio_decode_frame(movState); + if(frame_len < 0) return -1; + + movState->audio.samples_len = frame_len; + if(movState->audio.samples_len == 0) + break; + + movState->audio.samples_pos = (movState->audio.samples_len < sample_skip) ? + movState->audio.samples_len : sample_skip; + sample_skip -= movState->audio.samples_pos; + + movState->audio.current_pts += (double)movState->audio.samples_pos / + (double)movState->audio.st->codec->sample_rate; + continue; + } + + int rem = length - audio_size; + if(movState->audio.samples_pos >= 0) + { + int n = movState->audio.frame_size; + int len = movState->audio.samples_len - movState->audio.samples_pos; + if(rem > len) rem = len; + memcpy(samples + audio_size*n, + movState->audio.samples + movState->audio.samples_pos*n, + rem*n); + } + else + { + int n = movState->audio.frame_size; + int len = -movState->audio.samples_pos; + if(rem > len) rem = len; + + /* Add samples by copying the first sample */ + if(n == 1) + { + uint8_t sample = ((uint8_t*)movState->audio.samples)[0]; + uint8_t *q = (uint8_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 2) + { + uint16_t sample = ((uint16_t*)movState->audio.samples)[0]; + uint16_t *q = (uint16_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 4) + { + uint32_t sample = ((uint32_t*)movState->audio.samples)[0]; + uint32_t *q = (uint32_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 8) + { + uint64_t sample = ((uint64_t*)movState->audio.samples)[0]; + uint64_t *q = (uint64_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else + { + uint8_t *sample = movState->audio.samples; + uint8_t *q = samples + audio_size*n; + for(int i = 0;i < rem;i++) + { + memcpy(q, sample, n); + q += n; + } + } + } + + movState->audio.samples_pos += rem; + movState->audio.current_pts += (double)rem / movState->audio.st->codec->sample_rate; + audio_size += rem; + } + + return audio_size * movState->audio.frame_size; +} + +static int audio_thread(void *userdata) +{ + MovieState *movState = (MovieState*)userdata; + uint8_t *samples = NULL; + ALsizei buffer_len; + + alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); + alGenSources(1, &movState->audio.source); + + alSourcei(movState->audio.source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(movState->audio.source, AL_ROLLOFF_FACTOR, 0); + + av_new_packet(&movState->audio.pkt, 0); + + /* Find a suitable format for OpenAL. Currently does not handle surround + * sound (everything non-mono becomes stereo). */ + if(movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8 || + movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8P) + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_U8; + movState->audio.frame_size = 1; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO8; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO8; + } + } + else if((movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLT || + movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) && + alIsExtensionPresent("AL_EXT_FLOAT32")) + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_FLT; + movState->audio.frame_size = 4; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO_FLOAT32; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO_FLOAT32; + } + } + else + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_S16; + movState->audio.frame_size = 2; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO16; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO16; + } + } + buffer_len = AUDIO_BUFFER_TIME * movState->audio.st->codec->sample_rate / 1000 * + movState->audio.frame_size; + samples = av_malloc(buffer_len); + + movState->audio.samples = NULL; + movState->audio.samples_max = 0; + movState->audio.samples_pos = 0; + movState->audio.samples_len = 0; + + if(!(movState->audio.decoded_aframe=av_frame_alloc())) + { + fprintf(stderr, "Failed to allocate audio frame\n"); + goto finish; + } + + movState->audio.swres_ctx = swr_alloc_set_opts(NULL, + movState->audio.dst_ch_layout, + movState->audio.dst_sample_fmt, + movState->audio.st->codec->sample_rate, + movState->audio.st->codec->channel_layout, + movState->audio.st->codec->sample_fmt, + movState->audio.st->codec->sample_rate, + 0, NULL + ); + if(!movState->audio.swres_ctx || swr_init(movState->audio.swres_ctx) != 0) + { + fprintf(stderr, "Failed to initialize audio converter\n"); + goto finish; + } + + almtx_lock(&movState->audio.src_mutex); + while(alGetError() == AL_NO_ERROR && !movState->quit) + { + /* First remove any processed buffers. */ + ALint processed; + alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); + alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); + + /* Refill the buffer queue. */ + ALint queued; + alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); + while(queued < AUDIO_BUFFER_QUEUE_SIZE) + { + int audio_size; + + /* Read the next chunk of data, fill the buffer, and queue it on + * the source */ + audio_size = read_audio(movState, samples, buffer_len); + if(audio_size < 0) break; + + ALuint bufid = movState->audio.buffer[movState->audio.buffer_idx++]; + movState->audio.buffer_idx %= AUDIO_BUFFER_QUEUE_SIZE; + + alBufferData(bufid, movState->audio.format, samples, audio_size, + movState->audio.st->codec->sample_rate); + alSourceQueueBuffers(movState->audio.source, 1, &bufid); + queued++; + } + + /* Check that the source is playing. */ + ALint state; + alGetSourcei(movState->audio.source, AL_SOURCE_STATE, &state); + if(state == AL_STOPPED) + { + /* AL_STOPPED means there was an underrun. Double-check that all + * processed buffers are removed, then rewind the source to get it + * back into an AL_INITIAL state. */ + alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); + alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); + alSourceRewind(movState->audio.source); + continue; + } + + almtx_unlock(&movState->audio.src_mutex); + + /* (re)start the source if needed, and wait for a buffer to finish */ + if(state != AL_PLAYING && state != AL_PAUSED) + { + alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); + if(queued > 0) alSourcePlay(movState->audio.source); + } + SDL_Delay(AUDIO_BUFFER_TIME); + + almtx_lock(&movState->audio.src_mutex); + } + almtx_unlock(&movState->audio.src_mutex); + +finish: + av_frame_free(&movState->audio.decoded_aframe); + swr_free(&movState->audio.swres_ctx); + + av_freep(&samples); + av_freep(&movState->audio.samples); + + alDeleteSources(1, &movState->audio.source); + alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); + + return 0; +} + + +static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) +{ + (void)interval; + + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_REFRESH_EVENT, .data1=opaque} }); + return 0; /* 0 means stop timer */ +} + +/* Schedule a video refresh in 'delay' ms */ +static void schedule_refresh(MovieState *movState, int delay) +{ + SDL_AddTimer(delay, sdl_refresh_timer_cb, movState); +} + +static void video_display(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) +{ + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; + + if(!vp->bmp) + return; + + float aspect_ratio; + int win_w, win_h; + int w, h, x, y; + + if(movState->video.st->codec->sample_aspect_ratio.num == 0) + aspect_ratio = 0.0f; + else + { + aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio) * + movState->video.st->codec->width / + movState->video.st->codec->height; + } + if(aspect_ratio <= 0.0f) + { + aspect_ratio = (float)movState->video.st->codec->width / + (float)movState->video.st->codec->height; + } + + SDL_GetWindowSize(screen, &win_w, &win_h); + h = win_h; + w = ((int)rint(h * aspect_ratio) + 3) & ~3; + if(w > win_w) + { + w = win_w; + h = ((int)rint(w / aspect_ratio) + 3) & ~3; + } + x = (win_w - w) / 2; + y = (win_h - h) / 2; + + SDL_RenderCopy(renderer, vp->bmp, + &(SDL_Rect){ .x=0, .y=0, .w=vp->width, .h=vp->height }, + &(SDL_Rect){ .x=x, .y=y, .w=w, .h=h } + ); + SDL_RenderPresent(renderer); +} + +static void video_refresh_timer(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) +{ + if(!movState->video.st) + { + schedule_refresh(movState, 100); + return; + } + + almtx_lock(&movState->video.pictq_mutex); +retry: + if(movState->video.pictq_size == 0) + schedule_refresh(movState, 1); + else + { + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + movState->video.current_pts = vp->pts; + movState->video.current_pts_time = av_gettime(); + + delay = vp->pts - movState->video.frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) + { + /* if incorrect delay, use previous one */ + delay = movState->video.frame_last_delay; + } + /* save for next time */ + movState->video.frame_last_delay = delay; + movState->video.frame_last_pts = vp->pts; + + /* Update delay to sync to clock if not master source. */ + if(movState->av_sync_type != AV_SYNC_VIDEO_MASTER) + { + ref_clock = get_master_clock(movState); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account. */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; + } + } + + movState->video.frame_timer += delay; + /* Compute the REAL delay. */ + actual_delay = movState->video.frame_timer - (av_gettime() / 1000000.0); + if(!(actual_delay >= 0.010)) + { + /* We don't have time to handle this picture, just skip to the next one. */ + movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; + movState->video.pictq_size--; + alcnd_signal(&movState->video.pictq_cond); + goto retry; + } + schedule_refresh(movState, (int)(actual_delay*1000.0 + 0.5)); + + /* Show the picture! */ + video_display(movState, screen, renderer); + + /* Update queue for next picture. */ + movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; + movState->video.pictq_size--; + alcnd_signal(&movState->video.pictq_cond); + } + almtx_unlock(&movState->video.pictq_mutex); +} + + +static void update_picture(MovieState *movState, bool *first_update, SDL_Window *screen, SDL_Renderer *renderer) +{ + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; + + /* allocate or resize the buffer! */ + if(!vp->bmp || vp->width != movState->video.st->codec->width || + vp->height != movState->video.st->codec->height) + { + if(vp->bmp) + SDL_DestroyTexture(vp->bmp); + vp->bmp = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, + movState->video.st->codec->coded_width, movState->video.st->codec->coded_height + ); + if(!vp->bmp) + fprintf(stderr, "Failed to create YV12 texture!\n"); + vp->width = movState->video.st->codec->width; + vp->height = movState->video.st->codec->height; + + if(*first_update && vp->width > 0 && vp->height > 0) + { + /* For the first update, set the window size to the video size. */ + *first_update = false; + + int w = vp->width; + int h = vp->height; + if(movState->video.st->codec->sample_aspect_ratio.num != 0 && + movState->video.st->codec->sample_aspect_ratio.den != 0) + { + double aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio); + if(aspect_ratio >= 1.0) + w = (int)(w*aspect_ratio + 0.5); + else if(aspect_ratio > 0.0) + h = (int)(h/aspect_ratio + 0.5); + } + SDL_SetWindowSize(screen, w, h); + } + } + + if(vp->bmp) + { + AVFrame *frame = movState->video.decoded_vframe; + void *pixels = NULL; + int pitch = 0; + + if(movState->video.st->codec->pix_fmt == PIX_FMT_YUV420P) + SDL_UpdateYUVTexture(vp->bmp, NULL, + frame->data[0], frame->linesize[0], + frame->data[1], frame->linesize[1], + frame->data[2], frame->linesize[2] + ); + else if(SDL_LockTexture(vp->bmp, NULL, &pixels, &pitch) != 0) + fprintf(stderr, "Failed to lock texture\n"); + else + { + // Convert the image into YUV format that SDL uses + int coded_w = movState->video.st->codec->coded_width; + int coded_h = movState->video.st->codec->coded_height; + int w = movState->video.st->codec->width; + int h = movState->video.st->codec->height; + if(!movState->video.swscale_ctx) + movState->video.swscale_ctx = sws_getContext( + w, h, movState->video.st->codec->pix_fmt, + w, h, PIX_FMT_YUV420P, SWS_X, NULL, NULL, NULL + ); + + /* point pict at the queue */ + AVPicture pict; + pict.data[0] = pixels; + pict.data[2] = pict.data[0] + coded_w*coded_h; + pict.data[1] = pict.data[2] + coded_w*coded_h/4; + + pict.linesize[0] = pitch; + pict.linesize[2] = pitch / 2; + pict.linesize[1] = pitch / 2; + + sws_scale(movState->video.swscale_ctx, (const uint8_t**)frame->data, + frame->linesize, 0, h, pict.data, pict.linesize); + SDL_UnlockTexture(vp->bmp); + } + } + + almtx_lock(&movState->video.pictq_mutex); + vp->updated = true; + almtx_unlock(&movState->video.pictq_mutex); + alcnd_signal(&movState->video.pictq_cond); +} + +static int queue_picture(MovieState *movState, double pts) +{ + /* Wait until we have space for a new pic */ + almtx_lock(&movState->video.pictq_mutex); + while(movState->video.pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !movState->quit) + alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); + almtx_unlock(&movState->video.pictq_mutex); + + if(movState->quit) + return -1; + + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; + + /* We have to create/update the picture in the main thread */ + vp->updated = false; + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_UPDATE_EVENT, .data1=movState} }); + + /* Wait until the picture is updated. */ + almtx_lock(&movState->video.pictq_mutex); + while(!vp->updated && !movState->quit) + alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); + almtx_unlock(&movState->video.pictq_mutex); + if(movState->quit) + return -1; + vp->pts = pts; + + movState->video.pictq_windex = (movState->video.pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE; + almtx_lock(&movState->video.pictq_mutex); + movState->video.pictq_size++; + almtx_unlock(&movState->video.pictq_mutex); + + return 0; +} + +static double synchronize_video(MovieState *movState, double pts) +{ + double frame_delay; + + if(pts == 0.0) /* if we aren't given a pts, set it to the clock */ + pts = movState->video.clock; + else /* if we have pts, set video clock to it */ + movState->video.clock = pts; + + /* update the video clock */ + frame_delay = av_q2d(movState->video.st->codec->time_base); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += movState->video.decoded_vframe->repeat_pict * (frame_delay * 0.5); + movState->video.clock += frame_delay; + return pts; +} + +int video_thread(void *arg) +{ + MovieState *movState = (MovieState*)arg; + AVPacket *packet = (AVPacket[1]){}; + int64_t saved_pts, pkt_pts; + int frameFinished; + + movState->video.decoded_vframe = av_frame_alloc(); + while(packet_queue_get(&movState->video.q, packet, movState) >= 0) + { + if(packet->data == flush_pkt.data) + { + avcodec_flush_buffers(movState->video.st->codec); + + almtx_lock(&movState->video.pictq_mutex); + movState->video.pictq_size = 0; + movState->video.pictq_rindex = 0; + movState->video.pictq_windex = 0; + almtx_unlock(&movState->video.pictq_mutex); + + movState->video.clock = av_q2d(movState->video.st->time_base)*packet->pts; + movState->video.current_pts = movState->video.clock; + movState->video.current_pts_time = av_gettime(); + continue; + } + + pkt_pts = packet->pts; + + /* Decode video frame */ + avcodec_decode_video2(movState->video.st->codec, movState->video.decoded_vframe, + &frameFinished, packet); + if(pkt_pts != AV_NOPTS_VALUE && !movState->video.decoded_vframe->opaque) + { + /* Store the packet's original pts in the frame, in case the frame + * is not finished decoding yet. */ + saved_pts = pkt_pts; + movState->video.decoded_vframe->opaque = &saved_pts; + } + + av_free_packet(packet); + + if(frameFinished) + { + double pts = av_q2d(movState->video.st->time_base); + if(packet->dts != AV_NOPTS_VALUE) + pts *= packet->dts; + else if(movState->video.decoded_vframe->opaque) + pts *= *(int64_t*)movState->video.decoded_vframe->opaque; + else + pts *= 0.0; + movState->video.decoded_vframe->opaque = NULL; + + pts = synchronize_video(movState, pts); + if(queue_picture(movState, pts) < 0) + break; + } + } + + sws_freeContext(movState->video.swscale_ctx); + movState->video.swscale_ctx = NULL; + av_frame_free(&movState->video.decoded_vframe); + return 0; +} + + +static int stream_component_open(MovieState *movState, int stream_index) +{ + AVFormatContext *pFormatCtx = movState->pFormatCtx; + AVCodecContext *codecCtx; + AVCodec *codec; + + if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams) + return -1; + + /* Get a pointer to the codec context for the video stream, and open the + * associated codec */ + codecCtx = pFormatCtx->streams[stream_index]->codec; + + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || avcodec_open2(codecCtx, codec, NULL) < 0) + { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + + /* Initialize and start the media type handler */ + switch(codecCtx->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + movState->audioStream = stream_index; + movState->audio.st = pFormatCtx->streams[stream_index]; + + /* Averaging filter for audio sync */ + movState->audio.diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB); + /* Correct audio only if larger error than this */ + movState->audio.diff_threshold = 2.0 * 0.050/* 50 ms */; + + memset(&movState->audio.pkt, 0, sizeof(movState->audio.pkt)); + if(althrd_create(&movState->audio.thread, audio_thread, movState) != althrd_success) + { + movState->audioStream = -1; + movState->audio.st = NULL; + } + break; + + case AVMEDIA_TYPE_VIDEO: + movState->videoStream = stream_index; + movState->video.st = pFormatCtx->streams[stream_index]; + + movState->video.current_pts_time = av_gettime(); + movState->video.frame_timer = (double)movState->video.current_pts_time / + 1000000.0; + movState->video.frame_last_delay = 40e-3; + + if(althrd_create(&movState->video.thread, video_thread, movState) != althrd_success) + { + movState->videoStream = -1; + movState->video.st = NULL; + } + break; + + default: + break; + } + + return 0; +} + +static int decode_interrupt_cb(void *ctx) +{ + return ((MovieState*)ctx)->quit; +} + +int decode_thread(void *arg) +{ + MovieState *movState = (MovieState *)arg; + AVFormatContext *fmtCtx = movState->pFormatCtx; + AVPacket *packet = (AVPacket[1]){}; + int video_index = -1; + int audio_index = -1; + + movState->videoStream = -1; + movState->audioStream = -1; + + /* Dump information about file onto standard error */ + av_dump_format(fmtCtx, 0, movState->filename, 0); + + /* Find the first video and audio streams */ + for(unsigned int i = 0;i < fmtCtx->nb_streams;i++) + { + if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + else if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + movState->external_clock_base = av_gettime(); + if(audio_index >= 0) + stream_component_open(movState, audio_index); + if(video_index >= 0) + stream_component_open(movState, video_index); + + if(movState->videoStream < 0 && movState->audioStream < 0) + { + fprintf(stderr, "%s: could not open codecs\n", movState->filename); + goto fail; + } + + /* Main packet handling loop */ + while(!movState->quit) + { + if(movState->seek_req) + { + int64_t seek_target = movState->seek_pos; + int stream_index= -1; + + /* Prefer seeking on the video stream. */ + if(movState->videoStream >= 0) + stream_index = movState->videoStream; + else if(movState->audioStream >= 0) + stream_index = movState->audioStream; + + /* Get a seek timestamp for the appropriate stream. */ + int64_t timestamp = seek_target; + if(stream_index >= 0) + timestamp = av_rescale_q(seek_target, AV_TIME_BASE_Q, fmtCtx->streams[stream_index]->time_base); + + if(av_seek_frame(movState->pFormatCtx, stream_index, timestamp, 0) < 0) + fprintf(stderr, "%s: error while seeking\n", movState->pFormatCtx->filename); + else + { + /* Seek successful, clear the packet queues and send a special + * 'flush' packet with the new stream clock time. */ + if(movState->audioStream >= 0) + { + packet_queue_clear(&movState->audio.q); + flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, + fmtCtx->streams[movState->audioStream]->time_base + ); + packet_queue_put(&movState->audio.q, &flush_pkt); + } + if(movState->videoStream >= 0) + { + packet_queue_clear(&movState->video.q); + flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, + fmtCtx->streams[movState->videoStream]->time_base + ); + packet_queue_put(&movState->video.q, &flush_pkt); + } + movState->external_clock_base = av_gettime() - seek_target; + } + movState->seek_req = false; + } + + if(movState->audio.q.size >= MAX_AUDIOQ_SIZE || + movState->video.q.size >= MAX_VIDEOQ_SIZE) + { + SDL_Delay(10); + continue; + } + + if(av_read_frame(movState->pFormatCtx, packet) < 0) + { + packet_queue_flush(&movState->video.q); + packet_queue_flush(&movState->audio.q); + break; + } + + /* Place the packet in the queue it's meant for, or discard it. */ + if(packet->stream_index == movState->videoStream) + packet_queue_put(&movState->video.q, packet); + else if(packet->stream_index == movState->audioStream) + packet_queue_put(&movState->audio.q, packet); + else + av_free_packet(packet); + } + + /* all done - wait for it */ + while(!movState->quit) + { + if(movState->audio.q.nb_packets == 0 && movState->video.q.nb_packets == 0) + break; + SDL_Delay(100); + } + +fail: + movState->quit = true; + packet_queue_flush(&movState->video.q); + packet_queue_flush(&movState->audio.q); + + if(movState->videoStream >= 0) + althrd_join(movState->video.thread, NULL); + if(movState->audioStream >= 0) + althrd_join(movState->audio.thread, NULL); + + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_QUIT_EVENT, .data1=movState} }); + + return 0; +} + + +static void stream_seek(MovieState *movState, double incr) +{ + if(!movState->seek_req) + { + double newtime = get_master_clock(movState)+incr; + if(newtime <= 0.0) movState->seek_pos = 0; + else movState->seek_pos = (int64_t)(newtime * AV_TIME_BASE); + movState->seek_req = true; + } +} + +int main(int argc, char *argv[]) +{ + SDL_Event event; + MovieState *movState; + bool first_update = true; + SDL_Window *screen; + SDL_Renderer *renderer; + ALCdevice *device; + ALCcontext *context; + + if(argc < 2) + { + fprintf(stderr, "Usage: %s <file>\n", argv[0]); + return 1; + } + /* Register all formats and codecs */ + av_register_all(); + /* Initialize networking protocols */ + avformat_network_init(); + + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) + { + fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); + return 1; + } + + /* Make a window to put our video */ + screen = SDL_CreateWindow("alffplay", 0, 0, 640, 480, SDL_WINDOW_RESIZABLE); + if(!screen) + { + fprintf(stderr, "SDL: could not set video mode - exiting\n"); + return 1; + } + /* Make a renderer to handle the texture image surface and rendering. */ + renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED); + if(renderer) + { + SDL_RendererInfo rinf; + bool ok = false; + + /* Make sure the renderer supports YV12 textures. If not, fallback to a + * software renderer. */ + if(SDL_GetRendererInfo(renderer, &rinf) == 0) + { + for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++) + ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_YV12); + } + if(!ok) + { + fprintf(stderr, "YV12 pixelformat textures not supported on renderer %s\n", rinf.name); + SDL_DestroyRenderer(renderer); + renderer = NULL; + } + } + if(!renderer) + renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_SOFTWARE); + if(!renderer) + { + fprintf(stderr, "SDL: could not create renderer - exiting\n"); + return 1; + } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderFillRect(renderer, NULL); + SDL_RenderPresent(renderer); + + /* Open an audio device */ + device = alcOpenDevice(NULL); + if(!device) + { + fprintf(stderr, "OpenAL: could not open device - exiting\n"); + return 1; + } + context = alcCreateContext(device, NULL); + if(!context) + { + fprintf(stderr, "OpenAL: could not create context - exiting\n"); + return 1; + } + if(alcMakeContextCurrent(context) == ALC_FALSE) + { + fprintf(stderr, "OpenAL: could not make context current - exiting\n"); + return 1; + } + + if(!alIsExtensionPresent("AL_SOFTX_source_length")) /* FIXME */ + { + fprintf(stderr, "Required AL_SOFT_source_length not supported - exiting\n"); + return 1; + } + + if(!alIsExtensionPresent("AL_SOFT_source_latency")) + fprintf(stderr, "AL_SOFT_source_latency not supported, audio may be a bit laggy.\n"); + else + { + alGetSourcedvSOFT = alGetProcAddress("alGetSourcedvSOFT"); + has_latency_check = true; + } + + + movState = av_mallocz(sizeof(MovieState)); + + av_strlcpy(movState->filename, argv[1], sizeof(movState->filename)); + + packet_queue_init(&movState->audio.q); + packet_queue_init(&movState->video.q); + + almtx_init(&movState->video.pictq_mutex, almtx_plain); + alcnd_init(&movState->video.pictq_cond); + almtx_init(&movState->audio.src_mutex, almtx_recursive); + + movState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + + movState->pFormatCtx = avformat_alloc_context(); + movState->pFormatCtx->interrupt_callback = (AVIOInterruptCB){.callback=decode_interrupt_cb, .opaque=movState}; + + if(avio_open2(&movState->pFormatCtx->pb, movState->filename, AVIO_FLAG_READ, + &movState->pFormatCtx->interrupt_callback, NULL)) + { + fprintf(stderr, "Failed to open %s\n", movState->filename); + return 1; + } + + /* Open movie file */ + if(avformat_open_input(&movState->pFormatCtx, movState->filename, NULL, NULL) != 0) + { + fprintf(stderr, "Failed to open %s\n", movState->filename); + return 1; + } + + /* Retrieve stream information */ + if(avformat_find_stream_info(movState->pFormatCtx, NULL) < 0) + { + fprintf(stderr, "%s: failed to find stream info\n", movState->filename); + return 1; + } + + schedule_refresh(movState, 40); + + + if(althrd_create(&movState->parse_thread, decode_thread, movState) != althrd_success) + { + fprintf(stderr, "Failed to create parse thread!\n"); + return 1; + } + while(SDL_WaitEvent(&event) == 1) + { + switch(event.type) + { + case SDL_KEYDOWN: + switch(event.key.keysym.sym) + { + case SDLK_ESCAPE: + movState->quit = true; + break; + + case SDLK_LEFT: + stream_seek(movState, -10.0); + break; + case SDLK_RIGHT: + stream_seek(movState, 10.0); + break; + case SDLK_UP: + stream_seek(movState, 30.0); + break; + case SDLK_DOWN: + stream_seek(movState, -30.0); + break; + + default: + break; + } + break; + + case SDL_WINDOWEVENT: + switch(event.window.event) + { + case SDL_WINDOWEVENT_RESIZED: + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderFillRect(renderer, NULL); + break; + + default: + break; + } + break; + + case SDL_QUIT: + movState->quit = true; + break; + + case FF_UPDATE_EVENT: + update_picture(event.user.data1, &first_update, screen, renderer); + break; + + case FF_REFRESH_EVENT: + video_refresh_timer(event.user.data1, screen, renderer); + break; + + case FF_QUIT_EVENT: + althrd_join(movState->parse_thread, NULL); + + avformat_close_input(&movState->pFormatCtx); + + almtx_destroy(&movState->audio.src_mutex); + almtx_destroy(&movState->video.pictq_mutex); + alcnd_destroy(&movState->video.pictq_cond); + packet_queue_deinit(&movState->video.q); + packet_queue_deinit(&movState->audio.q); + + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); + + SDL_Quit(); + exit(0); + + default: + break; + } + } + + fprintf(stderr, "SDL_WaitEvent error - %s\n", SDL_GetError()); + return 1; +} diff --git a/examples/alloopback.c b/examples/alloopback.c index 649ef6ef..2361ada1 100644 --- a/examples/alloopback.c +++ b/examples/alloopback.c @@ -92,13 +92,15 @@ static ALuint CreateSineWave(void) } -int main() +int main(int argc, char *argv[]) { PlaybackInfo playback = { NULL, NULL, 0 }; SDL_AudioSpec desired, obtained; ALuint source, buffer; ALCint attrs[16]; ALenum state; + (void)argc; + (void)argv; /* Print out error if extension is missing. */ if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback")) @@ -167,6 +169,8 @@ int main() attrs[6] = 0; /* end of list */ + playback.FrameSize = FramesToBytes(1, attrs[1], attrs[3]); + /* Initialize OpenAL loopback device, using our format attributes. */ playback.Device = alcLoopbackOpenDeviceSOFT(NULL); if(!playback.Device) @@ -187,7 +191,6 @@ int main() fprintf(stderr, "Failed to set an OpenAL audio context\n"); goto error; } - playback.FrameSize = FramesToBytes(1, attrs[1], attrs[3]); /* Start SDL playing. Our callback (thus alcRenderSamplesSOFT) will now * start being called regularly to update the AL playback state. */ @@ -12,16 +12,16 @@ placement of sounds, making it seem as though they are coming from all around, including above and below the listener, instead of just to the front, back, and sides. -The built-in data set is based on the KEMAR HRTF data provided by MIT, which -can be found at <http://sound.media.mit.edu/resources/KEMAR.html>. It's only +The default data set is based on the KEMAR HRTF data provided by MIT, which can +be found at <http://sound.media.mit.edu/resources/KEMAR.html>. It's only available when using 44100hz playback. -External HRTF Data Sets -======================= +Custom HRTF Data Sets +===================== OpenAL Soft also provides an option to use user-specified data sets, in -addition to or in place of the built-in set. This allows users to provide their +addition to or in place of the default set. This allows users to provide their own data sets, which could be better suited for their heads, or to work with stereo speakers instead of headphones, or to support more playback sample rates, for example. diff --git a/hrtf/default-44100.mhr b/hrtf/default-44100.mhr Binary files differnew file mode 100644 index 00000000..e0d0bf4b --- /dev/null +++ b/hrtf/default-44100.mhr diff --git a/include/AL/alext.h b/include/AL/alext.h index f15ba33f..33f3a034 100644 --- a/include/AL/alext.h +++ b/include/AL/alext.h @@ -364,6 +364,12 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void); #endif #endif +#ifndef AL_SOFT_block_alignment +#define AL_SOFT_block_alignment 1 +#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C +#define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D +#endif + #ifdef __cplusplus } #endif diff --git a/include/align.h b/include/align.h new file mode 100644 index 00000000..babd4106 --- /dev/null +++ b/include/align.h @@ -0,0 +1,21 @@ +#ifndef AL_ALIGN_H +#define AL_ALIGN_H + +#ifdef HAVE_STDALIGN_H +#include <stdalign.h> +#endif + +#ifndef alignas +#ifdef HAVE_C11_ALIGNAS +#define alignas _Alignas +#elif defined(IN_IDE_PARSER) +/* KDevelop has problems with our align macro, so just use nothing for parsing. */ +#define alignas(x) +#else +/* NOTE: Our custom ALIGN macro can't take a type name like alignas can. For + * maximum compatibility, only provide constant integer values to alignas. */ +#define alignas(_x) ALIGN(_x) +#endif +#endif + +#endif /* AL_ALIGN_H */ diff --git a/include/atomic.h b/include/atomic.h new file mode 100644 index 00000000..4adb7a94 --- /dev/null +++ b/include/atomic.h @@ -0,0 +1,208 @@ +#ifndef AL_ATOMIC_H +#define AL_ATOMIC_H + +#include "static_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *volatile XchgPtr; + +typedef unsigned int uint; +typedef union { + uint value; +} RefCount; + +#define STATIC_REFCOUNT_INIT(V) {(V)} + +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__) + +inline void InitRef(volatile RefCount *ptr, uint value) +{ ptr->value = value; } +inline uint ReadRef(volatile RefCount *ptr) +{ __sync_synchronize(); return ptr->value; } +inline uint IncrementRef(volatile RefCount *ptr) +{ return __sync_add_and_fetch(&ptr->value, 1); } +inline uint DecrementRef(volatile RefCount *ptr) +{ return __sync_sub_and_fetch(&ptr->value, 1); } +inline uint ExchangeRef(volatile RefCount *ptr, uint newval) +{ return __sync_lock_test_and_set(&ptr->value, newval); } +inline uint CompExchangeRef(volatile RefCount *ptr, uint oldval, uint newval) +{ return __sync_val_compare_and_swap(&ptr->value, oldval, newval); } + +inline int ExchangeInt(volatile int *ptr, int newval) +{ return __sync_lock_test_and_set(ptr, newval); } +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ return __sync_lock_test_and_set(ptr, newval); } +inline int CompExchangeInt(volatile int *ptr, int oldval, int newval) +{ return __sync_val_compare_and_swap(ptr, oldval, newval); } +inline void *CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval) +{ return __sync_val_compare_and_swap(ptr, oldval, newval); } + +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +inline uint xaddl(volatile uint *dest, int incr) +{ + unsigned int ret; + __asm__ __volatile__("lock; xaddl %0,(%1)" + : "=r" (ret) + : "r" (dest), "0" (incr) + : "memory"); + return ret; +} + +inline void InitRef(volatile RefCount *ptr, uint value) +{ ptr->value = value; } +inline uint ReadRef(volatile RefCount *ptr) +{ __asm__ __volatile__("" ::: "memory"); return ptr->value; } +inline uint IncrementRef(volatile RefCount *ptr) +{ return xaddl(&ptr->value, 1)+1; } +inline uint DecrementRef(volatile RefCount *ptr) +{ return xaddl(&ptr->value, -1)-1; } +inline uint ExchangeRef(volatile RefCount *ptr, uint newval) +{ + int ret; + __asm__ __volatile__("lock; xchgl %0,(%1)" + : "=r" (ret) + : "r" (&ptr->value), "0" (newval) + : "memory"); + return ret; +} +inline uint CompExchangeRef(volatile RefCount *ptr, uint oldval, uint newval) +{ + int ret; + __asm__ __volatile__("lock; cmpxchgl %2,(%1)" + : "=a" (ret) + : "r" (&ptr->value), "r" (newval), "0" (oldval) + : "memory"); + return ret; +} + +inline int ExchangeInt(volatile int *dest, int newval) +{ + int ret; + __asm__ __volatile__("lock; xchgl %0,(%1)" + : "=r" (ret) + : "r" (dest), "0" (newval) + : "memory"); + return ret; +} +inline void *ExchangePtr(XchgPtr *dest, void *newval) +{ + void *ret; + __asm__ __volatile__( +#ifdef __i386__ + "lock; xchgl %0,(%1)" +#else + "lock; xchgq %0,(%1)" +#endif + : "=r" (ret) + : "r" (dest), "0" (newval) + : "memory" + ); + return ret; +} +inline int CompExchangeInt(volatile int *dest, int oldval, int newval) +{ + int ret; + __asm__ __volatile__("lock; cmpxchgl %2,(%1)" + : "=a" (ret) + : "r" (dest), "r" (newval), "0" (oldval) + : "memory"); + return ret; +} +inline void *CompExchangePtr(XchgPtr *dest, void *oldval, void *newval) +{ + void *ret; + __asm__ __volatile__( +#ifdef __i386__ + "lock; cmpxchgl %2,(%1)" +#else + "lock; cmpxchgq %2,(%1)" +#endif + : "=a" (ret) + : "r" (dest), "r" (newval), "0" (oldval) + : "memory" + ); + return ret; +} + +#elif defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +static_assert(sizeof(LONG)==sizeof(uint), "sizeof LONG does not match sizeof uint"); + +inline void InitRef(volatile RefCount *ptr, uint value) +{ ptr->value = value; } +inline uint ReadRef(volatile RefCount *ptr) +{ _ReadBarrier(); return ptr->value; } +inline uint IncrementRef(volatile RefCount *ptr) +{ + union { + volatile uint *u; + volatile LONG *l; + } u = { &ptr->value }; + return InterlockedIncrement(u.l); +} +inline uint DecrementRef(volatile RefCount *ptr) +{ + union { + volatile uint *u; + volatile LONG *l; + } u = { &ptr->value }; + return InterlockedDecrement(u.l); +} +inline uint ExchangeRef(volatile RefCount *ptr, uint newval) +{ + union { + volatile uint *i; + volatile LONG *l; + } u = { &ptr->value }; + return InterlockedExchange(u.l, newval); +} +inline uint CompExchangeRef(volatile RefCount *ptr, uint oldval, uint newval) +{ + union { + volatile uint *i; + volatile LONG *l; + } u = { &ptr->value }; + return InterlockedCompareExchange(u.l, newval, oldval); +} + +inline int ExchangeInt(volatile int *ptr, int newval) +{ + union { + volatile int *i; + volatile LONG *l; + } u = { ptr }; + return InterlockedExchange(u.l, newval); +} +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ + return InterlockedExchangePointer(ptr, newval); +} +inline int CompExchangeInt(volatile int *ptr, int oldval, int newval) +{ + union { + volatile int *i; + volatile LONG *l; + } u = { ptr }; + return InterlockedCompareExchange(u.l, newval, oldval); +} +inline void *CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval) +{ + return InterlockedCompareExchangePointer(ptr, newval, oldval); +} + +#else +#error "No atomic functions available on this platform!" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AL_ATOMIC_H */ diff --git a/include/bool.h b/include/bool.h new file mode 100644 index 00000000..6f714d09 --- /dev/null +++ b/include/bool.h @@ -0,0 +1,18 @@ +#ifndef AL_BOOL_H +#define AL_BOOL_H + +#ifdef HAVE_STDBOOL_H +#include <stdbool.h> +#endif + +#ifndef bool +#ifdef HAVE_C99_BOOL +#define bool _Bool +#else +#define bool int +#endif +#define false 0 +#define true 1 +#endif + +#endif /* AL_BOOL_H */ diff --git a/include/rwlock.h b/include/rwlock.h new file mode 100644 index 00000000..03482515 --- /dev/null +++ b/include/rwlock.h @@ -0,0 +1,30 @@ +#ifndef AL_RWLOCK_H +#define AL_RWLOCK_H + +#include "bool.h" +#include "atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + RefCount read_count; + RefCount write_count; + volatile int read_lock; + volatile int read_entry_lock; + volatile int write_lock; +} RWLock; +#define RWLOCK_STATIC_INITIALIZE { STATIC_REFCOUNT_INIT(0), STATIC_REFCOUNT_INIT(0), false, false, false } + +void RWLockInit(RWLock *lock); +void ReadLock(RWLock *lock); +void ReadUnlock(RWLock *lock); +void WriteLock(RWLock *lock); +void WriteUnlock(RWLock *lock); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_RWLOCK_H */ diff --git a/include/static_assert.h b/include/static_assert.h new file mode 100644 index 00000000..65283cf0 --- /dev/null +++ b/include/static_assert.h @@ -0,0 +1,17 @@ +#ifndef AL_STATIC_ASSERT_H +#define AL_STATIC_ASSERT_H + +#include <assert.h> + + +#ifndef static_assert +#ifdef HAVE_C11_STATIC_ASSERT +#define static_assert _Static_assert +#else +#define CTASTR2(_pre,_post) _pre##_post +#define CTASTR(_pre,_post) CTASTR2(_pre,_post) +#define static_assert(_cond, _msg) typedef struct { int CTASTR(static_assert_failed_at_line_,__LINE__) : !!(_cond); } CTASTR(static_assertion_,__COUNTER__) +#endif +#endif + +#endif /* AL_STATIC_ASSERT_H */ diff --git a/include/threads.h b/include/threads.h new file mode 100644 index 00000000..25a75625 --- /dev/null +++ b/include/threads.h @@ -0,0 +1,243 @@ +#ifndef AL_THREADS_H +#define AL_THREADS_H + +#include <time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + althrd_success = 0, + althrd_error, + althrd_nomem, + althrd_timedout, + althrd_busy +}; + +enum { + almtx_plain = 0, + almtx_recursive = 1, + almtx_timed = 2 +}; + +typedef int (*althrd_start_t)(void*); +typedef void (*altss_dtor_t)(void*); + + +#define AL_TIME_UTC 1 + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + + +#ifndef _TIMESPEC_DEFINED +#define _TIMESPEC_DEFINED +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +struct itimerspec { + struct timespec it_interval; + struct timespec it_value; +}; +#endif + +typedef DWORD althrd_t; +typedef CRITICAL_SECTION almtx_t; +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +typedef CONDITION_VARIABLE alcnd_t; +#else +typedef struct { void *Ptr; } alcnd_t; +#endif +typedef DWORD altss_t; +typedef LONG alonce_flag; + +#define AL_ONCE_FLAG_INIT 0 + +int althrd_sleep(const struct timespec *ts, struct timespec *rem); +void alcall_once(alonce_flag *once, void (*callback)(void)); + + +inline althrd_t althrd_current(void) +{ + return GetCurrentThreadId(); +} + +inline int althrd_equal(althrd_t thr0, althrd_t thr1) +{ + return thr0 == thr1; +} + +inline void althrd_exit(int res) +{ + ExitThread(res); +} + +inline void althrd_yield(void) +{ + SwitchToThread(); +} + + +inline int almtx_lock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + EnterCriticalSection(mtx); + return althrd_success; +} + +inline int almtx_unlock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + LeaveCriticalSection(mtx); + return althrd_success; +} + +inline int almtx_trylock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + if(!TryEnterCriticalSection(mtx)) + return althrd_busy; + return althrd_success; +} + + +inline void *altss_get(altss_t tss_id) +{ + return TlsGetValue(tss_id); +} + +inline int altss_set(altss_t tss_id, void *val) +{ + if(TlsSetValue(tss_id, val) == 0) + return althrd_error; + return althrd_success; +} + +#else + +#include <stdint.h> +#include <errno.h> +#include <pthread.h> + + +typedef pthread_t althrd_t; +typedef pthread_mutex_t almtx_t; +typedef pthread_cond_t alcnd_t; +typedef pthread_key_t altss_t; +typedef pthread_once_t alonce_flag; + +#define AL_ONCE_FLAG_INIT PTHREAD_ONCE_INIT + + +inline althrd_t althrd_current(void) +{ + return pthread_self(); +} + +inline int althrd_equal(althrd_t thr0, althrd_t thr1) +{ + return pthread_equal(thr0, thr1); +} + +inline void althrd_exit(int res) +{ + pthread_exit((void*)(intptr_t)res); +} + +inline void althrd_yield(void) +{ + sched_yield(); +} + +inline int althrd_sleep(const struct timespec *ts, struct timespec *rem) +{ + int ret = nanosleep(ts, rem); + if(ret != 0) + { + ret = ((errno==EINTR) ? -1 : -2); + errno = 0; + } + return ret; +} + + +inline int almtx_lock(almtx_t *mtx) +{ + if(pthread_mutex_lock(mtx) != 0) + return althrd_error; + return althrd_success; +} + +inline int almtx_unlock(almtx_t *mtx) +{ + if(pthread_mutex_unlock(mtx) != 0) + return althrd_error; + return althrd_success; +} + +inline int almtx_trylock(almtx_t *mtx) +{ + int ret = pthread_mutex_trylock(mtx); + switch(ret) + { + case 0: return althrd_success; + case EBUSY: return althrd_busy; + } + return althrd_error; +} + + +inline void *altss_get(altss_t tss_id) +{ + return pthread_getspecific(tss_id); +} + +inline int altss_set(altss_t tss_id, void *val) +{ + if(pthread_setspecific(tss_id, val) != 0) + return althrd_error; + return althrd_success; +} + + +inline void alcall_once(alonce_flag *once, void (*callback)(void)) +{ + pthread_once(once, callback); +} + +#endif + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg); +int althrd_detach(althrd_t thr); +int althrd_join(althrd_t thr, int *res); +void althrd_setname(althrd_t thr, const char *name); + +int almtx_init(almtx_t *mtx, int type); +void almtx_destroy(almtx_t *mtx); +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts); + +int alcnd_init(alcnd_t *cond); +int alcnd_signal(alcnd_t *cond); +int alcnd_broadcast(alcnd_t *cond); +int alcnd_wait(alcnd_t *cond, almtx_t *mtx); +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point); +void alcnd_destroy(alcnd_t *cond); + +int altss_create(altss_t *tss_id, altss_dtor_t callback); +void altss_delete(altss_t tss_id); + +int altimespec_get(struct timespec *ts, int base); + +void al_nssleep(time_t sec, long nsec); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_THREADS_H */ diff --git a/Alc/uintmap.h b/include/uintmap.h index 2c70f161..2c4c5e7a 100644 --- a/Alc/uintmap.h +++ b/include/uintmap.h @@ -4,6 +4,10 @@ #include "AL/al.h" #include "rwlock.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct UIntMap { struct { ALuint key; @@ -14,7 +18,8 @@ typedef struct UIntMap { ALsizei limit; RWLock lock; } UIntMap; -extern UIntMap TlsDestructor; +#define UINTMAP_STATIC_INITIALIZE_N(_n) { NULL, 0, 0, (_n), RWLOCK_STATIC_INITIALIZE } +#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(~0) void InitUIntMap(UIntMap *map, ALsizei limit); void ResetUIntMap(UIntMap *map); @@ -31,4 +36,8 @@ inline void LockUIntMapWrite(UIntMap *map) inline void UnlockUIntMapWrite(UIntMap *map) { WriteUnlock(&map->lock); } +#ifdef __cplusplus +} +#endif + #endif /* AL_UINTMAP_H */ diff --git a/utils/makehrtf.c b/utils/makehrtf.c index f9da9429..7c836b33 100644 --- a/utils/makehrtf.c +++ b/utils/makehrtf.c @@ -58,7 +58,6 @@ * 1999 */ -// Needed for 64-bit unsigned integer. #include "config.h" #include <stdio.h> @@ -67,9 +66,13 @@ #include <string.h> #include <ctype.h> #include <math.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif // Rely (if naively) on OpenAL's header for the types used for serialization. #include "AL/al.h" +#include "AL/alext.h" #ifndef M_PI #define M_PI (3.14159265358979323846) @@ -231,23 +234,12 @@ enum OutputFormatT { // Unsigned integer type. typedef unsigned int uint; -// Serialization types. The trailing digit indicates the number of bytes. -typedef ALubyte uint1; - -typedef ALint int4; -typedef ALuint uint4; +// Serialization types. The trailing digit indicates the number of bits. +typedef ALubyte uint8; -#if defined (HAVE_STDINT_H) -#include <stdint.h> - -typedef uint64_t uint8; -#elif defined (HAVE___INT64) -typedef unsigned __int64 uint8; -#elif (SIZEOF_LONG == 8) -typedef unsigned long uint8; -#elif (SIZEOF_LONG_LONG == 8) -typedef unsigned long long uint8; -#endif +typedef ALint int32; +typedef ALuint uint32; +typedef ALuint64SOFT uint64; typedef enum ByteOrderT ByteOrderT; typedef enum SourceFormatT SourceFormatT; @@ -779,21 +771,22 @@ static int HpTpdfDither (const double in, int * hpHist) { } // Allocates an array of doubles. -static double * CreateArray (const size_t n) { - double * a = NULL; - - a = (double *) calloc (n, sizeof (double)); - if (a == NULL) { - fprintf (stderr, "Error: Out of memory.\n"); - exit (-1); - } - return (a); +static double *CreateArray(const size_t n) +{ + double *a; + + a = calloc(n, sizeof(double)); + if(a == NULL) + { + fprintf(stderr, "Error: Out of memory.\n"); + exit(-1); + } + return a; } // Frees an array of doubles. -static void DestroyArray (const double * a) { - free ((void *) a); -} +static void DestroyArray(double *a) +{ free(a); } // Complex number routines. All outputs must be non-NULL. @@ -1220,6 +1213,9 @@ static void ResamplerRun (ResamplerT * rs, const uint inN, const double * in, co double r; uint j_f, j_s; + if (outN == 0) + return; + // Handle in-place operation. if (in == out) work = CreateArray (outN); @@ -1252,9 +1248,9 @@ static void ResamplerRun (ResamplerT * rs, const uint inN, const double * in, co // Read a binary value of the specified byte order and byte size from a file, // storing it as a 32-bit unsigned integer. -static int ReadBin4 (FILE * fp, const char * filename, const ByteOrderT order, const uint bytes, uint4 * out) { - uint1 in [4]; - uint4 accum; +static int ReadBin4 (FILE * fp, const char * filename, const ByteOrderT order, const uint bytes, uint32 * out) { + uint8 in [4]; + uint32 accum; uint i; if (fread (in, 1, bytes, fp) != bytes) { @@ -1280,9 +1276,9 @@ static int ReadBin4 (FILE * fp, const char * filename, const ByteOrderT order, c // Read a binary value of the specified byte order from a file, storing it as // a 64-bit unsigned integer. -static int ReadBin8 (FILE * fp, const char * filename, const ByteOrderT order, uint8 * out) { - uint1 in [8]; - uint8 accum; +static int ReadBin8 (FILE * fp, const char * filename, const ByteOrderT order, uint64 * out) { + uint8 in [8]; + uint64 accum; uint i; if (fread (in, 1, 8, fp) != 8) { @@ -1321,8 +1317,8 @@ static int WriteAscii (const char * out, FILE * fp, const char * filename) { // Write a binary value of the given byte order and byte size to a file, // loading it from a 32-bit unsigned integer. -static int WriteBin4 (const ByteOrderT order, const uint bytes, const uint4 in, FILE * fp, const char * filename) { - uint1 out [4]; +static int WriteBin4 (const ByteOrderT order, const uint bytes, const uint32 in, FILE * fp, const char * filename) { + uint8 out [4]; uint i; switch (order) { @@ -1352,12 +1348,12 @@ static int WriteBin4 (const ByteOrderT order, const uint bytes, const uint4 in, */ static int ReadBinAsDouble (FILE * fp, const char * filename, const ByteOrderT order, const ElementTypeT type, const uint bytes, const int bits, double * out) { union { - uint4 ui; - int4 i; + uint32 ui; + int32 i; float f; } v4; union { - uint8 ui; + uint64 ui; double f; } v8; @@ -1419,8 +1415,8 @@ static int ReadAsciiAsDouble (TokenReaderT * tr, const char * filename, const El // Read the RIFF/RIFX WAVE format chunk from a file, validating it against // the source parameters and data set metrics. static int ReadWaveFormat (FILE * fp, const ByteOrderT order, const uint hrirRate, SourceRefT * src) { - uint4 fourCC, chunkSize; - uint4 format, channels, rate, dummy, block, size, bits; + uint32 fourCC, chunkSize; + uint32 format, channels, rate, dummy, block, size, bits; chunkSize = 0; do { @@ -1522,7 +1518,7 @@ static int ReadWaveData (FILE * fp, const SourceRefT * src, const ByteOrderT ord // Read the RIFF/RIFX WAVE list or data chunk, converting all elements to // doubles. static int ReadWaveList (FILE * fp, const SourceRefT * src, const ByteOrderT order, const uint n, double * hrir) { - uint4 fourCC, chunkSize, listSize, count; + uint32 fourCC, chunkSize, listSize, count; uint block, skip, offset, i; double lastSample; @@ -1608,7 +1604,7 @@ static int ReadWaveList (FILE * fp, const SourceRefT * src, const ByteOrderT ord // Load a source HRIR from a RIFF/RIFX WAVE file. static int LoadWaveSource (FILE * fp, SourceRefT * src, const uint hrirRate, const uint n, double * hrir) { - uint4 fourCC, dummy; + uint32 fourCC, dummy; ByteOrderT order; if ((! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) || @@ -2061,14 +2057,14 @@ static int StoreMhr (const HrirDataT * hData, const char * filename) { } if (! WriteAscii (MHR_FORMAT, fp, filename)) return (0); - if (! WriteBin4 (BO_LITTLE, 4, (uint4) hData -> mIrRate, fp, filename)) + if (! WriteBin4 (BO_LITTLE, 4, (uint32) hData -> mIrRate, fp, filename)) return (0); - if (! WriteBin4 (BO_LITTLE, 1, (uint4) hData -> mIrPoints, fp, filename)) + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mIrPoints, fp, filename)) return (0); - if (! WriteBin4 (BO_LITTLE, 1, (uint4) hData -> mEvCount, fp, filename)) + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mEvCount, fp, filename)) return (0); for (e = 0; e < hData -> mEvCount; e ++) { - if (! WriteBin4 (BO_LITTLE, 1, (uint4) hData -> mAzCount [e], fp, filename)) + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mAzCount [e], fp, filename)) return (0); } step = hData -> mIrSize; @@ -2079,13 +2075,13 @@ static int StoreMhr (const HrirDataT * hData, const char * filename) { hpHist = 0; for (i = 0; i < n; i ++) { v = HpTpdfDither (32767.0 * hData -> mHrirs [j + i], & hpHist); - if (! WriteBin4 (BO_LITTLE, 2, (uint4) v, fp, filename)) + if (! WriteBin4 (BO_LITTLE, 2, (uint32) v, fp, filename)) return (0); } } for (j = 0; j < hData -> mIrCount; j ++) { v = (int) fmin (round (hData -> mIrRate * hData -> mHrtds [j]), MAX_HRTD); - if (! WriteBin4 (BO_LITTLE, 1, (uint4) v, fp, filename)) + if (! WriteBin4 (BO_LITTLE, 1, (uint32) v, fp, filename)) return (0); } fclose (fp); |