Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5e43fed09 | |||
| c851a68f09 | |||
| 5ac19a1aa1 | |||
| a3c6391bb4 | |||
| dfeead1931 | |||
| 5724481a72 | |||
| b88e5de1ae | |||
| bb59b318de | |||
| 32f350c1b5 | |||
| 84143100e5 | |||
| 452a1bd4de | |||
| 419cb0e847 | |||
| 6a0314bfbc | |||
| 6b630fd787 | |||
| 26b92d58ec | |||
| ddde29b2e9 | |||
| 5feca399c2 | |||
| e4a60406da | |||
| 6761218e51 | |||
| 31b9c50721 | |||
| f589244fb6 | |||
| 7a79f09a31 | |||
| db65113c5f | |||
| f3f94e478b | |||
| c39eeb9638 | |||
| 493c8bbc83 | |||
| 052fdd67cd | |||
| ed8f44d102 | |||
| 1d1acc0c34 | |||
| 1860b2ec8a | |||
| 7ad4f753a4 | |||
| d5a78f0843 | |||
| 32494e72a5 | |||
| 0396c0ecd2 | |||
| 51a447852f | |||
| 1684b2cc5f | |||
| 1dee902a5f | |||
| 79314c4461 | |||
| 2d6309d6fc | |||
| c69338bc0d | |||
| 39c3314d00 | |||
| db6905bf57 | |||
| 800326d37a | |||
| c708f871ec | |||
| 77a53594ba | |||
| 36a6b9d42a | |||
| b6d0ff02c2 | |||
| f21e2df4cf | |||
| 84a90b7cc3 | |||
| ebbbeb0d66 | |||
| cb8545346d | |||
| 5c6b8eda4f | |||
| ea95a8adef | |||
| 6eb980d1b2 | |||
| 48b7745a49 | |||
| 9ef4dd4247 | |||
| 952ff84b99 | |||
| b77e6096b8 | |||
| 7f4441ffbe | |||
| 8bacb5dfb8 | |||
| a6734c7a91 | |||
| db452a19da | |||
| 99737469e2 | |||
| b13396cce2 | |||
| d1f8b78dd4 | |||
| 4bdab0051f | |||
| 634b13a630 | |||
| 275658b6db | |||
| 9054f41434 | |||
| fa7993fe3e | |||
| 46cfedb117 | |||
| 212cb8e298 | |||
| 7394dd512d |
@@ -79,7 +79,7 @@ static struct BackendInfo BackendList[] = {
|
||||
{ "solaris", ALCsolarisBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_SNDIO
|
||||
{ "sndio", ALCsndioBackendFactory_getFactory },
|
||||
{ "sndio", SndioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
{ "oss", ALCossBackendFactory_getFactory },
|
||||
@@ -105,6 +105,9 @@ static struct BackendInfo BackendList[] = {
|
||||
#ifdef HAVE_SDL2
|
||||
{ "sdl2", ALCsdl2BackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_VITA
|
||||
{ "vita", ALCvitaBackendFactory_getFactory },
|
||||
#endif
|
||||
|
||||
{ "null", ALCnullBackendFactory_getFactory },
|
||||
#ifdef HAVE_WAVE
|
||||
@@ -656,9 +659,9 @@ static const struct {
|
||||
|
||||
DECL(AL_DEDICATED_GAIN),
|
||||
|
||||
DECL(AL_AUTOWAH_ATTACK_TIME),
|
||||
DECL(AL_AUTOWAH_RELEASE_TIME),
|
||||
DECL(AL_AUTOWAH_RESONANCE),
|
||||
DECL(AL_AUTOWAH_ATTACK_TIME),
|
||||
DECL(AL_AUTOWAH_RELEASE_TIME),
|
||||
DECL(AL_AUTOWAH_RESONANCE),
|
||||
DECL(AL_AUTOWAH_PEAK_GAIN),
|
||||
|
||||
DECL(AL_NUM_RESAMPLERS_SOFT),
|
||||
@@ -1165,75 +1168,6 @@ static void alc_initconfig(void)
|
||||
}
|
||||
#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <jni.h>
|
||||
|
||||
static JavaVM *gJavaVM;
|
||||
static pthread_key_t gJVMThreadKey;
|
||||
|
||||
static void CleanupJNIEnv(void* UNUSED(ptr))
|
||||
{
|
||||
JCALL0(gJavaVM,DetachCurrentThread)();
|
||||
}
|
||||
|
||||
void *Android_GetJNIEnv(void)
|
||||
{
|
||||
if(!gJavaVM)
|
||||
{
|
||||
WARN("gJavaVM is NULL!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* http://developer.android.com/guide/practices/jni.html
|
||||
*
|
||||
* All threads are Linux threads, scheduled by the kernel. They're usually
|
||||
* started from managed code (using Thread.start), but they can also be
|
||||
* created elsewhere and then attached to the JavaVM. For example, a thread
|
||||
* started with pthread_create can be attached with the JNI
|
||||
* AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a
|
||||
* thread is attached, it has no JNIEnv, and cannot make JNI calls.
|
||||
* Attaching a natively-created thread causes a java.lang.Thread object to
|
||||
* be constructed and added to the "main" ThreadGroup, making it visible to
|
||||
* the debugger. Calling AttachCurrentThread on an already-attached thread
|
||||
* is a no-op.
|
||||
*/
|
||||
JNIEnv *env = pthread_getspecific(gJVMThreadKey);
|
||||
if(!env)
|
||||
{
|
||||
int status = JCALL(gJavaVM,AttachCurrentThread)(&env, NULL);
|
||||
if(status < 0)
|
||||
{
|
||||
ERR("Failed to attach current thread\n");
|
||||
return NULL;
|
||||
}
|
||||
pthread_setspecific(gJVMThreadKey, env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* Automatically called by JNI. */
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved))
|
||||
{
|
||||
void *env;
|
||||
int err;
|
||||
|
||||
gJavaVM = jvm;
|
||||
if(JCALL(gJavaVM,GetEnv)(&env, JNI_VERSION_1_4) != JNI_OK)
|
||||
{
|
||||
ERR("Failed to get JNIEnv with JNI_VERSION_1_4\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
/* Create gJVMThreadKey so we can keep track of the JNIEnv assigned to each
|
||||
* thread. The JNIEnv *must* be detached before the thread is destroyed.
|
||||
*/
|
||||
if((err=pthread_key_create(&gJVMThreadKey, CleanupJNIEnv)) != 0)
|
||||
ERR("pthread_key_create failed: %d\n", err);
|
||||
pthread_setspecific(gJVMThreadKey, env);
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/************************************************
|
||||
* Library deinitialization
|
||||
@@ -1314,7 +1248,7 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum
|
||||
if(backendinfo->getFactory)
|
||||
{
|
||||
ALCbackendFactory *factory = backendinfo->getFactory();
|
||||
V(factory,probe)(type);
|
||||
V(factory,probe)(type, list);
|
||||
}
|
||||
|
||||
UnlockLists();
|
||||
@@ -1324,17 +1258,6 @@ static void ProbeAllDevicesList(void)
|
||||
static void ProbeCaptureDeviceList(void)
|
||||
{ ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
|
||||
|
||||
static void AppendDevice(const ALCchar *name, al_string *devnames)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
if(len > 0)
|
||||
alstr_append_range(devnames, name, name+len+1);
|
||||
}
|
||||
void AppendAllDevicesList(const ALCchar *name)
|
||||
{ AppendDevice(name, &alcAllDevicesList); }
|
||||
void AppendCaptureDeviceList(const ALCchar *name)
|
||||
{ AppendDevice(name, &alcCaptureDeviceList); }
|
||||
|
||||
|
||||
/************************************************
|
||||
* Device format information
|
||||
@@ -1715,10 +1638,11 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
|
||||
}
|
||||
|
||||
|
||||
struct Compressor *CreateDeviceLimiter(const ALCdevice *device)
|
||||
static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
||||
{
|
||||
return CompressorInit(0.0f, 0.0f, AL_FALSE, AL_TRUE, 0.0f, 0.0f, 0.5f, 2.0f,
|
||||
0.0f, -3.0f, 3.0f, device->Frequency);
|
||||
return CompressorInit(device->RealOut.NumChannels, device->Frequency,
|
||||
AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
|
||||
0.0f, 0.0f, threshold, INFINITY, 0.0f, 0.020f, 0.200f);
|
||||
}
|
||||
|
||||
/* UpdateClockBase
|
||||
@@ -1745,7 +1669,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
{
|
||||
enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
|
||||
enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
|
||||
ALCenum gainLimiter = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
||||
ALCenum gainLimiter = device->LimiterState;
|
||||
const ALsizei old_sends = device->NumAuxSends;
|
||||
ALsizei new_sends = device->NumAuxSends;
|
||||
enum DevFmtChannels oldChans;
|
||||
@@ -2061,6 +1985,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
device->RealOut.NumChannels = 0;
|
||||
|
||||
UpdateClockBase(device);
|
||||
device->FixedLatency = 0;
|
||||
|
||||
device->DitherSeed = DITHER_RNG_SEED;
|
||||
|
||||
@@ -2224,7 +2149,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
|
||||
if(depth > 0)
|
||||
{
|
||||
depth = clampi(depth, 2, 20);
|
||||
depth = clampi(depth, 2, 24);
|
||||
device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
|
||||
}
|
||||
}
|
||||
@@ -2234,19 +2159,57 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
|
||||
device->DitherDepth);
|
||||
|
||||
device->LimiterState = gainLimiter;
|
||||
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
|
||||
* ALC_FALSE. We default to on, so ALC_DONT_CARE_SOFT is the same as
|
||||
* ALC_TRUE.
|
||||
* ALC_FALSE. For ALC_DONT_CARE_SOFT, use the limiter for integer-based
|
||||
* output (where samples must be clamped), and don't for floating-point
|
||||
* (which can take unclamped samples).
|
||||
*/
|
||||
if(gainLimiter == ALC_DONT_CARE_SOFT)
|
||||
{
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
gainLimiter = ALC_TRUE;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
gainLimiter = ALC_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(gainLimiter != ALC_FALSE)
|
||||
{
|
||||
if(!device->Limiter || device->Frequency != GetCompressorSampleRate(device->Limiter))
|
||||
ALfloat thrshld = 1.0f;
|
||||
switch(device->FmtType)
|
||||
{
|
||||
al_free(device->Limiter);
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
thrshld = 127.0f / 128.0f;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
thrshld = 32767.0f / 32768.0f;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
if(device->DitherDepth > 0.0f)
|
||||
thrshld -= 1.0f / device->DitherDepth;
|
||||
|
||||
al_free(device->Limiter);
|
||||
device->Limiter = CreateDeviceLimiter(device, log10f(thrshld) * 20.0f);
|
||||
device->FixedLatency += (ALuint)(GetCompressorLookAhead(device->Limiter) *
|
||||
DEVICE_CLOCK_RES / device->Frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2257,6 +2220,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
|
||||
aluSelectPostProcess(device);
|
||||
|
||||
TRACE("Fixed device latency: %uns\n", device->FixedLatency);
|
||||
|
||||
/* Need to delay returning failure until replacement Send arrays have been
|
||||
* allocated with the appropriate size.
|
||||
*/
|
||||
@@ -2411,11 +2376,13 @@ static void InitDevice(ALCdevice *device, enum DeviceType type)
|
||||
device->Flags = 0;
|
||||
device->Render_Mode = NormalRender;
|
||||
device->AvgSpeakerDist = 0.0f;
|
||||
device->LimiterState = ALC_DONT_CARE_SOFT;
|
||||
|
||||
ATOMIC_INIT(&device->ContextList, NULL);
|
||||
|
||||
device->ClockBase = 0;
|
||||
device->SamplesDone = 0;
|
||||
device->FixedLatency = 0;
|
||||
|
||||
device->SourcesMax = 0;
|
||||
device->AuxiliaryEffectSlotMax = 0;
|
||||
@@ -2649,7 +2616,6 @@ static ALvoid InitContext(ALCcontext *Context)
|
||||
Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
|
||||
ATOMIC_FLAG_TEST_AND_SET(&Context->PropsClean, almemory_order_relaxed);
|
||||
ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
|
||||
almtx_init(&Context->EventThrdLock, almtx_plain);
|
||||
alsem_init(&Context->EventSem, 0);
|
||||
Context->AsyncEvents = NULL;
|
||||
ATOMIC_INIT(&Context->EnabledEvts, 0);
|
||||
@@ -2676,6 +2642,11 @@ static ALvoid InitContext(ALCcontext *Context)
|
||||
listener->Params.MetersPerUnit;
|
||||
listener->Params.SourceDistanceModel = Context->SourceDistanceModel;
|
||||
listener->Params.DistanceModel = Context->DistanceModel;
|
||||
|
||||
|
||||
Context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
|
||||
if(althrd_create(&Context->EventThread, EventThread, Context) != althrd_success)
|
||||
ERR("Failed to start event thread! Expect problems.\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -2784,17 +2755,7 @@ static void FreeContext(ALCcontext *context)
|
||||
}
|
||||
TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
if(ATOMIC_EXCHANGE(&context->EnabledEvts, 0, almemory_order_acq_rel))
|
||||
{
|
||||
static const AsyncEvent kill_evt = { 0 };
|
||||
while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
|
||||
althrd_yield();
|
||||
alsem_post(&context->EventSem);
|
||||
althrd_join(context->EventThread, NULL);
|
||||
}
|
||||
|
||||
almtx_destroy(&context->EventCbLock);
|
||||
almtx_destroy(&context->EventThrdLock);
|
||||
alsem_destroy(&context->EventSem);
|
||||
|
||||
ll_ringbuffer_free(context->AsyncEvents);
|
||||
@@ -2818,6 +2779,7 @@ static void FreeContext(ALCcontext *context)
|
||||
*/
|
||||
static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
|
||||
{
|
||||
static const AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
|
||||
ALCcontext *origctx, *newhead;
|
||||
bool ret = true;
|
||||
|
||||
@@ -2850,6 +2812,16 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
|
||||
ret = !!newhead;
|
||||
V0(device->Backend,unlock)();
|
||||
|
||||
/* Make sure the context is finished and no longer processing in the mixer
|
||||
* before sending the message queue kill event. The backend's lock does
|
||||
* this, although waiting for a non-odd mix count would work too.
|
||||
*/
|
||||
|
||||
while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
|
||||
althrd_yield();
|
||||
alsem_post(&context->EventSem);
|
||||
althrd_join(context->EventThread, NULL);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
return ret;
|
||||
}
|
||||
@@ -3631,7 +3603,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
values[i++] = ALC_OUTPUT_LIMITER_SOFT;
|
||||
values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
||||
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
values[i++] = ALC_DEVICE_CLOCK_SOFT;
|
||||
values[i++] = clock.ClockTime;
|
||||
|
||||
@@ -3657,7 +3629,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
|
||||
case ALC_DEVICE_LATENCY_SOFT:
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
*values = clock.Latency;
|
||||
break;
|
||||
@@ -3668,7 +3640,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
else
|
||||
{
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
values[0] = clock.ClockTime;
|
||||
values[1] = clock.Latency;
|
||||
@@ -4084,6 +4056,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
device->IsHeadphones = AL_FALSE;
|
||||
device->AmbiLayout = AmbiLayout_Default;
|
||||
device->AmbiScale = AmbiNorm_Default;
|
||||
device->LimiterState = ALC_TRUE;
|
||||
device->NumUpdates = 3;
|
||||
device->UpdateSize = 1024;
|
||||
|
||||
@@ -4222,8 +4195,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
ERR("Unsupported ambi-format: %s\n", fmt);
|
||||
}
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
{
|
||||
ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
|
||||
do {
|
||||
@@ -4551,8 +4522,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
|
||||
// Open the "backend"
|
||||
V(device->Backend,open)("Loopback");
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
{
|
||||
ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
|
||||
do {
|
||||
|
||||
@@ -211,32 +211,31 @@ void aluInit(void)
|
||||
|
||||
static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
|
||||
ALbitfieldSOFT enabledevt;
|
||||
AsyncEvent evt;
|
||||
size_t strpos;
|
||||
ALuint scale;
|
||||
|
||||
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(!(enabledevt&EventType_SourceStateChange)) return;
|
||||
|
||||
evt.EnumType = EventType_SourceStateChange;
|
||||
evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.ObjectId = id;
|
||||
evt.Param = AL_STOPPED;
|
||||
evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.u.user.id = id;
|
||||
evt.u.user.param = AL_STOPPED;
|
||||
|
||||
/* Normally snprintf would be used, but this is called from the mixer and
|
||||
* that function's not real-time safe, so we have to construct it manually.
|
||||
*/
|
||||
strcpy(evt.Message, "Source ID "); strpos = 10;
|
||||
strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
|
||||
scale = 1000000000;
|
||||
while(scale > 0 && scale > id)
|
||||
scale /= 10;
|
||||
while(scale > 0)
|
||||
{
|
||||
evt.Message[strpos++] = '0' + ((id/scale)%10);
|
||||
evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
|
||||
scale /= 10;
|
||||
}
|
||||
strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
|
||||
strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
|
||||
|
||||
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
||||
alsem_post(&context->EventSem);
|
||||
@@ -357,7 +356,7 @@ void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *t
|
||||
|
||||
state->sf = sf;
|
||||
state->m = table->m[si];
|
||||
state->l = -((state->m/2) - 1);
|
||||
state->l = (state->m/2) - 1;
|
||||
state->filter = table->Tab + table->filterOffset[si];
|
||||
}
|
||||
|
||||
@@ -463,12 +462,40 @@ static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool f
|
||||
slot->Params.AirAbsorptionGainHF = 1.0f;
|
||||
}
|
||||
|
||||
/* Swap effect states. No need to play with the ref counts since they
|
||||
* keep the same number of refs.
|
||||
*/
|
||||
state = props->State;
|
||||
props->State = slot->Params.EffectState;
|
||||
slot->Params.EffectState = state;
|
||||
|
||||
if(state == slot->Params.EffectState)
|
||||
{
|
||||
/* If the effect state is the same as current, we can decrement its
|
||||
* count safely to remove it from the update object (it can't reach
|
||||
* 0 refs since the current params also hold a reference).
|
||||
*/
|
||||
DecrementRef(&state->Ref);
|
||||
props->State = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, replace it and send off the old one with a release
|
||||
* event.
|
||||
*/
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
|
||||
evt.u.EffectState = slot->Params.EffectState;
|
||||
|
||||
slot->Params.EffectState = state;
|
||||
props->State = NULL;
|
||||
|
||||
if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) != 0))
|
||||
alsem_post(&context->EventSem);
|
||||
else
|
||||
{
|
||||
/* If writing the event failed, the queue was probably full.
|
||||
* Store the old state in the property object where it can
|
||||
* eventually be cleaned up sometime later (not ideal, but
|
||||
* better than blocking or leaking).
|
||||
*/
|
||||
props->State = evt.u.EffectState;
|
||||
}
|
||||
}
|
||||
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
@@ -644,7 +671,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
@@ -656,14 +683,14 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
Elev, Spread, coeffs);
|
||||
|
||||
/* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
|
||||
ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
|
||||
voice->Direct.Params[0].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
if(Slot)
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
||||
WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -672,8 +699,6 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
/* Local B-Format sources have their XYZ channels rotated according
|
||||
* to the orientation.
|
||||
*/
|
||||
const ALfloat sqrt_2 = sqrtf(2.0f);
|
||||
const ALfloat sqrt_3 = sqrtf(3.0f);
|
||||
ALfloat N[3], V[3], U[3];
|
||||
aluMatrixf matrix;
|
||||
|
||||
@@ -716,25 +741,25 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
* outputs on the columns.
|
||||
*/
|
||||
aluMatrixfSet(&matrix,
|
||||
// ACN0 ACN1 ACN2 ACN3
|
||||
sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
||||
0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
|
||||
0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
|
||||
0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
|
||||
// ACN0 ACN1 ACN2 ACN3
|
||||
SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
||||
0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
|
||||
0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
|
||||
0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
|
||||
);
|
||||
|
||||
voice->Direct.Buffer = Device->FOAOut.Buffer;
|
||||
voice->Direct.Channels = Device->FOAOut.NumChannels;
|
||||
for(c = 0;c < num_channels;c++)
|
||||
ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
if(Slot)
|
||||
{
|
||||
for(c = 0;c < num_channels;c++)
|
||||
ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
|
||||
);
|
||||
}
|
||||
@@ -890,7 +915,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
@@ -913,9 +938,8 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
continue;
|
||||
}
|
||||
|
||||
ComputeDryPanGains(&Device->Dry,
|
||||
coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
|
||||
);
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
}
|
||||
|
||||
for(i = 0;i < NumSends;i++)
|
||||
@@ -951,7 +975,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
@@ -976,9 +1000,8 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
chans[c].elevation, Spread, coeffs
|
||||
);
|
||||
|
||||
ComputeDryPanGains(&Device->Dry,
|
||||
coeffs, DryGain, voice->Direct.Params[c].Gains.Target
|
||||
);
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
@@ -1815,8 +1838,7 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
SamplesToDo, device->RealOut.NumChannels);
|
||||
|
||||
if(device->Limiter)
|
||||
ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
|
||||
device->RealOut.Buffer);
|
||||
ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
|
||||
|
||||
if(device->DitherDepth > 0.0f)
|
||||
ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
|
||||
@@ -1850,25 +1872,24 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
|
||||
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
|
||||
ALCcontext *ctx;
|
||||
AsyncEvent evt;
|
||||
va_list args;
|
||||
int msglen;
|
||||
|
||||
if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
evt.EnumType = EventType_Disconnected;
|
||||
evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.ObjectId = 0;
|
||||
evt.Param = 0;
|
||||
evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.u.user.id = 0;
|
||||
evt.u.user.param = 0;
|
||||
|
||||
va_start(args, msg);
|
||||
msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
|
||||
msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
|
||||
evt.Message[sizeof(evt.Message)-1] = 0;
|
||||
if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
|
||||
evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
|
||||
|
||||
ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
|
||||
while(ctx)
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alconfig.h"
|
||||
@@ -417,6 +420,29 @@ void ReadALConfig(void)
|
||||
|
||||
alstr_reset(&ppath);
|
||||
}
|
||||
#elif defined __vita__
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
const char* config_paths[] =
|
||||
{
|
||||
"app0:/alsoft.conf",
|
||||
"ux0:/data/openal/alsoft.conf"
|
||||
};
|
||||
|
||||
FILE* f;
|
||||
unsigned int i;
|
||||
for(i = 0; i < sizeof(config_paths) / sizeof(*config_paths); ++i)
|
||||
{
|
||||
TRACE("Loading config %s...\n", config_paths[i]);
|
||||
f = al_fopen(config_paths[i], "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
@@ -478,6 +504,26 @@ void ReadALConfig(void)
|
||||
alstr_clear(&fname);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
if(mainBundle)
|
||||
{
|
||||
unsigned char fileName[PATH_MAX];
|
||||
CFURLRef configURL;
|
||||
|
||||
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), NULL)) &&
|
||||
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
|
||||
{
|
||||
f = al_fopen((const char*)fileName, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if((str=getenv("HOME")) != NULL && *str)
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
|
||||
+9
-8
@@ -1375,11 +1375,6 @@ static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCalsaBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCalsaBackendFactory;
|
||||
@@ -1417,19 +1412,25 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
#define APPEND_OUTNAME(i) do { \
|
||||
if(!alstr_empty((i)->name)) \
|
||||
alstr_append_range(outnames, VECTOR_BEGIN((i)->name), \
|
||||
VECTOR_END((i)->name)+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
|
||||
extern inline void ALCdevice_Lock(ALCdevice *device);
|
||||
extern inline void ALCdevice_Unlock(ALCdevice *device);
|
||||
extern inline ClockLatency GetClockLatency(ALCdevice *device);
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
|
||||
+14
-3
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "alstring.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -115,7 +116,7 @@ struct ALCbackendFactoryVtable {
|
||||
|
||||
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
|
||||
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
|
||||
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
};
|
||||
@@ -124,7 +125,7 @@ struct ALCbackendFactoryVtable {
|
||||
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
|
||||
\
|
||||
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
@@ -142,13 +143,14 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
|
||||
@@ -161,6 +163,15 @@ inline void ALCdevice_Lock(ALCdevice *device)
|
||||
inline void ALCdevice_Unlock(ALCdevice *device)
|
||||
{ V0(device->Backend,unlock)(); }
|
||||
|
||||
|
||||
inline ClockLatency GetClockLatency(ALCdevice *device)
|
||||
{
|
||||
ClockLatency ret = V0(device->Backend,getClockLatency)();
|
||||
ret.Latency += device->FixedLatency;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
@@ -112,7 +111,11 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
|
||||
|
||||
/* open the default output unit */
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
@@ -451,7 +454,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
AudioComponentDescription desc;
|
||||
AudioDeviceID inputDevice;
|
||||
UInt32 outputFrameCount;
|
||||
UInt32 propertySize;
|
||||
AudioObjectPropertyAddress propertyAddress;
|
||||
@@ -465,7 +467,11 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
@@ -504,7 +510,9 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_IOS
|
||||
// Get the default input device
|
||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
@@ -517,7 +525,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
ERR("AudioObjectGetPropertyData failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
{
|
||||
ERR("No input device found\n");
|
||||
@@ -531,6 +538,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||
@@ -752,7 +760,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
|
||||
|
||||
@@ -776,15 +784,13 @@ static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFac
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(ca_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(ca_device);
|
||||
alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-9
@@ -969,11 +969,6 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCdsoundBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCdsoundBackendFactory;
|
||||
@@ -984,7 +979,7 @@ ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
|
||||
static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
|
||||
static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
|
||||
|
||||
@@ -1028,7 +1023,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
HRESULT hr, hrcom;
|
||||
|
||||
@@ -1036,12 +1031,17 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
|
||||
hrcom = CoInitialize(NULL);
|
||||
switch(type)
|
||||
{
|
||||
#define APPEND_OUTNAME(e) do { \
|
||||
if(!alstr_empty((e)->name)) \
|
||||
alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
|
||||
VECTOR_END((e)->name)+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
clear_devlist(&PlaybackDevices);
|
||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
@@ -1049,8 +1049,9 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
|
||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
|
||||
+2
-2
@@ -571,12 +571,12 @@ static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(jackDevice);
|
||||
alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
|
||||
@@ -87,7 +87,7 @@ ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
|
||||
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
|
||||
|
||||
@@ -110,7 +110,7 @@ static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(sel
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -171,7 +171,7 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
|
||||
|
||||
@@ -195,14 +195,13 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(nullDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+69
-73
@@ -206,6 +206,9 @@ static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
alsem_destroy(&self->mSem);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
@@ -251,19 +254,16 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
ALCopenslPlayback_lock(self);
|
||||
aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
|
||||
ALCopenslPlayback_unlock(self);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ALCopenslPlayback_lock(self);
|
||||
while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
|
||||
|
||||
while(SL_RESULT_SUCCESS == result &&
|
||||
!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
size_t todo, len0, len1;
|
||||
size_t todo;
|
||||
|
||||
if(ll_ringbuffer_write_space(self->mRing) == 0)
|
||||
{
|
||||
@@ -292,34 +292,33 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
}
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->mRing, data);
|
||||
|
||||
aluMixData(device, data[0].buf, data[0].len*device->UpdateSize);
|
||||
if(data[1].len > 0)
|
||||
aluMixData(device, data[1].buf, data[1].len*device->UpdateSize);
|
||||
|
||||
todo = data[0].len+data[1].len;
|
||||
ll_ringbuffer_write_advance(self->mRing, todo);
|
||||
|
||||
len0 = minu(todo, data[0].len);
|
||||
len1 = minu(todo-len0, data[1].len);
|
||||
|
||||
aluMixData(device, data[0].buf, len0*device->UpdateSize);
|
||||
for(size_t i = 0;i < len0;i++)
|
||||
for(size_t i = 0;i < todo;i++)
|
||||
{
|
||||
if(!data[0].len)
|
||||
{
|
||||
data[0] = data[1];
|
||||
data[1].buf = NULL;
|
||||
data[1].len = 0;
|
||||
}
|
||||
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
ll_ringbuffer_write_advance(self->mRing, 1);
|
||||
|
||||
data[0].buf += device->UpdateSize*self->mFrameSize;
|
||||
}
|
||||
|
||||
if(len1 > 0)
|
||||
{
|
||||
aluMixData(device, data[1].buf, len1*device->UpdateSize);
|
||||
for(size_t i = 0;i < len1;i++)
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[1].buf, device->UpdateSize*self->mFrameSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
ll_ringbuffer_write_advance(self->mRing, 1);
|
||||
|
||||
data[1].buf += device->UpdateSize*self->mFrameSize;
|
||||
aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
|
||||
break;
|
||||
}
|
||||
|
||||
data[0].len--;
|
||||
data[0].buf += device->UpdateSize*self->mFrameSize;
|
||||
}
|
||||
}
|
||||
ALCopenslPlayback_unlock(self);
|
||||
@@ -392,19 +391,24 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
SLInterfaceID ids[2];
|
||||
SLboolean reqs[2];
|
||||
SLresult result;
|
||||
JNIEnv *env;
|
||||
|
||||
if(self->mBufferQueueObj != NULL)
|
||||
VCALL0(self->mBufferQueueObj,Destroy)();
|
||||
self->mBufferQueueObj = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
sampleRate = device->Frequency;
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL)
|
||||
#if 0
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
{
|
||||
/* FIXME: Disabled until I figure out how to get the Context needed for
|
||||
* the getSystemService call.
|
||||
*/
|
||||
#if 0
|
||||
JNIEnv *env = Android_GetJNIEnv();
|
||||
jobject jctx = Android_GetContext();
|
||||
|
||||
/* Get necessary stuff for using java.lang.Integer,
|
||||
* android.content.Context, and android.media.AudioManager.
|
||||
*/
|
||||
@@ -440,7 +444,7 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
/* Now make the calls. */
|
||||
//AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
|
||||
strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
|
||||
jobject audMgr = JCALL(env,CallObjectMethod)(ctx_cls, ctx_getSysSvc, strobj);
|
||||
jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
|
||||
strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
|
||||
TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
|
||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
||||
@@ -461,8 +465,8 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
|
||||
if(!sampleRate) sampleRate = device->Frequency;
|
||||
else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if(sampleRate != device->Frequency)
|
||||
{
|
||||
@@ -546,6 +550,18 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "bufferQueue->Realize");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates,
|
||||
self->mFrameSize*device->UpdateSize, true
|
||||
);
|
||||
if(!self->mRing)
|
||||
{
|
||||
ERR("Out of memory allocating ring buffer %ux%u %u\n", device->UpdateSize,
|
||||
device->NumUpdates, self->mFrameSize);
|
||||
result = SL_RESULT_MEMORY_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
@@ -561,13 +577,10 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
|
||||
static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLresult result;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize,
|
||||
true);
|
||||
ll_ringbuffer_reset(self->mRing);
|
||||
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -634,9 +647,6 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
|
||||
} while(SL_RESULT_SUCCESS == result && state.count > 0);
|
||||
PRINTERR(result, "bufferQueue->GetState");
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
}
|
||||
|
||||
static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
|
||||
@@ -713,9 +723,6 @@ static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device
|
||||
|
||||
static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
if(self->mRecordObj != NULL)
|
||||
VCALL0(self->mRecordObj,Destroy)();
|
||||
self->mRecordObj = NULL;
|
||||
@@ -725,6 +732,9 @@ static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
@@ -843,8 +853,9 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
|
||||
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, device->UpdateSize*self->mFrameSize,
|
||||
false);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates,
|
||||
device->UpdateSize*self->mFrameSize, false
|
||||
);
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -941,14 +952,16 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
ll_ringbuffer_data_t data[2];
|
||||
SLresult result;
|
||||
size_t advance;
|
||||
ALCuint i;
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
PRINTERR(result, "recordObj->GetInterface");
|
||||
|
||||
/* Read the desired samples from the ring buffer then advance its read
|
||||
* pointer.
|
||||
*/
|
||||
ll_ringbuffer_get_read_vector(self->mRing, data);
|
||||
advance = 0;
|
||||
for(i = 0;i < samples;)
|
||||
{
|
||||
ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
|
||||
@@ -961,7 +974,11 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
{
|
||||
/* Finished a chunk, reset the offset and advance the read pointer. */
|
||||
self->mSplOffset = 0;
|
||||
advance++;
|
||||
|
||||
ll_ringbuffer_read_advance(self->mRing, 1);
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS != result) break;
|
||||
|
||||
data[0].len--;
|
||||
if(!data[0].len)
|
||||
@@ -972,24 +989,6 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
|
||||
i += rem;
|
||||
}
|
||||
ll_ringbuffer_read_advance(self->mRing, advance);
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
PRINTERR(result, "recordObj->GetInterface");
|
||||
|
||||
/* Enqueue any newly-writable chunks in the ring buffer. */
|
||||
ll_ringbuffer_get_write_vector(self->mRing, data);
|
||||
for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
@@ -1030,16 +1029,13 @@ static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory*
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(opensl_device);
|
||||
alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-21
@@ -786,7 +786,7 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
|
||||
static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
|
||||
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
|
||||
|
||||
@@ -820,41 +820,32 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
struct oss_device *cur;
|
||||
struct oss_device *cur = NULL;
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_capture);
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
cur = &oss_capture;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
|
||||
@@ -484,9 +484,8 @@ typedef struct ALCportBackendFactory {
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
|
||||
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
|
||||
|
||||
|
||||
@@ -518,15 +517,13 @@ static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -1760,9 +1760,8 @@ typedef struct ALCpulseBackendFactory {
|
||||
static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self);
|
||||
static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self);
|
||||
static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
|
||||
|
||||
|
||||
@@ -1835,23 +1834,25 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
#define APPEND_OUTNAME(e) do { \
|
||||
if(!alstr_empty((e)->name)) \
|
||||
alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
|
||||
VECTOR_END((e)->name)+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
ALCpulsePlayback_probeDevices();
|
||||
#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(alstr_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
|
||||
#undef APPEND_ALL_DEVICES_LIST
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCpulseCapture_probeDevices();
|
||||
#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(alstr_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
|
||||
#undef APPEND_CAPTURE_DEVICE_LIST
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1899,7 +1900,7 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+13
-18
@@ -119,6 +119,9 @@ static void deviceList(int type, vector_DevMap *devmap)
|
||||
if(max_cards < 0)
|
||||
return;
|
||||
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, *devmap, FREE_NAME);
|
||||
#undef FREE_NAME
|
||||
VECTOR_RESIZE(*devmap, 0, max_cards+1);
|
||||
|
||||
entry.name = strdup(qsaDevice);
|
||||
@@ -989,7 +992,7 @@ typedef struct ALCqsaBackendFactory {
|
||||
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 void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
|
||||
|
||||
@@ -1016,33 +1019,25 @@ static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
#define APPEND_OUTNAME(e) do { \
|
||||
const char *n_ = (e)->name; \
|
||||
if(n_ && n_[0]) \
|
||||
alstr_append_range(outnames, n_, n_+strlen(n_)+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
|
||||
VECTOR_RESIZE(DeviceNameMap, 0, 0);
|
||||
#undef FREE_NAME
|
||||
|
||||
deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
|
||||
#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
|
||||
VECTOR_RESIZE(CaptureNameMap, 0, 0);
|
||||
#undef FREE_NAME
|
||||
|
||||
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
|
||||
#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -221,7 +221,7 @@ ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
|
||||
static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
|
||||
static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
|
||||
static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type);
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
|
||||
|
||||
@@ -252,7 +252,7 @@ static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
int num_devices, i;
|
||||
al_string name;
|
||||
@@ -263,12 +263,13 @@ static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enu
|
||||
AL_STRING_INIT(name);
|
||||
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
|
||||
AppendAllDevicesList(defaultDeviceName);
|
||||
alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
|
||||
for(i = 0;i < num_devices;++i)
|
||||
{
|
||||
alstr_copy_cstr(&name, DEVNAME_PREFIX);
|
||||
alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
|
||||
AppendAllDevicesList(alstr_get_cstr(name));
|
||||
if(!alstr_empty(name))
|
||||
alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
|
||||
}
|
||||
alstr_reset(&name);
|
||||
}
|
||||
|
||||
+312
-54
@@ -27,15 +27,17 @@
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
typedef struct ALCsndioBackend {
|
||||
typedef struct SndioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
struct sio_hdl *sndHandle;
|
||||
@@ -45,40 +47,37 @@ typedef struct ALCsndioBackend {
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCsndioBackend;
|
||||
} SndioPlayback;
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr);
|
||||
static int SndioPlayback_mixerProc(void *ptr);
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self);
|
||||
static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self);
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self);
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self);
|
||||
static void SndioPlayback_stop(SndioPlayback *self);
|
||||
static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
|
||||
SET_VTABLE2(SndioPlayback, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
@@ -91,9 +90,9 @@ static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
static int SndioPlayback_mixerProc(void *ptr)
|
||||
{
|
||||
ALCsndioBackend *self = (ALCsndioBackend*)ptr;
|
||||
SndioPlayback *self = (SndioPlayback*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
@@ -109,9 +108,9 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
ALCsndioBackend_lock(self);
|
||||
SndioPlayback_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
ALCsndioBackend_unlock(self);
|
||||
SndioPlayback_unlock(self);
|
||||
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
wrote = sio_write(self->sndHandle, WritePtr, len);
|
||||
@@ -133,7 +132,7 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
@@ -154,7 +153,7 @@ static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
@@ -239,7 +238,7 @@ static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
@@ -256,7 +255,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
|
||||
if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
@@ -265,7 +264,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
static void SndioPlayback_stop(SndioPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
@@ -281,59 +280,318 @@ static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCsndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsndioBackendFactory;
|
||||
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
|
||||
typedef struct SndioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} SndioCapture;
|
||||
|
||||
static int SndioCapture_recordProc(void *ptr);
|
||||
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
|
||||
static void SndioCapture_Destruct(SndioCapture *self);
|
||||
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean SndioCapture_start(SndioCapture *self);
|
||||
static void SndioCapture_stop(SndioCapture *self);
|
||||
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
|
||||
static ALCuint SndioCapture_availableSamples(SndioCapture *self);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioCapture);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
|
||||
{
|
||||
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(SndioCapture, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->ring = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void SndioCapture_Destruct(SndioCapture *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
|
||||
static int SndioCapture_recordProc(void* ptr)
|
||||
{
|
||||
SndioCapture *self = (SndioCapture*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ll_ringbuffer_data_t data[2];
|
||||
size_t total, todo;
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->ring, data);
|
||||
todo = data[0].len + data[1].len;
|
||||
if(todo == 0)
|
||||
{
|
||||
static char junk[4096];
|
||||
sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
data[0].len *= frameSize;
|
||||
data[1].len *= frameSize;
|
||||
todo = minz(todo, device->UpdateSize) * frameSize;
|
||||
while(total < todo)
|
||||
{
|
||||
size_t got;
|
||||
|
||||
if(!data[0].len)
|
||||
data[0] = data[1];
|
||||
|
||||
got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
|
||||
if(!got)
|
||||
{
|
||||
SndioCapture_lock(self);
|
||||
aluHandleDisconnect(device, "Failed to read capture samples");
|
||||
SndioCapture_unlock(self);
|
||||
break;
|
||||
}
|
||||
|
||||
data[0].buf += got;
|
||||
data[0].len -= got;
|
||||
total += got;
|
||||
}
|
||||
ll_ringbuffer_write_advance(self->ring, total / frameSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->sndHandle = sio_open(NULL, SIO_REC, 0);
|
||||
if(self->sndHandle == NULL)
|
||||
{
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
sio_initpar(&par);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bps = 1;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bps = 1;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
par.bps = 2;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bps = 2;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bps = 4;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bps = 4;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
par.bits = par.bps * 8;
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = SIO_LE_NATIVE ? 0 : 1;
|
||||
par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
par.rate = device->Frequency;
|
||||
|
||||
par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
|
||||
par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
|
||||
(device->Frequency+19)/20);
|
||||
|
||||
device->UpdateSize = par.round;
|
||||
device->NumUpdates = maxu(par.appbufsz/par.round, 1);
|
||||
|
||||
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
|
||||
(device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
|
||||
(device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
|
||||
ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
|
||||
device->Frequency != par.rate)
|
||||
{
|
||||
ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
|
||||
DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
|
||||
device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("Failed to allocate %u-byte ringbuffer\n",
|
||||
device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean SndioCapture_start(SndioCapture *self)
|
||||
{
|
||||
if(!sio_start(self->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void SndioCapture_stop(SndioCapture *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(!sio_stop(self->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
}
|
||||
|
||||
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint SndioCapture_availableSamples(SndioCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
|
||||
typedef struct SndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} SndioBackendFactory;
|
||||
#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void)
|
||||
{
|
||||
static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
|
||||
{
|
||||
/* No dynamic loading */
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsndioBackend *backend;
|
||||
NEW_OBJ(backend, ALCsndioBackend)(device);
|
||||
SndioPlayback *backend;
|
||||
NEW_OBJ(backend, SndioPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
SndioCapture *backend;
|
||||
NEW_OBJ(backend, SndioCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
|
||||
|
||||
@@ -327,7 +327,7 @@ static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -337,7 +337,7 @@ static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(solaris_device);
|
||||
alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <psp2/audioin.h>
|
||||
#include <psp2/audioout.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#define AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
|
||||
|
||||
static const ALCchar playbackDeviceName[] = "PS Vita Speakers/Headphones";
|
||||
static const ALCchar captureDeviceName[] = "PS Vita Microphone";
|
||||
|
||||
extern unsigned int _oal_thread_priority __attribute__((weak));
|
||||
extern unsigned int _oal_thread_affinity __attribute__((weak));
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Playback
|
||||
// -----------------------------------------------------------------------------
|
||||
typedef struct ALCvitaPlayback
|
||||
{
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
SceUID thread;
|
||||
SceKernelLwMutexWork lock;
|
||||
|
||||
int portNumber;
|
||||
ALsizei frameSize;
|
||||
void* waveBuffer;
|
||||
|
||||
ALuint Frequency;
|
||||
enum DevFmtChannels FmtChans;
|
||||
enum DevFmtType FmtType;
|
||||
ALuint UpdateSize;
|
||||
} ALCvitaPlayback;
|
||||
|
||||
static void ALCvitaPlayback_Construct(ALCvitaPlayback *self, ALCdevice *device);
|
||||
static void ALCvitaPlayback_Destruct(ALCvitaPlayback *self);
|
||||
static ALCenum ALCvitaPlayback_open(ALCvitaPlayback *self, const ALCchar *name);
|
||||
static ALCboolean ALCvitaPlayback_reset(ALCvitaPlayback *self);
|
||||
static ALCboolean ALCvitaPlayback_start(ALCvitaPlayback *self);
|
||||
static void ALCvitaPlayback_stop(ALCvitaPlayback *self);
|
||||
static void ALCvitaPlayback_lock(ALCvitaPlayback *self);
|
||||
static void ALCvitaPlayback_unlock(ALCvitaPlayback *self);
|
||||
|
||||
static DECLARE_FORWARD2(ALCvitaPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCvitaPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCvitaPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCvitaPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCvitaPlayback);
|
||||
|
||||
static void ALCvitaPlayback_Construct(ALCvitaPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCvitaPlayback, ALCbackend, self);
|
||||
|
||||
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
|
||||
|
||||
self->portNumber = 0;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
|
||||
sceKernelCreateLwMutex(
|
||||
&self->lock,
|
||||
"OpenAL Vita playback mutex",
|
||||
SCE_KERNEL_MUTEX_ATTR_RECURSIVE, // No SCE_KERNEL_LW_MUTEX_ATTR_RECURSIVE in VitaSDK, but it's the same
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static void ALCvitaPlayback_Destruct(ALCvitaPlayback *self)
|
||||
{
|
||||
if (self->portNumber)
|
||||
{
|
||||
sceAudioOutReleasePort(self->portNumber);
|
||||
self->portNumber = 0;
|
||||
}
|
||||
|
||||
if (self->waveBuffer)
|
||||
{
|
||||
free(self->waveBuffer);
|
||||
self->waveBuffer = NULL;
|
||||
}
|
||||
|
||||
sceKernelDeleteLwMutex(&self->lock);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static int ALCvitaPlayback_MixerProc(SceSize args, void *argp)
|
||||
{
|
||||
(void)args;
|
||||
ALCvitaPlayback *self = *(ALCvitaPlayback **) argp;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
while (!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
ALCvitaPlayback_lock(self);
|
||||
aluMixData(device, self->waveBuffer, device->UpdateSize);
|
||||
ALCvitaPlayback_unlock(self);
|
||||
sceAudioOutOutput(self->portNumber, self->waveBuffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum ALCvitaPlayback_open(ALCvitaPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
/* Only signed short output sample format is supported */
|
||||
device->FmtType = DevFmtShort;
|
||||
|
||||
/* Only mono/stereo channel configurations are supported */
|
||||
if (device->FmtChans != DevFmtMono && device->FmtChans != DevFmtStereo)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
|
||||
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
|
||||
|
||||
self->portNumber = sceAudioOutOpenPort(
|
||||
SCE_AUDIO_OUT_PORT_TYPE_BGM,
|
||||
device->UpdateSize,
|
||||
device->Frequency,
|
||||
device->FmtChans == DevFmtStereo ? SCE_AUDIO_OUT_MODE_STEREO : SCE_AUDIO_OUT_MODE_MONO
|
||||
);
|
||||
|
||||
if (self->portNumber < 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
|
||||
self->waveBuffer = calloc(device->UpdateSize * self->frameSize, 1);
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name ? name : playbackDeviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCvitaPlayback_reset(ALCvitaPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
if (device->FmtChans != DevFmtMono && device->FmtChans != DevFmtStereo)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
|
||||
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
|
||||
|
||||
sceAudioOutSetConfig(
|
||||
self->portNumber,
|
||||
device->UpdateSize,
|
||||
device->Frequency,
|
||||
device->FmtChans == DevFmtStereo ? SCE_AUDIO_OUT_MODE_STEREO : SCE_AUDIO_OUT_MODE_MONO
|
||||
);
|
||||
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
|
||||
if (self->waveBuffer)
|
||||
{
|
||||
free(self->waveBuffer);
|
||||
}
|
||||
|
||||
self->waveBuffer = calloc(device->UpdateSize * self->frameSize, 1);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCvitaPlayback_start(ALCvitaPlayback *self)
|
||||
{
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
|
||||
int priority = 128; // middle
|
||||
int affinity = 0; // DEFAULT
|
||||
int stack_size = 0x10000; // 64Kib
|
||||
|
||||
if (&_oal_thread_priority != NULL) {
|
||||
priority = _oal_thread_priority;
|
||||
} else {
|
||||
SceKernelThreadInfo info;
|
||||
info.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelGetThreadInfo(sceKernelGetThreadId(), &info) == 0) {
|
||||
priority = info.currentPriority - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (priority < 64 && priority != 0) priority = 64;
|
||||
if (priority > 191) priority = 191;
|
||||
|
||||
if (&_oal_thread_affinity != NULL) {
|
||||
affinity = _oal_thread_affinity;
|
||||
}
|
||||
|
||||
self->thread = sceKernelCreateThread("OpenAL Vita playback thread", ALCvitaPlayback_MixerProc,
|
||||
priority, stack_size, 0, affinity, NULL);
|
||||
|
||||
if (self->thread < 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
int ret = sceKernelStartThread(self->thread, 4, &self);
|
||||
if (ret < 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCvitaPlayback_stop(ALCvitaPlayback *self)
|
||||
{
|
||||
if (ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
sceKernelWaitThreadEnd(self->thread, NULL, NULL);
|
||||
sceKernelDeleteThread(self->thread);
|
||||
}
|
||||
|
||||
static void ALCvitaPlayback_lock(ALCvitaPlayback *self)
|
||||
{
|
||||
sceKernelLockLwMutex(&self->lock, 1, NULL);
|
||||
}
|
||||
|
||||
static void ALCvitaPlayback_unlock(ALCvitaPlayback *self)
|
||||
{
|
||||
sceKernelUnlockLwMutex(&self->lock, 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Capture
|
||||
// -----------------------------------------------------------------------------
|
||||
typedef struct ALCvitaCapture
|
||||
{
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
SceKernelLwMutexWork lock;
|
||||
|
||||
int portNumber;
|
||||
ALsizei frameSize;
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
ALuint Frequency;
|
||||
enum DevFmtChannels FmtChans;
|
||||
enum DevFmtType FmtType;
|
||||
ALuint UpdateSize;
|
||||
} ALCvitaCapture;
|
||||
|
||||
static void ALCvitaCapture_Construct(ALCvitaCapture *self, ALCdevice *device);
|
||||
static void ALCvitaCapture_Destruct(ALCvitaCapture *self);
|
||||
static ALCenum ALCvitaCapture_open(ALCvitaCapture *self, const ALCchar *name);
|
||||
static ALCboolean ALCvitaCapture_reset(ALCvitaCapture *self);
|
||||
static ALCboolean ALCvitaCapture_start(ALCvitaCapture *self);
|
||||
static void ALCvitaCapture_stop(ALCvitaCapture *self);
|
||||
static void ALCvitaCapture_lock(ALCvitaCapture *self);
|
||||
static void ALCvitaCapture_unlock(ALCvitaCapture *self);
|
||||
static ALCenum ALCvitaCapture_captureSamples(ALCvitaCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCvitaCapture_availableSamples(ALCvitaCapture *self);
|
||||
static DECLARE_FORWARD(ALCvitaCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCvitaCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCvitaCapture);
|
||||
|
||||
static void ALCvitaCapture_Construct(ALCvitaCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCvitaCapture, ALCbackend, self);
|
||||
|
||||
self->portNumber = 0;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
}
|
||||
|
||||
static void ALCvitaCapture_Destruct(ALCvitaCapture *self)
|
||||
{
|
||||
if (self->portNumber)
|
||||
{
|
||||
sceAudioOutReleasePort(self->portNumber);
|
||||
self->portNumber = 0;
|
||||
}
|
||||
|
||||
if (self->ring)
|
||||
{
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static int ALCvitaCapture_MixerProc(void *ptr)
|
||||
{
|
||||
ALCvitaCapture *self = (ALCvitaCapture*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
void* buf = malloc(self->frameSize * device->UpdateSize);
|
||||
|
||||
while (!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
sceAudioInInput(self->portNumber, buf);
|
||||
ll_ringbuffer_write(self->ring, buf, self->frameSize * device->UpdateSize);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum ALCvitaCapture_open(ALCvitaCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
/* Only signed short output sample format is supported */
|
||||
device->FmtType = DevFmtShort;
|
||||
|
||||
/* Only mono channel configuration is supported */
|
||||
if (device->FmtChans != DevFmtMono)
|
||||
device->FmtChans = DevFmtMono;
|
||||
|
||||
/* TODO: Validate samplerate and update size */
|
||||
|
||||
self->portNumber = sceAudioInOpenPort(
|
||||
SCE_AUDIO_IN_PORT_TYPE_RAW,
|
||||
device->UpdateSize,
|
||||
device->Frequency,
|
||||
SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO
|
||||
);
|
||||
|
||||
if (self->portNumber < 0)
|
||||
{
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize * device->NumUpdates, self->frameSize, false);
|
||||
if (self->ring == NULL)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name ? name : captureDeviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCvitaCapture_reset(ALCvitaCapture *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCvitaCapture_start(ALCvitaCapture *self)
|
||||
{
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if (althrd_create(&self->thread, ALCvitaCapture_MixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCvitaCapture_stop(ALCvitaCapture *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
althrd_join(self->thread, &res);
|
||||
}
|
||||
|
||||
static void ALCvitaCapture_lock(ALCvitaCapture *self)
|
||||
{
|
||||
sceKernelLockLwMutex(&self->lock, 1, NULL);
|
||||
}
|
||||
|
||||
static void ALCvitaCapture_unlock(ALCvitaCapture *self)
|
||||
{
|
||||
sceKernelUnlockLwMutex(&self->lock, 1);
|
||||
}
|
||||
|
||||
|
||||
static ALCuint ALCvitaCapture_availableSamples(ALCvitaCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
static ALCenum ALCvitaCapture_captureSamples(ALCvitaCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Backends
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct ALCvitaBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCvitaBackendFactory;
|
||||
#define ALCvitaBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCvitaBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCvitaBackendFactory_init(ALCvitaBackendFactory *self);
|
||||
static void ALCvitaBackendFactory_deinit(ALCvitaBackendFactory *self);
|
||||
static ALCboolean ALCvitaBackendFactory_querySupport(ALCvitaBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCvitaBackendFactory_probe(ALCvitaBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCvitaBackendFactory_createBackend(ALCvitaBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCvitaBackendFactory);
|
||||
|
||||
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCvitaBackendFactory factory = ALCvitaBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCvitaBackendFactory_init(ALCvitaBackendFactory* UNUSED(self))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static void ALCvitaBackendFactory_deinit(ALCvitaBackendFactory* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCboolean ALCvitaBackendFactory_querySupport(ALCvitaBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if (type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCvitaBackendFactory_probe(ALCvitaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
if (type == ALL_DEVICE_PROBE)
|
||||
alstr_append_range(outnames, playbackDeviceName, playbackDeviceName+sizeof(playbackDeviceName));
|
||||
else if (type == CAPTURE_DEVICE_PROBE)
|
||||
alstr_append_range(outnames, captureDeviceName, playbackDeviceName+sizeof(captureDeviceName));
|
||||
}
|
||||
|
||||
static ALCbackend* ALCvitaBackendFactory_createBackend(ALCvitaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if (type == ALCbackend_Playback)
|
||||
{
|
||||
ALCvitaPlayback *backend;
|
||||
NEW_OBJ(backend, ALCvitaPlayback)(device);
|
||||
|
||||
if (!backend)
|
||||
return NULL;
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if (type == ALCbackend_Capture)
|
||||
{
|
||||
ALCvitaCapture *backend;
|
||||
NEW_OBJ(backend, ALCvitaCapture)(device);
|
||||
|
||||
if (!backend)
|
||||
return NULL;
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
+10
-9
@@ -1919,11 +1919,6 @@ ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer,
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCwasapiBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwasapiBackendFactory;
|
||||
@@ -1932,7 +1927,7 @@ typedef struct ALCwasapiBackendFactory {
|
||||
static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self);
|
||||
static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self);
|
||||
static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type);
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
|
||||
@@ -1989,7 +1984,7 @@ static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory*
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
ThreadRequest req = { NULL, 0 };
|
||||
|
||||
@@ -2003,13 +1998,19 @@ static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self),
|
||||
hr = WaitForResponse(&req);
|
||||
if(SUCCEEDED(hr)) switch(type)
|
||||
{
|
||||
#define APPEND_OUTNAME(e) do { \
|
||||
if(!alstr_empty((e)->name)) \
|
||||
alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
|
||||
VECTOR_END((e)->name)+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
CloseHandle(req.FinishedEvt);
|
||||
req.FinishedEvt = NULL;
|
||||
|
||||
+3
-3
@@ -403,7 +403,7 @@ ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
|
||||
|
||||
@@ -427,12 +427,12 @@ static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(waveDevice);
|
||||
alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
|
||||
+9
-15
@@ -700,17 +700,6 @@ static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendAllDevicesList(alstr_get_cstr(*name));
|
||||
}
|
||||
static inline void AppendCaptureDeviceList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendCaptureDeviceList(alstr_get_cstr(*name));
|
||||
}
|
||||
|
||||
typedef struct ALCwinmmBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwinmmBackendFactory;
|
||||
@@ -719,7 +708,7 @@ typedef struct ALCwinmmBackendFactory {
|
||||
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
|
||||
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
|
||||
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
|
||||
@@ -749,19 +738,24 @@ static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UN
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
#define APPEND_OUTNAME(n) do { \
|
||||
if(!alstr_empty(*(n))) \
|
||||
alstr_append_range(outnames, VECTOR_BEGIN(*(n)), VECTOR_END(*(n))+1); \
|
||||
} while(0)
|
||||
case ALL_DEVICE_PROBE:
|
||||
ProbePlaybackDevices();
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ProbeCaptureDevices();
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, APPEND_OUTNAME);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -438,7 +438,7 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
|
||||
CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
|
||||
+3
-3
@@ -24,9 +24,9 @@
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
|
||||
* coefficients should be divided by these values to get proper N3D scalings.
|
||||
*/
|
||||
const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
|
||||
extern const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
extern const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
extern const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
|
||||
|
||||
|
||||
struct AmbDecConf;
|
||||
|
||||
@@ -50,14 +50,6 @@ void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
/** Returns a JNIEnv*. */
|
||||
void *Android_GetJNIEnv(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
@@ -130,8 +130,8 @@ static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *con
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -149,9 +149,9 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
|
||||
@@ -99,8 +99,7 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontex
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -102,7 +102,7 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,7 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontex
|
||||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain,
|
||||
state->Gain);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
+2
-2
@@ -141,11 +141,11 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
|
||||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -76,12 +76,12 @@ typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
struct {
|
||||
/* Effect parameters */
|
||||
BiquadFilter filter[4];
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
BiquadFilter filter[4];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
@@ -128,12 +128,6 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
||||
ALfloat gain, f0norm;
|
||||
ALuint i;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
@@ -174,6 +168,12 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
|
||||
}
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -144,7 +144,7 @@ static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *c
|
||||
}
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -158,8 +158,8 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
@@ -208,7 +208,7 @@ static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *c
|
||||
state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
||||
+191
-159
@@ -274,7 +274,7 @@ typedef struct LateReverb {
|
||||
ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
|
||||
} LateReverb;
|
||||
|
||||
typedef struct ALreverbState {
|
||||
typedef struct ReverbState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* All delay lines are allocated as a single buffer to reduce memory
|
||||
@@ -283,6 +283,15 @@ typedef struct ALreverbState {
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint TotalSamples;
|
||||
|
||||
struct {
|
||||
/* Calculated parameters which indicate if cross-fading is needed after
|
||||
* an update.
|
||||
*/
|
||||
ALfloat Density, Diffusion;
|
||||
ALfloat DecayTime, HFDecayTime, LFDecayTime;
|
||||
ALfloat HFReference, LFReference;
|
||||
} Params;
|
||||
|
||||
/* Master effect filters */
|
||||
struct {
|
||||
BiquadFilter Lp;
|
||||
@@ -320,26 +329,34 @@ typedef struct ALreverbState {
|
||||
/* Temporary storage used when processing. */
|
||||
alignas(16) ALfloat TempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
|
||||
alignas(16) ALfloat MixSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
|
||||
} ALreverbState;
|
||||
} ReverbState;
|
||||
|
||||
static ALvoid ALreverbState_Destruct(ALreverbState *State);
|
||||
static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
|
||||
static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
|
||||
static ALvoid ReverbState_Destruct(ReverbState *State);
|
||||
static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
|
||||
static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ReverbState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
|
||||
|
||||
static void ALreverbState_Construct(ALreverbState *state)
|
||||
static void ReverbState_Construct(ReverbState *state)
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALreverbState, ALeffectState, state);
|
||||
SET_VTABLE2(ReverbState, ALeffectState, state);
|
||||
|
||||
state->TotalSamples = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
state->Params.Density = AL_EAXREVERB_DEFAULT_DENSITY;
|
||||
state->Params.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
|
||||
state->Params.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
|
||||
state->Params.HFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
|
||||
state->Params.LFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
|
||||
state->Params.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
|
||||
state->Params.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
BiquadFilter_clear(&state->Filter[i].Lp);
|
||||
@@ -421,7 +438,7 @@ static void ALreverbState_Construct(ALreverbState *state)
|
||||
state->Offset = 0;
|
||||
}
|
||||
|
||||
static ALvoid ALreverbState_Destruct(ALreverbState *State)
|
||||
static ALvoid ReverbState_Destruct(ReverbState *State)
|
||||
{
|
||||
al_free(State->SampleBuffer);
|
||||
State->SampleBuffer = NULL;
|
||||
@@ -475,7 +492,7 @@ static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const
|
||||
* for all lines given the sample rate (frequency). If an allocation failure
|
||||
* occurs, it returns AL_FALSE.
|
||||
*/
|
||||
static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
|
||||
static ALboolean AllocLines(const ALuint frequency, ReverbState *State)
|
||||
{
|
||||
ALuint totalSamples, i;
|
||||
ALfloat multiplier, length;
|
||||
@@ -550,10 +567,11 @@ static ALboolean AllocLines(const ALuint frequency, ALreverbState *State)
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
|
||||
static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat multiplier;
|
||||
ALsizei i, j;
|
||||
|
||||
/* Allocate the delay lines. */
|
||||
if(!AllocLines(frequency, State))
|
||||
@@ -566,6 +584,54 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev
|
||||
EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
|
||||
frequency);
|
||||
|
||||
/* Clear filters and gain coefficients since the delay lines were all just
|
||||
* cleared (if not reallocated).
|
||||
*/
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
BiquadFilter_clear(&State->Filter[i].Lp);
|
||||
BiquadFilter_clear(&State->Filter[i].Hp);
|
||||
}
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
State->EarlyDelayCoeff[i][0] = 0.0f;
|
||||
State->EarlyDelayCoeff[i][1] = 0.0f;
|
||||
}
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
State->Early.Coeff[i][0] = 0.0f;
|
||||
State->Early.Coeff[i][1] = 0.0f;
|
||||
}
|
||||
|
||||
State->Late.DensityGain[0] = 0.0f;
|
||||
State->Late.DensityGain[1] = 0.0f;
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
State->Late.T60[i].MidGain[0] = 0.0f;
|
||||
State->Late.T60[i].MidGain[1] = 0.0f;
|
||||
BiquadFilter_clear(&State->Late.T60[i].HFFilter);
|
||||
BiquadFilter_clear(&State->Late.T60[i].LFFilter);
|
||||
}
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
{
|
||||
State->Early.CurrentGain[i][j] = 0.0f;
|
||||
State->Early.PanGain[i][j] = 0.0f;
|
||||
State->Late.CurrentGain[i][j] = 0.0f;
|
||||
State->Late.PanGain[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset counters and offset base. */
|
||||
State->FadeCount = 0;
|
||||
State->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
|
||||
State->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
|
||||
State->Offset = 0;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
@@ -667,7 +733,7 @@ static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime
|
||||
}
|
||||
|
||||
/* Update the offsets for the main effect delay line. */
|
||||
static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State)
|
||||
static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ReverbState *State)
|
||||
{
|
||||
ALfloat multiplier, length;
|
||||
ALuint i;
|
||||
@@ -802,7 +868,6 @@ static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, co
|
||||
*/
|
||||
static aluMatrixf GetTransformFromVector(const ALfloat *vec)
|
||||
{
|
||||
const ALfloat sqrt_3 = 1.732050808f;
|
||||
aluMatrixf focus;
|
||||
ALfloat norm[3];
|
||||
ALfloat mag;
|
||||
@@ -817,9 +882,9 @@ static aluMatrixf GetTransformFromVector(const ALfloat *vec)
|
||||
mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
|
||||
if(mag > 1.0f)
|
||||
{
|
||||
norm[0] = vec[0] / mag * -sqrt_3;
|
||||
norm[1] = vec[1] / mag * sqrt_3;
|
||||
norm[2] = vec[2] / mag * sqrt_3;
|
||||
norm[0] = vec[0] / mag * -SQRTF_3;
|
||||
norm[1] = vec[1] / mag * SQRTF_3;
|
||||
norm[2] = vec[2] / mag * SQRTF_3;
|
||||
mag = 1.0f;
|
||||
}
|
||||
else
|
||||
@@ -828,9 +893,9 @@ static aluMatrixf GetTransformFromVector(const ALfloat *vec)
|
||||
* term. There's no need to renormalize the magnitude since it would
|
||||
* just be reapplied in the matrix.
|
||||
*/
|
||||
norm[0] = vec[0] * -sqrt_3;
|
||||
norm[1] = vec[1] * sqrt_3;
|
||||
norm[2] = vec[2] * sqrt_3;
|
||||
norm[0] = vec[0] * -SQRTF_3;
|
||||
norm[1] = vec[1] * SQRTF_3;
|
||||
norm[2] = vec[2] * SQRTF_3;
|
||||
}
|
||||
|
||||
aluMatrixfSet(&focus,
|
||||
@@ -844,7 +909,7 @@ static aluMatrixf GetTransformFromVector(const ALfloat *vec)
|
||||
}
|
||||
|
||||
/* Update the early and late 3D panning gains. */
|
||||
static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State)
|
||||
static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
|
||||
{
|
||||
aluMatrixf transform, rot;
|
||||
ALsizei i;
|
||||
@@ -869,19 +934,19 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection
|
||||
MATRIX_MULT(transform, rot, A2B);
|
||||
memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], earlyGain,
|
||||
State->Early.PanGain[i]);
|
||||
ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
|
||||
State->Early.PanGain[i]);
|
||||
|
||||
rot = GetTransformFromVector(LateReverbPan);
|
||||
MATRIX_MULT(transform, rot, A2B);
|
||||
memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], lateGain,
|
||||
State->Late.PanGain[i]);
|
||||
ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
|
||||
State->Late.PanGain[i]);
|
||||
#undef MATRIX_MULT
|
||||
}
|
||||
|
||||
static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static void ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *Device = Context->Device;
|
||||
const ALlistener *Listener = Context->Listener;
|
||||
@@ -949,32 +1014,35 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Conte
|
||||
State);
|
||||
|
||||
/* Calculate the max update size from the smallest relevant delay. */
|
||||
State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES, State->Late.Offset[0][1]);
|
||||
State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
|
||||
mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
|
||||
);
|
||||
|
||||
/* Determine if delay-line cross-fading is required. TODO: Add some fuzz
|
||||
* for the float comparisons? The math should be stable enough that the
|
||||
* result should be the same if nothing's changed, and changes in the float
|
||||
* values should (though may not always) be matched by changes in delay
|
||||
* offsets.
|
||||
/* Determine if delay-line cross-fading is required. Density is essentially
|
||||
* a master control for the feedback delays, so changes the offsets of many
|
||||
* delay lines.
|
||||
*/
|
||||
if(State->Late.DensityGain[1] != State->Late.DensityGain[0])
|
||||
if(State->Params.Density != props->Reverb.Density ||
|
||||
/* Diffusion and decay times influences the decay rate (gain) of the
|
||||
* late reverb T60 filter.
|
||||
*/
|
||||
State->Params.Diffusion != props->Reverb.Diffusion ||
|
||||
State->Params.DecayTime != props->Reverb.DecayTime ||
|
||||
State->Params.HFDecayTime != hfDecayTime ||
|
||||
State->Params.LFDecayTime != lfDecayTime ||
|
||||
/* HF/LF References control the weighting used to calculate the density
|
||||
* gain.
|
||||
*/
|
||||
State->Params.HFReference != props->Reverb.HFReference ||
|
||||
State->Params.LFReference != props->Reverb.LFReference)
|
||||
State->FadeCount = 0;
|
||||
else for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] ||
|
||||
State->EarlyDelayCoeff[i][1] != State->EarlyDelayCoeff[i][0] ||
|
||||
State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] ||
|
||||
State->Early.Offset[i][1] != State->Early.Offset[i][0] ||
|
||||
State->Early.Coeff[i][1] != State->Early.Coeff[i][0] ||
|
||||
State->LateDelayTap[i][1] != State->LateDelayTap[i][0] ||
|
||||
State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] ||
|
||||
State->Late.Offset[i][1] != State->Late.Offset[i][0] ||
|
||||
State->Late.T60[i].MidGain[1] != State->Late.T60[i].MidGain[0])
|
||||
{
|
||||
State->FadeCount = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
State->Params.Density = props->Reverb.Density;
|
||||
State->Params.Diffusion = props->Reverb.Diffusion;
|
||||
State->Params.DecayTime = props->Reverb.DecayTime;
|
||||
State->Params.HFDecayTime = hfDecayTime;
|
||||
State->Params.LFDecayTime = lfDecayTime;
|
||||
State->Params.HFReference = props->Reverb.HFReference;
|
||||
State->Params.LFReference = props->Reverb.LFReference;
|
||||
}
|
||||
|
||||
|
||||
@@ -1000,22 +1068,14 @@ static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei o
|
||||
}
|
||||
|
||||
|
||||
static inline ALvoid DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
|
||||
const ALfloat *restrict in, ALsizei count)
|
||||
static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
|
||||
const ALfloat *restrict in, ALsizei count)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < count;i++)
|
||||
Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
|
||||
}
|
||||
|
||||
static inline ALvoid DelayLineIn4Rev(const DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES])
|
||||
{
|
||||
ALsizei i;
|
||||
offset &= Delay->Mask;
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
Delay->Line[offset][i] = in[NUM_LINES-1-i];
|
||||
}
|
||||
|
||||
/* Applies a scattering matrix to the 4-line (vector) input. This is used
|
||||
* for both the below vector all-pass model and to perform modal feed-back
|
||||
* delay network (FDN) mixing.
|
||||
@@ -1065,17 +1125,24 @@ static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *re
|
||||
#define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
|
||||
VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
|
||||
|
||||
/* Same as above, but reverses the input. */
|
||||
static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in,
|
||||
const ALfloat xCoeff, const ALfloat yCoeff)
|
||||
/* Utilizes the above, but reverses the input channels. */
|
||||
static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
|
||||
const ALfloat xCoeff, const ALfloat yCoeff,
|
||||
const ALfloat (*restrict in)[MAX_UPDATE_SAMPLES],
|
||||
const ALsizei count)
|
||||
{
|
||||
out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] + in[2] );
|
||||
out[1] = xCoeff*in[2] + yCoeff*(in[0] + in[1] + -in[3]);
|
||||
out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]);
|
||||
out[3] = xCoeff*in[0] + yCoeff*( -in[1] + -in[2] + -in[3]);
|
||||
const DelayLineI delay = *Delay;
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < count;++i)
|
||||
{
|
||||
ALfloat f[NUM_LINES];
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
f[NUM_LINES-1-j] = in[j][i];
|
||||
|
||||
VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
|
||||
}
|
||||
}
|
||||
#define VectorScatterRevDelayIn(delay, o, in, xcoeff, ycoeff) \
|
||||
VectorPartialScatterRev((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
|
||||
|
||||
/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
|
||||
* filter to the 4-line input.
|
||||
@@ -1128,6 +1195,7 @@ static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES],
|
||||
|
||||
ASSUME(todo > 0);
|
||||
|
||||
fade *= 1.0f/FADE_SAMPLES;
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
{
|
||||
vap_offset[j][0] = offset-Vap->Offset[j][0];
|
||||
@@ -1174,14 +1242,12 @@ static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES],
|
||||
* Two static specializations are used for transitional (cross-faded) delay
|
||||
* line processing and non-transitional processing.
|
||||
*/
|
||||
static void EarlyReflection_Unfaded(ALreverbState *State, ALsizei offset, const ALsizei todo,
|
||||
static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
|
||||
ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
|
||||
{
|
||||
ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
|
||||
const DelayLineI early_delay = State->Early.Delay;
|
||||
const DelayLineI main_delay = State->Delay;
|
||||
ALsizei early_feedb_tap[NUM_LINES];
|
||||
ALfloat early_feedb_coeff[NUM_LINES];
|
||||
const ALfloat mixX = State->MixX;
|
||||
const ALfloat mixY = State->MixY;
|
||||
ALsizei late_feed_tap;
|
||||
@@ -1205,51 +1271,37 @@ static void EarlyReflection_Unfaded(ALreverbState *State, ALsizei offset, const
|
||||
*/
|
||||
VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
|
||||
|
||||
/* Apply a delay and bounce to generate secondary reflections, combine with
|
||||
* the primary reflections and write out the result for mixing.
|
||||
*/
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
{
|
||||
early_feedb_tap[j] = offset - State->Early.Offset[j][0];
|
||||
early_feedb_coeff[j] = State->Early.Coeff[j][0];
|
||||
ALint early_feedb_tap = offset - State->Early.Offset[j][0];
|
||||
ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
|
||||
temps[j][i];
|
||||
}
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
|
||||
|
||||
/* Also write the result back to the main delay line for the late reverb
|
||||
* stage to pick up at the appropriate time, appplying a scatter and
|
||||
* bounce to improve the initial diffusion in the late reverb.
|
||||
*/
|
||||
late_feed_tap = offset - State->LateFeedTap;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat f[NUM_LINES];
|
||||
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
f[j] = temps[j][i];
|
||||
|
||||
/* Apply a delay and bounce to generate secondary reflections, combine
|
||||
* with the primary reflections and write out the result for mixing.
|
||||
*/
|
||||
DelayLineIn4Rev(&early_delay, offset, f);
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
{
|
||||
f[j] += DelayLineOut(&early_delay, early_feedb_tap[j]++, j) * early_feedb_coeff[j];
|
||||
out[j][i] = f[j];
|
||||
}
|
||||
|
||||
/* Also write the result back to the main delay line for the late
|
||||
* reverb stage to pick up at the appropriate time, appplying a scatter
|
||||
* and bounce to improve the initial diffusion in the late reverb.
|
||||
*/
|
||||
VectorScatterRevDelayIn(&main_delay, late_feed_tap++, f, mixX, mixY);
|
||||
offset++;
|
||||
}
|
||||
VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
|
||||
}
|
||||
static void EarlyReflection_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
|
||||
static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
|
||||
const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
|
||||
{
|
||||
ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
|
||||
const DelayLineI early_delay = State->Early.Delay;
|
||||
const DelayLineI main_delay = State->Delay;
|
||||
ALsizei feedb_tap[NUM_LINES][2];
|
||||
ALfloat feedb_oldCoeff[NUM_LINES];
|
||||
ALfloat feedb_oldCoeffStep[NUM_LINES];
|
||||
ALfloat feedb_newCoeffStep[NUM_LINES];
|
||||
const ALfloat mixX = State->MixX;
|
||||
const ALfloat mixY = State->MixY;
|
||||
ALsizei late_feed_tap;
|
||||
ALfloat fadeCount;
|
||||
ALsizei i, j;
|
||||
|
||||
ASSUME(todo > 0);
|
||||
@@ -1261,8 +1313,8 @@ static void EarlyReflection_Faded(ALreverbState *State, ALsizei offset, const AL
|
||||
ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
|
||||
ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
|
||||
ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
|
||||
ALfloat fadeCount = fade;
|
||||
|
||||
fadeCount = fade * FADE_SAMPLES;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
|
||||
@@ -1278,36 +1330,28 @@ static void EarlyReflection_Faded(ALreverbState *State, ALsizei offset, const AL
|
||||
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
{
|
||||
feedb_tap[j][0] = offset - State->Early.Offset[j][0];
|
||||
feedb_tap[j][1] = offset - State->Early.Offset[j][1];
|
||||
feedb_oldCoeff[j] = State->Early.Coeff[j][0];
|
||||
feedb_oldCoeffStep[j] = -feedb_oldCoeff[j] / FADE_SAMPLES;
|
||||
feedb_newCoeffStep[j] = State->Early.Coeff[j][1] / FADE_SAMPLES;
|
||||
}
|
||||
late_feed_tap = offset - State->LateFeedTap;
|
||||
fadeCount = fade * FADE_SAMPLES;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat f[NUM_LINES];
|
||||
ALint feedb_tap0 = offset - State->Early.Offset[j][0];
|
||||
ALint feedb_tap1 = offset - State->Early.Offset[j][1];
|
||||
ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
|
||||
ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
|
||||
ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
|
||||
ALfloat fadeCount = fade;
|
||||
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
f[j] = temps[j][i];
|
||||
|
||||
DelayLineIn4Rev(&early_delay, offset, f);
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
const ALfloat fade0 = feedb_oldCoeff[j] + feedb_oldCoeffStep[j]*fadeCount;
|
||||
const ALfloat fade1 = feedb_newCoeffStep[j]*fadeCount;
|
||||
f[j] += FadedDelayLineOut(&early_delay,
|
||||
feedb_tap[j][0]++, feedb_tap[j][1]++, j, fade0, fade1
|
||||
);
|
||||
out[j][i] = f[j];
|
||||
const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
|
||||
const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
|
||||
out[j][i] = FadedDelayLineOut(&early_delay,
|
||||
feedb_tap0++, feedb_tap1++, j, fade0, fade1
|
||||
) + temps[j][i];
|
||||
fadeCount += 1.0f;
|
||||
}
|
||||
fadeCount += 1.0f;
|
||||
|
||||
VectorScatterRevDelayIn(&main_delay, late_feed_tap++, f, mixX, mixY);
|
||||
offset++;
|
||||
}
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
|
||||
|
||||
late_feed_tap = offset - State->LateFeedTap;
|
||||
VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
|
||||
}
|
||||
|
||||
/* Applies the two T60 damping filter sections. */
|
||||
@@ -1332,7 +1376,7 @@ static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo,
|
||||
* Two variations are made, one for for transitional (cross-faded) delay line
|
||||
* processing and one for non-transitional processing.
|
||||
*/
|
||||
static void LateReverb_Unfaded(ALreverbState *State, ALsizei offset, const ALsizei todo,
|
||||
static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
|
||||
ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
|
||||
{
|
||||
ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
|
||||
@@ -1367,20 +1411,10 @@ static void LateReverb_Unfaded(ALreverbState *State, ALsizei offset, const ALsiz
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
memcpy(out[j], temps[j], todo*sizeof(ALfloat));
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat f[NUM_LINES];
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
f[j] = temps[j][i];
|
||||
|
||||
/* Finally, scatter and bounce the results to refeed the feedback
|
||||
* buffer.
|
||||
*/
|
||||
VectorScatterRevDelayIn(&late_delay, offset, f, mixX, mixY);
|
||||
offset++;
|
||||
}
|
||||
/* Finally, scatter and bounce the results to refeed the feedback buffer. */
|
||||
VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
|
||||
}
|
||||
static void LateReverb_Faded(ALreverbState *State, ALsizei offset, const ALsizei todo,
|
||||
static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
|
||||
const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
|
||||
{
|
||||
ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
|
||||
@@ -1406,7 +1440,8 @@ static void LateReverb_Faded(ALreverbState *State, ALsizei offset, const ALsizei
|
||||
ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
|
||||
ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
|
||||
ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
|
||||
ALfloat fadeCount = fade * FADE_SAMPLES;
|
||||
ALfloat fadeCount = fade;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
|
||||
@@ -1428,18 +1463,10 @@ static void LateReverb_Faded(ALreverbState *State, ALsizei offset, const ALsizei
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
memcpy(out[j], temps[j], todo*sizeof(ALfloat));
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat f[NUM_LINES];
|
||||
for(j = 0;j < NUM_LINES;j++)
|
||||
f[j] = temps[j][i];
|
||||
|
||||
VectorScatterRevDelayIn(&late_delay, offset, f, mixX, mixY);
|
||||
offset++;
|
||||
}
|
||||
VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
|
||||
}
|
||||
|
||||
static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
|
||||
ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
|
||||
@@ -1458,6 +1485,11 @@ static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, c
|
||||
todo = mini(todo, State->MaxUpdate[0]);
|
||||
}
|
||||
todo = mini(todo, State->MaxUpdate[1]);
|
||||
/* If this is not the final update, ensure the update size is a
|
||||
* multiple of 4 for the SIMD mixers.
|
||||
*/
|
||||
if(todo < SamplesToDo-base)
|
||||
todo &= ~3;
|
||||
|
||||
/* Convert B-Format to A-Format for processing. */
|
||||
memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
|
||||
@@ -1479,7 +1511,7 @@ static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, c
|
||||
|
||||
if(UNLIKELY(fadeCount < FADE_SAMPLES))
|
||||
{
|
||||
ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES;
|
||||
ALfloat fade = (ALfloat)fadeCount;
|
||||
|
||||
/* Generate early reflections. */
|
||||
EarlyReflection_Faded(State, offset, todo, fade, samples);
|
||||
@@ -1557,9 +1589,9 @@ typedef struct ReverbStateFactory {
|
||||
|
||||
static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
|
||||
{
|
||||
ALreverbState *state;
|
||||
ReverbState *state;
|
||||
|
||||
NEW_OBJ0(state, ALreverbState)();
|
||||
NEW_OBJ0(state, ReverbState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
|
||||
+26
-3
@@ -102,7 +102,9 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef __vita__
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32_IE)
|
||||
@@ -218,6 +220,9 @@ void FillCPUCaps(int capfilter)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
#ifdef __vita__
|
||||
caps |= CPU_CAP_NEON;
|
||||
#else
|
||||
FILE *file = fopen("/proc/cpuinfo", "rt");
|
||||
if(!file)
|
||||
ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
|
||||
@@ -262,6 +267,7 @@ void FillCPUCaps(int capfilter)
|
||||
|
||||
alstr_reset(&features);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TRACE("Extensions:%s%s%s%s%s%s\n",
|
||||
@@ -680,6 +686,11 @@ void UnmapFileMem(const struct FileMapping *mapping)
|
||||
|
||||
void GetProcBinary(al_string *path, al_string *fname)
|
||||
{
|
||||
#ifdef __vita__
|
||||
if(path) alstr_copy_cstr(path, "app0:/");
|
||||
if(fname) alstr_copy_cstr(fname, "eboot.bin");
|
||||
|
||||
#else
|
||||
char *pathname = NULL;
|
||||
size_t pathlen;
|
||||
|
||||
@@ -770,7 +781,7 @@ void GetProcBinary(al_string *path, al_string *fname)
|
||||
if(fname) alstr_copy_cstr(fname, pathname);
|
||||
}
|
||||
free(pathname);
|
||||
|
||||
#endif
|
||||
if(path && fname)
|
||||
TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
|
||||
else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
|
||||
@@ -871,6 +882,10 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir)
|
||||
while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1)
|
||||
althrd_yield();
|
||||
|
||||
#ifdef __vita__
|
||||
DirectorySearch(subdir, ext, &results);
|
||||
#else
|
||||
|
||||
if(subdir[0] == '/')
|
||||
DirectorySearch(subdir, ext, &results);
|
||||
else
|
||||
@@ -953,7 +968,7 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir)
|
||||
|
||||
alstr_reset(&path);
|
||||
}
|
||||
|
||||
#endif
|
||||
ATOMIC_STORE_SEQ(&search_lock, 0);
|
||||
|
||||
return results;
|
||||
@@ -980,6 +995,10 @@ struct FileMapping MapFileToMem(const char *fname)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __vita__
|
||||
ptr = malloc(sbuf.st_size);
|
||||
read(fd, ptr, sbuf.st_size);
|
||||
#else
|
||||
ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if(ptr == MAP_FAILED)
|
||||
{
|
||||
@@ -987,7 +1006,7 @@ struct FileMapping MapFileToMem(const char *fname)
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
ret.fd = fd;
|
||||
ret.ptr = ptr;
|
||||
ret.len = sbuf.st_size;
|
||||
@@ -996,7 +1015,11 @@ struct FileMapping MapFileToMem(const char *fname)
|
||||
|
||||
void UnmapFileMem(const struct FileMapping *mapping)
|
||||
{
|
||||
#ifdef __vita__
|
||||
free(mapping->ptr);
|
||||
#else
|
||||
munmap(mapping->ptr, mapping->len);
|
||||
#endif
|
||||
close(mapping->fd);
|
||||
}
|
||||
|
||||
|
||||
+466
-168
@@ -5,228 +5,526 @@
|
||||
#include "mastering.h"
|
||||
#include "alu.h"
|
||||
#include "almalloc.h"
|
||||
#include "static_assert.h"
|
||||
|
||||
|
||||
extern inline ALuint GetCompressorSampleRate(const Compressor *Comp);
|
||||
/* These structures assume BUFFERSIZE is a power of 2. */
|
||||
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
|
||||
|
||||
#define RMS_WINDOW_SIZE (1<<7)
|
||||
#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
|
||||
#define RMS_VALUE_MAX (1<<24)
|
||||
typedef struct SlidingHold {
|
||||
ALfloat Values[BUFFERSIZE];
|
||||
ALsizei Expiries[BUFFERSIZE];
|
||||
ALsizei LowerIndex;
|
||||
ALsizei UpperIndex;
|
||||
ALsizei Length;
|
||||
} SlidingHold;
|
||||
|
||||
static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
|
||||
|
||||
|
||||
/* Multichannel compression is linked via one of two modes:
|
||||
/* General topology and basic automation was based on the following paper:
|
||||
*
|
||||
* Summed - Absolute sum of all channels.
|
||||
* Maxed - Absolute maximum of any channel.
|
||||
*/
|
||||
static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] += OutBuffer[c][i];
|
||||
}
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
|
||||
}
|
||||
|
||||
static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Envelope detection/sensing can be done via:
|
||||
* D. Giannoulis, M. Massberg and J. D. Reiss,
|
||||
* "Parameter Automation in a Dynamic Range Compressor,"
|
||||
* Journal of the Audio Engineering Society, v61 (10), Oct. 2013
|
||||
*
|
||||
* RMS - Rectangular windowed root mean square of linking stage.
|
||||
* Peak - Implicit output from linking stage.
|
||||
* Available (along with supplemental reading) at:
|
||||
*
|
||||
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
|
||||
*/
|
||||
static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
ALuint sum = Comp->RmsSum;
|
||||
ALuint *window = Comp->RmsWindow;
|
||||
ALsizei index = Comp->RmsIndex;
|
||||
ALsizei i;
|
||||
typedef struct Compressor {
|
||||
ALsizei NumChans;
|
||||
ALuint SampleRate;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = Comp->Envelope[i];
|
||||
struct {
|
||||
ALuint Knee : 1;
|
||||
ALuint Attack : 1;
|
||||
ALuint Release : 1;
|
||||
ALuint PostGain : 1;
|
||||
ALuint Declip : 1;
|
||||
} Auto;
|
||||
|
||||
sum -= window[index];
|
||||
window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
|
||||
sum += window[index];
|
||||
index = (index + 1) & RMS_WINDOW_MASK;
|
||||
ALsizei LookAhead;
|
||||
|
||||
Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
|
||||
}
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
|
||||
Comp->RmsSum = sum;
|
||||
Comp->RmsIndex = index;
|
||||
}
|
||||
ALfloat Threshold;
|
||||
ALfloat Slope;
|
||||
ALfloat Knee;
|
||||
|
||||
/* This isn't a very sophisticated envelope follower, but it gets the job
|
||||
* done. First, it operates at logarithmic scales to keep transitions
|
||||
* appropriate for human hearing. Second, it can apply adaptive (automated)
|
||||
* attack/release adjustments based on the signal.
|
||||
ALfloat Attack;
|
||||
ALfloat Release;
|
||||
|
||||
alignas(16) ALfloat SideChain[2*BUFFERSIZE];
|
||||
alignas(16) ALfloat CrestFactor[BUFFERSIZE];
|
||||
|
||||
SlidingHold *Hold;
|
||||
ALfloat (*Delay)[BUFFERSIZE];
|
||||
ALsizei DelayIndex;
|
||||
|
||||
ALfloat CrestCoeff;
|
||||
ALfloat GainEstimate;
|
||||
ALfloat AdaptCoeff;
|
||||
|
||||
ALfloat LastPeakSq;
|
||||
ALfloat LastRmsSq;
|
||||
ALfloat LastRelease;
|
||||
ALfloat LastAttack;
|
||||
ALfloat LastGainDev;
|
||||
} Compressor;
|
||||
|
||||
|
||||
/* This sliding hold follows the input level with an instant attack and a
|
||||
* fixed duration hold before an instant release to the next highest level.
|
||||
* It is a sliding window maximum (descending maxima) implementation based on
|
||||
* Richard Harter's ascending minima algorithm available at:
|
||||
*
|
||||
* http://www.richardhartersworld.com/cri/2001/slidingmin.html
|
||||
*/
|
||||
static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
{
|
||||
ALfloat attackMin = Comp->AttackMin;
|
||||
ALfloat attackMax = Comp->AttackMax;
|
||||
ALfloat releaseMin = Comp->ReleaseMin;
|
||||
ALfloat releaseMax = Comp->ReleaseMax;
|
||||
ALfloat last = Comp->EnvLast;
|
||||
ALsizei i;
|
||||
const ALsizei mask = BUFFERSIZE - 1;
|
||||
const ALsizei length = Hold->Length;
|
||||
ALfloat *restrict values = Hold->Values;
|
||||
ALsizei *restrict expiries = Hold->Expiries;
|
||||
ALsizei lowerIndex = Hold->LowerIndex;
|
||||
ALsizei upperIndex = Hold->UpperIndex;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
if(i >= expiries[upperIndex])
|
||||
upperIndex = (upperIndex + 1) & mask;
|
||||
|
||||
if(in >= values[upperIndex])
|
||||
{
|
||||
ALfloat env = log10f(maxf(Comp->Envelope[i], 0.000001f));
|
||||
ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
|
||||
|
||||
if(env > last)
|
||||
last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
|
||||
else
|
||||
last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
|
||||
|
||||
Comp->Envelope[i] = last;
|
||||
}
|
||||
|
||||
Comp->EnvLast = last;
|
||||
}
|
||||
|
||||
/* The envelope is converted to control gain with an optional soft knee. */
|
||||
static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
|
||||
{
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat knee = Comp->Knee;
|
||||
ALsizei i;
|
||||
|
||||
if(!(knee > 0.0f))
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
|
||||
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
|
||||
}
|
||||
values[upperIndex] = in;
|
||||
expiries[upperIndex] = i + length;
|
||||
lowerIndex = upperIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ALfloat lower = threshold - (0.5f * knee);
|
||||
const ALfloat upper = threshold + (0.5f * knee);
|
||||
const ALfloat m = 0.5f * Slope / knee;
|
||||
do {
|
||||
do {
|
||||
if(!(in >= values[lowerIndex]))
|
||||
goto found_place;
|
||||
} while(lowerIndex--);
|
||||
lowerIndex = mask;
|
||||
} while(1);
|
||||
found_place:
|
||||
|
||||
lowerIndex = (lowerIndex + 1) & mask;
|
||||
values[lowerIndex] = in;
|
||||
expiries[lowerIndex] = i + length;
|
||||
}
|
||||
|
||||
Hold->LowerIndex = lowerIndex;
|
||||
Hold->UpperIndex = upperIndex;
|
||||
|
||||
return values[upperIndex];
|
||||
}
|
||||
|
||||
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
|
||||
{
|
||||
const ALsizei lowerIndex = Hold->LowerIndex;
|
||||
ALsizei *restrict expiries = Hold->Expiries;
|
||||
ALsizei i = Hold->UpperIndex;
|
||||
|
||||
if(lowerIndex < i)
|
||||
{
|
||||
for(;i < BUFFERSIZE;i++)
|
||||
expiries[i] -= n;
|
||||
i = 0;
|
||||
}
|
||||
for(;i < lowerIndex;i++)
|
||||
expiries[i] -= n;
|
||||
|
||||
expiries[i] -= n;
|
||||
}
|
||||
|
||||
/* Multichannel compression is linked via the absolute maximum of all
|
||||
* channels.
|
||||
*/
|
||||
static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
sideChain[index + i] = 0.0f;
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
ALsizei offset = index;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat env = Comp->Envelope[i];
|
||||
ALfloat gain;
|
||||
|
||||
if(env > lower && env < upper)
|
||||
gain = m * (env - lower) * (lower - env);
|
||||
else
|
||||
gain = Slope * (threshold - env);
|
||||
|
||||
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
|
||||
sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This calculates the squared crest factor of the control signal for the
|
||||
* basic automation of the attack/release times. As suggested by the paper,
|
||||
* it uses an instantaneous squared peak detector and a squared RMS detector
|
||||
* both with 200ms release times.
|
||||
*/
|
||||
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALfloat a_crest = Comp->CrestCoeff;
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat y2_peak = Comp->LastPeakSq;
|
||||
ALfloat y2_rms = Comp->LastRmsSq;
|
||||
ALsizei i;
|
||||
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing,
|
||||
const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
|
||||
const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb,
|
||||
const ALfloat KneeDb, const ALuint SampleRate)
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat x_abs = sideChain[index + i];
|
||||
ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
|
||||
|
||||
y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
|
||||
y2_rms = lerp(x2, y2_rms, a_crest);
|
||||
crestFactor[i] = y2_peak / y2_rms;
|
||||
}
|
||||
|
||||
Comp->LastPeakSq = y2_peak;
|
||||
Comp->LastRmsSq = y2_rms;
|
||||
}
|
||||
|
||||
/* The side-chain starts with a simple peak detector (based on the absolute
|
||||
* value of the incoming signal) and performs most of its operations in the
|
||||
* log domain.
|
||||
*/
|
||||
static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALuint offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
|
||||
sideChain[offset] = logf(maxf(0.000001f, x_abs));
|
||||
}
|
||||
}
|
||||
|
||||
/* An optional hold can be used to extend the peak detector so it can more
|
||||
* solidly detect fast transients. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
SlidingHold *hold = Comp->Hold;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALsizei offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
const ALfloat x_G = logf(maxf(0.000001f, x_abs));
|
||||
|
||||
sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
|
||||
}
|
||||
|
||||
ShiftSlidingHold(hold, SamplesToDo);
|
||||
}
|
||||
|
||||
/* This is the heart of the feed-forward compressor. It operates in the log
|
||||
* domain (to better match human hearing) and can apply some basic automation
|
||||
* to knee width, attack/release times, make-up/post gain, and clipping
|
||||
* reduction.
|
||||
*/
|
||||
static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const bool autoKnee = Comp->Auto.Knee;
|
||||
const bool autoAttack = Comp->Auto.Attack;
|
||||
const bool autoRelease = Comp->Auto.Release;
|
||||
const bool autoPostGain = Comp->Auto.PostGain;
|
||||
const bool autoDeclip = Comp->Auto.Declip;
|
||||
const ALsizei lookAhead = Comp->LookAhead;
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat slope = Comp->Slope;
|
||||
const ALfloat attack = Comp->Attack;
|
||||
const ALfloat release = Comp->Release;
|
||||
const ALfloat c_est = Comp->GainEstimate;
|
||||
const ALfloat a_adp = Comp->AdaptCoeff;
|
||||
const ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat postGain = Comp->PostGain;
|
||||
ALfloat knee = Comp->Knee;
|
||||
ALfloat t_att = attack;
|
||||
ALfloat t_rel = release - attack;
|
||||
ALfloat a_att = expf(-1.0f / t_att);
|
||||
ALfloat a_rel = expf(-1.0f / t_rel);
|
||||
ALfloat y_1 = Comp->LastRelease;
|
||||
ALfloat y_L = Comp->LastAttack;
|
||||
ALfloat c_dev = Comp->LastGainDev;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALfloat y2_crest = crestFactor[i];
|
||||
const ALfloat x_G = sideChain[lookAhead + i];
|
||||
const ALfloat x_over = x_G - threshold;
|
||||
ALfloat knee_h;
|
||||
ALfloat y_G;
|
||||
ALfloat x_L;
|
||||
|
||||
if(autoKnee)
|
||||
knee = maxf(0.0f, 2.5f * (c_dev + c_est));
|
||||
knee_h = 0.5f * knee;
|
||||
|
||||
/* This is the gain computer. It applies a static compression curve
|
||||
* to the control signal.
|
||||
*/
|
||||
if(x_over <= -knee_h)
|
||||
y_G = 0.0f;
|
||||
else if(fabsf(x_over) < knee_h)
|
||||
y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
|
||||
else
|
||||
y_G = x_over;
|
||||
|
||||
x_L = -slope * y_G;
|
||||
|
||||
if(autoAttack)
|
||||
{
|
||||
t_att = 2.0f * attack / y2_crest;
|
||||
a_att = expf(-1.0f / t_att);
|
||||
}
|
||||
|
||||
if(autoRelease)
|
||||
{
|
||||
t_rel = 2.0f * release / y2_crest - t_att;
|
||||
a_rel = expf(-1.0f / t_rel);
|
||||
}
|
||||
|
||||
/* Gain smoothing (ballistics) is done via a smooth decoupled peak
|
||||
* detector. The attack time is subtracted from the release time
|
||||
* above to compensate for the chained operating mode.
|
||||
*/
|
||||
y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
|
||||
y_L = lerp(y_1, y_L, a_att);
|
||||
|
||||
/* Knee width and make-up gain automation make use of a smoothed
|
||||
* measurement of deviation between the control signal and estimate.
|
||||
* The estimate is also used to bias the measurement to hot-start its
|
||||
* average.
|
||||
*/
|
||||
c_dev = lerp(-y_L - c_est, c_dev, a_adp);
|
||||
|
||||
if(autoPostGain)
|
||||
{
|
||||
/* Clipping reduction is only viable when make-up gain is being
|
||||
* automated. It modifies the deviation to further attenuate the
|
||||
* control signal when clipping is detected. The adaptation
|
||||
* time is sufficiently long enough to suppress further clipping
|
||||
* at the same output level.
|
||||
*/
|
||||
if(autoDeclip)
|
||||
c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
|
||||
|
||||
postGain = -(c_dev + c_est);
|
||||
}
|
||||
|
||||
sideChain[i] = expf(postGain - y_L);
|
||||
}
|
||||
|
||||
Comp->LastRelease = y_1;
|
||||
Comp->LastAttack = y_L;
|
||||
Comp->LastGainDev = c_dev;
|
||||
}
|
||||
|
||||
/* Combined with the hold time, a look-ahead delay can improve handling of
|
||||
* fast transients by allowing the envelope time to converge prior to
|
||||
* reaching the offending impulse. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei mask = BUFFERSIZE - 1;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALsizei indexIn = Comp->DelayIndex;
|
||||
const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
|
||||
ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = OutBuffer[c][i];
|
||||
|
||||
OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
|
||||
delay[c][(indexIn + i) & mask] = sig;
|
||||
}
|
||||
}
|
||||
|
||||
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
|
||||
}
|
||||
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* NumChans - Number of channels to process.
|
||||
* SampleRate - Sample rate to process.
|
||||
* AutoKnee - Whether to automate the knee width parameter.
|
||||
* AutoAttack - Whether to automate the attack time parameter.
|
||||
* AutoRelease - Whether to automate the release time parameter.
|
||||
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
|
||||
* AutoDeclip - Whether to automate clipping reduction. Ignored when
|
||||
* not automating make-up gain.
|
||||
* LookAheadTime - Look-ahead time (in seconds).
|
||||
* HoldTime - Peak hold-time (in seconds).
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Make-up gain applied after compression (in dB).
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* Ratio - Compression ratio (x:1). Set to INFINITY for true
|
||||
* limiting. Ignored when automating knee width.
|
||||
* KneeDb - Knee width (in dB). Ignored when automating knee
|
||||
* width.
|
||||
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
|
||||
* automating attack time.
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
const ALfloat HoldTime, const ALfloat PreGainDb,
|
||||
const ALfloat PostGainDb, const ALfloat ThresholdDb,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime)
|
||||
{
|
||||
Compressor *Comp;
|
||||
ALsizei lookAhead;
|
||||
ALsizei hold;
|
||||
size_t size;
|
||||
ALsizei i;
|
||||
|
||||
lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
/* The sliding hold implementation doesn't handle a length of 1. A 1-sample
|
||||
* hold is useless anyway, it would only ever give back what was just given
|
||||
* to it.
|
||||
*/
|
||||
if(hold == 1)
|
||||
hold = 0;
|
||||
|
||||
size = sizeof(*Comp);
|
||||
if(RmsSensing)
|
||||
size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
size += sizeof(*Comp->Delay) * NumChans;
|
||||
if(hold > 0)
|
||||
size += sizeof(*Comp->Hold);
|
||||
}
|
||||
|
||||
Comp = al_calloc(16, size);
|
||||
|
||||
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
|
||||
Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
|
||||
Comp->SummedLink = SummedLink;
|
||||
Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->Ratio = Ratio;
|
||||
Comp->Threshold = ThresholdDb / 20.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
|
||||
Comp->NumChans = NumChans;
|
||||
Comp->SampleRate = SampleRate;
|
||||
Comp->Auto.Knee = AutoKnee;
|
||||
Comp->Auto.Attack = AutoAttack;
|
||||
Comp->Auto.Release = AutoRelease;
|
||||
Comp->Auto.PostGain = AutoPostGain;
|
||||
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
|
||||
Comp->LookAhead = lookAhead;
|
||||
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
|
||||
Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
|
||||
Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
|
||||
Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
|
||||
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
|
||||
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
|
||||
|
||||
Comp->RmsSum = 0;
|
||||
if(RmsSensing)
|
||||
Comp->RmsWindow = (ALuint*)(Comp+1);
|
||||
else
|
||||
Comp->RmsWindow = NULL;
|
||||
Comp->RmsIndex = 0;
|
||||
/* Knee width automation actually treats the compressor as a limiter. By
|
||||
* varying the knee width, it can effectively be seen as applying
|
||||
* compression over a wide range of ratios.
|
||||
*/
|
||||
if(AutoKnee)
|
||||
Comp->Slope = -1.0f;
|
||||
|
||||
for(i = 0;i < BUFFERSIZE;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
Comp->EnvLast = -6.0f;
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
if(hold > 0)
|
||||
{
|
||||
Comp->Hold = (SlidingHold*)(Comp + 1);
|
||||
Comp->Hold->Values[0] = -INFINITY;
|
||||
Comp->Hold->Expiries[0] = hold;
|
||||
Comp->Hold->Length = hold;
|
||||
Comp->Delay = (ALfloat(*)[])(Comp->Hold + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Comp->Delay = (ALfloat(*)[])(Comp + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
|
||||
Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
|
||||
Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
|
||||
|
||||
return Comp;
|
||||
}
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALfloat preGain = Comp->PreGain;
|
||||
ALfloat *restrict sideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
if(Comp->PreGain != 1.0f)
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
if(preGain != 1.0f)
|
||||
{
|
||||
for(c = 0;c < NumChans;c++)
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= Comp->PreGain;
|
||||
OutBuffer[c][i] *= preGain;
|
||||
}
|
||||
}
|
||||
|
||||
if(Comp->SummedLink)
|
||||
SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
LinkChannels(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->Auto.Attack || Comp->Auto.Release)
|
||||
CrestDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Hold)
|
||||
PeakHoldDetector(Comp, SamplesToDo);
|
||||
else
|
||||
MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
PeakDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->RmsWindow)
|
||||
RmsDetection(Comp, SamplesToDo);
|
||||
FollowEnvelope(Comp, SamplesToDo);
|
||||
GainCompressor(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Ratio > 0.0f)
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
|
||||
else
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f);
|
||||
if(Comp->Delay)
|
||||
SignalDelay(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->PostGain != 1.0f)
|
||||
sideChain = Comp->SideChain;
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] *= Comp->PostGain;
|
||||
}
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= Comp->Envelope[i];
|
||||
OutBuffer[c][i] *= sideChain[i];
|
||||
}
|
||||
|
||||
memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp)
|
||||
{ return Comp->LookAhead; }
|
||||
|
||||
+32
-40
@@ -6,52 +6,44 @@
|
||||
/* For BUFFERSIZE. */
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct Compressor {
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
ALboolean SummedLink;
|
||||
ALfloat AttackMin;
|
||||
ALfloat AttackMax;
|
||||
ALfloat ReleaseMin;
|
||||
ALfloat ReleaseMax;
|
||||
ALfloat Ratio;
|
||||
ALfloat Threshold;
|
||||
ALfloat Knee;
|
||||
ALuint SampleRate;
|
||||
struct Compressor;
|
||||
|
||||
ALuint RmsSum;
|
||||
ALuint *RmsWindow;
|
||||
ALsizei RmsIndex;
|
||||
ALfloat Envelope[BUFFERSIZE];
|
||||
ALfloat EnvLast;
|
||||
} Compressor;
|
||||
|
||||
/* The compressor requires the following information for proper
|
||||
* initialization:
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Gain applied after compression (in dB).
|
||||
* SummedLink - Whether to use summed (true) or maxed (false) linking.
|
||||
* RmsSensing - Whether to use RMS (true) or Peak (false) sensing.
|
||||
* AttackTimeMin - Minimum attack time (in seconds).
|
||||
* AttackTimeMax - Maximum attack time. Automates when min != max.
|
||||
* ReleaseTimeMin - Minimum release time (in seconds).
|
||||
* ReleaseTimeMax - Maximum release time. Automates when min != max.
|
||||
* Ratio - Compression ratio (x:1). Set to 0 for true limiter.
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* KneeDb - Knee width (below threshold; in dB).
|
||||
* NumChans - Number of channels to process.
|
||||
* SampleRate - Sample rate to process.
|
||||
* AutoKnee - Whether to automate the knee width parameter.
|
||||
* AutoAttack - Whether to automate the attack time parameter.
|
||||
* AutoRelease - Whether to automate the release time parameter.
|
||||
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
|
||||
* AutoDeclip - Whether to automate clipping reduction. Ignored when
|
||||
* not automating make-up gain.
|
||||
* LookAheadTime - Look-ahead time (in seconds).
|
||||
* HoldTime - Peak hold-time (in seconds).
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Make-up gain applied after compression (in dB).
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* Ratio - Compression ratio (x:1). Set to INFINIFTY for true
|
||||
* limiting. Ignored when automating knee width.
|
||||
* KneeDb - Knee width (in dB). Ignored when automating knee
|
||||
* width.
|
||||
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
|
||||
* automating attack time.
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin,
|
||||
const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb,
|
||||
const ALuint SampleRate);
|
||||
struct Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
const ALfloat HoldTime, const ALfloat PreGainDb,
|
||||
const ALfloat PostGainDb, const ALfloat ThresholdDb,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime);
|
||||
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
inline ALuint GetCompressorSampleRate(const Compressor *Comp)
|
||||
{ return Comp->SampleRate; }
|
||||
ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
|
||||
|
||||
#endif /* MASTERING_H */
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* SSE resamplers */
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
|
||||
+41
-51
@@ -9,12 +9,37 @@
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat do_lerp(const ALfloat *restrict vals, ALsizei frac)
|
||||
static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat do_cubic(const ALfloat *restrict vals, ALsizei frac)
|
||||
static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat do_bsinc(const InterpState *state, const ALfloat *restrict vals, ALsizei frac)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
ALsizei j_f, pi;
|
||||
ALfloat pf, r;
|
||||
|
||||
ASSUME(state->bsinc.m > 0);
|
||||
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(state->bsinc.filter + state->bsinc.m*pi*4, 16);
|
||||
scd = ASSUME_ALIGNED(fil + state->bsinc.m, 16);
|
||||
phd = ASSUME_ALIGNED(scd + state->bsinc.m, 16);
|
||||
spd = ASSUME_ALIGNED(phd + state->bsinc.m, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
for(j_f = 0;j_f < state->bsinc.m;j_f++)
|
||||
r += (fil[j_f] + state->bsinc.sf*scd[j_f] + pf*(phd[j_f] + state->bsinc.sf*spd[j_f])) * vals[j_f];
|
||||
return r;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
|
||||
@@ -30,16 +55,19 @@ const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Tag, Sampler, O) \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState *state, \
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment, \
|
||||
ALfloat *restrict dst, ALsizei numsamples) \
|
||||
{ \
|
||||
const InterpState istate = *state; \
|
||||
ALsizei i; \
|
||||
\
|
||||
ASSUME(numsamples > 0); \
|
||||
\
|
||||
src -= O; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(src, frac); \
|
||||
dst[i] = Sampler(&istate, src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
@@ -51,49 +79,10 @@ const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
DECL_TEMPLATE(point, do_point, 0)
|
||||
DECL_TEMPLATE(lerp, do_lerp, 0)
|
||||
DECL_TEMPLATE(cubic, do_cubic, 1)
|
||||
DECL_TEMPLATE(bsinc, do_bsinc, istate.bsinc.l)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
const ALfloat *const filter = state->bsinc.filter;
|
||||
const ALfloat sf = state->bsinc.sf;
|
||||
const ALsizei m = state->bsinc.m;
|
||||
ALsizei j_f, pi, i;
|
||||
ALfloat pf, r;
|
||||
|
||||
ASSUME(m > 0);
|
||||
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(filter + m*pi*4, 16);
|
||||
scd = ASSUME_ALIGNED(fil + m, 16);
|
||||
phd = ASSUME_ALIGNED(scd + m, 16);
|
||||
spd = ASSUME_ALIGNED(phd + m, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
for(j_f = 0;j_f < m;j_f++)
|
||||
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f];
|
||||
dst[i] = r;
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
@@ -119,21 +108,22 @@ void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
@@ -169,7 +159,7 @@ void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
|
||||
const int32x4_t increment4 = vdupq_n_s32(increment*4);
|
||||
const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
|
||||
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
|
||||
alignas(16) ALint pos_[4];
|
||||
alignas(16) ALsizei frac_[4];
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
int32x4_t pos4, frac4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
@@ -82,7 +81,7 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src += state->bsinc.l;
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
@@ -180,11 +179,12 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat step = (TargetGains[c] - gain) * delta;
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
@@ -261,7 +261,7 @@ void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restr
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src += state->bsinc.l;
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
@@ -149,11 +149,12 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat step = (TargetGains[c] - gain) * delta;
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
@@ -227,7 +228,7 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restri
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
ALint pos_[4];
|
||||
ALsizei frac_[4];
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
ALint pos_[4];
|
||||
ALsizei frac_[4];
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
|
||||
+19
-18
@@ -45,7 +45,7 @@
|
||||
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
||||
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
|
||||
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size);
|
||||
|
||||
|
||||
/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
|
||||
@@ -197,12 +197,11 @@ void aluInitMixer(void)
|
||||
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
|
||||
ALuint objid, ALuint param, const char *msg)
|
||||
{
|
||||
AsyncEvent evt;
|
||||
evt.EnumType = enumtype;
|
||||
evt.Type = type;
|
||||
evt.ObjectId = objid;
|
||||
evt.Param = param;
|
||||
strcpy(evt.Message, msg);
|
||||
AsyncEvent evt = ASYNC_EVENT(enumtype);
|
||||
evt.u.user.type = type;
|
||||
evt.u.user.id = objid;
|
||||
evt.u.user.param = param;
|
||||
strcpy(evt.u.user.msg, msg);
|
||||
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
||||
alsem_post(&context->EventSem);
|
||||
}
|
||||
@@ -487,6 +486,7 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
while(tmpiter && SrcBufferSize > FilledAmt)
|
||||
{
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < tmpiter->num_buffers;i++)
|
||||
@@ -499,23 +499,24 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
|
||||
DataSize = minu(SizeToDo, DataSize - pos);
|
||||
DataSize = mini(SizeToDo, DataSize - pos);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
}
|
||||
}
|
||||
if(pos > tmpiter->max_samples)
|
||||
if(UNLIKELY(!CompLen))
|
||||
pos -= tmpiter->max_samples;
|
||||
else
|
||||
{
|
||||
FilledAmt += tmpiter->max_samples - pos;
|
||||
FilledAmt += CompLen;
|
||||
if(SrcBufferSize <= FilledAmt)
|
||||
break;
|
||||
pos = 0;
|
||||
}
|
||||
if(SrcBufferSize > FilledAmt)
|
||||
{
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,8 +730,10 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
if(BufferListItem->max_samples > DataPosInt)
|
||||
break;
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
|
||||
buffers_done += BufferListItem->num_buffers;
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
|
||||
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
|
||||
{
|
||||
isplaying = false;
|
||||
@@ -738,8 +741,6 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
}
|
||||
} while(isplaying && OutPos < SamplesToDo);
|
||||
|
||||
|
||||
+9
-35
@@ -41,8 +41,7 @@
|
||||
extern inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
extern inline float ScaleAzimuthFront(float azimuth, float scale);
|
||||
extern inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
extern inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
extern inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
|
||||
static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
|
||||
@@ -75,9 +74,9 @@ void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALf
|
||||
/* Zeroth-order */
|
||||
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
||||
/* First-order */
|
||||
coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
|
||||
coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
|
||||
coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
|
||||
coeffs[1] = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */
|
||||
coeffs[2] = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */
|
||||
coeffs[3] = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */
|
||||
/* Second-order */
|
||||
coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
|
||||
coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
|
||||
@@ -152,7 +151,7 @@ void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALf
|
||||
}
|
||||
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
@@ -167,7 +166,7 @@ void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, AL
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
@@ -177,31 +176,6 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, con
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
float gain = 0.0f;
|
||||
for(j = 0;j < 4;j++)
|
||||
gain += chancoeffs[i][j] * mtx[j];
|
||||
gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
|
||||
}
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain;
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static inline const char *GetLabelFromChannel(enum Channel channel)
|
||||
{
|
||||
@@ -421,9 +395,9 @@ static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei orde
|
||||
TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
|
||||
|
||||
for(i = 0;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
device->NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
for(;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
device->NumChannelsPerOrder[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,7 +942,7 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
device->Dry.CoeffCount = 0;
|
||||
device->Dry.NumChannels = 0;
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
device->NumChannelsPerOrder[i] = 0;
|
||||
|
||||
device->AvgSpeakerDist = 0.0f;
|
||||
memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));
|
||||
|
||||
+37
-10
@@ -1,6 +1,6 @@
|
||||
# CMake build file list for OpenAL
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
|
||||
|
||||
PROJECT(OpenAL)
|
||||
|
||||
@@ -61,6 +61,13 @@ if(DEFINED LIB_SUFFIX)
|
||||
message(WARNING "LIB_SUFFIX is deprecated. Use the variables provided by the GNUInstallDirs module instead")
|
||||
endif()
|
||||
|
||||
if(VITA)
|
||||
SET(LIBTYPE STATIC)
|
||||
SET(ALSOFT_DLOPEN OFF)
|
||||
SET(ALSOFT_UTILS OFF)
|
||||
SET(ALSOFT_TESTS OFF)
|
||||
SET(ALSOFT_EXAMPLES OFF)
|
||||
endif()
|
||||
|
||||
SET(CPP_DEFS ) # C pre-process, not C++
|
||||
SET(INC_PATHS )
|
||||
@@ -103,7 +110,7 @@ ENDIF()
|
||||
|
||||
SET(LIB_MAJOR_VERSION "1")
|
||||
SET(LIB_MINOR_VERSION "19")
|
||||
SET(LIB_REVISION "0")
|
||||
SET(LIB_REVISION "1")
|
||||
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
|
||||
|
||||
SET(EXPORT_DECL "")
|
||||
@@ -114,13 +121,16 @@ CHECK_TYPE_SIZE("long" SIZEOF_LONG)
|
||||
CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG)
|
||||
|
||||
|
||||
CHECK_C_COMPILER_FLAG(-std=c11 HAVE_STD_C11)
|
||||
# GNU dialects, not the strict ISO ones: -std=c11 defines __STRICT_ANSI__, which
|
||||
# makes newlib hide the POSIX functions used all over the code (strdup,
|
||||
# strcasecmp, strncasecmp, strtok_r, nanosleep).
|
||||
CHECK_C_COMPILER_FLAG(-std=gnu11 HAVE_STD_C11)
|
||||
IF(HAVE_STD_C11)
|
||||
SET(CMAKE_C_FLAGS "-std=c11 ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_C_FLAGS "-std=gnu11 ${CMAKE_C_FLAGS}")
|
||||
ELSE()
|
||||
CHECK_C_COMPILER_FLAG(-std=c99 HAVE_STD_C99)
|
||||
CHECK_C_COMPILER_FLAG(-std=gnu99 HAVE_STD_C99)
|
||||
IF(HAVE_STD_C99)
|
||||
SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
@@ -632,9 +642,11 @@ IF(NOT HAVE_WINDOWS_H)
|
||||
MESSAGE(FATAL_ERROR "No timing function found!")
|
||||
ENDIF()
|
||||
|
||||
CHECK_SYMBOL_EXISTS(nanosleep time.h HAVE_NANOSLEEP)
|
||||
IF(NOT HAVE_NANOSLEEP)
|
||||
MESSAGE(FATAL_ERROR "No sleep function found!")
|
||||
IF(NOT VITA)
|
||||
CHECK_SYMBOL_EXISTS(nanosleep time.h HAVE_NANOSLEEP)
|
||||
IF(NOT HAVE_NANOSLEEP)
|
||||
MESSAGE(FATAL_ERROR "No sleep function found!")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# We need pthreads outside of Windows
|
||||
@@ -1249,6 +1261,20 @@ IF(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
|
||||
MESSAGE(FATAL_ERROR "Failed to enabled required SDL2 backend")
|
||||
ENDIF()
|
||||
|
||||
# Check for VITA backend
|
||||
IF(VITA)
|
||||
OPTION(ALSOFT_BACKEND_VITA "Enable VITA backend" ON)
|
||||
IF(ALSOFT_BACKEND_VITA)
|
||||
SET(HAVE_VITA 1)
|
||||
SET(ALC_OBJS ${ALC_OBJS} Alc/backends/vita.c)
|
||||
SET(BACKENDS "${BACKENDS} VITA,")
|
||||
add_definitions("-Dmemcpy=sceClibMemcpy")
|
||||
add_definitions("-Dmemset=sceClibMemset")
|
||||
add_definitions("-Dmemmove=sceClibMemmove")
|
||||
add_definitions("-Dmemcmp=sceClibMemcmp")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# Optionally enable the Wave Writer backend
|
||||
OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON)
|
||||
IF(ALSOFT_BACKEND_WAVE)
|
||||
@@ -1746,7 +1772,8 @@ IF(ALSOFT_EXAMPLES)
|
||||
PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
|
||||
TARGET_COMPILE_OPTIONS(alffplay PRIVATE ${C_FLAGS})
|
||||
TARGET_LINK_LIBRARIES(alffplay
|
||||
PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} common OpenAL)
|
||||
PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common common
|
||||
OpenAL)
|
||||
|
||||
IF(ALSOFT_INSTALL)
|
||||
INSTALL(TARGETS alffplay
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
openal-soft-1.19.1:
|
||||
|
||||
Implemented capture support for the SoundIO backend.
|
||||
|
||||
Fixed source buffer queues potentially not playing properly when a queue
|
||||
entry completes.
|
||||
|
||||
Fixed possible unexpected failures when generating auxiliary effect slots.
|
||||
|
||||
Fixed a crash with certain reverb or device settings.
|
||||
|
||||
Fixed OpenSL capture.
|
||||
|
||||
Improved output limiter response, better ensuring the sample amplitude is
|
||||
clamped for output.
|
||||
|
||||
openal-soft-1.19.0:
|
||||
|
||||
Implemented the ALC_SOFT_device_clock extension.
|
||||
|
||||
@@ -145,8 +145,8 @@ typedef struct ALeffectslot {
|
||||
* * Channel 3 is OpenAL -Z * sqrt(3)
|
||||
* Consequently, effects that only want to work with mono input can use
|
||||
* channel 0 by itself. Effects that want multichannel can process the
|
||||
* ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
|
||||
* first-order device output (FOAOut).
|
||||
* ambisonics signal and make a B-Format source pan for first-order device
|
||||
* output (FOAOut).
|
||||
*/
|
||||
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALeffectslot;
|
||||
|
||||
+29
-22
@@ -582,7 +582,7 @@ typedef struct DistanceComp {
|
||||
*/
|
||||
#define BUFFERSIZE 2048
|
||||
|
||||
typedef struct DryMixParams {
|
||||
typedef struct MixParams {
|
||||
AmbiConfig Ambi;
|
||||
/* Number of coefficients in each Ambi.Coeffs to mix together (4 for first-
|
||||
* order, 9 for second-order, etc). If the count is 0, Ambi.Map is used
|
||||
@@ -592,17 +592,7 @@ typedef struct DryMixParams {
|
||||
|
||||
ALfloat (*Buffer)[BUFFERSIZE];
|
||||
ALsizei NumChannels;
|
||||
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
|
||||
} DryMixParams;
|
||||
|
||||
typedef struct BFMixParams {
|
||||
AmbiConfig Ambi;
|
||||
/* Will only be 4 or 0. */
|
||||
ALsizei CoeffCount;
|
||||
|
||||
ALfloat (*Buffer)[BUFFERSIZE];
|
||||
ALsizei NumChannels;
|
||||
} BFMixParams;
|
||||
} MixParams;
|
||||
|
||||
typedef struct RealMixParams {
|
||||
enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
|
||||
@@ -632,6 +622,8 @@ struct ALCdevice_struct {
|
||||
enum AmbiLayout AmbiLayout;
|
||||
enum AmbiNorm AmbiScale;
|
||||
|
||||
ALCenum LimiterState;
|
||||
|
||||
al_string DeviceName;
|
||||
|
||||
ATOMIC(ALCenum) LastError;
|
||||
@@ -686,15 +678,17 @@ struct ALCdevice_struct {
|
||||
|
||||
ALuint64 ClockBase;
|
||||
ALuint SamplesDone;
|
||||
ALuint FixedLatency;
|
||||
|
||||
/* Temp storage used for mixer processing. */
|
||||
alignas(16) ALfloat TempBuffer[4][BUFFERSIZE];
|
||||
|
||||
/* The "dry" path corresponds to the main output. */
|
||||
DryMixParams Dry;
|
||||
MixParams Dry;
|
||||
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
|
||||
|
||||
/* First-order ambisonics output, to be upsampled to the dry buffer if different. */
|
||||
BFMixParams FOAOut;
|
||||
MixParams FOAOut;
|
||||
|
||||
/* "Real" output, which will be written to the device buffer. May alias the
|
||||
* dry buffer.
|
||||
@@ -759,21 +753,35 @@ struct ALCdevice_struct {
|
||||
|
||||
|
||||
enum {
|
||||
/* End event thread processing. */
|
||||
EventType_KillThread = 0,
|
||||
|
||||
/* User event types. */
|
||||
EventType_SourceStateChange = 1<<0,
|
||||
EventType_BufferCompleted = 1<<1,
|
||||
EventType_Error = 1<<2,
|
||||
EventType_Performance = 1<<3,
|
||||
EventType_Deprecated = 1<<4,
|
||||
EventType_Disconnected = 1<<5,
|
||||
|
||||
/* Internal events. */
|
||||
EventType_ReleaseEffectState = 65536,
|
||||
};
|
||||
|
||||
typedef struct AsyncEvent {
|
||||
unsigned int EnumType;
|
||||
ALenum Type;
|
||||
ALuint ObjectId;
|
||||
ALuint Param;
|
||||
ALchar Message[1008];
|
||||
union {
|
||||
char dummy;
|
||||
struct {
|
||||
ALenum type;
|
||||
ALuint id;
|
||||
ALuint param;
|
||||
ALchar msg[1008];
|
||||
} user;
|
||||
struct ALeffectState *EffectState;
|
||||
} u;
|
||||
} AsyncEvent;
|
||||
#define ASYNC_EVENT(t) { t, { 0 } }
|
||||
|
||||
struct ALCcontext_struct {
|
||||
RefCount ref;
|
||||
@@ -826,7 +834,6 @@ struct ALCcontext_struct {
|
||||
|
||||
ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
|
||||
|
||||
almtx_t EventThrdLock;
|
||||
althrd_t EventThread;
|
||||
alsem_t EventSem;
|
||||
struct ll_ringbuffer *AsyncEvents;
|
||||
@@ -856,9 +863,6 @@ void ALCcontext_ProcessUpdates(ALCcontext *context);
|
||||
|
||||
void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends);
|
||||
|
||||
void AppendAllDevicesList(const ALCchar *name);
|
||||
void AppendCaptureDeviceList(const ALCchar *name);
|
||||
|
||||
|
||||
extern ALint RTPrioLevel;
|
||||
void SetRTPriority(void);
|
||||
@@ -904,6 +908,9 @@ inline void UnlockEffectSlotList(ALCcontext *context)
|
||||
{ almtx_unlock(&context->EffectSlotLock); }
|
||||
|
||||
|
||||
int EventThread(void *arg);
|
||||
|
||||
|
||||
vector_al_string SearchDataFiles(const char *match, const char *subdir);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+9
-23
@@ -74,7 +74,7 @@ extern enum Resampler ResamplerDefault;
|
||||
typedef struct BsincState {
|
||||
ALfloat sf; /* Scale interpolation factor. */
|
||||
ALsizei m; /* Coefficient count. */
|
||||
ALint l; /* Left coefficient offset. */
|
||||
ALsizei l; /* Left coefficient offset. */
|
||||
/* Filter coefficients, followed by the scale, phase, and scale-phase
|
||||
* delta coefficients. Starting at phase index 0, each subsequent phase
|
||||
* index follows contiguously.
|
||||
@@ -491,15 +491,18 @@ inline float ScaleAzimuthFront(float azimuth, float scale)
|
||||
}
|
||||
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
/**
|
||||
* ComputeDryPanGains
|
||||
* ComputePanGains
|
||||
*
|
||||
* Computes panning gains using the given channel decoder coefficients and the
|
||||
* pre-calculated direction or angle coefficients.
|
||||
* pre-calculated direction or angle coefficients. For B-Format sources, the
|
||||
* coeffs are a 'slice' of a transform matrix for the input channel, used to
|
||||
* scale and orient the sound samples.
|
||||
*/
|
||||
inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(dry->CoeffCount > 0)
|
||||
ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount,
|
||||
@@ -508,23 +511,6 @@ inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX
|
||||
ComputePanningGainsBF(dry->Ambi.Map, dry->NumChannels, coeffs, ingain, gains);
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
/**
|
||||
* ComputeFirstOrderGains
|
||||
*
|
||||
* Sets channel gains for a first-order ambisonics input channel. The matrix is
|
||||
* a 1x4 'slice' of a transform matrix for the input channel, used to scale and
|
||||
* orient the sound samples.
|
||||
*/
|
||||
inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(foa->CoeffCount > 0)
|
||||
ComputeFirstOrderGainsMC(foa->Ambi.Coeffs, foa->NumChannels, mtx, ingain, gains);
|
||||
else
|
||||
ComputeFirstOrderGainsBF(foa->Ambi.Map, foa->NumChannels, mtx, ingain, gains);
|
||||
}
|
||||
|
||||
|
||||
ALboolean MixSource(struct ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo);
|
||||
|
||||
|
||||
@@ -122,12 +122,6 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
|
||||
|
||||
LockEffectSlotList(context);
|
||||
device = context->Device;
|
||||
if(device->AuxiliaryEffectSlotMax - VECTOR_SIZE(context->EffectSlotList) < (ALuint)n)
|
||||
{
|
||||
UnlockEffectSlotList(context);
|
||||
SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, "Exceeding %u auxiliary effect slot limit",
|
||||
device->AuxiliaryEffectSlotMax);
|
||||
}
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
|
||||
@@ -142,6 +136,13 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
|
||||
}
|
||||
if(iter == end)
|
||||
{
|
||||
if(device->AuxiliaryEffectSlotMax == VECTOR_SIZE(context->EffectSlotList))
|
||||
{
|
||||
UnlockEffectSlotList(context);
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
|
||||
"Exceeding %u auxiliary effect slot limit", device->AuxiliaryEffectSlotMax);
|
||||
}
|
||||
VECTOR_PUSH_BACK(context->EffectSlotList, NULL);
|
||||
iter = &VECTOR_BACK(context->EffectSlotList);
|
||||
}
|
||||
@@ -752,6 +753,9 @@ void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context)
|
||||
/* If there was an unused update container, put it back in the
|
||||
* freelist.
|
||||
*/
|
||||
if(props->State)
|
||||
ALeffectState_DecRef(props->State);
|
||||
props->State = NULL;
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
|
||||
|
||||
+7
-8
@@ -229,17 +229,16 @@ static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
|
||||
/** Can only be called while the mixer is locked! */
|
||||
static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
|
||||
ALbitfieldSOFT enabledevt;
|
||||
AsyncEvent evt;
|
||||
|
||||
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(!(enabledevt&EventType_SourceStateChange)) return;
|
||||
|
||||
evt.EnumType = EventType_SourceStateChange;
|
||||
evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.ObjectId = id;
|
||||
evt.Param = state;
|
||||
snprintf(evt.Message, sizeof(evt.Message), "Source ID %u state changed to %s", id,
|
||||
evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.u.user.id = id;
|
||||
evt.u.user.param = state;
|
||||
snprintf(evt.u.user.msg, sizeof(evt.u.user.msg), "Source ID %u state changed to %s", id,
|
||||
(state==AL_INITIAL) ? "AL_INITIAL" :
|
||||
(state==AL_PLAYING) ? "AL_PLAYING" :
|
||||
(state==AL_PAUSED) ? "AL_PAUSED" :
|
||||
@@ -1296,7 +1295,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p
|
||||
*/
|
||||
values[0] = GetSourceSecOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
clocktime = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
|
||||
@@ -1560,7 +1559,7 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp
|
||||
*/
|
||||
values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
clocktime = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = clocktime.Latency;
|
||||
|
||||
+25
-38
@@ -6,19 +6,16 @@
|
||||
#include "AL/alext.h"
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
|
||||
static int EventThread(void *arg)
|
||||
int EventThread(void *arg)
|
||||
{
|
||||
ALCcontext *context = arg;
|
||||
bool quitnow = false;
|
||||
|
||||
/* Clear all pending posts on the semaphore. */
|
||||
while(alsem_trywait(&context->EventSem) == althrd_success)
|
||||
{
|
||||
}
|
||||
|
||||
while(1)
|
||||
while(!quitnow)
|
||||
{
|
||||
ALbitfieldSOFT enabledevts;
|
||||
AsyncEvent evt;
|
||||
@@ -28,14 +25,24 @@ static int EventThread(void *arg)
|
||||
alsem_wait(&context->EventSem);
|
||||
continue;
|
||||
}
|
||||
if(!evt.EnumType)
|
||||
break;
|
||||
|
||||
almtx_lock(&context->EventCbLock);
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
|
||||
context->EventCb(evt.Type, evt.ObjectId, evt.Param, (ALsizei)strlen(evt.Message),
|
||||
evt.Message, context->EventParam);
|
||||
do {
|
||||
quitnow = evt.EnumType == EventType_KillThread;
|
||||
if(quitnow) break;
|
||||
|
||||
if(evt.EnumType == EventType_ReleaseEffectState)
|
||||
{
|
||||
ALeffectState_DecRef(evt.u.EffectState);
|
||||
continue;
|
||||
}
|
||||
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
|
||||
context->EventCb(evt.u.user.type, evt.u.user.id, evt.u.user.param,
|
||||
(ALsizei)strlen(evt.u.user.msg), evt.u.user.msg, context->EventParam
|
||||
);
|
||||
} while(ll_ringbuffer_read(context->AsyncEvents, (char*)&evt, 1) != 0);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
return 0;
|
||||
@@ -46,7 +53,6 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
ALCcontext *context;
|
||||
ALbitfieldSOFT enabledevts;
|
||||
ALbitfieldSOFT flags = 0;
|
||||
bool isrunning;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
@@ -74,13 +80,9 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid event type 0x%04x", types[i]);
|
||||
}
|
||||
|
||||
almtx_lock(&context->EventThrdLock);
|
||||
if(enable)
|
||||
{
|
||||
if(!context->AsyncEvents)
|
||||
context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
|
||||
isrunning = !!enabledevts;
|
||||
while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts|flags,
|
||||
almemory_order_acq_rel, almemory_order_acquire) == 0)
|
||||
{
|
||||
@@ -88,35 +90,20 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
* just try again.
|
||||
*/
|
||||
}
|
||||
if(!isrunning && flags)
|
||||
althrd_create(&context->EventThread, EventThread, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
|
||||
isrunning = !!enabledevts;
|
||||
while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts&~flags,
|
||||
almemory_order_acq_rel, almemory_order_acquire) == 0)
|
||||
{
|
||||
}
|
||||
if(isrunning && !(enabledevts&~flags))
|
||||
{
|
||||
static const AsyncEvent kill_evt = { 0 };
|
||||
while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
|
||||
althrd_yield();
|
||||
alsem_post(&context->EventSem);
|
||||
althrd_join(context->EventThread, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
almtx_lock(&context->EventCbLock);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
almtx_lock(&context->EventCbLock);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
almtx_unlock(&context->EventThrdLock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#define FLT_EPSILON (1.19209290e-07f)
|
||||
#endif
|
||||
|
||||
#define SQRT_2 1.41421356237309504880
|
||||
#define SQRT_3 1.73205080756887719318
|
||||
|
||||
#define SQRTF_2 1.41421356237309504880f
|
||||
#define SQRTF_3 1.73205080756887719318f
|
||||
|
||||
#ifndef HUGE_VALF
|
||||
static const union msvc_inf_hack {
|
||||
unsigned char b[4];
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#define AL_THREADS_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef __vita__
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
/* force_align_arg_pointer is required for proper function arguments aligning
|
||||
@@ -165,6 +168,14 @@ inline void althrd_yield(void)
|
||||
|
||||
inline int althrd_sleep(const struct timespec *ts, struct timespec *rem)
|
||||
{
|
||||
#ifdef __vita__
|
||||
(void)rem; // unused
|
||||
if(sceKernelDelayThread(ts->tv_sec * 1000000 + ts->tv_nsec / 1000) != 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
int ret = nanosleep(ts, rem);
|
||||
if(ret != 0)
|
||||
{
|
||||
@@ -172,6 +183,7 @@ inline int althrd_sleep(const struct timespec *ts, struct timespec *rem)
|
||||
errno = 0;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
/* Define if we have the SDL2 backend */
|
||||
#cmakedefine HAVE_SDL2
|
||||
|
||||
/* Define if we have the PS Vita backend */
|
||||
#cmakedefine HAVE_VITA
|
||||
|
||||
/* Define if we have the stat function */
|
||||
#cmakedefine HAVE_STAT
|
||||
|
||||
|
||||
+56
-45
@@ -39,7 +39,16 @@ extern "C" {
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "common/alhelpers.h"
|
||||
|
||||
extern "C" {
|
||||
/* Undefine this to disable use of experimental extensions. Don't use for
|
||||
* production code! Interfaces and behavior may change prior to being
|
||||
* finalized.
|
||||
*/
|
||||
#define ALLOW_EXPERIMENTAL_EXTS
|
||||
|
||||
#ifdef ALLOW_EXPERIMENTAL_EXTS
|
||||
#ifndef AL_SOFT_map_buffer
|
||||
#define AL_SOFT_map_buffer 1
|
||||
typedef unsigned int ALbitfieldSOFT;
|
||||
@@ -71,6 +80,7 @@ typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void
|
||||
typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
|
||||
typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
|
||||
#endif
|
||||
#endif /* ALLOW_EXPERIMENTAL_EXTS */
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -92,12 +102,16 @@ bool EnableWideStereo = false;
|
||||
LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
|
||||
LPALCGETINTEGER64VSOFT alcGetInteger64vSOFT;
|
||||
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
LPALBUFFERSTORAGESOFT alBufferStorageSOFT;
|
||||
LPALMAPBUFFERSOFT alMapBufferSOFT;
|
||||
LPALUNMAPBUFFERSOFT alUnmapBufferSOFT;
|
||||
#endif
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
LPALEVENTCONTROLSOFT alEventControlSOFT;
|
||||
LPALEVENTCALLBACKSOFT alEventCallbackSOFT;
|
||||
#endif
|
||||
|
||||
const seconds AVNoSyncThreshold(10);
|
||||
|
||||
@@ -263,9 +277,11 @@ struct AudioState {
|
||||
av_freep(&mSamples);
|
||||
}
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
static void AL_APIENTRY EventCallback(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar *message,
|
||||
void *userParam);
|
||||
#endif
|
||||
|
||||
nanoseconds getClockNoLock();
|
||||
nanoseconds getClock()
|
||||
@@ -688,6 +704,7 @@ bool AudioState::readAudio(uint8_t *samples, int length)
|
||||
}
|
||||
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar *message,
|
||||
void *userParam)
|
||||
@@ -731,24 +748,27 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
|
||||
self->mSrcCond.notify_one();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int AudioState::handler()
|
||||
{
|
||||
const std::array<ALenum,6> types{{
|
||||
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
|
||||
AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
|
||||
AL_EVENT_TYPE_DISCONNECTED_SOFT
|
||||
}};
|
||||
std::unique_lock<std::mutex> lock(mSrcMutex);
|
||||
milliseconds sleep_time = AudioBufferTime / 3;
|
||||
ALenum fmt;
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
const std::array<ALenum,6> evt_types{{
|
||||
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
|
||||
AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
|
||||
AL_EVENT_TYPE_DISCONNECTED_SOFT
|
||||
}};
|
||||
if(alEventControlSOFT)
|
||||
{
|
||||
alEventControlSOFT(types.size(), types.data(), AL_TRUE);
|
||||
alEventControlSOFT(evt_types.size(), evt_types.data(), AL_TRUE);
|
||||
alEventCallbackSOFT(EventCallback, this);
|
||||
sleep_time = AudioBufferTotalTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Find a suitable format for OpenAL. */
|
||||
mDstChanLayout = 0;
|
||||
@@ -899,9 +919,8 @@ int AudioState::handler()
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
goto finish;
|
||||
|
||||
if(!alBufferStorageSOFT)
|
||||
samples = av_malloc(buffer_len);
|
||||
else
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(alBufferStorageSOFT)
|
||||
{
|
||||
for(ALuint bufid : mBuffers)
|
||||
alBufferStorageSOFT(bufid, mFormat, nullptr, buffer_len, mCodecCtx->sample_rate,
|
||||
@@ -912,6 +931,9 @@ int AudioState::handler()
|
||||
samples = av_malloc(buffer_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
samples = av_malloc(buffer_len);
|
||||
|
||||
while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed) &&
|
||||
mConnected.test_and_set(std::memory_order_relaxed))
|
||||
@@ -934,15 +956,19 @@ int AudioState::handler()
|
||||
{
|
||||
ALuint bufid = mBuffers[mBufferIdx];
|
||||
|
||||
uint8_t *ptr = reinterpret_cast<uint8_t*>(
|
||||
samples ? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
|
||||
uint8_t *ptr = reinterpret_cast<uint8_t*>(samples
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
|
||||
#endif
|
||||
);
|
||||
if(!ptr) break;
|
||||
|
||||
/* Read the next chunk of data, filling the buffer, and queue it on
|
||||
* the source */
|
||||
bool got_audio = readAudio(ptr, buffer_len);
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(!samples) alUnmapBufferSOFT(bufid);
|
||||
#endif
|
||||
if(!got_audio) break;
|
||||
|
||||
if(samples)
|
||||
@@ -983,11 +1009,13 @@ int AudioState::handler()
|
||||
finish:
|
||||
av_freep(&samples);
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
if(alEventControlSOFT)
|
||||
{
|
||||
alEventControlSOFT(types.size(), types.data(), AL_FALSE);
|
||||
alEventControlSOFT(evt_types.size(), evt_types.data(), AL_FALSE);
|
||||
alEventCallbackSOFT(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1691,41 +1719,21 @@ int main(int argc, char *argv[])
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
/* Open an audio device */
|
||||
int fileidx = 1;
|
||||
ALCdevice *device = [argc,argv,&fileidx]() -> ALCdevice*
|
||||
{
|
||||
ALCdevice *dev = NULL;
|
||||
if(argc > 3 && strcmp(argv[1], "-device") == 0)
|
||||
{
|
||||
fileidx = 3;
|
||||
dev = alcOpenDevice(argv[2]);
|
||||
if(dev) return dev;
|
||||
std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
|
||||
}
|
||||
return alcOpenDevice(nullptr);
|
||||
}();
|
||||
ALCcontext *context = alcCreateContext(device, nullptr);
|
||||
if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
|
||||
++argv; --argc;
|
||||
if(InitAL(&argv, &argc))
|
||||
{
|
||||
std::cerr<< "Failed to set up audio device" <<std::endl;
|
||||
if(context)
|
||||
alcDestroyContext(context);
|
||||
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(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
|
||||
{
|
||||
std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
|
||||
alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
|
||||
alcGetProcAddress(device, "alcGetInteger64vSOFT")
|
||||
);
|
||||
{ auto device = alcGetContextsDevice(alcGetCurrentContext());
|
||||
if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
|
||||
{
|
||||
std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
|
||||
alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
|
||||
alcGetProcAddress(device, "alcGetInteger64vSOFT")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(alIsExtensionPresent("AL_SOFT_source_latency"))
|
||||
@@ -1735,6 +1743,7 @@ int main(int argc, char *argv[])
|
||||
alGetProcAddress("alGetSourcei64vSOFT")
|
||||
);
|
||||
}
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_map_buffer" <<std::endl;
|
||||
@@ -1745,6 +1754,8 @@ int main(int argc, char *argv[])
|
||||
alUnmapBufferSOFT = reinterpret_cast<LPALUNMAPBUFFERSOFT>(
|
||||
alGetProcAddress("alUnmapBufferSOFT"));
|
||||
}
|
||||
#endif
|
||||
#ifdef AL_SOFT_events
|
||||
if(alIsExtensionPresent("AL_SOFTX_events"))
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_events" <<std::endl;
|
||||
@@ -1753,7 +1764,9 @@ int main(int argc, char *argv[])
|
||||
alEventCallbackSOFT = reinterpret_cast<LPALEVENTCALLBACKSOFT>(
|
||||
alGetProcAddress("alEventCallbackSOFT"));
|
||||
}
|
||||
#endif
|
||||
|
||||
int fileidx = 0;
|
||||
for(;fileidx < argc;++fileidx)
|
||||
{
|
||||
if(strcmp(argv[fileidx], "-direct") == 0)
|
||||
@@ -1882,9 +1895,7 @@ int main(int argc, char *argv[])
|
||||
/* Nothing more to play. Shut everything down and quit. */
|
||||
movState = nullptr;
|
||||
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
alcCloseDevice(device);
|
||||
CloseAL();
|
||||
|
||||
SDL_DestroyRenderer(renderer);
|
||||
renderer = nullptr;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(native-tools)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user