Compare commits

...

73 Commits

Author SHA1 Message Date
Dima353 e5e43fed09 Fix compile on latest vitaSDK 2026-07-23 21:41:44 +03:00
Ivan Epifanov c851a68f09 Bump cmake version 2026-01-28 19:44:52 +03:00
Ivan Epifanov 5ac19a1aa1 Bump cmake version 2026-01-28 15:43:02 +03:00
Ivan Epifanov a3c6391bb4 Limit thread priority 2021-05-23 13:55:25 +03:00
Ivan Epifanov dfeead1931 Allow to change thread priority/affinity 2021-05-23 13:36:46 +03:00
Ivan Epifanov 5724481a72 Use native threads for vita playback 2021-02-28 15:28:00 +03:00
Ivan Epifanov b88e5de1ae Use native lightweight mutexes for locks 2021-02-28 14:39:45 +03:00
Ivan Epifanov bb59b318de Shut up warning 2021-02-28 14:39:23 +03:00
Ivan Epifanov 32f350c1b5 Typo 2021-02-28 14:03:42 +03:00
Ivan Epifanov 84143100e5 Align sample size 2021-02-26 14:55:25 +03:00
Ivan Epifanov 452a1bd4de Properly reset device 2021-01-28 15:18:17 +03:00
Ivan Epifanov 419cb0e847 Reset device to initial params 2021-01-27 19:32:41 +03:00
Ivan Epifanov 6a0314bfbc Enable vita backend and add vita-specific flags 2021-01-27 18:47:36 +03:00
Ivan Epifanov 6b630fd787 Add vita playback/capture backends, courtesy of @dwhinham with some fixes 2021-01-27 18:47:36 +03:00
Ivan Epifanov 26b92d58ec GCC10 fixes, courtesy of @fgsfdsfgs 2021-01-27 18:08:35 +03:00
Ivan Epifanov ddde29b2e9 Enable NEON, fix paths and mmap 2021-01-27 17:47:36 +03:00
Ivan Epifanov 5feca399c2 Nanosleep replacement 2021-01-27 17:46:52 +03:00
Ivan Epifanov e4a60406da Load config from vita-specific paths 2021-01-27 17:46:26 +03:00
Chris Robinson 6761218e51 Release 1.19.1 2018-10-11 15:05:31 -07:00
Chris Robinson 31b9c50721 Use the common init/close functions for alffplay 2018-10-10 16:16:00 -07:00
Chris Robinson f589244fb6 Allow building alffplay without experimental extensions 2018-10-08 15:03:49 -07:00
Chris Robinson 7a79f09a31 Add a comment about waiting to kill the event thread 2018-10-07 17:18:50 -07:00
Chris Robinson db65113c5f Use a 24-bit dither depth limit 2018-10-03 13:51:21 -07:00
Chris Robinson f3f94e478b Don't limit output for ALC_DONT_CARE_SOFT and float samples 2018-10-03 13:48:04 -07:00
Chris Robinson c39eeb9638 Don't try to get the JNIEnv on Android
It's currently not used. More stuff is needed anyway which may need a different
approach.
2018-10-02 12:40:26 -07:00
Chris Robinson 493c8bbc83 Add back an inadvertently removed static 2018-10-02 12:25:26 -07:00
Chris Robinson 052fdd67cd Use < instead of != for some loop checks 2018-10-01 15:59:05 -07:00
Chris Robinson ed8f44d102 Don't scale the reverb fade counter so much 2018-09-30 16:34:00 -07:00
Chris Robinson 1d1acc0c34 Ensure BUFFERSIZE is a power of 2 2018-09-30 12:12:27 -07:00
Chris Robinson 1860b2ec8a Make the Compressor struct opaque 2018-09-30 12:08:01 -07:00
Chris Robinson 7ad4f753a4 Fix some length ranges 2018-09-29 19:39:49 -07:00
Chris Robinson d5a78f0843 Improve a couple loops
Avoid masking the index with each iteration, and instead do up to when the mask
would apply. This allows for better optimizations, in particular fewer
instructions and better chances for vectorization.
2018-09-29 18:48:28 -07:00
Chris Robinson 32494e72a5 Don't use a ringbuffer design for the limiter's side chain
Rather than continuously wrapping when used, each update uses it from the front
and copies the tail to the front at the end. This allows for more effficient
accesses in loops.
2018-09-29 11:54:45 -07:00
Chris Robinson 0396c0ecd2 Add some assumes for the limiter 2018-09-29 09:47:05 -07:00
Chris Robinson 51a447852f Simplify a lower-bound clamp 2018-09-28 18:34:21 -07:00
Chris Robinson 1684b2cc5f Constify a couple more variables 2018-09-28 16:42:22 -07:00
Chris Robinson 1dee902a5f Update changelog 2018-09-26 08:23:48 -07:00
Chris Robinson 79314c4461 Include the limiter's lookAhead delay in the device latency 2018-09-25 23:05:27 -07:00
Chris Robinson 2d6309d6fc Don't hardcode the limiter threshold
It's now calculated from the output sample type and dither depth.
2018-09-25 10:33:20 -07:00
Chris Robinson c69338bc0d Update the output limiter/compressor
This provides better characteristics for an amplitude limiter. In particular,
it utilizes the peak amplitude instead of the RMS, and the used parameters
basically guarantee no output samples exceed the given threshold... almost, due
to floating-point errors as the threshold is converted from dB to log-e for the
envelope, then is negated and converted to linear amplitude to apply to the
signal. It's quite possible for some rounding errors to creep in and not
perfectly saturate the result.
2018-09-25 10:04:14 -07:00
Chris Robinson 39c3314d00 Only compare the reverb params that induce a need for fading
The offsets and coefficients are controlled by a relatively small set of input
parameters, just with different base constants or different calculations. This
lead to numerous redundant checks since if one value didn't change, others that
use the same inputs wouldn't have either.
2018-09-23 22:59:16 -07:00
Chris Robinson db6905bf57 Clear reverb gain coefficients when doing a device update 2018-09-23 02:22:23 -07:00
Chris Robinson 800326d37a Rename ALreverbState to ReverbState 2018-09-23 01:11:58 -07:00
Chris Robinson c708f871ec Update changelog 2018-09-22 20:13:31 -07:00
Chris Robinson 77a53594ba Improve the gain stepping difference check
Given the more stable stepping now in use, check that the total difference is
enough for perceptible transition, instead of the step itself.
2018-09-22 08:26:52 -07:00
Chris Robinson 36a6b9d42a Adjust comment spacing 2018-09-21 06:06:29 -07:00
Chris Robinson b6d0ff02c2 Use an internal event to more timely release old effect states 2018-09-21 04:14:15 -07:00
Chris Robinson f21e2df4cf Remove an unnecessary mutex 2018-09-20 22:53:16 -07:00
Chris Robinson 84a90b7cc3 Stop the event thread when releasing the context
To ensure no user callback gets called after alcDestroyContext.
2018-09-20 22:44:58 -07:00
Chris Robinson ebbbeb0d66 Put user events in a union structure 2018-09-20 21:59:38 -07:00
Chris Robinson cb8545346d Always start the event thread with the context 2018-09-20 19:58:01 -07:00
Chris Robinson 5c6b8eda4f Remove another duplicate function 2018-09-19 22:18:46 -07:00
Chris Robinson ea95a8adef Combine nearly-duplicate structures 2018-09-19 21:31:46 -07:00
Chris Robinson 6eb980d1b2 Remove a couple duplicate functions 2018-09-19 21:09:19 -07:00
Chris Robinson 48b7745a49 Add macros for commonly used square roots 2018-09-19 19:53:25 -07:00
Chris Robinson 9ef4dd4247 Use ALsizei for the source resample position 2018-09-18 19:08:13 -07:00
Chris Robinson 952ff84b99 Properly queue buffers for OpenSL capture 2018-09-17 23:43:23 -07:00
Chris Robinson b77e6096b8 Fix some potential race conditions with OpenSL
For playback, increment the ring buffer's write pointer before queueing audio,
to handle cases where the callback is invoked, advancing the read pointer,
before the write pointer is advanced.

For capture, limit the number of re-queued chunks to the number of fully read
chunks.
2018-09-17 22:49:52 -07:00
Chris Robinson 7f4441ffbe Handle the bsinc C resampler like the others 2018-09-17 04:07:56 -07:00
Chris Robinson 8bacb5dfb8 Fix buffer queue mixing logic
In particular, the source sample position was reduced by the size of the
next buffer list item when one is completed, rather than the size of the
one it just completed.
2018-09-16 17:38:55 -07:00
Chris Robinson a6734c7a91 Check the effect slot list size only when there's no free entries
The list can contain (reuable) NULL entries, so the max - current_size doesn't
indicate how many can be allocated.
2018-09-14 14:53:35 -07:00
Chris Robinson db452a19da The last reverb loop update doesn't need an aligned count. 2018-09-11 19:05:49 -07:00
Chris Robinson 99737469e2 Ensure the max reverb update size is a multiple of 4
It's not an issue for the final mix, but if one loop has an unaligned count,
the next loop will have unaligned input and output buffer targets which can
crash the SSE mixers.
2018-09-11 18:39:50 -07:00
Chris Robinson b13396cce2 Separate the delay line feeding from reading
Since it feeds a different line than it reads, the feeding could overwrite
what's subsequently read.
2018-09-10 13:30:20 -07:00
Chris Robinson d1f8b78dd4 Avoid a couple line count assumptions 2018-09-10 03:19:41 -07:00
Chris Robinson 4bdab0051f Combine identical loops into a separate function 2018-09-10 03:17:14 -07:00
Chris Robinson 634b13a630 Handle the early reflection delay separate from late refeed 2018-09-10 02:10:35 -07:00
Chris Robinson 275658b6db Some suggested changes for iOS 2018-09-07 23:01:17 -07:00
Chris Robinson 9054f41434 PATH_MAX not MAX_PATH 2018-09-07 22:52:34 -07:00
Chris Robinson fa7993fe3e Load .alsoftrc from the app bundle root on macOS
Not sure what priority this should have. Currently it loads after system-level
configs, and before user-level configs.
2018-09-07 22:32:48 -07:00
Chris Robinson 46cfedb117 Pass the device name list to the backend probe method 2018-09-07 22:02:37 -07:00
Chris Robinson 212cb8e298 Implement capture support for SoundIO 2018-09-07 20:08:24 -07:00
Chris Robinson 7394dd512d Rename ALCsndioBackend 2018-09-07 18:45:24 -07:00
61 changed files with 2250 additions and 1091 deletions
+82 -113
View File
@@ -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 {
+67 -46
View File
@@ -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)
+46
View File
@@ -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
View File
@@ -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
}
}
+1
View File
@@ -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
View File
@@ -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
+14 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+3 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+3 -3
View File
@@ -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;
+513
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
-8
View File
@@ -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
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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;
+1 -2
View File
@@ -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)
+1 -1
View File
@@ -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);
}
}
}
+1 -2
View File
@@ -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
View File
@@ -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)
+9 -9
View File
@@ -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)
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+6 -6
View File
@@ -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;
+5 -4
View File
@@ -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;
+1 -2
View File
@@ -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;
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+16
View File
@@ -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.
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+10 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6
View File
@@ -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];
+12
View File
@@ -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
}
+3
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0.2)
cmake_minimum_required(VERSION 3.5)
project(native-tools)