2007-11-13 18:02:18 -08:00
|
|
|
/**
|
|
|
|
* OpenAL cross platform audio library
|
|
|
|
* Copyright (C) 1999-2007 by authors.
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
2014-08-18 14:11:03 +02:00
|
|
|
* Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2007-11-13 18:02:18 -08:00
|
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2016-12-21 01:08:33 -08:00
|
|
|
#include "version.h"
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <memory.h>
|
2008-01-19 20:02:40 -08:00
|
|
|
#include <ctype.h>
|
2011-09-10 01:23:59 -07:00
|
|
|
#include <signal.h>
|
2010-03-24 22:43:08 -07:00
|
|
|
|
2018-11-14 17:01:19 -08:00
|
|
|
#include <cmath>
|
2018-11-14 04:15:44 -08:00
|
|
|
#include <atomic>
|
2018-11-14 06:17:47 -08:00
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
2018-11-14 04:15:44 -08:00
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
2018-11-28 11:30:44 -08:00
|
|
|
#include <numeric>
|
2018-11-16 05:23:42 -08:00
|
|
|
#include <algorithm>
|
2018-11-14 04:15:44 -08:00
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include "alMain.h"
|
2018-11-17 23:02:27 -08:00
|
|
|
#include "alcontext.h"
|
2007-12-18 18:13:49 -08:00
|
|
|
#include "alSource.h"
|
2012-10-09 04:48:12 -07:00
|
|
|
#include "alListener.h"
|
2007-12-31 01:09:57 -08:00
|
|
|
#include "alSource.h"
|
2008-07-17 18:38:07 -07:00
|
|
|
#include "alBuffer.h"
|
2018-03-22 07:05:40 -07:00
|
|
|
#include "alFilter.h"
|
|
|
|
#include "alEffect.h"
|
2008-01-16 14:01:24 -08:00
|
|
|
#include "alAuxEffectSlot.h"
|
2011-09-10 01:12:34 -07:00
|
|
|
#include "alError.h"
|
2018-01-11 06:50:53 -08:00
|
|
|
#include "mastering.h"
|
2016-03-15 05:08:05 -07:00
|
|
|
#include "bformatdec.h"
|
2018-11-21 15:31:32 -08:00
|
|
|
#include "uhjfilter.h"
|
2009-01-24 10:38:04 -08:00
|
|
|
#include "alu.h"
|
2018-01-11 07:56:54 -08:00
|
|
|
#include "alconfig.h"
|
2018-01-31 20:21:54 -08:00
|
|
|
#include "ringbuffer.h"
|
2018-11-22 04:53:29 -08:00
|
|
|
#include "filters/splitter.h"
|
2018-11-22 06:49:37 -08:00
|
|
|
#include "bs2b.h"
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-01-11 08:44:52 -08:00
|
|
|
#include "fpu_modes.h"
|
2018-01-11 07:19:19 -08:00
|
|
|
#include "cpu_caps.h"
|
2014-04-16 01:39:11 -07:00
|
|
|
#include "compat.h"
|
|
|
|
#include "threads.h"
|
2016-03-29 00:44:58 -07:00
|
|
|
#include "almalloc.h"
|
2014-03-28 05:40:24 -07:00
|
|
|
|
2013-10-27 14:24:55 -07:00
|
|
|
#include "backends/base.h"
|
2018-11-15 19:15:14 -08:00
|
|
|
#include "backends/null.h"
|
2018-11-15 19:42:13 -08:00
|
|
|
#include "backends/loopback.h"
|
2018-11-15 22:03:20 -08:00
|
|
|
#ifdef HAVE_JACK
|
|
|
|
#include "backends/jack.h"
|
|
|
|
#endif
|
2018-11-15 19:55:56 -08:00
|
|
|
#ifdef HAVE_PULSEAUDIO
|
|
|
|
#include "backends/pulseaudio.h"
|
|
|
|
#endif
|
2018-11-15 21:33:44 -08:00
|
|
|
#ifdef HAVE_ALSA
|
|
|
|
#include "backends/alsa.h"
|
|
|
|
#endif
|
2018-11-15 21:14:20 -08:00
|
|
|
#ifdef HAVE_WASAPI
|
|
|
|
#include "backends/wasapi.h"
|
|
|
|
#endif
|
2018-11-15 21:24:09 -08:00
|
|
|
#ifdef HAVE_COREAUDIO
|
|
|
|
#include "backends/coreaudio.h"
|
|
|
|
#endif
|
2018-11-15 21:42:17 -08:00
|
|
|
#ifdef HAVE_OPENSL
|
|
|
|
#include "backends/opensl.h"
|
|
|
|
#endif
|
2018-11-15 22:23:29 -08:00
|
|
|
#ifdef HAVE_SOLARIS
|
|
|
|
#include "backends/solaris.h"
|
|
|
|
#endif
|
2018-11-15 22:36:49 -08:00
|
|
|
#ifdef HAVE_SNDIO
|
|
|
|
#include "backends/sndio.h"
|
|
|
|
#endif
|
2018-11-15 23:02:26 -08:00
|
|
|
#ifdef HAVE_OSS
|
|
|
|
#include "backends/oss.h"
|
|
|
|
#endif
|
2018-11-15 23:10:06 -08:00
|
|
|
#ifdef HAVE_QSA
|
|
|
|
#include "backends/qsa.h"
|
|
|
|
#endif
|
2018-11-15 23:32:28 -08:00
|
|
|
#ifdef HAVE_DSOUND
|
|
|
|
#include "backends/dsound.h"
|
|
|
|
#endif
|
2018-11-15 23:41:09 -08:00
|
|
|
#ifdef HAVE_WINMM
|
|
|
|
#include "backends/winmm.h"
|
|
|
|
#endif
|
2018-11-15 23:50:15 -08:00
|
|
|
#ifdef HAVE_PORTAUDIO
|
|
|
|
#include "backends/portaudio.h"
|
|
|
|
#endif
|
2018-11-15 22:15:10 -08:00
|
|
|
#ifdef HAVE_SDL2
|
|
|
|
#include "backends/sdl2.h"
|
|
|
|
#endif
|
2018-11-15 21:53:14 -08:00
|
|
|
#ifdef HAVE_WAVE
|
|
|
|
#include "backends/wave.h"
|
|
|
|
#endif
|
2013-10-27 14:24:55 -07:00
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
namespace {
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Backends
|
|
|
|
************************************************/
|
2013-10-27 14:24:55 -07:00
|
|
|
struct BackendInfo {
|
|
|
|
const char *name;
|
2018-11-15 19:15:14 -08:00
|
|
|
BackendFactory& (*getFactory)(void);
|
2013-10-27 14:24:55 -07:00
|
|
|
};
|
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
struct BackendInfo BackendList[] = {
|
2018-11-15 22:03:20 -08:00
|
|
|
#ifdef HAVE_JACK
|
|
|
|
{ "jack", JackBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 19:55:56 -08:00
|
|
|
#ifdef HAVE_PULSEAUDIO
|
|
|
|
{ "pulse", PulseBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 21:33:44 -08:00
|
|
|
#ifdef HAVE_ALSA
|
|
|
|
{ "alsa", AlsaBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 21:14:20 -08:00
|
|
|
#ifdef HAVE_WASAPI
|
|
|
|
{ "wasapi", WasapiBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 21:24:09 -08:00
|
|
|
#ifdef HAVE_COREAUDIO
|
|
|
|
{ "core", CoreAudioBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 21:42:17 -08:00
|
|
|
#ifdef HAVE_OPENSL
|
|
|
|
{ "opensl", OSLBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 22:23:29 -08:00
|
|
|
#ifdef HAVE_SOLARIS
|
|
|
|
{ "solaris", SolarisBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 22:36:49 -08:00
|
|
|
#ifdef HAVE_SNDIO
|
|
|
|
{ "sndio", SndIOBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 23:02:26 -08:00
|
|
|
#ifdef HAVE_OSS
|
|
|
|
{ "oss", OSSBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 23:10:06 -08:00
|
|
|
#ifdef HAVE_QSA
|
|
|
|
{ "qsa", QSABackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 23:32:28 -08:00
|
|
|
#ifdef HAVE_DSOUND
|
|
|
|
{ "dsound", DSoundBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 23:41:09 -08:00
|
|
|
#ifdef HAVE_WINMM
|
|
|
|
{ "winmm", WinMMBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 23:50:15 -08:00
|
|
|
#ifdef HAVE_PORTAUDIO
|
|
|
|
{ "port", PortBackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 22:15:10 -08:00
|
|
|
#ifdef HAVE_SDL2
|
|
|
|
{ "sdl2", SDL2BackendFactory::getFactory },
|
|
|
|
#endif
|
2018-11-15 19:55:56 -08:00
|
|
|
|
2018-11-15 19:15:14 -08:00
|
|
|
{ "null", NullBackendFactory::getFactory },
|
2018-11-15 21:53:14 -08:00
|
|
|
#ifdef HAVE_WAVE
|
|
|
|
{ "wave", WaveBackendFactory::getFactory },
|
|
|
|
#endif
|
2007-11-13 18:02:18 -08:00
|
|
|
};
|
2018-11-14 06:17:47 -08:00
|
|
|
ALsizei BackendListSize = COUNTOF(BackendList);
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
struct BackendInfo PlaybackBackend;
|
|
|
|
struct BackendInfo CaptureBackend;
|
2011-08-19 00:23:26 -07:00
|
|
|
|
2013-10-27 14:24:55 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Functions, enums, and errors
|
|
|
|
************************************************/
|
2017-03-14 08:37:50 -07:00
|
|
|
#define DECL(x) { #x, (ALCvoid*)(x) }
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr struct {
|
2010-03-24 22:43:08 -07:00
|
|
|
const ALCchar *funcName;
|
|
|
|
ALCvoid *address;
|
2017-03-14 08:37:50 -07:00
|
|
|
} alcFunctions[] = {
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(alcCreateContext),
|
|
|
|
DECL(alcMakeContextCurrent),
|
|
|
|
DECL(alcProcessContext),
|
|
|
|
DECL(alcSuspendContext),
|
|
|
|
DECL(alcDestroyContext),
|
|
|
|
DECL(alcGetCurrentContext),
|
|
|
|
DECL(alcGetContextsDevice),
|
|
|
|
DECL(alcOpenDevice),
|
|
|
|
DECL(alcCloseDevice),
|
|
|
|
DECL(alcGetError),
|
|
|
|
DECL(alcIsExtensionPresent),
|
|
|
|
DECL(alcGetProcAddress),
|
|
|
|
DECL(alcGetEnumValue),
|
|
|
|
DECL(alcGetString),
|
|
|
|
DECL(alcGetIntegerv),
|
|
|
|
DECL(alcCaptureOpenDevice),
|
|
|
|
DECL(alcCaptureCloseDevice),
|
|
|
|
DECL(alcCaptureStart),
|
|
|
|
DECL(alcCaptureStop),
|
|
|
|
DECL(alcCaptureSamples),
|
|
|
|
|
|
|
|
DECL(alcSetThreadContext),
|
|
|
|
DECL(alcGetThreadContext),
|
|
|
|
|
|
|
|
DECL(alcLoopbackOpenDeviceSOFT),
|
|
|
|
DECL(alcIsRenderFormatSupportedSOFT),
|
|
|
|
DECL(alcRenderSamplesSOFT),
|
|
|
|
|
2014-01-15 16:27:17 -08:00
|
|
|
DECL(alcDevicePauseSOFT),
|
|
|
|
DECL(alcDeviceResumeSOFT),
|
|
|
|
|
2015-10-02 23:54:30 -07:00
|
|
|
DECL(alcGetStringiSOFT),
|
2015-05-16 01:46:06 -07:00
|
|
|
DECL(alcResetDeviceSOFT),
|
|
|
|
|
2015-10-28 13:38:30 -07:00
|
|
|
DECL(alcGetInteger64vSOFT),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(alEnable),
|
|
|
|
DECL(alDisable),
|
|
|
|
DECL(alIsEnabled),
|
|
|
|
DECL(alGetString),
|
|
|
|
DECL(alGetBooleanv),
|
|
|
|
DECL(alGetIntegerv),
|
|
|
|
DECL(alGetFloatv),
|
|
|
|
DECL(alGetDoublev),
|
|
|
|
DECL(alGetBoolean),
|
|
|
|
DECL(alGetInteger),
|
|
|
|
DECL(alGetFloat),
|
|
|
|
DECL(alGetDouble),
|
|
|
|
DECL(alGetError),
|
|
|
|
DECL(alIsExtensionPresent),
|
|
|
|
DECL(alGetProcAddress),
|
|
|
|
DECL(alGetEnumValue),
|
|
|
|
DECL(alListenerf),
|
|
|
|
DECL(alListener3f),
|
|
|
|
DECL(alListenerfv),
|
|
|
|
DECL(alListeneri),
|
|
|
|
DECL(alListener3i),
|
|
|
|
DECL(alListeneriv),
|
|
|
|
DECL(alGetListenerf),
|
|
|
|
DECL(alGetListener3f),
|
|
|
|
DECL(alGetListenerfv),
|
|
|
|
DECL(alGetListeneri),
|
|
|
|
DECL(alGetListener3i),
|
|
|
|
DECL(alGetListeneriv),
|
|
|
|
DECL(alGenSources),
|
|
|
|
DECL(alDeleteSources),
|
|
|
|
DECL(alIsSource),
|
|
|
|
DECL(alSourcef),
|
|
|
|
DECL(alSource3f),
|
|
|
|
DECL(alSourcefv),
|
|
|
|
DECL(alSourcei),
|
|
|
|
DECL(alSource3i),
|
|
|
|
DECL(alSourceiv),
|
|
|
|
DECL(alGetSourcef),
|
|
|
|
DECL(alGetSource3f),
|
|
|
|
DECL(alGetSourcefv),
|
|
|
|
DECL(alGetSourcei),
|
|
|
|
DECL(alGetSource3i),
|
|
|
|
DECL(alGetSourceiv),
|
|
|
|
DECL(alSourcePlayv),
|
|
|
|
DECL(alSourceStopv),
|
|
|
|
DECL(alSourceRewindv),
|
|
|
|
DECL(alSourcePausev),
|
|
|
|
DECL(alSourcePlay),
|
|
|
|
DECL(alSourceStop),
|
|
|
|
DECL(alSourceRewind),
|
|
|
|
DECL(alSourcePause),
|
|
|
|
DECL(alSourceQueueBuffers),
|
|
|
|
DECL(alSourceUnqueueBuffers),
|
|
|
|
DECL(alGenBuffers),
|
|
|
|
DECL(alDeleteBuffers),
|
|
|
|
DECL(alIsBuffer),
|
|
|
|
DECL(alBufferData),
|
|
|
|
DECL(alBufferf),
|
|
|
|
DECL(alBuffer3f),
|
|
|
|
DECL(alBufferfv),
|
|
|
|
DECL(alBufferi),
|
|
|
|
DECL(alBuffer3i),
|
|
|
|
DECL(alBufferiv),
|
|
|
|
DECL(alGetBufferf),
|
|
|
|
DECL(alGetBuffer3f),
|
|
|
|
DECL(alGetBufferfv),
|
|
|
|
DECL(alGetBufferi),
|
|
|
|
DECL(alGetBuffer3i),
|
|
|
|
DECL(alGetBufferiv),
|
|
|
|
DECL(alDopplerFactor),
|
|
|
|
DECL(alDopplerVelocity),
|
|
|
|
DECL(alSpeedOfSound),
|
|
|
|
DECL(alDistanceModel),
|
|
|
|
|
|
|
|
DECL(alGenFilters),
|
|
|
|
DECL(alDeleteFilters),
|
|
|
|
DECL(alIsFilter),
|
|
|
|
DECL(alFilteri),
|
|
|
|
DECL(alFilteriv),
|
|
|
|
DECL(alFilterf),
|
|
|
|
DECL(alFilterfv),
|
|
|
|
DECL(alGetFilteri),
|
|
|
|
DECL(alGetFilteriv),
|
|
|
|
DECL(alGetFilterf),
|
|
|
|
DECL(alGetFilterfv),
|
|
|
|
DECL(alGenEffects),
|
|
|
|
DECL(alDeleteEffects),
|
|
|
|
DECL(alIsEffect),
|
|
|
|
DECL(alEffecti),
|
|
|
|
DECL(alEffectiv),
|
|
|
|
DECL(alEffectf),
|
|
|
|
DECL(alEffectfv),
|
|
|
|
DECL(alGetEffecti),
|
|
|
|
DECL(alGetEffectiv),
|
|
|
|
DECL(alGetEffectf),
|
|
|
|
DECL(alGetEffectfv),
|
|
|
|
DECL(alGenAuxiliaryEffectSlots),
|
|
|
|
DECL(alDeleteAuxiliaryEffectSlots),
|
|
|
|
DECL(alIsAuxiliaryEffectSlot),
|
|
|
|
DECL(alAuxiliaryEffectSloti),
|
|
|
|
DECL(alAuxiliaryEffectSlotiv),
|
|
|
|
DECL(alAuxiliaryEffectSlotf),
|
|
|
|
DECL(alAuxiliaryEffectSlotfv),
|
|
|
|
DECL(alGetAuxiliaryEffectSloti),
|
|
|
|
DECL(alGetAuxiliaryEffectSlotiv),
|
|
|
|
DECL(alGetAuxiliaryEffectSlotf),
|
|
|
|
DECL(alGetAuxiliaryEffectSlotfv),
|
|
|
|
|
|
|
|
DECL(alDeferUpdatesSOFT),
|
|
|
|
DECL(alProcessUpdatesSOFT),
|
|
|
|
|
2012-10-13 00:56:39 -07:00
|
|
|
DECL(alSourcedSOFT),
|
|
|
|
DECL(alSource3dSOFT),
|
|
|
|
DECL(alSourcedvSOFT),
|
2012-08-20 15:26:35 -07:00
|
|
|
DECL(alGetSourcedSOFT),
|
|
|
|
DECL(alGetSource3dSOFT),
|
|
|
|
DECL(alGetSourcedvSOFT),
|
2012-10-13 00:56:39 -07:00
|
|
|
DECL(alSourcei64SOFT),
|
|
|
|
DECL(alSource3i64SOFT),
|
|
|
|
DECL(alSourcei64vSOFT),
|
2012-08-18 11:02:54 -07:00
|
|
|
DECL(alGetSourcei64SOFT),
|
2012-08-20 14:48:08 -07:00
|
|
|
DECL(alGetSource3i64SOFT),
|
2012-08-18 11:02:54 -07:00
|
|
|
DECL(alGetSourcei64vSOFT),
|
|
|
|
|
2017-04-21 04:15:08 -07:00
|
|
|
DECL(alGetStringiSOFT),
|
2018-01-20 13:37:43 -08:00
|
|
|
|
2018-01-23 14:33:30 -08:00
|
|
|
DECL(alBufferStorageSOFT),
|
2018-01-20 13:37:43 -08:00
|
|
|
DECL(alMapBufferSOFT),
|
|
|
|
DECL(alUnmapBufferSOFT),
|
2018-01-23 14:33:30 -08:00
|
|
|
DECL(alFlushMappedBufferSOFT),
|
2018-01-23 18:25:59 -08:00
|
|
|
|
|
|
|
DECL(alEventControlSOFT),
|
|
|
|
DECL(alEventCallbackSOFT),
|
|
|
|
DECL(alGetPointerSOFT),
|
|
|
|
DECL(alGetPointervSOFT),
|
2007-11-13 18:02:18 -08:00
|
|
|
};
|
2012-04-20 22:38:03 -07:00
|
|
|
#undef DECL
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
#define DECL(x) { #x, (x) }
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr struct {
|
2017-03-14 08:37:50 -07:00
|
|
|
const ALCchar *enumName;
|
|
|
|
ALCenum value;
|
|
|
|
} alcEnumerations[] = {
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(ALC_INVALID),
|
|
|
|
DECL(ALC_FALSE),
|
|
|
|
DECL(ALC_TRUE),
|
|
|
|
|
|
|
|
DECL(ALC_MAJOR_VERSION),
|
|
|
|
DECL(ALC_MINOR_VERSION),
|
|
|
|
DECL(ALC_ATTRIBUTES_SIZE),
|
|
|
|
DECL(ALC_ALL_ATTRIBUTES),
|
|
|
|
DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
|
|
|
|
DECL(ALC_DEVICE_SPECIFIER),
|
|
|
|
DECL(ALC_ALL_DEVICES_SPECIFIER),
|
|
|
|
DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
|
|
|
|
DECL(ALC_EXTENSIONS),
|
|
|
|
DECL(ALC_FREQUENCY),
|
|
|
|
DECL(ALC_REFRESH),
|
|
|
|
DECL(ALC_SYNC),
|
|
|
|
DECL(ALC_MONO_SOURCES),
|
|
|
|
DECL(ALC_STEREO_SOURCES),
|
|
|
|
DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
|
|
|
|
DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
|
|
|
|
DECL(ALC_CAPTURE_SAMPLES),
|
|
|
|
DECL(ALC_CONNECTED),
|
|
|
|
|
|
|
|
DECL(ALC_EFX_MAJOR_VERSION),
|
|
|
|
DECL(ALC_EFX_MINOR_VERSION),
|
|
|
|
DECL(ALC_MAX_AUXILIARY_SENDS),
|
|
|
|
|
|
|
|
DECL(ALC_FORMAT_CHANNELS_SOFT),
|
|
|
|
DECL(ALC_FORMAT_TYPE_SOFT),
|
|
|
|
|
|
|
|
DECL(ALC_MONO_SOFT),
|
|
|
|
DECL(ALC_STEREO_SOFT),
|
|
|
|
DECL(ALC_QUAD_SOFT),
|
|
|
|
DECL(ALC_5POINT1_SOFT),
|
|
|
|
DECL(ALC_6POINT1_SOFT),
|
|
|
|
DECL(ALC_7POINT1_SOFT),
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
DECL(ALC_BFORMAT3D_SOFT),
|
2012-04-20 22:38:03 -07:00
|
|
|
|
|
|
|
DECL(ALC_BYTE_SOFT),
|
|
|
|
DECL(ALC_UNSIGNED_BYTE_SOFT),
|
|
|
|
DECL(ALC_SHORT_SOFT),
|
|
|
|
DECL(ALC_UNSIGNED_SHORT_SOFT),
|
|
|
|
DECL(ALC_INT_SOFT),
|
|
|
|
DECL(ALC_UNSIGNED_INT_SOFT),
|
|
|
|
DECL(ALC_FLOAT_SOFT),
|
|
|
|
|
2015-09-17 04:01:46 -07:00
|
|
|
DECL(ALC_HRTF_SOFT),
|
|
|
|
DECL(ALC_DONT_CARE_SOFT),
|
|
|
|
DECL(ALC_HRTF_STATUS_SOFT),
|
|
|
|
DECL(ALC_HRTF_DISABLED_SOFT),
|
|
|
|
DECL(ALC_HRTF_ENABLED_SOFT),
|
|
|
|
DECL(ALC_HRTF_DENIED_SOFT),
|
|
|
|
DECL(ALC_HRTF_REQUIRED_SOFT),
|
|
|
|
DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT),
|
|
|
|
DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT),
|
2015-10-26 22:34:02 -07:00
|
|
|
DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT),
|
2015-10-02 23:54:30 -07:00
|
|
|
DECL(ALC_HRTF_SPECIFIER_SOFT),
|
2015-10-07 03:29:53 -07:00
|
|
|
DECL(ALC_HRTF_ID_SOFT),
|
2015-09-17 04:01:46 -07:00
|
|
|
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
DECL(ALC_AMBISONIC_LAYOUT_SOFT),
|
|
|
|
DECL(ALC_AMBISONIC_SCALING_SOFT),
|
|
|
|
DECL(ALC_AMBISONIC_ORDER_SOFT),
|
|
|
|
DECL(ALC_ACN_SOFT),
|
|
|
|
DECL(ALC_FUMA_SOFT),
|
|
|
|
DECL(ALC_N3D_SOFT),
|
|
|
|
DECL(ALC_SN3D_SOFT),
|
|
|
|
|
2017-04-30 04:21:48 -07:00
|
|
|
DECL(ALC_OUTPUT_LIMITER_SOFT),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(ALC_NO_ERROR),
|
|
|
|
DECL(ALC_INVALID_DEVICE),
|
|
|
|
DECL(ALC_INVALID_CONTEXT),
|
|
|
|
DECL(ALC_INVALID_ENUM),
|
|
|
|
DECL(ALC_INVALID_VALUE),
|
|
|
|
DECL(ALC_OUT_OF_MEMORY),
|
|
|
|
|
|
|
|
|
|
|
|
DECL(AL_INVALID),
|
|
|
|
DECL(AL_NONE),
|
|
|
|
DECL(AL_FALSE),
|
|
|
|
DECL(AL_TRUE),
|
|
|
|
|
|
|
|
DECL(AL_SOURCE_RELATIVE),
|
|
|
|
DECL(AL_CONE_INNER_ANGLE),
|
|
|
|
DECL(AL_CONE_OUTER_ANGLE),
|
|
|
|
DECL(AL_PITCH),
|
|
|
|
DECL(AL_POSITION),
|
|
|
|
DECL(AL_DIRECTION),
|
|
|
|
DECL(AL_VELOCITY),
|
|
|
|
DECL(AL_LOOPING),
|
|
|
|
DECL(AL_BUFFER),
|
|
|
|
DECL(AL_GAIN),
|
|
|
|
DECL(AL_MIN_GAIN),
|
|
|
|
DECL(AL_MAX_GAIN),
|
|
|
|
DECL(AL_ORIENTATION),
|
|
|
|
DECL(AL_REFERENCE_DISTANCE),
|
|
|
|
DECL(AL_ROLLOFF_FACTOR),
|
|
|
|
DECL(AL_CONE_OUTER_GAIN),
|
|
|
|
DECL(AL_MAX_DISTANCE),
|
|
|
|
DECL(AL_SEC_OFFSET),
|
|
|
|
DECL(AL_SAMPLE_OFFSET),
|
|
|
|
DECL(AL_BYTE_OFFSET),
|
|
|
|
DECL(AL_SOURCE_TYPE),
|
|
|
|
DECL(AL_STATIC),
|
|
|
|
DECL(AL_STREAMING),
|
|
|
|
DECL(AL_UNDETERMINED),
|
|
|
|
DECL(AL_METERS_PER_UNIT),
|
2015-09-17 04:01:46 -07:00
|
|
|
DECL(AL_LOOP_POINTS_SOFT),
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_DIRECT_CHANNELS_SOFT),
|
|
|
|
|
|
|
|
DECL(AL_DIRECT_FILTER),
|
|
|
|
DECL(AL_AUXILIARY_SEND_FILTER),
|
|
|
|
DECL(AL_AIR_ABSORPTION_FACTOR),
|
|
|
|
DECL(AL_ROOM_ROLLOFF_FACTOR),
|
|
|
|
DECL(AL_CONE_OUTER_GAINHF),
|
|
|
|
DECL(AL_DIRECT_FILTER_GAINHF_AUTO),
|
|
|
|
DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO),
|
|
|
|
DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO),
|
|
|
|
|
|
|
|
DECL(AL_SOURCE_STATE),
|
|
|
|
DECL(AL_INITIAL),
|
|
|
|
DECL(AL_PLAYING),
|
|
|
|
DECL(AL_PAUSED),
|
|
|
|
DECL(AL_STOPPED),
|
|
|
|
|
|
|
|
DECL(AL_BUFFERS_QUEUED),
|
|
|
|
DECL(AL_BUFFERS_PROCESSED),
|
|
|
|
|
|
|
|
DECL(AL_FORMAT_MONO8),
|
|
|
|
DECL(AL_FORMAT_MONO16),
|
|
|
|
DECL(AL_FORMAT_MONO_FLOAT32),
|
|
|
|
DECL(AL_FORMAT_MONO_DOUBLE_EXT),
|
|
|
|
DECL(AL_FORMAT_STEREO8),
|
|
|
|
DECL(AL_FORMAT_STEREO16),
|
|
|
|
DECL(AL_FORMAT_STEREO_FLOAT32),
|
|
|
|
DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
|
|
|
|
DECL(AL_FORMAT_MONO_IMA4),
|
|
|
|
DECL(AL_FORMAT_STEREO_IMA4),
|
2014-03-04 22:44:30 -08:00
|
|
|
DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
|
|
|
|
DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_FORMAT_QUAD8_LOKI),
|
|
|
|
DECL(AL_FORMAT_QUAD16_LOKI),
|
|
|
|
DECL(AL_FORMAT_QUAD8),
|
|
|
|
DECL(AL_FORMAT_QUAD16),
|
|
|
|
DECL(AL_FORMAT_QUAD32),
|
|
|
|
DECL(AL_FORMAT_51CHN8),
|
|
|
|
DECL(AL_FORMAT_51CHN16),
|
|
|
|
DECL(AL_FORMAT_51CHN32),
|
|
|
|
DECL(AL_FORMAT_61CHN8),
|
|
|
|
DECL(AL_FORMAT_61CHN16),
|
|
|
|
DECL(AL_FORMAT_61CHN32),
|
|
|
|
DECL(AL_FORMAT_71CHN8),
|
|
|
|
DECL(AL_FORMAT_71CHN16),
|
|
|
|
DECL(AL_FORMAT_71CHN32),
|
|
|
|
DECL(AL_FORMAT_REAR8),
|
|
|
|
DECL(AL_FORMAT_REAR16),
|
|
|
|
DECL(AL_FORMAT_REAR32),
|
|
|
|
DECL(AL_FORMAT_MONO_MULAW),
|
|
|
|
DECL(AL_FORMAT_MONO_MULAW_EXT),
|
|
|
|
DECL(AL_FORMAT_STEREO_MULAW),
|
|
|
|
DECL(AL_FORMAT_STEREO_MULAW_EXT),
|
|
|
|
DECL(AL_FORMAT_QUAD_MULAW),
|
|
|
|
DECL(AL_FORMAT_51CHN_MULAW),
|
|
|
|
DECL(AL_FORMAT_61CHN_MULAW),
|
|
|
|
DECL(AL_FORMAT_71CHN_MULAW),
|
|
|
|
DECL(AL_FORMAT_REAR_MULAW),
|
|
|
|
DECL(AL_FORMAT_MONO_ALAW_EXT),
|
|
|
|
DECL(AL_FORMAT_STEREO_ALAW_EXT),
|
|
|
|
|
2016-04-25 18:56:59 -07:00
|
|
|
DECL(AL_FORMAT_BFORMAT2D_8),
|
|
|
|
DECL(AL_FORMAT_BFORMAT2D_16),
|
|
|
|
DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
|
|
|
|
DECL(AL_FORMAT_BFORMAT2D_MULAW),
|
|
|
|
DECL(AL_FORMAT_BFORMAT3D_8),
|
|
|
|
DECL(AL_FORMAT_BFORMAT3D_16),
|
|
|
|
DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
|
|
|
|
DECL(AL_FORMAT_BFORMAT3D_MULAW),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_FREQUENCY),
|
|
|
|
DECL(AL_BITS),
|
|
|
|
DECL(AL_CHANNELS),
|
|
|
|
DECL(AL_SIZE),
|
Add an extension to alter the block alignment for buffer unpack/pack ops
This is for unpacking (reading, e.g. alBufferData) and packing (writing, e.g.
alGetBufferSamplesSOFT) operations. The alignments are specified in sample
frames, with 0 meaning the default (65 for IMA4, 1 otherwise). IMA4 alignment
must be a multiple of 8, plus 1 (e.g. alignment = n*8 + 1), otherwise an error
will occur during (un)packing. Chenging the block alignment does not affect
already-loaded sample data, only future unpack/pack operations... so for
example, this is perfectly valid:
// Load mono IMA4 data with a block alignment of 1024 bytes, or 2041 sample
// frames.
alBufferi(buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 2041);
alBufferData(buffer, AL_FORMAT_MONO_IMA4, data, data_len, srate);
alBufferi(buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 0);
2014-03-04 05:16:45 -08:00
|
|
|
DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
|
|
|
|
DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
|
2012-04-20 22:38:03 -07:00
|
|
|
|
2016-04-25 00:30:47 -07:00
|
|
|
DECL(AL_SOURCE_RADIUS),
|
|
|
|
|
2016-03-25 14:40:44 -07:00
|
|
|
DECL(AL_STEREO_ANGLES),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_UNUSED),
|
|
|
|
DECL(AL_PENDING),
|
|
|
|
DECL(AL_PROCESSED),
|
|
|
|
|
|
|
|
DECL(AL_NO_ERROR),
|
|
|
|
DECL(AL_INVALID_NAME),
|
|
|
|
DECL(AL_INVALID_ENUM),
|
|
|
|
DECL(AL_INVALID_VALUE),
|
|
|
|
DECL(AL_INVALID_OPERATION),
|
|
|
|
DECL(AL_OUT_OF_MEMORY),
|
|
|
|
|
|
|
|
DECL(AL_VENDOR),
|
|
|
|
DECL(AL_VERSION),
|
|
|
|
DECL(AL_RENDERER),
|
|
|
|
DECL(AL_EXTENSIONS),
|
|
|
|
|
|
|
|
DECL(AL_DOPPLER_FACTOR),
|
|
|
|
DECL(AL_DOPPLER_VELOCITY),
|
|
|
|
DECL(AL_DISTANCE_MODEL),
|
|
|
|
DECL(AL_SPEED_OF_SOUND),
|
|
|
|
DECL(AL_SOURCE_DISTANCE_MODEL),
|
|
|
|
DECL(AL_DEFERRED_UPDATES_SOFT),
|
2016-08-28 18:21:09 -07:00
|
|
|
DECL(AL_GAIN_LIMIT_SOFT),
|
2012-04-20 22:38:03 -07:00
|
|
|
|
|
|
|
DECL(AL_INVERSE_DISTANCE),
|
|
|
|
DECL(AL_INVERSE_DISTANCE_CLAMPED),
|
|
|
|
DECL(AL_LINEAR_DISTANCE),
|
|
|
|
DECL(AL_LINEAR_DISTANCE_CLAMPED),
|
|
|
|
DECL(AL_EXPONENT_DISTANCE),
|
|
|
|
DECL(AL_EXPONENT_DISTANCE_CLAMPED),
|
|
|
|
|
|
|
|
DECL(AL_FILTER_TYPE),
|
|
|
|
DECL(AL_FILTER_NULL),
|
|
|
|
DECL(AL_FILTER_LOWPASS),
|
|
|
|
DECL(AL_FILTER_HIGHPASS),
|
|
|
|
DECL(AL_FILTER_BANDPASS),
|
2012-04-20 01:24:58 -07:00
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_LOWPASS_GAIN),
|
|
|
|
DECL(AL_LOWPASS_GAINHF),
|
2012-04-20 01:24:58 -07:00
|
|
|
|
2014-05-17 07:29:50 -07:00
|
|
|
DECL(AL_HIGHPASS_GAIN),
|
|
|
|
DECL(AL_HIGHPASS_GAINLF),
|
|
|
|
|
2014-05-17 08:04:14 -07:00
|
|
|
DECL(AL_BANDPASS_GAIN),
|
|
|
|
DECL(AL_BANDPASS_GAINHF),
|
|
|
|
DECL(AL_BANDPASS_GAINLF),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EFFECT_TYPE),
|
|
|
|
DECL(AL_EFFECT_NULL),
|
|
|
|
DECL(AL_EFFECT_REVERB),
|
|
|
|
DECL(AL_EFFECT_EAXREVERB),
|
|
|
|
DECL(AL_EFFECT_CHORUS),
|
|
|
|
DECL(AL_EFFECT_DISTORTION),
|
|
|
|
DECL(AL_EFFECT_ECHO),
|
|
|
|
DECL(AL_EFFECT_FLANGER),
|
2018-03-18 17:47:17 +01:00
|
|
|
DECL(AL_EFFECT_PITCH_SHIFTER),
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
|
2018-05-20 17:23:03 +02:00
|
|
|
#if 0
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EFFECT_VOCAL_MORPHER),
|
2012-04-20 01:24:58 -07:00
|
|
|
#endif
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EFFECT_RING_MODULATOR),
|
|
|
|
DECL(AL_EFFECT_AUTOWAH),
|
|
|
|
DECL(AL_EFFECT_COMPRESSOR),
|
2013-05-18 01:33:01 -07:00
|
|
|
DECL(AL_EFFECT_EQUALIZER),
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
|
|
|
|
DECL(AL_EFFECT_DEDICATED_DIALOGUE),
|
|
|
|
|
2017-01-05 20:06:24 -08:00
|
|
|
DECL(AL_EFFECTSLOT_EFFECT),
|
|
|
|
DECL(AL_EFFECTSLOT_GAIN),
|
|
|
|
DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO),
|
|
|
|
DECL(AL_EFFECTSLOT_NULL),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_EAXREVERB_DENSITY),
|
|
|
|
DECL(AL_EAXREVERB_DIFFUSION),
|
|
|
|
DECL(AL_EAXREVERB_GAIN),
|
|
|
|
DECL(AL_EAXREVERB_GAINHF),
|
|
|
|
DECL(AL_EAXREVERB_GAINLF),
|
|
|
|
DECL(AL_EAXREVERB_DECAY_TIME),
|
|
|
|
DECL(AL_EAXREVERB_DECAY_HFRATIO),
|
|
|
|
DECL(AL_EAXREVERB_DECAY_LFRATIO),
|
|
|
|
DECL(AL_EAXREVERB_REFLECTIONS_GAIN),
|
|
|
|
DECL(AL_EAXREVERB_REFLECTIONS_DELAY),
|
|
|
|
DECL(AL_EAXREVERB_REFLECTIONS_PAN),
|
|
|
|
DECL(AL_EAXREVERB_LATE_REVERB_GAIN),
|
|
|
|
DECL(AL_EAXREVERB_LATE_REVERB_DELAY),
|
|
|
|
DECL(AL_EAXREVERB_LATE_REVERB_PAN),
|
|
|
|
DECL(AL_EAXREVERB_ECHO_TIME),
|
|
|
|
DECL(AL_EAXREVERB_ECHO_DEPTH),
|
|
|
|
DECL(AL_EAXREVERB_MODULATION_TIME),
|
|
|
|
DECL(AL_EAXREVERB_MODULATION_DEPTH),
|
|
|
|
DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF),
|
|
|
|
DECL(AL_EAXREVERB_HFREFERENCE),
|
|
|
|
DECL(AL_EAXREVERB_LFREFERENCE),
|
|
|
|
DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR),
|
|
|
|
DECL(AL_EAXREVERB_DECAY_HFLIMIT),
|
|
|
|
|
|
|
|
DECL(AL_REVERB_DENSITY),
|
|
|
|
DECL(AL_REVERB_DIFFUSION),
|
|
|
|
DECL(AL_REVERB_GAIN),
|
|
|
|
DECL(AL_REVERB_GAINHF),
|
|
|
|
DECL(AL_REVERB_DECAY_TIME),
|
|
|
|
DECL(AL_REVERB_DECAY_HFRATIO),
|
|
|
|
DECL(AL_REVERB_REFLECTIONS_GAIN),
|
|
|
|
DECL(AL_REVERB_REFLECTIONS_DELAY),
|
|
|
|
DECL(AL_REVERB_LATE_REVERB_GAIN),
|
|
|
|
DECL(AL_REVERB_LATE_REVERB_DELAY),
|
|
|
|
DECL(AL_REVERB_AIR_ABSORPTION_GAINHF),
|
|
|
|
DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
|
|
|
|
DECL(AL_REVERB_DECAY_HFLIMIT),
|
|
|
|
|
2013-03-13 23:31:12 -07:00
|
|
|
DECL(AL_CHORUS_WAVEFORM),
|
|
|
|
DECL(AL_CHORUS_PHASE),
|
|
|
|
DECL(AL_CHORUS_RATE),
|
|
|
|
DECL(AL_CHORUS_DEPTH),
|
|
|
|
DECL(AL_CHORUS_FEEDBACK),
|
|
|
|
DECL(AL_CHORUS_DELAY),
|
|
|
|
|
2013-10-03 07:55:12 -07:00
|
|
|
DECL(AL_DISTORTION_EDGE),
|
|
|
|
DECL(AL_DISTORTION_GAIN),
|
|
|
|
DECL(AL_DISTORTION_LOWPASS_CUTOFF),
|
|
|
|
DECL(AL_DISTORTION_EQCENTER),
|
|
|
|
DECL(AL_DISTORTION_EQBANDWIDTH),
|
|
|
|
|
|
|
|
DECL(AL_ECHO_DELAY),
|
|
|
|
DECL(AL_ECHO_LRDELAY),
|
|
|
|
DECL(AL_ECHO_DAMPING),
|
|
|
|
DECL(AL_ECHO_FEEDBACK),
|
|
|
|
DECL(AL_ECHO_SPREAD),
|
|
|
|
|
2013-03-13 23:31:12 -07:00
|
|
|
DECL(AL_FLANGER_WAVEFORM),
|
|
|
|
DECL(AL_FLANGER_PHASE),
|
|
|
|
DECL(AL_FLANGER_RATE),
|
|
|
|
DECL(AL_FLANGER_DEPTH),
|
|
|
|
DECL(AL_FLANGER_FEEDBACK),
|
|
|
|
DECL(AL_FLANGER_DELAY),
|
|
|
|
|
2018-05-20 17:23:03 +02:00
|
|
|
DECL(AL_FREQUENCY_SHIFTER_FREQUENCY),
|
|
|
|
DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
|
|
|
|
DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
|
|
|
|
|
2013-10-03 07:55:12 -07:00
|
|
|
DECL(AL_RING_MODULATOR_FREQUENCY),
|
|
|
|
DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
|
|
|
|
DECL(AL_RING_MODULATOR_WAVEFORM),
|
|
|
|
|
2018-03-18 17:47:17 +01:00
|
|
|
DECL(AL_PITCH_SHIFTER_COARSE_TUNE),
|
|
|
|
DECL(AL_PITCH_SHIFTER_FINE_TUNE),
|
|
|
|
|
2013-10-03 07:55:12 -07:00
|
|
|
DECL(AL_COMPRESSOR_ONOFF),
|
|
|
|
|
2013-05-18 01:33:01 -07:00
|
|
|
DECL(AL_EQUALIZER_LOW_GAIN),
|
|
|
|
DECL(AL_EQUALIZER_LOW_CUTOFF),
|
|
|
|
DECL(AL_EQUALIZER_MID1_GAIN),
|
|
|
|
DECL(AL_EQUALIZER_MID1_CENTER),
|
|
|
|
DECL(AL_EQUALIZER_MID1_WIDTH),
|
|
|
|
DECL(AL_EQUALIZER_MID2_GAIN),
|
|
|
|
DECL(AL_EQUALIZER_MID2_CENTER),
|
|
|
|
DECL(AL_EQUALIZER_MID2_WIDTH),
|
|
|
|
DECL(AL_EQUALIZER_HIGH_GAIN),
|
|
|
|
DECL(AL_EQUALIZER_HIGH_CUTOFF),
|
|
|
|
|
2012-04-20 22:38:03 -07:00
|
|
|
DECL(AL_DEDICATED_GAIN),
|
2017-04-21 04:15:08 -07:00
|
|
|
|
2018-09-07 18:45:24 -07:00
|
|
|
DECL(AL_AUTOWAH_ATTACK_TIME),
|
|
|
|
DECL(AL_AUTOWAH_RELEASE_TIME),
|
|
|
|
DECL(AL_AUTOWAH_RESONANCE),
|
2018-07-22 00:48:54 +02:00
|
|
|
DECL(AL_AUTOWAH_PEAK_GAIN),
|
|
|
|
|
2017-04-21 04:15:08 -07:00
|
|
|
DECL(AL_NUM_RESAMPLERS_SOFT),
|
|
|
|
DECL(AL_DEFAULT_RESAMPLER_SOFT),
|
|
|
|
DECL(AL_SOURCE_RESAMPLER_SOFT),
|
|
|
|
DECL(AL_RESAMPLER_NAME_SOFT),
|
2017-05-05 02:41:34 -07:00
|
|
|
|
|
|
|
DECL(AL_SOURCE_SPATIALIZE_SOFT),
|
|
|
|
DECL(AL_AUTO_SOFT),
|
2018-01-20 13:37:43 -08:00
|
|
|
|
|
|
|
DECL(AL_MAP_READ_BIT_SOFT),
|
|
|
|
DECL(AL_MAP_WRITE_BIT_SOFT),
|
2018-01-23 14:33:30 -08:00
|
|
|
DECL(AL_MAP_PERSISTENT_BIT_SOFT),
|
|
|
|
DECL(AL_PRESERVE_DATA_BIT_SOFT),
|
2018-01-23 18:25:59 -08:00
|
|
|
|
|
|
|
DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT),
|
|
|
|
DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT),
|
|
|
|
DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT),
|
|
|
|
DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT),
|
|
|
|
DECL(AL_EVENT_TYPE_ERROR_SOFT),
|
|
|
|
DECL(AL_EVENT_TYPE_PERFORMANCE_SOFT),
|
2018-01-24 18:42:00 -08:00
|
|
|
DECL(AL_EVENT_TYPE_DEPRECATED_SOFT),
|
2007-11-13 18:02:18 -08:00
|
|
|
};
|
2012-04-20 22:38:03 -07:00
|
|
|
#undef DECL
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCchar alcNoError[] = "No Error";
|
|
|
|
constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device";
|
|
|
|
constexpr ALCchar alcErrInvalidContext[] = "Invalid Context";
|
|
|
|
constexpr ALCchar alcErrInvalidEnum[] = "Invalid Enum";
|
|
|
|
constexpr ALCchar alcErrInvalidValue[] = "Invalid Value";
|
|
|
|
constexpr ALCchar alcErrOutOfMemory[] = "Out of Memory";
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* Global variables
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
/* Enumerated device names */
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCchar alcDefaultName[] = "OpenAL Soft\0";
|
2014-03-28 05:40:24 -07:00
|
|
|
|
2018-11-15 04:24:33 -08:00
|
|
|
std::string alcAllDevicesList;
|
|
|
|
std::string alcCaptureDeviceList;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2011-08-20 03:59:46 -07:00
|
|
|
/* Default is always the first in the list */
|
2018-11-14 06:17:47 -08:00
|
|
|
std::string alcDefaultAllDevicesSpecifier;
|
|
|
|
std::string alcCaptureDefaultDeviceSpecifier;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/* Default context extensions */
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALchar alExtList[] =
|
2018-01-20 13:37:43 -08:00
|
|
|
"AL_EXT_ALAW "
|
|
|
|
"AL_EXT_BFORMAT "
|
|
|
|
"AL_EXT_DOUBLE "
|
|
|
|
"AL_EXT_EXPONENT_DISTANCE "
|
|
|
|
"AL_EXT_FLOAT32 "
|
|
|
|
"AL_EXT_IMA4 "
|
|
|
|
"AL_EXT_LINEAR_DISTANCE "
|
|
|
|
"AL_EXT_MCFORMATS "
|
|
|
|
"AL_EXT_MULAW "
|
|
|
|
"AL_EXT_MULAW_BFORMAT "
|
|
|
|
"AL_EXT_MULAW_MCFORMATS "
|
|
|
|
"AL_EXT_OFFSET "
|
|
|
|
"AL_EXT_source_distance_model "
|
|
|
|
"AL_EXT_SOURCE_RADIUS "
|
|
|
|
"AL_EXT_STEREO_ANGLES "
|
|
|
|
"AL_LOKI_quadriphonic "
|
|
|
|
"AL_SOFT_block_alignment "
|
|
|
|
"AL_SOFT_deferred_updates "
|
|
|
|
"AL_SOFT_direct_channels "
|
2018-01-24 19:59:22 -08:00
|
|
|
"AL_SOFTX_events "
|
2018-07-15 21:01:26 -07:00
|
|
|
"AL_SOFTX_filter_gain_ex "
|
2018-01-20 13:37:43 -08:00
|
|
|
"AL_SOFT_gain_clamp_ex "
|
|
|
|
"AL_SOFT_loop_points "
|
|
|
|
"AL_SOFTX_map_buffer "
|
|
|
|
"AL_SOFT_MSADPCM "
|
|
|
|
"AL_SOFT_source_latency "
|
|
|
|
"AL_SOFT_source_length "
|
|
|
|
"AL_SOFT_source_resampler "
|
|
|
|
"AL_SOFT_source_spatialize";
|
2010-01-09 02:48:18 -08:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
std::atomic<ALCenum> LastNullDeviceError{ALC_NO_ERROR};
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/* Thread-local current context */
|
2018-12-08 21:58:44 -08:00
|
|
|
void ReleaseThreadCtx(ALCcontext *context)
|
|
|
|
{
|
|
|
|
auto ref = DecrementRef(&context->ref);
|
|
|
|
TRACEREF("%p decreasing refcount to %u\n", context, ref);
|
|
|
|
ERR("Context %p current for thread being destroyed, possible leak!\n", context);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::atomic<void(*)(ALCcontext*)> ThreadCtxProc{ReleaseThreadCtx};
|
2018-11-18 06:46:50 -08:00
|
|
|
class ThreadCtx {
|
2018-11-16 21:55:11 -08:00
|
|
|
ALCcontext *ctx{nullptr};
|
|
|
|
|
|
|
|
public:
|
|
|
|
~ThreadCtx()
|
|
|
|
{
|
|
|
|
auto destruct = ThreadCtxProc.load();
|
|
|
|
if(destruct && ctx)
|
|
|
|
destruct(ctx);
|
|
|
|
ctx = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ALCcontext *get() const noexcept { return ctx; }
|
|
|
|
void set(ALCcontext *ctx_) noexcept { ctx = ctx_; }
|
2018-11-18 06:46:50 -08:00
|
|
|
};
|
|
|
|
thread_local ThreadCtx LocalContext;
|
2012-04-20 01:18:38 -07:00
|
|
|
/* Process-wide current context */
|
2018-11-14 06:17:47 -08:00
|
|
|
std::atomic<ALCcontext*> GlobalContext{nullptr};
|
2011-07-10 21:30:25 -07:00
|
|
|
|
2011-09-10 01:23:59 -07:00
|
|
|
/* Flag to trap ALC device errors */
|
2018-11-14 06:17:47 -08:00
|
|
|
bool TrapALCError{false};
|
2011-09-10 01:23:59 -07:00
|
|
|
|
2011-08-20 03:59:46 -07:00
|
|
|
/* One-time configuration init control */
|
2018-11-14 06:17:47 -08:00
|
|
|
std::once_flag alc_config_once{};
|
2011-08-20 03:59:46 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/* Default effect that applies to sources that don't have an effect on send 0 */
|
2018-11-14 06:17:47 -08:00
|
|
|
ALeffect DefaultEffect;
|
2012-01-19 19:30:03 -08:00
|
|
|
|
2014-10-12 09:03:08 -07:00
|
|
|
/* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
|
|
|
|
* updates.
|
|
|
|
*/
|
2018-11-14 06:17:47 -08:00
|
|
|
bool SuspendDefers{true};
|
2014-10-12 09:03:08 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* ALC information
|
|
|
|
************************************************/
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCchar alcNoDeviceExtList[] =
|
2012-04-20 01:18:38 -07:00
|
|
|
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
|
|
|
|
"ALC_EXT_thread_local_context ALC_SOFT_loopback";
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCchar alcExtensionList[] =
|
2012-04-20 01:18:38 -07:00
|
|
|
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
|
|
|
|
"ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
|
2018-01-15 06:45:53 -08:00
|
|
|
"ALC_EXT_thread_local_context ALC_SOFT_device_clock ALC_SOFT_HRTF "
|
2017-05-25 04:16:07 -07:00
|
|
|
"ALC_SOFT_loopback ALC_SOFT_output_limiter ALC_SOFT_pause_device";
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCint alcMajorVersion = 1;
|
|
|
|
constexpr ALCint alcMinorVersion = 1;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
constexpr ALCint alcEFXMajorVersion = 1;
|
|
|
|
constexpr ALCint alcEFXMinorVersion = 0;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Device lists
|
|
|
|
************************************************/
|
2018-11-14 06:17:47 -08:00
|
|
|
std::atomic<ALCdevice*> DeviceList{nullptr};
|
2010-12-02 01:05:29 -08:00
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
std::recursive_mutex ListLock;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
/* Mixing thread piority level */
|
|
|
|
ALint RTPrioLevel;
|
|
|
|
|
2018-12-09 15:07:44 -08:00
|
|
|
FILE *gLogFile{stderr};
|
2018-11-14 06:17:47 -08:00
|
|
|
#ifdef _DEBUG
|
2018-12-09 15:07:44 -08:00
|
|
|
LogLevel gLogLevel{LogWarning};
|
2018-11-14 06:17:47 -08:00
|
|
|
#else
|
2018-12-09 15:07:44 -08:00
|
|
|
LogLevel gLogLevel{LogError};
|
2018-11-14 06:17:47 -08:00
|
|
|
#endif
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* Library initialization
|
|
|
|
************************************************/
|
2018-12-08 21:58:44 -08:00
|
|
|
#if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC)
|
|
|
|
BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
|
2008-07-17 18:38:07 -07:00
|
|
|
{
|
2014-04-17 00:56:02 -07:00
|
|
|
switch(reason)
|
2008-07-17 18:38:07 -07:00
|
|
|
{
|
|
|
|
case DLL_PROCESS_ATTACH:
|
2012-03-08 17:42:16 -08:00
|
|
|
/* Pin the DLL so we won't get unloaded until the process terminates */
|
|
|
|
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
2018-12-08 21:58:44 -08:00
|
|
|
(WCHAR*)module, &module);
|
2008-07-17 18:38:07 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2009-09-12 17:29:35 -07:00
|
|
|
#endif
|
|
|
|
|
2011-06-14 09:43:33 -07:00
|
|
|
static void alc_initconfig(void)
|
|
|
|
{
|
|
|
|
const char *devs, *str;
|
2018-01-11 07:19:19 -08:00
|
|
|
int capfilter;
|
2011-09-18 16:16:55 -07:00
|
|
|
float valf;
|
2011-08-19 01:41:16 -07:00
|
|
|
int i, n;
|
2011-06-14 06:57:51 -07:00
|
|
|
|
2011-07-10 21:30:25 -07:00
|
|
|
str = getenv("ALSOFT_LOGLEVEL");
|
|
|
|
if(str)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
long lvl = strtol(str, nullptr, 0);
|
2011-09-20 12:24:23 -07:00
|
|
|
if(lvl >= NoLog && lvl <= LogRef)
|
2018-12-09 15:07:44 -08:00
|
|
|
gLogLevel = static_cast<LogLevel>(lvl);
|
2011-07-10 21:30:25 -07:00
|
|
|
}
|
|
|
|
|
2011-06-17 18:54:05 -07:00
|
|
|
str = getenv("ALSOFT_LOGFILE");
|
|
|
|
if(str && str[0])
|
|
|
|
{
|
2018-11-10 04:27:10 -08:00
|
|
|
#ifdef _WIN32
|
2018-11-14 04:15:44 -08:00
|
|
|
std::wstring wname{utf8_to_wstr(str)};
|
|
|
|
FILE *logfile = _wfopen(wname.c_str(), L"wt");
|
2018-11-10 04:27:10 -08:00
|
|
|
#else
|
|
|
|
FILE *logfile = fopen(str, "wt");
|
|
|
|
#endif
|
2018-12-09 15:07:44 -08:00
|
|
|
if(logfile) gLogFile = logfile;
|
2011-07-13 01:43:00 -07:00
|
|
|
else ERR("Failed to open log file '%s'\n", str);
|
2011-06-17 18:54:05 -07:00
|
|
|
}
|
|
|
|
|
2016-12-21 01:08:33 -08:00
|
|
|
TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION,
|
|
|
|
ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
|
2012-12-04 13:46:51 -08:00
|
|
|
{
|
2018-11-18 07:00:43 -08:00
|
|
|
std::string names;
|
2017-02-28 04:21:16 -08:00
|
|
|
if(BackendListSize > 0)
|
2018-11-18 07:00:43 -08:00
|
|
|
names += BackendList[0].name;
|
2017-02-28 04:21:16 -08:00
|
|
|
for(i = 1;i < BackendListSize;i++)
|
2018-11-18 07:00:43 -08:00
|
|
|
{
|
|
|
|
names += ", ";
|
|
|
|
names += BackendList[i].name;
|
|
|
|
}
|
|
|
|
TRACE("Supported backends: %s\n", names.c_str());
|
2012-12-04 13:46:51 -08:00
|
|
|
}
|
2010-06-08 02:02:48 -07:00
|
|
|
ReadALConfig();
|
|
|
|
|
2014-10-12 09:03:08 -07:00
|
|
|
str = getenv("__ALSOFT_SUSPEND_CONTEXT");
|
|
|
|
if(str && *str)
|
|
|
|
{
|
|
|
|
if(strcasecmp(str, "ignore") == 0)
|
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
SuspendDefers = false;
|
2014-10-12 09:03:08 -07:00
|
|
|
TRACE("Selected context suspend behavior, \"ignore\"\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ERR("Unhandled context suspend behavior setting: \"%s\"\n", str);
|
|
|
|
}
|
|
|
|
|
2012-08-15 01:24:50 -07:00
|
|
|
capfilter = 0;
|
2014-06-04 02:57:13 -07:00
|
|
|
#if defined(HAVE_SSE4_1)
|
2015-10-11 06:38:00 -07:00
|
|
|
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
|
|
|
|
#elif defined(HAVE_SSE3)
|
|
|
|
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
|
2014-06-04 02:57:13 -07:00
|
|
|
#elif defined(HAVE_SSE2)
|
2013-05-22 16:59:20 -07:00
|
|
|
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
|
2014-06-04 02:57:13 -07:00
|
|
|
#elif defined(HAVE_SSE)
|
|
|
|
capfilter |= CPU_CAP_SSE;
|
2012-08-15 01:24:50 -07:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_NEON
|
|
|
|
capfilter |= CPU_CAP_NEON;
|
|
|
|
#endif
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueStr(nullptr, nullptr, "disable-cpu-exts", &str))
|
2012-08-13 10:37:49 -07:00
|
|
|
{
|
|
|
|
if(strcasecmp(str, "all") == 0)
|
|
|
|
capfilter = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *next = str;
|
|
|
|
do {
|
|
|
|
str = next;
|
|
|
|
while(isspace(str[0]))
|
|
|
|
str++;
|
2013-06-16 15:44:41 -07:00
|
|
|
next = strchr(str, ',');
|
|
|
|
|
2012-08-13 10:37:49 -07:00
|
|
|
if(!str[0] || str[0] == ',')
|
|
|
|
continue;
|
|
|
|
|
2018-12-08 21:58:44 -08:00
|
|
|
size_t len{next ? (size_t)(next-str) : strlen(str)};
|
2013-06-16 15:44:41 -07:00
|
|
|
while(len > 0 && isspace(str[len-1]))
|
|
|
|
len--;
|
|
|
|
if(len == 3 && strncasecmp(str, "sse", len) == 0)
|
2012-08-14 03:53:07 -07:00
|
|
|
capfilter &= ~CPU_CAP_SSE;
|
2013-06-16 15:44:41 -07:00
|
|
|
else if(len == 4 && strncasecmp(str, "sse2", len) == 0)
|
2013-05-22 16:59:20 -07:00
|
|
|
capfilter &= ~CPU_CAP_SSE2;
|
2015-10-11 06:38:00 -07:00
|
|
|
else if(len == 4 && strncasecmp(str, "sse3", len) == 0)
|
|
|
|
capfilter &= ~CPU_CAP_SSE3;
|
2014-06-04 02:57:13 -07:00
|
|
|
else if(len == 6 && strncasecmp(str, "sse4.1", len) == 0)
|
|
|
|
capfilter &= ~CPU_CAP_SSE4_1;
|
2013-06-16 15:44:41 -07:00
|
|
|
else if(len == 4 && strncasecmp(str, "neon", len) == 0)
|
2012-08-13 10:37:49 -07:00
|
|
|
capfilter &= ~CPU_CAP_NEON;
|
|
|
|
else
|
|
|
|
WARN("Invalid CPU extension \"%s\"\n", str);
|
|
|
|
} while(next++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FillCPUCaps(capfilter);
|
|
|
|
|
2011-07-23 05:44:55 -07:00
|
|
|
#ifdef _WIN32
|
2011-09-18 16:16:55 -07:00
|
|
|
RTPrioLevel = 1;
|
2011-07-23 05:44:55 -07:00
|
|
|
#else
|
2011-09-18 16:16:55 -07:00
|
|
|
RTPrioLevel = 0;
|
2011-07-23 05:44:55 -07:00
|
|
|
#endif
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueInt(nullptr, nullptr, "rt-prio", &RTPrioLevel);
|
2009-12-01 23:15:09 -08:00
|
|
|
|
2017-08-07 01:38:26 -07:00
|
|
|
aluInit();
|
2015-09-27 20:55:39 -07:00
|
|
|
aluInitMixer();
|
2010-01-11 05:37:20 -08:00
|
|
|
|
2012-04-16 20:13:50 -07:00
|
|
|
str = getenv("ALSOFT_TRAP_ERROR");
|
2018-11-14 04:15:44 -08:00
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
2012-04-16 20:13:50 -07:00
|
|
|
{
|
|
|
|
TrapALError = AL_TRUE;
|
2018-11-14 06:17:47 -08:00
|
|
|
TrapALCError = true;
|
2012-04-16 20:13:50 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str = getenv("ALSOFT_TRAP_AL_ERROR");
|
2018-11-14 04:15:44 -08:00
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
2012-04-16 20:13:50 -07:00
|
|
|
TrapALError = AL_TRUE;
|
2018-11-14 04:15:44 -08:00
|
|
|
TrapALError = GetConfigValueBool(nullptr, nullptr, "trap-al-error", TrapALError);
|
2011-09-10 01:23:59 -07:00
|
|
|
|
2012-04-16 20:13:50 -07:00
|
|
|
str = getenv("ALSOFT_TRAP_ALC_ERROR");
|
2018-11-14 04:15:44 -08:00
|
|
|
if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
|
2018-11-14 06:17:47 -08:00
|
|
|
TrapALCError = true;
|
|
|
|
TrapALCError = !!GetConfigValueBool(nullptr, nullptr, "trap-alc-error", TrapALCError);
|
2012-04-16 20:13:50 -07:00
|
|
|
}
|
2011-09-10 01:12:34 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueFloat(nullptr, "reverb", "boost", &valf))
|
2018-11-14 17:01:19 -08:00
|
|
|
ReverbBoost *= std::pow(10.0f, valf / 20.0f);
|
2011-09-18 16:16:55 -07:00
|
|
|
|
2012-02-19 14:27:00 -08:00
|
|
|
if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) ||
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueStr(nullptr, nullptr, "drivers", &devs))
|
2009-09-12 17:49:08 -07:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
size_t len;
|
|
|
|
const char *next = devs;
|
2010-06-05 19:33:06 -07:00
|
|
|
int endlist, delitem;
|
2009-09-12 17:49:08 -07:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
devs = next;
|
2013-06-16 15:44:41 -07:00
|
|
|
while(isspace(devs[0]))
|
|
|
|
devs++;
|
2009-09-12 17:49:08 -07:00
|
|
|
next = strchr(devs, ',');
|
|
|
|
|
2010-06-05 19:33:06 -07:00
|
|
|
delitem = (devs[0] == '-');
|
|
|
|
if(devs[0] == '-') devs++;
|
|
|
|
|
2009-09-12 17:49:08 -07:00
|
|
|
if(!devs[0] || devs[0] == ',')
|
2009-11-22 01:06:05 -08:00
|
|
|
{
|
|
|
|
endlist = 0;
|
2009-09-12 17:49:08 -07:00
|
|
|
continue;
|
2009-11-22 01:06:05 -08:00
|
|
|
}
|
|
|
|
endlist = 1;
|
2009-09-12 17:49:08 -07:00
|
|
|
|
|
|
|
len = (next ? ((size_t)(next-devs)) : strlen(devs));
|
2013-06-16 15:44:41 -07:00
|
|
|
while(len > 0 && isspace(devs[len-1]))
|
|
|
|
len--;
|
2018-03-09 18:56:24 -08:00
|
|
|
#ifdef HAVE_WASAPI
|
|
|
|
/* HACK: For backwards compatibility, convert backend references of
|
|
|
|
* mmdevapi to wasapi. This should eventually be removed.
|
|
|
|
*/
|
|
|
|
if(len == 8 && strncmp(devs, "mmdevapi", len) == 0)
|
|
|
|
{
|
|
|
|
devs = "wasapi";
|
|
|
|
len = 6;
|
|
|
|
}
|
|
|
|
#endif
|
2017-02-28 04:21:16 -08:00
|
|
|
for(n = i;n < BackendListSize;n++)
|
2009-09-12 17:49:08 -07:00
|
|
|
{
|
|
|
|
if(len == strlen(BackendList[n].name) &&
|
|
|
|
strncmp(BackendList[n].name, devs, len) == 0)
|
|
|
|
{
|
2010-06-05 19:33:06 -07:00
|
|
|
if(delitem)
|
2009-11-22 01:06:05 -08:00
|
|
|
{
|
2017-02-28 04:21:16 -08:00
|
|
|
for(;n+1 < BackendListSize;n++)
|
2010-06-05 19:33:06 -07:00
|
|
|
BackendList[n] = BackendList[n+1];
|
2017-02-28 04:21:16 -08:00
|
|
|
BackendListSize--;
|
2010-06-05 19:33:06 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-08-18 23:58:00 -07:00
|
|
|
struct BackendInfo Bkp = BackendList[n];
|
2017-02-28 04:21:16 -08:00
|
|
|
for(;n > i;n--)
|
2010-06-05 19:33:06 -07:00
|
|
|
BackendList[n] = BackendList[n-1];
|
|
|
|
BackendList[n] = Bkp;
|
|
|
|
|
|
|
|
i++;
|
2009-11-22 01:06:05 -08:00
|
|
|
}
|
|
|
|
break;
|
2009-09-12 17:49:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} while(next++);
|
|
|
|
|
2009-11-22 01:06:05 -08:00
|
|
|
if(endlist)
|
2017-02-28 04:21:16 -08:00
|
|
|
BackendListSize = i;
|
2009-09-12 17:49:08 -07:00
|
|
|
}
|
|
|
|
|
2018-03-08 18:53:49 -08:00
|
|
|
for(n = i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++)
|
2011-08-17 06:15:43 -07:00
|
|
|
{
|
2018-03-08 18:53:49 -08:00
|
|
|
BackendList[n] = BackendList[i];
|
|
|
|
|
2018-11-15 19:15:14 -08:00
|
|
|
BackendFactory &factory = BackendList[n].getFactory();
|
|
|
|
if(!factory.init())
|
2011-08-19 00:23:26 -07:00
|
|
|
{
|
2018-03-08 18:53:49 -08:00
|
|
|
WARN("Failed to initialize backend \"%s\"\n", BackendList[n].name);
|
2011-08-19 01:54:55 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-03-08 18:53:49 -08:00
|
|
|
TRACE("Initialized backend \"%s\"\n", BackendList[n].name);
|
2018-11-15 19:15:14 -08:00
|
|
|
if(!PlaybackBackend.name && factory.querySupport(ALCbackend_Playback))
|
2011-08-19 01:54:55 -07:00
|
|
|
{
|
2018-03-08 18:53:49 -08:00
|
|
|
PlaybackBackend = BackendList[n];
|
2011-08-19 01:54:55 -07:00
|
|
|
TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
|
2011-08-19 00:23:26 -07:00
|
|
|
}
|
2018-11-15 19:15:14 -08:00
|
|
|
if(!CaptureBackend.name && factory.querySupport(ALCbackend_Capture))
|
2011-08-17 06:15:43 -07:00
|
|
|
{
|
2018-03-08 18:53:49 -08:00
|
|
|
CaptureBackend = BackendList[n];
|
2011-08-19 01:54:55 -07:00
|
|
|
TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
|
2011-08-17 06:15:43 -07:00
|
|
|
}
|
2018-03-08 18:53:49 -08:00
|
|
|
n++;
|
2011-08-17 06:15:43 -07:00
|
|
|
}
|
2018-03-08 18:53:49 -08:00
|
|
|
BackendListSize = n;
|
|
|
|
|
2018-11-15 19:42:13 -08:00
|
|
|
LoopbackBackendFactory::getFactory().init();
|
2009-09-12 17:49:08 -07:00
|
|
|
|
2016-04-30 17:14:55 -07:00
|
|
|
if(!PlaybackBackend.name)
|
|
|
|
WARN("No playback backend available!\n");
|
|
|
|
if(!CaptureBackend.name)
|
|
|
|
WARN("No capture backend available!\n");
|
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueStr(nullptr, nullptr, "excludefx", &str))
|
2009-09-12 17:49:08 -07:00
|
|
|
{
|
|
|
|
const char *next = str;
|
|
|
|
do {
|
|
|
|
str = next;
|
|
|
|
next = strchr(str, ',');
|
2009-08-26 23:45:00 -07:00
|
|
|
|
2009-09-12 17:49:08 -07:00
|
|
|
if(!str[0] || next == str)
|
|
|
|
continue;
|
|
|
|
|
2018-12-08 21:58:44 -08:00
|
|
|
size_t len{next ? (size_t)(next-str) : strlen(str)};
|
2018-01-11 09:34:01 -08:00
|
|
|
for(n = 0;n < EFFECTLIST_SIZE;n++)
|
2009-09-12 17:49:08 -07:00
|
|
|
{
|
|
|
|
if(len == strlen(EffectList[n].name) &&
|
|
|
|
strncmp(EffectList[n].name, str, len) == 0)
|
|
|
|
DisabledEffects[EffectList[n].type] = AL_TRUE;
|
|
|
|
}
|
|
|
|
} while(next++);
|
|
|
|
}
|
2012-01-19 19:30:03 -08:00
|
|
|
|
2012-03-13 14:38:09 -07:00
|
|
|
InitEffect(&DefaultEffect);
|
2012-02-19 17:24:01 -08:00
|
|
|
str = getenv("ALSOFT_DEFAULT_REVERB");
|
2018-11-14 04:15:44 -08:00
|
|
|
if((str && str[0]) || ConfigValueStr(nullptr, nullptr, "default-reverb", &str))
|
2012-03-13 15:48:51 -07:00
|
|
|
LoadReverbPreset(str, &DefaultEffect);
|
2009-09-12 17:49:08 -07:00
|
|
|
}
|
2018-11-16 22:41:04 -08:00
|
|
|
#define DO_INITCONFIG() std::call_once(alc_config_once, [](){alc_initconfig();})
|
2009-09-12 17:49:08 -07:00
|
|
|
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Device enumeration
|
|
|
|
************************************************/
|
2018-11-15 04:24:33 -08:00
|
|
|
static void ProbeDevices(std::string *list, struct BackendInfo *backendinfo, enum DevProbe type)
|
2009-08-27 06:09:33 -07:00
|
|
|
{
|
2011-09-10 09:12:02 -07:00
|
|
|
DO_INITCONFIG();
|
|
|
|
|
2018-11-14 06:17:47 -08:00
|
|
|
std::lock_guard<std::recursive_mutex> _{ListLock};
|
2018-11-15 04:24:33 -08:00
|
|
|
list->clear();
|
2017-11-26 09:59:55 -08:00
|
|
|
if(backendinfo->getFactory)
|
2018-11-15 19:15:14 -08:00
|
|
|
backendinfo->getFactory().probe(type, list);
|
2009-08-27 06:09:33 -07:00
|
|
|
}
|
2012-05-09 16:28:16 -07:00
|
|
|
static void ProbeAllDevicesList(void)
|
2015-10-28 18:10:12 -07:00
|
|
|
{ ProbeDevices(&alcAllDevicesList, &PlaybackBackend, ALL_DEVICE_PROBE); }
|
2011-08-20 03:59:46 -07:00
|
|
|
static void ProbeCaptureDeviceList(void)
|
2015-10-28 18:10:12 -07:00
|
|
|
{ ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
|
2009-08-27 06:09:33 -07:00
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Device format information
|
|
|
|
************************************************/
|
2011-05-15 04:03:15 -07:00
|
|
|
const ALCchar *DevFmtTypeString(enum DevFmtType type)
|
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
|
|
|
case DevFmtByte: return "Signed Byte";
|
|
|
|
case DevFmtUByte: return "Unsigned Byte";
|
|
|
|
case DevFmtShort: return "Signed Short";
|
|
|
|
case DevFmtUShort: return "Unsigned Short";
|
2012-02-14 11:44:57 -08:00
|
|
|
case DevFmtInt: return "Signed Int";
|
|
|
|
case DevFmtUInt: return "Unsigned Int";
|
2011-05-15 04:03:15 -07:00
|
|
|
case DevFmtFloat: return "Float";
|
|
|
|
}
|
|
|
|
return "(unknown type)";
|
|
|
|
}
|
|
|
|
const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
|
|
|
|
{
|
|
|
|
switch(chans)
|
|
|
|
{
|
|
|
|
case DevFmtMono: return "Mono";
|
|
|
|
case DevFmtStereo: return "Stereo";
|
2011-05-24 14:34:53 -07:00
|
|
|
case DevFmtQuad: return "Quadraphonic";
|
2011-05-15 04:03:15 -07:00
|
|
|
case DevFmtX51: return "5.1 Surround";
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtX51Rear: return "5.1 Surround (Rear)";
|
2011-05-15 04:03:15 -07:00
|
|
|
case DevFmtX61: return "6.1 Surround";
|
|
|
|
case DevFmtX71: return "7.1 Surround";
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D: return "Ambisonic 3D";
|
2011-05-15 04:03:15 -07:00
|
|
|
}
|
|
|
|
return "(unknown channels)";
|
|
|
|
}
|
|
|
|
|
2017-01-18 07:13:23 -08:00
|
|
|
ALsizei BytesFromDevFmt(enum DevFmtType type)
|
2010-12-04 19:50:00 -08:00
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
|
|
|
case DevFmtByte: return sizeof(ALbyte);
|
|
|
|
case DevFmtUByte: return sizeof(ALubyte);
|
|
|
|
case DevFmtShort: return sizeof(ALshort);
|
|
|
|
case DevFmtUShort: return sizeof(ALushort);
|
2012-02-14 11:44:57 -08:00
|
|
|
case DevFmtInt: return sizeof(ALint);
|
|
|
|
case DevFmtUInt: return sizeof(ALuint);
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtFloat: return sizeof(ALfloat);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-12 18:26:07 -07:00
|
|
|
ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder)
|
2010-12-04 19:50:00 -08:00
|
|
|
{
|
|
|
|
switch(chans)
|
|
|
|
{
|
|
|
|
case DevFmtMono: return 1;
|
|
|
|
case DevFmtStereo: return 2;
|
|
|
|
case DevFmtQuad: return 4;
|
|
|
|
case DevFmtX51: return 6;
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtX51Rear: return 6;
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtX61: return 7;
|
|
|
|
case DevFmtX71: return 8;
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D: return (ambiorder >= 3) ? 16 :
|
|
|
|
(ambiorder == 2) ? 9 :
|
|
|
|
(ambiorder == 1) ? 4 : 1;
|
2010-12-04 19:50:00 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2016-09-06 09:09:25 -07:00
|
|
|
static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans,
|
|
|
|
enum DevFmtType *type)
|
2010-12-04 19:50:00 -08:00
|
|
|
{
|
2011-09-18 19:06:19 -07:00
|
|
|
static const struct {
|
|
|
|
ALenum format;
|
|
|
|
enum DevFmtChannels channels;
|
|
|
|
enum DevFmtType type;
|
|
|
|
} list[] = {
|
|
|
|
{ AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_MONO16, DevFmtMono, DevFmtShort },
|
|
|
|
{ AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat },
|
|
|
|
|
|
|
|
{ AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort },
|
|
|
|
{ AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat },
|
|
|
|
|
|
|
|
{ AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort },
|
|
|
|
{ AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat },
|
|
|
|
|
|
|
|
{ AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort },
|
|
|
|
{ AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat },
|
|
|
|
|
|
|
|
{ AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort },
|
|
|
|
{ AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat },
|
|
|
|
|
|
|
|
{ AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte },
|
|
|
|
{ AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort },
|
|
|
|
{ AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat },
|
|
|
|
};
|
|
|
|
ALuint i;
|
|
|
|
|
2012-02-19 12:07:40 -08:00
|
|
|
for(i = 0;i < COUNTOF(list);i++)
|
2010-12-04 19:50:00 -08:00
|
|
|
{
|
2011-09-18 19:06:19 -07:00
|
|
|
if(list[i].format == format)
|
|
|
|
{
|
|
|
|
*chans = list[i].channels;
|
|
|
|
*type = list[i].type;
|
2010-12-04 19:50:00 -08:00
|
|
|
return AL_TRUE;
|
2011-09-18 19:06:19 -07:00
|
|
|
}
|
2010-12-04 19:50:00 -08:00
|
|
|
}
|
2011-09-18 19:06:19 -07:00
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
return AL_FALSE;
|
|
|
|
}
|
|
|
|
|
2016-09-06 09:09:25 -07:00
|
|
|
static ALCboolean IsValidALCType(ALCenum type)
|
2011-07-02 02:51:33 -07:00
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
2011-11-01 16:00:47 -07:00
|
|
|
case ALC_BYTE_SOFT:
|
|
|
|
case ALC_UNSIGNED_BYTE_SOFT:
|
|
|
|
case ALC_SHORT_SOFT:
|
|
|
|
case ALC_UNSIGNED_SHORT_SOFT:
|
|
|
|
case ALC_INT_SOFT:
|
|
|
|
case ALC_UNSIGNED_INT_SOFT:
|
|
|
|
case ALC_FLOAT_SOFT:
|
2011-07-02 02:51:33 -07:00
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
|
|
|
|
2016-09-06 09:09:25 -07:00
|
|
|
static ALCboolean IsValidALCChannels(ALCenum channels)
|
2011-07-02 02:51:33 -07:00
|
|
|
{
|
|
|
|
switch(channels)
|
|
|
|
{
|
2011-11-01 16:00:47 -07:00
|
|
|
case ALC_MONO_SOFT:
|
|
|
|
case ALC_STEREO_SOFT:
|
|
|
|
case ALC_QUAD_SOFT:
|
|
|
|
case ALC_5POINT1_SOFT:
|
|
|
|
case ALC_6POINT1_SOFT:
|
|
|
|
case ALC_7POINT1_SOFT:
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
case ALC_BFORMAT3D_SOFT:
|
2011-07-02 02:51:33 -07:00
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
|
|
|
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
static ALCboolean IsValidAmbiLayout(ALCenum layout)
|
|
|
|
{
|
|
|
|
switch(layout)
|
|
|
|
{
|
|
|
|
case ALC_ACN_SOFT:
|
|
|
|
case ALC_FUMA_SOFT:
|
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ALCboolean IsValidAmbiScaling(ALCenum scaling)
|
|
|
|
{
|
|
|
|
switch(scaling)
|
|
|
|
{
|
|
|
|
case ALC_N3D_SOFT:
|
|
|
|
case ALC_SN3D_SOFT:
|
|
|
|
case ALC_FUMA_SOFT:
|
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Miscellaneous ALC helpers
|
|
|
|
************************************************/
|
2015-09-05 01:32:12 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/* SetDefaultWFXChannelOrder
|
|
|
|
*
|
|
|
|
* Sets the default channel order used by WaveFormatEx.
|
|
|
|
*/
|
|
|
|
void SetDefaultWFXChannelOrder(ALCdevice *device)
|
|
|
|
{
|
2017-03-17 15:45:39 -07:00
|
|
|
ALsizei i;
|
2012-11-04 04:41:11 -08:00
|
|
|
|
2014-11-07 02:18:24 -08:00
|
|
|
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[i] = InvalidChannel;
|
2012-11-04 04:41:11 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
switch(device->FmtChans)
|
|
|
|
{
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtMono:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontCenter;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtStereo:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtQuad:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = BackLeft;
|
|
|
|
device->RealOut.ChannelName[3] = BackRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtX51:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[3] = LFE;
|
|
|
|
device->RealOut.ChannelName[4] = SideLeft;
|
|
|
|
device->RealOut.ChannelName[5] = SideRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtX51Rear:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[3] = LFE;
|
|
|
|
device->RealOut.ChannelName[4] = BackLeft;
|
|
|
|
device->RealOut.ChannelName[5] = BackRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtX61:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[3] = LFE;
|
|
|
|
device->RealOut.ChannelName[4] = BackCenter;
|
|
|
|
device->RealOut.ChannelName[5] = SideLeft;
|
|
|
|
device->RealOut.ChannelName[6] = SideRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
|
|
|
case DevFmtX71:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[3] = LFE;
|
|
|
|
device->RealOut.ChannelName[4] = BackLeft;
|
|
|
|
device->RealOut.ChannelName[5] = BackRight;
|
|
|
|
device->RealOut.ChannelName[6] = SideLeft;
|
|
|
|
device->RealOut.ChannelName[7] = SideRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
break;
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D:
|
2016-07-29 21:55:43 -07:00
|
|
|
device->RealOut.ChannelName[0] = Aux0;
|
2018-11-18 08:01:50 -08:00
|
|
|
if(device->mAmbiOrder > 0)
|
2017-04-12 18:26:07 -07:00
|
|
|
{
|
|
|
|
device->RealOut.ChannelName[1] = Aux1;
|
|
|
|
device->RealOut.ChannelName[2] = Aux2;
|
|
|
|
device->RealOut.ChannelName[3] = Aux3;
|
|
|
|
}
|
2018-11-18 08:01:50 -08:00
|
|
|
if(device->mAmbiOrder > 1)
|
2017-04-12 18:26:07 -07:00
|
|
|
{
|
|
|
|
device->RealOut.ChannelName[4] = Aux4;
|
|
|
|
device->RealOut.ChannelName[5] = Aux5;
|
|
|
|
device->RealOut.ChannelName[6] = Aux6;
|
|
|
|
device->RealOut.ChannelName[7] = Aux7;
|
|
|
|
device->RealOut.ChannelName[8] = Aux8;
|
|
|
|
}
|
2018-11-18 08:01:50 -08:00
|
|
|
if(device->mAmbiOrder > 2)
|
2017-04-12 18:26:07 -07:00
|
|
|
{
|
|
|
|
device->RealOut.ChannelName[9] = Aux9;
|
|
|
|
device->RealOut.ChannelName[10] = Aux10;
|
|
|
|
device->RealOut.ChannelName[11] = Aux11;
|
|
|
|
device->RealOut.ChannelName[12] = Aux12;
|
|
|
|
device->RealOut.ChannelName[13] = Aux13;
|
|
|
|
device->RealOut.ChannelName[14] = Aux14;
|
|
|
|
device->RealOut.ChannelName[15] = Aux15;
|
|
|
|
}
|
2016-07-29 21:55:43 -07:00
|
|
|
break;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* SetDefaultChannelOrder
|
|
|
|
*
|
|
|
|
* Sets the default channel order used by most non-WaveFormatEx-based APIs.
|
|
|
|
*/
|
|
|
|
void SetDefaultChannelOrder(ALCdevice *device)
|
|
|
|
{
|
2017-03-17 15:45:39 -07:00
|
|
|
ALsizei i;
|
2012-11-04 04:41:11 -08:00
|
|
|
|
2014-11-07 02:18:24 -08:00
|
|
|
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[i] = InvalidChannel;
|
2012-11-04 04:41:11 -08:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
switch(device->FmtChans)
|
|
|
|
{
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtX51Rear:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = BackLeft;
|
|
|
|
device->RealOut.ChannelName[3] = BackRight;
|
|
|
|
device->RealOut.ChannelName[4] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[5] = LFE;
|
2014-11-07 00:54:16 -08:00
|
|
|
return;
|
|
|
|
case DevFmtX71:
|
2016-03-10 01:04:28 -08:00
|
|
|
device->RealOut.ChannelName[0] = FrontLeft;
|
|
|
|
device->RealOut.ChannelName[1] = FrontRight;
|
|
|
|
device->RealOut.ChannelName[2] = BackLeft;
|
|
|
|
device->RealOut.ChannelName[3] = BackRight;
|
|
|
|
device->RealOut.ChannelName[4] = FrontCenter;
|
|
|
|
device->RealOut.ChannelName[5] = LFE;
|
|
|
|
device->RealOut.ChannelName[6] = SideLeft;
|
|
|
|
device->RealOut.ChannelName[7] = SideRight;
|
2014-11-07 00:54:16 -08:00
|
|
|
return;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/* Same as WFX order */
|
|
|
|
case DevFmtMono:
|
|
|
|
case DevFmtStereo:
|
|
|
|
case DevFmtQuad:
|
2014-11-07 00:54:16 -08:00
|
|
|
case DevFmtX51:
|
2012-04-20 01:18:38 -07:00
|
|
|
case DevFmtX61:
|
2017-04-12 18:26:07 -07:00
|
|
|
case DevFmtAmbi3D:
|
2014-09-10 16:52:54 -07:00
|
|
|
SetDefaultWFXChannelOrder(device);
|
2012-04-20 01:18:38 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-12 09:03:08 -07:00
|
|
|
/* ALCcontext_DeferUpdates
|
|
|
|
*
|
|
|
|
* Defers/suspends updates for the given context's listener and sources. This
|
|
|
|
* does *NOT* stop mixing, but rather prevents certain property changes from
|
|
|
|
* taking effect.
|
|
|
|
*/
|
2017-03-19 13:48:40 -07:00
|
|
|
void ALCcontext_DeferUpdates(ALCcontext *context)
|
2014-10-12 09:03:08 -07:00
|
|
|
{
|
2018-11-20 10:45:01 -08:00
|
|
|
context->DeferUpdates.store(true);
|
2014-10-12 09:03:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ALCcontext_ProcessUpdates
|
|
|
|
*
|
|
|
|
* Resumes update processing after being deferred.
|
|
|
|
*/
|
|
|
|
void ALCcontext_ProcessUpdates(ALCcontext *context)
|
|
|
|
{
|
2018-11-26 21:37:58 -08:00
|
|
|
std::lock_guard<std::mutex> _{context->PropLock};
|
2018-11-20 10:45:01 -08:00
|
|
|
if(context->DeferUpdates.exchange(false))
|
2014-10-12 09:03:08 -07:00
|
|
|
{
|
2016-08-23 19:37:26 -07:00
|
|
|
/* Tell the mixer to stop applying updates, then wait for any active
|
|
|
|
* updating to finish, before providing updates.
|
|
|
|
*/
|
2018-11-21 07:52:17 -08:00
|
|
|
context->HoldUpdates.store(AL_TRUE);
|
|
|
|
while((context->UpdateCount.load(std::memory_order_acquire)&1) != 0)
|
2018-11-26 23:06:49 -08:00
|
|
|
std::this_thread::yield();
|
2016-08-23 19:37:26 -07:00
|
|
|
|
2018-11-20 10:45:01 -08:00
|
|
|
if(!context->PropsClean.test_and_set(std::memory_order_acq_rel))
|
2017-09-27 09:36:34 -07:00
|
|
|
UpdateContextProps(context);
|
2018-11-20 10:45:01 -08:00
|
|
|
if(!context->Listener.PropsClean.test_and_set(std::memory_order_acq_rel))
|
2017-09-27 09:36:34 -07:00
|
|
|
UpdateListenerProps(context);
|
2016-08-25 06:17:36 -07:00
|
|
|
UpdateAllEffectSlotProps(context);
|
Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.
The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.
There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
|
|
|
UpdateAllSourceProps(context);
|
2016-08-23 19:37:26 -07:00
|
|
|
|
|
|
|
/* Now with all updates declared, let the mixer continue applying them
|
|
|
|
* so they all happen at once.
|
|
|
|
*/
|
2018-11-21 07:52:17 -08:00
|
|
|
context->HoldUpdates.store(AL_FALSE);
|
2014-10-12 09:03:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-14 11:58:51 -07:00
|
|
|
/* alcSetError
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Stores the latest ALC device error
|
2011-09-14 11:58:51 -07:00
|
|
|
*/
|
|
|
|
static void alcSetError(ALCdevice *device, ALCenum errorCode)
|
|
|
|
{
|
2017-02-27 20:56:34 -08:00
|
|
|
WARN("Error generated on device %p, code 0x%04x\n", device, errorCode);
|
2011-09-14 11:58:51 -07:00
|
|
|
if(TrapALCError)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* DebugBreak() will cause an exception if there is no debugger */
|
|
|
|
if(IsDebuggerPresent())
|
|
|
|
DebugBreak();
|
|
|
|
#elif defined(SIGTRAP)
|
2011-09-30 20:46:18 -07:00
|
|
|
raise(SIGTRAP);
|
2011-09-14 11:58:51 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if(device)
|
2018-11-21 07:52:17 -08:00
|
|
|
device->LastError.store(errorCode);
|
2011-09-14 11:58:51 -07:00
|
|
|
else
|
2018-11-14 06:17:47 -08:00
|
|
|
LastNullDeviceError.store(errorCode);
|
2011-09-14 11:58:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-25 10:29:38 -07:00
|
|
|
static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
2017-05-27 03:36:34 -07:00
|
|
|
{
|
2018-09-25 10:04:14 -07:00
|
|
|
return CompressorInit(device->RealOut.NumChannels, device->Frequency,
|
|
|
|
AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
|
2018-09-25 10:29:38 -07:00
|
|
|
0.0f, 0.0f, threshold, INFINITY, 0.0f, 0.020f, 0.200f);
|
2017-05-27 03:36:34 -07:00
|
|
|
}
|
|
|
|
|
2014-02-01 16:37:11 -08:00
|
|
|
/* UpdateClockBase
|
|
|
|
*
|
|
|
|
* Updates the device's base clock time with however many samples have been
|
|
|
|
* done. This is used so frequency changes on the device don't cause the time
|
2017-02-28 23:18:51 -08:00
|
|
|
* to jump forward or back. Must not be called while the device is running/
|
|
|
|
* mixing.
|
2014-02-01 16:37:11 -08:00
|
|
|
*/
|
|
|
|
static inline void UpdateClockBase(ALCdevice *device)
|
|
|
|
{
|
2018-11-22 12:53:16 -08:00
|
|
|
using std::chrono::seconds;
|
|
|
|
using std::chrono::nanoseconds;
|
|
|
|
using std::chrono::duration_cast;
|
|
|
|
|
2017-02-28 23:18:51 -08:00
|
|
|
IncrementRef(&device->MixCount);
|
2018-11-22 12:53:16 -08:00
|
|
|
device->ClockBase += duration_cast<nanoseconds>(seconds{device->SamplesDone}) /
|
|
|
|
device->Frequency;
|
2014-02-01 16:37:11 -08:00
|
|
|
device->SamplesDone = 0;
|
2017-02-28 23:18:51 -08:00
|
|
|
IncrementRef(&device->MixCount);
|
2014-02-01 16:37:11 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 09:00:01 -07:00
|
|
|
/* UpdateDeviceParams
|
2010-08-09 00:28:48 -07:00
|
|
|
*
|
2011-09-10 09:00:01 -07:00
|
|
|
* Updates device parameters according to the attribute list (caller is
|
|
|
|
* responsible for holding the list lock).
|
2010-08-09 00:28:48 -07:00
|
|
|
*/
|
2012-01-25 20:00:34 -08:00
|
|
|
static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2015-09-05 01:32:12 -07:00
|
|
|
enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
|
2017-02-21 16:31:59 -08:00
|
|
|
enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
|
2018-10-03 13:48:04 -07:00
|
|
|
ALCenum gainLimiter = device->LimiterState;
|
2017-02-21 16:31:59 -08:00
|
|
|
const ALsizei old_sends = device->NumAuxSends;
|
|
|
|
ALsizei new_sends = device->NumAuxSends;
|
2012-01-20 12:20:24 -08:00
|
|
|
enum DevFmtChannels oldChans;
|
|
|
|
enum DevFmtType oldType;
|
2017-02-21 16:31:59 -08:00
|
|
|
ALboolean update_failed;
|
|
|
|
ALCsizei hrtf_id = -1;
|
|
|
|
ALCcontext *context;
|
2012-01-20 12:20:24 -08:00
|
|
|
ALCuint oldFreq;
|
2017-04-30 04:21:48 -07:00
|
|
|
int val;
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2010-08-12 17:24:55 -07:00
|
|
|
// Check for attributes
|
2012-02-25 18:03:33 -08:00
|
|
|
if(device->Type == Loopback)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
ALCsizei numMono, numStereo, numSends;
|
|
|
|
ALCenum alayout = AL_NONE;
|
|
|
|
ALCenum ascale = AL_NONE;
|
|
|
|
ALCenum schans = AL_NONE;
|
|
|
|
ALCenum stype = AL_NONE;
|
|
|
|
ALCsizei attrIdx = 0;
|
|
|
|
ALCsizei aorder = 0;
|
|
|
|
ALCuint freq = 0;
|
2011-06-15 06:45:51 -07:00
|
|
|
|
2012-02-25 18:03:33 -08:00
|
|
|
if(!attrList)
|
|
|
|
{
|
|
|
|
WARN("Missing attributes for loopback device\n");
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
}
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2010-08-12 17:24:55 -07:00
|
|
|
numMono = device->NumMonoSources;
|
|
|
|
numStereo = device->NumStereoSources;
|
2017-02-21 16:31:59 -08:00
|
|
|
numSends = old_sends;
|
2010-08-12 17:24:55 -07:00
|
|
|
|
2016-04-17 17:22:03 -07:00
|
|
|
#define TRACE_ATTR(a, v) TRACE("Loopback %s = %d\n", #a, v)
|
2010-08-12 17:24:55 -07:00
|
|
|
while(attrList[attrIdx])
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2017-04-16 16:21:11 -07:00
|
|
|
switch(attrList[attrIdx])
|
2010-08-12 17:24:55 -07:00
|
|
|
{
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_FORMAT_CHANNELS_SOFT:
|
|
|
|
schans = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, schans);
|
|
|
|
if(!IsValidALCChannels(schans))
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_FORMAT_TYPE_SOFT:
|
|
|
|
stype = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype);
|
|
|
|
if(!IsValidALCType(stype))
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_FREQUENCY:
|
|
|
|
freq = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_FREQUENCY, freq);
|
|
|
|
if(freq < MIN_OUTPUT_RATE)
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_AMBISONIC_LAYOUT_SOFT:
|
|
|
|
alayout = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout);
|
|
|
|
if(!IsValidAmbiLayout(alayout))
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_AMBISONIC_SCALING_SOFT:
|
|
|
|
ascale = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale);
|
|
|
|
if(!IsValidAmbiScaling(ascale))
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_AMBISONIC_ORDER_SOFT:
|
|
|
|
aorder = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder);
|
|
|
|
if(aorder < 1 || aorder > MAX_AMBI_ORDER)
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
break;
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_MONO_SOURCES:
|
|
|
|
numMono = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_MONO_SOURCES, numMono);
|
|
|
|
numMono = maxi(numMono, 0);
|
|
|
|
break;
|
2017-04-14 22:56:54 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_STEREO_SOURCES:
|
|
|
|
numStereo = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
|
|
|
|
numStereo = maxi(numStereo, 0);
|
|
|
|
break;
|
2010-08-12 17:24:55 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_MAX_AUXILIARY_SENDS:
|
|
|
|
numSends = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
|
|
|
|
numSends = clampi(numSends, 0, MAX_SENDS);
|
|
|
|
break;
|
2010-08-12 17:24:55 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_HRTF_SOFT:
|
|
|
|
TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
|
|
|
|
if(attrList[attrIdx + 1] == ALC_FALSE)
|
|
|
|
hrtf_appreq = Hrtf_Disable;
|
|
|
|
else if(attrList[attrIdx + 1] == ALC_TRUE)
|
|
|
|
hrtf_appreq = Hrtf_Enable;
|
|
|
|
else
|
|
|
|
hrtf_appreq = Hrtf_Default;
|
|
|
|
break;
|
2013-05-31 19:29:32 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_HRTF_ID_SOFT:
|
|
|
|
hrtf_id = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
|
|
|
|
break;
|
|
|
|
|
2017-04-30 04:21:48 -07:00
|
|
|
case ALC_OUTPUT_LIMITER_SOFT:
|
|
|
|
gainLimiter = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
|
|
|
|
break;
|
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
default:
|
|
|
|
TRACE("Loopback 0x%04X = %d (0x%x)\n", attrList[attrIdx],
|
|
|
|
attrList[attrIdx + 1], attrList[attrIdx + 1]);
|
|
|
|
break;
|
2016-04-17 17:22:03 -07:00
|
|
|
}
|
2015-10-07 03:29:53 -07:00
|
|
|
|
2010-08-12 17:24:55 -07:00
|
|
|
attrIdx += 2;
|
2010-08-09 00:28:48 -07:00
|
|
|
}
|
2016-04-17 17:22:03 -07:00
|
|
|
#undef TRACE_ATTR
|
2010-08-09 00:28:48 -07:00
|
|
|
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
if(!schans || !stype || !freq)
|
2012-01-25 19:54:55 -08:00
|
|
|
{
|
2012-02-25 18:03:33 -08:00
|
|
|
WARN("Missing format for loopback device\n");
|
|
|
|
return ALC_INVALID_VALUE;
|
2012-01-25 19:54:55 -08:00
|
|
|
}
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
if(schans == ALC_BFORMAT3D_SOFT && (!alayout || !ascale || !aorder))
|
|
|
|
{
|
|
|
|
WARN("Missing ambisonic info for loopback device\n");
|
|
|
|
return ALC_INVALID_VALUE;
|
|
|
|
}
|
2012-02-25 18:03:33 -08:00
|
|
|
|
|
|
|
if((device->Flags&DEVICE_RUNNING))
|
2013-11-02 17:30:28 -07:00
|
|
|
V0(device->Backend,stop)();
|
2015-09-05 01:32:12 -07:00
|
|
|
device->Flags &= ~DEVICE_RUNNING;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2014-08-29 19:34:20 -07:00
|
|
|
UpdateClockBase(device);
|
|
|
|
|
2017-04-12 18:26:07 -07:00
|
|
|
device->Frequency = freq;
|
2018-11-14 04:15:44 -08:00
|
|
|
device->FmtChans = static_cast<enum DevFmtChannels>(schans);
|
|
|
|
device->FmtType = static_cast<enum DevFmtType>(stype);
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
if(schans == ALC_BFORMAT3D_SOFT)
|
|
|
|
{
|
2018-11-18 08:01:50 -08:00
|
|
|
device->mAmbiOrder = aorder;
|
2018-11-20 22:47:24 -08:00
|
|
|
device->mAmbiLayout = static_cast<AmbiLayout>(alayout);
|
|
|
|
device->mAmbiScale = static_cast<AmbiNorm>(ascale);
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
}
|
2017-04-14 22:56:54 -07:00
|
|
|
|
|
|
|
if(numMono > INT_MAX-numStereo)
|
|
|
|
numMono = INT_MAX-numStereo;
|
|
|
|
numMono += numStereo;
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueInt(nullptr, nullptr, "sources", &numMono))
|
2017-04-14 22:56:54 -07:00
|
|
|
{
|
|
|
|
if(numMono <= 0)
|
|
|
|
numMono = 256;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
numMono = maxi(numMono, 256);
|
|
|
|
numStereo = mini(numStereo, numMono);
|
|
|
|
numMono -= numStereo;
|
|
|
|
device->SourcesMax = numMono + numStereo;
|
|
|
|
|
2012-02-25 18:03:33 -08:00
|
|
|
device->NumMonoSources = numMono;
|
|
|
|
device->NumStereoSources = numStereo;
|
2017-02-21 16:31:59 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueInt(nullptr, nullptr, "sends", &new_sends))
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
|
2017-02-21 17:19:02 -08:00
|
|
|
else
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
new_sends = numSends;
|
2012-02-25 18:03:33 -08:00
|
|
|
}
|
|
|
|
else if(attrList && attrList[0])
|
|
|
|
{
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
ALCsizei numMono, numStereo, numSends;
|
|
|
|
ALCsizei attrIdx = 0;
|
|
|
|
ALCuint freq;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
|
|
|
/* If a context is already running on the device, stop playback so the
|
|
|
|
* device attributes can be updated. */
|
|
|
|
if((device->Flags&DEVICE_RUNNING))
|
2014-01-15 16:30:23 -08:00
|
|
|
V0(device->Backend,stop)();
|
2012-02-25 18:03:33 -08:00
|
|
|
device->Flags &= ~DEVICE_RUNNING;
|
|
|
|
|
2017-02-21 16:31:59 -08:00
|
|
|
UpdateClockBase(device);
|
|
|
|
|
2012-02-25 18:03:33 -08:00
|
|
|
freq = device->Frequency;
|
|
|
|
numMono = device->NumMonoSources;
|
|
|
|
numStereo = device->NumStereoSources;
|
2017-02-21 16:31:59 -08:00
|
|
|
numSends = old_sends;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2016-04-17 17:22:03 -07:00
|
|
|
#define TRACE_ATTR(a, v) TRACE("%s = %d\n", #a, v)
|
2012-02-25 18:03:33 -08:00
|
|
|
while(attrList[attrIdx])
|
2011-09-18 16:16:55 -07:00
|
|
|
{
|
2017-04-16 16:21:11 -07:00
|
|
|
switch(attrList[attrIdx])
|
2012-02-25 18:03:33 -08:00
|
|
|
{
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_FREQUENCY:
|
|
|
|
freq = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_FREQUENCY, freq);
|
|
|
|
device->Flags |= DEVICE_FREQUENCY_REQUEST;
|
|
|
|
break;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_MONO_SOURCES:
|
|
|
|
numMono = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_MONO_SOURCES, numMono);
|
|
|
|
numMono = maxi(numMono, 0);
|
|
|
|
break;
|
2017-04-14 22:56:54 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_STEREO_SOURCES:
|
|
|
|
numStereo = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
|
|
|
|
numStereo = maxi(numStereo, 0);
|
|
|
|
break;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_MAX_AUXILIARY_SENDS:
|
|
|
|
numSends = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
|
|
|
|
numSends = clampi(numSends, 0, MAX_SENDS);
|
|
|
|
break;
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_HRTF_SOFT:
|
|
|
|
TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
|
|
|
|
if(attrList[attrIdx + 1] == ALC_FALSE)
|
|
|
|
hrtf_appreq = Hrtf_Disable;
|
|
|
|
else if(attrList[attrIdx + 1] == ALC_TRUE)
|
|
|
|
hrtf_appreq = Hrtf_Enable;
|
|
|
|
else
|
|
|
|
hrtf_appreq = Hrtf_Default;
|
|
|
|
break;
|
2013-05-31 19:29:32 -07:00
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
case ALC_HRTF_ID_SOFT:
|
|
|
|
hrtf_id = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
|
|
|
|
break;
|
|
|
|
|
2017-04-30 04:21:48 -07:00
|
|
|
case ALC_OUTPUT_LIMITER_SOFT:
|
|
|
|
gainLimiter = attrList[attrIdx + 1];
|
|
|
|
TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
|
|
|
|
break;
|
|
|
|
|
2017-04-16 16:21:11 -07:00
|
|
|
default:
|
|
|
|
TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx],
|
|
|
|
attrList[attrIdx + 1], attrList[attrIdx + 1]);
|
|
|
|
break;
|
2016-04-17 17:22:03 -07:00
|
|
|
}
|
2015-10-07 03:29:53 -07:00
|
|
|
|
2012-02-25 18:03:33 -08:00
|
|
|
attrIdx += 2;
|
2011-09-18 16:16:55 -07:00
|
|
|
}
|
2016-04-17 17:22:03 -07:00
|
|
|
#undef TRACE_ATTR
|
2012-02-25 18:03:33 -08:00
|
|
|
|
2018-11-18 18:45:45 -08:00
|
|
|
ConfigValueUInt(device->DeviceName.c_str(), nullptr, "frequency", &freq);
|
2012-02-25 18:03:33 -08:00
|
|
|
freq = maxu(freq, MIN_OUTPUT_RATE);
|
|
|
|
|
2010-08-12 17:24:55 -07:00
|
|
|
device->UpdateSize = (ALuint64)device->UpdateSize * freq /
|
|
|
|
device->Frequency;
|
2014-01-26 01:34:39 -08:00
|
|
|
/* SSE and Neon do best with the update size being a multiple of 4 */
|
|
|
|
if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
|
2012-09-20 14:54:28 -07:00
|
|
|
device->UpdateSize = (device->UpdateSize+3)&~3;
|
2010-08-12 17:24:55 -07:00
|
|
|
|
|
|
|
device->Frequency = freq;
|
2017-04-14 22:56:54 -07:00
|
|
|
|
|
|
|
if(numMono > INT_MAX-numStereo)
|
|
|
|
numMono = INT_MAX-numStereo;
|
|
|
|
numMono += numStereo;
|
2018-11-18 18:45:45 -08:00
|
|
|
if(ConfigValueInt(device->DeviceName.c_str(), nullptr, "sources", &numMono))
|
2017-04-14 22:56:54 -07:00
|
|
|
{
|
|
|
|
if(numMono <= 0)
|
|
|
|
numMono = 256;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
numMono = maxi(numMono, 256);
|
|
|
|
numStereo = mini(numStereo, numMono);
|
|
|
|
numMono -= numStereo;
|
|
|
|
device->SourcesMax = numMono + numStereo;
|
|
|
|
|
2010-08-12 17:24:55 -07:00
|
|
|
device->NumMonoSources = numMono;
|
|
|
|
device->NumStereoSources = numStereo;
|
2017-02-21 16:31:59 -08:00
|
|
|
|
2018-11-18 18:45:45 -08:00
|
|
|
if(ConfigValueInt(device->DeviceName.c_str(), nullptr, "sends", &new_sends))
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
|
2017-02-21 17:19:02 -08:00
|
|
|
else
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
new_sends = numSends;
|
2010-08-09 00:28:48 -07:00
|
|
|
}
|
|
|
|
|
2011-06-15 01:59:07 -07:00
|
|
|
if((device->Flags&DEVICE_RUNNING))
|
2012-01-25 20:00:34 -08:00
|
|
|
return ALC_NO_ERROR;
|
2012-01-17 15:55:37 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
device->Uhj_Encoder = nullptr;
|
|
|
|
device->Bs2b = nullptr;
|
2016-02-26 16:09:06 -08:00
|
|
|
|
2018-11-21 05:06:31 -08:00
|
|
|
device->ChannelDelay.clear();
|
|
|
|
device->ChannelDelay.shrink_to_fit();
|
2017-02-28 21:01:13 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
device->Dry.Buffer = nullptr;
|
2016-03-09 23:43:57 -08:00
|
|
|
device->Dry.NumChannels = 0;
|
2018-11-14 04:15:44 -08:00
|
|
|
device->FOAOut.Buffer = nullptr;
|
2016-07-05 14:18:17 -07:00
|
|
|
device->FOAOut.NumChannels = 0;
|
2018-11-14 04:15:44 -08:00
|
|
|
device->RealOut.Buffer = nullptr;
|
2016-03-09 22:57:38 -08:00
|
|
|
device->RealOut.NumChannels = 0;
|
2018-11-19 06:22:09 -08:00
|
|
|
device->MixBuffer.clear();
|
|
|
|
device->MixBuffer.shrink_to_fit();
|
2014-11-22 04:20:17 -08:00
|
|
|
|
2014-02-01 16:37:11 -08:00
|
|
|
UpdateClockBase(device);
|
2018-11-22 12:53:16 -08:00
|
|
|
device->FixedLatency = std::chrono::nanoseconds::zero();
|
2014-02-01 16:37:11 -08:00
|
|
|
|
2017-05-23 00:02:04 -07:00
|
|
|
device->DitherSeed = DITHER_RNG_SEED;
|
|
|
|
|
2016-04-14 14:24:52 -07:00
|
|
|
/*************************************************************************
|
|
|
|
* Update device format request if HRTF is requested
|
|
|
|
*/
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_DISABLED_SOFT;
|
2015-09-04 20:16:48 -07:00
|
|
|
if(device->Type != Loopback)
|
2013-05-31 19:29:32 -07:00
|
|
|
{
|
2015-09-04 20:16:48 -07:00
|
|
|
const char *hrtf;
|
2018-11-18 18:45:45 -08:00
|
|
|
if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf", &hrtf))
|
2015-05-15 23:28:03 -07:00
|
|
|
{
|
2015-09-04 20:16:48 -07:00
|
|
|
if(strcasecmp(hrtf, "true") == 0)
|
2015-09-05 01:32:12 -07:00
|
|
|
hrtf_userreq = Hrtf_Enable;
|
2015-09-04 20:16:48 -07:00
|
|
|
else if(strcasecmp(hrtf, "false") == 0)
|
2015-09-05 01:32:12 -07:00
|
|
|
hrtf_userreq = Hrtf_Disable;
|
2015-09-04 20:16:48 -07:00
|
|
|
else if(strcasecmp(hrtf, "auto") != 0)
|
|
|
|
ERR("Unexpected hrtf value: %s\n", hrtf);
|
|
|
|
}
|
|
|
|
|
2015-09-05 14:22:19 -07:00
|
|
|
if(hrtf_userreq == Hrtf_Enable || (hrtf_userreq != Hrtf_Disable && hrtf_appreq == Hrtf_Enable))
|
2015-09-04 20:16:48 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
struct Hrtf *hrtf = nullptr;
|
2018-11-18 19:19:35 -08:00
|
|
|
if(device->HrtfList.empty())
|
2018-11-18 18:45:45 -08:00
|
|
|
device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
|
2018-11-18 19:19:35 -08:00
|
|
|
if(!device->HrtfList.empty())
|
2015-10-06 06:48:53 -07:00
|
|
|
{
|
2018-11-18 19:19:35 -08:00
|
|
|
if(hrtf_id >= 0 && (size_t)hrtf_id < device->HrtfList.size())
|
|
|
|
hrtf = GetLoadedHrtf(device->HrtfList[hrtf_id].hrtf);
|
2017-04-05 12:27:30 -07:00
|
|
|
else
|
2018-11-18 19:19:35 -08:00
|
|
|
hrtf = GetLoadedHrtf(device->HrtfList.front().hrtf);
|
2017-04-05 12:27:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if(hrtf)
|
|
|
|
{
|
2017-04-05 11:29:58 -07:00
|
|
|
device->FmtChans = DevFmtStereo;
|
|
|
|
device->Frequency = hrtf->sampleRate;
|
2015-09-05 14:22:19 -07:00
|
|
|
device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST;
|
2017-04-06 13:00:29 -07:00
|
|
|
if(device->HrtfHandle)
|
|
|
|
Hrtf_DecRef(device->HrtfHandle);
|
|
|
|
device->HrtfHandle = hrtf;
|
2015-10-06 06:48:53 -07:00
|
|
|
}
|
2015-09-05 14:22:19 -07:00
|
|
|
else
|
2015-09-04 20:16:48 -07:00
|
|
|
{
|
2016-04-14 14:24:52 -07:00
|
|
|
hrtf_userreq = Hrtf_Default;
|
|
|
|
hrtf_appreq = Hrtf_Disable;
|
2017-03-10 10:47:43 -08:00
|
|
|
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
2015-09-04 20:16:48 -07:00
|
|
|
}
|
2015-05-15 23:28:03 -07:00
|
|
|
}
|
2014-11-23 08:38:33 -08:00
|
|
|
}
|
2013-05-31 19:29:32 -07:00
|
|
|
|
2014-08-29 19:34:20 -07:00
|
|
|
oldFreq = device->Frequency;
|
|
|
|
oldChans = device->FmtChans;
|
|
|
|
oldType = device->FmtType;
|
|
|
|
|
|
|
|
TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u update size x%d\n",
|
2014-11-21 14:40:10 -08:00
|
|
|
(device->Flags&DEVICE_CHANNELS_REQUEST)?"*":"", DevFmtChannelsString(device->FmtChans),
|
|
|
|
(device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)?"*":"", DevFmtTypeString(device->FmtType),
|
|
|
|
(device->Flags&DEVICE_FREQUENCY_REQUEST)?"*":"", device->Frequency,
|
|
|
|
device->UpdateSize, device->NumUpdates
|
|
|
|
);
|
2014-08-29 19:34:20 -07:00
|
|
|
|
2013-11-02 17:30:28 -07:00
|
|
|
if(V0(device->Backend,reset)() == ALC_FALSE)
|
2012-01-25 20:00:34 -08:00
|
|
|
return ALC_INVALID_DEVICE;
|
2012-01-17 15:55:37 -08:00
|
|
|
|
2012-02-15 11:28:19 -08:00
|
|
|
if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST))
|
2012-01-20 12:20:24 -08:00
|
|
|
{
|
2012-02-15 11:28:19 -08:00
|
|
|
ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans),
|
|
|
|
DevFmtChannelsString(device->FmtChans));
|
2012-01-20 12:20:24 -08:00
|
|
|
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
|
2011-05-29 02:56:00 -07:00
|
|
|
}
|
2012-02-15 16:26:32 -08:00
|
|
|
if(device->FmtType != oldType && (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
|
|
|
|
{
|
|
|
|
ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType),
|
|
|
|
DevFmtTypeString(device->FmtType));
|
|
|
|
device->Flags &= ~DEVICE_SAMPLE_TYPE_REQUEST;
|
|
|
|
}
|
2012-02-15 11:28:19 -08:00
|
|
|
if(device->Frequency != oldFreq && (device->Flags&DEVICE_FREQUENCY_REQUEST))
|
2012-01-20 12:20:24 -08:00
|
|
|
{
|
2012-02-15 11:28:19 -08:00
|
|
|
ERR("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency);
|
2012-01-20 12:20:24 -08:00
|
|
|
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
|
|
|
|
}
|
|
|
|
|
2014-11-22 04:51:34 -08:00
|
|
|
if((device->UpdateSize&3) != 0)
|
|
|
|
{
|
|
|
|
if((CPUCapFlags&CPU_CAP_SSE))
|
|
|
|
WARN("SSE performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
|
|
|
|
if((CPUCapFlags&CPU_CAP_NEON))
|
|
|
|
WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
|
|
|
|
}
|
|
|
|
|
2016-04-14 15:25:12 -07:00
|
|
|
TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
|
|
|
|
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
|
|
|
|
device->Frequency, device->UpdateSize, device->NumUpdates
|
|
|
|
);
|
|
|
|
|
|
|
|
aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
|
2017-02-20 16:57:25 -08:00
|
|
|
TRACE("Channel config, Dry: %d, FOA: %d, Real: %d\n", device->Dry.NumChannels,
|
|
|
|
device->FOAOut.NumChannels, device->RealOut.NumChannels);
|
2014-11-21 14:35:19 -08:00
|
|
|
|
2016-03-15 05:08:05 -07:00
|
|
|
/* Allocate extra channels for any post-filter output. */
|
2018-11-19 06:22:09 -08:00
|
|
|
ALsizei num_chans{device->Dry.NumChannels + device->FOAOut.NumChannels +
|
|
|
|
device->RealOut.NumChannels};
|
2017-02-19 17:45:27 -08:00
|
|
|
|
2018-11-19 06:22:09 -08:00
|
|
|
TRACE("Allocating %d channels, " SZFMT " bytes\n", num_chans,
|
|
|
|
num_chans*sizeof(device->MixBuffer[0]));
|
|
|
|
device->MixBuffer.resize(num_chans);
|
2014-11-21 13:45:57 -08:00
|
|
|
|
2018-11-19 06:22:09 -08:00
|
|
|
device->Dry.Buffer = &reinterpret_cast<ALfloat(&)[BUFFERSIZE]>(device->MixBuffer[0]);
|
2017-02-20 16:57:25 -08:00
|
|
|
if(device->RealOut.NumChannels != 0)
|
|
|
|
device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels +
|
|
|
|
device->FOAOut.NumChannels;
|
2016-03-22 17:52:20 -07:00
|
|
|
else
|
|
|
|
{
|
2017-02-20 16:57:25 -08:00
|
|
|
device->RealOut.Buffer = device->Dry.Buffer;
|
|
|
|
device->RealOut.NumChannels = device->Dry.NumChannels;
|
2016-03-22 17:52:20 -07:00
|
|
|
}
|
2017-02-19 17:45:27 -08:00
|
|
|
|
2017-02-20 16:57:25 -08:00
|
|
|
if(device->FOAOut.NumChannels != 0)
|
|
|
|
device->FOAOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels;
|
2017-02-19 17:45:27 -08:00
|
|
|
else
|
|
|
|
{
|
2017-02-20 16:57:25 -08:00
|
|
|
device->FOAOut.Buffer = device->Dry.Buffer;
|
|
|
|
device->FOAOut.NumChannels = device->Dry.NumChannels;
|
2017-02-19 17:45:27 -08:00
|
|
|
}
|
2016-03-22 17:52:20 -07:00
|
|
|
|
2017-02-21 16:31:59 -08:00
|
|
|
device->NumAuxSends = new_sends;
|
2017-04-14 22:56:54 -07:00
|
|
|
TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
|
|
|
|
device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
|
|
|
|
device->AuxiliaryEffectSlotMax, device->NumAuxSends);
|
2017-04-26 18:38:09 -07:00
|
|
|
|
2017-06-19 00:17:11 -07:00
|
|
|
device->DitherDepth = 0.0f;
|
2018-11-18 18:45:45 -08:00
|
|
|
if(GetConfigValueBool(device->DeviceName.c_str(), nullptr, "dither", 1))
|
2017-06-17 23:09:51 -07:00
|
|
|
{
|
|
|
|
ALint depth = 0;
|
2018-11-18 18:45:45 -08:00
|
|
|
ConfigValueInt(device->DeviceName.c_str(), nullptr, "dither-depth", &depth);
|
2017-06-17 23:09:51 -07:00
|
|
|
if(depth <= 0)
|
|
|
|
{
|
|
|
|
switch(device->FmtType)
|
|
|
|
{
|
|
|
|
case DevFmtByte:
|
|
|
|
case DevFmtUByte:
|
|
|
|
depth = 8;
|
|
|
|
break;
|
|
|
|
case DevFmtShort:
|
|
|
|
case DevFmtUShort:
|
|
|
|
depth = 16;
|
|
|
|
break;
|
|
|
|
case DevFmtInt:
|
|
|
|
case DevFmtUInt:
|
|
|
|
case DevFmtFloat:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-05-23 19:49:49 -07:00
|
|
|
|
|
|
|
if(depth > 0)
|
|
|
|
{
|
2018-10-03 13:51:21 -07:00
|
|
|
depth = clampi(depth, 2, 24);
|
2018-11-14 17:01:19 -08:00
|
|
|
device->DitherDepth = std::pow(2.0f, (ALfloat)(depth-1));
|
2018-05-23 19:49:49 -07:00
|
|
|
}
|
2017-06-17 23:09:51 -07:00
|
|
|
}
|
2017-06-19 00:17:11 -07:00
|
|
|
if(!(device->DitherDepth > 0.0f))
|
|
|
|
TRACE("Dithering disabled\n");
|
|
|
|
else
|
2018-11-18 08:03:22 -08:00
|
|
|
TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device->DitherDepth)+0.5f)+1,
|
2017-06-19 00:17:11 -07:00
|
|
|
device->DitherDepth);
|
2017-06-17 23:09:51 -07:00
|
|
|
|
2018-10-03 13:48:04 -07:00
|
|
|
device->LimiterState = gainLimiter;
|
2018-11-18 18:45:45 -08:00
|
|
|
if(ConfigValueBool(device->DeviceName.c_str(), nullptr, "output-limiter", &val))
|
2017-05-11 11:04:25 -07:00
|
|
|
gainLimiter = val ? ALC_TRUE : ALC_FALSE;
|
2018-10-03 13:48:04 -07:00
|
|
|
|
2017-05-11 11:04:25 -07:00
|
|
|
/* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and
|
2018-10-03 13:48:04 -07:00
|
|
|
* 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).
|
2017-05-11 11:04:25 -07:00
|
|
|
*/
|
2018-10-03 13:48:04 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2017-05-11 11:04:25 -07:00
|
|
|
if(gainLimiter != ALC_FALSE)
|
2017-05-05 07:38:26 -07:00
|
|
|
{
|
2018-09-25 10:29:38 -07:00
|
|
|
ALfloat thrshld = 1.0f;
|
|
|
|
switch(device->FmtType)
|
2017-05-27 03:36:34 -07:00
|
|
|
{
|
2018-09-25 10:29:38 -07:00
|
|
|
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;
|
2017-05-27 03:36:34 -07:00
|
|
|
}
|
2018-09-25 10:29:38 -07:00
|
|
|
if(device->DitherDepth > 0.0f)
|
|
|
|
thrshld -= 1.0f / device->DitherDepth;
|
|
|
|
|
2018-11-21 16:46:52 -08:00
|
|
|
device->Limiter.reset(CreateDeviceLimiter(device, std::log10(thrshld) * 20.0f));
|
2018-11-22 12:53:16 -08:00
|
|
|
/* Convert the lookahead from samples to nanosamples to nanoseconds. */
|
|
|
|
device->FixedLatency += std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
|
|
std::chrono::seconds(GetCompressorLookAhead(device->Limiter.get()))
|
|
|
|
) / device->Frequency;
|
2017-05-05 07:38:26 -07:00
|
|
|
}
|
2017-05-11 11:04:25 -07:00
|
|
|
else
|
2018-11-14 04:15:44 -08:00
|
|
|
device->Limiter = nullptr;
|
2017-05-11 16:29:05 -07:00
|
|
|
TRACE("Output limiter %s\n", device->Limiter ? "enabled" : "disabled");
|
2017-04-26 18:38:09 -07:00
|
|
|
|
2018-02-10 15:50:05 -08:00
|
|
|
aluSelectPostProcess(device);
|
|
|
|
|
2018-11-22 12:53:16 -08:00
|
|
|
TRACE("Fixed device latency: %ldns\n", (long)device->FixedLatency.count());
|
2018-09-25 23:05:27 -07:00
|
|
|
|
2017-04-26 18:38:09 -07:00
|
|
|
/* Need to delay returning failure until replacement Send arrays have been
|
|
|
|
* allocated with the appropriate size.
|
|
|
|
*/
|
2017-02-21 16:31:59 -08:00
|
|
|
update_failed = AL_FALSE;
|
2018-11-21 09:07:02 -08:00
|
|
|
FPUCtl mixer_mode{};
|
2018-11-21 07:52:17 -08:00
|
|
|
context = device->ContextList.load();
|
2011-08-28 19:28:41 -07:00
|
|
|
while(context)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2017-07-13 21:30:05 -07:00
|
|
|
if(context->DefaultSlot)
|
|
|
|
{
|
2018-11-20 05:01:08 -08:00
|
|
|
ALeffectslot *slot = context->DefaultSlot.get();
|
2018-11-19 22:34:26 -08:00
|
|
|
EffectState *state = slot->Effect.State;
|
2017-07-13 21:30:05 -07:00
|
|
|
|
2018-11-19 22:34:26 -08:00
|
|
|
state->mOutBuffer = device->Dry.Buffer;
|
|
|
|
state->mOutChannels = device->Dry.NumChannels;
|
|
|
|
if(state->deviceUpdate(device) == AL_FALSE)
|
2017-07-13 21:30:05 -07:00
|
|
|
update_failed = AL_TRUE;
|
|
|
|
else
|
2017-09-27 11:13:18 -07:00
|
|
|
UpdateEffectSlotProps(slot, context);
|
2017-07-13 21:30:05 -07:00
|
|
|
}
|
|
|
|
|
2018-11-26 21:37:58 -08:00
|
|
|
std::unique_lock<std::mutex> proplock{context->PropLock};
|
2018-11-26 21:50:48 -08:00
|
|
|
std::unique_lock<std::mutex> slotlock{context->EffectSlotLock};
|
2018-11-18 02:39:27 -08:00
|
|
|
for(auto &slot : context->EffectSlotList)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2018-11-19 22:34:26 -08:00
|
|
|
EffectState *state = slot->Effect.State;
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2018-11-19 22:34:26 -08:00
|
|
|
state->mOutBuffer = device->Dry.Buffer;
|
|
|
|
state->mOutChannels = device->Dry.NumChannels;
|
|
|
|
if(state->deviceUpdate(device) == AL_FALSE)
|
2017-02-21 16:31:59 -08:00
|
|
|
update_failed = AL_TRUE;
|
|
|
|
else
|
2018-11-20 10:01:20 -08:00
|
|
|
UpdateEffectSlotProps(slot.get(), context);
|
2010-08-09 00:28:48 -07:00
|
|
|
}
|
2018-11-21 05:35:47 -08:00
|
|
|
slotlock.unlock();
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2018-11-26 21:50:48 -08:00
|
|
|
std::unique_lock<std::mutex> srclock{context->SourceLock};
|
2018-11-18 02:15:31 -08:00
|
|
|
for(auto &sublist : context->SourceList)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2018-11-18 02:15:31 -08:00
|
|
|
uint64_t usemask = ~sublist.FreeMask;
|
2018-01-27 13:02:17 -08:00
|
|
|
while(usemask)
|
2010-08-09 00:28:48 -07:00
|
|
|
{
|
2018-01-27 13:02:17 -08:00
|
|
|
ALsizei idx = CTZ64(usemask);
|
2018-11-18 02:15:31 -08:00
|
|
|
ALsource *source = sublist.Sources + idx;
|
2017-02-21 16:31:59 -08:00
|
|
|
|
2018-01-27 13:02:17 -08:00
|
|
|
usemask &= ~(U64(1) << idx);
|
|
|
|
|
|
|
|
if(old_sends != device->NumAuxSends)
|
2017-02-21 16:31:59 -08:00
|
|
|
{
|
2018-01-27 13:02:17 -08:00
|
|
|
ALsizei s;
|
|
|
|
for(s = device->NumAuxSends;s < old_sends;s++)
|
|
|
|
{
|
|
|
|
if(source->Send[s].Slot)
|
|
|
|
DecrementRef(&source->Send[s].Slot->ref);
|
2018-11-14 04:15:44 -08:00
|
|
|
source->Send[s].Slot = nullptr;
|
2018-01-27 13:02:17 -08:00
|
|
|
}
|
2018-11-20 12:25:15 -08:00
|
|
|
source->Send.resize(device->NumAuxSends);
|
|
|
|
source->Send.shrink_to_fit();
|
2018-01-27 13:02:17 -08:00
|
|
|
for(s = old_sends;s < device->NumAuxSends;s++)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
source->Send[s].Slot = nullptr;
|
2018-01-27 13:02:17 -08:00
|
|
|
source->Send[s].Gain = 1.0f;
|
|
|
|
source->Send[s].GainHF = 1.0f;
|
|
|
|
source->Send[s].HFReference = LOWPASSFREQREF;
|
|
|
|
source->Send[s].GainLF = 1.0f;
|
|
|
|
source->Send[s].LFReference = HIGHPASSFREQREF;
|
|
|
|
}
|
2017-02-21 16:31:59 -08:00
|
|
|
}
|
2017-02-14 19:59:39 -08:00
|
|
|
|
2018-11-20 10:45:01 -08:00
|
|
|
source->PropsClean.clear(std::memory_order_release);
|
2018-01-27 13:02:17 -08:00
|
|
|
}
|
2017-04-17 21:16:01 -07:00
|
|
|
}
|
2017-09-27 11:13:18 -07:00
|
|
|
|
|
|
|
/* Clear any pre-existing voice property structs, in case the number of
|
|
|
|
* auxiliary sends is changing. Active sources will have updates
|
|
|
|
* respecified in UpdateAllSourceProps.
|
|
|
|
*/
|
2018-11-23 16:16:31 -08:00
|
|
|
ALvoiceProps *vprops{context->FreeVoiceProps.exchange(nullptr, std::memory_order_acq_rel)};
|
2017-09-27 11:13:18 -07:00
|
|
|
while(vprops)
|
|
|
|
{
|
2018-11-30 16:56:23 -08:00
|
|
|
ALvoiceProps *next = vprops->next.load(std::memory_order_relaxed);
|
|
|
|
delete vprops;
|
2017-09-27 11:13:18 -07:00
|
|
|
vprops = next;
|
|
|
|
}
|
|
|
|
|
2017-04-17 21:16:01 -07:00
|
|
|
AllocateVoices(context, context->MaxVoices, old_sends);
|
2018-11-23 16:16:31 -08:00
|
|
|
auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed);
|
|
|
|
std::for_each(context->Voices, voices_end,
|
|
|
|
[device](ALvoice *voice) -> void
|
|
|
|
{
|
2018-11-30 16:56:23 -08:00
|
|
|
delete voice->Update.exchange(nullptr, std::memory_order_acq_rel);
|
2017-04-17 21:16:01 -07:00
|
|
|
|
2018-11-29 22:49:01 -08:00
|
|
|
if(voice->SourceID.load(std::memory_order_acquire) == 0u)
|
2018-11-23 16:16:31 -08:00
|
|
|
return;
|
2017-03-10 04:35:32 -08:00
|
|
|
|
2018-11-23 16:16:31 -08:00
|
|
|
if(device->AvgSpeakerDist > 0.0f)
|
|
|
|
{
|
|
|
|
/* Reinitialize the NFC filters for new parameters. */
|
|
|
|
ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
|
|
|
|
(device->AvgSpeakerDist * device->Frequency);
|
|
|
|
std::for_each(voice->Direct.Params, voice->Direct.Params+voice->NumChannels,
|
2018-12-05 15:20:52 -08:00
|
|
|
[w1](DirectParams ¶ms) noexcept -> void
|
|
|
|
{ params.NFCtrlFilter.init(0.0f, w1); }
|
2018-11-23 16:16:31 -08:00
|
|
|
);
|
|
|
|
}
|
2017-03-10 04:35:32 -08:00
|
|
|
}
|
2018-11-23 16:16:31 -08:00
|
|
|
);
|
2018-11-21 06:41:49 -08:00
|
|
|
srclock.unlock();
|
2011-08-28 19:28:41 -07:00
|
|
|
|
2018-11-20 10:45:01 -08:00
|
|
|
context->PropsClean.test_and_set(std::memory_order_release);
|
2017-09-27 09:36:34 -07:00
|
|
|
UpdateContextProps(context);
|
2018-11-20 10:45:01 -08:00
|
|
|
context->Listener.PropsClean.test_and_set(std::memory_order_release);
|
2016-11-23 01:31:13 -08:00
|
|
|
UpdateListenerProps(context);
|
2017-02-14 19:59:39 -08:00
|
|
|
UpdateAllSourceProps(context);
|
2014-03-19 13:14:11 -07:00
|
|
|
|
2018-11-21 07:52:17 -08:00
|
|
|
context = context->next.load(std::memory_order_relaxed);
|
2010-08-09 00:28:48 -07:00
|
|
|
}
|
2018-11-21 09:07:02 -08:00
|
|
|
mixer_mode.leave();
|
2017-02-21 16:31:59 -08:00
|
|
|
if(update_failed)
|
|
|
|
return ALC_INVALID_DEVICE;
|
2012-03-05 07:11:09 -08:00
|
|
|
|
2014-01-15 16:44:12 -08:00
|
|
|
if(!(device->Flags&DEVICE_PAUSED))
|
|
|
|
{
|
|
|
|
if(V0(device->Backend,start)() == ALC_FALSE)
|
|
|
|
return ALC_INVALID_DEVICE;
|
|
|
|
device->Flags |= DEVICE_RUNNING;
|
|
|
|
}
|
2010-08-09 00:28:48 -07:00
|
|
|
|
2012-01-25 20:00:34 -08:00
|
|
|
return ALC_NO_ERROR;
|
2010-08-09 00:28:48 -07:00
|
|
|
}
|
|
|
|
|
2018-01-28 18:03:54 -08:00
|
|
|
|
2018-11-18 07:33:42 -08:00
|
|
|
ALCdevice_struct::ALCdevice_struct(DeviceType type)
|
|
|
|
: Type{type}
|
2018-01-28 18:03:54 -08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-11-18 07:33:42 -08:00
|
|
|
/* ALCdevice_struct::~ALCdevice_struct
|
2011-09-10 09:00:01 -07:00
|
|
|
*
|
|
|
|
* Frees the device structure, and destroys any objects the app failed to
|
2011-09-10 20:52:19 -07:00
|
|
|
* delete. Called once there's no more references on the device.
|
2011-09-10 09:00:01 -07:00
|
|
|
*/
|
2018-11-18 07:33:42 -08:00
|
|
|
ALCdevice_struct::~ALCdevice_struct()
|
2011-09-10 02:43:07 -07:00
|
|
|
{
|
2018-11-18 07:33:42 -08:00
|
|
|
TRACE("%p\n", this);
|
2011-09-10 03:01:24 -07:00
|
|
|
|
2018-11-25 17:51:39 -08:00
|
|
|
DELETE_OBJ(Backend);
|
2018-11-18 07:33:42 -08:00
|
|
|
Backend = nullptr;
|
2012-08-18 14:49:42 -07:00
|
|
|
|
2018-11-28 11:30:44 -08:00
|
|
|
size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
|
|
|
|
[](size_t cur, const BufferSubList &sublist) noexcept -> size_t
|
|
|
|
{ return cur + POPCNT64(~sublist.FreeMask); }
|
|
|
|
)};
|
2018-11-25 15:30:32 -08:00
|
|
|
if(count > 0)
|
|
|
|
WARN(SZFMT " Buffer%s not deleted\n", count, (count==1)?"":"s");
|
2011-09-10 02:43:07 -07:00
|
|
|
|
2018-11-28 11:30:44 -08:00
|
|
|
count = std::accumulate(EffectList.cbegin(), EffectList.cend(), size_t{0u},
|
|
|
|
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
|
|
|
{ return cur + POPCNT64(~sublist.FreeMask); }
|
|
|
|
);
|
2018-11-25 16:13:07 -08:00
|
|
|
if(count > 0)
|
|
|
|
WARN(SZFMT " Effect%s not deleted\n", count, (count==1)?"":"s");
|
2011-09-10 02:43:07 -07:00
|
|
|
|
2018-11-28 11:30:44 -08:00
|
|
|
count = std::accumulate(FilterList.cbegin(), FilterList.cend(), size_t{0u},
|
|
|
|
[](size_t cur, const FilterSubList &sublist) noexcept -> size_t
|
|
|
|
{ return cur + POPCNT64(~sublist.FreeMask); }
|
|
|
|
);
|
2018-11-25 16:13:07 -08:00
|
|
|
if(count > 0)
|
|
|
|
WARN(SZFMT " Filter%s not deleted\n", count, (count==1)?"":"s");
|
2018-11-25 17:51:39 -08:00
|
|
|
|
2018-11-18 07:33:42 -08:00
|
|
|
if(HrtfHandle)
|
|
|
|
Hrtf_DecRef(HrtfHandle);
|
|
|
|
HrtfHandle = nullptr;
|
2011-09-10 02:43:07 -07:00
|
|
|
}
|
|
|
|
|
2011-09-10 09:00:01 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
static void ALCdevice_IncRef(ALCdevice *device)
|
2011-09-10 03:01:24 -07:00
|
|
|
{
|
2018-11-19 02:17:06 -08:00
|
|
|
auto ref = IncrementRef(&device->ref);
|
2011-09-20 12:24:23 -07:00
|
|
|
TRACEREF("%p increasing refcount to %u\n", device, ref);
|
2011-09-10 03:01:24 -07:00
|
|
|
}
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
static void ALCdevice_DecRef(ALCdevice *device)
|
2011-09-10 02:43:07 -07:00
|
|
|
{
|
2018-11-19 02:17:06 -08:00
|
|
|
auto ref = DecrementRef(&device->ref);
|
2011-09-20 12:24:23 -07:00
|
|
|
TRACEREF("%p decreasing refcount to %u\n", device, ref);
|
2018-11-18 07:33:42 -08:00
|
|
|
if(ref == 0) delete device;
|
2011-09-10 02:43:07 -07:00
|
|
|
}
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
/* Simple RAII device reference. Takes the reference of the provided ALCdevice,
|
|
|
|
* and decrements it when leaving scope. Movable (transfer reference) but not
|
|
|
|
* copyable (no new references).
|
|
|
|
*/
|
|
|
|
class DeviceRef {
|
|
|
|
ALCdevice *mDev{nullptr};
|
|
|
|
|
|
|
|
void reset() noexcept
|
|
|
|
{
|
|
|
|
if(mDev)
|
|
|
|
ALCdevice_DecRef(mDev);
|
|
|
|
mDev = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
DeviceRef() noexcept = default;
|
|
|
|
DeviceRef(DeviceRef&& rhs) noexcept : mDev{rhs.mDev}
|
|
|
|
{ rhs.mDev = nullptr; }
|
|
|
|
explicit DeviceRef(ALCdevice *dev) noexcept : mDev(dev) { }
|
|
|
|
~DeviceRef() { reset(); }
|
|
|
|
|
|
|
|
DeviceRef& operator=(const DeviceRef&) = delete;
|
|
|
|
DeviceRef& operator=(DeviceRef&& rhs) noexcept
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
mDev = rhs.mDev;
|
|
|
|
rhs.mDev = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() const noexcept { return mDev != nullptr; }
|
|
|
|
|
|
|
|
ALCdevice* operator->() noexcept { return mDev; }
|
|
|
|
ALCdevice* get() noexcept { return mDev; }
|
|
|
|
|
|
|
|
ALCdevice* release() noexcept
|
|
|
|
{
|
|
|
|
ALCdevice *ret{mDev};
|
|
|
|
mDev = nullptr;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-09-10 05:19:08 -07:00
|
|
|
/* VerifyDevice
|
|
|
|
*
|
2018-11-23 17:54:12 -08:00
|
|
|
* Checks if the device handle is valid, and returns a new reference if so.
|
2011-09-10 05:19:08 -07:00
|
|
|
*/
|
2018-11-23 17:54:12 -08:00
|
|
|
static DeviceRef VerifyDevice(ALCdevice *device)
|
2011-09-10 05:19:08 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::lock_guard<std::recursive_mutex> _{ListLock};
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *tmpDevice{DeviceList.load()};
|
2015-10-19 18:48:33 -07:00
|
|
|
while(tmpDevice)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
if(tmpDevice == device)
|
2015-10-19 18:48:33 -07:00
|
|
|
{
|
|
|
|
ALCdevice_IncRef(tmpDevice);
|
2018-11-23 17:54:12 -08:00
|
|
|
return DeviceRef{tmpDevice};
|
2015-10-19 18:48:33 -07:00
|
|
|
}
|
2018-11-21 07:52:17 -08:00
|
|
|
tmpDevice = tmpDevice->next.load(std::memory_order_relaxed);
|
2015-10-19 18:48:33 -07:00
|
|
|
}
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
return DeviceRef{};
|
2011-09-10 05:19:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-20 05:01:08 -08:00
|
|
|
ALCcontext_struct::ALCcontext_struct(ALCdevice *device)
|
|
|
|
: Device{device}
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-09-10 09:00:01 -07:00
|
|
|
/* InitContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Initializes context fields
|
2011-09-10 09:00:01 -07:00
|
|
|
*/
|
2012-04-19 23:00:58 -07:00
|
|
|
static ALvoid InitContext(ALCcontext *Context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-17 23:41:11 -08:00
|
|
|
ALlistener &listener = Context->Listener;
|
2017-03-27 23:16:23 -07:00
|
|
|
struct ALeffectslotArray *auxslots;
|
2016-05-11 18:40:17 -07:00
|
|
|
|
2012-04-19 23:00:58 -07:00
|
|
|
//Validate Context
|
2017-07-13 23:13:02 -07:00
|
|
|
if(Context->DefaultSlot)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
auxslots = static_cast<ALeffectslotArray*>(al_calloc(DEF_ALIGN,
|
|
|
|
FAM_SIZE(struct ALeffectslotArray, slot, 1)));
|
2017-07-13 23:13:02 -07:00
|
|
|
auxslots->count = 1;
|
2018-11-20 05:01:08 -08:00
|
|
|
auxslots->slot[0] = Context->DefaultSlot.get();
|
2017-07-13 23:13:02 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
auxslots = static_cast<ALeffectslotArray*>(al_calloc(DEF_ALIGN,
|
|
|
|
sizeof(struct ALeffectslotArray)));
|
2017-07-13 23:13:02 -07:00
|
|
|
auxslots->count = 0;
|
|
|
|
}
|
2018-11-26 14:31:54 -08:00
|
|
|
Context->ActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
|
2017-03-27 23:16:23 -07:00
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
//Set globals
|
2018-11-18 03:39:32 -08:00
|
|
|
Context->mDistanceModel = DistanceModel::Default;
|
2012-04-19 23:00:58 -07:00
|
|
|
Context->SourceDistanceModel = AL_FALSE;
|
|
|
|
Context->DopplerFactor = 1.0f;
|
|
|
|
Context->DopplerVelocity = 1.0f;
|
|
|
|
Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
|
2017-09-27 08:55:42 -07:00
|
|
|
Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
|
|
|
|
|
2012-04-19 23:00:58 -07:00
|
|
|
Context->ExtensionList = alExtList;
|
2017-09-27 08:55:42 -07:00
|
|
|
|
|
|
|
|
2018-11-17 23:41:11 -08:00
|
|
|
listener.Params.Matrix = aluMatrixf::Identity;
|
|
|
|
aluVectorSet(&listener.Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
listener.Params.Gain = listener.Gain;
|
|
|
|
listener.Params.MetersPerUnit = Context->MetersPerUnit;
|
|
|
|
listener.Params.DopplerFactor = Context->DopplerFactor;
|
|
|
|
listener.Params.SpeedOfSound = Context->SpeedOfSound * Context->DopplerVelocity;
|
|
|
|
listener.Params.ReverbSpeedOfSound = listener.Params.SpeedOfSound *
|
|
|
|
listener.Params.MetersPerUnit;
|
|
|
|
listener.Params.SourceDistanceModel = Context->SourceDistanceModel;
|
2018-11-18 03:39:32 -08:00
|
|
|
listener.Params.mDistanceModel = Context->mDistanceModel;
|
2018-09-20 19:58:01 -07:00
|
|
|
|
|
|
|
|
2018-12-04 16:38:22 -08:00
|
|
|
Context->AsyncEvents = ll_ringbuffer_create(511, sizeof(AsyncEvent), false);
|
2018-11-18 18:04:27 -08:00
|
|
|
StartEventThrd(Context);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-18 05:40:00 -08:00
|
|
|
/* ALCcontext_struct::~ALCcontext_struct()
|
2011-09-10 09:00:01 -07:00
|
|
|
*
|
2011-09-10 20:52:19 -07:00
|
|
|
* Cleans up the context, and destroys any remaining objects the app failed to
|
|
|
|
* delete. Called once there's no more references on the context.
|
2011-09-10 09:00:01 -07:00
|
|
|
*/
|
2018-11-18 05:40:00 -08:00
|
|
|
ALCcontext_struct::~ALCcontext_struct()
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-18 05:40:00 -08:00
|
|
|
TRACE("%p\n", this);
|
2011-09-02 02:00:37 -07:00
|
|
|
|
2018-11-27 21:19:30 -08:00
|
|
|
ALcontextProps *cprops{Update.exchange(nullptr, std::memory_order_relaxed)};
|
2018-11-18 05:40:00 -08:00
|
|
|
if(cprops)
|
2017-09-27 08:55:42 -07:00
|
|
|
{
|
|
|
|
TRACE("Freed unapplied context update %p\n", cprops);
|
|
|
|
al_free(cprops);
|
|
|
|
}
|
2018-11-18 05:40:00 -08:00
|
|
|
size_t count{0};
|
2018-11-27 21:19:30 -08:00
|
|
|
cprops = FreeContextProps.exchange(nullptr, std::memory_order_acquire);
|
2017-09-27 08:55:42 -07:00
|
|
|
while(cprops)
|
|
|
|
{
|
2018-11-27 21:19:30 -08:00
|
|
|
ALcontextProps *next{cprops->next.load(std::memory_order_relaxed)};
|
2017-09-27 08:55:42 -07:00
|
|
|
al_free(cprops);
|
|
|
|
cprops = next;
|
|
|
|
++count;
|
|
|
|
}
|
2018-11-14 04:15:44 -08:00
|
|
|
TRACE("Freed " SZFMT " context property object%s\n", count, (count==1)?"":"s");
|
2017-09-27 08:55:42 -07:00
|
|
|
|
2018-11-28 11:30:44 -08:00
|
|
|
count = std::accumulate(SourceList.cbegin(), SourceList.cend(), size_t{0u},
|
|
|
|
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
|
|
|
|
{ return cur + POPCNT64(~sublist.FreeMask); }
|
|
|
|
);
|
2018-11-25 08:42:43 -08:00
|
|
|
if(count > 0)
|
|
|
|
WARN(SZFMT " Source%s not deleted\n", count, (count==1)?"":"s");
|
2018-11-18 05:40:00 -08:00
|
|
|
SourceList.clear();
|
|
|
|
NumSources = 0;
|
2011-08-28 15:44:03 -07:00
|
|
|
|
2017-09-27 11:13:18 -07:00
|
|
|
count = 0;
|
2018-11-27 21:19:30 -08:00
|
|
|
ALeffectslotProps *eprops{FreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)};
|
2017-09-27 11:13:18 -07:00
|
|
|
while(eprops)
|
|
|
|
{
|
2018-11-27 21:19:30 -08:00
|
|
|
ALeffectslotProps *next{eprops->next.load(std::memory_order_relaxed)};
|
2018-11-19 22:34:26 -08:00
|
|
|
if(eprops->State) eprops->State->DecRef();
|
2017-09-27 11:13:18 -07:00
|
|
|
al_free(eprops);
|
|
|
|
eprops = next;
|
|
|
|
++count;
|
|
|
|
}
|
2018-11-14 04:15:44 -08:00
|
|
|
TRACE("Freed " SZFMT " AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
|
2018-01-27 17:24:18 -08:00
|
|
|
|
2018-11-27 21:19:30 -08:00
|
|
|
al_free(ActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed));
|
|
|
|
DefaultSlot = nullptr;
|
|
|
|
|
|
|
|
count = std::count_if(EffectSlotList.cbegin(), EffectSlotList.cend(),
|
|
|
|
[](const ALeffectslotPtr &slot) noexcept -> bool { return slot != nullptr; }
|
|
|
|
);
|
2018-11-25 09:23:01 -08:00
|
|
|
if(count > 0)
|
|
|
|
WARN(SZFMT " AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
|
2018-11-18 05:40:00 -08:00
|
|
|
EffectSlotList.clear();
|
2011-08-28 15:44:03 -07:00
|
|
|
|
2017-09-27 11:13:18 -07:00
|
|
|
count = 0;
|
2018-11-27 21:19:30 -08:00
|
|
|
ALvoiceProps *vprops{FreeVoiceProps.exchange(nullptr, std::memory_order_acquire)};
|
2017-09-27 11:13:18 -07:00
|
|
|
while(vprops)
|
|
|
|
{
|
2018-11-27 21:19:30 -08:00
|
|
|
ALvoiceProps *next{vprops->next.load(std::memory_order_relaxed)};
|
2018-11-30 16:56:23 -08:00
|
|
|
delete vprops;
|
2017-09-27 11:13:18 -07:00
|
|
|
vprops = next;
|
|
|
|
++count;
|
|
|
|
}
|
2018-11-14 04:15:44 -08:00
|
|
|
TRACE("Freed " SZFMT " voice property object%s\n", count, (count==1)?"":"s");
|
2017-09-27 11:13:18 -07:00
|
|
|
|
2018-12-04 19:45:11 -08:00
|
|
|
std::for_each(Voices, Voices + MaxVoices, DeinitVoice);
|
2018-11-18 05:40:00 -08:00
|
|
|
al_free(Voices);
|
|
|
|
Voices = nullptr;
|
2018-11-23 16:16:31 -08:00
|
|
|
VoiceCount.store(0, std::memory_order_relaxed);
|
2018-11-18 05:40:00 -08:00
|
|
|
MaxVoices = 0;
|
2011-08-30 20:13:42 -07:00
|
|
|
|
2018-11-27 21:19:30 -08:00
|
|
|
ALlistenerProps *lprops{Listener.Update.exchange(nullptr, std::memory_order_relaxed)};
|
2018-11-18 05:40:00 -08:00
|
|
|
if(lprops)
|
2016-05-11 18:40:17 -07:00
|
|
|
{
|
|
|
|
TRACE("Freed unapplied listener update %p\n", lprops);
|
|
|
|
al_free(lprops);
|
|
|
|
}
|
|
|
|
count = 0;
|
2018-11-27 21:19:30 -08:00
|
|
|
lprops = FreeListenerProps.exchange(nullptr, std::memory_order_acquire);
|
2016-05-11 18:40:17 -07:00
|
|
|
while(lprops)
|
|
|
|
{
|
2018-11-27 21:19:30 -08:00
|
|
|
ALlistenerProps *next{lprops->next.load(std::memory_order_relaxed)};
|
2016-05-11 18:40:17 -07:00
|
|
|
al_free(lprops);
|
|
|
|
lprops = next;
|
|
|
|
++count;
|
|
|
|
}
|
2018-11-14 04:15:44 -08:00
|
|
|
TRACE("Freed " SZFMT " listener property object%s\n", count, (count==1)?"":"s");
|
2016-05-11 18:40:17 -07:00
|
|
|
|
2018-11-18 05:40:00 -08:00
|
|
|
ll_ringbuffer_free(AsyncEvents);
|
|
|
|
AsyncEvents = nullptr;
|
2018-01-23 18:25:59 -08:00
|
|
|
|
2018-11-18 05:40:00 -08:00
|
|
|
ALCdevice_DecRef(Device);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 09:00:01 -07:00
|
|
|
/* ReleaseContext
|
|
|
|
*
|
|
|
|
* Removes the context reference from the given device and removes it from
|
2017-03-28 05:33:43 -07:00
|
|
|
* being current on the running thread or globally. Returns true if other
|
|
|
|
* contexts still exist on the device.
|
2011-09-10 09:00:01 -07:00
|
|
|
*/
|
2017-03-28 05:33:43 -07:00
|
|
|
static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
|
2011-09-10 07:35:48 -07:00
|
|
|
{
|
2017-03-28 05:33:43 -07:00
|
|
|
ALCcontext *origctx, *newhead;
|
|
|
|
bool ret = true;
|
2011-09-10 07:35:48 -07:00
|
|
|
|
2018-11-16 21:55:11 -08:00
|
|
|
if(LocalContext.get() == context)
|
2011-09-12 03:35:21 -07:00
|
|
|
{
|
|
|
|
WARN("%p released while current on thread\n", context);
|
2018-11-16 21:55:11 -08:00
|
|
|
LocalContext.set(nullptr);
|
2011-09-12 03:35:21 -07:00
|
|
|
ALCcontext_DecRef(context);
|
|
|
|
}
|
|
|
|
|
2014-07-23 06:36:34 -07:00
|
|
|
origctx = context;
|
2018-11-14 06:17:47 -08:00
|
|
|
if(GlobalContext.compare_exchange_strong(origctx, nullptr))
|
2011-09-12 03:35:21 -07:00
|
|
|
ALCcontext_DecRef(context);
|
|
|
|
|
2018-01-12 02:37:48 -08:00
|
|
|
V0(device->Backend,lock)();
|
2014-08-01 02:04:40 -07:00
|
|
|
origctx = context;
|
2018-11-21 07:52:17 -08:00
|
|
|
newhead = context->next.load(std::memory_order_relaxed);
|
2018-11-19 03:21:58 -08:00
|
|
|
if(!device->ContextList.compare_exchange_strong(origctx, newhead))
|
2011-09-10 07:35:48 -07:00
|
|
|
{
|
2018-03-02 18:38:59 -08:00
|
|
|
ALCcontext *list;
|
|
|
|
do {
|
|
|
|
/* origctx is what the desired context failed to match. Try
|
|
|
|
* swapping out the next one in the list.
|
|
|
|
*/
|
|
|
|
list = origctx;
|
|
|
|
origctx = context;
|
2018-11-19 03:21:58 -08:00
|
|
|
} while(!list->next.compare_exchange_strong(origctx, newhead));
|
2011-09-10 07:35:48 -07:00
|
|
|
}
|
2017-03-28 05:33:43 -07:00
|
|
|
else
|
|
|
|
ret = !!newhead;
|
2018-01-12 02:37:48 -08:00
|
|
|
V0(device->Backend,unlock)();
|
2011-09-10 07:35:48 -07:00
|
|
|
|
2018-10-07 17:18:50 -07:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2018-11-18 18:04:27 -08:00
|
|
|
StopEventThrd(context);
|
2018-09-20 22:44:58 -07:00
|
|
|
|
2011-09-10 07:35:48 -07:00
|
|
|
ALCcontext_DecRef(context);
|
2017-03-28 05:33:43 -07:00
|
|
|
return ret;
|
2011-09-10 07:35:48 -07:00
|
|
|
}
|
|
|
|
|
2018-01-12 02:37:48 -08:00
|
|
|
static void ALCcontext_IncRef(ALCcontext *context)
|
2011-08-28 18:08:48 -07:00
|
|
|
{
|
2018-11-19 02:17:06 -08:00
|
|
|
auto ref = IncrementRef(&context->ref);
|
2011-09-20 12:24:23 -07:00
|
|
|
TRACEREF("%p increasing refcount to %u\n", context, ref);
|
2011-08-28 18:08:48 -07:00
|
|
|
}
|
|
|
|
|
2011-08-29 13:22:07 -07:00
|
|
|
void ALCcontext_DecRef(ALCcontext *context)
|
2011-08-28 15:44:03 -07:00
|
|
|
{
|
2018-11-19 02:17:06 -08:00
|
|
|
auto ref = DecrementRef(&context->ref);
|
2012-04-20 01:18:38 -07:00
|
|
|
TRACEREF("%p decreasing refcount to %u\n", context, ref);
|
2018-11-18 05:40:00 -08:00
|
|
|
if(ref == 0) delete context;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
2011-09-10 05:19:08 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/* VerifyContext
|
|
|
|
*
|
2018-11-21 02:02:53 -08:00
|
|
|
* Checks if the given context is valid, returning a new reference to it if so.
|
2012-04-20 01:18:38 -07:00
|
|
|
*/
|
2018-11-21 02:02:53 -08:00
|
|
|
static ContextRef VerifyContext(ALCcontext *context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::lock_guard<std::recursive_mutex> _{ListLock};
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *dev{DeviceList.load()};
|
2012-04-20 01:18:38 -07:00
|
|
|
while(dev)
|
2011-07-13 01:21:30 -07:00
|
|
|
{
|
2018-11-21 02:02:53 -08:00
|
|
|
ALCcontext *ctx = dev->ContextList.load(std::memory_order_acquire);
|
2014-08-01 02:04:40 -07:00
|
|
|
while(ctx)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2018-11-21 02:02:53 -08:00
|
|
|
if(ctx == context)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2014-08-01 02:04:40 -07:00
|
|
|
ALCcontext_IncRef(ctx);
|
2018-11-21 02:02:53 -08:00
|
|
|
return ContextRef{ctx};
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
2018-11-21 02:02:53 -08:00
|
|
|
ctx = ctx->next.load(std::memory_order_relaxed);
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
2018-11-21 02:02:53 -08:00
|
|
|
dev = dev->next.load(std::memory_order_relaxed);
|
2011-07-13 01:21:30 -07:00
|
|
|
}
|
2011-09-10 05:19:08 -07:00
|
|
|
|
2018-11-21 02:02:53 -08:00
|
|
|
return ContextRef{};
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
/* GetContextRef
|
|
|
|
*
|
2018-11-24 14:07:32 -08:00
|
|
|
* Returns a new reference to the currently active context for this thread.
|
2012-04-20 01:18:38 -07:00
|
|
|
*/
|
2018-11-24 14:07:32 -08:00
|
|
|
ContextRef GetContextRef(void)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-16 21:55:11 -08:00
|
|
|
ALCcontext *context{LocalContext.get()};
|
2012-04-20 01:18:38 -07:00
|
|
|
if(context)
|
|
|
|
ALCcontext_IncRef(context);
|
|
|
|
else
|
2011-07-13 01:21:30 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::lock_guard<std::recursive_mutex> _{ListLock};
|
|
|
|
context = GlobalContext.load(std::memory_order_acquire);
|
2018-11-14 04:15:44 -08:00
|
|
|
if(context) ALCcontext_IncRef(context);
|
2011-07-13 01:21:30 -07:00
|
|
|
}
|
2018-11-24 14:07:32 -08:00
|
|
|
return ContextRef{context};
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-14 19:59:39 -08:00
|
|
|
void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends)
|
|
|
|
{
|
2018-11-23 16:16:31 -08:00
|
|
|
ALCdevice *device{context->Device};
|
2018-12-04 19:45:11 -08:00
|
|
|
const ALsizei num_sends{device->NumAuxSends};
|
2017-02-14 19:59:39 -08:00
|
|
|
|
|
|
|
if(num_voices == context->MaxVoices && num_sends == old_sends)
|
|
|
|
return;
|
|
|
|
|
2017-02-21 11:17:47 -08:00
|
|
|
/* Allocate the voice pointers, voices, and the voices' stored source
|
|
|
|
* property set (including the dynamically-sized Send[] array) in one
|
|
|
|
* chunk.
|
2017-02-14 19:59:39 -08:00
|
|
|
*/
|
2018-12-04 19:45:11 -08:00
|
|
|
const size_t sizeof_voice{RoundUp(FAM_SIZE(ALvoice, Send, num_sends), 16)};
|
|
|
|
const size_t size{sizeof(ALvoice*) + sizeof_voice};
|
2017-02-14 19:59:39 -08:00
|
|
|
|
2018-11-23 16:16:31 -08:00
|
|
|
auto voices = static_cast<ALvoice**>(al_calloc(16, RoundUp(size*num_voices, 16)));
|
|
|
|
auto voice = reinterpret_cast<ALvoice*>((char*)voices + RoundUp(num_voices*sizeof(ALvoice*), 16));
|
2017-02-14 19:59:39 -08:00
|
|
|
|
2018-12-04 19:45:11 -08:00
|
|
|
auto viter = voices;
|
2017-02-14 19:59:39 -08:00
|
|
|
if(context->Voices)
|
|
|
|
{
|
2018-11-23 16:16:31 -08:00
|
|
|
const ALsizei v_count = mini(context->VoiceCount.load(std::memory_order_relaxed),
|
|
|
|
num_voices);
|
2017-04-19 12:34:45 -07:00
|
|
|
const ALsizei s_count = mini(old_sends, num_sends);
|
|
|
|
|
2018-12-04 19:45:11 -08:00
|
|
|
/* Copy the old voice data to the new storage. */
|
|
|
|
auto copy_voice = [&voice,sizeof_voice,s_count](ALvoice *old_voice) -> ALvoice*
|
2017-02-14 19:59:39 -08:00
|
|
|
{
|
2018-11-30 19:04:38 -08:00
|
|
|
voice = new (voice) ALvoice{};
|
2017-02-14 19:59:39 -08:00
|
|
|
|
2018-12-04 19:45:11 -08:00
|
|
|
/* Make sure the old voice's Update (if any) is cleared so it
|
|
|
|
* doesn't get deleted on deinit.
|
2017-02-14 19:59:39 -08:00
|
|
|
*/
|
2018-11-30 19:04:38 -08:00
|
|
|
voice->Update.store(old_voice->Update.exchange(nullptr, std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
|
|
|
|
voice->SourceID.store(old_voice->SourceID.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
voice->Playing.store(old_voice->Playing.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
|
|
|
|
voice->Props = old_voice->Props;
|
|
|
|
/* Clear extraneous property set sends. */
|
|
|
|
std::fill(std::begin(voice->Props.Send)+s_count, std::end(voice->Props.Send),
|
|
|
|
ALvoiceProps::SendData{});
|
|
|
|
|
|
|
|
voice->position.store(old_voice->position.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
voice->position_fraction.store(
|
|
|
|
old_voice->position_fraction.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
|
|
|
|
voice->current_buffer.store(old_voice->current_buffer.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
voice->loop_buffer.store(old_voice->loop_buffer.load(std::memory_order_relaxed),
|
|
|
|
std::memory_order_relaxed);
|
|
|
|
|
|
|
|
voice->NumChannels = old_voice->NumChannels;
|
|
|
|
voice->SampleSize = old_voice->SampleSize;
|
|
|
|
|
|
|
|
voice->Step = old_voice->Step;
|
|
|
|
voice->Resampler = old_voice->Resampler;
|
|
|
|
|
|
|
|
voice->Flags = old_voice->Flags;
|
|
|
|
|
|
|
|
voice->Offset = old_voice->Offset;
|
|
|
|
|
2018-11-30 21:23:43 -08:00
|
|
|
std::copy(std::begin(old_voice->PrevSamples), std::end(old_voice->PrevSamples),
|
|
|
|
std::begin(voice->PrevSamples));
|
2018-11-30 19:04:38 -08:00
|
|
|
|
|
|
|
voice->ResampleState = old_voice->ResampleState;
|
|
|
|
|
|
|
|
voice->Direct = old_voice->Direct;
|
2018-11-23 16:16:31 -08:00
|
|
|
std::copy_n(old_voice->Send, s_count, voice->Send);
|
2017-02-14 19:59:39 -08:00
|
|
|
|
2018-11-30 16:56:23 -08:00
|
|
|
/* Set this voice's reference. */
|
2018-12-04 19:45:11 -08:00
|
|
|
ALvoice *ret = voice;
|
2018-11-30 16:56:23 -08:00
|
|
|
/* Increment pointer to the next storage space. */
|
|
|
|
voice = reinterpret_cast<ALvoice*>((char*)voice + sizeof_voice);
|
2018-12-04 19:45:11 -08:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
viter = std::transform(context->Voices, context->Voices+v_count, viter, copy_voice);
|
|
|
|
|
2018-11-30 19:04:38 -08:00
|
|
|
/* Deinit old voices. */
|
2018-12-04 19:45:11 -08:00
|
|
|
auto voices_end = context->Voices + context->MaxVoices;
|
2018-11-30 19:04:38 -08:00
|
|
|
std::for_each(context->Voices, voices_end, DeinitVoice);
|
2017-02-14 19:59:39 -08:00
|
|
|
}
|
2018-12-04 19:45:11 -08:00
|
|
|
/* Finish setting the voices and references. */
|
|
|
|
auto init_voice = [&voice,sizeof_voice]() -> ALvoice*
|
2017-02-14 19:59:39 -08:00
|
|
|
{
|
2018-12-04 19:45:11 -08:00
|
|
|
ALvoice *ret = new (voice) ALvoice{};
|
2018-11-30 16:56:23 -08:00
|
|
|
voice = reinterpret_cast<ALvoice*>((char*)voice + sizeof_voice);
|
2018-12-04 19:45:11 -08:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
std::generate(viter, voices+num_voices, init_voice);
|
2017-02-14 19:59:39 -08:00
|
|
|
|
|
|
|
al_free(context->Voices);
|
|
|
|
context->Voices = voices;
|
|
|
|
context->MaxVoices = num_voices;
|
2018-11-23 16:16:31 -08:00
|
|
|
context->VoiceCount = mini(context->VoiceCount.load(std::memory_order_relaxed), num_voices);
|
2017-02-14 19:59:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* Standard ALC functions
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
/* alcGetError
|
|
|
|
*
|
|
|
|
* Return last ALC generated error code for the given device
|
2018-11-23 17:54:12 -08:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(dev) return dev->LastError.exchange(ALC_NO_ERROR);
|
|
|
|
return LastNullDeviceError.exchange(ALC_NO_ERROR);
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcSuspendContext
|
|
|
|
*
|
2014-10-12 09:03:08 -07:00
|
|
|
* Suspends updates for the given context
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2014-10-12 09:03:08 -07:00
|
|
|
ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2014-10-12 09:03:08 -07:00
|
|
|
if(!SuspendDefers)
|
|
|
|
return;
|
|
|
|
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx{VerifyContext(context)};
|
|
|
|
if(!ctx)
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
2014-10-12 09:03:08 -07:00
|
|
|
else
|
2018-11-21 02:02:53 -08:00
|
|
|
ALCcontext_DeferUpdates(ctx.get());
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcProcessContext
|
|
|
|
*
|
2014-10-12 09:03:08 -07:00
|
|
|
* Resumes processing updates for the given context
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2014-10-12 09:03:08 -07:00
|
|
|
ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2014-10-12 09:03:08 -07:00
|
|
|
if(!SuspendDefers)
|
|
|
|
return;
|
|
|
|
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx{VerifyContext(context)};
|
|
|
|
if(!ctx)
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
2014-10-12 09:03:08 -07:00
|
|
|
else
|
2018-11-21 02:02:53 -08:00
|
|
|
ALCcontext_ProcessUpdates(ctx.get());
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetString
|
|
|
|
*
|
2012-04-19 23:28:34 -07:00
|
|
|
* Returns information about the device, and error strings
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2012-04-19 23:28:34 -07:00
|
|
|
ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
const ALCchar *value = nullptr;
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2011-06-14 09:43:33 -07:00
|
|
|
switch(param)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
|
|
|
case ALC_NO_ERROR:
|
|
|
|
value = alcNoError;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_INVALID_ENUM:
|
|
|
|
value = alcErrInvalidEnum;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_INVALID_VALUE:
|
|
|
|
value = alcErrInvalidValue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_INVALID_DEVICE:
|
|
|
|
value = alcErrInvalidDevice;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_INVALID_CONTEXT:
|
|
|
|
value = alcErrInvalidContext;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_OUT_OF_MEMORY:
|
|
|
|
value = alcErrOutOfMemory;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_DEVICE_SPECIFIER:
|
2012-02-28 03:36:16 -08:00
|
|
|
value = alcDefaultName;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_ALL_DEVICES_SPECIFIER:
|
2018-11-23 17:54:12 -08:00
|
|
|
dev = VerifyDevice(Device);
|
|
|
|
if(dev)
|
|
|
|
value = dev->DeviceName.c_str();
|
2012-02-28 03:36:16 -08:00
|
|
|
else
|
|
|
|
{
|
2012-05-09 16:28:16 -07:00
|
|
|
ProbeAllDevicesList();
|
2018-11-15 04:24:33 -08:00
|
|
|
value = alcAllDevicesList.c_str();
|
2012-02-28 03:36:16 -08:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_CAPTURE_DEVICE_SPECIFIER:
|
2018-11-23 17:54:12 -08:00
|
|
|
dev = VerifyDevice(Device);
|
|
|
|
if(dev)
|
|
|
|
value = dev->DeviceName.c_str();
|
2007-11-13 18:02:18 -08:00
|
|
|
else
|
2009-08-27 06:09:33 -07:00
|
|
|
{
|
|
|
|
ProbeCaptureDeviceList();
|
2018-11-15 04:24:33 -08:00
|
|
|
value = alcCaptureDeviceList.c_str();
|
2009-08-27 06:09:33 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
break;
|
|
|
|
|
2009-08-27 20:13:35 -07:00
|
|
|
/* Default devices are always first in the list */
|
|
|
|
case ALC_DEFAULT_DEVICE_SPECIFIER:
|
2012-02-20 20:45:22 -08:00
|
|
|
value = alcDefaultName;
|
2009-08-27 20:13:35 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
|
2018-11-15 04:24:33 -08:00
|
|
|
if(alcAllDevicesList.empty())
|
2012-05-09 16:28:16 -07:00
|
|
|
ProbeAllDevicesList();
|
2010-02-10 16:34:43 -08:00
|
|
|
|
2018-11-15 04:24:33 -08:00
|
|
|
/* Copy first entry as default. */
|
|
|
|
alcDefaultAllDevicesSpecifier = alcAllDevicesList.c_str();
|
2018-11-14 06:17:47 -08:00
|
|
|
value = alcDefaultAllDevicesSpecifier.c_str();
|
2009-08-27 20:13:35 -07:00
|
|
|
break;
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
|
2018-11-15 04:24:33 -08:00
|
|
|
if(alcCaptureDeviceList.empty())
|
2010-02-10 16:34:43 -08:00
|
|
|
ProbeCaptureDeviceList();
|
|
|
|
|
2018-11-15 04:24:33 -08:00
|
|
|
/* Copy first entry as default. */
|
|
|
|
alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.c_str();
|
2018-11-14 06:17:47 -08:00
|
|
|
value = alcCaptureDefaultDeviceSpecifier.c_str();
|
2007-11-13 18:02:18 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_EXTENSIONS:
|
2018-11-23 17:54:12 -08:00
|
|
|
dev = VerifyDevice(Device);
|
|
|
|
if(dev) value = alcExtensionList;
|
|
|
|
else value = alcNoDeviceExtList;
|
2007-11-13 18:02:18 -08:00
|
|
|
break;
|
|
|
|
|
2015-10-02 23:54:30 -07:00
|
|
|
case ALC_HRTF_SPECIFIER_SOFT:
|
2018-11-23 17:54:12 -08:00
|
|
|
dev = VerifyDevice(Device);
|
|
|
|
if(!dev)
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_DEVICE);
|
2015-10-02 23:54:30 -07:00
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
value = (dev->HrtfHandle ? dev->HrtfName.c_str() : "");
|
2015-10-02 23:54:30 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
default:
|
2018-11-23 17:54:12 -08:00
|
|
|
dev = VerifyDevice(Device);
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_ENUM);
|
2007-11-13 18:02:18 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
static inline ALCsizei NumAttrsForDevice(ALCdevice *device)
|
|
|
|
{
|
2017-12-21 11:46:01 -08:00
|
|
|
if(device->Type == Capture) return 9;
|
2018-01-12 11:34:23 -08:00
|
|
|
if(device->Type != Loopback) return 29;
|
|
|
|
if(device->FmtChans == DevFmtAmbi3D)
|
2017-12-21 11:46:01 -08:00
|
|
|
return 35;
|
|
|
|
return 29;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
}
|
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2014-02-01 18:40:36 -08:00
|
|
|
ALCsizei i;
|
2011-09-10 05:19:08 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(size <= 0 || values == nullptr)
|
2010-01-08 01:21:57 -08:00
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_VALUE);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2010-01-08 01:21:57 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 05:19:08 -07:00
|
|
|
if(!device)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2011-05-29 23:20:33 -07:00
|
|
|
switch(param)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2011-05-29 23:20:33 -07:00
|
|
|
case ALC_MAJOR_VERSION:
|
2014-02-01 18:40:36 -08:00
|
|
|
values[0] = alcMajorVersion;
|
|
|
|
return 1;
|
2011-05-29 23:20:33 -07:00
|
|
|
case ALC_MINOR_VERSION:
|
2014-02-01 18:40:36 -08:00
|
|
|
values[0] = alcMinorVersion;
|
|
|
|
return 1;
|
2011-05-29 23:20:33 -07:00
|
|
|
|
|
|
|
case ALC_ATTRIBUTES_SIZE:
|
|
|
|
case ALC_ALL_ATTRIBUTES:
|
|
|
|
case ALC_FREQUENCY:
|
|
|
|
case ALC_REFRESH:
|
|
|
|
case ALC_SYNC:
|
|
|
|
case ALC_MONO_SOURCES:
|
|
|
|
case ALC_STEREO_SOURCES:
|
|
|
|
case ALC_CAPTURE_SAMPLES:
|
|
|
|
case ALC_FORMAT_CHANNELS_SOFT:
|
|
|
|
case ALC_FORMAT_TYPE_SOFT:
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
case ALC_AMBISONIC_LAYOUT_SOFT:
|
|
|
|
case ALC_AMBISONIC_SCALING_SOFT:
|
|
|
|
case ALC_AMBISONIC_ORDER_SOFT:
|
2017-09-27 11:58:36 -07:00
|
|
|
case ALC_MAX_AMBISONIC_ORDER_SOFT:
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_DEVICE);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2011-05-29 23:20:33 -07:00
|
|
|
|
|
|
|
default:
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_ENUM);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2011-05-29 23:20:33 -07:00
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2011-05-29 23:20:33 -07:00
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
|
|
|
|
if(device->Type == Capture)
|
2011-05-29 23:20:33 -07:00
|
|
|
{
|
|
|
|
switch(param)
|
|
|
|
{
|
2017-12-21 11:46:01 -08:00
|
|
|
case ALC_ATTRIBUTES_SIZE:
|
|
|
|
values[0] = NumAttrsForDevice(device);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_ALL_ATTRIBUTES:
|
2018-11-20 23:42:21 -08:00
|
|
|
i = 0;
|
2017-12-21 11:46:01 -08:00
|
|
|
if(size < NumAttrsForDevice(device))
|
|
|
|
alcSetError(device, ALC_INVALID_VALUE);
|
2018-11-20 23:42:21 -08:00
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_MAJOR_VERSION;
|
|
|
|
values[i++] = alcMajorVersion;
|
|
|
|
values[i++] = ALC_MINOR_VERSION;
|
|
|
|
values[i++] = alcMinorVersion;
|
|
|
|
values[i++] = ALC_CAPTURE_SAMPLES;
|
|
|
|
values[i++] = V0(device->Backend,availableSamples)();
|
|
|
|
values[i++] = ALC_CONNECTED;
|
2018-11-21 07:52:17 -08:00
|
|
|
values[i++] = device->Connected.load(std::memory_order_relaxed);
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = 0;
|
2017-12-21 11:46:01 -08:00
|
|
|
}
|
|
|
|
return i;
|
|
|
|
|
|
|
|
case ALC_MAJOR_VERSION:
|
|
|
|
values[0] = alcMajorVersion;
|
|
|
|
return 1;
|
|
|
|
case ALC_MINOR_VERSION:
|
|
|
|
values[0] = alcMinorVersion;
|
|
|
|
return 1;
|
|
|
|
|
2011-05-29 23:20:33 -07:00
|
|
|
case ALC_CAPTURE_SAMPLES:
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
values[0] = V0(device->Backend,availableSamples)();
|
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2011-05-29 23:20:33 -07:00
|
|
|
case ALC_CONNECTED:
|
2018-11-28 12:24:12 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
|
|
|
values[0] = device->Connected.load(std::memory_order_acquire);
|
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2009-08-26 19:15:17 -07:00
|
|
|
|
2011-05-29 23:20:33 -07:00
|
|
|
default:
|
|
|
|
alcSetError(device, ALC_INVALID_ENUM);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
|
|
|
|
/* render device */
|
|
|
|
switch(param)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_ATTRIBUTES_SIZE:
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
values[0] = NumAttrsForDevice(device);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_ALL_ATTRIBUTES:
|
2018-11-20 23:42:21 -08:00
|
|
|
i = 0;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
if(size < NumAttrsForDevice(device))
|
2014-02-01 18:40:36 -08:00
|
|
|
alcSetError(device, ALC_INVALID_VALUE);
|
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_MAJOR_VERSION;
|
|
|
|
values[i++] = alcMajorVersion;
|
|
|
|
values[i++] = ALC_MINOR_VERSION;
|
|
|
|
values[i++] = alcMinorVersion;
|
|
|
|
values[i++] = ALC_EFX_MAJOR_VERSION;
|
|
|
|
values[i++] = alcEFXMajorVersion;
|
|
|
|
values[i++] = ALC_EFX_MINOR_VERSION;
|
|
|
|
values[i++] = alcEFXMinorVersion;
|
|
|
|
|
|
|
|
values[i++] = ALC_FREQUENCY;
|
|
|
|
values[i++] = device->Frequency;
|
|
|
|
if(device->Type != Loopback)
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
{
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_REFRESH;
|
|
|
|
values[i++] = device->Frequency / device->UpdateSize;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_SYNC;
|
|
|
|
values[i++] = ALC_FALSE;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
}
|
2018-11-20 23:42:21 -08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if(device->FmtChans == DevFmtAmbi3D)
|
|
|
|
{
|
|
|
|
values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
|
|
|
|
values[i++] = static_cast<ALCint>(device->mAmbiLayout);
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_AMBISONIC_SCALING_SOFT;
|
|
|
|
values[i++] = static_cast<ALCint>(device->mAmbiScale);
|
2017-04-12 18:26:07 -07:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_AMBISONIC_ORDER_SOFT;
|
|
|
|
values[i++] = device->mAmbiOrder;
|
|
|
|
}
|
|
|
|
|
|
|
|
values[i++] = ALC_FORMAT_CHANNELS_SOFT;
|
|
|
|
values[i++] = device->FmtChans;
|
|
|
|
|
|
|
|
values[i++] = ALC_FORMAT_TYPE_SOFT;
|
|
|
|
values[i++] = device->FmtType;
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_MONO_SOURCES;
|
|
|
|
values[i++] = device->NumMonoSources;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_STEREO_SOURCES;
|
|
|
|
values[i++] = device->NumStereoSources;
|
2007-12-17 16:55:07 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_MAX_AUXILIARY_SENDS;
|
|
|
|
values[i++] = device->NumAuxSends;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_HRTF_SOFT;
|
|
|
|
values[i++] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
|
2013-05-31 15:49:56 -07:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_HRTF_STATUS_SOFT;
|
|
|
|
values[i++] = device->HrtfStatus;
|
2017-04-30 08:54:49 -07:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_OUTPUT_LIMITER_SOFT;
|
|
|
|
values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
2017-09-27 11:58:36 -07:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = ALC_MAX_AMBISONIC_ORDER_SOFT;
|
|
|
|
values[i++] = MAX_AMBI_ORDER;
|
2015-05-15 23:28:03 -07:00
|
|
|
|
2018-11-20 23:42:21 -08:00
|
|
|
values[i++] = 0;
|
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return i;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2017-12-21 11:46:01 -08:00
|
|
|
case ALC_MAJOR_VERSION:
|
|
|
|
values[0] = alcMajorVersion;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_MINOR_VERSION:
|
|
|
|
values[0] = alcMinorVersion;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_EFX_MAJOR_VERSION:
|
|
|
|
values[0] = alcEFXMajorVersion;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_EFX_MINOR_VERSION:
|
|
|
|
values[0] = alcEFXMinorVersion;
|
|
|
|
return 1;
|
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_FREQUENCY:
|
|
|
|
values[0] = device->Frequency;
|
|
|
|
return 1;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_REFRESH:
|
|
|
|
if(device->Type == Loopback)
|
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
values[0] = device->Frequency / device->UpdateSize;
|
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_SYNC:
|
|
|
|
if(device->Type == Loopback)
|
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
values[0] = ALC_FALSE;
|
|
|
|
return 1;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_FORMAT_CHANNELS_SOFT:
|
|
|
|
if(device->Type != Loopback)
|
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-12 18:26:07 -07:00
|
|
|
values[0] = device->FmtChans;
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_FORMAT_TYPE_SOFT:
|
|
|
|
if(device->Type != Loopback)
|
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
values[0] = device->FmtType;
|
|
|
|
return 1;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
case ALC_AMBISONIC_LAYOUT_SOFT:
|
2017-04-12 18:26:07 -07:00
|
|
|
if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-11-20 22:47:24 -08:00
|
|
|
values[0] = static_cast<ALCint>(device->mAmbiLayout);
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_AMBISONIC_SCALING_SOFT:
|
2017-04-12 18:26:07 -07:00
|
|
|
if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-11-20 22:47:24 -08:00
|
|
|
values[0] = static_cast<ALCint>(device->mAmbiScale);
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ALC_AMBISONIC_ORDER_SOFT:
|
2017-04-12 18:26:07 -07:00
|
|
|
if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
{
|
|
|
|
alcSetError(device, ALC_INVALID_DEVICE);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-11-18 08:01:50 -08:00
|
|
|
values[0] = device->mAmbiOrder;
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
return 1;
|
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_MONO_SOURCES:
|
|
|
|
values[0] = device->NumMonoSources;
|
|
|
|
return 1;
|
2008-01-10 08:24:23 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_STEREO_SOURCES:
|
|
|
|
values[0] = device->NumStereoSources;
|
|
|
|
return 1;
|
2008-01-10 08:24:23 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_MAX_AUXILIARY_SENDS:
|
|
|
|
values[0] = device->NumAuxSends;
|
|
|
|
return 1;
|
2011-05-29 23:20:33 -07:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_CONNECTED:
|
2018-11-28 12:24:12 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
|
|
|
values[0] = device->Connected.load(std::memory_order_acquire);
|
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2010-07-30 20:23:55 -07:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
case ALC_HRTF_SOFT:
|
2017-03-10 10:47:43 -08:00
|
|
|
values[0] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
|
2014-02-01 18:40:36 -08:00
|
|
|
return 1;
|
2013-05-31 15:49:56 -07:00
|
|
|
|
2015-05-15 23:28:03 -07:00
|
|
|
case ALC_HRTF_STATUS_SOFT:
|
2017-03-10 10:47:43 -08:00
|
|
|
values[0] = device->HrtfStatus;
|
2015-09-24 10:45:34 -07:00
|
|
|
return 1;
|
2015-05-15 23:28:03 -07:00
|
|
|
|
2015-10-26 22:34:02 -07:00
|
|
|
case ALC_NUM_HRTF_SPECIFIERS_SOFT:
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
device->HrtfList.clear();
|
|
|
|
device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
|
|
|
|
values[0] = (ALCint)device->HrtfList.size();
|
|
|
|
}
|
2015-10-02 23:54:30 -07:00
|
|
|
return 1;
|
|
|
|
|
2017-04-30 08:54:49 -07:00
|
|
|
case ALC_OUTPUT_LIMITER_SOFT:
|
2017-05-05 07:38:26 -07:00
|
|
|
values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
2017-04-30 08:54:49 -07:00
|
|
|
return 1;
|
|
|
|
|
2017-12-21 11:46:01 -08:00
|
|
|
case ALC_MAX_AMBISONIC_ORDER_SOFT:
|
|
|
|
values[0] = MAX_AMBI_ORDER;
|
|
|
|
return 1;
|
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
default:
|
|
|
|
alcSetError(device, ALC_INVALID_ENUM);
|
|
|
|
return 0;
|
2014-02-01 17:55:42 -08:00
|
|
|
}
|
2014-02-01 18:40:36 -08:00
|
|
|
return 0;
|
2014-02-01 17:55:42 -08:00
|
|
|
}
|
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
/* alcGetIntegerv
|
|
|
|
*
|
|
|
|
* Returns information about the device and the version of OpenAL
|
|
|
|
*/
|
|
|
|
ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
|
2014-02-01 17:55:42 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
2018-11-14 04:15:44 -08:00
|
|
|
if(size <= 0 || values == nullptr)
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2014-02-01 18:40:36 -08:00
|
|
|
else
|
2018-11-23 17:54:12 -08:00
|
|
|
GetIntegerv(dev.get(), param, size, values);
|
2014-02-01 18:40:36 -08:00
|
|
|
}
|
2014-02-01 17:55:42 -08:00
|
|
|
|
2014-02-01 18:40:36 -08:00
|
|
|
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
2018-11-14 04:15:44 -08:00
|
|
|
if(size <= 0 || values == nullptr)
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
|
|
|
else if(!dev || dev->Type == Capture)
|
2014-02-01 17:55:42 -08:00
|
|
|
{
|
2018-11-24 16:58:49 -08:00
|
|
|
al::vector<ALCint> ivals(size);
|
2018-11-23 17:54:12 -08:00
|
|
|
size = GetIntegerv(dev.get(), pname, size, ivals.data());
|
2018-11-20 23:42:21 -08:00
|
|
|
std::copy(ivals.begin(), ivals.begin()+size, values);
|
2014-02-01 17:55:42 -08:00
|
|
|
}
|
|
|
|
else /* render device */
|
|
|
|
{
|
|
|
|
switch(pname)
|
|
|
|
{
|
|
|
|
case ALC_ATTRIBUTES_SIZE:
|
2018-11-23 17:54:12 -08:00
|
|
|
*values = NumAttrsForDevice(dev.get())+4;
|
2014-02-01 17:55:42 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_ALL_ATTRIBUTES:
|
2018-11-23 17:54:12 -08:00
|
|
|
if(size < NumAttrsForDevice(dev.get())+4)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2014-02-01 17:55:42 -08:00
|
|
|
else
|
|
|
|
{
|
2018-11-20 23:42:21 -08:00
|
|
|
ALsizei i{0};
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2014-02-01 17:55:42 -08:00
|
|
|
values[i++] = ALC_FREQUENCY;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->Frequency;
|
2014-02-01 17:55:42 -08:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
if(dev->Type != Loopback)
|
2014-02-01 17:55:42 -08:00
|
|
|
{
|
|
|
|
values[i++] = ALC_REFRESH;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->Frequency / dev->UpdateSize;
|
2014-02-01 17:55:42 -08:00
|
|
|
|
|
|
|
values[i++] = ALC_SYNC;
|
|
|
|
values[i++] = ALC_FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
if(dev->FmtChans == DevFmtAmbi3D)
|
2017-03-18 14:33:40 -07:00
|
|
|
{
|
|
|
|
values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = static_cast<ALCint64SOFT>(dev->mAmbiLayout);
|
2017-03-18 14:33:40 -07:00
|
|
|
|
|
|
|
values[i++] = ALC_AMBISONIC_SCALING_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = static_cast<ALCint64SOFT>(dev->mAmbiScale);
|
2017-03-18 14:33:40 -07:00
|
|
|
|
|
|
|
values[i++] = ALC_AMBISONIC_ORDER_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->mAmbiOrder;
|
2017-03-18 14:33:40 -07:00
|
|
|
}
|
2014-02-01 17:55:42 -08:00
|
|
|
|
2017-04-12 18:26:07 -07:00
|
|
|
values[i++] = ALC_FORMAT_CHANNELS_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->FmtChans;
|
2017-04-12 18:26:07 -07:00
|
|
|
|
2014-02-01 17:55:42 -08:00
|
|
|
values[i++] = ALC_FORMAT_TYPE_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->FmtType;
|
2014-02-01 17:55:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
values[i++] = ALC_MONO_SOURCES;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->NumMonoSources;
|
2014-02-01 17:55:42 -08:00
|
|
|
|
|
|
|
values[i++] = ALC_STEREO_SOURCES;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->NumStereoSources;
|
2014-02-01 17:55:42 -08:00
|
|
|
|
|
|
|
values[i++] = ALC_MAX_AUXILIARY_SENDS;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->NumAuxSends;
|
2014-02-01 17:55:42 -08:00
|
|
|
|
|
|
|
values[i++] = ALC_HRTF_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = (dev->HrtfHandle ? ALC_TRUE : ALC_FALSE);
|
2014-02-01 17:55:42 -08:00
|
|
|
|
2015-05-15 23:28:03 -07:00
|
|
|
values[i++] = ALC_HRTF_STATUS_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->HrtfStatus;
|
2015-05-15 23:28:03 -07:00
|
|
|
|
2017-04-30 08:54:49 -07:00
|
|
|
values[i++] = ALC_OUTPUT_LIMITER_SOFT;
|
2018-11-23 17:54:12 -08:00
|
|
|
values[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE;
|
2017-04-30 08:54:49 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
ClockLatency clock{GetClockLatency(dev.get())};
|
2014-02-01 17:55:42 -08:00
|
|
|
values[i++] = ALC_DEVICE_CLOCK_SOFT;
|
2018-11-22 14:32:48 -08:00
|
|
|
values[i++] = clock.ClockTime.count();
|
2016-06-03 09:40:30 -07:00
|
|
|
|
|
|
|
values[i++] = ALC_DEVICE_LATENCY_SOFT;
|
2018-11-22 14:32:48 -08:00
|
|
|
values[i++] = clock.Latency.count();
|
2014-02-01 17:55:42 -08:00
|
|
|
|
|
|
|
values[i++] = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_DEVICE_CLOCK_SOFT:
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
using std::chrono::seconds;
|
|
|
|
using std::chrono::nanoseconds;
|
|
|
|
using std::chrono::duration_cast;
|
|
|
|
|
|
|
|
nanoseconds basecount;
|
2018-11-20 23:42:21 -08:00
|
|
|
ALuint samplecount;
|
|
|
|
ALuint refcount;
|
|
|
|
do {
|
2018-11-23 17:54:12 -08:00
|
|
|
while(((refcount=ReadRef(&dev->MixCount))&1) != 0)
|
2018-11-26 23:06:49 -08:00
|
|
|
std::this_thread::yield();
|
2018-11-23 17:54:12 -08:00
|
|
|
basecount = dev->ClockBase;
|
|
|
|
samplecount = dev->SamplesDone;
|
|
|
|
} while(refcount != ReadRef(&dev->MixCount));
|
|
|
|
*values = (duration_cast<nanoseconds>(seconds{samplecount})/dev->Frequency +
|
|
|
|
basecount).count();
|
2018-11-20 23:42:21 -08:00
|
|
|
}
|
2014-02-01 17:55:42 -08:00
|
|
|
break;
|
|
|
|
|
2016-06-03 09:40:30 -07:00
|
|
|
case ALC_DEVICE_LATENCY_SOFT:
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
ClockLatency clock{GetClockLatency(dev.get())};
|
2018-11-22 14:32:48 -08:00
|
|
|
*values = clock.Latency.count();
|
2018-11-20 23:42:21 -08:00
|
|
|
}
|
2016-06-03 09:40:30 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ALC_DEVICE_CLOCK_LATENCY_SOFT:
|
|
|
|
if(size < 2)
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2016-06-03 09:40:30 -07:00
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
ClockLatency clock{GetClockLatency(dev.get())};
|
2018-11-22 14:32:48 -08:00
|
|
|
values[0] = clock.ClockTime.count();
|
|
|
|
values[1] = clock.Latency.count();
|
2016-06-03 09:40:30 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-02-01 17:55:42 -08:00
|
|
|
default:
|
2018-11-24 16:58:49 -08:00
|
|
|
al::vector<ALCint> ivals(size);
|
2018-11-23 17:54:12 -08:00
|
|
|
size = GetIntegerv(dev.get(), pname, size, ivals.data());
|
2018-11-20 23:42:21 -08:00
|
|
|
std::copy(ivals.begin(), ivals.begin()+size, values);
|
2014-02-01 17:55:42 -08:00
|
|
|
break;
|
2011-05-29 23:20:33 -07:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcIsExtensionPresent
|
|
|
|
*
|
|
|
|
* Determines if there is support for a particular extension
|
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
2010-03-19 20:49:23 -07:00
|
|
|
if(!extName)
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2011-09-10 05:19:08 -07:00
|
|
|
else
|
2010-03-19 20:49:23 -07:00
|
|
|
{
|
2011-09-10 05:19:08 -07:00
|
|
|
size_t len = strlen(extName);
|
2018-11-23 17:54:12 -08:00
|
|
|
const char *ptr = (dev ? alcExtensionList : alcNoDeviceExtList);
|
2011-09-10 05:19:08 -07:00
|
|
|
while(ptr && *ptr)
|
2008-01-19 20:02:40 -08:00
|
|
|
{
|
2011-09-10 05:19:08 -07:00
|
|
|
if(strncasecmp(ptr, extName, len) == 0 &&
|
|
|
|
(ptr[len] == '\0' || isspace(ptr[len])))
|
2018-11-23 17:54:12 -08:00
|
|
|
return ALC_TRUE;
|
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if((ptr=strchr(ptr, ' ')) != nullptr)
|
2011-09-10 05:19:08 -07:00
|
|
|
{
|
|
|
|
do {
|
|
|
|
++ptr;
|
|
|
|
} while(isspace(*ptr));
|
|
|
|
}
|
2008-01-19 20:02:40 -08:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2018-11-23 17:54:12 -08:00
|
|
|
return ALC_FALSE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetProcAddress
|
|
|
|
*
|
|
|
|
* Retrieves the function address for a particular extension function
|
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2010-03-19 20:49:23 -07:00
|
|
|
if(!funcName)
|
2012-06-19 00:25:45 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2012-06-19 00:25:45 -07:00
|
|
|
}
|
2011-09-10 05:19:08 -07:00
|
|
|
else
|
|
|
|
{
|
2018-11-24 19:16:21 -08:00
|
|
|
for(const auto &func : alcFunctions)
|
2017-03-14 08:37:50 -07:00
|
|
|
{
|
2018-11-24 19:16:21 -08:00
|
|
|
if(strcmp(func.funcName, funcName) == 0)
|
|
|
|
return func.address;
|
2017-03-14 08:37:50 -07:00
|
|
|
}
|
2010-03-19 20:49:23 -07:00
|
|
|
}
|
2012-06-19 00:25:45 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
return nullptr;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetEnumValue
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Get the value for a particular ALC enumeration name
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2010-03-24 22:57:35 -07:00
|
|
|
if(!enumName)
|
2012-04-20 01:24:58 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2012-04-20 01:24:58 -07:00
|
|
|
}
|
2011-09-10 05:19:08 -07:00
|
|
|
else
|
|
|
|
{
|
2018-11-24 19:16:21 -08:00
|
|
|
for(const auto &enm : alcEnumerations)
|
2017-03-14 08:37:50 -07:00
|
|
|
{
|
2018-11-24 19:16:21 -08:00
|
|
|
if(strcmp(enm.enumName, enumName) == 0)
|
|
|
|
return enm.value;
|
2017-03-14 08:37:50 -07:00
|
|
|
}
|
2010-03-24 22:57:35 -07:00
|
|
|
}
|
2012-04-20 01:24:58 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
return 0;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcCreateContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Create and attach a context to the given device.
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2009-09-24 11:31:01 -07:00
|
|
|
ALCcontext *ALContext;
|
2016-09-24 18:46:41 -07:00
|
|
|
ALfloat valf;
|
2012-01-25 20:00:34 -08:00
|
|
|
ALCenum err;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2017-03-28 05:33:43 -07:00
|
|
|
/* Explicitly hold the list lock while taking the BackendLock in case the
|
|
|
|
* device is asynchronously destropyed, to ensure this new context is
|
|
|
|
* properly cleaned up after being made.
|
|
|
|
*/
|
2018-11-14 06:17:47 -08:00
|
|
|
std::unique_lock<std::recursive_mutex> listlock{ListLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type == Capture || !dev->Connected.load(std::memory_order_relaxed))
|
2009-09-24 11:31:01 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2018-11-14 04:15:44 -08:00
|
|
|
return nullptr;
|
2009-09-24 11:31:01 -07:00
|
|
|
}
|
2018-11-26 21:29:11 -08:00
|
|
|
std::unique_lock<std::mutex> backlock{dev->BackendLock};
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2009-09-24 11:31:01 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
dev->LastError.store(ALC_NO_ERROR);
|
2009-09-24 11:31:01 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
ALContext = new ALCcontext{dev.get()};
|
2018-11-18 05:40:00 -08:00
|
|
|
ALCdevice_IncRef(ALContext->Device);
|
2017-04-19 12:34:45 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
if((err=UpdateDeviceParams(dev.get(), attrList)) != ALC_NO_ERROR)
|
2016-05-15 14:12:56 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), err);
|
2016-05-15 14:12:56 -07:00
|
|
|
if(err == ALC_INVALID_DEVICE)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
V0(dev->Backend,lock)();
|
|
|
|
aluHandleDisconnect(dev.get(), "Device update failure");
|
|
|
|
V0(dev->Backend,unlock)();
|
2016-05-15 14:12:56 -07:00
|
|
|
}
|
2018-11-28 12:24:12 -08:00
|
|
|
backlock.unlock();
|
|
|
|
|
|
|
|
delete ALContext;
|
|
|
|
ALContext = nullptr;
|
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
return nullptr;
|
2016-05-15 14:12:56 -07:00
|
|
|
}
|
2018-11-23 17:54:12 -08:00
|
|
|
AllocateVoices(ALContext, 256, dev->NumAuxSends);
|
2016-05-15 14:12:56 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
if(DefaultEffect.type != AL_EFFECT_NULL && dev->Type == Playback)
|
2017-07-13 21:30:05 -07:00
|
|
|
{
|
2018-11-20 05:01:08 -08:00
|
|
|
ALContext->DefaultSlot.reset(new ALeffectslot{});
|
|
|
|
if(InitEffectSlot(ALContext->DefaultSlot.get()) == AL_NO_ERROR)
|
|
|
|
aluInitEffectPanning(ALContext->DefaultSlot.get());
|
2017-07-19 18:26:46 -07:00
|
|
|
else
|
2017-07-13 21:30:05 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
ALContext->DefaultSlot = nullptr;
|
2017-07-13 22:33:13 -07:00
|
|
|
ERR("Failed to initialize the default effect slot\n");
|
|
|
|
}
|
2017-07-13 21:30:05 -07:00
|
|
|
}
|
|
|
|
|
2011-09-10 05:19:08 -07:00
|
|
|
InitContext(ALContext);
|
2009-09-24 11:31:01 -07:00
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
if(ConfigValueFloat(dev->DeviceName.c_str(), nullptr, "volume-adjust", &valf))
|
2016-09-24 18:46:41 -07:00
|
|
|
{
|
2018-11-15 06:48:52 -08:00
|
|
|
if(!std::isfinite(valf))
|
2016-09-24 18:46:41 -07:00
|
|
|
ERR("volume-adjust must be finite: %f\n", valf);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ALfloat db = clampf(valf, -24.0f, 24.0f);
|
|
|
|
if(db != valf)
|
|
|
|
WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
|
2018-11-14 17:01:19 -08:00
|
|
|
ALContext->GainBoost = std::pow(10.0f, db/20.0f);
|
2016-09-24 18:46:41 -07:00
|
|
|
TRACE("volume-adjust gain: %f\n", ALContext->GainBoost);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UpdateListenerProps(ALContext);
|
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
ALCcontext *head = dev->ContextList.load();
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2018-11-21 07:52:17 -08:00
|
|
|
ALContext->next.store(head, std::memory_order_relaxed);
|
2018-11-23 17:54:12 -08:00
|
|
|
} while(!dev->ContextList.compare_exchange_weak(head, ALContext));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2018-11-20 23:42:21 -08:00
|
|
|
backlock.unlock();
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2017-07-19 18:26:46 -07:00
|
|
|
if(ALContext->DefaultSlot)
|
|
|
|
{
|
2018-11-20 05:01:08 -08:00
|
|
|
if(InitializeEffect(ALContext, ALContext->DefaultSlot.get(), &DefaultEffect) == AL_NO_ERROR)
|
|
|
|
UpdateEffectSlotProps(ALContext->DefaultSlot.get(), ALContext);
|
2017-07-19 18:26:46 -07:00
|
|
|
else
|
|
|
|
ERR("Failed to initialize the default effect\n");
|
|
|
|
}
|
|
|
|
|
2011-09-20 11:55:40 -07:00
|
|
|
TRACE("Created context %p\n", ALContext);
|
2007-11-13 18:02:18 -08:00
|
|
|
return ALContext;
|
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcDestroyContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Remove a context from its device
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::unique_lock<std::recursive_mutex> listlock{ListLock};
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx{VerifyContext(context)};
|
|
|
|
if(!ctx)
|
2017-03-28 05:33:43 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
2017-03-28 05:33:43 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-21 02:02:53 -08:00
|
|
|
ALCdevice* Device{ctx->Device};
|
2011-09-10 19:19:32 -07:00
|
|
|
if(Device)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{Device->BackendLock};
|
2018-11-21 02:02:53 -08:00
|
|
|
if(!ReleaseContext(ctx.get(), Device))
|
2011-09-10 19:19:32 -07:00
|
|
|
{
|
2013-11-02 17:30:28 -07:00
|
|
|
V0(Device->Backend,stop)();
|
2011-09-10 19:19:32 -07:00
|
|
|
Device->Flags &= ~DEVICE_RUNNING;
|
|
|
|
}
|
2011-06-15 01:59:07 -07:00
|
|
|
}
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetCurrentContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Returns the currently active context on the calling thread
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2012-04-20 01:18:38 -07:00
|
|
|
ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
|
2009-09-12 16:45:46 -07:00
|
|
|
{
|
2018-11-16 21:55:11 -08:00
|
|
|
ALCcontext *Context{LocalContext.get()};
|
2018-11-14 06:17:47 -08:00
|
|
|
if(!Context) Context = GlobalContext.load();
|
2011-06-15 23:22:34 -07:00
|
|
|
return Context;
|
2009-09-12 16:45:46 -07:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetThreadContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Returns the currently active thread-local context
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-23 17:44:01 -07:00
|
|
|
ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-16 21:55:11 -08:00
|
|
|
return LocalContext.get();
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcMakeContextCurrent
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Makes the given context the active process-wide context, and removes the
|
|
|
|
* thread-local context for the calling thread.
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
/* context must be valid or nullptr */
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx;
|
|
|
|
if(context)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-21 02:02:53 -08:00
|
|
|
ctx = VerifyContext(context);
|
|
|
|
if(!ctx)
|
|
|
|
{
|
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2018-11-21 02:02:53 -08:00
|
|
|
/* Release this reference (if any) to store it in the GlobalContext
|
|
|
|
* pointer. Take ownership of the reference (if any) that was previously
|
|
|
|
* stored there.
|
|
|
|
*/
|
|
|
|
ctx = ContextRef{GlobalContext.exchange(ctx.release())};
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-21 02:02:53 -08:00
|
|
|
/* Reset (decrement) the previous global reference by replacing it with the
|
|
|
|
* thread-local context. Take ownership of the thread-local context
|
|
|
|
* reference (if any), clearing the storage to null.
|
|
|
|
*/
|
|
|
|
ctx = ContextRef{LocalContext.get()};
|
|
|
|
if(ctx) LocalContext.set(nullptr);
|
|
|
|
/* Reset (decrement) the previous thread-local reference. */
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2011-09-12 03:18:27 -07:00
|
|
|
return ALC_TRUE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcSetThreadContext
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Makes the given context the active context for the current thread
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-23 19:49:31 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
|
2009-09-12 16:45:46 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
/* context must be valid or nullptr */
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx;
|
|
|
|
if(context)
|
2011-08-28 18:08:48 -07:00
|
|
|
{
|
2018-11-21 02:02:53 -08:00
|
|
|
ctx = VerifyContext(context);
|
|
|
|
if(!ctx)
|
|
|
|
{
|
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2011-08-28 18:08:48 -07:00
|
|
|
}
|
2011-09-12 03:18:27 -07:00
|
|
|
/* context's reference count is already incremented */
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef old{LocalContext.get()};
|
|
|
|
LocalContext.set(ctx.release());
|
2009-09-12 16:45:46 -07:00
|
|
|
|
2011-09-12 03:18:27 -07:00
|
|
|
return ALC_TRUE;
|
2009-09-12 16:45:46 -07:00
|
|
|
}
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcGetContextsDevice
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Returns the device that a particular context is attached to
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2011-09-12 03:18:27 -07:00
|
|
|
ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
|
2011-09-10 19:14:14 -07:00
|
|
|
{
|
2018-11-21 02:02:53 -08:00
|
|
|
ContextRef ctx{VerifyContext(Context)};
|
|
|
|
if(!ctx)
|
2011-09-12 03:18:27 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_CONTEXT);
|
|
|
|
return nullptr;
|
2011-09-12 03:18:27 -07:00
|
|
|
}
|
2018-11-21 02:02:53 -08:00
|
|
|
return ctx->Device;
|
2011-09-10 19:14:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* alcOpenDevice
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Opens the named device.
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2010-03-19 14:34:18 -07:00
|
|
|
ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2011-06-14 09:43:33 -07:00
|
|
|
DO_INITCONFIG();
|
|
|
|
|
2011-08-19 02:55:36 -07:00
|
|
|
if(!PlaybackBackend.name)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_VALUE);
|
|
|
|
return nullptr;
|
2011-08-19 02:55:36 -07:00
|
|
|
}
|
|
|
|
|
2015-08-13 21:18:39 -07:00
|
|
|
if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0
|
|
|
|
#ifdef _WIN32
|
2015-08-14 17:02:11 -07:00
|
|
|
/* Some old Windows apps hardcode these expecting OpenAL to use a
|
|
|
|
* specific audio API, even when they're not enumerated. Creative's
|
|
|
|
* router effectively ignores them too.
|
2015-08-13 21:18:39 -07:00
|
|
|
*/
|
2015-08-14 17:02:11 -07:00
|
|
|
|| strcasecmp(deviceName, "DirectSound3D") == 0 || strcasecmp(deviceName, "DirectSound") == 0
|
|
|
|
|| strcasecmp(deviceName, "MMSYSTEM") == 0
|
2015-08-13 21:18:39 -07:00
|
|
|
#endif
|
|
|
|
))
|
2018-11-14 04:15:44 -08:00
|
|
|
deviceName = nullptr;
|
2007-12-26 17:38:42 -08:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
std::unique_ptr<ALCdevice> device{new ALCdevice{Playback}};
|
2017-02-28 21:01:13 -08:00
|
|
|
|
2010-03-19 20:49:23 -07:00
|
|
|
//Set output format
|
2012-03-13 15:32:44 -07:00
|
|
|
device->FmtChans = DevFmtChannelsDefault;
|
|
|
|
device->FmtType = DevFmtTypeDefault;
|
2012-02-15 15:22:44 -08:00
|
|
|
device->Frequency = DEFAULT_OUTPUT_RATE;
|
2018-10-03 13:48:04 -07:00
|
|
|
device->LimiterState = ALC_TRUE;
|
2017-02-22 15:56:09 -08:00
|
|
|
device->NumUpdates = 3;
|
2011-09-18 16:16:55 -07:00
|
|
|
device->UpdateSize = 1024;
|
|
|
|
|
2018-01-28 18:03:54 -08:00
|
|
|
device->SourcesMax = 256;
|
|
|
|
device->AuxiliaryEffectSlotMax = 64;
|
|
|
|
device->NumAuxSends = DEFAULT_SENDS;
|
2013-10-27 14:24:55 -07:00
|
|
|
|
2018-11-16 00:06:54 -08:00
|
|
|
const ALCchar *fmt{};
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueStr(deviceName, nullptr, "channels", &fmt))
|
2012-02-15 15:22:44 -08:00
|
|
|
{
|
2018-11-16 05:23:42 -08:00
|
|
|
static constexpr struct ChannelMap {
|
2012-02-15 15:22:44 -08:00
|
|
|
const char name[16];
|
|
|
|
enum DevFmtChannels chans;
|
2017-04-12 18:26:07 -07:00
|
|
|
ALsizei order;
|
2012-02-15 15:22:44 -08:00
|
|
|
} chanlist[] = {
|
2017-04-12 18:26:07 -07:00
|
|
|
{ "mono", DevFmtMono, 0 },
|
|
|
|
{ "stereo", DevFmtStereo, 0 },
|
|
|
|
{ "quad", DevFmtQuad, 0 },
|
|
|
|
{ "surround51", DevFmtX51, 0 },
|
|
|
|
{ "surround61", DevFmtX61, 0 },
|
|
|
|
{ "surround71", DevFmtX71, 0 },
|
|
|
|
{ "surround51rear", DevFmtX51Rear, 0 },
|
|
|
|
{ "ambi1", DevFmtAmbi3D, 1 },
|
|
|
|
{ "ambi2", DevFmtAmbi3D, 2 },
|
|
|
|
{ "ambi3", DevFmtAmbi3D, 3 },
|
2012-02-15 15:22:44 -08:00
|
|
|
};
|
|
|
|
|
2018-11-16 05:23:42 -08:00
|
|
|
auto iter = std::find_if(std::begin(chanlist), std::end(chanlist),
|
|
|
|
[fmt](const ChannelMap &entry) -> bool
|
|
|
|
{ return strcasecmp(entry.name, fmt) == 0; }
|
|
|
|
);
|
|
|
|
if(iter == std::end(chanlist))
|
|
|
|
ERR("Unsupported channels: %s\n", fmt);
|
|
|
|
else
|
2012-02-15 15:22:44 -08:00
|
|
|
{
|
2018-11-16 05:23:42 -08:00
|
|
|
device->FmtChans = iter->chans;
|
2018-11-18 08:01:50 -08:00
|
|
|
device->mAmbiOrder = iter->order;
|
2018-11-16 05:23:42 -08:00
|
|
|
device->Flags |= DEVICE_CHANNELS_REQUEST;
|
2012-02-15 15:22:44 -08:00
|
|
|
}
|
|
|
|
}
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueStr(deviceName, nullptr, "sample-type", &fmt))
|
2012-02-15 15:22:44 -08:00
|
|
|
{
|
2018-11-16 05:23:42 -08:00
|
|
|
static constexpr struct TypeMap {
|
2012-02-15 15:22:44 -08:00
|
|
|
const char name[16];
|
|
|
|
enum DevFmtType type;
|
2012-02-19 11:56:01 -08:00
|
|
|
} typelist[] = {
|
2012-02-15 15:22:44 -08:00
|
|
|
{ "int8", DevFmtByte },
|
|
|
|
{ "uint8", DevFmtUByte },
|
|
|
|
{ "int16", DevFmtShort },
|
|
|
|
{ "uint16", DevFmtUShort },
|
|
|
|
{ "int32", DevFmtInt },
|
|
|
|
{ "uint32", DevFmtUInt },
|
|
|
|
{ "float32", DevFmtFloat },
|
|
|
|
};
|
|
|
|
|
2018-11-16 05:23:42 -08:00
|
|
|
auto iter = std::find_if(std::begin(typelist), std::end(typelist),
|
|
|
|
[fmt](const TypeMap &entry) -> bool
|
|
|
|
{ return strcasecmp(entry.name, fmt) == 0; }
|
|
|
|
);
|
|
|
|
if(iter == std::end(typelist))
|
|
|
|
ERR("Unsupported sample-type: %s\n", fmt);
|
|
|
|
else
|
2012-02-15 15:22:44 -08:00
|
|
|
{
|
2018-11-16 05:23:42 -08:00
|
|
|
device->FmtType = iter->type;
|
|
|
|
device->Flags |= DEVICE_SAMPLE_TYPE_REQUEST;
|
2012-02-15 15:22:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueUInt(deviceName, nullptr, "frequency", &device->Frequency))
|
2012-02-15 12:26:19 -08:00
|
|
|
{
|
2011-05-03 02:29:26 -07:00
|
|
|
device->Flags |= DEVICE_FREQUENCY_REQUEST;
|
2012-02-15 12:26:19 -08:00
|
|
|
if(device->Frequency < MIN_OUTPUT_RATE)
|
|
|
|
ERR("%uhz request clamped to %uhz minimum\n", device->Frequency, MIN_OUTPUT_RATE);
|
|
|
|
device->Frequency = maxu(device->Frequency, MIN_OUTPUT_RATE);
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(deviceName, nullptr, "periods", &device->NumUpdates);
|
2012-03-04 05:44:37 -08:00
|
|
|
device->NumUpdates = clampu(device->NumUpdates, 2, 16);
|
2009-09-16 22:58:54 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(deviceName, nullptr, "period_size", &device->UpdateSize);
|
2012-03-04 05:44:37 -08:00
|
|
|
device->UpdateSize = clampu(device->UpdateSize, 64, 8192);
|
2014-11-17 21:33:09 -08:00
|
|
|
if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
|
2012-09-20 14:54:28 -07:00
|
|
|
device->UpdateSize = (device->UpdateSize+3)&~3;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(deviceName, nullptr, "sources", &device->SourcesMax);
|
2016-06-08 11:19:33 -07:00
|
|
|
if(device->SourcesMax == 0) device->SourcesMax = 256;
|
2008-01-19 19:28:34 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(deviceName, nullptr, "slots", &device->AuxiliaryEffectSlotMax);
|
2017-02-21 16:54:55 -08:00
|
|
|
if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
|
2018-03-04 14:00:28 -08:00
|
|
|
else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
|
2009-06-07 14:53:22 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueInt(deviceName, nullptr, "sends", &device->NumAuxSends))
|
2017-02-21 17:19:02 -08:00
|
|
|
device->NumAuxSends = clampi(
|
|
|
|
DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
|
|
|
|
);
|
2009-07-06 03:09:01 -07:00
|
|
|
|
2011-09-18 16:16:55 -07:00
|
|
|
device->NumStereoSources = 1;
|
2016-06-08 11:19:33 -07:00
|
|
|
device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
|
2009-09-15 19:06:47 -07:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
device->Backend = PlaybackBackend.getFactory().createBackend(
|
|
|
|
device.get(), ALCbackend_Playback);
|
2018-01-28 18:03:54 -08:00
|
|
|
if(!device->Backend)
|
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
2018-01-28 18:03:54 -08:00
|
|
|
}
|
|
|
|
|
2010-03-19 20:49:23 -07:00
|
|
|
// Find a playback device to open
|
2018-11-16 00:06:54 -08:00
|
|
|
ALCenum err{V(device->Backend,open)(deviceName)};
|
|
|
|
if(err != ALC_NO_ERROR)
|
2010-03-19 20:49:23 -07:00
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, err);
|
|
|
|
return nullptr;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-18 18:45:45 -08:00
|
|
|
if(ConfigValueStr(device->DeviceName.c_str(), nullptr, "ambi-format", &fmt))
|
2016-07-31 07:46:38 -07:00
|
|
|
{
|
|
|
|
if(strcasecmp(fmt, "fuma") == 0)
|
2017-02-27 16:11:45 -08:00
|
|
|
{
|
2018-11-20 22:47:24 -08:00
|
|
|
device->mAmbiLayout = AmbiLayout::FuMa;
|
|
|
|
device->mAmbiScale = AmbiNorm::FuMa;
|
2017-02-27 16:11:45 -08:00
|
|
|
}
|
2016-07-31 07:46:38 -07:00
|
|
|
else if(strcasecmp(fmt, "acn+sn3d") == 0)
|
2017-02-27 16:11:45 -08:00
|
|
|
{
|
2018-11-20 22:47:24 -08:00
|
|
|
device->mAmbiLayout = AmbiLayout::ACN;
|
|
|
|
device->mAmbiScale = AmbiNorm::SN3D;
|
2017-02-27 16:11:45 -08:00
|
|
|
}
|
2016-07-31 07:46:38 -07:00
|
|
|
else if(strcasecmp(fmt, "acn+n3d") == 0)
|
2017-02-27 16:11:45 -08:00
|
|
|
{
|
2018-11-20 22:47:24 -08:00
|
|
|
device->mAmbiLayout = AmbiLayout::ACN;
|
|
|
|
device->mAmbiScale = AmbiNorm::N3D;
|
2017-02-27 16:11:45 -08:00
|
|
|
}
|
2016-07-31 07:46:38 -07:00
|
|
|
else
|
|
|
|
ERR("Unsupported ambi-format: %s\n", fmt);
|
|
|
|
}
|
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *head{DeviceList.load()};
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2018-11-20 22:47:24 -08:00
|
|
|
device->next.store(head, std::memory_order_relaxed);
|
2018-11-23 20:16:34 -08:00
|
|
|
} while(!DeviceList.compare_exchange_weak(head, device.get()));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2011-09-12 03:57:53 -07:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
TRACE("Created device %p, \"%s\"\n", device.get(), device->DeviceName.c_str());
|
|
|
|
return device.release();
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcCloseDevice
|
|
|
|
*
|
2012-04-20 01:18:38 -07:00
|
|
|
* Closes the given device.
|
2011-09-10 19:14:14 -07:00
|
|
|
*/
|
2014-08-01 02:04:40 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::unique_lock<std::recursive_mutex> listlock{ListLock};
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *iter{DeviceList.load()};
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2016-11-22 03:00:16 -08:00
|
|
|
if(iter == device)
|
2014-08-01 02:04:40 -07:00
|
|
|
break;
|
2018-11-21 07:52:17 -08:00
|
|
|
iter = iter->next.load(std::memory_order_relaxed);
|
2018-11-14 04:15:44 -08:00
|
|
|
} while(iter != nullptr);
|
2016-11-22 03:00:16 -08:00
|
|
|
if(!iter || iter->Type == Capture)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2016-11-22 03:00:16 -08:00
|
|
|
alcSetError(iter, ALC_INVALID_DEVICE);
|
2010-03-19 20:49:23 -07:00
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2018-11-26 21:29:11 -08:00
|
|
|
std::unique_lock<std::mutex> backlock{device->BackendLock};
|
2008-01-14 10:39:54 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *origdev{device};
|
2018-11-21 07:52:17 -08:00
|
|
|
ALCdevice *nextdev{device->next.load(std::memory_order_relaxed)};
|
2018-11-14 06:17:47 -08:00
|
|
|
if(!DeviceList.compare_exchange_strong(origdev, nextdev))
|
2014-08-01 02:04:40 -07:00
|
|
|
{
|
2018-03-02 18:38:59 -08:00
|
|
|
ALCdevice *list;
|
|
|
|
do {
|
|
|
|
list = origdev;
|
|
|
|
origdev = device;
|
2018-11-19 03:21:58 -08:00
|
|
|
} while(!list->next.compare_exchange_strong(origdev, nextdev));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2010-03-19 20:49:23 -07:00
|
|
|
|
2018-11-21 07:52:17 -08:00
|
|
|
ALCcontext *ctx{device->ContextList.load()};
|
2018-11-14 04:15:44 -08:00
|
|
|
while(ctx != nullptr)
|
2010-03-19 20:49:23 -07:00
|
|
|
{
|
2018-11-21 07:52:17 -08:00
|
|
|
ALCcontext *next = ctx->next.load(std::memory_order_relaxed);
|
2012-03-02 00:10:24 -08:00
|
|
|
WARN("Releasing context %p\n", ctx);
|
2014-08-01 02:04:40 -07:00
|
|
|
ReleaseContext(ctx, device);
|
|
|
|
ctx = next;
|
2011-09-10 19:14:14 -07:00
|
|
|
}
|
2014-08-01 02:04:40 -07:00
|
|
|
if((device->Flags&DEVICE_RUNNING))
|
|
|
|
V0(device->Backend,stop)();
|
|
|
|
device->Flags &= ~DEVICE_RUNNING;
|
2018-11-20 23:42:21 -08:00
|
|
|
backlock.unlock();
|
2012-03-02 00:10:24 -08:00
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
ALCdevice_DecRef(device);
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2010-03-19 20:49:23 -07:00
|
|
|
return ALC_TRUE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2008-01-14 10:54:33 -08:00
|
|
|
|
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
/************************************************
|
|
|
|
* ALC capture functions
|
|
|
|
************************************************/
|
|
|
|
ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
|
|
|
|
{
|
|
|
|
DO_INITCONFIG();
|
|
|
|
|
|
|
|
if(!CaptureBackend.name)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_VALUE);
|
|
|
|
return nullptr;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if(samples <= 0)
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_VALUE);
|
|
|
|
return nullptr;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0))
|
2018-11-14 04:15:44 -08:00
|
|
|
deviceName = nullptr;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
std::unique_ptr<ALCdevice> device{new ALCdevice{Capture}};
|
2015-10-06 00:23:11 -07:00
|
|
|
|
2012-04-20 01:18:38 -07:00
|
|
|
device->Frequency = frequency;
|
2018-01-28 18:03:54 -08:00
|
|
|
device->Flags |= DEVICE_FREQUENCY_REQUEST;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
|
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_ENUM);
|
|
|
|
return nullptr;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
2018-01-28 18:03:54 -08:00
|
|
|
device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
device->UpdateSize = samples;
|
|
|
|
device->NumUpdates = 1;
|
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
device->Backend = CaptureBackend.getFactory().createBackend(device.get(), ALCbackend_Capture);
|
2018-01-28 18:03:54 -08:00
|
|
|
if(!device->Backend)
|
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
2018-01-28 18:03:54 -08:00
|
|
|
}
|
|
|
|
|
2017-04-10 09:28:25 -07:00
|
|
|
TRACE("Capture format: %s, %s, %uhz, %u update size x%d\n",
|
|
|
|
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
|
|
|
|
device->Frequency, device->UpdateSize, device->NumUpdates
|
|
|
|
);
|
2018-11-16 00:06:54 -08:00
|
|
|
ALCenum err{V(device->Backend,open)(deviceName)};
|
|
|
|
if(err != ALC_NO_ERROR)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, err);
|
|
|
|
return nullptr;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *head{DeviceList.load()};
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2018-11-21 07:52:17 -08:00
|
|
|
device->next.store(head, std::memory_order_relaxed);
|
2018-11-23 20:16:34 -08:00
|
|
|
} while(!DeviceList.compare_exchange_weak(head, device.get()));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
TRACE("Created device %p, \"%s\"\n", device.get(), device->DeviceName.c_str());
|
|
|
|
return device.release();
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::unique_lock<std::recursive_mutex> listlock{ListLock};
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *iter{DeviceList.load()};
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2016-11-22 03:00:16 -08:00
|
|
|
if(iter == device)
|
2014-08-01 02:04:40 -07:00
|
|
|
break;
|
2018-11-21 07:52:17 -08:00
|
|
|
iter = iter->next.load(std::memory_order_relaxed);
|
2018-11-14 04:15:44 -08:00
|
|
|
} while(iter != nullptr);
|
2016-11-22 03:00:16 -08:00
|
|
|
if(!iter || iter->Type != Capture)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2016-11-22 03:00:16 -08:00
|
|
|
alcSetError(iter, ALC_INVALID_DEVICE);
|
2012-04-20 01:18:38 -07:00
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *origdev{device};
|
2018-11-21 07:52:17 -08:00
|
|
|
ALCdevice *nextdev{device->next.load(std::memory_order_relaxed)};
|
2018-11-14 04:15:44 -08:00
|
|
|
if(!DeviceList.compare_exchange_strong(origdev, nextdev))
|
2014-08-01 02:04:40 -07:00
|
|
|
{
|
2018-03-02 18:38:59 -08:00
|
|
|
ALCdevice *list;
|
|
|
|
do {
|
|
|
|
list = origdev;
|
|
|
|
origdev = device;
|
2018-11-19 03:21:58 -08:00
|
|
|
} while(!list->next.compare_exchange_strong(origdev, nextdev));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2012-04-20 01:18:38 -07:00
|
|
|
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{device->BackendLock};
|
2018-11-20 23:42:21 -08:00
|
|
|
if((device->Flags&DEVICE_RUNNING))
|
|
|
|
V0(device->Backend,stop)();
|
|
|
|
device->Flags &= ~DEVICE_RUNNING;
|
|
|
|
}
|
2018-06-08 01:42:13 +00:00
|
|
|
|
2014-08-01 02:04:40 -07:00
|
|
|
ALCdevice_DecRef(device);
|
2012-04-20 01:18:38 -07:00
|
|
|
|
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Capture)
|
|
|
|
{
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
if(!dev->Connected.load(std::memory_order_acquire))
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
|
|
|
else if(!(dev->Flags&DEVICE_RUNNING))
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
if(V0(dev->Backend,start)())
|
|
|
|
dev->Flags |= DEVICE_RUNNING;
|
|
|
|
else
|
2012-10-07 00:13:23 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
aluHandleDisconnect(dev.get(), "Device start failure");
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2012-10-07 00:13:23 -07:00
|
|
|
}
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Capture)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2012-10-07 00:13:23 -07:00
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
if((dev->Flags&DEVICE_RUNNING))
|
|
|
|
V0(dev->Backend,stop)();
|
|
|
|
dev->Flags &= ~DEVICE_RUNNING;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Capture)
|
2012-04-20 01:18:38 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
|
|
|
return;
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
2018-11-23 17:54:12 -08:00
|
|
|
|
|
|
|
ALCenum err{ALC_INVALID_VALUE};
|
2018-11-26 21:29:11 -08:00
|
|
|
{ std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
if(samples >= 0 && V0(dev->Backend,availableSamples)() >= (ALCuint)samples)
|
|
|
|
err = V(dev->Backend,captureSamples)(buffer, samples);
|
|
|
|
}
|
|
|
|
if(err != ALC_NO_ERROR)
|
|
|
|
alcSetError(dev.get(), err);
|
2012-04-20 01:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* ALC loopback functions
|
|
|
|
************************************************/
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcLoopbackOpenDeviceSOFT
|
|
|
|
*
|
|
|
|
* Open a loopback device, for manual rendering.
|
|
|
|
*/
|
2012-03-01 08:30:21 -08:00
|
|
|
ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
|
2011-03-11 00:13:44 -08:00
|
|
|
{
|
2011-06-14 09:43:33 -07:00
|
|
|
DO_INITCONFIG();
|
|
|
|
|
2012-03-01 08:30:21 -08:00
|
|
|
/* Make sure the device name, if specified, is us. */
|
|
|
|
if(deviceName && strcmp(deviceName, alcDefaultName) != 0)
|
2012-02-15 20:52:49 -08:00
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_INVALID_VALUE);
|
|
|
|
return nullptr;
|
2012-02-15 20:52:49 -08:00
|
|
|
}
|
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
std::unique_ptr<ALCdevice> device{new ALCdevice{Loopback}};
|
2014-02-01 16:37:11 -08:00
|
|
|
|
2016-06-08 11:19:33 -07:00
|
|
|
device->SourcesMax = 256;
|
2017-02-21 16:54:55 -08:00
|
|
|
device->AuxiliaryEffectSlotMax = 64;
|
|
|
|
device->NumAuxSends = DEFAULT_SENDS;
|
2011-09-18 16:16:55 -07:00
|
|
|
|
2011-03-11 00:13:44 -08:00
|
|
|
//Set output format
|
2011-09-18 16:16:55 -07:00
|
|
|
device->NumUpdates = 0;
|
|
|
|
device->UpdateSize = 0;
|
|
|
|
|
2012-03-13 15:32:44 -07:00
|
|
|
device->Frequency = DEFAULT_OUTPUT_RATE;
|
|
|
|
device->FmtChans = DevFmtChannelsDefault;
|
|
|
|
device->FmtType = DevFmtTypeDefault;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(nullptr, nullptr, "sources", &device->SourcesMax);
|
2016-06-08 11:19:33 -07:00
|
|
|
if(device->SourcesMax == 0) device->SourcesMax = 256;
|
2011-09-18 16:16:55 -07:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
ConfigValueUInt(nullptr, nullptr, "slots", &device->AuxiliaryEffectSlotMax);
|
2017-02-21 16:54:55 -08:00
|
|
|
if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
|
2018-03-04 14:00:28 -08:00
|
|
|
else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2018-11-14 04:15:44 -08:00
|
|
|
if(ConfigValueInt(nullptr, nullptr, "sends", &device->NumAuxSends))
|
2017-02-21 17:19:02 -08:00
|
|
|
device->NumAuxSends = clampi(
|
|
|
|
DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
|
|
|
|
);
|
2011-03-11 00:13:44 -08:00
|
|
|
|
|
|
|
device->NumStereoSources = 1;
|
2016-06-08 11:19:33 -07:00
|
|
|
device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
|
2011-03-11 00:13:44 -08:00
|
|
|
|
2018-11-15 19:42:13 -08:00
|
|
|
device->Backend = LoopbackBackendFactory::getFactory().createBackend(
|
2018-11-23 20:16:34 -08:00
|
|
|
device.get(), ALCbackend_Loopback);
|
2018-01-28 18:03:54 -08:00
|
|
|
if(!device->Backend)
|
|
|
|
{
|
2018-11-23 20:16:34 -08:00
|
|
|
device = nullptr;
|
2018-11-14 04:15:44 -08:00
|
|
|
alcSetError(nullptr, ALC_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
2018-01-28 18:03:54 -08:00
|
|
|
}
|
|
|
|
|
2011-03-11 00:13:44 -08:00
|
|
|
// Open the "backend"
|
2013-11-02 17:30:28 -07:00
|
|
|
V(device->Backend,open)("Loopback");
|
2014-08-01 02:04:40 -07:00
|
|
|
|
|
|
|
{
|
2018-11-14 04:15:44 -08:00
|
|
|
ALCdevice *head{DeviceList.load()};
|
2014-08-01 02:04:40 -07:00
|
|
|
do {
|
2018-11-21 07:52:17 -08:00
|
|
|
device->next.store(head, std::memory_order_relaxed);
|
2018-11-23 20:16:34 -08:00
|
|
|
} while(!DeviceList.compare_exchange_weak(head, device.get()));
|
2014-08-01 02:04:40 -07:00
|
|
|
}
|
2011-09-18 16:16:55 -07:00
|
|
|
|
2018-11-23 20:16:34 -08:00
|
|
|
TRACE("Created device %p\n", device.get());
|
|
|
|
return device.release();
|
2011-03-11 00:13:44 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcIsRenderFormatSupportedSOFT
|
|
|
|
*
|
|
|
|
* Determines if the loopback device supports the given format for rendering.
|
|
|
|
*/
|
2011-07-02 02:51:33 -07:00
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type)
|
2011-03-11 00:13:44 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Loopback)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2011-03-12 06:23:17 -08:00
|
|
|
else if(freq <= 0)
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2011-03-12 06:23:17 -08:00
|
|
|
else
|
|
|
|
{
|
Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).
Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */
In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 18:34:23 -08:00
|
|
|
if(IsValidALCType(type) && IsValidALCChannels(channels) && freq >= MIN_OUTPUT_RATE)
|
2018-11-23 17:54:12 -08:00
|
|
|
return ALC_TRUE;
|
2011-03-11 00:13:44 -08:00
|
|
|
}
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
return ALC_FALSE;
|
2011-03-11 00:13:44 -08:00
|
|
|
}
|
|
|
|
|
2011-09-10 19:14:14 -07:00
|
|
|
/* alcRenderSamplesSOFT
|
|
|
|
*
|
|
|
|
* Renders some samples into a buffer, using the format last set by the
|
|
|
|
* attributes given to alcCreateContext.
|
|
|
|
*/
|
2013-11-25 17:29:39 -08:00
|
|
|
FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
|
2011-03-11 00:13:44 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Loopback)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2018-11-14 04:15:44 -08:00
|
|
|
else if(samples < 0 || (samples > 0 && buffer == nullptr))
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2011-03-11 00:13:44 -08:00
|
|
|
else
|
2017-02-18 17:32:07 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
V0(dev->Backend,lock)();
|
|
|
|
aluMixData(dev.get(), buffer, samples);
|
|
|
|
V0(dev->Backend,unlock)();
|
2017-02-18 17:32:07 -08:00
|
|
|
}
|
2011-03-11 00:13:44 -08:00
|
|
|
}
|
2014-01-15 16:27:17 -08:00
|
|
|
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* ALC DSP pause/resume functions
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
/* alcDevicePauseSOFT
|
|
|
|
*
|
|
|
|
* Pause the DSP to stop audio processing.
|
|
|
|
*/
|
|
|
|
ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Playback)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2014-01-15 16:27:17 -08:00
|
|
|
else
|
|
|
|
{
|
2018-11-26 21:29:11 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
if((dev->Flags&DEVICE_RUNNING))
|
|
|
|
V0(dev->Backend,stop)();
|
|
|
|
dev->Flags &= ~DEVICE_RUNNING;
|
|
|
|
dev->Flags |= DEVICE_PAUSED;
|
2014-01-15 16:27:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* alcDeviceResumeSOFT
|
|
|
|
*
|
|
|
|
* Resume the DSP to restart audio processing.
|
|
|
|
*/
|
|
|
|
ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
|
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type != Playback)
|
2018-11-28 11:55:43 -08:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2018-11-28 11:55:43 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
|
|
|
if(!(dev->Flags&DEVICE_PAUSED))
|
|
|
|
return;
|
|
|
|
dev->Flags &= ~DEVICE_PAUSED;
|
|
|
|
if(dev->ContextList.load() == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(V0(dev->Backend,start)() == ALC_FALSE)
|
2014-01-15 16:27:17 -08:00
|
|
|
{
|
2018-11-28 11:55:43 -08:00
|
|
|
V0(dev->Backend,lock)();
|
|
|
|
aluHandleDisconnect(dev.get(), "Device start failure");
|
|
|
|
V0(dev->Backend,unlock)();
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
|
|
|
return;
|
2014-01-15 16:27:17 -08:00
|
|
|
}
|
2018-11-28 11:55:43 -08:00
|
|
|
dev->Flags |= DEVICE_RUNNING;
|
2014-01-15 16:27:17 -08:00
|
|
|
}
|
2015-10-02 23:54:30 -07:00
|
|
|
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
* ALC HRTF functions
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
/* alcGetStringiSOFT
|
|
|
|
*
|
|
|
|
* Gets a string parameter at the given index.
|
|
|
|
*/
|
2015-10-07 03:29:53 -07:00
|
|
|
ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index)
|
2015-10-02 23:54:30 -07:00
|
|
|
{
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
|
|
|
if(!dev || dev->Type == Capture)
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2015-10-02 23:54:30 -07:00
|
|
|
else switch(paramName)
|
|
|
|
{
|
|
|
|
case ALC_HRTF_SPECIFIER_SOFT:
|
2018-11-23 17:54:12 -08:00
|
|
|
if(index >= 0 && (size_t)index < dev->HrtfList.size())
|
|
|
|
return dev->HrtfList[index].name.c_str();
|
|
|
|
alcSetError(dev.get(), ALC_INVALID_VALUE);
|
2015-10-02 23:54:30 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_ENUM);
|
2015-10-02 23:54:30 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
return nullptr;
|
2015-10-02 23:54:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* alcResetDeviceSOFT
|
|
|
|
*
|
|
|
|
* Resets the given device output, using the specified attribute list.
|
|
|
|
*/
|
|
|
|
ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs)
|
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
std::unique_lock<std::recursive_mutex> listlock{ListLock};
|
2018-11-23 17:54:12 -08:00
|
|
|
DeviceRef dev{VerifyDevice(device)};
|
2018-11-28 19:29:20 -08:00
|
|
|
if(!dev || dev->Type == Capture)
|
2015-10-02 23:54:30 -07:00
|
|
|
{
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2018-11-23 17:54:12 -08:00
|
|
|
alcSetError(dev.get(), ALC_INVALID_DEVICE);
|
2015-10-02 23:54:30 -07:00
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2018-11-28 11:55:43 -08:00
|
|
|
std::lock_guard<std::mutex> _{dev->BackendLock};
|
2018-11-14 06:17:47 -08:00
|
|
|
listlock.unlock();
|
2015-10-02 23:54:30 -07:00
|
|
|
|
2018-11-28 12:24:12 -08:00
|
|
|
/* Force the backend to stop mixing first since we're resetting. Also reset
|
|
|
|
* the connected state so lost devices can attempt recover.
|
|
|
|
*/
|
|
|
|
if((dev->Flags&DEVICE_RUNNING))
|
|
|
|
V0(dev->Backend,stop)();
|
|
|
|
dev->Flags &= ~DEVICE_RUNNING;
|
|
|
|
device->Connected.store(AL_TRUE);
|
|
|
|
|
2018-11-23 17:54:12 -08:00
|
|
|
ALCenum err{UpdateDeviceParams(dev.get(), attribs)};
|
2018-11-28 11:55:43 -08:00
|
|
|
if(LIKELY(err == ALC_NO_ERROR)) return ALC_TRUE;
|
2016-05-27 19:23:39 -07:00
|
|
|
|
2018-11-28 11:55:43 -08:00
|
|
|
alcSetError(dev.get(), err);
|
|
|
|
if(err == ALC_INVALID_DEVICE)
|
2015-10-02 23:54:30 -07:00
|
|
|
{
|
2018-11-28 11:55:43 -08:00
|
|
|
V0(dev->Backend,lock)();
|
|
|
|
aluHandleDisconnect(dev.get(), "Device start failure");
|
|
|
|
V0(dev->Backend,unlock)();
|
2015-10-02 23:54:30 -07:00
|
|
|
}
|
2018-11-28 11:55:43 -08:00
|
|
|
return ALC_FALSE;
|
2015-10-02 23:54:30 -07:00
|
|
|
}
|