Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf9c36408a | |||
| 12db67f548 | |||
| e25ba747e6 | |||
| 11b44d29e6 | |||
| 75841642bf | |||
| 1ab082caaf | |||
| 82a990e08e | |||
| 5ab4e584ee | |||
| 4ec67e6226 | |||
| 21b71b2fd2 | |||
| f91445029d | |||
| fea74124c8 | |||
| 0135ddc2e5 | |||
| 5f7268c0cc | |||
| c484935542 | |||
| ff696bc1fe | |||
| 8fa3f6da64 | |||
| 249afde5f9 | |||
| a535169bbd | |||
| 22d77b87a3 | |||
| 67ab9ec466 | |||
| 0da55fd912 | |||
| 5048322fff | |||
| f313f9c117 | |||
| d6326c1791 | |||
| d050af7eeb | |||
| 2be4c93f9f | |||
| 09826cc684 | |||
| e4e240fa9b | |||
| 6be752a9b1 | |||
| faefa1d554 | |||
| 56a33ef955 | |||
| dadf7a4cf2 | |||
| 3af2ff7b25 | |||
| 72ce0d1e9c | |||
| 3cd4cfe73d | |||
| 74139c914d | |||
| cb83f48105 | |||
| a14f651034 | |||
| 958fa34272 | |||
| af626fdded | |||
| 7daefd4e77 | |||
| 32bda7b94c | |||
| 77e317609b | |||
| ce9222b686 | |||
| 8a0d1e5191 | |||
| d874b6bb27 | |||
| 00694826ef | |||
| 058d57ef03 | |||
| e8ce8924d1 | |||
| ec13cf6c9c | |||
| a69d608a1e | |||
| aefa11b6ad | |||
| cee2d226d2 | |||
| f08a7b341f | |||
| 15e6821147 | |||
| 3a16fed279 | |||
| ef7eced7a7 | |||
| a729007887 | |||
| 323162c49f | |||
| ea4379c5b7 | |||
| 6124f447cd | |||
| 47f1db36a7 | |||
| b88b57868a | |||
| 9fd7349220 | |||
| cfec20830b | |||
| ebee8da05c | |||
| 7cadbebe9f | |||
| e9a7218a06 | |||
| 8f2bbc434c | |||
| b13fead555 | |||
| 2f2d941edb | |||
| 740e7d979c | |||
| 464a7c0545 | |||
| 55c329b462 | |||
| 5d5eff7502 | |||
| 4b7cbb50ab | |||
| be552a35e6 | |||
| 1deb8b6160 | |||
| c465718ddd | |||
| d1bb04d588 | |||
| d70a98fe03 | |||
| 9d01ac440d | |||
| 44a940d8d6 | |||
| e07166e93c | |||
| d1077795de | |||
| 31b02e044f | |||
| 552d3a85af | |||
| 6fcbb7c738 | |||
| e09468cdca | |||
| 36edd80073 | |||
| 0a361fa9e2 | |||
| 2b013fc54e | |||
| e3a825b37c | |||
| 9fc01934c2 | |||
| 879b79740f | |||
| e18f7ca3e4 | |||
| d4f3490a88 | |||
| 1e8feeff03 | |||
| a35b9bbd3e | |||
| ba0644254c | |||
| 39e4756b37 | |||
| b4aea294c3 | |||
| 10ff6cba9c |
@@ -54,57 +54,52 @@
|
||||
struct BackendInfo {
|
||||
const char *name;
|
||||
ALCbackendFactory* (*getFactory)(void);
|
||||
ALCboolean (*Init)(BackendFuncs*);
|
||||
void (*Deinit)(void);
|
||||
void (*Probe)(enum DevProbe);
|
||||
BackendFuncs Funcs;
|
||||
};
|
||||
|
||||
#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
static struct BackendInfo BackendList[] = {
|
||||
#ifdef HAVE_JACK
|
||||
{ "jack", ALCjackBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "jack", ALCjackBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_PULSEAUDIO
|
||||
{ "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "pulse", ALCpulseBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
{ "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "alsa", ALCalsaBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_COREAUDIO
|
||||
{ "core", ALCcoreAudioBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "core", ALCcoreAudioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
{ "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "oss", ALCossBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_SOLARIS
|
||||
{ "solaris", ALCsolarisBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "solaris", ALCsolarisBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_SNDIO
|
||||
{ "sndio", ALCsndioBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "sndio", ALCsndioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_QSA
|
||||
{ "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
|
||||
{ "qsa", ALCqsaBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_MMDEVAPI
|
||||
{ "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "mmdevapi", ALCmmdevBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_DSOUND
|
||||
{ "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "dsound", ALCdsoundBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_WINMM
|
||||
{ "winmm", ALCwinmmBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "winmm", ALCwinmmBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_PORTAUDIO
|
||||
{ "port", ALCportBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "port", ALCportBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OPENSL
|
||||
{ "opensl", ALCopenslBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "opensl", ALCopenslBackendFactory_getFactory },
|
||||
#endif
|
||||
|
||||
{ "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "null", ALCnullBackendFactory_getFactory },
|
||||
#ifdef HAVE_WAVE
|
||||
{ "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
|
||||
{ "wave", ALCwaveBackendFactory_getFactory },
|
||||
#endif
|
||||
};
|
||||
static ALsizei BackendListSize = COUNTOF(BackendList);
|
||||
@@ -1091,43 +1086,20 @@ static void alc_initconfig(void)
|
||||
|
||||
for(i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++)
|
||||
{
|
||||
if(BackendList[i].getFactory)
|
||||
{
|
||||
ALCbackendFactory *factory = BackendList[i].getFactory();
|
||||
if(!V0(factory,init)())
|
||||
{
|
||||
WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
|
||||
if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
|
||||
{
|
||||
PlaybackBackend = BackendList[i];
|
||||
TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
|
||||
}
|
||||
if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
|
||||
{
|
||||
CaptureBackend = BackendList[i];
|
||||
TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!BackendList[i].Init(&BackendList[i].Funcs))
|
||||
ALCbackendFactory *factory = BackendList[i].getFactory();
|
||||
if(!V0(factory,init)())
|
||||
{
|
||||
WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
|
||||
if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name)
|
||||
if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
|
||||
{
|
||||
PlaybackBackend = BackendList[i];
|
||||
TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
|
||||
}
|
||||
if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name)
|
||||
if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
|
||||
{
|
||||
CaptureBackend = BackendList[i];
|
||||
TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
|
||||
@@ -1241,7 +1213,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved))
|
||||
pthread_setspecific(gJVMThreadKey, env);
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1299,13 +1270,8 @@ static void alc_deinit(void)
|
||||
|
||||
for(i = 0;i < BackendListSize;i++)
|
||||
{
|
||||
if(!BackendList[i].getFactory)
|
||||
BackendList[i].Deinit();
|
||||
else
|
||||
{
|
||||
ALCbackendFactory *factory = BackendList[i].getFactory();
|
||||
V0(factory,deinit)();
|
||||
}
|
||||
ALCbackendFactory *factory = BackendList[i].getFactory();
|
||||
V0(factory,deinit)();
|
||||
}
|
||||
{
|
||||
ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
|
||||
@@ -1321,18 +1287,16 @@ static void alc_deinit(void)
|
||||
************************************************/
|
||||
static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum DevProbe type)
|
||||
{
|
||||
ALCbackendFactory *factory;
|
||||
|
||||
DO_INITCONFIG();
|
||||
|
||||
LockLists();
|
||||
alstr_clear(list);
|
||||
|
||||
if(backendinfo->Probe)
|
||||
backendinfo->Probe(type);
|
||||
else if(backendinfo->getFactory)
|
||||
{
|
||||
ALCbackendFactory *factory = backendinfo->getFactory();
|
||||
V(factory,probe)(type);
|
||||
}
|
||||
factory = backendinfo->getFactory();
|
||||
V(factory,probe)(type);
|
||||
|
||||
UnlockLists();
|
||||
}
|
||||
static void ProbeAllDevicesList(void)
|
||||
@@ -1777,7 +1741,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
ALCsizei hrtf_id = -1;
|
||||
ALCcontext *context;
|
||||
ALCuint oldFreq;
|
||||
FPUCtl oldMode;
|
||||
size_t size;
|
||||
ALCsizei i;
|
||||
int val;
|
||||
@@ -2222,6 +2185,39 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
|
||||
device->AuxiliaryEffectSlotMax, device->NumAuxSends);
|
||||
|
||||
device->DitherDepth = 0.0f;
|
||||
if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "dither", 1))
|
||||
{
|
||||
ALint depth = 0;
|
||||
ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "dither-depth", &depth);
|
||||
if(depth <= 0)
|
||||
{
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
depth = 8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
depth = 16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(depth > 24)
|
||||
depth = 24;
|
||||
device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f;
|
||||
}
|
||||
if(!(device->DitherDepth > 0.0f))
|
||||
TRACE("Dithering disabled\n");
|
||||
else
|
||||
TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
|
||||
device->DitherDepth);
|
||||
|
||||
if(ConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "output-limiter", &val))
|
||||
gainLimiter = val ? ALC_TRUE : ALC_FALSE;
|
||||
/* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and
|
||||
@@ -2247,25 +2243,25 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
* allocated with the appropriate size.
|
||||
*/
|
||||
update_failed = AL_FALSE;
|
||||
SetMixerFPUMode(&oldMode);
|
||||
if(device->DefaultSlot)
|
||||
{
|
||||
ALeffectslot *slot = device->DefaultSlot;
|
||||
ALeffectState *state = slot->Effect.State;
|
||||
|
||||
state->OutBuffer = device->Dry.Buffer;
|
||||
state->OutChannels = device->Dry.NumChannels;
|
||||
if(V(state,deviceUpdate)(device) == AL_FALSE)
|
||||
update_failed = AL_TRUE;
|
||||
else
|
||||
UpdateEffectSlotProps(slot);
|
||||
}
|
||||
|
||||
START_MIXER_MODE();
|
||||
context = ATOMIC_LOAD_SEQ(&device->ContextList);
|
||||
while(context)
|
||||
{
|
||||
ALsizei pos;
|
||||
|
||||
if(context->DefaultSlot)
|
||||
{
|
||||
ALeffectslot *slot = context->DefaultSlot;
|
||||
ALeffectState *state = slot->Effect.State;
|
||||
|
||||
state->OutBuffer = device->Dry.Buffer;
|
||||
state->OutChannels = device->Dry.NumChannels;
|
||||
if(V(state,deviceUpdate)(device) == AL_FALSE)
|
||||
update_failed = AL_TRUE;
|
||||
else
|
||||
UpdateEffectSlotProps(slot);
|
||||
}
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
LockUIntMapRead(&context->EffectSlotMap);
|
||||
for(pos = 0;pos < context->EffectSlotMap.size;pos++)
|
||||
@@ -2362,7 +2358,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
|
||||
context = context->next;
|
||||
}
|
||||
RestoreFPUMode(&oldMode);
|
||||
END_MIXER_MODE();
|
||||
if(update_failed)
|
||||
return ALC_INVALID_DEVICE;
|
||||
|
||||
@@ -2393,12 +2389,6 @@ static ALCvoid FreeDevice(ALCdevice *device)
|
||||
|
||||
almtx_destroy(&device->BackendLock);
|
||||
|
||||
if(device->DefaultSlot)
|
||||
{
|
||||
DeinitEffectSlot(device->DefaultSlot);
|
||||
device->DefaultSlot = NULL;
|
||||
}
|
||||
|
||||
if(device->BufferMap.size > 0)
|
||||
{
|
||||
WARN("(%p) Deleting %d Buffer%s\n", device, device->BufferMap.size,
|
||||
@@ -2559,8 +2549,17 @@ static ALvoid InitContext(ALCcontext *Context)
|
||||
InitUIntMap(&Context->SourceMap, Context->Device->SourcesMax);
|
||||
InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
|
||||
|
||||
auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray));
|
||||
auxslots->count = 0;
|
||||
if(Context->DefaultSlot)
|
||||
{
|
||||
auxslots = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, 1));
|
||||
auxslots->count = 1;
|
||||
auxslots->slot[0] = Context->DefaultSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray));
|
||||
auxslots->count = 0;
|
||||
}
|
||||
ATOMIC_INIT(&Context->ActiveAuxSlots, auxslots);
|
||||
|
||||
//Set globals
|
||||
@@ -2590,6 +2589,12 @@ static void FreeContext(ALCcontext *context)
|
||||
|
||||
TRACE("%p\n", context);
|
||||
|
||||
if(context->DefaultSlot)
|
||||
{
|
||||
DeinitEffectSlot(context->DefaultSlot);
|
||||
context->DefaultSlot = NULL;
|
||||
}
|
||||
|
||||
auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, NULL, almemory_order_relaxed);
|
||||
al_free(auxslots);
|
||||
|
||||
@@ -3598,7 +3603,10 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
|
||||
|
||||
ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR);
|
||||
|
||||
ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
|
||||
if(device->Type == Playback && DefaultEffect.type != AL_EFFECT_NULL)
|
||||
ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)+sizeof(ALeffectslot));
|
||||
else
|
||||
ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
|
||||
if(!ALContext)
|
||||
{
|
||||
almtx_unlock(&device->BackendLock);
|
||||
@@ -3610,6 +3618,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
|
||||
|
||||
InitRef(&ALContext->ref, 1);
|
||||
ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
|
||||
ALContext->DefaultSlot = NULL;
|
||||
|
||||
ALContext->Voices = NULL;
|
||||
ALContext->VoiceCount = 0;
|
||||
@@ -3621,9 +3630,6 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
|
||||
{
|
||||
almtx_unlock(&device->BackendLock);
|
||||
|
||||
al_free(ALContext->Voices);
|
||||
ALContext->Voices = NULL;
|
||||
|
||||
al_free(ALContext);
|
||||
ALContext = NULL;
|
||||
|
||||
@@ -3639,6 +3645,18 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
|
||||
}
|
||||
AllocateVoices(ALContext, 256, device->NumAuxSends);
|
||||
|
||||
if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback)
|
||||
{
|
||||
ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener));
|
||||
if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR)
|
||||
aluInitEffectPanning(ALContext->DefaultSlot);
|
||||
else
|
||||
{
|
||||
ALContext->DefaultSlot = NULL;
|
||||
ERR("Failed to initialize the default effect slot\n");
|
||||
}
|
||||
}
|
||||
|
||||
ALCdevice_IncRef(ALContext->Device);
|
||||
InitContext(ALContext);
|
||||
|
||||
@@ -3666,6 +3684,14 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
|
||||
}
|
||||
almtx_unlock(&device->BackendLock);
|
||||
|
||||
if(ALContext->DefaultSlot)
|
||||
{
|
||||
if(InitializeEffect(device, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR)
|
||||
UpdateEffectSlotProps(ALContext->DefaultSlot);
|
||||
else
|
||||
ERR("Failed to initialize the default effect\n");
|
||||
}
|
||||
|
||||
ALCdevice_DecRef(device);
|
||||
|
||||
TRACE("Created context %p\n", ALContext);
|
||||
@@ -3801,6 +3827,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
|
||||
*/
|
||||
ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
{
|
||||
ALCbackendFactory *factory;
|
||||
const ALCchar *fmt;
|
||||
ALCdevice *device;
|
||||
ALCenum err;
|
||||
@@ -3826,7 +3853,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
))
|
||||
deviceName = NULL;
|
||||
|
||||
device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot));
|
||||
device = al_calloc(16, sizeof(ALCdevice));
|
||||
if(!device)
|
||||
{
|
||||
alcSetError(NULL, ALC_OUT_OF_MEMORY);
|
||||
@@ -3887,14 +3914,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
device->NumUpdates = 3;
|
||||
device->UpdateSize = 1024;
|
||||
|
||||
if(!PlaybackBackend.getFactory)
|
||||
device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs,
|
||||
ALCbackend_Playback);
|
||||
else
|
||||
{
|
||||
ALCbackendFactory *factory = PlaybackBackend.getFactory();
|
||||
device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
|
||||
}
|
||||
factory = PlaybackBackend.getFactory();
|
||||
device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
|
||||
if(!device->Backend)
|
||||
{
|
||||
al_free(device);
|
||||
@@ -4026,32 +4047,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
ERR("Unsupported ambi-format: %s\n", fmt);
|
||||
}
|
||||
|
||||
device->DitherEnabled = GetConfigValueBool(
|
||||
alstr_get_cstr(device->DeviceName), NULL, "dither", 1
|
||||
);
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
if(DefaultEffect.type != AL_EFFECT_NULL)
|
||||
{
|
||||
device->DefaultSlot = (ALeffectslot*)device->_slot_mem;
|
||||
if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR)
|
||||
{
|
||||
device->DefaultSlot = NULL;
|
||||
ERR("Failed to initialize the default effect slot\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
aluInitEffectPanning(device->DefaultSlot);
|
||||
if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR)
|
||||
{
|
||||
DeinitEffectSlot(device->DefaultSlot);
|
||||
device->DefaultSlot = NULL;
|
||||
ERR("Failed to initialize the default effect\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
|
||||
do {
|
||||
@@ -4126,6 +4123,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
|
||||
************************************************/
|
||||
ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
|
||||
{
|
||||
ALCbackendFactory *factory;
|
||||
ALCdevice *device = NULL;
|
||||
ALCenum err;
|
||||
ALCsizei i;
|
||||
@@ -4183,14 +4181,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
|
||||
device->ChannelDelay[i].Buffer = NULL;
|
||||
}
|
||||
|
||||
if(!CaptureBackend.getFactory)
|
||||
device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs,
|
||||
ALCbackend_Capture);
|
||||
else
|
||||
{
|
||||
ALCbackendFactory *factory = CaptureBackend.getFactory();
|
||||
device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
|
||||
}
|
||||
factory = CaptureBackend.getFactory();
|
||||
device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
|
||||
if(!device->Backend)
|
||||
{
|
||||
al_free(device);
|
||||
@@ -4450,8 +4442,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
|
||||
// Open the "backend"
|
||||
V(device->Backend,open)("Loopback");
|
||||
|
||||
device->DitherEnabled = GetConfigValueBool(NULL, NULL, "dither", 1);
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
{
|
||||
|
||||
@@ -142,11 +142,11 @@ static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
|
||||
|
||||
/* Prior to VS2013, MSVC lacks the round() family of functions. */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
static long lroundf(float val)
|
||||
static float roundf(float val)
|
||||
{
|
||||
if(val < 0.0)
|
||||
return fastf2i(ceilf(val-0.5f));
|
||||
return fastf2i(floorf(val+0.5f));
|
||||
if(val < 0.0f)
|
||||
return ceilf(val-0.5f);
|
||||
return floorf(val+0.5f);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -214,7 +214,7 @@ static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
|
||||
* modified for use with an interpolated increment for buttery-smooth pitch
|
||||
* changes.
|
||||
*/
|
||||
static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
|
||||
ALboolean BsincPrepare(const ALuint increment, BsincState *state)
|
||||
{
|
||||
static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
|
||||
static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
|
||||
@@ -231,7 +231,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
|
||||
{ 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
|
||||
};
|
||||
ALfloat sf;
|
||||
ALuint si, pi;
|
||||
ALsizei si, pi;
|
||||
ALboolean uncut = AL_TRUE;
|
||||
|
||||
if(increment > FRACTIONONE)
|
||||
@@ -249,7 +249,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
|
||||
else
|
||||
{
|
||||
sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
|
||||
si = fastf2u(sf);
|
||||
si = fastf2i(sf);
|
||||
/* The interpolation factor is fit to this diagonally-symmetric
|
||||
* curve to reduce the transition ripple caused by interpolating
|
||||
* different scales of the sinc function.
|
||||
@@ -1030,7 +1030,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *p
|
||||
{
|
||||
SendSlots[i] = props->Send[i].Slot;
|
||||
if(!SendSlots[i] && i == 0)
|
||||
SendSlots[i] = Device->DefaultSlot;
|
||||
SendSlots[i] = ALContext->DefaultSlot;
|
||||
if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
|
||||
{
|
||||
SendSlots[i] = NULL;
|
||||
@@ -1100,7 +1100,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *prop
|
||||
{
|
||||
SendSlots[i] = props->Send[i].Slot;
|
||||
if(!SendSlots[i] && i == 0)
|
||||
SendSlots[i] = Device->DefaultSlot;
|
||||
SendSlots[i] = ALContext->DefaultSlot;
|
||||
if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
|
||||
{
|
||||
SendSlots[i] = NULL;
|
||||
@@ -1526,11 +1526,37 @@ static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp
|
||||
}
|
||||
}
|
||||
|
||||
static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
|
||||
const ALfloat quant_scale, const ALsizei SamplesToDo,
|
||||
const ALsizei numchans)
|
||||
{
|
||||
const ALfloat invscale = 1.0f / quant_scale;
|
||||
ALuint seed = *dither_seed;
|
||||
ALsizei c, i;
|
||||
|
||||
/* NOTE: Non-dithered conversions have unused extra parameters. */
|
||||
static inline ALfloat aluF2F(ALfloat val, ...)
|
||||
/* Dithering. Step 1, generate whitenoise (uniform distribution of random
|
||||
* values between -1 and +1). Step 2 is to add the noise to the samples,
|
||||
* before rounding and after scaling up to the desired quantization depth.
|
||||
*/
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
ALfloat *restrict samples = Samples[c];
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat val = samples[i] * quant_scale;
|
||||
ALuint rng0 = dither_rng(&seed);
|
||||
ALuint rng1 = dither_rng(&seed);
|
||||
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
||||
samples[i] = roundf(val) * invscale;
|
||||
}
|
||||
}
|
||||
*dither_seed = seed;
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Conv_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
static inline ALint aluF2I(ALfloat val, ...)
|
||||
static inline ALint Conv_ALint(ALfloat val)
|
||||
{
|
||||
/* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
|
||||
* integer range normalized floats can be safely converted to (a bit of the
|
||||
@@ -1538,87 +1564,58 @@ static inline ALint aluF2I(ALfloat val, ...)
|
||||
*/
|
||||
return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
|
||||
}
|
||||
static inline ALshort aluF2S(ALfloat val, ...)
|
||||
static inline ALshort Conv_ALshort(ALfloat val)
|
||||
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
|
||||
static inline ALbyte aluF2B(ALfloat val, ...)
|
||||
static inline ALbyte Conv_ALbyte(ALfloat val)
|
||||
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
|
||||
|
||||
/* Dithered conversion functions. Only applies to 8- and 16-bit output for now,
|
||||
* as 32-bit int and float are at the limits of the rendered sample depth. This
|
||||
* can change if the dithering bit depth becomes configurable (effectively
|
||||
* quantizing to a lower bit depth than the output is capable of).
|
||||
*/
|
||||
static inline ALshort aluF2SDithered(ALfloat val, const ALfloat dither_val)
|
||||
{
|
||||
val = val*32768.0f + dither_val;
|
||||
return lroundf(clampf(val, -32768.0f, 32767.0f));
|
||||
}
|
||||
static inline ALbyte aluF2BDithered(ALfloat val, const ALfloat dither_val)
|
||||
{
|
||||
val = val*128.0f + dither_val;
|
||||
return lroundf(clampf(val, -128.0f, 127.0f));
|
||||
}
|
||||
|
||||
/* Define unsigned output variations. */
|
||||
#define DECL_TEMPLATE(T, Name, func, O) \
|
||||
static inline T Name(ALfloat val, const ALfloat dither_val) \
|
||||
{ return func(val, dither_val)+O; }
|
||||
#define DECL_TEMPLATE(T, func, O) \
|
||||
static inline T Conv_##T(ALfloat val) { return func(val)+O; }
|
||||
|
||||
DECL_TEMPLATE(ALubyte, aluF2UB, aluF2B, 128)
|
||||
DECL_TEMPLATE(ALushort, aluF2US, aluF2S, 32768)
|
||||
DECL_TEMPLATE(ALuint, aluF2UI, aluF2I, 2147483648u)
|
||||
DECL_TEMPLATE(ALubyte, aluF2UBDithered, aluF2BDithered, 128)
|
||||
DECL_TEMPLATE(ALushort, aluF2USDithered, aluF2SDithered, 32768)
|
||||
DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
|
||||
DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
|
||||
DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
#define DECL_TEMPLATE(T, D, func) \
|
||||
static void Write##T##D(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
|
||||
const ALfloat *restrict DitherValues, \
|
||||
ALsizei SamplesToDo, ALsizei numchans) \
|
||||
#define DECL_TEMPLATE(T, A) \
|
||||
static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
|
||||
ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans) \
|
||||
{ \
|
||||
ALsizei i, j; \
|
||||
for(j = 0;j < numchans;j++) \
|
||||
{ \
|
||||
const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
|
||||
T *restrict out = (T*)OutBuffer + j; \
|
||||
T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
|
||||
\
|
||||
for(i = 0;i < SamplesToDo;i++) \
|
||||
out[i*numchans] = func(in[i], DitherValues[i]); \
|
||||
out[i*numchans] = Conv_##T(in[i]); \
|
||||
} \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALfloat, /*no dither*/, aluF2F)
|
||||
DECL_TEMPLATE(ALuint, /*no dither*/, aluF2UI)
|
||||
DECL_TEMPLATE(ALint, /*no dither*/, aluF2I)
|
||||
DECL_TEMPLATE(ALushort, /*no dither*/, aluF2US)
|
||||
DECL_TEMPLATE(ALshort, /*no dither*/, aluF2S)
|
||||
DECL_TEMPLATE(ALubyte, /*no dither*/, aluF2UB)
|
||||
DECL_TEMPLATE(ALbyte, /*no dither*/, aluF2B)
|
||||
|
||||
DECL_TEMPLATE(ALushort, _Dithered, aluF2USDithered)
|
||||
DECL_TEMPLATE(ALshort, _Dithered, aluF2SDithered)
|
||||
DECL_TEMPLATE(ALubyte, _Dithered, aluF2UBDithered)
|
||||
DECL_TEMPLATE(ALbyte, _Dithered, aluF2BDithered)
|
||||
DECL_TEMPLATE(ALfloat, F32)
|
||||
DECL_TEMPLATE(ALuint, UI32)
|
||||
DECL_TEMPLATE(ALint, I32)
|
||||
DECL_TEMPLATE(ALushort, UI16)
|
||||
DECL_TEMPLATE(ALshort, I16)
|
||||
DECL_TEMPLATE(ALubyte, UI8)
|
||||
DECL_TEMPLATE(ALbyte, I8)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
{
|
||||
ALsizei SamplesToDo;
|
||||
ALvoice **voice, **voice_end;
|
||||
ALeffectslot *slot;
|
||||
ALsource *source;
|
||||
ALsizei SamplesDone;
|
||||
ALCcontext *ctx;
|
||||
FPUCtl oldMode;
|
||||
ALsizei i, c;
|
||||
|
||||
SetMixerFPUMode(&oldMode);
|
||||
|
||||
while(size > 0)
|
||||
START_MIXER_MODE();
|
||||
for(SamplesDone = 0;SamplesDone < NumSamples;)
|
||||
{
|
||||
SamplesToDo = mini(size, BUFFERSIZE);
|
||||
SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
|
||||
for(c = 0;c < device->Dry.NumChannels;c++)
|
||||
memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
|
||||
if(device->Dry.Buffer != device->FOAOut.Buffer)
|
||||
@@ -1630,13 +1627,6 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
|
||||
IncrementRef(&device->MixCount);
|
||||
|
||||
if((slot=device->DefaultSlot) != NULL)
|
||||
{
|
||||
CalcEffectSlotParams(device->DefaultSlot, device);
|
||||
for(c = 0;c < slot->NumChannels;c++)
|
||||
memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
|
||||
while(ctx)
|
||||
{
|
||||
@@ -1653,18 +1643,17 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
}
|
||||
|
||||
/* source processing */
|
||||
voice = ctx->Voices;
|
||||
voice_end = voice + ctx->VoiceCount;
|
||||
for(;voice != voice_end;++voice)
|
||||
for(i = 0;i < ctx->VoiceCount;i++)
|
||||
{
|
||||
source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
|
||||
if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
|
||||
(*voice)->Step > 0)
|
||||
ALvoice *voice = ctx->Voices[i];
|
||||
ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
|
||||
if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
|
||||
voice->Step > 0)
|
||||
{
|
||||
if(!MixSource(*voice, source, device, SamplesToDo))
|
||||
if(!MixSource(voice, source, device, SamplesToDo))
|
||||
{
|
||||
ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
|
||||
ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
|
||||
ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
|
||||
ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1681,14 +1670,6 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
ctx = ctx->next;
|
||||
}
|
||||
|
||||
if(device->DefaultSlot != NULL)
|
||||
{
|
||||
const ALeffectslot *slot = device->DefaultSlot;
|
||||
ALeffectState *state = slot->Params.EffectState;
|
||||
V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
|
||||
state->OutChannels);
|
||||
}
|
||||
|
||||
/* Increment the clock time. Every second's worth of samples is
|
||||
* converted and added to clock base so that large sample counts don't
|
||||
* overflow during conversion. This also guarantees an exact, stable
|
||||
@@ -1770,133 +1751,80 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer)
|
||||
if(OutBuffer)
|
||||
{
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
|
||||
ALsizei OutChannels = device->RealOut.NumChannels;
|
||||
struct Compressor *Limiter = device->Limiter;
|
||||
ALfloat *DitherValues;
|
||||
ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
|
||||
ALsizei Channels = device->RealOut.NumChannels;
|
||||
|
||||
/* Use NFCtrlData for temp value storage. */
|
||||
ApplyDistanceComp(OutBuffer, device->ChannelDelay, device->NFCtrlData,
|
||||
SamplesToDo, OutChannels);
|
||||
ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
|
||||
SamplesToDo, Channels);
|
||||
|
||||
if(Limiter)
|
||||
ApplyCompression(Limiter, OutChannels, SamplesToDo, OutBuffer);
|
||||
if(device->Limiter)
|
||||
ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
|
||||
|
||||
/* Dithering. Step 1, generate whitenoise (uniform distribution of
|
||||
* random values between -1 and +1). Use NFCtrlData for random
|
||||
* value storage. Step 2 is to add the noise to the samples, before
|
||||
* rounding and after scaling up to the desired quantization depth,
|
||||
* which occurs in the sample conversion stage.
|
||||
*/
|
||||
if(!device->DitherEnabled)
|
||||
memset(device->NFCtrlData, 0, SamplesToDo*sizeof(ALfloat));
|
||||
else
|
||||
{
|
||||
ALuint dither_seed = device->DitherSeed;
|
||||
ALsizei i;
|
||||
if(device->DitherDepth > 0.0f)
|
||||
ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
|
||||
Channels);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALuint rng0 = dither_rng(&dither_seed);
|
||||
ALuint rng1 = dither_rng(&dither_seed);
|
||||
device->NFCtrlData[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
||||
}
|
||||
device->DitherSeed = dither_seed;
|
||||
}
|
||||
DitherValues = device->NFCtrlData;
|
||||
|
||||
#define WRITE(T, D, a, b, c, d, e) do { \
|
||||
Write##T##D(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
|
||||
buffer = (T*)buffer + (d)*(e); \
|
||||
} while(0)
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
if(device->DitherEnabled)
|
||||
WRITE(ALbyte, _Dithered, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
else
|
||||
WRITE(ALbyte, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
if(device->DitherEnabled)
|
||||
WRITE(ALubyte, _Dithered, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
else
|
||||
WRITE(ALubyte, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
if(device->DitherEnabled)
|
||||
WRITE(ALshort, _Dithered, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
else
|
||||
WRITE(ALshort, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
if(device->DitherEnabled)
|
||||
WRITE(ALushort, _Dithered, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
else
|
||||
WRITE(ALushort, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
WRITE(ALint, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
WRITE(ALuint, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
WRITE(ALfloat, /*no dither*/, OutBuffer, buffer, DitherValues,
|
||||
SamplesToDo, OutChannels);
|
||||
WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
}
|
||||
#undef WRITE
|
||||
}
|
||||
|
||||
size -= SamplesToDo;
|
||||
SamplesDone += SamplesToDo;
|
||||
}
|
||||
|
||||
RestoreFPUMode(&oldMode);
|
||||
END_MIXER_MODE();
|
||||
}
|
||||
|
||||
|
||||
void aluHandleDisconnect(ALCdevice *device)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCcontext *ctx;
|
||||
|
||||
device->Connected = ALC_FALSE;
|
||||
|
||||
Context = ATOMIC_LOAD_SEQ(&device->ContextList);
|
||||
while(Context)
|
||||
ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
|
||||
while(ctx)
|
||||
{
|
||||
ALvoice **voice, **voice_end;
|
||||
|
||||
voice = Context->Voices;
|
||||
voice_end = voice + Context->VoiceCount;
|
||||
while(voice != voice_end)
|
||||
ALsizei i;
|
||||
for(i = 0;i < ctx->VoiceCount;i++)
|
||||
{
|
||||
ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
|
||||
almemory_order_acq_rel);
|
||||
ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
|
||||
ALvoice *voice = ctx->Voices[i];
|
||||
ALsource *source;
|
||||
|
||||
source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
|
||||
ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
|
||||
|
||||
if(source)
|
||||
{
|
||||
ALenum playing = AL_PLAYING;
|
||||
(void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
|
||||
}
|
||||
|
||||
voice++;
|
||||
}
|
||||
Context->VoiceCount = 0;
|
||||
ctx->VoiceCount = 0;
|
||||
|
||||
Context = Context->next;
|
||||
ctx = ctx->next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,157 +79,3 @@ void ALCbackend_unlock(ALCbackend *self)
|
||||
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, 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);
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
|
||||
|
||||
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 self->Funcs->OpenPlayback(device, name);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->ClosePlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->ResetPlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->StartPlayback(device);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopPlayback(device);
|
||||
}
|
||||
|
||||
|
||||
typedef struct CaptureWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
const BackendFuncs *Funcs;
|
||||
} CaptureWrapper;
|
||||
|
||||
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);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self);
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
|
||||
|
||||
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 self->Funcs->OpenCapture(device, name);
|
||||
}
|
||||
|
||||
static void CaptureWrapper_close(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->CloseCapture(device);
|
||||
}
|
||||
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StartCapture(device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopCapture(device);
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->CaptureSamples(device, buffer, samples);
|
||||
}
|
||||
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->AvailableSamples(device);
|
||||
}
|
||||
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
PlaybackWrapper *backend;
|
||||
|
||||
NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
|
||||
if(!backend) return NULL;
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
CaptureWrapper *backend;
|
||||
|
||||
NEW_OBJ(backend, CaptureWrapper)(device, funcs);
|
||||
if(!backend) return NULL;
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+1
-2
@@ -142,6 +142,7 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
@@ -151,6 +152,4 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
|
||||
|
||||
#endif /* AL_BACKENDS_BASE_H */
|
||||
|
||||
+34
-11
@@ -65,6 +65,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
||||
#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
|
||||
|
||||
#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
|
||||
|
||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||
|
||||
|
||||
@@ -109,6 +111,15 @@ typedef struct {
|
||||
#define WM_USER_Enumerate (WM_USER+5)
|
||||
#define WM_USER_Last (WM_USER+5)
|
||||
|
||||
static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
|
||||
"Open Device",
|
||||
"Reset Device",
|
||||
"Start Device",
|
||||
"Stop Device",
|
||||
"Close Device",
|
||||
"Enumerate Devices",
|
||||
};
|
||||
|
||||
static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
|
||||
{
|
||||
req->result = res;
|
||||
@@ -403,7 +414,11 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
TRACE("Starting message loop\n");
|
||||
while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
|
||||
{
|
||||
TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
|
||||
TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
|
||||
(msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
|
||||
MessageStr[msg.message-WM_USER] : "Unknown",
|
||||
msg.message, (void*)msg.lParam, (void*)msg.wParam
|
||||
);
|
||||
switch(msg.message)
|
||||
{
|
||||
case WM_USER_OpenDevice:
|
||||
@@ -891,8 +906,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
CoTaskMemFree(wfx);
|
||||
wfx = NULL;
|
||||
|
||||
buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
|
||||
device->Frequency-1) / device->Frequency;
|
||||
buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
|
||||
device->Frequency);
|
||||
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
device->Frequency = OutputType.Format.nSamplesPerSec;
|
||||
@@ -1081,7 +1096,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
|
||||
min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
|
||||
/* 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;
|
||||
@@ -1600,8 +1615,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
self->client = ptr;
|
||||
|
||||
buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
|
||||
device->Frequency-1) / device->Frequency;
|
||||
buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
|
||||
device->Frequency);
|
||||
// Make sure buffer is at least 100ms in size
|
||||
buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
|
||||
device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
|
||||
device->NumUpdates;
|
||||
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
switch(device->FmtChans)
|
||||
@@ -1741,9 +1760,10 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
device->FmtChans);
|
||||
if(!self->ChannelConv)
|
||||
{
|
||||
ERR("Failed to create stereo-to-mono converter\n");
|
||||
ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
|
||||
return E_FAIL;
|
||||
}
|
||||
TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
|
||||
/* The channel converter always outputs float, so change the input type
|
||||
* for the resampler/type-converter.
|
||||
*/
|
||||
@@ -1755,9 +1775,10 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
device->FmtChans);
|
||||
if(!self->ChannelConv)
|
||||
{
|
||||
ERR("Failed to create mono-to-stereo converter\n");
|
||||
ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
|
||||
return E_FAIL;
|
||||
}
|
||||
TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
|
||||
srcType = DevFmtFloat;
|
||||
}
|
||||
|
||||
@@ -1769,12 +1790,14 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
);
|
||||
if(!self->SampleConv)
|
||||
{
|
||||
ERR("Failed to create converter for format, dst: %s %s %uhz, src: %d-bit %luhz\n",
|
||||
ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
|
||||
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
|
||||
device->Frequency, OutputType.Format.wBitsPerSample,
|
||||
OutputType.Format.nSamplesPerSec);
|
||||
device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
|
||||
return E_FAIL;
|
||||
}
|
||||
TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
|
||||
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
|
||||
device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
|
||||
}
|
||||
|
||||
hr = IAudioClient_Initialize(self->client,
|
||||
|
||||
+12
-4
@@ -163,12 +163,12 @@ static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
|
||||
|
||||
if((fd=open("/dev/mixer", O_RDONLY)) < 0)
|
||||
{
|
||||
ERR("Could not open /dev/mixer\n");
|
||||
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
|
||||
{
|
||||
ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
||||
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
for(i = 0;i < si.numaudios;i++)
|
||||
@@ -821,7 +821,11 @@ void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProb
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
AppendAllDevicesList(cur->handle);
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
@@ -832,7 +836,11 @@ void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProb
|
||||
cur = &oss_capture;
|
||||
while(cur != NULL)
|
||||
{
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -817,7 +817,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
frame_size = pa_frame_size(&self->spec);
|
||||
buffer_size = device->UpdateSize * device->NumUpdates * frame_size;
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
@@ -830,15 +829,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
}
|
||||
|
||||
/* Make sure we're going to write at least 2 'periods' (minreqs), in
|
||||
* case the server increased it since starting playback.
|
||||
* case the server increased it since starting playback. Also round up
|
||||
* the number of writable periods if it's not an integer count.
|
||||
*/
|
||||
buffer_size = maxu(buffer_size, self->attr.minreq*2);
|
||||
buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
|
||||
self->attr.minreq;
|
||||
|
||||
/* NOTE: This assumes pa_stream_writable_size returns between 0 and
|
||||
* tlength, else there will be more latency than intended.
|
||||
*/
|
||||
len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
|
||||
if(len < self->attr.minreq)
|
||||
if(len < (int32_t)self->attr.minreq)
|
||||
{
|
||||
if(pa_stream_is_corked(self->stream))
|
||||
{
|
||||
@@ -968,7 +969,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
const char *mapname = NULL;
|
||||
pa_channel_map chanmap;
|
||||
pa_operation *o;
|
||||
ALuint len;
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
|
||||
@@ -1107,11 +1107,10 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
|
||||
ALCpulsePlayback_bufferAttrCallback(self->stream, self);
|
||||
|
||||
len = self->attr.minreq / pa_frame_size(&self->spec);
|
||||
device->NumUpdates = (ALuint)clampd(
|
||||
(ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5, 2.0, 16.0
|
||||
device->NumUpdates = (ALuint)clampu64(
|
||||
(self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
|
||||
);
|
||||
device->UpdateSize = len;
|
||||
device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
|
||||
|
||||
/* HACK: prebuf should be 0 as that's what we set it to. However on some
|
||||
* systems it comes back as non-0, so we have to make sure the device will
|
||||
@@ -1121,7 +1120,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
*/
|
||||
if(self->attr.prebuf != 0)
|
||||
{
|
||||
len = self->attr.prebuf / pa_frame_size(&self->spec);
|
||||
ALuint len = self->attr.prebuf / pa_frame_size(&self->spec);
|
||||
if(len <= device->UpdateSize*device->NumUpdates)
|
||||
ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
|
||||
len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
|
||||
|
||||
+201
-47
@@ -159,10 +159,33 @@ static void deviceList(int type, vector_DevMap *devmap)
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
/* Wrappers to use an old-style backend with the new interface. */
|
||||
typedef struct PlaybackWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
qsa_data *ExtraData;
|
||||
} PlaybackWrapper;
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
|
||||
|
||||
|
||||
FORCE_ALIGN static int qsa_proc_playback(void *ptr)
|
||||
{
|
||||
ALCdevice* device=(ALCdevice*)ptr;
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
PlaybackWrapper *self = ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data = self->ExtraData;
|
||||
snd_pcm_channel_status_t status;
|
||||
struct sched_param param;
|
||||
struct timeval timeout;
|
||||
@@ -250,8 +273,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
/* Playback */
|
||||
/************/
|
||||
|
||||
static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
|
||||
static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data;
|
||||
int card, dev;
|
||||
int status;
|
||||
@@ -299,14 +323,14 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
|
||||
}
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
self->ExtraData = data;
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void qsa_close_playback(ALCdevice* device)
|
||||
static void qsa_close_playback(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
qsa_data *data = self->ExtraData;
|
||||
|
||||
if (data->buffer!=NULL)
|
||||
{
|
||||
@@ -317,12 +341,13 @@ static void qsa_close_playback(ALCdevice* device)
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
free(data);
|
||||
|
||||
device->ExtraData=NULL;
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean qsa_reset_playback(ALCdevice* device)
|
||||
static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data = self->ExtraData;
|
||||
int32_t format=-1;
|
||||
|
||||
switch(device->FmtType)
|
||||
@@ -567,20 +592,20 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean qsa_start_playback(ALCdevice* device)
|
||||
static ALCboolean qsa_start_playback(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_data *data = (qsa_data*)device->ExtraData;
|
||||
qsa_data *data = self->ExtraData;
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
|
||||
if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void qsa_stop_playback(ALCdevice* device)
|
||||
static void qsa_stop_playback(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_data *data = (qsa_data*)device->ExtraData;
|
||||
qsa_data *data = self->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
@@ -590,12 +615,70 @@ static void qsa_stop_playback(ALCdevice* device)
|
||||
althrd_join(data->thread, &res);
|
||||
}
|
||||
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
|
||||
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
|
||||
{
|
||||
return qsa_open_playback(self, name);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_close_playback(self);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
|
||||
{
|
||||
return qsa_reset_playback(self);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
|
||||
{
|
||||
return qsa_start_playback(self);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_stop_playback(self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********/
|
||||
/* Capture */
|
||||
/***********/
|
||||
|
||||
static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
typedef struct CaptureWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
qsa_data *ExtraData;
|
||||
} CaptureWrapper;
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
|
||||
static void CaptureWrapper_close(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self);
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
|
||||
|
||||
|
||||
static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data;
|
||||
int card, dev;
|
||||
int format=-1;
|
||||
@@ -646,7 +729,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
}
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
self->ExtraData = data;
|
||||
|
||||
switch (device->FmtType)
|
||||
{
|
||||
@@ -699,7 +782,6 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
{
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
free(data);
|
||||
device->ExtraData=NULL;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
@@ -707,20 +789,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void qsa_close_capture(ALCdevice* device)
|
||||
static void qsa_close_capture(CaptureWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
qsa_data *data = self->ExtraData;
|
||||
|
||||
if (data->pcmHandle!=NULL)
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
|
||||
free(data);
|
||||
device->ExtraData=NULL;
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void qsa_start_capture(ALCdevice* device)
|
||||
static void qsa_start_capture(CaptureWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
qsa_data *data = self->ExtraData;
|
||||
int rstatus;
|
||||
|
||||
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
|
||||
@@ -740,16 +822,16 @@ static void qsa_start_capture(ALCdevice* device)
|
||||
snd_pcm_capture_go(data->pcmHandle);
|
||||
}
|
||||
|
||||
static void qsa_stop_capture(ALCdevice* device)
|
||||
static void qsa_stop_capture(CaptureWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
|
||||
qsa_data *data = self->ExtraData;
|
||||
snd_pcm_capture_flush(data->pcmHandle);
|
||||
}
|
||||
|
||||
static ALCuint qsa_available_samples(ALCdevice* device)
|
||||
static ALCuint qsa_available_samples(CaptureWrapper *self)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data = self->ExtraData;
|
||||
snd_pcm_channel_status_t status;
|
||||
ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
ALint free_size;
|
||||
@@ -778,9 +860,10 @@ static ALCuint qsa_available_samples(ALCdevice* device)
|
||||
return free_size/frame_size;
|
||||
}
|
||||
|
||||
static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
qsa_data *data = self->ExtraData;
|
||||
char* read_ptr;
|
||||
snd_pcm_channel_status_t status;
|
||||
fd_set rfds;
|
||||
@@ -853,27 +936,65 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static const BackendFuncs qsa_funcs= {
|
||||
qsa_open_playback,
|
||||
qsa_close_playback,
|
||||
qsa_reset_playback,
|
||||
qsa_start_playback,
|
||||
qsa_stop_playback,
|
||||
qsa_open_capture,
|
||||
qsa_close_capture,
|
||||
qsa_start_capture,
|
||||
qsa_stop_capture,
|
||||
qsa_capture_samples,
|
||||
qsa_available_samples
|
||||
};
|
||||
|
||||
ALCboolean alc_qsa_init(BackendFuncs* func_list)
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
|
||||
{
|
||||
*func_list = qsa_funcs;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
|
||||
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
|
||||
{
|
||||
return qsa_open_capture(self, name);
|
||||
}
|
||||
|
||||
static void CaptureWrapper_close(CaptureWrapper *self)
|
||||
{
|
||||
qsa_close_capture(self);
|
||||
}
|
||||
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
|
||||
{
|
||||
qsa_start_capture(self);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_qsa_deinit(void)
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self)
|
||||
{
|
||||
qsa_stop_capture(self);
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
return qsa_capture_samples(self, buffer, samples);
|
||||
}
|
||||
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
|
||||
{
|
||||
return qsa_available_samples(self);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCqsaBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCqsaBackendFactory;
|
||||
#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
|
||||
static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
|
||||
static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
|
||||
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
|
||||
|
||||
static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
|
||||
{
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
|
||||
@@ -884,7 +1005,14 @@ void alc_qsa_deinit(void)
|
||||
#undef FREE_NAME
|
||||
}
|
||||
|
||||
void alc_qsa_probe(enum DevProbe type)
|
||||
static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -913,3 +1041,29 @@ void alc_qsa_probe(enum DevProbe type)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
PlaybackWrapper *backend;
|
||||
NEW_OBJ(backend, PlaybackWrapper)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
CaptureWrapper *backend;
|
||||
NEW_OBJ(backend, CaptureWrapper)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
+3
-2
@@ -320,8 +320,9 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(chanmask, self->mFile);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
|
||||
val = fwrite((device->FmtType == DevFmtFloat) ?
|
||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
|
||||
(void)val;
|
||||
|
||||
fputs("data", self->mFile);
|
||||
|
||||
@@ -75,6 +75,45 @@ void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat
|
||||
}
|
||||
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
|
||||
{
|
||||
ALfloat w = freq_mult * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_clear(SplitterAllpass *splitter)
|
||||
{
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, d, x;
|
||||
ALfloat z1;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = samples[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
samples[i] = x;
|
||||
}
|
||||
splitter->z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
|
||||
|
||||
@@ -60,4 +60,16 @@ void bandsplit_clear(BandSplitter *splitter);
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count);
|
||||
|
||||
/* The all-pass portion of the band splitter. Applies the same phase shift
|
||||
* without splitting the signal.
|
||||
*/
|
||||
typedef struct SplitterAllpass {
|
||||
ALfloat coeff;
|
||||
ALfloat z1;
|
||||
} SplitterAllpass;
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
|
||||
void splitterap_clear(SplitterAllpass *splitter);
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
|
||||
|
||||
#endif /* BFORMATDEC_H */
|
||||
|
||||
+30
-16
@@ -9,6 +9,7 @@
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
|
||||
{
|
||||
SampleConverter *converter;
|
||||
ALsizei step;
|
||||
|
||||
if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
|
||||
return NULL;
|
||||
@@ -21,17 +22,21 @@ SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType
|
||||
converter->mDstTypeSize = BytesFromDevFmt(dstType);
|
||||
|
||||
converter->mSrcPrepCount = 0;
|
||||
|
||||
converter->mFracOffset = 0;
|
||||
converter->mIncrement = (ALsizei)clampu64((ALuint64)srcRate*FRACTIONONE/dstRate,
|
||||
1, MAX_PITCH*FRACTIONONE);
|
||||
|
||||
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
|
||||
START_MIXER_MODE();
|
||||
step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
|
||||
converter->mIncrement = maxi(step, 1);
|
||||
if(converter->mIncrement == FRACTIONONE)
|
||||
converter->mResample = Resample_copy32_C;
|
||||
else
|
||||
{
|
||||
/* TODO: Allow other resamplers. */
|
||||
converter->mResample = SelectResampler(LinearResampler);
|
||||
BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
|
||||
converter->mResample = SelectResampler(BSincResampler);
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return converter;
|
||||
}
|
||||
@@ -59,7 +64,7 @@ static inline ALfloat Sample_ALushort(ALushort val)
|
||||
static inline ALfloat Sample_ALint(ALint val)
|
||||
{ return (val>>7) * (1.0f/16777216.0f); }
|
||||
static inline ALfloat Sample_ALuint(ALuint val)
|
||||
{ return ((ALint)(val>>7) - 16777216) * (1.0f/16777216.0f); }
|
||||
{ return Sample_ALint(val - INT_MAX - 1); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
@@ -113,17 +118,17 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum Dev
|
||||
|
||||
|
||||
static inline ALbyte ALbyte_Sample(ALfloat val)
|
||||
{ return (ALbyte)clampf(val*128.0f, -128.0f, 127.0f); }
|
||||
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
|
||||
static inline ALubyte ALubyte_Sample(ALfloat val)
|
||||
{ return ALbyte_Sample(val)+128; }
|
||||
|
||||
static inline ALshort ALshort_Sample(ALfloat val)
|
||||
{ return (ALshort)clampf(val*32768.0f, -32768.0f, 32767.0f); }
|
||||
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
|
||||
static inline ALushort ALushort_Sample(ALfloat val)
|
||||
{ return ALshort_Sample(val)+32768; }
|
||||
|
||||
static inline ALint ALint_Sample(ALfloat val)
|
||||
{ return (ALint)clampf(val*16777216.0f, -16777216.0f, 16777215.0f) << 7; }
|
||||
{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
|
||||
static inline ALuint ALuint_Sample(ALfloat val)
|
||||
{ return ALint_Sample(val)+INT_MAX+1; }
|
||||
|
||||
@@ -194,6 +199,12 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
|
||||
prepcount = 0;
|
||||
}
|
||||
|
||||
if(srcframes < 1)
|
||||
{
|
||||
/* No output samples if there's no input samples. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
|
||||
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
|
||||
{
|
||||
@@ -208,7 +219,7 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
return (ALsizei)clampu64(DataSize64/increment, 1, INT_MAX);
|
||||
return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +230,8 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
const ALsizei increment = converter->mIncrement;
|
||||
ALsizei pos = 0;
|
||||
|
||||
while(pos < dstframes)
|
||||
START_MIXER_MODE();
|
||||
while(pos < dstframes && *srcframes > 0)
|
||||
{
|
||||
ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
|
||||
ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
|
||||
@@ -241,7 +253,8 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
}
|
||||
*src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
|
||||
*srcframes += prepcount;
|
||||
prepcount = 0;
|
||||
converter->mSrcPrepCount = 0;
|
||||
continue;
|
||||
}
|
||||
toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
|
||||
|
||||
@@ -253,7 +266,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
*/
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
|
||||
(const ALbyte*)src + converter->mSrcTypeSize*chan,
|
||||
(const ALbyte*)*src + converter->mSrcTypeSize*chan,
|
||||
converter->mNumChannels, converter->mSrcType, toread
|
||||
);
|
||||
|
||||
@@ -269,13 +282,13 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
DstSize = (ALsizei)clampu64(DataSize64/increment, 1, BUFFERSIZE);
|
||||
DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
DstSize = mini(DstSize, dstframes-pos);
|
||||
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
{
|
||||
const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
|
||||
ALbyte *DstSamples = (ALbyte*)dst + converter->mSrcTypeSize*chan;
|
||||
ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
|
||||
const ALfloat *ResampledData;
|
||||
ALsizei SrcDataEnd;
|
||||
|
||||
@@ -294,7 +307,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
|
||||
if(SrcDataEnd >= prepcount+toread)
|
||||
memset(converter->Chan[chan].mPrevSamples, 0,
|
||||
sizeof(converter->Chan[chan].mPrevSamples));
|
||||
sizeof(converter->Chan[chan].mPrevSamples));
|
||||
else
|
||||
{
|
||||
size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
|
||||
@@ -305,7 +318,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
}
|
||||
|
||||
/* Now resample, and store the result in the output buffer. */
|
||||
ResampledData = converter->mResample(NULL,
|
||||
ResampledData = converter->mResample(&converter->mState,
|
||||
SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
|
||||
DstData, DstSize
|
||||
);
|
||||
@@ -329,6 +342,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
dst = (ALbyte*)dst + DstFrameSize*DstSize;
|
||||
pos += DstSize;
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef struct SampleConverter {
|
||||
|
||||
ALsizei mFracOffset;
|
||||
ALsizei mIncrement;
|
||||
InterpState mState;
|
||||
ResamplerFunc mResample;
|
||||
|
||||
alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
|
||||
|
||||
+60
-68
@@ -91,7 +91,7 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
|
||||
ALsizei maxlen;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
@@ -172,107 +172,99 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALchorusState *state, const ALsizei SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALsizei bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALsizei offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALsizei it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
leftbuf[offset&bufmask] = SamplesIn[it]; \
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] += out[it][0] * feedback; \
|
||||
\
|
||||
rightbuf[offset&bufmask] = SamplesIn[it]; \
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] += out[it][1] * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALsizei it, kt;
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
ALint moddelays[2][128];
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
ProcessTriangle(state, td, SamplesIn[0]+base, temps);
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+59
-68
@@ -172,107 +172,98 @@ static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Devi
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
|
||||
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
|
||||
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALflangerState *state, const ALsizei SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALsizei bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALsizei offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALsizei it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
leftbuf[offset&bufmask] = SamplesIn[it]; \
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] += out[it][0] * feedback; \
|
||||
\
|
||||
rightbuf[offset&bufmask] = SamplesIn[it]; \
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] += out[it][1] * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALsizei it, kt;
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
ALint moddelays[2][128];
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
ProcessTriangle(state, td, SamplesIn[0]+base, temps);
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *
|
||||
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
|
||||
state->Process = ModulateSquare;
|
||||
|
||||
state->step = fastf2u(props->Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
Device->Frequency);
|
||||
if(state->step == 0) state->step = 1;
|
||||
|
||||
|
||||
+44
-67
@@ -331,21 +331,6 @@ ALfloat ReverbBoost = 1.0f;
|
||||
*/
|
||||
ALboolean EmulateEAXReverb = AL_FALSE;
|
||||
|
||||
/* This coefficient is used to define the sinus depth according to the
|
||||
* modulation depth property. This value must be below 1, which would cause the
|
||||
* sampler to stall on the downswing, and above 1 it will cause it to sample
|
||||
* backwards.
|
||||
*/
|
||||
static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 2048.0f;
|
||||
|
||||
/* A filter is used to avoid the terrible distortion caused by changing
|
||||
* modulation time and/or depth. To be consistent across different sample
|
||||
* rates, the coefficient must be raised to a constant divided by the sample
|
||||
* rate: coeff^(constant / rate).
|
||||
*/
|
||||
static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
|
||||
static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
|
||||
|
||||
/* The all-pass and delay lines have a variable length dependent on the
|
||||
* effect's density parameter. The resulting density multiplier is:
|
||||
*
|
||||
@@ -473,20 +458,33 @@ static const ALfloat LATE_LINE_LENGTHS[4] =
|
||||
9.709681e-3f, 1.223343e-2f, 1.689561e-2f, 1.941936e-2f
|
||||
};
|
||||
|
||||
/* HACK: Workaround for a modff bug in 32-bit Windows, which attempts to write
|
||||
* a 64-bit double to the 32-bit float parameter.
|
||||
/* This coefficient is used to define the sinus depth according to the
|
||||
* modulation depth property. This value must be below half the shortest late
|
||||
* line length (0.0097/2 = ~0.0048), otherwise with certain parameters (high
|
||||
* mod time, low density) the downswing can sample before the input.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined (_M_X64) && !defined(_M_ARM)
|
||||
static inline float hack_modff(float x, float *y)
|
||||
static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 4096.0f;
|
||||
|
||||
/* A filter is used to avoid the terrible distortion caused by changing
|
||||
* modulation time and/or depth. To be consistent across different sample
|
||||
* rates, the coefficient must be raised to a constant divided by the sample
|
||||
* rate: coeff^(constant / rate).
|
||||
*/
|
||||
static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
|
||||
static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
|
||||
|
||||
|
||||
/* Prior to VS2013, MSVC lacks the round() family of functions. */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
static inline long lroundf(float val)
|
||||
{
|
||||
double di;
|
||||
double df = modf((double)x, &di);
|
||||
*y = (float)di;
|
||||
return (float)df;
|
||||
if(val < 0.0)
|
||||
return fastf2i(ceilf(val-0.5f));
|
||||
return fastf2i(floorf(val+0.5f));
|
||||
}
|
||||
#define modff hack_modff
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************
|
||||
* Device Update *
|
||||
**************************************/
|
||||
@@ -513,7 +511,7 @@ static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const
|
||||
/* All line lengths are powers of 2, calculated from their lengths in
|
||||
* seconds, rounded up.
|
||||
*/
|
||||
samples = fastf2u(ceilf(length*frequency));
|
||||
samples = fastf2i(ceilf(length*frequency));
|
||||
samples = NextPowerOf2(samples + extra);
|
||||
|
||||
/* All lines share a single sample buffer. */
|
||||
@@ -629,7 +627,7 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev
|
||||
|
||||
/* The late feed taps are set a fixed position past the latest delay tap. */
|
||||
for(i = 0;i < 4;i++)
|
||||
State->LateFeedTap = fastf2u((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
|
||||
State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
|
||||
EARLY_TAP_LENGTHS[3]*multiplier) *
|
||||
frequency);
|
||||
|
||||
@@ -752,7 +750,7 @@ static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALflo
|
||||
g = maxf(0.001f, gain);
|
||||
g2 = g * g;
|
||||
cw = cosf(w);
|
||||
p = g / (g*cw + sqrt((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
|
||||
p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
|
||||
|
||||
coeffs[0] = p;
|
||||
coeffs[1] = -p;
|
||||
@@ -1057,7 +1055,7 @@ static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
|
||||
* (1 sample) and when the timing changes, the index is rescaled to the new
|
||||
* range to keep the sinus consistent.
|
||||
*/
|
||||
range = maxu(fastf2u(modTime*frequency), 1);
|
||||
range = maxi(fastf2i(modTime*frequency), 1);
|
||||
State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
|
||||
State->Mod.Range);
|
||||
State->Mod.Range = range;
|
||||
@@ -1067,12 +1065,12 @@ static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
|
||||
* time changes the pitch, creating the modulation effect. The scale needs
|
||||
* to be multiplied by the modulation time so that a given depth produces a
|
||||
* consistent shift in frequency over all ranges of time. Since the depth
|
||||
* is applied to a sinus value, it needs to be halved once for the sinus
|
||||
* range (-1...+1 to 0...1) and again for the sinus swing in time (half of
|
||||
* it is spent decreasing the frequency, half is spent increasing it).
|
||||
* is applied to a sinus value, it needs to be halved for the sinus swing
|
||||
* in time (half of it is spent decreasing the frequency, half is spent
|
||||
* increasing it).
|
||||
*/
|
||||
State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
|
||||
2.0f * frequency;
|
||||
State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f *
|
||||
frequency;
|
||||
}
|
||||
|
||||
/* Update the offsets for the main effect delay line. */
|
||||
@@ -1096,13 +1094,13 @@ static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay,
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
|
||||
State->EarlyDelayTap[i][1] = fastf2u(length * frequency);
|
||||
State->EarlyDelayTap[i][1] = fastf2i(length * frequency);
|
||||
|
||||
length = EARLY_TAP_LENGTHS[i]*multiplier;
|
||||
State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
|
||||
|
||||
length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
|
||||
State->LateDelayTap[i][1] = State->LateFeedTap + fastf2u(length * frequency);
|
||||
State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,7 +1124,7 @@ static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, c
|
||||
length = EARLY_LINE_LENGTHS[i] * multiplier;
|
||||
|
||||
/* Calculate the delay offset for each delay line. */
|
||||
State->Early.Offset[i][1] = fastf2u(length * frequency);
|
||||
State->Early.Offset[i][1] = fastf2i(length * frequency);
|
||||
|
||||
/* Calculate the gain (coefficient) for each line. */
|
||||
State->Early.Coeff[i] = CalcDecayCoeff(length, decayTime);
|
||||
@@ -1183,7 +1181,7 @@ static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, co
|
||||
length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
|
||||
|
||||
/* Calculate the delay offset for each delay line. */
|
||||
State->Late.Offset[i][1] = fastf2u(length * frequency);
|
||||
State->Late.Offset[i][1] = fastf2i(length * frequency);
|
||||
|
||||
/* Approximate the absorption that the vector all-pass would exhibit
|
||||
* given the current diffusion so we don't have to process a full T60
|
||||
@@ -1446,7 +1444,7 @@ static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const AL
|
||||
Delay->Line[offset][i] = in[3-i];
|
||||
}
|
||||
|
||||
static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, const ALsizei todo)
|
||||
static void CalcModulationDelays(ALreverbState *State, ALint *restrict delays, const ALsizei todo)
|
||||
{
|
||||
ALfloat sinus, range;
|
||||
ALsizei index, i;
|
||||
@@ -1456,10 +1454,9 @@ static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays,
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
/* Calculate the sinus rhythm (dependent on modulation time and the
|
||||
* sampling rate). The center of the sinus is moved to reduce the
|
||||
* delay of the effect when the time or depth are low.
|
||||
* sampling rate).
|
||||
*/
|
||||
sinus = 1.0f - cosf(F_TAU * index / State->Mod.Range);
|
||||
sinus = sinf(F_TAU * index / State->Mod.Range);
|
||||
|
||||
/* Step the modulation index forward, keeping it bound to its range. */
|
||||
index = (index+1) % State->Mod.Range;
|
||||
@@ -1470,8 +1467,8 @@ static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays,
|
||||
*/
|
||||
range = lerp(range, State->Mod.Depth, State->Mod.Coeff);
|
||||
|
||||
/* Calculate the read offset with fraction. */
|
||||
delays[i] = range*sinus;
|
||||
/* Calculate the read offset. */
|
||||
delays[i] = lroundf(range*sinus);
|
||||
}
|
||||
State->Mod.Index = index;
|
||||
State->Mod.Filter = range;
|
||||
@@ -1686,14 +1683,13 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo, \
|
||||
const ALfloat apFeedCoeff = State->ApFeedCoeff; \
|
||||
const ALfloat mixX = State->MixX; \
|
||||
const ALfloat mixY = State->MixY; \
|
||||
ALfloat fdelay, frac; \
|
||||
ALint moddelay[MAX_UPDATE_SAMPLES]; \
|
||||
ALsizei delay; \
|
||||
ALsizei offset; \
|
||||
ALsizei i, j; \
|
||||
ALfloat f[4]; \
|
||||
\
|
||||
/* Calculations modulation delays, uing the output as temp storage. */ \
|
||||
CalcModulationDelays(State, &out[0][0], todo); \
|
||||
CalcModulationDelays(State, moddelay, todo); \
|
||||
\
|
||||
offset = State->Offset; \
|
||||
for(i = 0;i < todo;i++) \
|
||||
@@ -1704,31 +1700,12 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo, \
|
||||
offset-State->LateDelayTap[j][1], j, fade \
|
||||
) * State->Late.DensityGain; \
|
||||
\
|
||||
/* Separate the integer offset and fraction between it and the next \
|
||||
* sample. \
|
||||
*/ \
|
||||
frac = modff(out[0][i], &fdelay); \
|
||||
delay = offset - fastf2i(fdelay); \
|
||||
\
|
||||
delay = offset - moddelay[i]; \
|
||||
for(j = 0;j < 4;j++) \
|
||||
{ \
|
||||
ALfloat out0, out1; \
|
||||
\
|
||||
/* Get the two samples crossed by the offset delay. */ \
|
||||
out0 = DELAY_OUT_##T(&State->Late.Delay, \
|
||||
f[j] += DELAY_OUT_##T(&State->Late.Delay, \
|
||||
delay-State->Late.Offset[j][0], \
|
||||
delay-State->Late.Offset[j][1], j, fade \
|
||||
); \
|
||||
out1 = DELAY_OUT_##T(&State->Late.Delay, \
|
||||
delay-State->Late.Offset[j][0]-1, \
|
||||
delay-State->Late.Offset[j][1]-1, j, fade \
|
||||
); \
|
||||
\
|
||||
/* The modulated result is obtained by linearly interpolating the \
|
||||
* two samples that were acquired above. \
|
||||
*/ \
|
||||
f[j] += lerp(out0, out1, frac); \
|
||||
} \
|
||||
\
|
||||
for(j = 0;j < 4;j++) \
|
||||
f[j] = LateT60Filter(j, f[j], State); \
|
||||
|
||||
+23
-2
@@ -40,6 +40,10 @@
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef AL_NO_UID_DEFS
|
||||
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
|
||||
#define INITGUID
|
||||
@@ -113,8 +117,10 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
|
||||
extern inline ALuint NextPowerOf2(ALuint value);
|
||||
extern inline size_t RoundUp(size_t value, size_t r);
|
||||
extern inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALint fastf2i(ALfloat f);
|
||||
extern inline ALuint fastf2u(ALfloat f);
|
||||
|
||||
|
||||
ALuint CPUCapFlags = 0;
|
||||
@@ -707,9 +713,22 @@ void UnmapFileMem(const struct FileMapping *mapping)
|
||||
al_string GetProcPath(void)
|
||||
{
|
||||
al_string ret = AL_STRING_INIT_STATIC();
|
||||
const char *fname;
|
||||
char *pathname, *sep;
|
||||
size_t pathlen;
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||
mib[3] = getpid();
|
||||
if (sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1) {
|
||||
WARN("Failed to sysctl kern.proc.pathname.%d: %s\n", mib[3], strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
pathname = malloc(pathlen + 1);
|
||||
sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
|
||||
pathname[pathlen] = 0;
|
||||
#else
|
||||
const char *fname;
|
||||
ssize_t len;
|
||||
|
||||
pathlen = 256;
|
||||
@@ -738,6 +757,8 @@ al_string GetProcPath(void)
|
||||
}
|
||||
|
||||
pathname[len] = 0;
|
||||
#endif
|
||||
|
||||
sep = strrchr(pathname, '/');
|
||||
if(sep)
|
||||
alstr_copy_range(&ret, pathname, sep);
|
||||
|
||||
+55
-53
@@ -37,7 +37,7 @@
|
||||
|
||||
/* Current data set limits defined by the makehrtf utility. */
|
||||
#define MIN_IR_SIZE (8)
|
||||
#define MAX_IR_SIZE (128)
|
||||
#define MAX_IR_SIZE (512)
|
||||
#define MOD_IR_SIZE (8)
|
||||
|
||||
#define MIN_EV_COUNT (5)
|
||||
@@ -284,8 +284,10 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
|
||||
/* Round up to the next IR size multiple. */
|
||||
max_length = RoundUp(max_length, MOD_IR_SIZE);
|
||||
|
||||
TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
|
||||
return max_length;
|
||||
#undef NUM_BANDS
|
||||
}
|
||||
@@ -360,6 +362,41 @@ static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
static ALubyte GetLE_ALubyte(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALubyte ret = (*data)[0];
|
||||
*data += 1; *len -= 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALshort GetLE_ALshort(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALshort ret = (*data)[0] | ((*data)[1]<<8);
|
||||
*data += 2; *len -= 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALushort ret = (*data)[0] | ((*data)[1]<<8);
|
||||
*data += 2; *len -= 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALint GetLE_ALuint(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
|
||||
*data += 4; *len -= 4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t size)
|
||||
{
|
||||
const ALubyte *ret = *data;
|
||||
*data += size; *len -= size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename)
|
||||
{
|
||||
const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
|
||||
@@ -381,22 +418,13 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate = *(data++);
|
||||
rate |= *(data++)<<8;
|
||||
rate |= *(data++)<<16;
|
||||
rate |= *(data++)<<24;
|
||||
datalen -= 4;
|
||||
rate = GetLE_ALuint(&data, &datalen);
|
||||
|
||||
irCount = *(data++);
|
||||
irCount |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
irCount = GetLE_ALushort(&data, &datalen);
|
||||
|
||||
irSize = *(data++);
|
||||
irSize |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
irSize = GetLE_ALushort(&data, &datalen);
|
||||
|
||||
evCount = *(data++);
|
||||
datalen -= 1;
|
||||
evCount = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
@@ -429,14 +457,10 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = *(data++);
|
||||
evOffset[0] |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
evOffset[0] = GetLE_ALushort(&data, &datalen);
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = *(data++);
|
||||
evOffset[i] |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
evOffset[i] = GetLE_ALushort(&data, &datalen);
|
||||
if(evOffset[i] <= evOffset[i-1])
|
||||
{
|
||||
ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
|
||||
@@ -492,22 +516,15 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = *(data++);
|
||||
coeff |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
coeffs[i+j][0] = coeff / 32768.0f;
|
||||
}
|
||||
coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
|
||||
}
|
||||
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = *(data++);
|
||||
datalen -= 1;
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
|
||||
@@ -567,17 +584,11 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate = *(data++);
|
||||
rate |= *(data++)<<8;
|
||||
rate |= *(data++)<<16;
|
||||
rate |= *(data++)<<24;
|
||||
datalen -= 4;
|
||||
rate = GetLE_ALuint(&data, &datalen);
|
||||
|
||||
irSize = *(data++);
|
||||
datalen -= 1;
|
||||
irSize = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
evCount = *(data++);
|
||||
datalen -= 1;
|
||||
evCount = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
@@ -600,9 +611,7 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
azCount = data;
|
||||
data += evCount;
|
||||
datalen -= evCount;
|
||||
azCount = Get_ALubytePtr(&data, &datalen, evCount);
|
||||
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
@@ -656,22 +665,15 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = *(data++);
|
||||
coeff |= *(data++)<<8;
|
||||
datalen -= 2;
|
||||
coeffs[i+j][0] = coeff / 32768.0f;
|
||||
}
|
||||
coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
|
||||
}
|
||||
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = *(data++);
|
||||
datalen -= 1;
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
#include "atomic.h"
|
||||
|
||||
|
||||
#define HRTFDELAY_BITS (20)
|
||||
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
|
||||
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
|
||||
|
||||
/* The maximum number of virtual speakers used to generate HRTF coefficients
|
||||
* for decoding B-Format.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
ALfloat sig = Comp->Envelope[i];
|
||||
|
||||
sum -= window[index];
|
||||
window[index] = fastf2u(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
|
||||
window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
|
||||
sum += window[index];
|
||||
index = (index + 1) & RMS_WINDOW_MASK;
|
||||
|
||||
|
||||
+16
-18
@@ -471,13 +471,8 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
);
|
||||
else
|
||||
{
|
||||
static void (*const NfcUpdate[MAX_AMBI_ORDER])(
|
||||
NfcFilter*,float*,const float*,const int
|
||||
) = {
|
||||
NfcFilterUpdate1, NfcFilterUpdate2, NfcFilterUpdate3
|
||||
};
|
||||
ALfloat *nfcsamples = Device->NFCtrlData;
|
||||
ALsizei ord, chanoffset = 0;
|
||||
ALsizei chanoffset = 0;
|
||||
|
||||
MixSamples(samples,
|
||||
voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
|
||||
@@ -485,18 +480,21 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
DstBufferSize
|
||||
);
|
||||
chanoffset += voice->Direct.ChannelsPerOrder[0];
|
||||
for(ord = 1;ord < MAX_AMBI_ORDER+1;ord++)
|
||||
{
|
||||
if(voice->Direct.ChannelsPerOrder[ord] <= 0)
|
||||
break;
|
||||
NfcUpdate[ord-1](&parms->NFCtrlFilter[ord-1], nfcsamples, samples,
|
||||
DstBufferSize);
|
||||
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[ord],
|
||||
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset,
|
||||
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize
|
||||
);
|
||||
chanoffset += voice->Direct.ChannelsPerOrder[ord];
|
||||
}
|
||||
#define APPLY_NFC_MIX(order) \
|
||||
if(voice->Direct.ChannelsPerOrder[order] > 0) \
|
||||
{ \
|
||||
NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
|
||||
samples, DstBufferSize); \
|
||||
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
|
||||
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
|
||||
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
|
||||
); \
|
||||
chanoffset += voice->Direct.ChannelsPerOrder[order]; \
|
||||
}
|
||||
APPLY_NFC_MIX(1)
|
||||
APPLY_NFC_MIX(2)
|
||||
APPLY_NFC_MIX(3)
|
||||
#undef APPLY_NFC_MIX
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+7
-9
@@ -152,8 +152,7 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
* ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
|
||||
*
|
||||
* The gain of the source is compensated for size, so that the
|
||||
* loundness doesn't depend on the spread. That is, the factors are
|
||||
* scaled so that ZH0 remains 1 regardless of the spread. Thus:
|
||||
* loundness doesn't depend on the spread. Thus:
|
||||
*
|
||||
* ZH0 = 1.0f;
|
||||
* ZH1 = 0.5f * (ca+1.0f);
|
||||
@@ -163,11 +162,13 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
* ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
|
||||
*/
|
||||
ALfloat ca = cosf(spread * 0.5f);
|
||||
/* Increase the source volume by up to +3dB for a full spread. */
|
||||
ALfloat scale = sqrtf(1.0f + spread/F_TAU);
|
||||
|
||||
ALfloat ZH0_norm = 1.0f;
|
||||
ALfloat ZH1_norm = 0.5f * (ca+1.f);
|
||||
ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca;
|
||||
ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f);
|
||||
ALfloat ZH0_norm = scale;
|
||||
ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
|
||||
ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
|
||||
ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
|
||||
|
||||
/* Zeroth-order */
|
||||
coeffs[0] *= ZH0_norm;
|
||||
@@ -963,9 +964,6 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
device->Hrtf, device->Dry.NumChannels,
|
||||
AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints)
|
||||
);
|
||||
|
||||
/* Round up to the nearest multiple of 8 */
|
||||
device->Hrtf->IrSize = (device->Hrtf->IrSize+7)&~7;
|
||||
}
|
||||
|
||||
static void InitUhjPanning(ALCdevice *device)
|
||||
|
||||
+119
-72
@@ -65,6 +65,8 @@ endif()
|
||||
IF(WIN32)
|
||||
ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502")
|
||||
|
||||
OPTION(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF)
|
||||
|
||||
# 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.
|
||||
@@ -98,7 +100,7 @@ ENDIF()
|
||||
|
||||
SET(LIB_MAJOR_VERSION "1")
|
||||
SET(LIB_MINOR_VERSION "18")
|
||||
SET(LIB_REVISION "0")
|
||||
SET(LIB_REVISION "1")
|
||||
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
|
||||
|
||||
SET(EXPORT_DECL "")
|
||||
@@ -501,6 +503,7 @@ 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)
|
||||
CHECK_SYMBOL_EXISTS(modff math.h HAVE_MODFF)
|
||||
CHECK_SYMBOL_EXISTS(log2f math.h HAVE_LOG2F)
|
||||
IF(NOT HAVE_C99_VLA)
|
||||
CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA)
|
||||
IF(NOT HAVE_ALLOCA)
|
||||
@@ -1192,91 +1195,111 @@ CONFIGURE_FILE(
|
||||
"${OpenAL_BINARY_DIR}/openal.pc"
|
||||
@ONLY)
|
||||
|
||||
MACRO(ADD_INCLUDE_DIRS TRGT TESTVAR INCVAR)
|
||||
IF(${TESTVAR})
|
||||
SET_PROPERTY(TARGET ${TRGT} APPEND PROPERTY INCLUDE_DIRECTORIES ${${INCVAR}})
|
||||
ENDIF()
|
||||
ENDMACRO()
|
||||
|
||||
UNSET(HAS_ROUTER)
|
||||
SET(IMPL_TARGET OpenAL)
|
||||
|
||||
# Build main library
|
||||
IF(LIBTYPE STREQUAL "STATIC")
|
||||
ADD_LIBRARY(OpenAL STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
|
||||
ELSE()
|
||||
ADD_LIBRARY(OpenAL SHARED ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
|
||||
ENDIF()
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES)
|
||||
IF(WIN32 AND ALSOFT_NO_UID_DEFS)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
|
||||
ENDIF()
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
|
||||
IF(HAVE_ALSA)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_OSS)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_SOLARIS)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_SNDIO)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_QSA)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_DSOUND)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_PORTAUDIO)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_PULSEAUDIO)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(HAVE_JACK)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${JACK_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
IF(WIN32)
|
||||
IF(MSVC)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
|
||||
ELSEIF(CMAKE_COMPILER_IS_GNUCC)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
|
||||
IF(WIN32 AND ALSOFT_BUILD_ROUTER)
|
||||
ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c ${COMMON_OBJS})
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
|
||||
SET_PROPERTY(TARGET OpenAL APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
|
||||
)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
|
||||
IF(MSVC)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
|
||||
ELSEIF(CMAKE_COMPILER_IS_GNUCC)
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "")
|
||||
SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME})
|
||||
IF(TARGET build_version)
|
||||
ADD_DEPENDENCIES(OpenAL build_version)
|
||||
ENDIF()
|
||||
SET(HAS_ROUTER 1)
|
||||
|
||||
SET(LIBNAME "soft_oal")
|
||||
SET(IMPL_TARGET soft_oal)
|
||||
ENDIF()
|
||||
|
||||
ADD_LIBRARY(${IMPL_TARGET} SHARED ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
|
||||
IF(WIN32)
|
||||
SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES PREFIX "")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
|
||||
VERSION ${LIB_VERSION}
|
||||
SOVERSION ${LIB_MAJOR_VERSION}
|
||||
)
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY INCLUDE_DIRECTORIES
|
||||
"${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc"
|
||||
)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_ALSA ALSA_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_OSS OSS_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SOLARIS AUDIOIO_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SNDIO SOUNDIO_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_QSA QSA_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_DSOUND DSOUND_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PORTAUDIO PORTAUDIO_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PULSEAUDIO PULSEAUDIO_INCLUDE_DIRS)
|
||||
ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_JACK JACK_INCLUDE_DIRS)
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
|
||||
)
|
||||
IF(WIN32 AND ALSOFT_NO_UID_DEFS)
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
|
||||
ENDIF()
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
|
||||
|
||||
SET_TARGET_PROPERTIES(OpenAL PROPERTIES VERSION ${LIB_VERSION}
|
||||
SOVERSION ${LIB_MAJOR_VERSION})
|
||||
SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME})
|
||||
TARGET_LINK_LIBRARIES(${IMPL_TARGET} ${EXTRA_LIBS})
|
||||
IF(TARGET build_version)
|
||||
ADD_DEPENDENCIES(${IMPL_TARGET} build_version)
|
||||
ENDIF()
|
||||
|
||||
if(WIN32 AND NOT LIBTYPE STREQUAL "STATIC")
|
||||
SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "")
|
||||
IF(WIN32)
|
||||
IF(MSVC)
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
|
||||
ELSEIF(CMAKE_COMPILER_IS_GNUCC)
|
||||
SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
|
||||
ENDIF()
|
||||
|
||||
IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB)
|
||||
FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
|
||||
FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
|
||||
IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE)
|
||||
MESSAGE(STATUS "")
|
||||
IF(NOT SED_EXECUTABLE)
|
||||
MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB)
|
||||
FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
|
||||
FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
|
||||
IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE)
|
||||
MESSAGE(STATUS "")
|
||||
IF(NOT SED_EXECUTABLE)
|
||||
MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
|
||||
ENDIF()
|
||||
IF(NOT DLLTOOL_EXECUTABLE)
|
||||
MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS
|
||||
" -Wl,--output-def,OpenAL32.def")
|
||||
ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD
|
||||
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
||||
COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
||||
COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
|
||||
VERBATIM
|
||||
)
|
||||
ENDIF()
|
||||
IF(NOT DLLTOOL_EXECUTABLE)
|
||||
MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
SET(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -Wl,--output-def,OpenAL32.def")
|
||||
ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD
|
||||
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
||||
COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
||||
COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
|
||||
VERBATIM
|
||||
)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
|
||||
TARGET_LINK_LIBRARIES(OpenAL ${EXTRA_LIBS})
|
||||
IF(TARGET build_version)
|
||||
ADD_DEPENDENCIES(OpenAL build_version)
|
||||
ENDIF()
|
||||
|
||||
IF(ALSOFT_INSTALL)
|
||||
# Add an install target here
|
||||
INSTALL(TARGETS OpenAL EXPORT OpenAL
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
@@ -1300,9 +1323,19 @@ IF(ALSOFT_INSTALL)
|
||||
)
|
||||
INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
IF(TARGET soft_oal)
|
||||
INSTALL(TARGETS soft_oal
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
||||
if(HAS_ROUTER)
|
||||
message(STATUS "")
|
||||
message(STATUS "Building DLL router")
|
||||
endif()
|
||||
|
||||
MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Building OpenAL with support for the following backends:")
|
||||
MESSAGE(STATUS " ${BACKENDS}")
|
||||
@@ -1409,6 +1442,20 @@ IF(ALSOFT_TESTS)
|
||||
ENDIF()
|
||||
|
||||
IF(ALSOFT_EXAMPLES)
|
||||
ADD_EXECUTABLE(alrecord examples/alrecord.c ${COMMON_OBJS})
|
||||
TARGET_LINK_LIBRARIES(alrecord OpenAL)
|
||||
SET_PROPERTY(TARGET alrecord APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
|
||||
|
||||
IF(ALSOFT_INSTALL)
|
||||
INSTALL(TARGETS alrecord
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
MESSAGE(STATUS "Building example programs")
|
||||
|
||||
IF(SDL2_FOUND)
|
||||
IF(SDL_SOUND_FOUND)
|
||||
SET(EX_COMMON_OBJS examples/common/alhelpers.c)
|
||||
@@ -1485,7 +1532,7 @@ IF(ALSOFT_EXAMPLES)
|
||||
ENDIF()
|
||||
IF(FFVER_OK)
|
||||
ADD_EXECUTABLE(alffplay examples/alffplay.cpp ${COMMON_OBJS})
|
||||
TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} OpenAL ${FFMPEG_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} OpenAL)
|
||||
SET_PROPERTY(TARGET alffplay APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
|
||||
SET_PROPERTY(TARGET alffplay APPEND PROPERTY
|
||||
INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
openal-soft-1.18.1:
|
||||
|
||||
Fixed an issue where resuming a source might not restart playing it.
|
||||
|
||||
Fixed PulseAudio playback when the configured stream length is much less
|
||||
than the requested length.
|
||||
|
||||
Fixed MMDevAPI capture with sample rates not matching the backing device.
|
||||
|
||||
Fixed int32 output for the Wave Writer.
|
||||
|
||||
Fixed enumeration of OSS devices that are missing device files.
|
||||
|
||||
Added correct retrieval of the executable's path on FreeBSD.
|
||||
|
||||
Added a config option to specify the dithering depth.
|
||||
|
||||
Added a 5.1 decoder preset that excludes front-center output.
|
||||
|
||||
openal-soft-1.18.0:
|
||||
|
||||
Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
|
||||
|
||||
@@ -26,7 +26,7 @@ struct ALlistenerProps {
|
||||
};
|
||||
|
||||
typedef struct ALlistener {
|
||||
ALfloat Position[3];
|
||||
alignas(16) ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
|
||||
+34
-34
@@ -409,6 +409,24 @@ inline size_t RoundUp(size_t value, size_t r)
|
||||
return value - (value%r);
|
||||
}
|
||||
|
||||
/* Scales the given value using 64-bit integer math, rounding the result. */
|
||||
inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
|
||||
{
|
||||
return (val*new_scale + old_scale/2) / old_scale;
|
||||
}
|
||||
|
||||
/* Scales the given value using 64-bit integer math, flooring the result. */
|
||||
inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
|
||||
{
|
||||
return val * new_scale / old_scale;
|
||||
}
|
||||
|
||||
/* Scales the given value using 64-bit integer math, ceiling the result. */
|
||||
inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
|
||||
{
|
||||
return (val*new_scale + old_scale-1) / old_scale;
|
||||
}
|
||||
|
||||
/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
inline ALint fastf2i(ALfloat f)
|
||||
@@ -425,36 +443,12 @@ inline ALint fastf2i(ALfloat f)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
inline ALuint fastf2u(ALfloat f)
|
||||
{ return fastf2i(f); }
|
||||
|
||||
|
||||
enum DevProbe {
|
||||
ALL_DEVICE_PROBE,
|
||||
CAPTURE_DEVICE_PROBE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*);
|
||||
void (*ClosePlayback)(ALCdevice*);
|
||||
ALCboolean (*ResetPlayback)(ALCdevice*);
|
||||
ALCboolean (*StartPlayback)(ALCdevice*);
|
||||
void (*StopPlayback)(ALCdevice*);
|
||||
|
||||
ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*);
|
||||
void (*CloseCapture)(ALCdevice*);
|
||||
void (*StartCapture)(ALCdevice*);
|
||||
void (*StopCapture)(ALCdevice*);
|
||||
ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint);
|
||||
ALCuint (*AvailableSamples)(ALCdevice*);
|
||||
} BackendFuncs;
|
||||
|
||||
ALCboolean alc_qsa_init(BackendFuncs *func_list);
|
||||
void alc_qsa_deinit(void);
|
||||
void alc_qsa_probe(enum DevProbe type);
|
||||
|
||||
struct ALCbackend;
|
||||
|
||||
|
||||
@@ -794,7 +788,7 @@ struct ALCdevice_struct
|
||||
DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Dithering control. */
|
||||
bool DitherEnabled;
|
||||
ALfloat DitherDepth;
|
||||
ALuint DitherSeed;
|
||||
|
||||
/* Running count of the mixer invocations, in 31.1 fixed point. This
|
||||
@@ -804,21 +798,13 @@ struct ALCdevice_struct
|
||||
*/
|
||||
RefCount MixCount;
|
||||
|
||||
/* Default effect slot */
|
||||
struct ALeffectslot *DefaultSlot;
|
||||
|
||||
// Contexts created on this device
|
||||
ATOMIC(ALCcontext*) ContextList;
|
||||
|
||||
almtx_t BackendLock;
|
||||
struct ALCbackend *Backend;
|
||||
|
||||
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
|
||||
@@ -880,12 +866,15 @@ struct ALCcontext_struct {
|
||||
|
||||
ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
|
||||
|
||||
/* Default effect slot */
|
||||
struct ALeffectslot *DefaultSlot;
|
||||
|
||||
ALCdevice *Device;
|
||||
const ALCchar *ExtensionList;
|
||||
|
||||
ALCcontext *volatile next;
|
||||
|
||||
/* Memory space used by the listener */
|
||||
/* Memory space used by the listener (and possibly default effect slot) */
|
||||
alignas(16) ALCbyte _listener_mem[];
|
||||
};
|
||||
|
||||
@@ -918,6 +907,17 @@ typedef struct {
|
||||
} FPUCtl;
|
||||
void SetMixerFPUMode(FPUCtl *ctl);
|
||||
void RestoreFPUMode(const FPUCtl *ctl);
|
||||
#ifdef __GNUC__
|
||||
/* Use an alternate macro set with GCC to avoid accidental continue or break
|
||||
* statements within the mixer mode.
|
||||
*/
|
||||
#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
|
||||
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
|
||||
#else
|
||||
#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
|
||||
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
|
||||
#endif
|
||||
#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
|
||||
|
||||
|
||||
typedef struct ll_ringbuffer ll_ringbuffer_t;
|
||||
|
||||
@@ -85,6 +85,8 @@ typedef union InterpState {
|
||||
BsincState bsinc;
|
||||
} InterpState;
|
||||
|
||||
ALboolean BsincPrepare(const ALuint increment, BsincState *state);
|
||||
|
||||
typedef const ALfloat* (*ResamplerFunc)(const InterpState *state,
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei dstlen
|
||||
@@ -500,7 +502,7 @@ void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans,
|
||||
|
||||
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
|
||||
|
||||
void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
|
||||
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
|
||||
/* Caller must lock the device. */
|
||||
void aluHandleDisconnect(ALCdevice *device);
|
||||
|
||||
|
||||
@@ -506,7 +506,6 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
|
||||
if(newtype != EffectSlot->Effect.Type)
|
||||
{
|
||||
ALeffectStateFactory *factory;
|
||||
FPUCtl oldMode;
|
||||
|
||||
factory = getFactoryByType(newtype);
|
||||
if(!factory)
|
||||
@@ -517,19 +516,19 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
|
||||
State = V0(factory,create)();
|
||||
if(!State) return AL_OUT_OF_MEMORY;
|
||||
|
||||
SetMixerFPUMode(&oldMode);
|
||||
START_MIXER_MODE();
|
||||
almtx_lock(&Device->BackendLock);
|
||||
State->OutBuffer = Device->Dry.Buffer;
|
||||
State->OutChannels = Device->Dry.NumChannels;
|
||||
if(V(State,deviceUpdate)(Device) == AL_FALSE)
|
||||
{
|
||||
almtx_unlock(&Device->BackendLock);
|
||||
RestoreFPUMode(&oldMode);
|
||||
LEAVE_MIXER_MODE();
|
||||
ALeffectState_DecRef(State);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
almtx_unlock(&Device->BackendLock);
|
||||
RestoreFPUMode(&oldMode);
|
||||
END_MIXER_MODE();
|
||||
|
||||
if(!effect)
|
||||
{
|
||||
|
||||
+1
-11
@@ -2502,17 +2502,7 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
|
||||
|
||||
case AL_PAUSED:
|
||||
assert(voice != NULL);
|
||||
/* A source that's paused simply resumes. Clear its mixing
|
||||
* parameters and mark it as 'fading' so it fades in from
|
||||
* silence.
|
||||
*/
|
||||
voice->Step = 0;
|
||||
voice->Flags |= VOICE_IS_FADING;
|
||||
memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*
|
||||
voice->NumChannels);
|
||||
for(s = 0;s < device->NumAuxSends;s++)
|
||||
memset(voice->Send[s].Params, 0, sizeof(voice->Send[s].Params[0])*
|
||||
voice->NumChannels);
|
||||
/* A source that's paused simply resumes. */
|
||||
ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
|
||||
ATOMIC_STORE(&source->state, AL_PLAYING, almemory_order_release);
|
||||
goto finish_play;
|
||||
|
||||
+10
-3
@@ -188,11 +188,18 @@
|
||||
#output-limiter = true
|
||||
|
||||
## dither:
|
||||
# Applies dithering on the final mix for 8- and 16-bit output. This replaces
|
||||
# the distortion created by nearest-value quantization with low-level
|
||||
# whitenoise.
|
||||
# Applies dithering on the final mix, for 8- and 16-bit output by default.
|
||||
# This replaces the distortion created by nearest-value quantization with low-
|
||||
# level whitenoise.
|
||||
#dither = true
|
||||
|
||||
## dither-depth:
|
||||
# Quantization bit-depth for dithered output. A value of 0 (or less) will
|
||||
# match the output sample depth. For int32, uint32, and float32 output, 0 will
|
||||
# disable dithering because they're at or beyond the rendered precision. The
|
||||
# maximum dither depth is 24.
|
||||
#dither-depth = 0
|
||||
|
||||
## volume-adjust:
|
||||
# A global volume adjustment for source output, expressed in decibels. The
|
||||
# value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version: 1.18.0.{build}
|
||||
version: 1.18.1.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
|
||||
+12
-1
@@ -15,7 +15,18 @@
|
||||
#endif
|
||||
|
||||
#ifndef HUGE_VALF
|
||||
#define HUGE_VALF (1.0f/0.0f)
|
||||
static const union msvc_inf_hack {
|
||||
unsigned char b[4];
|
||||
float f;
|
||||
} msvc_inf_union = {{ 0x00, 0x00, 0x80, 0x7F }};
|
||||
#define HUGE_VALF (msvc_inf_union.f)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LOG2F
|
||||
static inline float log2f(float f)
|
||||
{
|
||||
return logf(f) / logf(2.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEG2RAD(x) ((float)(x) * (F_PI/180.0f))
|
||||
|
||||
+2
-2
@@ -257,7 +257,7 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi
|
||||
else
|
||||
{
|
||||
sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
|
||||
sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
|
||||
sleeptime += (DWORD)(time_point->tv_sec - curtime.tv_sec)*1000;
|
||||
if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0)
|
||||
return althrd_success;
|
||||
}
|
||||
@@ -364,7 +364,7 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi
|
||||
else
|
||||
{
|
||||
sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
|
||||
sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
|
||||
sleeptime += (DWORD)(time_point->tv_sec - curtime.tv_sec)*1000;
|
||||
}
|
||||
|
||||
IncrementRef(&icond->wait_count);
|
||||
|
||||
@@ -80,6 +80,9 @@
|
||||
/* Define if we have the modff function */
|
||||
#cmakedefine HAVE_MODFF
|
||||
|
||||
/* Define if we have the log2f function */
|
||||
#cmakedefine HAVE_LOG2F
|
||||
|
||||
/* Define if we have the strtof function */
|
||||
#cmakedefine HAVE_STRTOF
|
||||
|
||||
|
||||
+29
-6
@@ -16,6 +16,7 @@
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
|
||||
extern "C" {
|
||||
#include "libavcodec/avcodec.h"
|
||||
@@ -40,6 +41,7 @@ namespace
|
||||
|
||||
static const std::string AppName("alffplay");
|
||||
|
||||
static bool do_direct_out = false;
|
||||
static bool has_latency_check = false;
|
||||
static LPALGETSOURCEDVSOFT alGetSourcedvSOFT;
|
||||
|
||||
@@ -730,6 +732,17 @@ int AudioState::handler()
|
||||
alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, mBuffers);
|
||||
alGenSources(1, &mSource);
|
||||
|
||||
if(do_direct_out)
|
||||
{
|
||||
if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
|
||||
std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
|
||||
else
|
||||
{
|
||||
alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
|
||||
std::cout<< "Direct out enabled" <<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
while(alGetError() == AL_NO_ERROR && !mMovie->mQuit.load())
|
||||
{
|
||||
/* First remove any processed buffers. */
|
||||
@@ -1358,7 +1371,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] <files...>" <<std::endl;
|
||||
std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] [-direct] <files...>" <<std::endl;
|
||||
return 1;
|
||||
}
|
||||
/* Register all formats and codecs */
|
||||
@@ -1418,12 +1431,9 @@ int main(int argc, char *argv[])
|
||||
ALCdevice *dev = NULL;
|
||||
if(argc > 3 && strcmp(argv[1], "-device") == 0)
|
||||
{
|
||||
fileidx = 3;
|
||||
dev = alcOpenDevice(argv[2]);
|
||||
if(dev)
|
||||
{
|
||||
fileidx = 3;
|
||||
return dev;
|
||||
}
|
||||
if(dev) return dev;
|
||||
std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
|
||||
}
|
||||
return alcOpenDevice(nullptr);
|
||||
@@ -1437,6 +1447,19 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ALCchar *name = nullptr;
|
||||
if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT"))
|
||||
name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
|
||||
if(!name || alcGetError(device) != AL_NO_ERROR)
|
||||
name = alcGetString(device, ALC_DEVICE_SPECIFIER);
|
||||
std::cout<< "Opened \""<<name<<"\"" <<std::endl;
|
||||
|
||||
if(fileidx < argc && strcmp(argv[fileidx], "-direct") == 0)
|
||||
{
|
||||
++fileidx;
|
||||
do_direct_out = true;
|
||||
}
|
||||
|
||||
while(fileidx < argc && !movState)
|
||||
{
|
||||
movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* OpenAL Recording Example
|
||||
*
|
||||
* Copyright (c) 2017 by Chris Robinson <chris.kcat@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This file contains a relatively simple recorder. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "common/alhelpers.h"
|
||||
|
||||
|
||||
#if defined(_WIN64)
|
||||
#define SZFMT "%I64u"
|
||||
#elif defined(_WIN32)
|
||||
#define SZFMT "%u"
|
||||
#else
|
||||
#define SZFMT "%zu"
|
||||
#endif
|
||||
|
||||
|
||||
static void fwrite16le(ALushort val, FILE *f)
|
||||
{
|
||||
ALubyte data[2] = { val&0xff, (val>>8)&0xff };
|
||||
fwrite(data, 1, 2, f);
|
||||
}
|
||||
|
||||
static void fwrite32le(ALuint val, FILE *f)
|
||||
{
|
||||
ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
|
||||
fwrite(data, 1, 4, f);
|
||||
}
|
||||
|
||||
|
||||
typedef struct Recorder {
|
||||
ALCdevice *mDevice;
|
||||
|
||||
FILE *mFile;
|
||||
long mDataSizeOffset;
|
||||
size_t mDataSize;
|
||||
float mRecTime;
|
||||
|
||||
int mChannels;
|
||||
int mBits;
|
||||
int mSampleRate;
|
||||
size_t mFrameSize;
|
||||
ALbyte *mBuffer;
|
||||
ALsizei mBufferSize;
|
||||
} Recorder;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const char optlist[] =
|
||||
" --channels/-c <channels> Set channel count (1 or 2)\n"
|
||||
" --bits/-b <bits> Set channel count (8, 16, or 32)\n"
|
||||
" --rate/-r <rate> Set sample rate (8000 to 96000)\n"
|
||||
" --time/-t <time> Time in seconds to record (1 to 10)\n"
|
||||
" --outfile/-o <filename> Output filename (default: record.wav)";
|
||||
const char *fname = "record.wav";
|
||||
const char *devname = NULL;
|
||||
const char *progname;
|
||||
Recorder recorder;
|
||||
long total_size;
|
||||
ALenum format;
|
||||
ALCenum err;
|
||||
|
||||
progname = argv[0];
|
||||
if(argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Record from a device to a wav file.\n\n"
|
||||
"Usage: %s [-device <name>] [options...]\n\n"
|
||||
"Available options:\n%s\n", progname, optlist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
recorder.mDevice = NULL;
|
||||
recorder.mFile = NULL;
|
||||
recorder.mDataSizeOffset = 0;
|
||||
recorder.mDataSize = 0;
|
||||
recorder.mRecTime = 4.0f;
|
||||
recorder.mChannels = 1;
|
||||
recorder.mBits = 16;
|
||||
recorder.mSampleRate = 44100;
|
||||
recorder.mFrameSize = recorder.mChannels * recorder.mBits / 8;
|
||||
recorder.mBuffer = NULL;
|
||||
recorder.mBufferSize = 0;
|
||||
|
||||
argv++; argc--;
|
||||
if(argc > 1 && strcmp(argv[0], "-device") == 0)
|
||||
{
|
||||
devname = argv[1];
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
|
||||
while(argc > 0)
|
||||
{
|
||||
char *end;
|
||||
if(strcmp(argv[0], "--") == 0)
|
||||
break;
|
||||
else if(strcmp(argv[0], "--channels") == 0 || strcmp(argv[0], "-c") == 0)
|
||||
{
|
||||
if(!(argc > 1))
|
||||
{
|
||||
fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
recorder.mChannels = strtol(argv[1], &end, 0);
|
||||
if((recorder.mChannels != 1 && recorder.mChannels != 2) || (end && *end != '\0'))
|
||||
{
|
||||
fprintf(stderr, "Invalid channels: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if(strcmp(argv[0], "--bits") == 0 || strcmp(argv[0], "-b") == 0)
|
||||
{
|
||||
if(!(argc > 1))
|
||||
{
|
||||
fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
recorder.mBits = strtol(argv[1], &end, 0);
|
||||
if((recorder.mBits != 8 && recorder.mBits != 16 && recorder.mBits != 32) ||
|
||||
(end && *end != '\0'))
|
||||
{
|
||||
fprintf(stderr, "Invalid bit count: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if(strcmp(argv[0], "--rate") == 0 || strcmp(argv[0], "-r") == 0)
|
||||
{
|
||||
if(!(argc > 1))
|
||||
{
|
||||
fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
recorder.mSampleRate = strtol(argv[1], &end, 0);
|
||||
if(!(recorder.mSampleRate >= 8000 && recorder.mSampleRate <= 96000) || (end && *end != '\0'))
|
||||
{
|
||||
fprintf(stderr, "Invalid sample rate: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if(strcmp(argv[0], "--time") == 0 || strcmp(argv[0], "-t") == 0)
|
||||
{
|
||||
if(!(argc > 1))
|
||||
{
|
||||
fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
recorder.mRecTime = strtod(argv[1], &end);
|
||||
if(!(recorder.mRecTime >= 1.0f && recorder.mRecTime <= 10.0f) || (end && *end != '\0'))
|
||||
{
|
||||
fprintf(stderr, "Invalid record time: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if(strcmp(argv[0], "--outfile") == 0 || strcmp(argv[0], "-o") == 0)
|
||||
{
|
||||
if(!(argc > 1))
|
||||
{
|
||||
fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fname = argv[1];
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if(strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-h") == 0)
|
||||
{
|
||||
fprintf(stderr, "Record from a device to a wav file.\n\n"
|
||||
"Usage: %s [-device <name>] [options...]\n\n"
|
||||
"Available options:\n%s\n", progname, optlist);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Invalid option '%s'.\n\n"
|
||||
"Usage: %s [-device <name>] [options...]\n\n"
|
||||
"Available options:\n%s\n", argv[0], progname, optlist);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
recorder.mFrameSize = recorder.mChannels * recorder.mBits / 8;
|
||||
|
||||
format = AL_NONE;
|
||||
if(recorder.mChannels == 1)
|
||||
{
|
||||
if(recorder.mBits == 8)
|
||||
format = AL_FORMAT_MONO8;
|
||||
else if(recorder.mBits == 16)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if(recorder.mBits == 32)
|
||||
format = AL_FORMAT_MONO_FLOAT32;
|
||||
}
|
||||
else if(recorder.mChannels == 2)
|
||||
{
|
||||
if(recorder.mBits == 8)
|
||||
format = AL_FORMAT_STEREO8;
|
||||
else if(recorder.mBits == 16)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else if(recorder.mBits == 32)
|
||||
format = AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
|
||||
recorder.mDevice = alcCaptureOpenDevice(devname, recorder.mSampleRate, format, 32768);
|
||||
if(!recorder.mDevice)
|
||||
{
|
||||
fprintf(stderr, "Failed to open %s, %s %d-bit, %s, %dhz (%d samples)\n",
|
||||
devname ? devname : "default device",
|
||||
(recorder.mBits == 32) ? "Float" :
|
||||
(recorder.mBits != 8) ? "Signed" : "Unsigned", recorder.mBits,
|
||||
(recorder.mChannels == 1) ? "Mono" : "Stereo", recorder.mSampleRate,
|
||||
32768
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "Opened \"%s\"\n", alcGetString(
|
||||
recorder.mDevice, ALC_CAPTURE_DEVICE_SPECIFIER
|
||||
));
|
||||
|
||||
recorder.mFile = fopen(fname, "wb");
|
||||
if(!recorder.mFile)
|
||||
{
|
||||
fprintf(stderr, "Failed to open '%s' for writing\n", fname);
|
||||
alcCaptureCloseDevice(recorder.mDevice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fputs("RIFF", recorder.mFile);
|
||||
fwrite32le(0xFFFFFFFF, recorder.mFile); // 'RIFF' header len; filled in at close
|
||||
|
||||
fputs("WAVE", recorder.mFile);
|
||||
|
||||
fputs("fmt ", recorder.mFile);
|
||||
fwrite32le(18, recorder.mFile); // 'fmt ' header len
|
||||
|
||||
// 16-bit val, format type id (1 = integer PCM, 3 = float PCM)
|
||||
fwrite16le((recorder.mBits == 32) ? 0x0003 : 0x0001, recorder.mFile);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(recorder.mChannels, recorder.mFile);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(recorder.mSampleRate, recorder.mFile);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(recorder.mSampleRate * recorder.mFrameSize, recorder.mFile);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(recorder.mFrameSize, recorder.mFile);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(recorder.mBits, recorder.mFile);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(0, recorder.mFile);
|
||||
|
||||
fputs("data", recorder.mFile);
|
||||
fwrite32le(0xFFFFFFFF, recorder.mFile); // 'data' header len; filled in at close
|
||||
|
||||
recorder.mDataSizeOffset = ftell(recorder.mFile) - 4;
|
||||
if(ferror(recorder.mFile) || recorder.mDataSizeOffset < 0)
|
||||
{
|
||||
fprintf(stderr, "Error writing header: %s\n", strerror(errno));
|
||||
fclose(recorder.mFile);
|
||||
alcCaptureCloseDevice(recorder.mDevice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Recording '%s', %s %d-bit, %s, %dhz (%g second%s)\n", fname,
|
||||
(recorder.mBits == 32) ? "Float" :
|
||||
(recorder.mBits != 8) ? "Signed" : "Unsigned", recorder.mBits,
|
||||
(recorder.mChannels == 1) ? "Mono" : "Stereo", recorder.mSampleRate,
|
||||
recorder.mRecTime, (recorder.mRecTime != 1.0f) ? "s" : ""
|
||||
);
|
||||
|
||||
alcCaptureStart(recorder.mDevice);
|
||||
while((double)recorder.mDataSize/(double)recorder.mSampleRate < recorder.mRecTime &&
|
||||
(err=alcGetError(recorder.mDevice)) == ALC_NO_ERROR && !ferror(recorder.mFile))
|
||||
{
|
||||
ALCint count = 0;
|
||||
fprintf(stderr, "\rCaptured "SZFMT" samples", recorder.mDataSize);
|
||||
alcGetIntegerv(recorder.mDevice, ALC_CAPTURE_SAMPLES, 1, &count);
|
||||
if(count < 1)
|
||||
{
|
||||
al_nssleep(10000000);
|
||||
continue;
|
||||
}
|
||||
if(count > recorder.mBufferSize)
|
||||
{
|
||||
ALbyte *data = calloc(recorder.mFrameSize, count);
|
||||
free(recorder.mBuffer);
|
||||
recorder.mBuffer = data;
|
||||
recorder.mBufferSize = count;
|
||||
}
|
||||
alcCaptureSamples(recorder.mDevice, recorder.mBuffer, count);
|
||||
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
|
||||
/* Byteswap multibyte samples on big-endian systems (wav needs little-
|
||||
* endian, and OpenAL gives the system's native-endian).
|
||||
*/
|
||||
if(recorder.mBits == 16)
|
||||
{
|
||||
ALCint i;
|
||||
for(i = 0;i < count*recorder.mChannels;i++)
|
||||
{
|
||||
ALbyte b = recorder.mBuffer[i*2 + 0];
|
||||
recorder.mBuffer[i*2 + 0] = recorder.mBuffer[i*2 + 1];
|
||||
recorder.mBuffer[i*2 + 1] = b;
|
||||
}
|
||||
}
|
||||
else if(recorder.mBits == 32)
|
||||
{
|
||||
ALCint i;
|
||||
for(i = 0;i < count*recorder.mChannels;i++)
|
||||
{
|
||||
ALbyte b0 = recorder.mBuffer[i*4 + 0];
|
||||
ALbyte b1 = recorder.mBuffer[i*4 + 1];
|
||||
recorder.mBuffer[i*4 + 0] = recorder.mBuffer[i*4 + 3];
|
||||
recorder.mBuffer[i*4 + 1] = recorder.mBuffer[i*4 + 2];
|
||||
recorder.mBuffer[i*4 + 2] = b1;
|
||||
recorder.mBuffer[i*4 + 3] = b0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
recorder.mDataSize += fwrite(recorder.mBuffer, recorder.mFrameSize, count, recorder.mFile);
|
||||
}
|
||||
alcCaptureStop(recorder.mDevice);
|
||||
fprintf(stderr, "\rCaptured "SZFMT" samples\n", recorder.mDataSize);
|
||||
if(err != ALC_NO_ERROR)
|
||||
fprintf(stderr, "Got device error 0x%04x: %s\n", err, alcGetString(recorder.mDevice, err));
|
||||
|
||||
alcCaptureCloseDevice(recorder.mDevice);
|
||||
recorder.mDevice = NULL;
|
||||
|
||||
free(recorder.mBuffer);
|
||||
recorder.mBuffer = NULL;
|
||||
recorder.mBufferSize = 0;
|
||||
|
||||
total_size = ftell(recorder.mFile);
|
||||
if(fseek(recorder.mFile, recorder.mDataSizeOffset, SEEK_SET) == 0)
|
||||
{
|
||||
fwrite32le(recorder.mDataSize*recorder.mFrameSize, recorder.mFile);
|
||||
if(fseek(recorder.mFile, 4, SEEK_SET) == 0)
|
||||
fwrite32le(total_size - 8, recorder.mFile);
|
||||
}
|
||||
|
||||
fclose(recorder.mFile);
|
||||
recorder.mFile = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+50
-19
@@ -35,6 +35,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
@@ -53,6 +54,7 @@ enum WaveType {
|
||||
WT_Sawtooth,
|
||||
WT_Triangle,
|
||||
WT_Impulse,
|
||||
WT_WhiteNoise,
|
||||
};
|
||||
|
||||
static const char *GetWaveTypeName(enum WaveType type)
|
||||
@@ -64,10 +66,17 @@ static const char *GetWaveTypeName(enum WaveType type)
|
||||
case WT_Sawtooth: return "sawtooth";
|
||||
case WT_Triangle: return "triangle";
|
||||
case WT_Impulse: return "impulse";
|
||||
case WT_WhiteNoise: return "noise";
|
||||
}
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
static inline ALuint dither_rng(ALuint *seed)
|
||||
{
|
||||
*seed = (*seed * 96314165) + 907633515;
|
||||
return *seed;
|
||||
}
|
||||
|
||||
static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
|
||||
{
|
||||
ALdouble smps_per_cycle = (ALdouble)srate / freq;
|
||||
@@ -81,6 +90,7 @@ static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
|
||||
*/
|
||||
static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate)
|
||||
{
|
||||
ALuint seed = 22222;
|
||||
ALint data_size;
|
||||
ALfloat *data;
|
||||
ALuint buffer;
|
||||
@@ -89,25 +99,44 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate)
|
||||
|
||||
data_size = srate * sizeof(ALfloat);
|
||||
data = calloc(1, data_size);
|
||||
if(type == WT_Sine)
|
||||
ApplySin(data, 1.0, srate, freq);
|
||||
else if(type == WT_Square)
|
||||
for(i = 1;freq*i < srate/2;i+=2)
|
||||
ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
|
||||
else if(type == WT_Sawtooth)
|
||||
for(i = 1;freq*i < srate/2;i++)
|
||||
ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
|
||||
else if(type == WT_Triangle)
|
||||
for(i = 1;freq*i < srate/2;i+=2)
|
||||
ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
|
||||
else if(type == WT_Impulse)
|
||||
switch(type)
|
||||
{
|
||||
/* NOTE: Impulse isn't really a waveform, but it can still be useful to
|
||||
* test (other than resampling, the ALSOFT_DEFAULT_REVERB environment
|
||||
* variable can prove useful here to test the reverb response).
|
||||
*/
|
||||
for(i = 0;i < srate;i++)
|
||||
data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
|
||||
case WT_Sine:
|
||||
ApplySin(data, 1.0, srate, freq);
|
||||
break;
|
||||
case WT_Square:
|
||||
for(i = 1;freq*i < srate/2;i+=2)
|
||||
ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
|
||||
break;
|
||||
case WT_Sawtooth:
|
||||
for(i = 1;freq*i < srate/2;i++)
|
||||
ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
|
||||
break;
|
||||
case WT_Triangle:
|
||||
for(i = 1;freq*i < srate/2;i+=2)
|
||||
ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
|
||||
break;
|
||||
case WT_Impulse:
|
||||
/* NOTE: Impulse isn't handled using additive synthesis, and is
|
||||
* instead just a non-0 sample at a given rate. This can still be
|
||||
* useful to test (other than resampling, the ALSOFT_DEFAULT_REVERB
|
||||
* environment variable can prove useful here to test the reverb
|
||||
* response).
|
||||
*/
|
||||
for(i = 0;i < srate;i++)
|
||||
data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
|
||||
break;
|
||||
case WT_WhiteNoise:
|
||||
/* NOTE: WhiteNoise is just uniform set of uncorrelated values, and
|
||||
* is not influenced by the waveform frequency.
|
||||
*/
|
||||
for(i = 0;i < srate;i++)
|
||||
{
|
||||
ALuint rng0 = dither_rng(&seed);
|
||||
ALuint rng1 = dither_rng(&seed);
|
||||
data[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Buffer the audio data into a new buffer object. */
|
||||
@@ -166,7 +195,7 @@ int main(int argc, char *argv[])
|
||||
" --help/-h This help text\n"
|
||||
" -t <seconds> Time to play a tone (default 5 seconds)\n"
|
||||
" --waveform/-w <type> Waveform type: sine (default), square, sawtooth,\n"
|
||||
" triangle, impulse\n"
|
||||
" triangle, impulse, noise\n"
|
||||
" --freq/-f <hz> Tone frequency (default 1000 hz)\n"
|
||||
" --srate/-s <sample rate> Sampling rate (default output rate)\n",
|
||||
appname
|
||||
@@ -192,6 +221,8 @@ int main(int argc, char *argv[])
|
||||
wavetype = WT_Triangle;
|
||||
else if(strcmp(argv[i], "impulse") == 0)
|
||||
wavetype = WT_Impulse;
|
||||
else if(strcmp(argv[i], "noise") == 0)
|
||||
wavetype = WT_WhiteNoise;
|
||||
else
|
||||
fprintf(stderr, "Unhandled waveform: %s\n", argv[i]);
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
# AmbDec configuration
|
||||
# Written by Ambisonic Decoder Toolbox, version 8.0
|
||||
|
||||
# input channel order: WYXVU
|
||||
|
||||
/description itu50-noCenter_2h0p_allrad_5200_rE_max_1_band
|
||||
|
||||
# Although unused in this configuration, the front-center is declared here so
|
||||
# that an appropriate distance may be set (for proper delaying or attenuating
|
||||
# of dialog and such which feed it directly). It otherwise does not contribute
|
||||
# to positional sound output.
|
||||
|
||||
/version 3
|
||||
|
||||
/dec/chan_mask 11b
|
||||
/dec/freq_bands 1
|
||||
/dec/speakers 5
|
||||
/dec/coeff_scale fuma
|
||||
|
||||
/opt/input_scale fuma
|
||||
/opt/nfeff_comp input
|
||||
/opt/delay_comp on
|
||||
/opt/level_comp on
|
||||
/opt/xover_freq 400.000000
|
||||
/opt/xover_ratio 0.000000
|
||||
|
||||
/speakers/{
|
||||
# id dist azim elev conn
|
||||
#-----------------------------------------------------------------------
|
||||
add_spkr LS 1.000000 110.000000 0.000000 system:playback_3
|
||||
add_spkr LF 1.000000 30.000000 0.000000 system:playback_1
|
||||
add_spkr CE 1.000000 0.000000 0.000000 system:playback_5
|
||||
add_spkr RF 1.000000 -30.000000 0.000000 system:playback_2
|
||||
add_spkr RS 1.000000 -110.000000 0.000000 system:playback_4
|
||||
/}
|
||||
|
||||
/matrix/{
|
||||
order_gain 1.00000000e+00 8.66025404e-01 5.00000000e-01 0.000000
|
||||
add_row 4.70934222e-01 3.78169605e-01 -4.00084750e-01 -8.22264454e-02 -4.43765986e-02
|
||||
add_row 2.66639870e-01 2.55418584e-01 3.32591390e-01 2.82949132e-01 8.16816772e-02
|
||||
add_row 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
||||
add_row 2.66634915e-01 -2.55421639e-01 3.32586482e-01 -2.82947688e-01 8.16782588e-02
|
||||
add_row 4.70935891e-01 -3.78173080e-01 -4.00080588e-01 8.22279700e-02 -4.43716394e-02
|
||||
/}
|
||||
|
||||
/end
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "router.h"
|
||||
|
||||
|
||||
ATOMIC(DriverIface*) CurrentCtxDriver = ATOMIC_INIT_STATIC(NULL);
|
||||
|
||||
#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) \
|
||||
{ \
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver); \
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
|
||||
return iface->n(a); \
|
||||
}
|
||||
#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) \
|
||||
{ \
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver); \
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
|
||||
return iface->n(a, b); \
|
||||
}
|
||||
#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) \
|
||||
{ \
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver); \
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
|
||||
return iface->n(a, b, c); \
|
||||
}
|
||||
#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) \
|
||||
{ \
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver); \
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
|
||||
return iface->n(a, b, c, d); \
|
||||
}
|
||||
#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) \
|
||||
{ \
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver); \
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
|
||||
return iface->n(a, b, c, d, e); \
|
||||
}
|
||||
|
||||
|
||||
/* Ugly hack for some apps calling alGetError without a current context, and
|
||||
* expecting it to be AL_NO_ERROR.
|
||||
*/
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
{
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver);
|
||||
if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);
|
||||
return iface ? iface->alGetError() : AL_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
DECL_THUNK1(void, alDopplerFactor, ALfloat)
|
||||
DECL_THUNK1(void, alDopplerVelocity, ALfloat)
|
||||
DECL_THUNK1(void, alSpeedOfSound, ALfloat)
|
||||
DECL_THUNK1(void, alDistanceModel, ALenum)
|
||||
|
||||
DECL_THUNK1(void, alEnable, ALenum)
|
||||
DECL_THUNK1(void, alDisable, ALenum)
|
||||
DECL_THUNK1(ALboolean, alIsEnabled, ALenum)
|
||||
|
||||
DECL_THUNK1(const ALchar*, alGetString, ALenum)
|
||||
DECL_THUNK2(void, alGetBooleanv, ALenum, ALboolean*)
|
||||
DECL_THUNK2(void, alGetIntegerv, ALenum, ALint*)
|
||||
DECL_THUNK2(void, alGetFloatv, ALenum, ALfloat*)
|
||||
DECL_THUNK2(void, alGetDoublev, ALenum, ALdouble*)
|
||||
DECL_THUNK1(ALboolean, alGetBoolean, ALenum)
|
||||
DECL_THUNK1(ALint, alGetInteger, ALenum)
|
||||
DECL_THUNK1(ALfloat, alGetFloat, ALenum)
|
||||
DECL_THUNK1(ALdouble, alGetDouble, ALenum)
|
||||
|
||||
DECL_THUNK1(ALboolean, alIsExtensionPresent, const ALchar*)
|
||||
DECL_THUNK1(void*, alGetProcAddress, const ALchar*)
|
||||
DECL_THUNK1(ALenum, alGetEnumValue, const ALchar*)
|
||||
|
||||
DECL_THUNK2(void, alListenerf, ALenum, ALfloat)
|
||||
DECL_THUNK4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat)
|
||||
DECL_THUNK2(void, alListenerfv, ALenum, const ALfloat*)
|
||||
DECL_THUNK2(void, alListeneri, ALenum, ALint)
|
||||
DECL_THUNK4(void, alListener3i, ALenum, ALint, ALint, ALint)
|
||||
DECL_THUNK2(void, alListeneriv, ALenum, const ALint*)
|
||||
DECL_THUNK2(void, alGetListenerf, ALenum, ALfloat*)
|
||||
DECL_THUNK4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*)
|
||||
DECL_THUNK2(void, alGetListenerfv, ALenum, ALfloat*)
|
||||
DECL_THUNK2(void, alGetListeneri, ALenum, ALint*)
|
||||
DECL_THUNK4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*)
|
||||
DECL_THUNK2(void, alGetListeneriv, ALenum, ALint*)
|
||||
|
||||
DECL_THUNK2(void, alGenSources, ALsizei, ALuint*)
|
||||
DECL_THUNK2(void, alDeleteSources, ALsizei, const ALuint*)
|
||||
DECL_THUNK1(ALboolean, alIsSource, ALuint)
|
||||
DECL_THUNK3(void, alSourcef, ALuint, ALenum, ALfloat)
|
||||
DECL_THUNK5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
|
||||
DECL_THUNK3(void, alSourcefv, ALuint, ALenum, const ALfloat*)
|
||||
DECL_THUNK3(void, alSourcei, ALuint, ALenum, ALint)
|
||||
DECL_THUNK5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint)
|
||||
DECL_THUNK3(void, alSourceiv, ALuint, ALenum, const ALint*)
|
||||
DECL_THUNK3(void, alGetSourcef, ALuint, ALenum, ALfloat*)
|
||||
DECL_THUNK5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
|
||||
DECL_THUNK3(void, alGetSourcefv, ALuint, ALenum, ALfloat*)
|
||||
DECL_THUNK3(void, alGetSourcei, ALuint, ALenum, ALint*)
|
||||
DECL_THUNK5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*)
|
||||
DECL_THUNK3(void, alGetSourceiv, ALuint, ALenum, ALint*)
|
||||
DECL_THUNK2(void, alSourcePlayv, ALsizei, const ALuint*)
|
||||
DECL_THUNK2(void, alSourceStopv, ALsizei, const ALuint*)
|
||||
DECL_THUNK2(void, alSourceRewindv, ALsizei, const ALuint*)
|
||||
DECL_THUNK2(void, alSourcePausev, ALsizei, const ALuint*)
|
||||
DECL_THUNK1(void, alSourcePlay, ALuint)
|
||||
DECL_THUNK1(void, alSourceStop, ALuint)
|
||||
DECL_THUNK1(void, alSourceRewind, ALuint)
|
||||
DECL_THUNK1(void, alSourcePause, ALuint)
|
||||
DECL_THUNK3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*)
|
||||
DECL_THUNK3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*)
|
||||
|
||||
DECL_THUNK2(void, alGenBuffers, ALsizei, ALuint*)
|
||||
DECL_THUNK2(void, alDeleteBuffers, ALsizei, const ALuint*)
|
||||
DECL_THUNK1(ALboolean, alIsBuffer, ALuint)
|
||||
DECL_THUNK3(void, alBufferf, ALuint, ALenum, ALfloat)
|
||||
DECL_THUNK5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
|
||||
DECL_THUNK3(void, alBufferfv, ALuint, ALenum, const ALfloat*)
|
||||
DECL_THUNK3(void, alBufferi, ALuint, ALenum, ALint)
|
||||
DECL_THUNK5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint)
|
||||
DECL_THUNK3(void, alBufferiv, ALuint, ALenum, const ALint*)
|
||||
DECL_THUNK3(void, alGetBufferf, ALuint, ALenum, ALfloat*)
|
||||
DECL_THUNK5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
|
||||
DECL_THUNK3(void, alGetBufferfv, ALuint, ALenum, ALfloat*)
|
||||
DECL_THUNK3(void, alGetBufferi, ALuint, ALenum, ALint*)
|
||||
DECL_THUNK5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*)
|
||||
DECL_THUNK3(void, alGetBufferiv, ALuint, ALenum, ALint*)
|
||||
DECL_THUNK5(void, alBufferData, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei)
|
||||
+956
@@ -0,0 +1,956 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "router.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define DECL(x) { #x, (ALCvoid*)(x) }
|
||||
static const struct {
|
||||
const ALCchar *funcName;
|
||||
ALCvoid *address;
|
||||
} alcFunctions[] = {
|
||||
DECL(alcCreateContext),
|
||||
DECL(alcMakeContextCurrent),
|
||||
DECL(alcProcessContext),
|
||||
DECL(alcSuspendContext),
|
||||
DECL(alcDestroyContext),
|
||||
DECL(alcGetCurrentContext),
|
||||
DECL(alcGetContextsDevice),
|
||||
DECL(alcOpenDevice),
|
||||
DECL(alcCloseDevice),
|
||||
DECL(alcGetError),
|
||||
DECL(alcIsExtensionPresent),
|
||||
DECL(alcGetProcAddress),
|
||||
DECL(alcGetEnumValue),
|
||||
DECL(alcGetString),
|
||||
DECL(alcGetIntegerv),
|
||||
DECL(alcCaptureOpenDevice),
|
||||
DECL(alcCaptureCloseDevice),
|
||||
DECL(alcCaptureStart),
|
||||
DECL(alcCaptureStop),
|
||||
DECL(alcCaptureSamples),
|
||||
|
||||
DECL(alcSetThreadContext),
|
||||
DECL(alcGetThreadContext),
|
||||
|
||||
DECL(alEnable),
|
||||
DECL(alDisable),
|
||||
DECL(alIsEnabled),
|
||||
DECL(alGetString),
|
||||
DECL(alGetBooleanv),
|
||||
DECL(alGetIntegerv),
|
||||
DECL(alGetFloatv),
|
||||
DECL(alGetDoublev),
|
||||
DECL(alGetBoolean),
|
||||
DECL(alGetInteger),
|
||||
DECL(alGetFloat),
|
||||
DECL(alGetDouble),
|
||||
DECL(alGetError),
|
||||
DECL(alIsExtensionPresent),
|
||||
DECL(alGetProcAddress),
|
||||
DECL(alGetEnumValue),
|
||||
DECL(alListenerf),
|
||||
DECL(alListener3f),
|
||||
DECL(alListenerfv),
|
||||
DECL(alListeneri),
|
||||
DECL(alListener3i),
|
||||
DECL(alListeneriv),
|
||||
DECL(alGetListenerf),
|
||||
DECL(alGetListener3f),
|
||||
DECL(alGetListenerfv),
|
||||
DECL(alGetListeneri),
|
||||
DECL(alGetListener3i),
|
||||
DECL(alGetListeneriv),
|
||||
DECL(alGenSources),
|
||||
DECL(alDeleteSources),
|
||||
DECL(alIsSource),
|
||||
DECL(alSourcef),
|
||||
DECL(alSource3f),
|
||||
DECL(alSourcefv),
|
||||
DECL(alSourcei),
|
||||
DECL(alSource3i),
|
||||
DECL(alSourceiv),
|
||||
DECL(alGetSourcef),
|
||||
DECL(alGetSource3f),
|
||||
DECL(alGetSourcefv),
|
||||
DECL(alGetSourcei),
|
||||
DECL(alGetSource3i),
|
||||
DECL(alGetSourceiv),
|
||||
DECL(alSourcePlayv),
|
||||
DECL(alSourceStopv),
|
||||
DECL(alSourceRewindv),
|
||||
DECL(alSourcePausev),
|
||||
DECL(alSourcePlay),
|
||||
DECL(alSourceStop),
|
||||
DECL(alSourceRewind),
|
||||
DECL(alSourcePause),
|
||||
DECL(alSourceQueueBuffers),
|
||||
DECL(alSourceUnqueueBuffers),
|
||||
DECL(alGenBuffers),
|
||||
DECL(alDeleteBuffers),
|
||||
DECL(alIsBuffer),
|
||||
DECL(alBufferData),
|
||||
DECL(alBufferf),
|
||||
DECL(alBuffer3f),
|
||||
DECL(alBufferfv),
|
||||
DECL(alBufferi),
|
||||
DECL(alBuffer3i),
|
||||
DECL(alBufferiv),
|
||||
DECL(alGetBufferf),
|
||||
DECL(alGetBuffer3f),
|
||||
DECL(alGetBufferfv),
|
||||
DECL(alGetBufferi),
|
||||
DECL(alGetBuffer3i),
|
||||
DECL(alGetBufferiv),
|
||||
DECL(alDopplerFactor),
|
||||
DECL(alDopplerVelocity),
|
||||
DECL(alSpeedOfSound),
|
||||
DECL(alDistanceModel),
|
||||
};
|
||||
#undef DECL
|
||||
|
||||
#define DECL(x) { #x, (x) }
|
||||
static const struct {
|
||||
const ALCchar *enumName;
|
||||
ALCenum value;
|
||||
} alcEnumerations[] = {
|
||||
DECL(ALC_INVALID),
|
||||
DECL(ALC_FALSE),
|
||||
DECL(ALC_TRUE),
|
||||
|
||||
DECL(ALC_MAJOR_VERSION),
|
||||
DECL(ALC_MINOR_VERSION),
|
||||
DECL(ALC_ATTRIBUTES_SIZE),
|
||||
DECL(ALC_ALL_ATTRIBUTES),
|
||||
DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
|
||||
DECL(ALC_DEVICE_SPECIFIER),
|
||||
DECL(ALC_ALL_DEVICES_SPECIFIER),
|
||||
DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
|
||||
DECL(ALC_EXTENSIONS),
|
||||
DECL(ALC_FREQUENCY),
|
||||
DECL(ALC_REFRESH),
|
||||
DECL(ALC_SYNC),
|
||||
DECL(ALC_MONO_SOURCES),
|
||||
DECL(ALC_STEREO_SOURCES),
|
||||
DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
|
||||
DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
|
||||
DECL(ALC_CAPTURE_SAMPLES),
|
||||
|
||||
DECL(ALC_NO_ERROR),
|
||||
DECL(ALC_INVALID_DEVICE),
|
||||
DECL(ALC_INVALID_CONTEXT),
|
||||
DECL(ALC_INVALID_ENUM),
|
||||
DECL(ALC_INVALID_VALUE),
|
||||
DECL(ALC_OUT_OF_MEMORY),
|
||||
|
||||
DECL(AL_INVALID),
|
||||
DECL(AL_NONE),
|
||||
DECL(AL_FALSE),
|
||||
DECL(AL_TRUE),
|
||||
|
||||
DECL(AL_SOURCE_RELATIVE),
|
||||
DECL(AL_CONE_INNER_ANGLE),
|
||||
DECL(AL_CONE_OUTER_ANGLE),
|
||||
DECL(AL_PITCH),
|
||||
DECL(AL_POSITION),
|
||||
DECL(AL_DIRECTION),
|
||||
DECL(AL_VELOCITY),
|
||||
DECL(AL_LOOPING),
|
||||
DECL(AL_BUFFER),
|
||||
DECL(AL_GAIN),
|
||||
DECL(AL_MIN_GAIN),
|
||||
DECL(AL_MAX_GAIN),
|
||||
DECL(AL_ORIENTATION),
|
||||
DECL(AL_REFERENCE_DISTANCE),
|
||||
DECL(AL_ROLLOFF_FACTOR),
|
||||
DECL(AL_CONE_OUTER_GAIN),
|
||||
DECL(AL_MAX_DISTANCE),
|
||||
DECL(AL_SEC_OFFSET),
|
||||
DECL(AL_SAMPLE_OFFSET),
|
||||
DECL(AL_BYTE_OFFSET),
|
||||
DECL(AL_SOURCE_TYPE),
|
||||
DECL(AL_STATIC),
|
||||
DECL(AL_STREAMING),
|
||||
DECL(AL_UNDETERMINED),
|
||||
|
||||
DECL(AL_SOURCE_STATE),
|
||||
DECL(AL_INITIAL),
|
||||
DECL(AL_PLAYING),
|
||||
DECL(AL_PAUSED),
|
||||
DECL(AL_STOPPED),
|
||||
|
||||
DECL(AL_BUFFERS_QUEUED),
|
||||
DECL(AL_BUFFERS_PROCESSED),
|
||||
|
||||
DECL(AL_FORMAT_MONO8),
|
||||
DECL(AL_FORMAT_MONO16),
|
||||
DECL(AL_FORMAT_STEREO8),
|
||||
DECL(AL_FORMAT_STEREO16),
|
||||
|
||||
DECL(AL_FREQUENCY),
|
||||
DECL(AL_BITS),
|
||||
DECL(AL_CHANNELS),
|
||||
DECL(AL_SIZE),
|
||||
|
||||
DECL(AL_UNUSED),
|
||||
DECL(AL_PENDING),
|
||||
DECL(AL_PROCESSED),
|
||||
|
||||
DECL(AL_NO_ERROR),
|
||||
DECL(AL_INVALID_NAME),
|
||||
DECL(AL_INVALID_ENUM),
|
||||
DECL(AL_INVALID_VALUE),
|
||||
DECL(AL_INVALID_OPERATION),
|
||||
DECL(AL_OUT_OF_MEMORY),
|
||||
|
||||
DECL(AL_VENDOR),
|
||||
DECL(AL_VERSION),
|
||||
DECL(AL_RENDERER),
|
||||
DECL(AL_EXTENSIONS),
|
||||
|
||||
DECL(AL_DOPPLER_FACTOR),
|
||||
DECL(AL_DOPPLER_VELOCITY),
|
||||
DECL(AL_DISTANCE_MODEL),
|
||||
DECL(AL_SPEED_OF_SOUND),
|
||||
|
||||
DECL(AL_INVERSE_DISTANCE),
|
||||
DECL(AL_INVERSE_DISTANCE_CLAMPED),
|
||||
DECL(AL_LINEAR_DISTANCE),
|
||||
DECL(AL_LINEAR_DISTANCE_CLAMPED),
|
||||
DECL(AL_EXPONENT_DISTANCE),
|
||||
DECL(AL_EXPONENT_DISTANCE_CLAMPED),
|
||||
};
|
||||
#undef DECL
|
||||
|
||||
static const ALCchar alcNoError[] = "No Error";
|
||||
static const ALCchar alcErrInvalidDevice[] = "Invalid Device";
|
||||
static const ALCchar alcErrInvalidContext[] = "Invalid Context";
|
||||
static const ALCchar alcErrInvalidEnum[] = "Invalid Enum";
|
||||
static const ALCchar alcErrInvalidValue[] = "Invalid Value";
|
||||
static const ALCchar alcErrOutOfMemory[] = "Out of Memory";
|
||||
static const ALCchar alcExtensionList[] =
|
||||
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
|
||||
"ALC_EXT_thread_local_context";
|
||||
|
||||
static const ALCint alcMajorVersion = 1;
|
||||
static const ALCint alcMinorVersion = 1;
|
||||
|
||||
|
||||
static almtx_t EnumerationLock;
|
||||
static almtx_t ContextSwitchLock;
|
||||
|
||||
static ATOMIC(ALCenum) LastError = ATOMIC_INIT_STATIC(ALC_NO_ERROR);
|
||||
static PtrIntMap DeviceIfaceMap = PTRINTMAP_STATIC_INITIALIZE;
|
||||
static PtrIntMap ContextIfaceMap = PTRINTMAP_STATIC_INITIALIZE;
|
||||
|
||||
|
||||
typedef struct EnumeratedList {
|
||||
ALCchar *Names;
|
||||
ALCchar *NamesEnd;
|
||||
ALCint *Indicies;
|
||||
ALCsizei IndexSize;
|
||||
} EnumeratedList;
|
||||
static EnumeratedList DevicesList = { NULL, NULL, NULL, 0 };
|
||||
static EnumeratedList AllDevicesList = { NULL, NULL, NULL, 0 };
|
||||
static EnumeratedList CaptureDevicesList = { NULL, NULL, NULL, 0 };
|
||||
|
||||
static void ClearDeviceList(EnumeratedList *list)
|
||||
{
|
||||
al_free(list->Names);
|
||||
list->Names = NULL;
|
||||
list->NamesEnd = NULL;
|
||||
|
||||
al_free(list->Indicies);
|
||||
list->Indicies = NULL;
|
||||
list->IndexSize = 0;
|
||||
}
|
||||
|
||||
static void AppendDeviceList(EnumeratedList *list, const ALCchar *names, ALint idx)
|
||||
{
|
||||
const ALCchar *name_end = names;
|
||||
ALCsizei count = 0;
|
||||
ALCchar *new_list;
|
||||
ALCint *new_indicies;
|
||||
size_t len;
|
||||
ALCsizei i;
|
||||
|
||||
if(!name_end)
|
||||
return;
|
||||
while(*name_end)
|
||||
{
|
||||
TRACE("Enumerated \"%s\", driver %d\n", name_end, idx);
|
||||
count++;
|
||||
name_end += strlen(name_end)+1;
|
||||
}
|
||||
if(names == name_end)
|
||||
return;
|
||||
|
||||
len = (list->NamesEnd - list->Names) + (name_end - names);
|
||||
new_list = al_calloc(DEF_ALIGN, len + 1);
|
||||
memcpy(new_list, list->Names, list->NamesEnd - list->Names);
|
||||
memcpy(new_list + (list->NamesEnd - list->Names), names, name_end - names);
|
||||
al_free(list->Names);
|
||||
list->Names = new_list;
|
||||
list->NamesEnd = list->Names + len;
|
||||
|
||||
new_indicies = al_calloc(16, sizeof(ALCint)*(list->IndexSize + count));
|
||||
for(i = 0;i < list->IndexSize;i++)
|
||||
new_indicies[i] = list->Indicies[i];
|
||||
for(i = 0;i < count;i++)
|
||||
new_indicies[list->IndexSize+i] = idx;
|
||||
al_free(list->Indicies);
|
||||
list->Indicies = new_indicies;
|
||||
list->IndexSize += count;
|
||||
}
|
||||
|
||||
static ALint GetDriverIndexForName(const EnumeratedList *list, const ALCchar *name)
|
||||
{
|
||||
const ALCchar *devnames = list->Names;
|
||||
const ALCint *index = list->Indicies;
|
||||
|
||||
while(devnames && *devnames)
|
||||
{
|
||||
if(strcmp(name, devnames) == 0)
|
||||
return *index;
|
||||
devnames += strlen(devnames)+1;
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void InitALC(void)
|
||||
{
|
||||
almtx_init(&EnumerationLock, almtx_recursive);
|
||||
almtx_init(&ContextSwitchLock, almtx_plain);
|
||||
}
|
||||
|
||||
void ReleaseALC(void)
|
||||
{
|
||||
ClearDeviceList(&DevicesList);
|
||||
ClearDeviceList(&AllDevicesList);
|
||||
ClearDeviceList(&CaptureDevicesList);
|
||||
|
||||
ResetPtrIntMap(&ContextIfaceMap);
|
||||
ResetPtrIntMap(&DeviceIfaceMap);
|
||||
|
||||
almtx_destroy(&ContextSwitchLock);
|
||||
almtx_destroy(&EnumerationLock);
|
||||
}
|
||||
|
||||
|
||||
ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename)
|
||||
{
|
||||
ALCdevice *device = NULL;
|
||||
ALint idx;
|
||||
|
||||
/* Prior to the enumeration extension, apps would hardcode these names as a
|
||||
* quality hint for the wrapper driver. Ignore them since there's no sane
|
||||
* way to map them.
|
||||
*/
|
||||
if(devicename && (devicename[0] == '\0' ||
|
||||
strcmp(devicename, "DirectSound3D") == 0 ||
|
||||
strcmp(devicename, "DirectSound") == 0 ||
|
||||
strcmp(devicename, "MMSYSTEM") == 0))
|
||||
devicename = NULL;
|
||||
if(devicename)
|
||||
{
|
||||
almtx_lock(&EnumerationLock);
|
||||
if(!DevicesList.Names)
|
||||
(void)alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
idx = GetDriverIndexForName(&DevicesList, devicename);
|
||||
if(idx < 0)
|
||||
{
|
||||
if(!AllDevicesList.Names)
|
||||
(void)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
idx = GetDriverIndexForName(&AllDevicesList, devicename);
|
||||
}
|
||||
almtx_unlock(&EnumerationLock);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
|
||||
TRACE("Failed to find driver for name \"%s\"\n", devicename);
|
||||
return NULL;
|
||||
}
|
||||
TRACE("Found driver %d for name \"%s\"\n", idx, devicename);
|
||||
device = DriverList[idx].alcOpenDevice(devicename);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
|
||||
{
|
||||
idx = i;
|
||||
TRACE("Using default device from driver %d\n", idx);
|
||||
device = DriverList[idx].alcOpenDevice(NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(device)
|
||||
{
|
||||
if(InsertPtrIntMapEntry(&DeviceIfaceMap, device, idx) != ALC_NO_ERROR)
|
||||
{
|
||||
DriverList[idx].alcCloseDevice(device);
|
||||
device = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
|
||||
{
|
||||
ALint idx;
|
||||
|
||||
if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
if(!DriverList[idx].alcCloseDevice(device))
|
||||
return ALC_FALSE;
|
||||
RemovePtrIntMapKey(&DeviceIfaceMap, device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALint idx;
|
||||
|
||||
if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
context = DriverList[idx].alcCreateContext(device, attrlist);
|
||||
if(context)
|
||||
{
|
||||
if(InsertPtrIntMapEntry(&ContextIfaceMap, context, idx) != ALC_NO_ERROR)
|
||||
{
|
||||
DriverList[idx].alcDestroyContext(context);
|
||||
context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
|
||||
{
|
||||
ALint idx = -1;
|
||||
|
||||
almtx_lock(&ContextSwitchLock);
|
||||
if(context)
|
||||
{
|
||||
idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
|
||||
almtx_unlock(&ContextSwitchLock);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
if(!DriverList[idx].alcMakeContextCurrent(context))
|
||||
{
|
||||
almtx_unlock(&ContextSwitchLock);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unset the context from the old driver if it's different from the new
|
||||
* current one.
|
||||
*/
|
||||
if(idx < 0)
|
||||
{
|
||||
DriverIface *oldiface = altss_get(ThreadCtxDriver);
|
||||
if(oldiface) oldiface->alcSetThreadContext(NULL);
|
||||
oldiface = ATOMIC_EXCHANGE_PTR_SEQ(&CurrentCtxDriver, NULL);
|
||||
if(oldiface) oldiface->alcMakeContextCurrent(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
DriverIface *oldiface = altss_get(ThreadCtxDriver);
|
||||
if(oldiface && oldiface != &DriverList[idx])
|
||||
oldiface->alcSetThreadContext(NULL);
|
||||
oldiface = ATOMIC_EXCHANGE_PTR_SEQ(&CurrentCtxDriver, &DriverList[idx]);
|
||||
if(oldiface && oldiface != &DriverList[idx])
|
||||
oldiface->alcMakeContextCurrent(NULL);
|
||||
}
|
||||
almtx_unlock(&ContextSwitchLock);
|
||||
altss_set(ThreadCtxDriver, NULL);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context)
|
||||
{
|
||||
if(context)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcProcessContext(context);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context)
|
||||
{
|
||||
if(context)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcSuspendContext(context);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context)
|
||||
{
|
||||
ALint idx;
|
||||
|
||||
if(!context || (idx=LookupPtrIntMapKey(&ContextIfaceMap, context)) < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
|
||||
return;
|
||||
}
|
||||
|
||||
DriverList[idx].alcDestroyContext(context);
|
||||
RemovePtrIntMapKey(&ContextIfaceMap, context);
|
||||
}
|
||||
|
||||
ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
|
||||
{
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver);
|
||||
if(!iface) iface = ATOMIC_LOAD_SEQ(&CurrentCtxDriver);
|
||||
return iface ? iface->alcGetCurrentContext() : NULL;
|
||||
}
|
||||
|
||||
ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context)
|
||||
{
|
||||
if(context)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcGetContextsDevice(context);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
|
||||
{
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0) return ALC_INVALID_DEVICE;
|
||||
return DriverList[idx].alcGetError(device);
|
||||
}
|
||||
return ATOMIC_EXCHANGE_SEQ(&LastError, ALC_NO_ERROR);
|
||||
}
|
||||
|
||||
ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname)
|
||||
{
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
return DriverList[idx].alcIsExtensionPresent(device, extname);
|
||||
}
|
||||
|
||||
len = strlen(extname);
|
||||
ptr = alcExtensionList;
|
||||
while(ptr && *ptr)
|
||||
{
|
||||
if(strncasecmp(ptr, extname, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
|
||||
return ALC_TRUE;
|
||||
if((ptr=strchr(ptr, ' ')) != NULL)
|
||||
{
|
||||
do {
|
||||
++ptr;
|
||||
} while(isspace(*ptr));
|
||||
}
|
||||
}
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
return DriverList[idx].alcGetProcAddress(device, funcname);
|
||||
}
|
||||
|
||||
for(i = 0;i < COUNTOF(alcFunctions);i++)
|
||||
{
|
||||
if(strcmp(funcname, alcFunctions[i].funcName) == 0)
|
||||
return alcFunctions[i].address;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
return DriverList[idx].alcGetEnumValue(device, enumname);
|
||||
}
|
||||
|
||||
for(i = 0;i < COUNTOF(alcEnumerations);i++)
|
||||
{
|
||||
if(strcmp(enumname, alcEnumerations[i].enumName) == 0)
|
||||
return alcEnumerations[i].value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param)
|
||||
{
|
||||
ALsizei i = 0;
|
||||
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
return DriverList[idx].alcGetString(device, param);
|
||||
}
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case ALC_NO_ERROR:
|
||||
return alcNoError;
|
||||
case ALC_INVALID_ENUM:
|
||||
return alcErrInvalidEnum;
|
||||
case ALC_INVALID_VALUE:
|
||||
return alcErrInvalidValue;
|
||||
case ALC_INVALID_DEVICE:
|
||||
return alcErrInvalidDevice;
|
||||
case ALC_INVALID_CONTEXT:
|
||||
return alcErrInvalidContext;
|
||||
case ALC_OUT_OF_MEMORY:
|
||||
return alcErrOutOfMemory;
|
||||
case ALC_EXTENSIONS:
|
||||
return alcExtensionList;
|
||||
|
||||
case ALC_DEVICE_SPECIFIER:
|
||||
almtx_lock(&EnumerationLock);
|
||||
ClearDeviceList(&DevicesList);
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
/* Only enumerate names from drivers that support it. */
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
|
||||
AppendDeviceList(&DevicesList,
|
||||
DriverList[i].alcGetString(NULL, ALC_DEVICE_SPECIFIER), i
|
||||
);
|
||||
}
|
||||
almtx_unlock(&EnumerationLock);
|
||||
return DevicesList.Names;
|
||||
|
||||
case ALC_ALL_DEVICES_SPECIFIER:
|
||||
almtx_lock(&EnumerationLock);
|
||||
ClearDeviceList(&AllDevicesList);
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
/* If the driver doesn't support ALC_ENUMERATE_ALL_EXT, substitute
|
||||
* standard enumeration.
|
||||
*/
|
||||
if(DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
|
||||
AppendDeviceList(&AllDevicesList,
|
||||
DriverList[i].alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER), i
|
||||
);
|
||||
else if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
|
||||
AppendDeviceList(&AllDevicesList,
|
||||
DriverList[i].alcGetString(NULL, ALC_DEVICE_SPECIFIER), i
|
||||
);
|
||||
}
|
||||
almtx_unlock(&EnumerationLock);
|
||||
return AllDevicesList.Names;
|
||||
|
||||
case ALC_CAPTURE_DEVICE_SPECIFIER:
|
||||
almtx_lock(&EnumerationLock);
|
||||
ClearDeviceList(&CaptureDevicesList);
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
|
||||
AppendDeviceList(&CaptureDevicesList,
|
||||
DriverList[i].alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER), i
|
||||
);
|
||||
}
|
||||
almtx_unlock(&EnumerationLock);
|
||||
return CaptureDevicesList.Names;
|
||||
|
||||
case ALC_DEFAULT_DEVICE_SPECIFIER:
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
|
||||
return DriverList[i].alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
}
|
||||
return "";
|
||||
|
||||
case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
|
||||
return DriverList[i].alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
|
||||
}
|
||||
return "";
|
||||
|
||||
case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
|
||||
return DriverList[i].alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||
}
|
||||
return "";
|
||||
|
||||
default:
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
|
||||
{
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return;
|
||||
}
|
||||
return DriverList[idx].alcGetIntegerv(device, param, size, values);
|
||||
}
|
||||
|
||||
if(size <= 0 || values == NULL)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case ALC_MAJOR_VERSION:
|
||||
if(size >= 1)
|
||||
{
|
||||
values[0] = alcMajorVersion;
|
||||
return;
|
||||
}
|
||||
/*fall-through*/
|
||||
case ALC_MINOR_VERSION:
|
||||
if(size >= 1)
|
||||
{
|
||||
values[0] = alcMinorVersion;
|
||||
return;
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
|
||||
return;
|
||||
|
||||
case ALC_ATTRIBUTES_SIZE:
|
||||
case ALC_ALL_ATTRIBUTES:
|
||||
case ALC_FREQUENCY:
|
||||
case ALC_REFRESH:
|
||||
case ALC_SYNC:
|
||||
case ALC_MONO_SOURCES:
|
||||
case ALC_STEREO_SOURCES:
|
||||
case ALC_CAPTURE_SAMPLES:
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return;
|
||||
|
||||
default:
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_ENUM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize)
|
||||
{
|
||||
ALCdevice *device = NULL;
|
||||
ALint idx;
|
||||
|
||||
if(devicename && devicename[0] == '\0')
|
||||
devicename = NULL;
|
||||
if(devicename)
|
||||
{
|
||||
almtx_lock(&EnumerationLock);
|
||||
if(!CaptureDevicesList.Names)
|
||||
(void)alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
||||
idx = GetDriverIndexForName(&CaptureDevicesList, devicename);
|
||||
almtx_unlock(&EnumerationLock);
|
||||
if(idx < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
|
||||
TRACE("Failed to find driver for name \"%s\"\n", devicename);
|
||||
return NULL;
|
||||
}
|
||||
TRACE("Found driver %d for name \"%s\"\n", idx, devicename);
|
||||
device = DriverList[idx].alcCaptureOpenDevice(
|
||||
devicename, frequency, format, buffersize
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
|
||||
DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
|
||||
{
|
||||
idx = i;
|
||||
TRACE("Using default capture device from driver %d\n", idx);
|
||||
device = DriverList[idx].alcCaptureOpenDevice(
|
||||
NULL, frequency, format, buffersize
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(device)
|
||||
{
|
||||
if(InsertPtrIntMapEntry(&DeviceIfaceMap, device, idx) != ALC_NO_ERROR)
|
||||
{
|
||||
DriverList[idx].alcCaptureCloseDevice(device);
|
||||
device = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
|
||||
{
|
||||
ALint idx;
|
||||
|
||||
if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
if(!DriverList[idx].alcCaptureCloseDevice(device))
|
||||
return ALC_FALSE;
|
||||
RemovePtrIntMapKey(&DeviceIfaceMap, device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
|
||||
{
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcCaptureStart(device);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
|
||||
{
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcCaptureStop(device);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
}
|
||||
|
||||
ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
|
||||
{
|
||||
if(device)
|
||||
{
|
||||
ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
|
||||
if(idx >= 0)
|
||||
return DriverList[idx].alcCaptureSamples(device, buffer, samples);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
|
||||
}
|
||||
|
||||
|
||||
ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
|
||||
{
|
||||
ALCenum err = ALC_INVALID_CONTEXT;
|
||||
ALint idx;
|
||||
|
||||
if(!context)
|
||||
{
|
||||
DriverIface *oldiface = altss_get(ThreadCtxDriver);
|
||||
if(oldiface && !oldiface->alcSetThreadContext(NULL))
|
||||
return ALC_FALSE;
|
||||
altss_set(ThreadCtxDriver, NULL);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
|
||||
if(idx >= 0)
|
||||
{
|
||||
if(DriverList[idx].alcSetThreadContext(context))
|
||||
{
|
||||
DriverIface *oldiface = altss_get(ThreadCtxDriver);
|
||||
if(oldiface != &DriverList[idx])
|
||||
{
|
||||
altss_set(ThreadCtxDriver, &DriverList[idx]);
|
||||
if(oldiface) oldiface->alcSetThreadContext(NULL);
|
||||
}
|
||||
return ALC_TRUE;
|
||||
}
|
||||
err = DriverList[idx].alcGetError(NULL);
|
||||
}
|
||||
ATOMIC_STORE_SEQ(&LastError, err);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
|
||||
{
|
||||
DriverIface *iface = altss_get(ThreadCtxDriver);
|
||||
if(iface) return iface->alcGetThreadContext();
|
||||
return NULL;
|
||||
}
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "router.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
|
||||
DriverIface *DriverList = NULL;
|
||||
int DriverListSize = 0;
|
||||
static int DriverListSizeMax = 0;
|
||||
|
||||
altss_t ThreadCtxDriver;
|
||||
|
||||
enum LogLevel LogLevel = LogLevel_Error;
|
||||
FILE *LogFile;
|
||||
|
||||
static void LoadDriverList(void);
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HINSTANCE UNUSED(module), DWORD reason, void* UNUSED(reserved))
|
||||
{
|
||||
const char *str;
|
||||
int i;
|
||||
|
||||
switch(reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
LogFile = stderr;
|
||||
str = getenv("ALROUTER_LOGFILE");
|
||||
if(str && *str != '\0')
|
||||
{
|
||||
FILE *f = fopen(str, "w");
|
||||
if(f == NULL)
|
||||
ERR("Could not open log file: %s\n", str);
|
||||
else
|
||||
LogFile = f;
|
||||
}
|
||||
str = getenv("ALROUTER_LOGLEVEL");
|
||||
if(str && *str != '\0')
|
||||
{
|
||||
char *end = NULL;
|
||||
long l = strtol(str, &end, 0);
|
||||
if(!end || *end != '\0')
|
||||
ERR("Invalid log level value: %s\n", str);
|
||||
else if(l < LogLevel_None || l > LogLevel_Trace)
|
||||
ERR("Log level out of range: %s\n", str);
|
||||
else
|
||||
LogLevel = l;
|
||||
}
|
||||
TRACE("Initializing router v0.1-%s %s\n", ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
|
||||
LoadDriverList();
|
||||
|
||||
altss_create(&ThreadCtxDriver, NULL);
|
||||
InitALC();
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
ReleaseALC();
|
||||
altss_delete(ThreadCtxDriver);
|
||||
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].Module)
|
||||
FreeLibrary(DriverList[i].Module);
|
||||
}
|
||||
al_free(DriverList);
|
||||
DriverList = NULL;
|
||||
DriverListSize = 0;
|
||||
DriverListSizeMax = 0;
|
||||
|
||||
if(LogFile && LogFile != stderr)
|
||||
fclose(LogFile);
|
||||
LogFile = NULL;
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define CAST_FUNC(x) (__typeof(x))
|
||||
#else
|
||||
#define CAST_FUNC(x) (void*)
|
||||
#endif
|
||||
|
||||
static void AddModule(HMODULE module, const WCHAR *name)
|
||||
{
|
||||
DriverIface newdrv;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
for(i = 0;i < DriverListSize;i++)
|
||||
{
|
||||
if(DriverList[i].Module == module)
|
||||
{
|
||||
TRACE("Skipping already-loaded module %p\n", module);
|
||||
FreeLibrary(module);
|
||||
return;
|
||||
}
|
||||
if(wcscmp(DriverList[i].Name, name) == 0)
|
||||
{
|
||||
TRACE("Skipping similarly-named module %ls\n", name);
|
||||
FreeLibrary(module);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(DriverListSize == DriverListSizeMax)
|
||||
{
|
||||
int newmax = DriverListSizeMax ? DriverListSizeMax<<1 : 4;
|
||||
void *newlist = al_calloc(DEF_ALIGN, sizeof(DriverList[0])*newmax);
|
||||
if(!newlist) return;
|
||||
|
||||
memcpy(newlist, DriverList, DriverListSize*sizeof(DriverList[0]));
|
||||
al_free(DriverList);
|
||||
DriverList = newlist;
|
||||
DriverListSizeMax = newmax;
|
||||
}
|
||||
|
||||
memset(&newdrv, 0, sizeof(newdrv));
|
||||
/* Load required functions. */
|
||||
#define LOAD_PROC(x) do { \
|
||||
newdrv.x = CAST_FUNC(newdrv.x) GetProcAddress(module, #x); \
|
||||
if(!newdrv.x) \
|
||||
{ \
|
||||
ERR("Failed to find entry point for %s in %ls\n", #x, name); \
|
||||
err = 1; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD_PROC(alcCreateContext);
|
||||
LOAD_PROC(alcMakeContextCurrent);
|
||||
LOAD_PROC(alcProcessContext);
|
||||
LOAD_PROC(alcSuspendContext);
|
||||
LOAD_PROC(alcDestroyContext);
|
||||
LOAD_PROC(alcGetCurrentContext);
|
||||
LOAD_PROC(alcGetContextsDevice);
|
||||
LOAD_PROC(alcOpenDevice);
|
||||
LOAD_PROC(alcCloseDevice);
|
||||
LOAD_PROC(alcGetError);
|
||||
LOAD_PROC(alcIsExtensionPresent);
|
||||
LOAD_PROC(alcGetProcAddress);
|
||||
LOAD_PROC(alcGetEnumValue);
|
||||
LOAD_PROC(alcGetString);
|
||||
LOAD_PROC(alcGetIntegerv);
|
||||
LOAD_PROC(alcCaptureOpenDevice);
|
||||
LOAD_PROC(alcCaptureCloseDevice);
|
||||
LOAD_PROC(alcCaptureStart);
|
||||
LOAD_PROC(alcCaptureStop);
|
||||
LOAD_PROC(alcCaptureSamples);
|
||||
|
||||
LOAD_PROC(alEnable);
|
||||
LOAD_PROC(alDisable);
|
||||
LOAD_PROC(alIsEnabled);
|
||||
LOAD_PROC(alGetString);
|
||||
LOAD_PROC(alGetBooleanv);
|
||||
LOAD_PROC(alGetIntegerv);
|
||||
LOAD_PROC(alGetFloatv);
|
||||
LOAD_PROC(alGetDoublev);
|
||||
LOAD_PROC(alGetBoolean);
|
||||
LOAD_PROC(alGetInteger);
|
||||
LOAD_PROC(alGetFloat);
|
||||
LOAD_PROC(alGetDouble);
|
||||
LOAD_PROC(alGetError);
|
||||
LOAD_PROC(alIsExtensionPresent);
|
||||
LOAD_PROC(alGetProcAddress);
|
||||
LOAD_PROC(alGetEnumValue);
|
||||
LOAD_PROC(alListenerf);
|
||||
LOAD_PROC(alListener3f);
|
||||
LOAD_PROC(alListenerfv);
|
||||
LOAD_PROC(alListeneri);
|
||||
LOAD_PROC(alListener3i);
|
||||
LOAD_PROC(alListeneriv);
|
||||
LOAD_PROC(alGetListenerf);
|
||||
LOAD_PROC(alGetListener3f);
|
||||
LOAD_PROC(alGetListenerfv);
|
||||
LOAD_PROC(alGetListeneri);
|
||||
LOAD_PROC(alGetListener3i);
|
||||
LOAD_PROC(alGetListeneriv);
|
||||
LOAD_PROC(alGenSources);
|
||||
LOAD_PROC(alDeleteSources);
|
||||
LOAD_PROC(alIsSource);
|
||||
LOAD_PROC(alSourcef);
|
||||
LOAD_PROC(alSource3f);
|
||||
LOAD_PROC(alSourcefv);
|
||||
LOAD_PROC(alSourcei);
|
||||
LOAD_PROC(alSource3i);
|
||||
LOAD_PROC(alSourceiv);
|
||||
LOAD_PROC(alGetSourcef);
|
||||
LOAD_PROC(alGetSource3f);
|
||||
LOAD_PROC(alGetSourcefv);
|
||||
LOAD_PROC(alGetSourcei);
|
||||
LOAD_PROC(alGetSource3i);
|
||||
LOAD_PROC(alGetSourceiv);
|
||||
LOAD_PROC(alSourcePlayv);
|
||||
LOAD_PROC(alSourceStopv);
|
||||
LOAD_PROC(alSourceRewindv);
|
||||
LOAD_PROC(alSourcePausev);
|
||||
LOAD_PROC(alSourcePlay);
|
||||
LOAD_PROC(alSourceStop);
|
||||
LOAD_PROC(alSourceRewind);
|
||||
LOAD_PROC(alSourcePause);
|
||||
LOAD_PROC(alSourceQueueBuffers);
|
||||
LOAD_PROC(alSourceUnqueueBuffers);
|
||||
LOAD_PROC(alGenBuffers);
|
||||
LOAD_PROC(alDeleteBuffers);
|
||||
LOAD_PROC(alIsBuffer);
|
||||
LOAD_PROC(alBufferf);
|
||||
LOAD_PROC(alBuffer3f);
|
||||
LOAD_PROC(alBufferfv);
|
||||
LOAD_PROC(alBufferi);
|
||||
LOAD_PROC(alBuffer3i);
|
||||
LOAD_PROC(alBufferiv);
|
||||
LOAD_PROC(alGetBufferf);
|
||||
LOAD_PROC(alGetBuffer3f);
|
||||
LOAD_PROC(alGetBufferfv);
|
||||
LOAD_PROC(alGetBufferi);
|
||||
LOAD_PROC(alGetBuffer3i);
|
||||
LOAD_PROC(alGetBufferiv);
|
||||
LOAD_PROC(alBufferData);
|
||||
LOAD_PROC(alDopplerFactor);
|
||||
LOAD_PROC(alDopplerVelocity);
|
||||
LOAD_PROC(alSpeedOfSound);
|
||||
LOAD_PROC(alDistanceModel);
|
||||
if(!err)
|
||||
{
|
||||
ALCint alc_ver[2] = { 0, 0 };
|
||||
wcsncpy(newdrv.Name, name, 32);
|
||||
newdrv.Module = module;
|
||||
newdrv.alcGetIntegerv(NULL, ALC_MAJOR_VERSION, 1, &alc_ver[0]);
|
||||
newdrv.alcGetIntegerv(NULL, ALC_MINOR_VERSION, 1, &alc_ver[1]);
|
||||
if(newdrv.alcGetError(NULL) == ALC_NO_ERROR)
|
||||
newdrv.ALCVer = MAKE_ALC_VER(alc_ver[0], alc_ver[1]);
|
||||
else
|
||||
newdrv.ALCVer = MAKE_ALC_VER(1, 0);
|
||||
|
||||
#undef LOAD_PROC
|
||||
#define LOAD_PROC(x) do { \
|
||||
newdrv.x = CAST_FUNC(newdrv.x) newdrv.alcGetProcAddress(NULL, #x); \
|
||||
if(!newdrv.x) \
|
||||
{ \
|
||||
ERR("Failed to find entry point for %s in %ls\n", #x, name); \
|
||||
err = 1; \
|
||||
} \
|
||||
} while(0)
|
||||
if(newdrv.alcIsExtensionPresent(NULL, "ALC_EXT_thread_local_context"))
|
||||
{
|
||||
LOAD_PROC(alcSetThreadContext);
|
||||
LOAD_PROC(alcGetThreadContext);
|
||||
}
|
||||
}
|
||||
|
||||
if(!err)
|
||||
{
|
||||
TRACE("Loaded module %p, %ls, ALC %d.%d\n", module, name,
|
||||
newdrv.ALCVer>>8, newdrv.ALCVer&255);
|
||||
DriverList[DriverListSize++] = newdrv;
|
||||
}
|
||||
#undef LOAD_PROC
|
||||
}
|
||||
|
||||
static void SearchDrivers(WCHAR *path)
|
||||
{
|
||||
WCHAR srchPath[MAX_PATH+1] = L"";
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE srchHdl;
|
||||
|
||||
TRACE("Searching for drivers in %ls...\n", path);
|
||||
wcsncpy(srchPath, path, MAX_PATH);
|
||||
wcsncat(srchPath, L"\\*oal.dll", MAX_PATH - lstrlenW(srchPath));
|
||||
srchHdl = FindFirstFileW(srchPath, &fdata);
|
||||
if(srchHdl != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do {
|
||||
HMODULE mod;
|
||||
|
||||
wcsncpy(srchPath, path, MAX_PATH);
|
||||
wcsncat(srchPath, L"\\", MAX_PATH - lstrlenW(srchPath));
|
||||
wcsncat(srchPath, fdata.cFileName, MAX_PATH - lstrlenW(srchPath));
|
||||
TRACE("Found %ls\n", srchPath);
|
||||
|
||||
mod = LoadLibraryW(srchPath);
|
||||
if(!mod)
|
||||
WARN("Could not load %ls\n", srchPath);
|
||||
else
|
||||
AddModule(mod, fdata.cFileName);
|
||||
} while(FindNextFileW(srchHdl, &fdata));
|
||||
FindClose(srchHdl);
|
||||
}
|
||||
}
|
||||
|
||||
static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
|
||||
{
|
||||
WCHAR *res = NULL;
|
||||
while(str && *str != '\0')
|
||||
{
|
||||
if(*str == ch)
|
||||
res = str;
|
||||
++str;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int GetLoadedModuleDirectory(const WCHAR *name, WCHAR *moddir, DWORD length)
|
||||
{
|
||||
HMODULE module = NULL;
|
||||
WCHAR *sep0, *sep1;
|
||||
|
||||
if(name)
|
||||
{
|
||||
module = GetModuleHandleW(name);
|
||||
if(!module) return 0;
|
||||
}
|
||||
|
||||
if(GetModuleFileNameW(module, moddir, length) == 0)
|
||||
return 0;
|
||||
|
||||
sep0 = strrchrW(moddir, '/');
|
||||
if(sep0) sep1 = strrchrW(sep0+1, '\\');
|
||||
else sep1 = strrchrW(moddir, '\\');
|
||||
|
||||
if(sep1) *sep1 = '\0';
|
||||
else if(sep0) *sep0 = '\0';
|
||||
else *moddir = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void LoadDriverList(void)
|
||||
{
|
||||
WCHAR path[MAX_PATH+1] = L"";
|
||||
int len;
|
||||
|
||||
if(GetLoadedModuleDirectory(L"OpenAL32.dll", path, MAX_PATH))
|
||||
SearchDrivers(path);
|
||||
|
||||
GetCurrentDirectoryW(MAX_PATH, path);
|
||||
len = lstrlenW(path);
|
||||
if(len > 0 && (path[len-1] == '\\' || path[len-1] == '/'))
|
||||
path[len-1] = '\0';
|
||||
SearchDrivers(path);
|
||||
|
||||
if(GetLoadedModuleDirectory(NULL, path, MAX_PATH))
|
||||
SearchDrivers(path);
|
||||
|
||||
GetSystemDirectoryW(path, MAX_PATH);
|
||||
len = lstrlenW(path);
|
||||
if(len > 0 && (path[len-1] == '\\' || path[len-1] == '/'))
|
||||
path[len-1] = '\0';
|
||||
SearchDrivers(path);
|
||||
}
|
||||
|
||||
|
||||
void InitPtrIntMap(PtrIntMap *map)
|
||||
{
|
||||
map->keys = NULL;
|
||||
map->values = NULL;
|
||||
map->size = 0;
|
||||
map->capacity = 0;
|
||||
RWLockInit(&map->lock);
|
||||
}
|
||||
|
||||
void ResetPtrIntMap(PtrIntMap *map)
|
||||
{
|
||||
WriteLock(&map->lock);
|
||||
al_free(map->keys);
|
||||
map->keys = NULL;
|
||||
map->values = NULL;
|
||||
map->size = 0;
|
||||
map->capacity = 0;
|
||||
WriteUnlock(&map->lock);
|
||||
}
|
||||
|
||||
ALenum InsertPtrIntMapEntry(PtrIntMap *map, ALvoid *key, ALint value)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
|
||||
WriteLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei count = map->size;
|
||||
do {
|
||||
ALsizei step = count>>1;
|
||||
ALsizei i = pos+step;
|
||||
if(!(map->keys[i] < key))
|
||||
count = step;
|
||||
else
|
||||
{
|
||||
pos = i+1;
|
||||
count -= step+1;
|
||||
}
|
||||
} while(count > 0);
|
||||
}
|
||||
|
||||
if(pos == map->size || map->keys[pos] != key)
|
||||
{
|
||||
if(map->size == map->capacity)
|
||||
{
|
||||
ALvoid **keys = NULL;
|
||||
ALint *values;
|
||||
ALsizei newcap;
|
||||
|
||||
newcap = (map->capacity ? (map->capacity<<1) : 4);
|
||||
if(newcap > map->capacity)
|
||||
keys = al_calloc(16, (sizeof(map->keys[0])+sizeof(map->values[0]))*newcap);
|
||||
if(!keys)
|
||||
{
|
||||
WriteUnlock(&map->lock);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
values = (ALint*)&keys[newcap];
|
||||
|
||||
if(map->keys)
|
||||
{
|
||||
memcpy(keys, map->keys, map->size*sizeof(map->keys[0]));
|
||||
memcpy(values, map->values, map->size*sizeof(map->values[0]));
|
||||
}
|
||||
al_free(map->keys);
|
||||
map->keys = keys;
|
||||
map->values = values;
|
||||
map->capacity = newcap;
|
||||
}
|
||||
|
||||
if(pos < map->size)
|
||||
{
|
||||
memmove(&map->keys[pos+1], &map->keys[pos],
|
||||
(map->size-pos)*sizeof(map->keys[0]));
|
||||
memmove(&map->values[pos+1], &map->values[pos],
|
||||
(map->size-pos)*sizeof(map->values[0]));
|
||||
}
|
||||
map->size++;
|
||||
}
|
||||
map->keys[pos] = key;
|
||||
map->values[pos] = value;
|
||||
WriteUnlock(&map->lock);
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
ALint RemovePtrIntMapKey(PtrIntMap *map, ALvoid *key)
|
||||
{
|
||||
ALint ret = -1;
|
||||
WriteLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALsizei count = map->size;
|
||||
do {
|
||||
ALsizei step = count>>1;
|
||||
ALsizei i = pos+step;
|
||||
if(!(map->keys[i] < key))
|
||||
count = step;
|
||||
else
|
||||
{
|
||||
pos = i+1;
|
||||
count -= step+1;
|
||||
}
|
||||
} while(count > 0);
|
||||
if(pos < map->size && map->keys[pos] == key)
|
||||
{
|
||||
ret = map->values[pos];
|
||||
if(pos < map->size-1)
|
||||
{
|
||||
memmove(&map->keys[pos], &map->keys[pos+1],
|
||||
(map->size-1-pos)*sizeof(map->keys[0]));
|
||||
memmove(&map->values[pos], &map->values[pos+1],
|
||||
(map->size-1-pos)*sizeof(map->values[0]));
|
||||
}
|
||||
map->size--;
|
||||
}
|
||||
}
|
||||
WriteUnlock(&map->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ALint LookupPtrIntMapKey(PtrIntMap *map, ALvoid *key)
|
||||
{
|
||||
ALint ret = -1;
|
||||
ReadLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALsizei count = map->size;
|
||||
do {
|
||||
ALsizei step = count>>1;
|
||||
ALsizei i = pos+step;
|
||||
if(!(map->keys[i] < key))
|
||||
count = step;
|
||||
else
|
||||
{
|
||||
pos = i+1;
|
||||
count -= step+1;
|
||||
}
|
||||
} while(count > 0);
|
||||
if(pos < map->size && map->keys[pos] == key)
|
||||
ret = map->values[pos];
|
||||
}
|
||||
ReadUnlock(&map->lock);
|
||||
return ret;
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
#ifndef ROUTER_ROUTER_H
|
||||
#define ROUTER_ROUTER_H
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
#include "atomic.h"
|
||||
#include "rwlock.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
#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 MAKE_ALC_VER(major, minor) (((major)<<8) | (minor))
|
||||
|
||||
typedef struct DriverIface {
|
||||
WCHAR Name[32];
|
||||
HMODULE Module;
|
||||
int ALCVer;
|
||||
|
||||
LPALCCREATECONTEXT alcCreateContext;
|
||||
LPALCMAKECONTEXTCURRENT alcMakeContextCurrent;
|
||||
LPALCPROCESSCONTEXT alcProcessContext;
|
||||
LPALCSUSPENDCONTEXT alcSuspendContext;
|
||||
LPALCDESTROYCONTEXT alcDestroyContext;
|
||||
LPALCGETCURRENTCONTEXT alcGetCurrentContext;
|
||||
LPALCGETCONTEXTSDEVICE alcGetContextsDevice;
|
||||
LPALCOPENDEVICE alcOpenDevice;
|
||||
LPALCCLOSEDEVICE alcCloseDevice;
|
||||
LPALCGETERROR alcGetError;
|
||||
LPALCISEXTENSIONPRESENT alcIsExtensionPresent;
|
||||
LPALCGETPROCADDRESS alcGetProcAddress;
|
||||
LPALCGETENUMVALUE alcGetEnumValue;
|
||||
LPALCGETSTRING alcGetString;
|
||||
LPALCGETINTEGERV alcGetIntegerv;
|
||||
LPALCCAPTUREOPENDEVICE alcCaptureOpenDevice;
|
||||
LPALCCAPTURECLOSEDEVICE alcCaptureCloseDevice;
|
||||
LPALCCAPTURESTART alcCaptureStart;
|
||||
LPALCCAPTURESTOP alcCaptureStop;
|
||||
LPALCCAPTURESAMPLES alcCaptureSamples;
|
||||
|
||||
PFNALCSETTHREADCONTEXTPROC alcSetThreadContext;
|
||||
PFNALCGETTHREADCONTEXTPROC alcGetThreadContext;
|
||||
|
||||
LPALENABLE alEnable;
|
||||
LPALDISABLE alDisable;
|
||||
LPALISENABLED alIsEnabled;
|
||||
LPALGETSTRING alGetString;
|
||||
LPALGETBOOLEANV alGetBooleanv;
|
||||
LPALGETINTEGERV alGetIntegerv;
|
||||
LPALGETFLOATV alGetFloatv;
|
||||
LPALGETDOUBLEV alGetDoublev;
|
||||
LPALGETBOOLEAN alGetBoolean;
|
||||
LPALGETINTEGER alGetInteger;
|
||||
LPALGETFLOAT alGetFloat;
|
||||
LPALGETDOUBLE alGetDouble;
|
||||
LPALGETERROR alGetError;
|
||||
LPALISEXTENSIONPRESENT alIsExtensionPresent;
|
||||
LPALGETPROCADDRESS alGetProcAddress;
|
||||
LPALGETENUMVALUE alGetEnumValue;
|
||||
LPALLISTENERF alListenerf;
|
||||
LPALLISTENER3F alListener3f;
|
||||
LPALLISTENERFV alListenerfv;
|
||||
LPALLISTENERI alListeneri;
|
||||
LPALLISTENER3I alListener3i;
|
||||
LPALLISTENERIV alListeneriv;
|
||||
LPALGETLISTENERF alGetListenerf;
|
||||
LPALGETLISTENER3F alGetListener3f;
|
||||
LPALGETLISTENERFV alGetListenerfv;
|
||||
LPALGETLISTENERI alGetListeneri;
|
||||
LPALGETLISTENER3I alGetListener3i;
|
||||
LPALGETLISTENERIV alGetListeneriv;
|
||||
LPALGENSOURCES alGenSources;
|
||||
LPALDELETESOURCES alDeleteSources;
|
||||
LPALISSOURCE alIsSource;
|
||||
LPALSOURCEF alSourcef;
|
||||
LPALSOURCE3F alSource3f;
|
||||
LPALSOURCEFV alSourcefv;
|
||||
LPALSOURCEI alSourcei;
|
||||
LPALSOURCE3I alSource3i;
|
||||
LPALSOURCEIV alSourceiv;
|
||||
LPALGETSOURCEF alGetSourcef;
|
||||
LPALGETSOURCE3F alGetSource3f;
|
||||
LPALGETSOURCEFV alGetSourcefv;
|
||||
LPALGETSOURCEI alGetSourcei;
|
||||
LPALGETSOURCE3I alGetSource3i;
|
||||
LPALGETSOURCEIV alGetSourceiv;
|
||||
LPALSOURCEPLAYV alSourcePlayv;
|
||||
LPALSOURCESTOPV alSourceStopv;
|
||||
LPALSOURCEREWINDV alSourceRewindv;
|
||||
LPALSOURCEPAUSEV alSourcePausev;
|
||||
LPALSOURCEPLAY alSourcePlay;
|
||||
LPALSOURCESTOP alSourceStop;
|
||||
LPALSOURCEREWIND alSourceRewind;
|
||||
LPALSOURCEPAUSE alSourcePause;
|
||||
LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers;
|
||||
LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers;
|
||||
LPALGENBUFFERS alGenBuffers;
|
||||
LPALDELETEBUFFERS alDeleteBuffers;
|
||||
LPALISBUFFER alIsBuffer;
|
||||
LPALBUFFERF alBufferf;
|
||||
LPALBUFFER3F alBuffer3f;
|
||||
LPALBUFFERFV alBufferfv;
|
||||
LPALBUFFERI alBufferi;
|
||||
LPALBUFFER3I alBuffer3i;
|
||||
LPALBUFFERIV alBufferiv;
|
||||
LPALGETBUFFERF alGetBufferf;
|
||||
LPALGETBUFFER3F alGetBuffer3f;
|
||||
LPALGETBUFFERFV alGetBufferfv;
|
||||
LPALGETBUFFERI alGetBufferi;
|
||||
LPALGETBUFFER3I alGetBuffer3i;
|
||||
LPALGETBUFFERIV alGetBufferiv;
|
||||
LPALBUFFERDATA alBufferData;
|
||||
LPALDOPPLERFACTOR alDopplerFactor;
|
||||
LPALDOPPLERVELOCITY alDopplerVelocity;
|
||||
LPALSPEEDOFSOUND alSpeedOfSound;
|
||||
LPALDISTANCEMODEL alDistanceModel;
|
||||
} DriverIface;
|
||||
|
||||
extern DriverIface *DriverList;
|
||||
extern int DriverListSize;
|
||||
|
||||
extern altss_t ThreadCtxDriver;
|
||||
extern ATOMIC(DriverIface*) CurrentCtxDriver;
|
||||
|
||||
|
||||
typedef struct PtrIntMap {
|
||||
ALvoid **keys;
|
||||
/* Shares memory with keys. */
|
||||
ALint *values;
|
||||
|
||||
ALsizei size;
|
||||
ALsizei capacity;
|
||||
RWLock lock;
|
||||
} PtrIntMap;
|
||||
#define PTRINTMAP_STATIC_INITIALIZE { NULL, NULL, 0, 0, RWLOCK_STATIC_INITIALIZE }
|
||||
|
||||
void InitPtrIntMap(PtrIntMap *map);
|
||||
void ResetPtrIntMap(PtrIntMap *map);
|
||||
ALenum InsertPtrIntMapEntry(PtrIntMap *map, ALvoid *key, ALint value);
|
||||
ALint RemovePtrIntMapKey(PtrIntMap *map, ALvoid *key);
|
||||
ALint LookupPtrIntMapKey(PtrIntMap *map, ALvoid *key);
|
||||
|
||||
|
||||
void InitALC(void);
|
||||
void ReleaseALC(void);
|
||||
|
||||
|
||||
enum LogLevel {
|
||||
LogLevel_None = 0,
|
||||
LogLevel_Error = 1,
|
||||
LogLevel_Warn = 2,
|
||||
LogLevel_Trace = 3,
|
||||
};
|
||||
extern enum LogLevel LogLevel;
|
||||
extern FILE *LogFile;
|
||||
|
||||
#define TRACE(...) do { \
|
||||
if(LogLevel >= LogLevel_Trace) \
|
||||
{ \
|
||||
fprintf(LogFile, "AL Router (II): " __VA_ARGS__); \
|
||||
fflush(LogFile); \
|
||||
} \
|
||||
} while(0)
|
||||
#define WARN(...) do { \
|
||||
if(LogLevel >= LogLevel_Warn) \
|
||||
{ \
|
||||
fprintf(LogFile, "AL Router (WW): " __VA_ARGS__); \
|
||||
fflush(LogFile); \
|
||||
} \
|
||||
} while(0)
|
||||
#define ERR(...) do { \
|
||||
if(LogLevel >= LogLevel_Error) \
|
||||
{ \
|
||||
fprintf(LogFile, "AL Router (EE): " __VA_ARGS__); \
|
||||
fflush(LogFile); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif /* ROUTER_ROUTER_H */
|
||||
+60
-74
@@ -2,7 +2,7 @@
|
||||
* HRTF utility for producing and demonstrating the process of creating an
|
||||
* OpenAL Soft compatible HRIR data set.
|
||||
*
|
||||
* Copyright (C) 2011-2014 Christopher Fitzgerald
|
||||
* Copyright (C) 2011-2017 Christopher Fitzgerald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -83,7 +83,7 @@
|
||||
#endif
|
||||
|
||||
// The epsilon used to maintain signal stability.
|
||||
#define EPSILON (1e-15)
|
||||
#define EPSILON (1e-9)
|
||||
|
||||
// Constants for accessing the token reader's ring buffer.
|
||||
#define TR_RING_BITS (16)
|
||||
@@ -145,16 +145,16 @@
|
||||
#define MAX_ASCII_BITS (32)
|
||||
|
||||
// The limits to the FFT window size override on the command line.
|
||||
#define MIN_FFTSIZE (512)
|
||||
#define MAX_FFTSIZE (16384)
|
||||
#define MIN_FFTSIZE (65536)
|
||||
#define MAX_FFTSIZE (131072)
|
||||
|
||||
// The limits to the equalization range limit on the command line.
|
||||
#define MIN_LIMIT (2.0)
|
||||
#define MAX_LIMIT (120.0)
|
||||
|
||||
// The limits to the truncation window size on the command line.
|
||||
#define MIN_TRUNCSIZE (8)
|
||||
#define MAX_TRUNCSIZE (128)
|
||||
#define MIN_TRUNCSIZE (16)
|
||||
#define MAX_TRUNCSIZE (512)
|
||||
|
||||
// The limits to the custom head radius on the command line.
|
||||
#define MIN_CUSTOM_RADIUS (0.05)
|
||||
@@ -165,6 +165,7 @@
|
||||
#define MOD_TRUNCSIZE (8)
|
||||
|
||||
// The defaults for the command line options.
|
||||
#define DEFAULT_FFTSIZE (65536)
|
||||
#define DEFAULT_EQUALIZE (1)
|
||||
#define DEFAULT_SURFACE (1)
|
||||
#define DEFAULT_LIMIT (24.0)
|
||||
@@ -985,10 +986,10 @@ static void FftInverse(const uint n, const double *inR, const double *inI, doubl
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the complex helical sequence (or discrete-time analytical
|
||||
* signal) of the given input using the Hilbert transform. Given the
|
||||
* negative natural logarithm of a signal's magnitude response, the imaginary
|
||||
* components can be used as the angles for minimum-phase reconstruction.
|
||||
/* Calculate the complex helical sequence (or discrete-time analytical signal)
|
||||
* of the given input using the Hilbert transform. Given the natural logarithm
|
||||
* of a signal's magnitude response, the imaginary components can be used as
|
||||
* the angles for minimum-phase reconstruction.
|
||||
*/
|
||||
static void Hilbert(const uint n, const double *in, double *outR, double *outI)
|
||||
{
|
||||
@@ -1009,24 +1010,20 @@ static void Hilbert(const uint n, const double *in, double *outR, double *outI)
|
||||
outI[i] = 0.0;
|
||||
}
|
||||
}
|
||||
FftForward(n, outR, outI, outR, outI);
|
||||
/* Currently the Fourier routines operate only on point counts that are
|
||||
* powers of two. If that changes and n is odd, the following conditional
|
||||
* should be: i < (n + 1) / 2.
|
||||
*/
|
||||
for(i = 1;i < (n/2);i++)
|
||||
FftInverse(n, outR, outI, outR, outI);
|
||||
for(i = 1;i < (n+1)/2;i++)
|
||||
{
|
||||
outR[i] *= 2.0;
|
||||
outI[i] *= 2.0;
|
||||
}
|
||||
// If n is odd, the following increment should be skipped.
|
||||
i++;
|
||||
/* Increment i if n is even. */
|
||||
i += (n&1)^1;
|
||||
for(;i < n;i++)
|
||||
{
|
||||
outR[i] = 0.0;
|
||||
outI[i] = 0.0;
|
||||
}
|
||||
FftInverse(n, outR, outI, outR, outI);
|
||||
FftForward(n, outR, outI, outR, outI);
|
||||
}
|
||||
|
||||
/* Calculate the magnitude response of the given input. This is used in
|
||||
@@ -1080,15 +1077,15 @@ static void LimitMagnitudeResponse(const uint n, const double limit, const doubl
|
||||
static void MinimumPhase(const uint n, const double *in, double *outR, double *outI)
|
||||
{
|
||||
const uint m = 1 + (n / 2);
|
||||
double aR, aI;
|
||||
double *mags;
|
||||
double aR, aI;
|
||||
uint i;
|
||||
|
||||
mags = CreateArray(n);
|
||||
for(i = 0;i < m;i++)
|
||||
{
|
||||
mags[i] = fmax(in[i], EPSILON);
|
||||
outR[i] = -log(mags[i]);
|
||||
mags[i] = fmax(EPSILON, in[i]);
|
||||
outR[i] = log(mags[i]);
|
||||
}
|
||||
for(;i < n;i++)
|
||||
{
|
||||
@@ -1097,9 +1094,8 @@ static void MinimumPhase(const uint n, const double *in, double *outR, double *o
|
||||
}
|
||||
Hilbert(n, outR, outR, outI);
|
||||
// Remove any DC offset the filter has.
|
||||
outR[0] = 0.0;
|
||||
outI[0] = 0.0;
|
||||
for(i = 1;i < n;i++)
|
||||
mags[0] = EPSILON;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
ComplexExp(0.0, outI[i], &aR, &aI);
|
||||
ComplexMul(mags[i], 0.0, aR, aI, &outR[i], &outI[i]);
|
||||
@@ -1272,13 +1268,13 @@ static void ResamplerSetup(ResamplerT *rs, const uint srcRate, const uint dstRat
|
||||
*/
|
||||
if(rs->mP > rs->mQ)
|
||||
{
|
||||
cutoff = 0.45 / rs->mP;
|
||||
width = 0.1 / rs->mP;
|
||||
cutoff = 0.475 / rs->mP;
|
||||
width = 0.05 / rs->mP;
|
||||
}
|
||||
else
|
||||
{
|
||||
cutoff = 0.45 / rs->mQ;
|
||||
width = 0.1 / rs->mQ;
|
||||
cutoff = 0.475 / rs->mQ;
|
||||
width = 0.05 / rs->mQ;
|
||||
}
|
||||
// A rejection of -180 dB is used for the stop band.
|
||||
l = CalcKaiserOrder(180.0, width) / 2;
|
||||
@@ -1923,12 +1919,13 @@ static int StoreMhr(const HrirDataT *hData, const char *filename)
|
||||
|
||||
// Calculate the onset time of an HRIR and average it with any existing
|
||||
// timing for its elevation and azimuth.
|
||||
static void AverageHrirOnset(const double *hrir, const uint n, const double f, const uint ei, const uint ai, const HrirDataT *hData)
|
||||
static void AverageHrirOnset(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData)
|
||||
{
|
||||
double mag;
|
||||
uint i, j;
|
||||
uint n, i, j;
|
||||
|
||||
mag = 0.0;
|
||||
n = hData->mIrPoints;
|
||||
for(i = 0;i < n;i++)
|
||||
mag = fmax(fabs(hrir[i]), mag);
|
||||
mag *= 0.15;
|
||||
@@ -1943,7 +1940,7 @@ static void AverageHrirOnset(const double *hrir, const uint n, const double f, c
|
||||
|
||||
// Calculate the magnitude response of an HRIR and average it with any
|
||||
// existing responses for its elevation and azimuth.
|
||||
static void AverageHrirMagnitude(const double *hrir, const uint npoints, const double f, const uint ei, const uint ai, const HrirDataT *hData)
|
||||
static void AverageHrirMagnitude(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData)
|
||||
{
|
||||
double *re, *im;
|
||||
uint n, m, i, j;
|
||||
@@ -1951,7 +1948,7 @@ static void AverageHrirMagnitude(const double *hrir, const uint npoints, const d
|
||||
n = hData->mFftSize;
|
||||
re = CreateArray(n);
|
||||
im = CreateArray(n);
|
||||
for(i = 0;i < npoints;i++)
|
||||
for(i = 0;i < hData->mIrPoints;i++)
|
||||
{
|
||||
re[i] = hrir[i];
|
||||
im[i] = 0.0;
|
||||
@@ -2107,6 +2104,23 @@ static void ReconstructHrirs(const HrirDataT *hData)
|
||||
DestroyArray (re);
|
||||
}
|
||||
|
||||
// Resamples the HRIRs for use at the given sampling rate.
|
||||
static void ResampleHrirs(const uint rate, HrirDataT *hData)
|
||||
{
|
||||
uint n, step, start, end, j;
|
||||
ResamplerT rs;
|
||||
|
||||
ResamplerSetup(&rs, hData->mIrRate, rate);
|
||||
n = hData->mIrPoints;
|
||||
step = hData->mIrSize;
|
||||
start = hData->mEvOffset[hData->mEvStart] * step;
|
||||
end = hData->mIrCount * step;
|
||||
for(j = start;j < end;j += step)
|
||||
ResamplerRun(&rs, n, &hData->mHrirs[j], n, &hData->mHrirs[j]);
|
||||
ResamplerClear(&rs);
|
||||
hData->mIrRate = rate;
|
||||
}
|
||||
|
||||
/* Given an elevation index and an azimuth, calculate the indices of the two
|
||||
* HRIRs that bound the coordinate along with a factor for calculating the
|
||||
* continous HRIR using interpolation.
|
||||
@@ -2343,14 +2357,10 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc
|
||||
return 0;
|
||||
}
|
||||
hData->mIrPoints = points;
|
||||
hData->mFftSize = fftSize;
|
||||
if(fftSize <= 0)
|
||||
{
|
||||
points = 1;
|
||||
while(points < (4 * hData->mIrPoints))
|
||||
points <<= 1;
|
||||
hData->mFftSize = points;
|
||||
hData->mIrSize = 1 + (points / 2);
|
||||
hData->mFftSize = DEFAULT_FFTSIZE;
|
||||
hData->mIrSize = 1 + (DEFAULT_FFTSIZE / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2596,37 +2606,17 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src)
|
||||
}
|
||||
|
||||
// Process the list of sources in the data set definition.
|
||||
static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReaderT *tr, HrirDataT *hData)
|
||||
static int ProcessSources(const HeadModelT model, TokenReaderT *tr, HrirDataT *hData)
|
||||
{
|
||||
uint *setCount, *setFlag;
|
||||
uint line, col, ei, ai;
|
||||
uint res_points;
|
||||
SourceRefT src;
|
||||
double factor;
|
||||
double *hrir;
|
||||
ResamplerT rs;
|
||||
|
||||
ResamplerSetup(&rs, hData->mIrRate, dstRate);
|
||||
/* Scale the number of IR points for resampling. This could be improved by
|
||||
* also including space for the resampler build-up and fall-off (rs.mL*2),
|
||||
* instead of clipping them off, but that could affect the HRTDs. It's not
|
||||
* a big deal to exclude them for sources that aren't already minimum-
|
||||
* phase).
|
||||
*/
|
||||
res_points = (uint)(((uint64)hData->mIrPoints*dstRate + hData->mIrRate-1) /
|
||||
hData->mIrRate);
|
||||
/* Clamp to the IR size to prevent overflow, and don't go less than the
|
||||
* original point count.
|
||||
*/
|
||||
if(res_points > hData->mIrSize)
|
||||
res_points = hData->mIrSize;
|
||||
else if(res_points < hData->mIrPoints)
|
||||
res_points = hData->mIrPoints;
|
||||
|
||||
setCount = (uint*)calloc(hData->mEvCount, sizeof(uint));
|
||||
setFlag = (uint*)calloc(hData->mIrCount, sizeof(uint));
|
||||
hrir = CreateArray(res_points);
|
||||
|
||||
hrir = CreateArray(hData->mIrPoints);
|
||||
while(TrIsOperator(tr, "["))
|
||||
{
|
||||
TrIndication(tr, & line, & col);
|
||||
@@ -2651,13 +2641,9 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
|
||||
if(!LoadSource(&src, hData->mIrRate, hData->mIrPoints, hrir))
|
||||
goto error;
|
||||
|
||||
if(hData->mIrRate != dstRate)
|
||||
ResamplerRun(&rs, hData->mIrPoints, hrir,
|
||||
res_points, hrir);
|
||||
|
||||
if(model == HM_DATASET)
|
||||
AverageHrirOnset(hrir, res_points, 1.0 / factor, ei, ai, hData);
|
||||
AverageHrirMagnitude(hrir, res_points, 1.0 / factor, ei, ai, hData);
|
||||
AverageHrirOnset(hrir, 1.0 / factor, ei, ai, hData);
|
||||
AverageHrirMagnitude(hrir, 1.0 / factor, ei, ai, hData);
|
||||
factor += 1.0;
|
||||
if(!TrIsOperator(tr, "+"))
|
||||
break;
|
||||
@@ -2666,8 +2652,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
|
||||
setFlag[hData->mEvOffset[ei] + ai] = 1;
|
||||
setCount[ei]++;
|
||||
}
|
||||
hData->mIrPoints = res_points;
|
||||
hData->mIrRate = dstRate;
|
||||
|
||||
ei = 0;
|
||||
while(ei < hData->mEvCount && setCount[ei] < 1)
|
||||
@@ -2681,7 +2665,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
|
||||
{
|
||||
if(!TrLoad(tr))
|
||||
{
|
||||
ResamplerClear(&rs);
|
||||
DestroyArray(hrir);
|
||||
free(setFlag);
|
||||
free(setCount);
|
||||
@@ -2696,7 +2679,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
|
||||
TrError(tr, "Missing source references.\n");
|
||||
|
||||
error:
|
||||
ResamplerClear(&rs);
|
||||
DestroyArray(hrir);
|
||||
free(setFlag);
|
||||
free(setCount);
|
||||
@@ -2747,7 +2729,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
|
||||
}
|
||||
hData.mHrirs = CreateArray(hData.mIrCount * hData.mIrSize);
|
||||
hData.mHrtds = CreateArray(hData.mIrCount);
|
||||
if(!ProcessSources(model, outRate ? outRate : hData.mIrRate, &tr, &hData))
|
||||
if(!ProcessSources(model, &tr, &hData))
|
||||
{
|
||||
DestroyArray(hData.mHrtds);
|
||||
DestroyArray(hData.mHrirs);
|
||||
@@ -2768,6 +2750,11 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
|
||||
}
|
||||
fprintf(stdout, "Performing minimum phase reconstruction...\n");
|
||||
ReconstructHrirs(&hData);
|
||||
if(outRate != 0 && outRate != hData.mIrRate)
|
||||
{
|
||||
fprintf(stdout, "Resampling HRIRs...\n");
|
||||
ResampleHrirs(outRate, &hData);
|
||||
}
|
||||
fprintf(stdout, "Truncating minimum-phase HRIRs...\n");
|
||||
hData.mIrPoints = truncSize;
|
||||
fprintf(stdout, "Synthesizing missing elevations...\n");
|
||||
@@ -2809,8 +2796,7 @@ static void PrintHelp(const char *argv0, FILE *ofile)
|
||||
fprintf(ofile, "Options:\n");
|
||||
fprintf(ofile, " -r=<rate> Change the data set sample rate to the specified value and\n");
|
||||
fprintf(ofile, " resample the HRIRs accordingly.\n");
|
||||
fprintf(ofile, " -f=<points> Override the FFT window size (defaults to the first power-\n");
|
||||
fprintf(ofile, " of-two that fits four times the number of HRIR points).\n");
|
||||
fprintf(ofile, " -f=<points> Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE);
|
||||
fprintf(ofile, " -e={on|off} Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off"));
|
||||
fprintf(ofile, " -s={on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off"));
|
||||
fprintf(ofile, " -l={<dB>|none} Specify a limit to the magnitude range of the diffuse-field\n");
|
||||
|
||||
Reference in New Issue
Block a user