mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-21 07:33:45 +00:00
Merge branch 'development' of https://github.com/TorqueGameEngines/Torque3D into Enumnanigans
This commit is contained in:
commit
72c67e196a
394 changed files with 49666 additions and 46898 deletions
|
|
@ -108,10 +108,11 @@ mark_as_advanced(SDL_WAYLAND)
|
|||
mark_as_advanced(SDL_WERROR)
|
||||
mark_as_advanced(SDL_X11)
|
||||
mark_as_advanced(SDL_XINPUT)
|
||||
|
||||
mark_as_advanced(SDL2_DIR)
|
||||
add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(convexMath ${TORQUE_LIB_TARG_DIRECTORY}/convexMath EXCLUDE_FROM_ALL)
|
||||
|
||||
# Assimp
|
||||
advanced_option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
|
||||
|
|
@ -189,7 +190,7 @@ mark_as_advanced(PNG_PREFIX)
|
|||
add_subdirectory(tinyxml ${TORQUE_LIB_TARG_DIRECTORY}/tinyxml EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(opcode ${TORQUE_LIB_TARG_DIRECTORY}/opcode EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(pcre ${TORQUE_LIB_TARG_DIRECTORY}/pcre EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(convexDecomp ${TORQUE_LIB_TARG_DIRECTORY}/convexDecomp EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(squish ${TORQUE_LIB_TARG_DIRECTORY}/squish EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(collada ${TORQUE_LIB_TARG_DIRECTORY}/collada EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
|
||||
|
|
@ -198,64 +199,59 @@ add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
|
|||
if(TORQUE_SFX_OPENAL)
|
||||
advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
|
||||
advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||
advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_RTKIT)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
||||
#Hide some unnecessary fields as advanced
|
||||
mark_as_advanced(ALSOFT_INSTALL_AMBDEC_PRESETS)
|
||||
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
||||
mark_as_advanced(ALSOFT_BACKEND_MMDEVAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
||||
mark_as_advanced(ALSOFT_BACKEND_JACK)
|
||||
mark_as_advanced(ALSOFT_BACKEND_OBOE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_OPENSL)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PORTAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PULSEAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_SDL2)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WAVE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WINMM)
|
||||
mark_as_advanced(ALSOFT_INSTALL_CONFIG)
|
||||
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_NEON)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE2)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE3)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
|
||||
mark_as_advanced(ALSOFT_DLOPEN)
|
||||
mark_as_advanced(ALSOFT_EMBED_HRTF_DATA)
|
||||
mark_as_advanced(ALSOFT_EXAMPLES)
|
||||
mark_as_advanced(ALSOFT_INSTALL_HRTF_DATA)
|
||||
mark_as_advanced(ALSOFT_EAX)
|
||||
advanced_option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
|
||||
advanced_option(ALSOFT_EXAMPLES "Build example programs" OFF)
|
||||
mark_as_advanced(ALSOFT_INSTALL)
|
||||
advanced_option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||
mark_as_advanced(ALSOFT_UPDATE_BUILD_VERSION)
|
||||
mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
|
||||
mark_as_advanced(ALSOFT_NO_UID_DEFS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_ALSA)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_JACK)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_MMDEVAPI)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_NEON)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OSS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_QSA)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SNDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SOLARIS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WINMM)
|
||||
mark_as_advanced(ALSOFT_SEARCH_INSTALL_DATADIR)
|
||||
mark_as_advanced(ALSOFT_TESTS)
|
||||
mark_as_advanced(ALSOFT_UTILS)
|
||||
mark_as_advanced(ALSOFT_WERROR)
|
||||
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
||||
mark_as_advanced(CMAKE_DEBUG_POSTFIX)
|
||||
mark_as_advanced(FORCE_STATIC_VCRT)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_OSX_FRAMEWORK)
|
||||
mark_as_advanced(ALSOFT_STATIC_LIBGCC)
|
||||
mark_as_advanced(ALSOFT_STATIC_STDCXX)
|
||||
mark_as_advanced(AUDIOTOOLBOX_LIBRARY)
|
||||
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
advanced_option(INSTALL_DOCS "" OFF)
|
||||
|
|
@ -272,9 +268,33 @@ add_subdirectory(flac ${TORQUE_LIB_TARG_DIRECTORY}/flac EXCLUDE_FROM_ALL)
|
|||
advanced_option(INSTALL_DOCS "" OFF)
|
||||
advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
|
||||
advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
|
||||
mark_as_advanced(OPUS_ASSERTIONS)
|
||||
mark_as_advanced(OPUS_BUILD_PROGRAMS)
|
||||
mark_as_advanced(OPUS_BUILD_SHARED_LIBRARY)
|
||||
mark_as_advanced(OPUS_BUILD_TESTING)
|
||||
mark_as_advanced(OPUS_CHECK_ASM)
|
||||
mark_as_advanced(OPUS_CUSTOM_MODES)
|
||||
mark_as_advanced(OPUS_DISABLE_INTRINSICS)
|
||||
mark_as_advanced(OPUS_ENABLE_FLOAT_API)
|
||||
mark_as_advanced(OPUS_FIXED_POINT)
|
||||
mark_as_advanced(OPUS_FLOAT_APPROX)
|
||||
mark_as_advanced(OPUS_FUZZING)
|
||||
mark_as_advanced(OPUS_HARDENING)
|
||||
mark_as_advanced(OPUS_STACK_PROTECTOR)
|
||||
mark_as_advanced(OPUS_USE_ALLOCA)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_AVX)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE2)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE4_1)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_AVX)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE2)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE4_1)
|
||||
add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
|
||||
|
||||
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR)
|
||||
mark_as_advanced(PULSEAUDIO_LIBRARY)
|
||||
advanced_option(BUILD_PROGRAMS "" OFF)
|
||||
advanced_option(BUILD_EXAMPLES "" OFF)
|
||||
advanced_option(ENABLE_CPACK "" OFF)
|
||||
|
|
@ -287,4 +307,29 @@ add_subdirectory(libsndfile ${TORQUE_LIB_TARG_DIRECTORY}/libsndfile EXCLUDE_FROM
|
|||
if(TORQUE_TESTING)
|
||||
add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
#endif()
|
||||
#endif()
|
||||
|
||||
#misc randoms
|
||||
mark_as_advanced(WINDRES)
|
||||
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
||||
mark_as_advanced(CCACHE_BINARY)
|
||||
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
||||
mark_as_advanced(FORCE_STATIC_VCRT)
|
||||
mark_as_advanced(HAVE_GIT)
|
||||
mark_as_advanced(LIBRARY_SUFFIX)
|
||||
mark_as_advanced(USE_STATIC_CRT)
|
||||
|
||||
mark_as_advanced(BUILD_CXXLIBS)
|
||||
mark_as_advanced(BUILD_DOCS)
|
||||
mark_as_advanced(BUILD_REGTEST)
|
||||
mark_as_advanced(BUILD_UTILS)
|
||||
|
||||
mark_as_advanced(ENABLE_64_BIT_WORDS)
|
||||
mark_as_advanced(ENABLE_BOW_DOCS)
|
||||
mark_as_advanced(ENABLE_EXTERNAL_LIBS)
|
||||
|
||||
mark_as_advanced(WITH_ASM)
|
||||
mark_as_advanced(WITH_AVX)
|
||||
mark_as_advanced(WITH_FORTIFY_SOURCE)
|
||||
mark_as_advanced(WITH_OGG)
|
||||
mark_as_advanced(WITH_STACK_PROTECTOR)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# When on Windows, we need to link against winsock and windows codecs
|
||||
if (WIN32)
|
||||
set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} WS2_32.LIB windowscodecs.lib)
|
||||
set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} WS2_32.LIB windowscodecs.lib winmm.lib)
|
||||
if (TORQUE_D3D11)
|
||||
set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} dxguid.lib)
|
||||
endif (TORQUE_D3D11)
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
file(GLOB CONVEX_DECOMP_SOURCES "*.cpp")
|
||||
add_library(convexDecomp STATIC ${CONVEX_DECOMP_SOURCES})
|
||||
target_include_directories(convexDecomp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_compile_definitions(convexDecomp PUBLIC LINUX)
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
|
||||
NvConcavityVolume.cpp : This is a code snippet that computes the volume of concavity of a traingle mesh.
|
||||
|
||||
*/
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#define SHOW_DEBUG 0
|
||||
#if SHOW_DEBUG
|
||||
#include "RenderDebug.h"
|
||||
#endif
|
||||
#include "NvConcavityVolume.h"
|
||||
#include "NvFloatMath.h"
|
||||
#include "NvRayCast.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma warning(disable:4100 4189 4505 4127 4101)
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
bool raycast(const NxF32 *p1,const NxF32 *normal,NxF32 *dest,iRayCast *cast_hull,iRayCast *cast_mesh)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
NxF32 hit_hull[3];
|
||||
NxF32 hit_hullNormal[3];
|
||||
|
||||
NxF32 hit_mesh[3];
|
||||
NxF32 hit_meshNormal[3];
|
||||
|
||||
bool hitHull = cast_hull->castRay(p1,normal,hit_hull,hit_hullNormal);
|
||||
bool hitMesh = cast_mesh->castRay(p1,normal,hit_mesh,hit_meshNormal);
|
||||
|
||||
if ( hitMesh )
|
||||
{
|
||||
float dot = fm_dot(normal,hit_meshNormal);
|
||||
if ( dot < 0 ) // skip if we hit an internal face of the mesh when projection out towards the convex hull.
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
NxF32 d1 = fm_distanceSquared(p1,hit_mesh);
|
||||
NxF32 d2 = fm_distanceSquared(p1,hit_hull);
|
||||
if ( d1 < d2 )
|
||||
{
|
||||
dest[0] = hit_mesh[0];
|
||||
dest[1] = hit_mesh[1];
|
||||
dest[2] = hit_mesh[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[0] = hit_hull[0];
|
||||
dest[1] = hit_hull[1];
|
||||
dest[2] = hit_hull[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( hitHull )
|
||||
{
|
||||
dest[0] = hit_hull[0];
|
||||
dest[1] = hit_hull[1];
|
||||
dest[2] = hit_hull[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void addTri(NxU32 *indices,NxU32 i1,NxU32 i2,NxU32 i3,NxU32 &tcount)
|
||||
{
|
||||
indices[tcount*3+0] = i1;
|
||||
indices[tcount*3+1] = i2;
|
||||
indices[tcount*3+2] = i3;
|
||||
tcount++;
|
||||
}
|
||||
|
||||
NxF32 computeConcavityVolume(NxU32 vcount_hull,
|
||||
const NxF32 *vertices_hull,
|
||||
NxU32 tcount_hull,
|
||||
const NxU32 *indices_hull,
|
||||
NxU32 vcount_mesh,
|
||||
const NxF32 *vertices_mesh,
|
||||
NxU32 tcount_mesh,
|
||||
const NxU32 *indices_mesh)
|
||||
{
|
||||
NxF32 total_volume = 0;
|
||||
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->pushRenderState();
|
||||
NVSHARE::gRenderDebug->setCurrentDisplayTime(150.0f);
|
||||
#endif
|
||||
|
||||
iRayCast *cast_hull = createRayCast(vertices_hull,tcount_hull,indices_hull);
|
||||
iRayCast *cast_mesh = createRayCast(vertices_mesh,tcount_mesh,indices_mesh);
|
||||
|
||||
|
||||
const NxU32 *indices = indices_mesh;
|
||||
#if 0
|
||||
static NxU32 index = 0;
|
||||
NxU32 i = index++;
|
||||
indices = &indices[i*3];
|
||||
#else
|
||||
for (NxU32 i=0; i<tcount_mesh; i++)
|
||||
#endif
|
||||
{
|
||||
NxU32 i1 = indices[0];
|
||||
NxU32 i2 = indices[1];
|
||||
NxU32 i3 = indices[2];
|
||||
|
||||
const NxF32 *p1 = &vertices_mesh[i1*3];
|
||||
const NxF32 *p2 = &vertices_mesh[i2*3];
|
||||
const NxF32 *p3 = &vertices_mesh[i3*3];
|
||||
|
||||
NxF32 normal[3];
|
||||
NxF32 d = fm_computePlane(p3,p2,p1,normal);
|
||||
|
||||
NxF32 vertices[6*3];
|
||||
|
||||
vertices[0] = p1[0];
|
||||
vertices[1] = p1[1];
|
||||
vertices[2] = p1[2];
|
||||
|
||||
vertices[3] = p2[0];
|
||||
vertices[4] = p2[1];
|
||||
vertices[5] = p2[2];
|
||||
|
||||
vertices[6] = p3[0];
|
||||
vertices[7] = p3[1];
|
||||
vertices[8] = p3[2];
|
||||
|
||||
NxF32 midPoint[3];
|
||||
midPoint[0] = (p1[0]+p2[0]+p3[0])/3;
|
||||
midPoint[1] = (p1[1]+p2[1]+p3[1])/3;
|
||||
midPoint[2] = (p1[2]+p2[2]+p3[2])/3;
|
||||
|
||||
fm_lerp(midPoint,p1,&vertices[0],0.9999f);
|
||||
fm_lerp(midPoint,p2,&vertices[3],0.9999f);
|
||||
fm_lerp(midPoint,p3,&vertices[6],0.9999f);
|
||||
|
||||
NxF32 *_p1 = &vertices[3*3];
|
||||
NxF32 *_p2 = &vertices[4*3];
|
||||
NxF32 *_p3 = &vertices[5*3];
|
||||
|
||||
NxU32 hitCount = 0;
|
||||
|
||||
if ( raycast(&vertices[0],normal, _p1,cast_hull,cast_mesh) ) hitCount++;
|
||||
if ( raycast(&vertices[3],normal, _p2,cast_hull,cast_mesh) ) hitCount++;
|
||||
if ( raycast(&vertices[6],normal, _p3,cast_hull,cast_mesh) ) hitCount++;
|
||||
|
||||
// form triangle mesh!
|
||||
if ( hitCount == 3 )
|
||||
{
|
||||
NxU32 tcount = 0;
|
||||
NxU32 tindices[8*3];
|
||||
|
||||
addTri(tindices,2,1,0,tcount);
|
||||
addTri(tindices,3,4,5,tcount);
|
||||
|
||||
addTri(tindices,0,3,2,tcount);
|
||||
addTri(tindices,2,3,5,tcount);
|
||||
|
||||
addTri(tindices,1,3,0,tcount);
|
||||
addTri(tindices,4,3,1,tcount);
|
||||
|
||||
addTri(tindices,5,4,1,tcount);
|
||||
addTri(tindices,2,5,1,tcount);
|
||||
|
||||
NxF32 volume = fm_computeMeshVolume(vertices,tcount,tindices);
|
||||
total_volume+=volume;
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->setCurrentColor(0x0000FF,0xFFFFFF);
|
||||
NVSHARE::gRenderDebug->addToCurrentState(NVSHARE::DebugRenderState::SolidWireShaded);
|
||||
|
||||
for (NxU32 i=0; i<tcount; i++)
|
||||
{
|
||||
NxU32 i1 = tindices[i*3+0];
|
||||
NxU32 i2 = tindices[i*3+1];
|
||||
NxU32 i3 = tindices[i*3+2];
|
||||
|
||||
const NxF32 *p1 = &vertices[i1*3];
|
||||
const NxF32 *p2 = &vertices[i2*3];
|
||||
const NxF32 *p3 = &vertices[i3*3];
|
||||
|
||||
NVSHARE::gRenderDebug->DebugTri(p1,p2,p3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
indices+=3;
|
||||
}
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->popRenderState();
|
||||
#endif
|
||||
|
||||
releaseRayCast(cast_hull);
|
||||
releaseRayCast(cast_mesh);
|
||||
|
||||
return total_volume;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef NV_CONCAVITY_H
|
||||
|
||||
#define NV_CONCAVITY_H
|
||||
|
||||
/*
|
||||
|
||||
NvConcavityVolume.h : This is a code snippet that computes the volume of concavity of a traingle mesh.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
// computes the 'volume of concavity' of a triangle mesh projected against its surrounding convex hull.
|
||||
|
||||
NxF32 computeConcavityVolume(NxU32 vcount_hull,
|
||||
const NxF32 *vertices_hull,
|
||||
NxU32 tcount_hull,
|
||||
const NxU32 *indices_hull,
|
||||
NxU32 vcount_mesh,
|
||||
const NxF32 *vertices_mesh,
|
||||
NxU32 tcount_mesh,
|
||||
const NxU32 *indices_mesh);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -1,788 +0,0 @@
|
|||
|
||||
/*
|
||||
|
||||
NvConvexDecomposition.cpp : The main interface to the convex decomposition library.
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "NvConvexDecomposition.h"
|
||||
#include "NvHashMap.h"
|
||||
#include "NvFloatMath.h"
|
||||
#include "NvRemoveTjunctions.h"
|
||||
#include "NvMeshIslandGeneration.h"
|
||||
#include "NvStanHull.h"
|
||||
#include "NvConcavityVolume.h"
|
||||
#include "NvSplitMesh.h"
|
||||
#include "NvThreadConfig.h"
|
||||
|
||||
|
||||
#pragma warning(disable:4996 4100 4189)
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
|
||||
#define GRANULARITY 0.0000000001f
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Array;
|
||||
|
||||
class ConvexHull : public Memalloc
|
||||
{
|
||||
public:
|
||||
ConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
|
||||
{
|
||||
mTested = false;
|
||||
mVcount = vcount;
|
||||
mTcount = tcount;
|
||||
mVertices = 0;
|
||||
mIndices = 0;
|
||||
mHullVolume = 0;
|
||||
if ( vcount )
|
||||
{
|
||||
mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*vcount);
|
||||
memcpy(mVertices,vertices,sizeof(NxF32)*3*vcount);
|
||||
}
|
||||
if ( tcount )
|
||||
{
|
||||
mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*3*tcount);
|
||||
memcpy(mIndices,indices,sizeof(NxU32)*3*tcount);
|
||||
}
|
||||
if ( mVcount && mTcount )
|
||||
{
|
||||
mHullVolume = fm_computeMeshVolume( mVertices, mTcount, mIndices);
|
||||
}
|
||||
}
|
||||
|
||||
~ConvexHull(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(void)
|
||||
{
|
||||
MEMALLOC_FREE(mVertices);
|
||||
MEMALLOC_FREE(mIndices);
|
||||
mVertices = 0;
|
||||
mIndices = 0;
|
||||
mVcount = 0;
|
||||
mTcount = 0;
|
||||
mHullVolume = 0;
|
||||
}
|
||||
|
||||
// return true if merging this hull with the 'mergeHull' produces a new convex hull which is no greater in volume than the
|
||||
// mergeThresholdPercentage
|
||||
bool canMerge(ConvexHull *mergeHull,NxF32 mergeThresholdPercent,NxU32 maxVertices,NxF32 skinWidth,NxF32 &percent)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( mHullVolume > 0 && mergeHull->mHullVolume > 0 )
|
||||
{
|
||||
NxU32 combineVcount = mVcount + mergeHull->mVcount;
|
||||
NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3);
|
||||
NxF32 *dest = vertices;
|
||||
const NxF32 *source = mVertices;
|
||||
|
||||
for (NxU32 i=0; i<mVcount; i++)
|
||||
{
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[1];
|
||||
dest[2] = source[2];
|
||||
dest+=3;
|
||||
source+=3;
|
||||
}
|
||||
source = mergeHull->mVertices;
|
||||
for (NxU32 i=0; i<mergeHull->mVcount; i++)
|
||||
{
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[1];
|
||||
dest[2] = source[2];
|
||||
dest+=3;
|
||||
source+=3;
|
||||
}
|
||||
|
||||
// create the combined convex hull.
|
||||
HullDesc hd;
|
||||
hd.mVcount = combineVcount;
|
||||
hd.mVertices = vertices;
|
||||
hd.mVertexStride = sizeof(NxF32)*3;
|
||||
hd.mMaxVertices = maxVertices;
|
||||
hd.mSkinWidth = skinWidth;
|
||||
HullLibrary hl;
|
||||
HullResult result;
|
||||
hl.CreateConvexHull(hd,result);
|
||||
|
||||
NxF32 combinedVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
|
||||
NxF32 seperateVolume = mHullVolume+mergeHull->mHullVolume;
|
||||
|
||||
NxF32 percentMerge = 100 - (seperateVolume*100 / combinedVolume );
|
||||
|
||||
if ( percentMerge <= mergeThresholdPercent )
|
||||
{
|
||||
percent = percentMerge;
|
||||
ret = true;
|
||||
}
|
||||
MEMALLOC_FREE(vertices);
|
||||
hl.ReleaseResult(result);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void merge(ConvexHull *mergeHull,NxU32 maxVertices,NxF32 skinWidth)
|
||||
{
|
||||
NxU32 combineVcount = mVcount + mergeHull->mVcount;
|
||||
NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3);
|
||||
NxF32 *dest = vertices;
|
||||
const NxF32 *source = mVertices;
|
||||
|
||||
for (NxU32 i=0; i<mVcount; i++)
|
||||
{
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[1];
|
||||
dest[2] = source[2];
|
||||
dest+=3;
|
||||
source+=3;
|
||||
}
|
||||
source = mergeHull->mVertices;
|
||||
for (NxU32 i=0; i<mergeHull->mVcount; i++)
|
||||
{
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[1];
|
||||
dest[2] = source[2];
|
||||
dest+=3;
|
||||
source+=3;
|
||||
}
|
||||
|
||||
// create the combined convex hull.
|
||||
HullDesc hd;
|
||||
hd.mVcount = combineVcount;
|
||||
hd.mVertices = vertices;
|
||||
hd.mVertexStride = sizeof(NxF32)*3;
|
||||
hd.mMaxVertices = maxVertices;
|
||||
hd.mSkinWidth = skinWidth;
|
||||
HullLibrary hl;
|
||||
HullResult result;
|
||||
hl.CreateConvexHull(hd,result);
|
||||
|
||||
reset();
|
||||
mergeHull->reset();
|
||||
mergeHull->mTested = true; // it's been tested.
|
||||
mVcount = result.mNumOutputVertices;
|
||||
mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*mVcount);
|
||||
memcpy(mVertices,result.mOutputVertices,sizeof(NxF32)*3*mVcount);
|
||||
mTcount = result.mNumFaces;
|
||||
mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*mTcount*3);
|
||||
memcpy(mIndices, result.mIndices, sizeof(NxU32)*mTcount*3);
|
||||
|
||||
MEMALLOC_FREE(vertices);
|
||||
hl.ReleaseResult(result);
|
||||
}
|
||||
|
||||
void setTested(bool state)
|
||||
{
|
||||
mTested = state;
|
||||
}
|
||||
|
||||
bool beenTested(void) const { return mTested; };
|
||||
|
||||
bool mTested;
|
||||
NxF32 mHullVolume;
|
||||
NxU32 mVcount;
|
||||
NxF32 *mVertices;
|
||||
NxU32 mTcount;
|
||||
NxU32 *mIndices;
|
||||
};
|
||||
|
||||
typedef Array< ConvexHull *> ConvexHullVector;
|
||||
|
||||
class ConvexDecomposition : public iConvexDecomposition, public CONVEX_DECOMPOSITION::Memalloc, public ThreadInterface
|
||||
{
|
||||
public:
|
||||
ConvexDecomposition(void)
|
||||
{
|
||||
mVertexIndex = 0;
|
||||
mComplete = false;
|
||||
mCancel = false;
|
||||
mThread = 0;
|
||||
}
|
||||
|
||||
~ConvexDecomposition(void)
|
||||
{
|
||||
wait();
|
||||
reset();
|
||||
if ( mThread )
|
||||
{
|
||||
tc_releaseThread(mThread);
|
||||
}
|
||||
}
|
||||
|
||||
void wait(void) const
|
||||
{
|
||||
while ( mThread && !mComplete );
|
||||
}
|
||||
|
||||
virtual void reset(void) // reset the input mesh data.
|
||||
{
|
||||
wait();
|
||||
if ( mVertexIndex )
|
||||
{
|
||||
fm_releaseVertexIndex(mVertexIndex);
|
||||
mVertexIndex = 0;
|
||||
}
|
||||
mIndices.clear();
|
||||
ConvexHullVector::Iterator i;
|
||||
for (i=mHulls.begin(); i!=mHulls.end(); ++i)
|
||||
{
|
||||
ConvexHull *ch = (*i);
|
||||
delete ch;
|
||||
}
|
||||
mHulls.clear();
|
||||
}
|
||||
|
||||
virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3)
|
||||
{
|
||||
bool ret = true;
|
||||
wait();
|
||||
if ( mVertexIndex == 0 )
|
||||
{
|
||||
mVertexIndex = fm_createVertexIndex(GRANULARITY,false);
|
||||
}
|
||||
|
||||
bool newPos;
|
||||
NxU32 i1 = mVertexIndex->getIndex(p1,newPos);
|
||||
NxU32 i2 = mVertexIndex->getIndex(p2,newPos);
|
||||
NxU32 i3 = mVertexIndex->getIndex(p3,newPos);
|
||||
|
||||
if ( i1 == i2 || i1 == i3 || i2 == i3 )
|
||||
{
|
||||
ret = false; // triangle is degenerate
|
||||
}
|
||||
else
|
||||
{
|
||||
mIndices.pushBack(i1);
|
||||
mIndices.pushBack(i2);
|
||||
mIndices.pushBack(i3);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConvexHull * getNonTested(void) const
|
||||
{
|
||||
ConvexHull *ret = 0;
|
||||
for (NxU32 i=0; i<mHulls.size(); i++)
|
||||
{
|
||||
ConvexHull *ch = mHulls[i];
|
||||
if ( !ch->beenTested() )
|
||||
{
|
||||
ret = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual NxU32 computeConvexDecomposition(NxF32 skinWidth,
|
||||
NxU32 decompositionDepth,
|
||||
NxU32 maxHullVertices,
|
||||
NxF32 concavityThresholdPercent,
|
||||
NxF32 mergeThresholdPercent,
|
||||
NxF32 volumeSplitThresholdPercent,
|
||||
bool useInitialIslandGeneration,
|
||||
bool useIslandGeneration,
|
||||
bool useThreads)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
if ( mThread )
|
||||
return 0;
|
||||
|
||||
if ( mVertexIndex )
|
||||
{
|
||||
|
||||
mSkinWidth = skinWidth;
|
||||
mDecompositionDepth = decompositionDepth;
|
||||
mMaxHullVertices = maxHullVertices;
|
||||
mConcavityThresholdPercent = concavityThresholdPercent;
|
||||
mMergeThresholdPercent = mergeThresholdPercent;
|
||||
mVolumeSplitThresholdPercent = volumeSplitThresholdPercent;
|
||||
mUseInitialIslandGeneration = useInitialIslandGeneration;
|
||||
mUseIslandGeneration = false; // Not currently supported. useIslandGeneration;
|
||||
mComplete = false;
|
||||
mCancel = false;
|
||||
|
||||
if ( useThreads )
|
||||
{
|
||||
mThread = tc_createThread(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
threadMain();
|
||||
ret = getHullCount();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void performConvexDecomposition(NxU32 vcount,
|
||||
const NxF32 *vertices,
|
||||
NxU32 tcount,
|
||||
const NxU32 *indices,
|
||||
NxF32 skinWidth,
|
||||
NxU32 decompositionDepth,
|
||||
NxU32 maxHullVertices,
|
||||
NxF32 concavityThresholdPercent,
|
||||
NxF32 mergeThresholdPercent,
|
||||
NxF32 volumeSplitThresholdPercent,
|
||||
bool useInitialIslandGeneration,
|
||||
bool useIslandGeneration,
|
||||
NxU32 depth)
|
||||
{
|
||||
if ( mCancel ) return;
|
||||
if ( depth >= decompositionDepth ) return;
|
||||
|
||||
RemoveTjunctionsDesc desc;
|
||||
desc.mVcount = vcount;
|
||||
desc.mVertices = vertices;
|
||||
desc.mTcount = tcount;
|
||||
desc.mIndices = indices;
|
||||
|
||||
#if 0
|
||||
RemoveTjunctions *rt = createRemoveTjunctions();
|
||||
rt->removeTjunctions(desc);
|
||||
#else
|
||||
|
||||
desc.mTcountOut = desc.mTcount;
|
||||
desc.mIndicesOut = desc.mIndices;
|
||||
|
||||
#endif
|
||||
// ok..we now have a clean mesh without any tjunctions.
|
||||
bool island = (depth == 0 ) ? useInitialIslandGeneration : useIslandGeneration;
|
||||
if ( island )
|
||||
{
|
||||
MeshIslandGeneration *mi = createMeshIslandGeneration();
|
||||
NxU32 icount = mi->islandGenerate(desc.mTcountOut,desc.mIndicesOut,desc.mVertices);
|
||||
for (NxU32 i=0; i<icount && !mCancel; i++)
|
||||
{
|
||||
NxU32 tcount;
|
||||
NxU32 *indices = mi->getIsland(i,tcount);
|
||||
|
||||
baseConvexDecomposition(desc.mVcount,desc.mVertices,
|
||||
tcount,indices,
|
||||
skinWidth,
|
||||
decompositionDepth,
|
||||
maxHullVertices,
|
||||
concavityThresholdPercent,
|
||||
mergeThresholdPercent,
|
||||
volumeSplitThresholdPercent,
|
||||
useInitialIslandGeneration,
|
||||
useIslandGeneration,depth);
|
||||
}
|
||||
releaseMeshIslandGeneration(mi);
|
||||
}
|
||||
else
|
||||
{
|
||||
baseConvexDecomposition(desc.mVcount,desc.mVertices,desc.mTcountOut,
|
||||
desc.mIndicesOut,
|
||||
skinWidth,
|
||||
decompositionDepth,
|
||||
maxHullVertices,
|
||||
concavityThresholdPercent,
|
||||
mergeThresholdPercent,
|
||||
volumeSplitThresholdPercent,
|
||||
useInitialIslandGeneration,
|
||||
useIslandGeneration,depth);
|
||||
}
|
||||
#if 0
|
||||
releaseRemoveTjunctions(rt);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void baseConvexDecomposition(NxU32 vcount,
|
||||
const NxF32 *vertices,
|
||||
NxU32 tcount,
|
||||
const NxU32 *indices,
|
||||
NxF32 skinWidth,
|
||||
NxU32 decompositionDepth,
|
||||
NxU32 maxHullVertices,
|
||||
NxF32 concavityThresholdPercent,
|
||||
NxF32 mergeThresholdPercent,
|
||||
NxF32 volumeSplitThresholdPercent,
|
||||
bool useInitialIslandGeneration,
|
||||
bool useIslandGeneration,
|
||||
NxU32 depth)
|
||||
{
|
||||
|
||||
if ( mCancel ) return;
|
||||
|
||||
bool split = false; // by default we do not split
|
||||
|
||||
|
||||
NxU32 *out_indices = (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 );
|
||||
NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount );
|
||||
|
||||
NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices );
|
||||
// get a copy of only the unique vertices which are actually being used.
|
||||
|
||||
HullDesc hd;
|
||||
hd.mVcount = out_vcount;
|
||||
hd.mVertices = out_vertices;
|
||||
hd.mVertexStride = sizeof(NxF32)*3;
|
||||
hd.mMaxVertices = maxHullVertices;
|
||||
hd.mSkinWidth = skinWidth;
|
||||
HullLibrary hl;
|
||||
HullResult result;
|
||||
hl.CreateConvexHull(hd,result);
|
||||
|
||||
NxF32 meshVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
|
||||
|
||||
if ( (depth+1) < decompositionDepth )
|
||||
{
|
||||
// compute the volume of this mesh...
|
||||
NxF32 percentVolume = (meshVolume*100)/mOverallMeshVolume; // what percentage of the overall mesh volume are we?
|
||||
if ( percentVolume > volumeSplitThresholdPercent ) // this piece must be greater thant he volume split threshold percent
|
||||
{
|
||||
// ok..now we will compute the concavity...
|
||||
NxF32 concave_volume = computeConcavityVolume(result.mNumOutputVertices, result.mOutputVertices, result.mNumFaces, result.mIndices, out_vcount, out_vertices, tcount, out_indices );
|
||||
NxF32 concave_percent = (concave_volume*100) / meshVolume;
|
||||
if ( concave_percent >= concavityThresholdPercent )
|
||||
{
|
||||
// ready to do split here..
|
||||
split = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !split )
|
||||
{
|
||||
saveConvexHull(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices);
|
||||
}
|
||||
|
||||
// Compute the best fit plane relative to the computed convex hull.
|
||||
NxF32 plane[4];
|
||||
bool ok = fm_computeSplitPlane(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices,plane);
|
||||
assert(ok);
|
||||
|
||||
hl.ReleaseResult(result);
|
||||
MEMALLOC_FREE(out_indices);
|
||||
MEMALLOC_FREE(out_vertices);
|
||||
|
||||
if ( split )
|
||||
{
|
||||
iSplitMesh *sm = createSplitMesh();
|
||||
|
||||
NvSplitMesh n;
|
||||
n.mVcount = vcount;
|
||||
n.mVertices = vertices;
|
||||
n.mTcount = tcount;
|
||||
n.mIndices = indices;
|
||||
if ( ok )
|
||||
{
|
||||
NvSplitMesh leftMesh;
|
||||
NvSplitMesh rightMesh;
|
||||
|
||||
sm->splitMesh(n,leftMesh,rightMesh,plane,GRANULARITY);
|
||||
|
||||
if ( leftMesh.mTcount )
|
||||
{
|
||||
performConvexDecomposition(leftMesh.mVcount,
|
||||
leftMesh.mVertices,
|
||||
leftMesh.mTcount,
|
||||
leftMesh.mIndices,
|
||||
skinWidth,
|
||||
decompositionDepth,
|
||||
maxHullVertices,
|
||||
concavityThresholdPercent,
|
||||
mergeThresholdPercent,
|
||||
volumeSplitThresholdPercent,
|
||||
useInitialIslandGeneration,
|
||||
useIslandGeneration,
|
||||
depth+1);
|
||||
|
||||
}
|
||||
if ( rightMesh.mTcount )
|
||||
{
|
||||
performConvexDecomposition(rightMesh.mVcount,
|
||||
rightMesh.mVertices,
|
||||
rightMesh.mTcount,
|
||||
rightMesh.mIndices,
|
||||
skinWidth,
|
||||
decompositionDepth,
|
||||
maxHullVertices,
|
||||
concavityThresholdPercent,
|
||||
mergeThresholdPercent,
|
||||
volumeSplitThresholdPercent,
|
||||
useInitialIslandGeneration,
|
||||
useIslandGeneration,
|
||||
depth+1);
|
||||
}
|
||||
}
|
||||
releaseSplitMesh(sm);
|
||||
}
|
||||
}
|
||||
|
||||
// Copies only the vertices which are actually used.
|
||||
// Then computes the convex hull around these used vertices.
|
||||
// Next computes the volume of this convex hull.
|
||||
// Frees up scratch memory and returns the volume of the convex hull around the source triangle mesh.
|
||||
NxF32 computeHullMeshVolume(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxU32 maxVertices,NxF32 skinWidth)
|
||||
{
|
||||
if ( mCancel ) return 0;
|
||||
// first thing we should do is compute the overall mesh volume.
|
||||
NxU32 *out_indices = (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 );
|
||||
NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount );
|
||||
|
||||
NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices );
|
||||
// get a copy of only the unique vertices which are actually being used.
|
||||
|
||||
HullDesc hd;
|
||||
hd.mVcount = out_vcount;
|
||||
hd.mVertices = out_vertices;
|
||||
hd.mVertexStride = sizeof(NxF32)*3;
|
||||
hd.mMaxVertices = maxVertices;
|
||||
hd.mSkinWidth = skinWidth;
|
||||
HullLibrary hl;
|
||||
HullResult result;
|
||||
hl.CreateConvexHull(hd,result);
|
||||
|
||||
NxF32 volume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
|
||||
|
||||
hl.ReleaseResult(result);
|
||||
MEMALLOC_FREE(out_indices);
|
||||
MEMALLOC_FREE(out_vertices);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
virtual bool isComputeComplete(void) // if building the convex hulls in a background thread, this returns true if it is complete.
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if ( mThread )
|
||||
{
|
||||
ret = mComplete;
|
||||
if ( ret )
|
||||
{
|
||||
tc_releaseThread(mThread);
|
||||
mThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
virtual NxU32 getHullCount(void)
|
||||
{
|
||||
NxU32 hullCount = 0;
|
||||
wait();
|
||||
if ( mCancel )
|
||||
{
|
||||
reset();
|
||||
}
|
||||
for (NxU32 i=0; i<mHulls.size(); i++)
|
||||
{
|
||||
ConvexHull *ch = mHulls[i];
|
||||
if ( ch->mTcount )
|
||||
{
|
||||
hullCount++;
|
||||
}
|
||||
}
|
||||
return hullCount;
|
||||
}
|
||||
|
||||
virtual bool getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
wait();
|
||||
NxU32 index = 0;
|
||||
for (NxU32 i=0; i<mHulls.size(); i++)
|
||||
{
|
||||
ConvexHull *ch = mHulls[i];
|
||||
if ( ch->mTcount )
|
||||
{
|
||||
if ( hullIndex == index )
|
||||
{
|
||||
ret = true;
|
||||
result.mVcount = ch->mVcount;
|
||||
result.mTcount = ch->mTcount;
|
||||
result.mVertices = ch->mVertices;
|
||||
result.mIndices = ch->mIndices;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void saveConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
|
||||
{
|
||||
ConvexHull *ch = MEMALLOC_NEW(ConvexHull)(vcount,vertices,tcount,indices);
|
||||
mHulls.pushBack(ch);
|
||||
}
|
||||
|
||||
virtual void threadMain(void)
|
||||
{
|
||||
mOverallMeshVolume = computeHullMeshVolume( mVertexIndex->getVcount(),
|
||||
mVertexIndex->getVerticesFloat(),
|
||||
mIndices.size()/3,
|
||||
&mIndices[0],
|
||||
mMaxHullVertices, mSkinWidth );
|
||||
|
||||
performConvexDecomposition(mVertexIndex->getVcount(),mVertexIndex->getVerticesFloat(),
|
||||
mIndices.size()/3,&mIndices[0],
|
||||
mSkinWidth,
|
||||
mDecompositionDepth,
|
||||
mMaxHullVertices,
|
||||
mConcavityThresholdPercent,
|
||||
mMergeThresholdPercent,
|
||||
mVolumeSplitThresholdPercent,
|
||||
mUseInitialIslandGeneration,
|
||||
mUseIslandGeneration,0);
|
||||
|
||||
if ( mHulls.size() && !mCancel )
|
||||
{
|
||||
// While convex hulls can be merged...
|
||||
ConvexHull *ch = getNonTested();
|
||||
while ( ch && !mCancel )
|
||||
{
|
||||
// Sort all convex hulls by volume, largest to smallest.
|
||||
NxU32 hullCount = mHulls.size();
|
||||
ConvexHull *bestHull = 0;
|
||||
NxF32 bestPercent = 100;
|
||||
|
||||
for (NxU32 i=0; i<hullCount; i++)
|
||||
{
|
||||
ConvexHull *mergeHull = mHulls[i];
|
||||
if ( !mergeHull->beenTested() && mergeHull != ch )
|
||||
{
|
||||
NxF32 percent;
|
||||
if ( ch->canMerge(mergeHull,mMergeThresholdPercent,mMaxHullVertices,mSkinWidth,percent) )
|
||||
{
|
||||
if ( percent < bestPercent )
|
||||
{
|
||||
bestHull = mergeHull;
|
||||
bestPercent = percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( bestHull )
|
||||
{
|
||||
ch->merge(bestHull,mMaxHullVertices,mSkinWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->setTested(true);
|
||||
}
|
||||
|
||||
ch = getNonTested();
|
||||
}
|
||||
}
|
||||
mComplete = true;
|
||||
}
|
||||
|
||||
virtual bool cancelCompute(void) // cause background thread computation to abort early. Will return no results. Use 'isComputeComplete' to confirm the thread is done.
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( mThread && !mComplete )
|
||||
{
|
||||
mCancel = true;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mComplete;
|
||||
bool mCancel;
|
||||
fm_VertexIndex *mVertexIndex;
|
||||
NxU32Array mIndices;
|
||||
NxF32 mOverallMeshVolume;
|
||||
ConvexHullVector mHulls;
|
||||
Thread *mThread;
|
||||
|
||||
NxF32 mSkinWidth;
|
||||
NxU32 mDecompositionDepth;
|
||||
NxU32 mMaxHullVertices;
|
||||
NxF32 mConcavityThresholdPercent;
|
||||
NxF32 mMergeThresholdPercent;
|
||||
NxF32 mVolumeSplitThresholdPercent;
|
||||
bool mUseInitialIslandGeneration;
|
||||
bool mUseIslandGeneration;
|
||||
|
||||
};
|
||||
|
||||
|
||||
iConvexDecomposition * createConvexDecomposition(void)
|
||||
{
|
||||
ConvexDecomposition *cd = MEMALLOC_NEW(ConvexDecomposition);
|
||||
return static_cast< iConvexDecomposition *>(cd);
|
||||
|
||||
}
|
||||
|
||||
void releaseConvexDecomposition(iConvexDecomposition *ic)
|
||||
{
|
||||
ConvexDecomposition *cd = static_cast< ConvexDecomposition *>(ic);
|
||||
delete cd;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#ifndef CONVEX_DECOMPOSITION_H
|
||||
|
||||
#define CONVEX_DECOMPOSITION_H
|
||||
|
||||
/*
|
||||
|
||||
NvConvexDecomposition.h : The main interface to the convex decomposition library.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "NvSimpleTypes.h"
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
struct ConvexHullResult
|
||||
{
|
||||
NxU32 mVcount; // number of vertices.
|
||||
NxF32 *mVertices; // vertex positions.
|
||||
NxU32 mTcount; // number of triangles.
|
||||
NxU32 *mIndices; // indexed triangle list.
|
||||
};
|
||||
|
||||
class iConvexDecomposition
|
||||
{
|
||||
public:
|
||||
virtual void reset(void) = 0; // reset the input mesh data.
|
||||
|
||||
virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3) = 0; // add the input mesh one triangle at a time.
|
||||
|
||||
virtual NxU32 computeConvexDecomposition(NxF32 skinWidth=0, // Skin width on the convex hulls generated
|
||||
NxU32 decompositionDepth=8, // recursion depth for convex decomposition.
|
||||
NxU32 maxHullVertices=64, // maximum number of vertices in output convex hulls.
|
||||
NxF32 concavityThresholdPercent=0.1f, // The percentage of concavity allowed without causing a split to occur.
|
||||
NxF32 mergeThresholdPercent=30.0f, // The percentage of volume difference allowed to merge two convex hulls.
|
||||
NxF32 volumeSplitThresholdPercent=0.1f, // The percentage of the total volume of the object above which splits will still occur.
|
||||
bool useInitialIslandGeneration=true, // whether or not to perform initial island generation on the input mesh.
|
||||
bool useIslandGeneration=false, // Whether or not to perform island generation at each split. Currently disabled due to bug in RemoveTjunctions
|
||||
bool useBackgroundThread=true) = 0; // Whether or not to compute the convex decomposition in a background thread, the default is true.
|
||||
|
||||
virtual bool isComputeComplete(void) = 0; // if building the convex hulls in a background thread, this returns true if it is complete.
|
||||
|
||||
virtual bool cancelCompute(void) = 0; // cause background thread computation to abort early. Will return no results. Use 'isComputeComplete' to confirm the thread is done.
|
||||
|
||||
|
||||
virtual NxU32 getHullCount(void) = 0; // returns the number of convex hulls produced.
|
||||
virtual bool getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result) = 0; // returns each convex hull result.
|
||||
|
||||
protected:
|
||||
virtual ~iConvexDecomposition(void)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
iConvexDecomposition * createConvexDecomposition(void);
|
||||
void releaseConvexDecomposition(iConvexDecomposition *ic);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
// a set of routines that let you do common 3d math
|
||||
// operations without any vector, matrix, or quaternion
|
||||
// classes or templates.
|
||||
//
|
||||
// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
|
||||
// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
|
||||
// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
|
||||
//
|
||||
//
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "NvFloatMath.h"
|
||||
|
||||
#define REAL NxF32
|
||||
|
||||
#include "NvFloatMath.inl"
|
||||
|
||||
#undef REAL
|
||||
#define REAL NxF64
|
||||
|
||||
#include "NvFloatMath.inl"
|
||||
|
|
@ -1,586 +0,0 @@
|
|||
#ifndef NV_FLOAT_MATH_H
|
||||
|
||||
#define NV_FLOAT_MATH_H
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
// a set of routines that let you do common 3d math
|
||||
// operations without any vector, matrix, or quaternion
|
||||
// classes or templates.
|
||||
//
|
||||
// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
|
||||
// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
|
||||
// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
|
||||
//
|
||||
//
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
enum FM_ClipState
|
||||
{
|
||||
FMCS_XMIN = (1<<0),
|
||||
FMCS_XMAX = (1<<1),
|
||||
FMCS_YMIN = (1<<2),
|
||||
FMCS_YMAX = (1<<3),
|
||||
FMCS_ZMIN = (1<<4),
|
||||
FMCS_ZMAX = (1<<5),
|
||||
};
|
||||
|
||||
enum FM_Axis
|
||||
{
|
||||
FM_XAXIS = (1<<0),
|
||||
FM_YAXIS = (1<<1),
|
||||
FM_ZAXIS = (1<<2)
|
||||
};
|
||||
|
||||
enum LineSegmentType
|
||||
{
|
||||
LS_START,
|
||||
LS_MIDDLE,
|
||||
LS_END
|
||||
};
|
||||
|
||||
|
||||
const NxF32 FM_PI = 3.1415926535897932384626433832795028841971693993751f;
|
||||
const NxF32 FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
|
||||
const NxF32 FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
|
||||
|
||||
//***************** Float versions
|
||||
//***
|
||||
//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z
|
||||
//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W
|
||||
//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix
|
||||
//*** bounding volumes are expressed as two sets of 3 floats/NxF64 representing bmin(x,y,z) and bmax(x,y,z)
|
||||
//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D
|
||||
|
||||
FM_Axis fm_getDominantAxis(const NxF32 normal[3]);
|
||||
FM_Axis fm_getDominantAxis(const NxF64 normal[3]);
|
||||
|
||||
void fm_decomposeTransform(const NxF32 local_transform[16],NxF32 trans[3],NxF32 rot[4],NxF32 scale[3]);
|
||||
void fm_decomposeTransform(const NxF64 local_transform[16],NxF64 trans[3],NxF64 rot[4],NxF64 scale[3]);
|
||||
|
||||
void fm_multiplyTransform(const NxF32 *pA,const NxF32 *pB,NxF32 *pM);
|
||||
void fm_multiplyTransform(const NxF64 *pA,const NxF64 *pB,NxF64 *pM);
|
||||
|
||||
void fm_inverseTransform(const NxF32 matrix[16],NxF32 inverse_matrix[16]);
|
||||
void fm_inverseTransform(const NxF64 matrix[16],NxF64 inverse_matrix[16]);
|
||||
|
||||
void fm_identity(NxF32 matrix[16]); // set 4x4 matrix to identity.
|
||||
void fm_identity(NxF64 matrix[16]); // set 4x4 matrix to identity.
|
||||
|
||||
void fm_inverseRT(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // inverse rotate translate the point.
|
||||
void fm_inverseRT(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // inverse rotate translate the point.
|
||||
|
||||
void fm_transform(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // rotate and translate this point.
|
||||
void fm_transform(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // rotate and translate this point.
|
||||
|
||||
NxF32 fm_getDeterminant(const NxF32 matrix[16]);
|
||||
NxF64 fm_getDeterminant(const NxF64 matrix[16]);
|
||||
|
||||
void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF32 pDst[16],const NxF32 matrix[16]);
|
||||
void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF64 pDst[16],const NxF32 matrix[16]);
|
||||
|
||||
void fm_rotate(const NxF32 matrix[16],const NxF32 pos[3],NxF32 t[3]); // only rotate the point by a 4x4 matrix, don't translate.
|
||||
void fm_rotate(const NxF64 matri[16],const NxF64 pos[3],NxF64 t[3]); // only rotate the point by a 4x4 matrix, don't translate.
|
||||
|
||||
void fm_eulerToMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
void fm_eulerToMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
|
||||
void fm_getAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]);
|
||||
void fm_getAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]);
|
||||
|
||||
void fm_getAABBCenter(const NxF32 bmin[3],const NxF32 bmax[3],NxF32 center[3]);
|
||||
void fm_getAABBCenter(const NxF64 bmin[3],const NxF64 bmax[3],NxF64 center[3]);
|
||||
|
||||
void fm_eulerToQuat(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion.
|
||||
void fm_eulerToQuat(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion.
|
||||
|
||||
void fm_quatToEuler(const NxF32 quat[4],NxF32 &ax,NxF32 &ay,NxF32 &az);
|
||||
void fm_quatToEuler(const NxF64 quat[4],NxF64 &ax,NxF64 &ay,NxF64 &az);
|
||||
|
||||
void fm_eulerToQuat(const NxF32 euler[3],NxF32 quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees!
|
||||
void fm_eulerToQuat(const NxF64 euler[3],NxF64 quat[4]); // convert euler angles to quaternion.
|
||||
|
||||
void fm_scale(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // apply scale to the matrix.
|
||||
void fm_scale(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // apply scale to the matrix.
|
||||
|
||||
void fm_eulerToQuatDX(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
|
||||
void fm_eulerToQuatDX(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
|
||||
|
||||
void fm_eulerToMatrixDX(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
|
||||
void fm_eulerToMatrixDX(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
|
||||
|
||||
void fm_quatToMatrix(const NxF32 quat[4],NxF32 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
|
||||
void fm_quatToMatrix(const NxF64 quat[4],NxF64 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
|
||||
|
||||
void fm_quatRotate(const NxF32 quat[4],const NxF32 v[3],NxF32 r[3]); // rotate a vector directly by a quaternion.
|
||||
void fm_quatRotate(const NxF64 quat[4],const NxF64 v[3],NxF64 r[3]); // rotate a vector directly by a quaternion.
|
||||
|
||||
void fm_getTranslation(const NxF32 matrix[16],NxF32 t[3]);
|
||||
void fm_getTranslation(const NxF64 matrix[16],NxF64 t[3]);
|
||||
|
||||
void fm_setTranslation(const NxF32 *translation,NxF32 matrix[16]);
|
||||
void fm_setTranslation(const NxF64 *translation,NxF64 matrix[16]);
|
||||
|
||||
void fm_multiplyQuat(const NxF32 *qa,const NxF32 *qb,NxF32 *quat);
|
||||
void fm_multiplyQuat(const NxF64 *qa,const NxF64 *qb,NxF64 *quat);
|
||||
|
||||
void fm_matrixToQuat(const NxF32 matrix[16],NxF32 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
|
||||
void fm_matrixToQuat(const NxF64 matrix[16],NxF64 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
|
||||
|
||||
NxF32 fm_sphereVolume(NxF32 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
|
||||
NxF64 fm_sphereVolume(NxF64 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
|
||||
|
||||
NxF32 fm_cylinderVolume(NxF32 radius,NxF32 h);
|
||||
NxF64 fm_cylinderVolume(NxF64 radius,NxF64 h);
|
||||
|
||||
NxF32 fm_capsuleVolume(NxF32 radius,NxF32 h);
|
||||
NxF64 fm_capsuleVolume(NxF64 radius,NxF64 h);
|
||||
|
||||
NxF32 fm_distance(const NxF32 p1[3],const NxF32 p2[3]);
|
||||
NxF64 fm_distance(const NxF64 p1[3],const NxF64 p2[3]);
|
||||
|
||||
NxF32 fm_distanceSquared(const NxF32 p1[3],const NxF32 p2[3]);
|
||||
NxF64 fm_distanceSquared(const NxF64 p1[3],const NxF64 p2[3]);
|
||||
|
||||
NxF32 fm_distanceSquaredXZ(const NxF32 p1[3],const NxF32 p2[3]);
|
||||
NxF64 fm_distanceSquaredXZ(const NxF64 p1[3],const NxF64 p2[3]);
|
||||
|
||||
NxF32 fm_computePlane(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 *n); // return D
|
||||
NxF64 fm_computePlane(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 *n); // return D
|
||||
|
||||
NxF32 fm_distToPlane(const NxF32 plane[4],const NxF32 pos[3]); // computes the distance of this point from the plane.
|
||||
NxF64 fm_distToPlane(const NxF64 plane[4],const NxF64 pos[3]); // computes the distance of this point from the plane.
|
||||
|
||||
NxF32 fm_dot(const NxF32 p1[3],const NxF32 p2[3]);
|
||||
NxF64 fm_dot(const NxF64 p1[3],const NxF64 p2[3]);
|
||||
|
||||
void fm_cross(NxF32 cross[3],const NxF32 a[3],const NxF32 b[3]);
|
||||
void fm_cross(NxF64 cross[3],const NxF64 a[3],const NxF64 b[3]);
|
||||
|
||||
void fm_computeNormalVector(NxF32 n[3],const NxF32 p1[3],const NxF32 p2[3]); // as P2-P1 normalized.
|
||||
void fm_computeNormalVector(NxF64 n[3],const NxF64 p1[3],const NxF64 p2[3]); // as P2-P1 normalized.
|
||||
|
||||
bool fm_computeWindingOrder(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]); // returns true if the triangle is clockwise.
|
||||
bool fm_computeWindingOrder(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]); // returns true if the triangle is clockwise.
|
||||
|
||||
NxF32 fm_normalize(NxF32 n[3]); // normalize this vector and return the distance
|
||||
NxF64 fm_normalize(NxF64 n[3]); // normalize this vector and return the distance
|
||||
|
||||
void fm_matrixMultiply(const NxF32 A[16],const NxF32 B[16],NxF32 dest[16]);
|
||||
void fm_matrixMultiply(const NxF64 A[16],const NxF64 B[16],NxF64 dest[16]);
|
||||
|
||||
void fm_composeTransform(const NxF32 position[3],const NxF32 quat[4],const NxF32 scale[3],NxF32 matrix[16]);
|
||||
void fm_composeTransform(const NxF64 position[3],const NxF64 quat[4],const NxF64 scale[3],NxF64 matrix[16]);
|
||||
|
||||
NxF32 fm_computeArea(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]);
|
||||
NxF64 fm_computeArea(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]);
|
||||
|
||||
void fm_lerp(const NxF32 p1[3],const NxF32 p2[3],NxF32 dest[3],NxF32 lerpValue);
|
||||
void fm_lerp(const NxF64 p1[3],const NxF64 p2[3],NxF64 dest[3],NxF64 lerpValue);
|
||||
|
||||
bool fm_insideTriangleXZ(const NxF32 test[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]);
|
||||
bool fm_insideTriangleXZ(const NxF64 test[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]);
|
||||
|
||||
bool fm_insideAABB(const NxF32 pos[3],const NxF32 bmin[3],const NxF32 bmax[3]);
|
||||
bool fm_insideAABB(const NxF64 pos[3],const NxF64 bmin[3],const NxF64 bmax[3]);
|
||||
|
||||
bool fm_insideAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 tbmin[3],const NxF32 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
|
||||
bool fm_insideAABB(const NxF64 obmin[3],const NxF64 obmax[3],const NxF64 tbmin[3],const NxF64 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
|
||||
|
||||
NxU32 fm_clipTestPoint(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]);
|
||||
NxU32 fm_clipTestPoint(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]);
|
||||
|
||||
NxU32 fm_clipTestPointXZ(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]); // only tests X and Z, not Y
|
||||
NxU32 fm_clipTestPointXZ(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]); // only tests X and Z, not Y
|
||||
|
||||
|
||||
NxU32 fm_clipTestAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxU32 &andCode);
|
||||
NxU32 fm_clipTestAABB(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxU32 &andCode);
|
||||
|
||||
|
||||
bool fm_lineTestAABBXZ(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time);
|
||||
bool fm_lineTestAABBXZ(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time);
|
||||
|
||||
bool fm_lineTestAABB(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time);
|
||||
bool fm_lineTestAABB(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time);
|
||||
|
||||
|
||||
void fm_initMinMax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]);
|
||||
void fm_initMinMax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]);
|
||||
|
||||
void fm_initMinMax(NxF32 bmin[3],NxF32 bmax[3]);
|
||||
void fm_initMinMax(NxF64 bmin[3],NxF64 bmax[3]);
|
||||
|
||||
void fm_minmax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]); // accmulate to a min-max value
|
||||
void fm_minmax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]); // accmulate to a min-max value
|
||||
|
||||
|
||||
NxF32 fm_solveX(const NxF32 plane[4],NxF32 y,NxF32 z); // solve for X given this plane equation and the other two components.
|
||||
NxF64 fm_solveX(const NxF64 plane[4],NxF64 y,NxF64 z); // solve for X given this plane equation and the other two components.
|
||||
|
||||
NxF32 fm_solveY(const NxF32 plane[4],NxF32 x,NxF32 z); // solve for Y given this plane equation and the other two components.
|
||||
NxF64 fm_solveY(const NxF64 plane[4],NxF64 x,NxF64 z); // solve for Y given this plane equation and the other two components.
|
||||
|
||||
NxF32 fm_solveZ(const NxF32 plane[4],NxF32 x,NxF32 y); // solve for Z given this plane equation and the other two components.
|
||||
NxF64 fm_solveZ(const NxF64 plane[4],NxF64 x,NxF64 y); // solve for Z given this plane equation and the other two components.
|
||||
|
||||
bool fm_computeBestFitPlane(NxU32 vcount, // number of input data points
|
||||
const NxF32 *points, // starting address of points array.
|
||||
NxU32 vstride, // stride between input points.
|
||||
const NxF32 *weights, // *optional point weighting values.
|
||||
NxU32 wstride, // weight stride for each vertex.
|
||||
NxF32 plane[4]);
|
||||
|
||||
bool fm_computeBestFitPlane(NxU32 vcount, // number of input data points
|
||||
const NxF64 *points, // starting address of points array.
|
||||
NxU32 vstride, // stride between input points.
|
||||
const NxF64 *weights, // *optional point weighting values.
|
||||
NxU32 wstride, // weight stride for each vertex.
|
||||
NxF64 plane[4]);
|
||||
|
||||
|
||||
NxF32 fm_computeBestFitAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]); // returns the diagonal distance
|
||||
NxF64 fm_computeBestFitAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]); // returns the diagonal distance
|
||||
|
||||
NxF32 fm_computeBestFitSphere(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 center[3]);
|
||||
NxF64 fm_computeBestFitSphere(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 center[3]);
|
||||
|
||||
bool fm_lineSphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]);
|
||||
bool fm_lineSphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 p1[3],const NxF64 p2[3],NxF64 intersect[3]);
|
||||
|
||||
bool fm_intersectRayAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3],const NxF32 dir[3],NxF32 intersect[3]);
|
||||
bool fm_intersectLineSegmentAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]);
|
||||
|
||||
bool fm_lineIntersectsTriangle(const NxF32 rayStart[3],const NxF32 rayEnd[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 sect[3]);
|
||||
bool fm_lineIntersectsTriangle(const NxF64 rayStart[3],const NxF64 rayEnd[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 sect[3]);
|
||||
|
||||
bool fm_rayIntersectsTriangle(const NxF32 origin[3],const NxF32 dir[3],const NxF32 v0[3],const NxF32 v1[3],const NxF32 v2[3],NxF32 &t);
|
||||
bool fm_rayIntersectsTriangle(const NxF64 origin[3],const NxF64 dir[3],const NxF64 v0[3],const NxF64 v1[3],const NxF64 v2[3],NxF64 &t);
|
||||
|
||||
bool fm_raySphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 pos[3],const NxF32 dir[3],NxF32 distance,NxF32 intersect[3]);
|
||||
bool fm_raySphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 pos[3],const NxF64 dir[3],NxF64 distance,NxF64 intersect[3]);
|
||||
|
||||
void fm_catmullRom(NxF32 out_vector[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],const NxF32 *p4, const NxF32 s);
|
||||
void fm_catmullRom(NxF64 out_vector[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],const NxF64 *p4, const NxF64 s);
|
||||
|
||||
bool fm_intersectAABB(const NxF32 bmin1[3],const NxF32 bmax1[3],const NxF32 bmin2[3],const NxF32 bmax2[3]);
|
||||
bool fm_intersectAABB(const NxF64 bmin1[3],const NxF64 bmax1[3],const NxF64 bmin2[3],const NxF64 bmax2[3]);
|
||||
|
||||
|
||||
// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1
|
||||
void fm_rotationArc(const NxF32 v0[3],const NxF32 v1[3],NxF32 quat[4]);
|
||||
void fm_rotationArc(const NxF64 v0[3],const NxF64 v1[3],NxF64 quat[4]);
|
||||
|
||||
NxF32 fm_distancePointLineSegment(const NxF32 Point[3],const NxF32 LineStart[3],const NxF32 LineEnd[3],NxF32 intersection[3],LineSegmentType &type,NxF32 epsilon);
|
||||
NxF64 fm_distancePointLineSegment(const NxF64 Point[3],const NxF64 LineStart[3],const NxF64 LineEnd[3],NxF64 intersection[3],LineSegmentType &type,NxF64 epsilon);
|
||||
|
||||
|
||||
bool fm_colinear(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 epsilon=0.999); // true if these three points in a row are co-linear
|
||||
bool fm_colinear(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 epsilon=0.999f);
|
||||
|
||||
bool fm_colinear(const NxF32 a1[3],const NxF32 a2[3],const NxF32 b1[3],const NxF32 b2[3],NxF32 epsilon=0.999f); // true if these two line segments are co-linear.
|
||||
bool fm_colinear(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 epsilon=0.999); // true if these two line segments are co-linear.
|
||||
|
||||
enum IntersectResult
|
||||
{
|
||||
IR_DONT_INTERSECT,
|
||||
IR_DO_INTERSECT,
|
||||
IR_COINCIDENT,
|
||||
IR_PARALLEL,
|
||||
};
|
||||
|
||||
IntersectResult fm_intersectLineSegments2d(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3], NxF32 intersectionPoint[3]);
|
||||
IntersectResult fm_intersectLineSegments2d(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 intersectionPoint[3]);
|
||||
|
||||
IntersectResult fm_intersectLineSegments2dTime(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3],NxF32 &t1,NxF32 &t2);
|
||||
IntersectResult fm_intersectLineSegments2dTime(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 &t1,NxF64 &t2);
|
||||
|
||||
// Plane-Triangle splitting
|
||||
|
||||
enum PlaneTriResult
|
||||
{
|
||||
PTR_ON_PLANE,
|
||||
PTR_FRONT,
|
||||
PTR_BACK,
|
||||
PTR_SPLIT,
|
||||
};
|
||||
|
||||
PlaneTriResult fm_planeTriIntersection(const NxF32 plane[4], // the plane equation in Ax+By+Cz+D format
|
||||
const NxF32 *triangle, // the source triangle.
|
||||
NxU32 tstride, // stride in bytes of the input and output *vertices*
|
||||
NxF32 epsilon, // the co-planer epsilon value.
|
||||
NxF32 *front, // the triangle in front of the
|
||||
NxU32 &fcount, // number of vertices in the 'front' triangle
|
||||
NxF32 *back, // the triangle in back of the plane
|
||||
NxU32 &bcount); // the number of vertices in the 'back' triangle.
|
||||
|
||||
|
||||
PlaneTriResult fm_planeTriIntersection(const NxF64 plane[4], // the plane equation in Ax+By+Cz+D format
|
||||
const NxF64 *triangle, // the source triangle.
|
||||
NxU32 tstride, // stride in bytes of the input and output *vertices*
|
||||
NxF64 epsilon, // the co-planer epsilon value.
|
||||
NxF64 *front, // the triangle in front of the
|
||||
NxU32 &fcount, // number of vertices in the 'front' triangle
|
||||
NxF64 *back, // the triangle in back of the plane
|
||||
NxU32 &bcount); // the number of vertices in the 'back' triangle.
|
||||
|
||||
|
||||
void fm_intersectPointPlane(const NxF32 p1[3],const NxF32 p2[3],NxF32 *split,const NxF32 plane[4]);
|
||||
void fm_intersectPointPlane(const NxF64 p1[3],const NxF64 p2[3],NxF64 *split,const NxF64 plane[4]);
|
||||
|
||||
PlaneTriResult fm_getSidePlane(const NxF32 p[3],const NxF32 plane[4],NxF32 epsilon);
|
||||
PlaneTriResult fm_getSidePlane(const NxF64 p[3],const NxF64 plane[4],NxF64 epsilon);
|
||||
|
||||
|
||||
void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 matrix[16],bool bruteForce=true);
|
||||
void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 matrix[16],bool bruteForce=true);
|
||||
|
||||
void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3],NxF32 quat[4],bool bruteForce=true);
|
||||
void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3],NxF64 quat[4],bool bruteForce=true);
|
||||
|
||||
void fm_computeBestFitABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3]);
|
||||
void fm_computeBestFitABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3]);
|
||||
|
||||
|
||||
//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius.
|
||||
void fm_computeBestFitCapsule(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF32 matrix[16],bool bruteForce=true);
|
||||
void fm_computeBestFitCapsule(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF64 matrix[16],bool bruteForce=true);
|
||||
|
||||
|
||||
void fm_planeToMatrix(const NxF32 plane[4],NxF32 matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0
|
||||
void fm_planeToQuat(const NxF32 plane[4],NxF32 quat[4],NxF32 pos[3]); // convert a plane equation to a quaternion and translation
|
||||
|
||||
void fm_planeToMatrix(const NxF64 plane[4],NxF64 matrix[16]); // convert a plane equation to a 4x4 rotation matrix
|
||||
void fm_planeToQuat(const NxF64 plane[4],NxF64 quat[4],NxF64 pos[3]); // convert a plane equation to a quaternion and translation
|
||||
|
||||
inline void fm_doubleToFloat3(const NxF64 p[3],NxF32 t[3]) { t[0] = (NxF32) p[0]; t[1] = (NxF32)p[1]; t[2] = (NxF32)p[2]; };
|
||||
inline void fm_floatToDouble3(const NxF32 p[3],NxF64 t[3]) { t[0] = (NxF64)p[0]; t[1] = (NxF64)p[1]; t[2] = (NxF64)p[2]; };
|
||||
|
||||
|
||||
void fm_eulerMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
void fm_eulerMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
|
||||
|
||||
NxF32 fm_computeMeshVolume(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices);
|
||||
NxF64 fm_computeMeshVolume(const NxF64 *vertices,NxU32 tcount,const NxU32 *indices);
|
||||
|
||||
|
||||
#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity
|
||||
|
||||
class fm_VertexIndex
|
||||
{
|
||||
public:
|
||||
virtual NxU32 getIndex(const NxF32 pos[3],bool &newPos) = 0; // get welded index for this NxF32 vector[3]
|
||||
virtual NxU32 getIndex(const NxF64 pos[3],bool &newPos) = 0; // get welded index for this NxF64 vector[3]
|
||||
virtual const NxF32 * getVerticesFloat(void) const = 0;
|
||||
virtual const NxF64 * getVerticesDouble(void) const = 0;
|
||||
virtual const NxF32 * getVertexFloat(NxU32 index) const = 0;
|
||||
virtual const NxF64 * getVertexDouble(NxU32 index) const = 0;
|
||||
virtual NxU32 getVcount(void) const = 0;
|
||||
virtual bool isDouble(void) const = 0;
|
||||
virtual bool saveAsObj(const char *fname,NxU32 tcount,NxU32 *indices) = 0;
|
||||
};
|
||||
|
||||
fm_VertexIndex * fm_createVertexIndex(NxF64 granularity,bool snapToGrid); // create an indexed vertex system for doubles
|
||||
fm_VertexIndex * fm_createVertexIndex(NxF32 granularity,bool snapToGrid); // create an indexed vertext system for floats
|
||||
void fm_releaseVertexIndex(fm_VertexIndex *vindex);
|
||||
|
||||
|
||||
|
||||
#if 0 // currently disabled
|
||||
|
||||
class fm_LineSegment
|
||||
{
|
||||
public:
|
||||
fm_LineSegment(void)
|
||||
{
|
||||
mE1 = mE2 = 0;
|
||||
}
|
||||
|
||||
fm_LineSegment(NxU32 e1,NxU32 e2)
|
||||
{
|
||||
mE1 = e1;
|
||||
mE2 = e2;
|
||||
}
|
||||
|
||||
NxU32 mE1;
|
||||
NxU32 mE2;
|
||||
};
|
||||
|
||||
|
||||
// LineSweep *only* supports doublees. As a geometric operation it needs as much precision as possible.
|
||||
class fm_LineSweep
|
||||
{
|
||||
public:
|
||||
|
||||
virtual fm_LineSegment * performLineSweep(const fm_LineSegment *segments,
|
||||
NxU32 icount,
|
||||
const NxF64 *planeEquation,
|
||||
fm_VertexIndex *pool,
|
||||
NxU32 &scount) = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
fm_LineSweep * fm_createLineSweep(void);
|
||||
void fm_releaseLineSweep(fm_LineSweep *sweep);
|
||||
|
||||
#endif
|
||||
|
||||
class fm_Triangulate
|
||||
{
|
||||
public:
|
||||
virtual const NxF64 * triangulate3d(NxU32 pcount,
|
||||
const NxF64 *points,
|
||||
NxU32 vstride,
|
||||
NxU32 &tcount,
|
||||
bool consolidate,
|
||||
NxF64 epsilon) = 0;
|
||||
|
||||
virtual const NxF32 * triangulate3d(NxU32 pcount,
|
||||
const NxF32 *points,
|
||||
NxU32 vstride,
|
||||
NxU32 &tcount,
|
||||
bool consolidate,
|
||||
NxF32 epsilon) = 0;
|
||||
};
|
||||
|
||||
fm_Triangulate * fm_createTriangulate(void);
|
||||
void fm_releaseTriangulate(fm_Triangulate *t);
|
||||
|
||||
|
||||
const NxF32 * fm_getPoint(const NxF32 *points,NxU32 pstride,NxU32 index);
|
||||
const NxF64 * fm_getPoint(const NxF64 *points,NxU32 pstride,NxU32 index);
|
||||
|
||||
bool fm_insideTriangle(NxF32 Ax, NxF32 Ay,NxF32 Bx, NxF32 By,NxF32 Cx, NxF32 Cy,NxF32 Px, NxF32 Py);
|
||||
bool fm_insideTriangle(NxF64 Ax, NxF64 Ay,NxF64 Bx, NxF64 By,NxF64 Cx, NxF64 Cy,NxF64 Px, NxF64 Py);
|
||||
NxF32 fm_areaPolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride);
|
||||
NxF64 fm_areaPolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride);
|
||||
|
||||
bool fm_pointInsidePolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride,const NxF32 *point,NxU32 xindex=0,NxU32 yindex=1);
|
||||
bool fm_pointInsidePolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride,const NxF64 *point,NxU32 xindex=0,NxU32 yindex=1);
|
||||
|
||||
NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF32 *points,NxU32 pstride,NxF32 *dest,NxF32 epsilon=0.999999f); // collapses co-linear edges.
|
||||
NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF64 *points,NxU32 pstride,NxF64 *dest,NxF64 epsilon=0.999999); // collapses co-linear edges.
|
||||
|
||||
|
||||
bool fm_computeSplitPlane(NxU32 vcount,const NxF64 *vertices,NxU32 tcount,const NxU32 *indices,NxF64 *plane);
|
||||
bool fm_computeSplitPlane(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxF32 *plane);
|
||||
|
||||
void fm_nearestPointInTriangle(const NxF32 *pos,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 *nearest);
|
||||
void fm_nearestPointInTriangle(const NxF64 *pos,const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 *nearest);
|
||||
|
||||
NxF32 fm_areaTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3);
|
||||
NxF64 fm_areaTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3);
|
||||
|
||||
void fm_subtract(const NxF32 *A,const NxF32 *B,NxF32 *diff); // compute A-B and store the result in 'diff'
|
||||
void fm_subtract(const NxF64 *A,const NxF64 *B,NxF64 *diff); // compute A-B and store the result in 'diff'
|
||||
|
||||
void fm_multiply(NxF32 *A,NxF32 scaler);
|
||||
void fm_multiply(NxF64 *A,NxF64 scaler);
|
||||
|
||||
void fm_add(const NxF32 *A,const NxF32 *B,NxF32 *sum);
|
||||
void fm_add(const NxF64 *A,const NxF64 *B,NxF64 *sum);
|
||||
|
||||
void fm_copy3(const NxF32 *source,NxF32 *dest);
|
||||
void fm_copy3(const NxF64 *source,NxF64 *dest);
|
||||
|
||||
// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices.
|
||||
// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size
|
||||
// is the input buffer. The routine returns the new vertex count after re-indexing.
|
||||
NxU32 fm_copyUniqueVertices(NxU32 vcount,const NxF32 *input_vertices,NxF32 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices);
|
||||
NxU32 fm_copyUniqueVertices(NxU32 vcount,const NxF64 *input_vertices,NxF64 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices);
|
||||
|
||||
bool fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
|
||||
bool fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
|
||||
|
||||
bool fm_samePlane(const NxF32 p1[4],const NxF32 p2[4],NxF32 normalEpsilon=0.01f,NxF32 dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon
|
||||
bool fm_samePlane(const NxF64 p1[4],const NxF64 p2[4],NxF64 normalEpsilon=0.01,NxF64 dEpsilon=0.001,bool doubleSided=false);
|
||||
|
||||
void fm_OBBtoAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 matrix[16],NxF32 abmin[3],NxF32 abmax[3]);
|
||||
|
||||
// a utility class that will tesseleate a mesh.
|
||||
class fm_Tesselate
|
||||
{
|
||||
public:
|
||||
virtual const NxU32 * tesselate(fm_VertexIndex *vindex,NxU32 tcount,const NxU32 *indices,NxF32 longEdge,NxU32 maxDepth,NxU32 &outcount) = 0;
|
||||
};
|
||||
|
||||
fm_Tesselate * fm_createTesselate(void);
|
||||
void fm_releaseTesselate(fm_Tesselate *t);
|
||||
|
||||
void fm_computeMeanNormals(NxU32 vcount, // the number of vertices
|
||||
const NxF32 *vertices, // the base address of the vertex position data.
|
||||
NxU32 vstride, // the stride between position data.
|
||||
NxF32 *normals, // the base address of the destination for mean vector normals
|
||||
NxU32 nstride, // the stride between normals
|
||||
NxU32 tcount, // the number of triangles
|
||||
const NxU32 *indices); // the triangle indices
|
||||
|
||||
void fm_computeMeanNormals(NxU32 vcount, // the number of vertices
|
||||
const NxF64 *vertices, // the base address of the vertex position data.
|
||||
NxU32 vstride, // the stride between position data.
|
||||
NxF64 *normals, // the base address of the destination for mean vector normals
|
||||
NxU32 nstride, // the stride between normals
|
||||
NxU32 tcount, // the number of triangles
|
||||
const NxU32 *indices); // the triangle indices
|
||||
|
||||
|
||||
bool fm_isValidTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 epsilon=0.00001f);
|
||||
bool fm_isValidTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 epsilon=0.00001f);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,783 +0,0 @@
|
|||
/*
|
||||
|
||||
NvMeshIslandGeneration.cpp : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands'
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#pragma warning(disable:4100 4288)
|
||||
#include "NvMeshIslandGeneration.h"
|
||||
#include "NvFloatMath.h"
|
||||
#include "NvHashMap.h"
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector;
|
||||
|
||||
class Edge;
|
||||
class Island;
|
||||
|
||||
class AABB
|
||||
{
|
||||
public:
|
||||
NxF32 mMin[3];
|
||||
NxF32 mMax[3];
|
||||
};
|
||||
|
||||
class Triangle
|
||||
{
|
||||
public:
|
||||
Triangle(void)
|
||||
{
|
||||
mConsumed = false;
|
||||
mIsland = 0;
|
||||
mHandle = 0;
|
||||
mId = 0;
|
||||
}
|
||||
|
||||
void minmax(const NxF32 *p,AABB &box)
|
||||
{
|
||||
if ( p[0] < box.mMin[0] ) box.mMin[0] = p[0];
|
||||
if ( p[1] < box.mMin[1] ) box.mMin[1] = p[1];
|
||||
if ( p[2] < box.mMin[2] ) box.mMin[2] = p[2];
|
||||
|
||||
if ( p[0] > box.mMax[0] ) box.mMax[0] = p[0];
|
||||
if ( p[1] > box.mMax[1] ) box.mMax[1] = p[1];
|
||||
if ( p[2] > box.mMax[2] ) box.mMax[2] = p[2];
|
||||
}
|
||||
|
||||
void minmax(const NxF64 *p,AABB &box)
|
||||
{
|
||||
if ( (NxF32)p[0] < box.mMin[0] ) box.mMin[0] = (NxF32)p[0];
|
||||
if ( (NxF32)p[1] < box.mMin[1] ) box.mMin[1] = (NxF32)p[1];
|
||||
if ( (NxF32)p[2] < box.mMin[2] ) box.mMin[2] = (NxF32)p[2];
|
||||
if ( (NxF32)p[0] > box.mMax[0] ) box.mMax[0] = (NxF32)p[0];
|
||||
if ( (NxF32)p[1] > box.mMax[1] ) box.mMax[1] = (NxF32)p[1];
|
||||
if ( (NxF32)p[2] > box.mMax[2] ) box.mMax[2] = (NxF32)p[2];
|
||||
}
|
||||
|
||||
void buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id);
|
||||
|
||||
void render(NxU32 color)
|
||||
{
|
||||
// gRenderDebug->DebugBound(&mBox.mMin[0],&mBox.mMax[0],color,60.0f);
|
||||
}
|
||||
|
||||
void getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d);
|
||||
|
||||
NxU32 mHandle;
|
||||
bool mConsumed;
|
||||
Edge *mEdges[3];
|
||||
Island *mIsland; // identifies which island it is a member of
|
||||
unsigned short mId;
|
||||
AABB mBox;
|
||||
};
|
||||
|
||||
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
Edge(void)
|
||||
{
|
||||
mI1 = 0;
|
||||
mI2 = 0;
|
||||
mHash = 0;
|
||||
mNext = 0;
|
||||
mPrevious = 0;
|
||||
mParent = 0;
|
||||
mNextTriangleEdge = 0;
|
||||
}
|
||||
|
||||
void init(NxU32 i1,NxU32 i2,Triangle *parent)
|
||||
{
|
||||
assert( i1 < 65536 );
|
||||
assert( i2 < 65536 );
|
||||
|
||||
mI1 = i1;
|
||||
mI2 = i2;
|
||||
mHash = (i2<<16)|i1;
|
||||
mReverseHash = (i1<<16)|i2;
|
||||
mNext = 0;
|
||||
mPrevious = 0;
|
||||
mParent = parent;
|
||||
}
|
||||
|
||||
NxU32 mI1;
|
||||
NxU32 mI2;
|
||||
NxU32 mHash;
|
||||
NxU32 mReverseHash;
|
||||
|
||||
Edge *mNext;
|
||||
Edge *mPrevious;
|
||||
Edge *mNextTriangleEdge;
|
||||
Triangle *mParent;
|
||||
};
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::HashMap< NxU32, Edge * > EdgeHashMap;
|
||||
typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector;
|
||||
|
||||
class EdgeCheck
|
||||
{
|
||||
public:
|
||||
EdgeCheck(Triangle *t,Edge *e)
|
||||
{
|
||||
mTriangle = t;
|
||||
mEdge = e;
|
||||
}
|
||||
|
||||
Triangle *mTriangle;
|
||||
Edge *mEdge;
|
||||
};
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< EdgeCheck > EdgeCheckQueue;
|
||||
|
||||
class Island
|
||||
{
|
||||
public:
|
||||
Island(Triangle *t,Triangle *root)
|
||||
{
|
||||
mVerticesFloat = 0;
|
||||
mVerticesDouble = 0;
|
||||
t->mIsland = this;
|
||||
mTriangles.pushBack(t);
|
||||
mCoplanar = false;
|
||||
fm_initMinMax(mMin,mMax);
|
||||
}
|
||||
|
||||
void add(Triangle *t,Triangle *root)
|
||||
{
|
||||
t->mIsland = this;
|
||||
mTriangles.pushBack(t);
|
||||
}
|
||||
|
||||
void merge(Island &isl)
|
||||
{
|
||||
TriangleVector::Iterator i;
|
||||
for (i=isl.mTriangles.begin(); i!=isl.mTriangles.end(); ++i)
|
||||
{
|
||||
Triangle *t = (*i);
|
||||
mTriangles.pushBack(t);
|
||||
}
|
||||
isl.mTriangles.clear();
|
||||
}
|
||||
|
||||
bool isTouching(Island *isl,const NxF32 *vertices_f,const NxF64 *vertices_d)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
mVerticesFloat = vertices_f;
|
||||
mVerticesDouble = vertices_d;
|
||||
|
||||
if ( fm_intersectAABB(mMin,mMax,isl->mMin,isl->mMax) ) // if the two islands has an intersecting AABB
|
||||
{
|
||||
// todo..
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void SAP_DeletePair(const void* object0, const void* object1, void* user_data, void* pair_user_data)
|
||||
{
|
||||
}
|
||||
|
||||
void render(NxU32 color)
|
||||
{
|
||||
// gRenderDebug->DebugBound(mMin,mMax,color,60.0f);
|
||||
TriangleVector::Iterator i;
|
||||
for (i=mTriangles.begin(); i!=mTriangles.end(); ++i)
|
||||
{
|
||||
Triangle *t = (*i);
|
||||
t->render(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const NxF64 *mVerticesDouble;
|
||||
const NxF32 *mVerticesFloat;
|
||||
|
||||
NxF32 mMin[3];
|
||||
NxF32 mMax[3];
|
||||
bool mCoplanar; // marked as co-planar..
|
||||
TriangleVector mTriangles;
|
||||
};
|
||||
|
||||
|
||||
void Triangle::getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d)
|
||||
{
|
||||
NxU32 i1 = mEdges[0]->mI1;
|
||||
NxU32 i2 = mEdges[1]->mI1;
|
||||
NxU32 i3 = mEdges[2]->mI1;
|
||||
if ( vertices_f )
|
||||
{
|
||||
const NxF32 *p1 = &vertices_f[i1*3];
|
||||
const NxF32 *p2 = &vertices_f[i2*3];
|
||||
const NxF32 *p3 = &vertices_f[i3*3];
|
||||
fm_copy3(p1,tri);
|
||||
fm_copy3(p2,tri+3);
|
||||
fm_copy3(p3,tri+6);
|
||||
}
|
||||
else
|
||||
{
|
||||
const NxF64 *p1 = &vertices_d[i1*3];
|
||||
const NxF64 *p2 = &vertices_d[i2*3];
|
||||
const NxF64 *p3 = &vertices_d[i3*3];
|
||||
fm_doubleToFloat3(p1,tri);
|
||||
fm_doubleToFloat3(p2,tri+3);
|
||||
fm_doubleToFloat3(p3,tri+6);
|
||||
}
|
||||
}
|
||||
|
||||
void Triangle::buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id)
|
||||
{
|
||||
mId = (unsigned short)id;
|
||||
NxU32 i1 = mEdges[0]->mI1;
|
||||
NxU32 i2 = mEdges[1]->mI1;
|
||||
NxU32 i3 = mEdges[2]->mI1;
|
||||
|
||||
if ( vertices_f )
|
||||
{
|
||||
const NxF32 *p1 = &vertices_f[i1*3];
|
||||
const NxF32 *p2 = &vertices_f[i2*3];
|
||||
const NxF32 *p3 = &vertices_f[i3*3];
|
||||
mBox.mMin[0] = p1[0];
|
||||
mBox.mMin[1] = p1[1];
|
||||
mBox.mMin[2] = p1[2];
|
||||
mBox.mMax[0] = p1[0];
|
||||
mBox.mMax[1] = p1[1];
|
||||
mBox.mMax[2] = p1[2];
|
||||
minmax(p2,mBox);
|
||||
minmax(p3,mBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
const NxF64 *p1 = &vertices_d[i1*3];
|
||||
const NxF64 *p2 = &vertices_d[i2*3];
|
||||
const NxF64 *p3 = &vertices_d[i3*3];
|
||||
mBox.mMin[0] = (NxF32)p1[0];
|
||||
mBox.mMin[1] = (NxF32)p1[1];
|
||||
mBox.mMin[2] = (NxF32)p1[2];
|
||||
mBox.mMax[0] = (NxF32)p1[0];
|
||||
mBox.mMax[1] = (NxF32)p1[1];
|
||||
mBox.mMax[2] = (NxF32)p1[2];
|
||||
minmax(p2,mBox);
|
||||
minmax(p3,mBox);
|
||||
}
|
||||
|
||||
assert(mIsland);
|
||||
if ( mIsland )
|
||||
{
|
||||
if ( mBox.mMin[0] < mIsland->mMin[0] ) mIsland->mMin[0] = mBox.mMin[0];
|
||||
if ( mBox.mMin[1] < mIsland->mMin[1] ) mIsland->mMin[1] = mBox.mMin[1];
|
||||
if ( mBox.mMin[2] < mIsland->mMin[2] ) mIsland->mMin[2] = mBox.mMin[2];
|
||||
|
||||
if ( mBox.mMax[0] > mIsland->mMax[0] ) mIsland->mMax[0] = mBox.mMax[0];
|
||||
if ( mBox.mMax[1] > mIsland->mMax[1] ) mIsland->mMax[1] = mBox.mMax[1];
|
||||
if ( mBox.mMax[2] > mIsland->mMax[2] ) mIsland->mMax[2] = mBox.mMax[2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< Island * > IslandVector;
|
||||
|
||||
class MyMeshIslandGeneration : public MeshIslandGeneration
|
||||
{
|
||||
public:
|
||||
MyMeshIslandGeneration(void)
|
||||
{
|
||||
mTriangles = 0;
|
||||
mEdges = 0;
|
||||
mVerticesDouble = 0;
|
||||
mVerticesFloat = 0;
|
||||
}
|
||||
|
||||
~MyMeshIslandGeneration(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(void)
|
||||
{
|
||||
delete []mTriangles;
|
||||
delete []mEdges;
|
||||
mTriangles = 0;
|
||||
mEdges = 0;
|
||||
mTriangleEdges.clear();
|
||||
IslandVector::Iterator i;
|
||||
for (i=mIslands.begin(); i!=mIslands.end(); ++i)
|
||||
{
|
||||
Island *_i = (*i);
|
||||
delete _i;
|
||||
}
|
||||
mIslands.clear();
|
||||
}
|
||||
|
||||
NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices)
|
||||
{
|
||||
mVerticesDouble = vertices;
|
||||
mVerticesFloat = 0;
|
||||
return islandGenerate(tcount,indices);
|
||||
}
|
||||
|
||||
NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices)
|
||||
{
|
||||
mVerticesDouble = 0;
|
||||
mVerticesFloat = vertices;
|
||||
return islandGenerate(tcount,indices);
|
||||
}
|
||||
|
||||
NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
reset();
|
||||
|
||||
mTcount = tcount;
|
||||
mTriangles = new Triangle[tcount];
|
||||
mEdges = new Edge[tcount*3];
|
||||
Edge *e = mEdges;
|
||||
|
||||
for (NxU32 i=0; i<tcount; i++)
|
||||
{
|
||||
Triangle &t = mTriangles[i];
|
||||
|
||||
NxU32 i1 = *indices++;
|
||||
NxU32 i2 = *indices++;
|
||||
NxU32 i3 = *indices++;
|
||||
|
||||
t.mEdges[0] = e;
|
||||
t.mEdges[1] = e+1;
|
||||
t.mEdges[2] = e+2;
|
||||
|
||||
e = addEdge(e,&t,i1,i2);
|
||||
e = addEdge(e,&t,i2,i3);
|
||||
e = addEdge(e,&t,i3,i1);
|
||||
|
||||
}
|
||||
|
||||
// while there are still edges to process...
|
||||
while ( mTriangleEdges.size() != 0 )
|
||||
{
|
||||
|
||||
EdgeHashMap::Iterator iter = mTriangleEdges.getIterator();
|
||||
|
||||
Triangle *t = iter->second->mParent;
|
||||
|
||||
Island *i = new Island(t,mTriangles); // the initial triangle...
|
||||
removeTriangle(t); // remove this triangle from the triangle-edges hashmap
|
||||
|
||||
mIslands.pushBack(i);
|
||||
|
||||
// now keep adding to this island until we can no longer walk any shared edges..
|
||||
addEdgeCheck(t,t->mEdges[0]);
|
||||
addEdgeCheck(t,t->mEdges[1]);
|
||||
addEdgeCheck(t,t->mEdges[2]);
|
||||
|
||||
while ( !mEdgeCheckQueue.empty() )
|
||||
{
|
||||
|
||||
EdgeCheck e = mEdgeCheckQueue.popBack();
|
||||
|
||||
// Process all triangles which share this edge
|
||||
Edge *edge = locateSharedEdge(e.mEdge);
|
||||
|
||||
while ( edge )
|
||||
{
|
||||
Triangle *t = edge->mParent;
|
||||
assert(!t->mConsumed);
|
||||
i->add(t,mTriangles);
|
||||
removeTriangle(t); // remove this triangle from the triangle-edges hashmap
|
||||
|
||||
// now keep adding to this island until we can no longer walk any shared edges..
|
||||
|
||||
if ( edge != t->mEdges[0] )
|
||||
{
|
||||
addEdgeCheck(t,t->mEdges[0]);
|
||||
}
|
||||
|
||||
if ( edge != t->mEdges[1] )
|
||||
{
|
||||
addEdgeCheck(t,t->mEdges[1]);
|
||||
}
|
||||
|
||||
if ( edge != t->mEdges[2] )
|
||||
{
|
||||
addEdgeCheck(t,t->mEdges[2]);
|
||||
}
|
||||
|
||||
edge = locateSharedEdge(e.mEdge); // keep going until all shared edges have been processed!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ret = (NxU32)mIslands.size();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NxU32 * getIsland(NxU32 index,NxU32 &otcount)
|
||||
{
|
||||
NxU32 *ret = 0;
|
||||
|
||||
mIndices.clear();
|
||||
if ( index < mIslands.size() )
|
||||
{
|
||||
Island *i = mIslands[index];
|
||||
otcount = (NxU32)i->mTriangles.size();
|
||||
TriangleVector::Iterator j;
|
||||
for (j=i->mTriangles.begin(); j!=i->mTriangles.end(); ++j)
|
||||
{
|
||||
Triangle *t = (*j);
|
||||
mIndices.pushBack(t->mEdges[0]->mI1);
|
||||
mIndices.pushBack(t->mEdges[1]->mI1);
|
||||
mIndices.pushBack(t->mEdges[2]->mI1);
|
||||
}
|
||||
ret = &mIndices[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void removeTriangle(Triangle *t)
|
||||
{
|
||||
t->mConsumed = true;
|
||||
|
||||
removeEdge(t->mEdges[0]);
|
||||
removeEdge(t->mEdges[1]);
|
||||
removeEdge(t->mEdges[2]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Edge * locateSharedEdge(Edge *e)
|
||||
{
|
||||
Edge *ret = 0;
|
||||
|
||||
const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mReverseHash );
|
||||
if ( found != NULL )
|
||||
{
|
||||
ret = (*found).second;
|
||||
assert( ret->mHash == e->mReverseHash );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void removeEdge(Edge *e)
|
||||
{
|
||||
const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mHash );
|
||||
|
||||
if ( found != NULL )
|
||||
{
|
||||
Edge *prev = 0;
|
||||
Edge *scan = (*found).second;
|
||||
while ( scan && scan != e )
|
||||
{
|
||||
prev = scan;
|
||||
scan = scan->mNextTriangleEdge;
|
||||
}
|
||||
|
||||
if ( scan )
|
||||
{
|
||||
if ( prev == 0 )
|
||||
{
|
||||
if ( scan->mNextTriangleEdge )
|
||||
{
|
||||
mTriangleEdges.erase(e->mHash);
|
||||
mTriangleEdges[e->mHash] = scan->mNextTriangleEdge;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTriangleEdges.erase(e->mHash); // no more polygons have an edge here
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev->mNextTriangleEdge = scan->mNextTriangleEdge;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0); // impossible!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Edge * addEdge(Edge *e,Triangle *t,NxU32 i1,NxU32 i2)
|
||||
{
|
||||
|
||||
e->init(i1,i2,t);
|
||||
|
||||
const EdgeHashMap::Entry *found = mTriangleEdges.find(e->mHash);
|
||||
if ( found == NULL )
|
||||
{
|
||||
mTriangleEdges[ e->mHash ] = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
Edge *pn = (*found).second;
|
||||
e->mNextTriangleEdge = pn;
|
||||
mTriangleEdges.erase(e->mHash);
|
||||
mTriangleEdges[e->mHash] = e;
|
||||
}
|
||||
|
||||
e++;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void addEdgeCheck(Triangle *t,Edge *e)
|
||||
{
|
||||
EdgeCheck ec(t,e);
|
||||
mEdgeCheckQueue.pushBack(ec);
|
||||
}
|
||||
|
||||
NxU32 mergeCoplanarIslands(const NxF32 *vertices)
|
||||
{
|
||||
mVerticesFloat = vertices;
|
||||
mVerticesDouble = 0;
|
||||
return mergeCoplanarIslands();
|
||||
}
|
||||
|
||||
NxU32 mergeCoplanarIslands(const NxF64 *vertices)
|
||||
{
|
||||
mVerticesDouble = vertices;
|
||||
mVerticesFloat = 0;
|
||||
return mergeCoplanarIslands();
|
||||
}
|
||||
|
||||
// this island needs to be merged
|
||||
void mergeTouching(Island *isl)
|
||||
{
|
||||
Island *touching = 0;
|
||||
|
||||
IslandVector::Iterator i;
|
||||
for (i=mIslands.begin(); i!=mIslands.end(); ++i)
|
||||
{
|
||||
Island *_i = (*i);
|
||||
if ( !_i->mCoplanar ) // can't merge with coplanar islands!
|
||||
{
|
||||
if ( _i->isTouching(isl,mVerticesFloat,mVerticesDouble) )
|
||||
{
|
||||
touching = _i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NxU32 mergeCoplanarIslands(void)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
if ( !mIslands.empty() )
|
||||
{
|
||||
|
||||
|
||||
NxU32 cp_count = 0;
|
||||
NxU32 npc_count = 0;
|
||||
|
||||
NxU32 count = (NxU32)mIslands.size();
|
||||
|
||||
for (NxU32 i=0; i<count; i++)
|
||||
{
|
||||
|
||||
NxU32 otcount;
|
||||
const NxU32 *oindices = getIsland(i,otcount);
|
||||
|
||||
if ( otcount )
|
||||
{
|
||||
|
||||
bool isCoplanar;
|
||||
|
||||
if ( mVerticesFloat )
|
||||
isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesFloat, true);
|
||||
else
|
||||
isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesDouble, true);
|
||||
|
||||
if ( isCoplanar )
|
||||
{
|
||||
Island *isl = mIslands[i];
|
||||
isl->mCoplanar = true;
|
||||
cp_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
npc_count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ( cp_count )
|
||||
{
|
||||
if ( npc_count == 0 ) // all islands are co-planar!
|
||||
{
|
||||
IslandVector temp = mIslands;
|
||||
mIslands.clear();
|
||||
Island *isl = mIslands[0];
|
||||
mIslands.pushBack(isl);
|
||||
for (NxU32 i=1; i<cp_count; i++)
|
||||
{
|
||||
Island *_i = mIslands[i];
|
||||
isl->merge(*_i);
|
||||
delete _i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
Triangle *t = mTriangles;
|
||||
for (NxU32 i=0; i<mTcount; i++)
|
||||
{
|
||||
t->buildBox(mVerticesFloat,mVerticesDouble,i);
|
||||
t++;
|
||||
}
|
||||
|
||||
IslandVector::Iterator i;
|
||||
for (i=mIslands.begin(); i!=mIslands.end(); ++i)
|
||||
{
|
||||
Island *isl = (*i);
|
||||
|
||||
NxU32 color = 0x00FF00;
|
||||
|
||||
if ( isl->mCoplanar )
|
||||
{
|
||||
color = 0xFFFF00;
|
||||
}
|
||||
|
||||
mergeTouching(isl);
|
||||
|
||||
}
|
||||
|
||||
IslandVector temp = mIslands;
|
||||
mIslands.clear();
|
||||
for (i=temp.begin(); i!=temp.end(); i++)
|
||||
{
|
||||
Island *isl = (*i);
|
||||
if ( isl->mCoplanar )
|
||||
{
|
||||
delete isl; // kill it
|
||||
}
|
||||
else
|
||||
{
|
||||
mIslands.pushBack(isl);
|
||||
}
|
||||
}
|
||||
ret = (NxU32)mIslands.size();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = npc_count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NxU32 mergeTouchingIslands(const NxF32 *vertices)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NxU32 mergeTouchingIslands(const NxF64 *vertices)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NxU32 mTcount;
|
||||
Triangle *mTriangles;
|
||||
Edge *mEdges;
|
||||
EdgeHashMap mTriangleEdges;
|
||||
IslandVector mIslands;
|
||||
EdgeCheckQueue mEdgeCheckQueue;
|
||||
const NxF64 *mVerticesDouble;
|
||||
const NxF32 *mVerticesFloat;
|
||||
NxU32Vector mIndices;
|
||||
};
|
||||
|
||||
|
||||
MeshIslandGeneration * createMeshIslandGeneration(void)
|
||||
{
|
||||
MyMeshIslandGeneration *mig = new MyMeshIslandGeneration;
|
||||
return static_cast< MeshIslandGeneration *>(mig);
|
||||
}
|
||||
|
||||
void releaseMeshIslandGeneration(MeshIslandGeneration *cm)
|
||||
{
|
||||
MyMeshIslandGeneration *mig = static_cast< MyMeshIslandGeneration *>(cm);
|
||||
delete mig;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
#ifndef MESH_ISLAND_GENERATION_H
|
||||
|
||||
#define MESH_ISLAND_GENERATION_H
|
||||
|
||||
/*
|
||||
|
||||
NvMeshIslandGeneration.h : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands'
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class MeshIslandGeneration
|
||||
{
|
||||
public:
|
||||
|
||||
virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices) = 0;
|
||||
virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices) = 0;
|
||||
|
||||
// sometimes island generation can produce co-planar islands. Slivers if you will. If you are passing these islands into a geometric system
|
||||
// that wants to turn them into physical objects, they may not be acceptable. In this case it may be preferable to merge the co-planar islands with
|
||||
// other islands that it 'touches'.
|
||||
virtual NxU32 mergeCoplanarIslands(const NxF32 *vertices) = 0;
|
||||
virtual NxU32 mergeCoplanarIslands(const NxF64 *vertices) = 0;
|
||||
|
||||
virtual NxU32 mergeTouchingIslands(const NxF32 *vertices) = 0;
|
||||
virtual NxU32 mergeTouchingIslands(const NxF64 *vertices) = 0;
|
||||
|
||||
virtual NxU32 * getIsland(NxU32 index,NxU32 &tcount) = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
MeshIslandGeneration * createMeshIslandGeneration(void);
|
||||
void releaseMeshIslandGeneration(MeshIslandGeneration *cm);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
|
||||
NvRayCast.cpp : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures. That is a 'to do' item.
|
||||
|
||||
*/
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include "NvRayCast.h"
|
||||
#include "NvUserMemAlloc.h"
|
||||
#include "NvFloatMath.h"
|
||||
|
||||
#pragma warning(disable:4100)
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class RayCast : public iRayCast, public Memalloc
|
||||
{
|
||||
public:
|
||||
RayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
|
||||
{
|
||||
mVertices = vertices;
|
||||
mTcount = tcount;
|
||||
mIndices = indices;
|
||||
}
|
||||
|
||||
~RayCast(void)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *dest,NxF32 *hitNormal)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
NxF32 p2[3];
|
||||
|
||||
const NxF32 RAY_DIST=50;
|
||||
|
||||
dest[0] = p2[0] = orig[0]+ dir[0]*RAY_DIST;
|
||||
dest[1] = p2[1] = orig[1]+ dir[1]*RAY_DIST;
|
||||
dest[2] = p2[2] = orig[2]+ dir[2]*RAY_DIST;
|
||||
|
||||
NxF32 nearest = 1e9;
|
||||
NxU32 near_face=0;
|
||||
|
||||
|
||||
for (NxU32 i=0; i<mTcount; i++)
|
||||
{
|
||||
NxU32 i1 = mIndices[i*3+0];
|
||||
NxU32 i2 = mIndices[i*3+1];
|
||||
NxU32 i3 = mIndices[i*3+2];
|
||||
|
||||
const NxF32 *t1 = &mVertices[i1*3];
|
||||
const NxF32 *t2 = &mVertices[i2*3];
|
||||
const NxF32 *t3 = &mVertices[i3*3];
|
||||
|
||||
NxF32 t;
|
||||
if ( fm_rayIntersectsTriangle(orig,dir,t1,t2,t3,t) )
|
||||
{
|
||||
if ( t < nearest )
|
||||
{
|
||||
dest[0] = orig[0]+dir[0]*t;
|
||||
dest[1] = orig[1]+dir[1]*t;
|
||||
dest[2] = orig[2]+dir[2]*t;
|
||||
ret = true;
|
||||
near_face = i;
|
||||
nearest = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ret )
|
||||
{
|
||||
// If the nearest face we hit was back-facing, then reject this cast!
|
||||
NxU32 i1 = mIndices[near_face*3+0];
|
||||
NxU32 i2 = mIndices[near_face*3+1];
|
||||
NxU32 i3 = mIndices[near_face*3+2];
|
||||
|
||||
const NxF32 *t1 = &mVertices[i1*3];
|
||||
const NxF32 *t2 = &mVertices[i2*3];
|
||||
const NxF32 *t3 = &mVertices[i3*3];
|
||||
|
||||
fm_computePlane(t3,t2,t1,hitNormal);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
const NxF32 *mVertices;
|
||||
NxU32 mTcount;
|
||||
const NxU32 *mIndices;
|
||||
|
||||
};
|
||||
|
||||
|
||||
iRayCast *createRayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
|
||||
{
|
||||
RayCast *rc = MEMALLOC_NEW(RayCast)(vertices,tcount,indices);
|
||||
return static_cast< iRayCast *>(rc);
|
||||
}
|
||||
|
||||
void releaseRayCast(iRayCast *rc)
|
||||
{
|
||||
RayCast *r = static_cast< RayCast *>(rc);
|
||||
delete r;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
#ifndef NV_RAYCAST_H
|
||||
|
||||
#define NV_RAYCAST_H
|
||||
|
||||
/*
|
||||
|
||||
NvRayCast.h : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures. That is a 'to do' item.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "NvSimpleTypes.h"
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class iRayCast
|
||||
{
|
||||
public:
|
||||
virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *hitPoint,NxF32 *hitNormal) = 0;
|
||||
protected:
|
||||
virtual ~iRayCast(void) { };
|
||||
};
|
||||
|
||||
|
||||
iRayCast *createRayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices);
|
||||
void releaseRayCast(iRayCast *rc);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,713 +0,0 @@
|
|||
/*
|
||||
|
||||
NvRemoveTjunctions.cpp : A code snippet to remove tjunctions from a triangle mesh. This version is currently disabled as it appears to have a bug.
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#pragma warning(disable:4702)
|
||||
#pragma warning(disable:4127) //conditional expression is constant (because _HAS_EXCEPTIONS=0)
|
||||
#include <vector>
|
||||
#if defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
#include <ext/hash_map>
|
||||
#elif LINUX
|
||||
#include <hash_map>
|
||||
#elif _MSC_VER < 1500
|
||||
#include <hash_map>
|
||||
#elif _MSC_VER > 1800
|
||||
#include <unordered_map>
|
||||
#endif
|
||||
#include "NvUserMemAlloc.h"
|
||||
#include "NvHashMap.h"
|
||||
#include "NvRemoveTjunctions.h"
|
||||
#include "NvFloatMath.h"
|
||||
#ifdef LINUX
|
||||
#include <climits>
|
||||
#endif
|
||||
|
||||
#pragma warning(disable:4189)
|
||||
|
||||
using namespace CONVEX_DECOMPOSITION;
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class AABB
|
||||
{
|
||||
public:
|
||||
NxF32 mMin[3];
|
||||
NxF32 mMax[3];
|
||||
};
|
||||
|
||||
bool gDebug=false;
|
||||
NxU32 gCount=0;
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector;
|
||||
|
||||
class Triangle
|
||||
{
|
||||
public:
|
||||
Triangle(void)
|
||||
{
|
||||
mPending = false;
|
||||
mSplit = false;
|
||||
mI1 = mI2 = mI3 = 0xFFFFFFFF;
|
||||
mId = 0;
|
||||
}
|
||||
|
||||
Triangle(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id)
|
||||
{
|
||||
mPending = false;
|
||||
init(i1,i2,i3,vertices,id);
|
||||
mSplit = false;
|
||||
}
|
||||
|
||||
void init(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id)
|
||||
{
|
||||
mSplit = false;
|
||||
mI1 = i1;
|
||||
mI2 = i2;
|
||||
mI3 = i3;
|
||||
mId = id;
|
||||
|
||||
const float *p1 = &vertices[mI1*3];
|
||||
const float *p2 = &vertices[mI2*3];
|
||||
const float *p3 = &vertices[mI3*3];
|
||||
|
||||
initMinMax(p1,p2,p3);
|
||||
}
|
||||
|
||||
void initMinMax(const float *p1,const float *p2,const float *p3)
|
||||
{
|
||||
fm_copy3(p1,mBmin);
|
||||
fm_copy3(p1,mBmax);
|
||||
fm_minmax(p2,mBmin,mBmax);
|
||||
fm_minmax(p3,mBmin,mBmax);
|
||||
}
|
||||
|
||||
void init(const NxU32 *idx,const float *vertices,NxU32 id)
|
||||
{
|
||||
mSplit = false;
|
||||
mI1 = idx[0];
|
||||
mI2 = idx[1];
|
||||
mI3 = idx[2];
|
||||
mId = id;
|
||||
|
||||
const float *p1 = &vertices[mI1*3];
|
||||
const float *p2 = &vertices[mI2*3];
|
||||
const float *p3 = &vertices[mI3*3];
|
||||
|
||||
initMinMax(p1,p2,p3);
|
||||
|
||||
}
|
||||
|
||||
bool intersects(const float *pos,const float *p1,const float *p2,float epsilon) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
float sect[3];
|
||||
LineSegmentType type;
|
||||
|
||||
float dist = fm_distancePointLineSegment(pos,p1,p2,sect,type,epsilon);
|
||||
|
||||
if ( type == LS_MIDDLE && dist < epsilon )
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool intersects(NxU32 i,const float *vertices,NxU32 &edge,float epsilon) const
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
const float *pos = &vertices[i*3];
|
||||
const float *p1 = &vertices[mI1*3];
|
||||
const float *p2 = &vertices[mI2*3];
|
||||
const float *p3 = &vertices[mI3*3];
|
||||
if ( intersects(pos,p1,p2,epsilon) )
|
||||
{
|
||||
edge = 0;
|
||||
}
|
||||
else if ( intersects(pos,p2,p3,epsilon) )
|
||||
{
|
||||
edge = 1;
|
||||
}
|
||||
else if ( intersects(pos,p3,p1,epsilon) )
|
||||
{
|
||||
edge = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool intersects(const Triangle *t,const float *vertices,NxU32 &intersection_index,NxU32 &edge,float epsilon)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( fm_intersectAABB(mBmin,mBmax,t->mBmin,t->mBmax) ) // only if the AABB's of the two triangles intersect...
|
||||
{
|
||||
|
||||
if ( t->intersects(mI1,vertices,edge,epsilon) )
|
||||
{
|
||||
intersection_index = mI1;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if ( t->intersects(mI2,vertices,edge,epsilon) )
|
||||
{
|
||||
intersection_index = mI2;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if ( t->intersects(mI3,vertices,edge,epsilon) )
|
||||
{
|
||||
intersection_index = mI3;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mSplit:1;
|
||||
bool mPending:1;
|
||||
NxU32 mI1;
|
||||
NxU32 mI2;
|
||||
NxU32 mI3;
|
||||
NxU32 mId;
|
||||
float mBmin[3];
|
||||
float mBmax[3];
|
||||
};
|
||||
|
||||
class RtEdge
|
||||
{
|
||||
public:
|
||||
RtEdge(void)
|
||||
{
|
||||
mNextEdge = 0;
|
||||
mTriangle = 0;
|
||||
mHash = 0;
|
||||
}
|
||||
|
||||
NxU32 init(Triangle *t,NxU32 i1,NxU32 i2)
|
||||
{
|
||||
mTriangle = t;
|
||||
mNextEdge = 0;
|
||||
NX_ASSERT( i1 < 65536 );
|
||||
NX_ASSERT( i2 < 65536 );
|
||||
if ( i1 < i2 )
|
||||
{
|
||||
mHash = (i1<<16)|i2;
|
||||
}
|
||||
else
|
||||
{
|
||||
mHash = (i2<<16)|i1;
|
||||
}
|
||||
return mHash;
|
||||
}
|
||||
RtEdge *mNextEdge;
|
||||
Triangle *mTriangle;
|
||||
NxU32 mHash;
|
||||
};
|
||||
|
||||
|
||||
typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector;
|
||||
typedef CONVEX_DECOMPOSITION::HashMap< NxU32, RtEdge * > EdgeMap;
|
||||
|
||||
class MyRemoveTjunctions : public RemoveTjunctions
|
||||
{
|
||||
public:
|
||||
MyRemoveTjunctions(void)
|
||||
{
|
||||
mInputTriangles = 0;
|
||||
mEdges = 0;
|
||||
mVcount = 0;
|
||||
mVertices = 0;
|
||||
mEdgeCount = 0;
|
||||
}
|
||||
~MyRemoveTjunctions(void)
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc)
|
||||
{
|
||||
NxU32 ret = 0;
|
||||
|
||||
mEpsilon = desc.mEpsilon;
|
||||
|
||||
size_t TcountOut;
|
||||
|
||||
desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, desc.mTcount, desc.mIndices, TcountOut, desc.mIds);
|
||||
|
||||
#ifdef WIN32
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4267)
|
||||
#endif
|
||||
|
||||
NX_ASSERT( TcountOut < UINT_MAX );
|
||||
desc.mTcountOut = TcountOut;
|
||||
|
||||
#ifdef WIN32
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
if ( !mIds.empty() )
|
||||
{
|
||||
desc.mIdsOut = &mIds[0];
|
||||
}
|
||||
|
||||
ret = desc.mTcountOut;
|
||||
|
||||
bool check = ret != desc.mTcount;
|
||||
#if 0
|
||||
while ( check )
|
||||
{
|
||||
NxU32 tcount = ret;
|
||||
NxU32 *indices = new NxU32[tcount*3];
|
||||
NxU32 *ids = new NxU32[tcount];
|
||||
memcpy(indices,desc.mIndicesOut,sizeof(NxU32)*ret*3);
|
||||
memcpy(ids,desc.mIdsOut,sizeof(NxU32)*ret);
|
||||
desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, tcount, indices, desc.mTcountOut, ids );
|
||||
if ( !mIds.empty() )
|
||||
{
|
||||
desc.mIdsOut = &mIds[0];
|
||||
}
|
||||
ret = desc.mTcountOut;
|
||||
delete []indices;
|
||||
delete []ids;
|
||||
check = ret != tcount;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
RtEdge * addEdge(Triangle *t,RtEdge *e,NxU32 i1,NxU32 i2)
|
||||
{
|
||||
NxU32 hash = e->init(t,i1,i2);
|
||||
const EdgeMap::Entry *found = mEdgeMap.find(hash);
|
||||
if ( found == NULL )
|
||||
{
|
||||
mEdgeMap[hash] = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
RtEdge *old_edge = (*found).second;
|
||||
e->mNextEdge = old_edge;
|
||||
mEdgeMap.erase(hash);
|
||||
mEdgeMap[hash] = e;
|
||||
}
|
||||
e++;
|
||||
mEdgeCount++;
|
||||
return e;
|
||||
}
|
||||
|
||||
RtEdge * init(Triangle *t,const NxU32 *indices,const float *vertices,RtEdge *e,NxU32 id)
|
||||
{
|
||||
t->init(indices,vertices,id);
|
||||
e = addEdge(t,e,t->mI1,t->mI2);
|
||||
e = addEdge(t,e,t->mI2,t->mI3);
|
||||
e = addEdge(t,e,t->mI3,t->mI1);
|
||||
return e;
|
||||
}
|
||||
|
||||
void release(void)
|
||||
{
|
||||
mIds.clear();
|
||||
mEdgeMap.clear();
|
||||
mIndices.clear();
|
||||
mSplit.clear();
|
||||
delete []mInputTriangles;
|
||||
delete []mEdges;
|
||||
mInputTriangles = 0;
|
||||
mEdges = 0;
|
||||
mVcount = 0;
|
||||
mVertices = 0;
|
||||
mEdgeCount = 0;
|
||||
|
||||
}
|
||||
|
||||
virtual NxU32 * removeTjunctions(NxU32 vcount,
|
||||
const float *vertices,
|
||||
size_t tcount,
|
||||
const NxU32 *indices,
|
||||
size_t &tcount_out,
|
||||
const NxU32 * ids)
|
||||
{
|
||||
NxU32 *ret = 0;
|
||||
|
||||
release();
|
||||
|
||||
mVcount = vcount;
|
||||
mVertices = vertices;
|
||||
mTcount = (NxU32)tcount;
|
||||
tcount_out = 0;
|
||||
|
||||
mTcount = (NxU32)tcount;
|
||||
mMaxTcount = (NxU32)tcount*2;
|
||||
mInputTriangles = new Triangle[mMaxTcount];
|
||||
Triangle *t = mInputTriangles;
|
||||
|
||||
mEdges = new RtEdge[mMaxTcount*3];
|
||||
mEdgeCount = 0;
|
||||
|
||||
NxU32 id = 0;
|
||||
|
||||
RtEdge *e = mEdges;
|
||||
for (NxU32 i=0; i<tcount; i++)
|
||||
{
|
||||
if ( ids ) id = *ids++;
|
||||
e =init(t,indices,vertices,e,id);
|
||||
indices+=3;
|
||||
t++;
|
||||
}
|
||||
|
||||
{
|
||||
TriangleVector test;
|
||||
for (EdgeMap::Iterator i = mEdgeMap.getIterator(); !i.done(); ++i)
|
||||
{
|
||||
RtEdge *e = (*i).second;
|
||||
if ( e->mNextEdge == 0 ) // open edge!
|
||||
{
|
||||
Triangle *t = e->mTriangle;
|
||||
if ( !t->mPending )
|
||||
{
|
||||
test.pushBack(t);
|
||||
t->mPending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !test.empty() )
|
||||
{
|
||||
TriangleVector::Iterator i;
|
||||
for (i=test.begin(); i!=test.end(); ++i)
|
||||
{
|
||||
Triangle *t = (*i);
|
||||
locateIntersection(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while ( !mSplit.empty() )
|
||||
{
|
||||
TriangleVector scan = mSplit;
|
||||
mSplit.clear();
|
||||
TriangleVector::Iterator i;
|
||||
for (i=scan.begin(); i!=scan.end(); ++i)
|
||||
{
|
||||
Triangle *t = (*i);
|
||||
locateIntersection(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mIndices.clear();
|
||||
mIds.clear();
|
||||
|
||||
t = mInputTriangles;
|
||||
for (NxU32 i=0; i<mTcount; i++)
|
||||
{
|
||||
mIndices.pushBack(t->mI1);
|
||||
mIndices.pushBack(t->mI2);
|
||||
mIndices.pushBack(t->mI3);
|
||||
mIds.pushBack(t->mId);
|
||||
t++;
|
||||
}
|
||||
|
||||
|
||||
mEdgeMap.clear();
|
||||
|
||||
delete []mEdges;
|
||||
mEdges = 0;
|
||||
delete []mInputTriangles;
|
||||
mInputTriangles = 0;
|
||||
tcount_out = mIndices.size()/3;
|
||||
ret = tcount_out ? &mIndices[0] : 0;
|
||||
#ifdef _DEBUG
|
||||
if ( ret )
|
||||
{
|
||||
const NxU32 *scan = ret;
|
||||
for (NxU32 i=0; i<tcount_out; i++)
|
||||
{
|
||||
NxU32 i1 = scan[0];
|
||||
NxU32 i2 = scan[1];
|
||||
NxU32 i3 = scan[2];
|
||||
assert( i1 != i2 && i1 != i3 && i2 != i3 );
|
||||
scan+=3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
Triangle * locateIntersection(Triangle *scan,Triangle *t)
|
||||
{
|
||||
Triangle *ret = 0;
|
||||
|
||||
NxU32 t1 = (NxU32)(scan-mInputTriangles);
|
||||
NxU32 t2 = (NxU32)(t-mInputTriangles);
|
||||
|
||||
NX_ASSERT( t1 < mTcount );
|
||||
NX_ASSERT( t2 < mTcount );
|
||||
|
||||
NX_ASSERT( scan->mI1 < mVcount );
|
||||
NX_ASSERT( scan->mI2 < mVcount );
|
||||
NX_ASSERT( scan->mI3 < mVcount );
|
||||
|
||||
NX_ASSERT( t->mI1 < mVcount );
|
||||
NX_ASSERT( t->mI2 < mVcount );
|
||||
NX_ASSERT( t->mI3 < mVcount );
|
||||
|
||||
|
||||
NxU32 intersection_index;
|
||||
NxU32 edge;
|
||||
|
||||
if ( scan != t && scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) )
|
||||
{
|
||||
|
||||
if ( t->mI1 == intersection_index || t->mI2 == intersection_index || t->mI3 == intersection_index )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// here is where it intersects!
|
||||
NxU32 i1,i2,i3;
|
||||
NxU32 j1,j2,j3;
|
||||
NxU32 id = t->mId;
|
||||
|
||||
switch ( edge )
|
||||
{
|
||||
case 0:
|
||||
i1 = t->mI1;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI3;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI2;
|
||||
j3 = t->mI3;
|
||||
break;
|
||||
case 1:
|
||||
i1 = t->mI2;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI1;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI3;
|
||||
j3 = t->mI1;
|
||||
break;
|
||||
case 2:
|
||||
i1 = t->mI3;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI2;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI1;
|
||||
j3 = t->mI2;
|
||||
break;
|
||||
default:
|
||||
NX_ASSERT(0);
|
||||
i1 = i2 = i3 = 0;
|
||||
j1 = j2 = j3 = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( mTcount < mMaxTcount )
|
||||
{
|
||||
t->init(i1,i2,i3,mVertices,id);
|
||||
Triangle *newt = &mInputTriangles[mTcount];
|
||||
newt->init(j1,j2,j3,mVertices,id);
|
||||
mTcount++;
|
||||
t->mSplit = true;
|
||||
newt->mSplit = true;
|
||||
|
||||
mSplit.pushBack(t);
|
||||
mSplit.pushBack(newt);
|
||||
ret = scan;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Triangle * testIntersection(Triangle *scan,Triangle *t)
|
||||
{
|
||||
Triangle *ret = 0;
|
||||
|
||||
NxU32 t1 = (NxU32)(scan-mInputTriangles);
|
||||
NxU32 t2 = (NxU32)(t-mInputTriangles);
|
||||
|
||||
NX_ASSERT( t1 < mTcount );
|
||||
NX_ASSERT( t2 < mTcount );
|
||||
|
||||
NX_ASSERT( scan->mI1 < mVcount );
|
||||
NX_ASSERT( scan->mI2 < mVcount );
|
||||
NX_ASSERT( scan->mI3 < mVcount );
|
||||
|
||||
NX_ASSERT( t->mI1 < mVcount );
|
||||
NX_ASSERT( t->mI2 < mVcount );
|
||||
NX_ASSERT( t->mI3 < mVcount );
|
||||
|
||||
|
||||
NxU32 intersection_index;
|
||||
NxU32 edge;
|
||||
|
||||
assert( scan != t );
|
||||
|
||||
if ( scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) )
|
||||
{
|
||||
// here is where it intersects!
|
||||
NxU32 i1,i2,i3;
|
||||
NxU32 j1,j2,j3;
|
||||
NxU32 id = t->mId;
|
||||
|
||||
switch ( edge )
|
||||
{
|
||||
case 0:
|
||||
i1 = t->mI1;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI3;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI2;
|
||||
j3 = t->mI3;
|
||||
break;
|
||||
case 1:
|
||||
i1 = t->mI2;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI1;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI3;
|
||||
j3 = t->mI1;
|
||||
break;
|
||||
case 2:
|
||||
i1 = t->mI3;
|
||||
i2 = intersection_index;
|
||||
i3 = t->mI2;
|
||||
j1 = intersection_index;
|
||||
j2 = t->mI1;
|
||||
j3 = t->mI2;
|
||||
break;
|
||||
default:
|
||||
NX_ASSERT(0);
|
||||
i1 = i2 = i3 = 0;
|
||||
j1 = j2 = j3 = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( mTcount < mMaxTcount )
|
||||
{
|
||||
t->init(i1,i2,i3,mVertices,id);
|
||||
Triangle *newt = &mInputTriangles[mTcount];
|
||||
newt->init(j1,j2,j3,mVertices,id);
|
||||
mTcount++;
|
||||
t->mSplit = true;
|
||||
newt->mSplit = true;
|
||||
|
||||
mSplit.pushBack(t);
|
||||
mSplit.pushBack(newt);
|
||||
ret = scan;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Triangle * locateIntersection(Triangle *t)
|
||||
{
|
||||
Triangle *ret = 0;
|
||||
|
||||
Triangle *scan = mInputTriangles;
|
||||
|
||||
for (NxU32 i=0; i<mTcount; i++)
|
||||
{
|
||||
ret = locateIntersection(scan,t);
|
||||
if ( ret )
|
||||
break;
|
||||
scan++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Triangle *mInputTriangles;
|
||||
NxU32 mVcount;
|
||||
NxU32 mMaxTcount;
|
||||
NxU32 mTcount;
|
||||
const float *mVertices;
|
||||
NxU32Vector mIndices;
|
||||
NxU32Vector mIds;
|
||||
TriangleVector mSplit;
|
||||
NxU32 mEdgeCount;
|
||||
RtEdge *mEdges;
|
||||
EdgeMap mEdgeMap;
|
||||
NxF32 mEpsilon;
|
||||
};
|
||||
|
||||
RemoveTjunctions * createRemoveTjunctions(void)
|
||||
{
|
||||
MyRemoveTjunctions *m = new MyRemoveTjunctions;
|
||||
return static_cast< RemoveTjunctions *>(m);
|
||||
}
|
||||
|
||||
void releaseRemoveTjunctions(RemoveTjunctions *tj)
|
||||
{
|
||||
MyRemoveTjunctions *m = static_cast< MyRemoveTjunctions *>(tj);
|
||||
delete m;
|
||||
}
|
||||
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
#ifndef REMOVE_TJUNCTIONS_H
|
||||
|
||||
#define REMOVE_TJUNCTIONS_H
|
||||
|
||||
/*
|
||||
|
||||
NvRemoveTjunctions.h : A code snippet to remove tjunctions from a triangle mesh. This version is currently disabled as it appears to have a bug.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class RemoveTjunctionsDesc
|
||||
{
|
||||
public:
|
||||
RemoveTjunctionsDesc(void)
|
||||
{
|
||||
mVcount = 0;
|
||||
mVertices = 0;
|
||||
mTcount = 0;
|
||||
mIndices = 0;
|
||||
mIds = 0;
|
||||
mTcountOut = 0;
|
||||
mIndicesOut = 0;
|
||||
mIdsOut = 0;
|
||||
mEpsilon = 0.00000001f;
|
||||
}
|
||||
|
||||
// input
|
||||
NxF32 mEpsilon;
|
||||
NxF32 mDistanceEpsilon;
|
||||
NxU32 mVcount; // input vertice count.
|
||||
const NxF32 *mVertices; // input vertices as NxF32s or...
|
||||
NxU32 mTcount; // number of input triangles.
|
||||
const NxU32 *mIndices; // triangle indices.
|
||||
const NxU32 *mIds; // optional triangle Id numbers.
|
||||
// output..
|
||||
NxU32 mTcountOut; // number of output triangles.
|
||||
const NxU32 *mIndicesOut; // output triangle indices
|
||||
const NxU32 *mIdsOut; // output retained id numbers.
|
||||
};
|
||||
|
||||
// Removes t-junctions from an input mesh. Does not generate any new data points, but may possible produce additional triangles and new indices.
|
||||
class RemoveTjunctions
|
||||
{
|
||||
public:
|
||||
|
||||
virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc) =0; // returns number of triangles output and the descriptor is filled with the appropriate results.
|
||||
|
||||
|
||||
};
|
||||
|
||||
RemoveTjunctions * createRemoveTjunctions(void);
|
||||
void releaseRemoveTjunctions(RemoveTjunctions *tj);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#ifndef NV_SIMPLE_TYPES_H
|
||||
|
||||
#define NV_SIMPLE_TYPES_H
|
||||
|
||||
/*
|
||||
|
||||
NvSimpleTypes.h : Defines basic data types for integers and floats.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/malloc.h>
|
||||
#else
|
||||
#if defined( __FreeBSD__)
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__APPLE__) || defined(__CELLOS_LV2__) || defined(LINUX)
|
||||
|
||||
#ifndef stricmp
|
||||
#define stricmp(a, b) strcasecmp((a), (b))
|
||||
#define _stricmp(a, b) strcasecmp((a), (b))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef __int64 NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned __int64 NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(LINUX)
|
||||
typedef long long NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned long long NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
typedef long long NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned long long NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
typedef long long NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned long long NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
typedef long long NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned long long NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(_XBOX)
|
||||
typedef __int64 NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned __int64 NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#elif defined(__PPCGEKKO__)
|
||||
typedef long long NxI64;
|
||||
typedef signed int NxI32;
|
||||
typedef signed short NxI16;
|
||||
typedef signed char NxI8;
|
||||
|
||||
typedef unsigned long long NxU64;
|
||||
typedef unsigned int NxU32;
|
||||
typedef unsigned short NxU16;
|
||||
typedef unsigned char NxU8;
|
||||
|
||||
typedef float NxF32;
|
||||
typedef double NxF64;
|
||||
|
||||
#else
|
||||
#error Unknown platform!
|
||||
#endif
|
||||
|
||||
#ifndef NX_INLINE
|
||||
#define NX_INLINE inline
|
||||
#define NX_ASSERT assert
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
/*
|
||||
|
||||
NvSplitMesh.cpp : A code snippet to split a mesh into two seperate meshes based on a slicing plane.
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#define SHOW_DEBUG 0
|
||||
#if SHOW_DEBUG
|
||||
#include "RenderDebug.h"
|
||||
#endif
|
||||
|
||||
#include "NvSplitMesh.h"
|
||||
#include "NvFloatMath.h"
|
||||
#include "NvHashMap.h"
|
||||
|
||||
#pragma warning(disable:4100)
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
|
||||
typedef Array< NxU32 > NxU32Array;
|
||||
|
||||
class SplitMesh : public iSplitMesh, public Memalloc
|
||||
{
|
||||
public:
|
||||
SplitMesh(void)
|
||||
{
|
||||
mLeftVertices = 0;
|
||||
mRightVertices = 0;
|
||||
}
|
||||
|
||||
~SplitMesh(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(void)
|
||||
{
|
||||
if ( mLeftVertices )
|
||||
{
|
||||
fm_releaseVertexIndex(mLeftVertices);
|
||||
mLeftVertices = 0;
|
||||
}
|
||||
if ( mRightVertices )
|
||||
{
|
||||
fm_releaseVertexIndex(mRightVertices);
|
||||
mRightVertices = 0;
|
||||
}
|
||||
mLeftIndices.clear();
|
||||
mRightIndices.clear();
|
||||
}
|
||||
|
||||
|
||||
virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision)
|
||||
{
|
||||
reset();
|
||||
|
||||
mLeftVertices = fm_createVertexIndex(precision,false);
|
||||
mRightVertices = fm_createVertexIndex(precision,false);
|
||||
|
||||
for (NxU32 i=0; i<source.mTcount; i++)
|
||||
{
|
||||
NxU32 i1 = source.mIndices[i*3+0];
|
||||
NxU32 i2 = source.mIndices[i*3+1];
|
||||
NxU32 i3 = source.mIndices[i*3+2];
|
||||
|
||||
const NxF32 *p1 = &source.mVertices[i1*3];
|
||||
const NxF32 *p2 = &source.mVertices[i2*3];
|
||||
const NxF32 *p3 = &source.mVertices[i3*3];
|
||||
|
||||
NxF32 source_tri[3*3];
|
||||
|
||||
source_tri[0] = p1[0];
|
||||
source_tri[1] = p1[1];
|
||||
source_tri[2] = p1[2];
|
||||
|
||||
source_tri[3] = p2[0];
|
||||
source_tri[4] = p2[1];
|
||||
source_tri[5] = p2[2];
|
||||
|
||||
source_tri[6] = p3[0];
|
||||
source_tri[7] = p3[1];
|
||||
source_tri[8] = p3[2];
|
||||
|
||||
NxF32 front_tri[3*5];
|
||||
NxF32 back_tri[3*5];
|
||||
|
||||
NxU32 fcount,bcount;
|
||||
|
||||
fm_planeTriIntersection(planeEquation,source_tri,sizeof(NxF32)*3,0.000001f,front_tri,fcount,back_tri,bcount);
|
||||
bool newPos;
|
||||
|
||||
if ( fcount )
|
||||
{
|
||||
NxU32 i1,i2,i3,i4;
|
||||
i1 = mLeftVertices->getIndex( &front_tri[0],newPos );
|
||||
i2 = mLeftVertices->getIndex( &front_tri[3],newPos );
|
||||
i3 = mLeftVertices->getIndex( &front_tri[6],newPos );
|
||||
mLeftIndices.pushBack(i1);
|
||||
mLeftIndices.pushBack(i2);
|
||||
mLeftIndices.pushBack(i3);
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->setCurrentColor(0xFFFFFF);
|
||||
NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[3],&front_tri[6]);
|
||||
#endif
|
||||
if ( fcount == 4 )
|
||||
{
|
||||
i4 = mLeftVertices->getIndex( &front_tri[9],newPos );
|
||||
mLeftIndices.pushBack(i1);
|
||||
mLeftIndices.pushBack(i3);
|
||||
mLeftIndices.pushBack(i4);
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->setCurrentColor(0xFFFF00);
|
||||
NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[6],&front_tri[9]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if ( bcount )
|
||||
{
|
||||
NxU32 i1,i2,i3,i4;
|
||||
i1 = mRightVertices->getIndex( &back_tri[0],newPos );
|
||||
i2 = mRightVertices->getIndex( &back_tri[3],newPos );
|
||||
i3 = mRightVertices->getIndex( &back_tri[6],newPos );
|
||||
mRightIndices.pushBack(i1);
|
||||
mRightIndices.pushBack(i2);
|
||||
mRightIndices.pushBack(i3);
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->setCurrentColor(0xFF8080);
|
||||
NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[3],&back_tri[6]);
|
||||
#endif
|
||||
if ( bcount == 4 )
|
||||
{
|
||||
i4 = mRightVertices->getIndex( &back_tri[9],newPos );
|
||||
mRightIndices.pushBack(i1);
|
||||
mRightIndices.pushBack(i3);
|
||||
mRightIndices.pushBack(i4);
|
||||
#if SHOW_DEBUG
|
||||
NVSHARE::gRenderDebug->setCurrentColor(0x00FF00);
|
||||
NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[6],&back_tri[9]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leftMesh.mVcount = mLeftVertices->getVcount();
|
||||
leftMesh.mVertices = mLeftVertices->getVerticesFloat();
|
||||
leftMesh.mTcount = mLeftIndices.size()/3;
|
||||
leftMesh.mIndices = &mLeftIndices[0];
|
||||
|
||||
rightMesh.mVcount = mRightVertices->getVcount();
|
||||
rightMesh.mVertices = mRightVertices->getVerticesFloat();
|
||||
rightMesh.mTcount = mRightIndices.size()/3;
|
||||
rightMesh.mIndices = &mRightIndices[0];
|
||||
|
||||
}
|
||||
|
||||
|
||||
fm_VertexIndex *mLeftVertices;
|
||||
fm_VertexIndex *mRightVertices;
|
||||
NxU32Array mLeftIndices;
|
||||
NxU32Array mRightIndices;
|
||||
};
|
||||
|
||||
iSplitMesh *createSplitMesh(void)
|
||||
{
|
||||
SplitMesh *sm = MEMALLOC_NEW(SplitMesh);
|
||||
return static_cast< iSplitMesh *>(sm);
|
||||
}
|
||||
|
||||
void releaseSplitMesh(iSplitMesh *splitMesh)
|
||||
{
|
||||
SplitMesh *sm = static_cast< SplitMesh *>(splitMesh);
|
||||
delete sm;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
#ifndef NV_SPLIT_MESH_H
|
||||
|
||||
#define NV_SPLIT_MESH_H
|
||||
|
||||
/*
|
||||
|
||||
NvSplitMesh.h : A code snippet to split a mesh into two seperate meshes based on a slicing plane.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
struct NvSplitMesh
|
||||
{
|
||||
NxU32 mVcount;
|
||||
const NxF32 *mVertices;
|
||||
NxU32 mTcount;
|
||||
const NxU32 *mIndices;
|
||||
};
|
||||
|
||||
|
||||
class iSplitMesh
|
||||
{
|
||||
public:
|
||||
virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision) = 0;
|
||||
protected:
|
||||
virtual ~iSplitMesh(void) { };
|
||||
};
|
||||
|
||||
iSplitMesh *createSplitMesh(void);
|
||||
void releaseSplitMesh(iSplitMesh *splitMesh);
|
||||
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,201 +0,0 @@
|
|||
#ifndef NV_STAN_HULL_H
|
||||
|
||||
#define NV_STAN_HULL_H
|
||||
|
||||
/*
|
||||
|
||||
NvStanHull.h : A convex hull generator written by Stan Melax
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class HullResult
|
||||
{
|
||||
public:
|
||||
HullResult(void)
|
||||
{
|
||||
mPolygons = true;
|
||||
mNumOutputVertices = 0;
|
||||
mOutputVertices = 0;
|
||||
mNumFaces = 0;
|
||||
mNumIndices = 0;
|
||||
mIndices = 0;
|
||||
}
|
||||
bool mPolygons; // true if indices represents polygons, false indices are triangles
|
||||
NxU32 mNumOutputVertices; // number of vertices in the output hull
|
||||
NxF32 *mOutputVertices; // array of vertices, 3 floats each x,y,z
|
||||
NxU32 mNumFaces; // the number of faces produced
|
||||
NxU32 mNumIndices; // the total number of indices
|
||||
NxU32 *mIndices; // pointer to indices.
|
||||
|
||||
// If triangles, then indices are array indexes into the vertex list.
|
||||
// If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc..
|
||||
};
|
||||
|
||||
enum HullFlag
|
||||
{
|
||||
QF_TRIANGLES = (1<<0), // report results as triangles, not polygons.
|
||||
QF_REVERSE_ORDER = (1<<1), // reverse order of the triangle indices.
|
||||
QF_SKIN_WIDTH = (1<<2), // extrude hull based on this skin width
|
||||
QF_DEFAULT = (QF_TRIANGLES | QF_SKIN_WIDTH)
|
||||
};
|
||||
|
||||
|
||||
class HullDesc
|
||||
{
|
||||
public:
|
||||
HullDesc(void)
|
||||
{
|
||||
mFlags = QF_DEFAULT;
|
||||
mVcount = 0;
|
||||
mVertices = 0;
|
||||
mVertexStride = 0;
|
||||
mNormalEpsilon = 0.001f;
|
||||
mMaxVertices = 4096; // maximum number of points to be considered for a convex hull.
|
||||
mSkinWidth = 0.01f; // default is one centimeter
|
||||
};
|
||||
|
||||
HullDesc(HullFlag flag,
|
||||
NxU32 vcount,
|
||||
const NxF32 *vertices,
|
||||
NxU32 stride)
|
||||
{
|
||||
mFlags = flag;
|
||||
mVcount = vcount;
|
||||
mVertices = vertices;
|
||||
mVertexStride = stride;
|
||||
mNormalEpsilon = 0.001f;
|
||||
mMaxVertices = 4096;
|
||||
mSkinWidth = 0.01f; // default is one centimeter
|
||||
}
|
||||
|
||||
bool HasHullFlag(HullFlag flag) const
|
||||
{
|
||||
if ( mFlags & flag ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetHullFlag(HullFlag flag)
|
||||
{
|
||||
mFlags|=flag;
|
||||
}
|
||||
|
||||
void ClearHullFlag(HullFlag flag)
|
||||
{
|
||||
mFlags&=~flag;
|
||||
}
|
||||
|
||||
NxU32 mFlags; // flags to use when generating the convex hull.
|
||||
NxU32 mVcount; // number of vertices in the input point cloud
|
||||
const NxF32 *mVertices; // the array of vertices.
|
||||
NxU32 mVertexStride; // the stride of each vertex, in bytes.
|
||||
NxF32 mNormalEpsilon; // the epsilon for removing duplicates. This is a normalized value, if normalized bit is on.
|
||||
NxF32 mSkinWidth;
|
||||
NxU32 mMaxVertices; // maximum number of vertices to be considered for the hull!
|
||||
};
|
||||
|
||||
enum HullError
|
||||
{
|
||||
QE_OK, // success!
|
||||
QE_FAIL, // failed.
|
||||
QE_NOT_READY,
|
||||
};
|
||||
|
||||
// This class is used when converting a convex hull into a triangle mesh.
|
||||
class ConvexHullVertex
|
||||
{
|
||||
public:
|
||||
NxF32 mPos[3];
|
||||
NxF32 mNormal[3];
|
||||
NxF32 mTexel[2];
|
||||
};
|
||||
|
||||
// A virtual interface to receive the triangles from the convex hull.
|
||||
class ConvexHullTriangleInterface
|
||||
{
|
||||
public:
|
||||
virtual void ConvexHullTriangle(const ConvexHullVertex &v1,const ConvexHullVertex &v2,const ConvexHullVertex &v3) = 0;
|
||||
};
|
||||
|
||||
|
||||
class HullLibrary
|
||||
{
|
||||
public:
|
||||
|
||||
HullError CreateConvexHull(const HullDesc &desc, // describes the input request
|
||||
HullResult &result); // contains the resulst
|
||||
|
||||
HullError ReleaseResult(HullResult &result); // release memory allocated for this result, we are done with it.
|
||||
|
||||
HullError CreateTriangleMesh(HullResult &answer,ConvexHullTriangleInterface *iface);
|
||||
private:
|
||||
NxF32 ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C);
|
||||
void AddConvexTriangle(ConvexHullTriangleInterface *callback,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3);
|
||||
|
||||
void BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount);
|
||||
|
||||
bool CleanupVertices(NxU32 svcount,
|
||||
const NxF32 *svertices,
|
||||
NxU32 stride,
|
||||
NxU32 &vcount, // output number of vertices
|
||||
NxF32 *vertices, // location to store the results.
|
||||
NxF32 normalepsilon,
|
||||
NxF32 *scale);
|
||||
};
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
/*
|
||||
|
||||
NvThreadConfig.cpp : A simple wrapper class to define threading and mutex locks.
|
||||
|
||||
*/
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <cassert>
|
||||
#include "NvThreadConfig.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#define _WIN32_WINNT 0x400
|
||||
#include <windows.h>
|
||||
|
||||
#pragma comment(lib,"winmm.lib")
|
||||
|
||||
// #ifndef _WIN32_WINNT
|
||||
|
||||
// #endif
|
||||
// #include <windows.h>
|
||||
//#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#if defined(_XBOX)
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
//#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#define __stdcall
|
||||
#endif
|
||||
|
||||
#if defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
|
||||
#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define VERIFY( x ) (x)
|
||||
#else
|
||||
#define VERIFY( x ) assert((x))
|
||||
#endif
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
NxU32 tc_timeGetTime(void)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
#elif defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, (struct timezone *)0);
|
||||
return tp.tv_sec * 1000 + tp.tv_usec / 1000;
|
||||
#elif defined( _XBOX )
|
||||
return GetTickCount();
|
||||
#else
|
||||
return timeGetTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc_sleep(NxU32 ms)
|
||||
{
|
||||
#if defined(__linux__) || defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
usleep(ms * 1000);
|
||||
#else
|
||||
Sleep(ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc_spinloop()
|
||||
{
|
||||
|
||||
#if defined( _XBOX )
|
||||
// Pause would do nothing on the Xbox. Threads are not scheduled.
|
||||
#elif defined( _WIN64 )
|
||||
YieldProcessor( );
|
||||
#elif defined( __APPLE__ )
|
||||
pthread_yield_np();
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(_POSIX_PRIORITY_SCHEDULING)
|
||||
sched_yield();
|
||||
#else
|
||||
asm("pause");
|
||||
#endif
|
||||
#elif
|
||||
__asm { pause };
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc_interlockedExchange(void *dest, const int64_t exchange)
|
||||
{
|
||||
#if defined( __linux__ ) || defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
|
||||
// not working
|
||||
assert(false);
|
||||
//__sync_lock_test_and_set((int64_t*)dest, exchange);
|
||||
#elif defined( _XBOX ) || defined( _WIN64 )
|
||||
InterlockedExchange((volatile LONG *)dest, exchange);
|
||||
#else
|
||||
__asm
|
||||
{
|
||||
mov ebx, dword ptr [exchange]
|
||||
mov ecx, dword ptr [exchange + 4]
|
||||
mov edi, dest
|
||||
mov eax, dword ptr [edi]
|
||||
mov edx, dword ptr [edi + 4]
|
||||
jmp start
|
||||
retry:
|
||||
pause
|
||||
start:
|
||||
lock cmpxchg8b [edi]
|
||||
jnz retry
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare)
|
||||
{
|
||||
#if defined( __linux__ ) || defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
|
||||
// not working
|
||||
assert(false);
|
||||
return 0;
|
||||
//return __sync_val_compare_and_swap((uintptr_t*)dest, exchange, compare);
|
||||
//return __sync_bool_compare_and_swap((uintptr_t*)dest, exchange, compare);
|
||||
#elif defined( _XBOX ) || defined( _WIN64 )
|
||||
return InterlockedCompareExchange((volatile LONG *)dest, exchange, compare);
|
||||
#else
|
||||
char _ret;
|
||||
//
|
||||
__asm
|
||||
{
|
||||
mov edx, [dest]
|
||||
mov eax, [compare]
|
||||
mov ecx, [exchange]
|
||||
|
||||
lock cmpxchg [edx], ecx
|
||||
|
||||
setz al
|
||||
mov byte ptr [_ret], al
|
||||
}
|
||||
//
|
||||
return _ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2)
|
||||
{
|
||||
#if defined( __linux__ ) || defined( __APPLE__ ) || defined( __FreeBSD__)
|
||||
// not working
|
||||
assert(false);
|
||||
return 0;
|
||||
//uint64_t exchange = ((uint64_t)exchange1 << 32) | (uint64_t)exchange2;
|
||||
//uint64_t compare = ((uint64_t)compare1 << 32) | (uint64_t)compare2;
|
||||
//return __sync_bool_compare_and_swap((int64_t*)dest, exchange, compare);
|
||||
#elif defined( _XBOX ) || defined( _WIN64 )
|
||||
assert(false);
|
||||
return 0;
|
||||
#else
|
||||
char _ret;
|
||||
//
|
||||
__asm
|
||||
{
|
||||
mov ebx, [exchange1]
|
||||
mov ecx, [exchange2]
|
||||
mov edi, [dest]
|
||||
mov eax, [compare1]
|
||||
mov edx, [compare2]
|
||||
lock cmpxchg8b [edi]
|
||||
setz al
|
||||
mov byte ptr [_ret], al
|
||||
}
|
||||
//
|
||||
return _ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
class MyThreadMutex : public ThreadMutex
|
||||
{
|
||||
public:
|
||||
MyThreadMutex(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
InitializeCriticalSection(&m_Mutex);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
pthread_mutexattr_t mutexAttr; // Mutex Attribute
|
||||
VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 );
|
||||
VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 );
|
||||
VERIFY( pthread_mutex_init(&m_Mutex, &mutexAttr) == 0 );
|
||||
VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
~MyThreadMutex(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
DeleteCriticalSection(&m_Mutex);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_mutex_destroy(&m_Mutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void lock(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
EnterCriticalSection(&m_Mutex);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_mutex_lock(&m_Mutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool tryLock(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
bool bRet = false;
|
||||
//assert(("TryEnterCriticalSection seems to not work on XP???", 0));
|
||||
bRet = TryEnterCriticalSection(&m_Mutex) ? true : false;
|
||||
return bRet;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
NxI32 result = pthread_mutex_trylock(&m_Mutex);
|
||||
return (result == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void unlock(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
LeaveCriticalSection(&m_Mutex);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_mutex_unlock(&m_Mutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
CRITICAL_SECTION m_Mutex;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
pthread_mutex_t m_Mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
ThreadMutex * tc_createThreadMutex(void)
|
||||
{
|
||||
MyThreadMutex *m = new MyThreadMutex;
|
||||
return static_cast< ThreadMutex *>(m);
|
||||
}
|
||||
|
||||
void tc_releaseThreadMutex(ThreadMutex *tm)
|
||||
{
|
||||
MyThreadMutex *m = static_cast< MyThreadMutex *>(tm);
|
||||
delete m;
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
static void* _ThreadWorkerFunc(void* arg);
|
||||
#endif
|
||||
|
||||
class MyThread : public Thread
|
||||
{
|
||||
public:
|
||||
MyThread(ThreadInterface *iface)
|
||||
{
|
||||
mInterface = iface;
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
mThread = CreateThread(0, 0, _ThreadWorkerFunc, this, 0, 0);
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_create(&mThread, NULL, _ThreadWorkerFunc, this) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
~MyThread(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
if ( mThread )
|
||||
{
|
||||
CloseHandle(mThread);
|
||||
mThread = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void onJobExecute(void)
|
||||
{
|
||||
mInterface->threadMain();
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadInterface *mInterface;
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
HANDLE mThread;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
pthread_t mThread;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
Thread * tc_createThread(ThreadInterface *tinterface)
|
||||
{
|
||||
MyThread *m = new MyThread(tinterface);
|
||||
return static_cast< Thread *>(m);
|
||||
}
|
||||
|
||||
void tc_releaseThread(Thread *t)
|
||||
{
|
||||
MyThread *m = static_cast<MyThread *>(t);
|
||||
delete m;
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg)
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
static void* _ThreadWorkerFunc(void* arg)
|
||||
#endif
|
||||
{
|
||||
MyThread *worker = (MyThread *) arg;
|
||||
worker->onJobExecute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
class MyThreadEvent : public ThreadEvent
|
||||
{
|
||||
public:
|
||||
MyThreadEvent(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
mEvent = ::CreateEventA(NULL,TRUE,TRUE,"ThreadEvent");
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
pthread_mutexattr_t mutexAttr; // Mutex Attribute
|
||||
VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 );
|
||||
VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 );
|
||||
VERIFY( pthread_mutex_init(&mEventMutex, &mutexAttr) == 0 );
|
||||
VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 );
|
||||
VERIFY( pthread_cond_init(&mEvent, NULL) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
~MyThreadEvent(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
if ( mEvent )
|
||||
{
|
||||
::CloseHandle(mEvent);
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_cond_destroy(&mEvent) == 0 );
|
||||
VERIFY( pthread_mutex_destroy(&mEventMutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void setEvent(void) // signal the event
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
if ( mEvent )
|
||||
{
|
||||
::SetEvent(mEvent);
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_mutex_lock(&mEventMutex) == 0 );
|
||||
VERIFY( pthread_cond_signal(&mEvent) == 0 );
|
||||
VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void resetEvent(void)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
if ( mEvent )
|
||||
{
|
||||
::ResetEvent(mEvent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void waitForSingleObject(NxU32 ms)
|
||||
{
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
if ( mEvent )
|
||||
{
|
||||
::WaitForSingleObject(mEvent,ms);
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
VERIFY( pthread_mutex_lock(&mEventMutex) == 0 );
|
||||
if (ms == 0xffffffff)
|
||||
{
|
||||
VERIFY( pthread_cond_wait(&mEvent, &mEventMutex) == 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec ts;
|
||||
#ifdef __APPLE__
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, (struct timezone *)0);
|
||||
ts.tv_nsec = tp.tv_usec * 1000;
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
#endif
|
||||
ts.tv_nsec += ms * 1000000;
|
||||
ts.tv_sec += ts.tv_nsec / 1000000000;
|
||||
ts.tv_nsec %= 1000000000;
|
||||
NxI32 result = pthread_cond_timedwait(&mEvent, &mEventMutex, &ts);
|
||||
assert(result == 0 || result == ETIMEDOUT);
|
||||
}
|
||||
VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(WIN32) || defined(_XBOX)
|
||||
HANDLE mEvent;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
|
||||
|
||||
pthread_mutex_t mEventMutex;
|
||||
pthread_cond_t mEvent;
|
||||
#endif
|
||||
};
|
||||
|
||||
ThreadEvent * tc_createThreadEvent(void)
|
||||
{
|
||||
MyThreadEvent *m = new MyThreadEvent;
|
||||
return static_cast<ThreadEvent *>(m);
|
||||
}
|
||||
|
||||
void tc_releaseThreadEvent(ThreadEvent *t)
|
||||
{
|
||||
MyThreadEvent *m = static_cast< MyThreadEvent *>(t);
|
||||
delete m;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
#ifndef NV_THREAD_CONFIG_H
|
||||
|
||||
#define NV_THREAD_CONFIG_H
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
/*
|
||||
|
||||
NvThreadConfig.h : A simple wrapper class to define threading and mutex locks.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
NxU32 tc_timeGetTime(void);
|
||||
void tc_sleep(NxU32 ms);
|
||||
|
||||
void tc_spinloop();
|
||||
void tc_interlockedExchange(void *dest, const int64_t exchange);
|
||||
NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare);
|
||||
NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2);
|
||||
|
||||
class ThreadMutex
|
||||
{
|
||||
public:
|
||||
virtual void lock(void) = 0;
|
||||
virtual void unlock(void) = 0;
|
||||
virtual bool tryLock(void) = 0;
|
||||
};
|
||||
|
||||
|
||||
ThreadMutex * tc_createThreadMutex(void);
|
||||
void tc_releaseThreadMutex(ThreadMutex *tm);
|
||||
|
||||
class ThreadInterface
|
||||
{
|
||||
public:
|
||||
virtual void threadMain(void) = 0;
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
Thread * tc_createThread(ThreadInterface *tinterface);
|
||||
void tc_releaseThread(Thread *t);
|
||||
|
||||
class ThreadEvent
|
||||
{
|
||||
public:
|
||||
virtual void setEvent(void) = 0; // signal the event
|
||||
virtual void resetEvent(void) = 0;
|
||||
virtual void waitForSingleObject(NxU32 ms) = 0;
|
||||
};
|
||||
|
||||
ThreadEvent * tc_createThreadEvent(void);
|
||||
void tc_releaseThreadEvent(ThreadEvent *t);
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#ifndef NV_USER_MEMALLOC_H
|
||||
|
||||
#define NV_USER_MEMALLOC_H
|
||||
|
||||
#include "NvSimpleTypes.h"
|
||||
|
||||
/*
|
||||
|
||||
NvUserMemAlloc.h : Modify these macros to change the default memory allocation behavior of the convex decomposition code.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MEMALLOC_NEW
|
||||
#define MEMALLOC_NEW(x) new x
|
||||
#define MEMALLOC_MALLOC(x) ::malloc(x)
|
||||
#define MEMALLOC_FREE(x) ::free(x)
|
||||
#define MEMALLOC_REALLOC(x,y) ::realloc(x,y)
|
||||
#endif
|
||||
|
||||
namespace CONVEX_DECOMPOSITION
|
||||
{
|
||||
|
||||
class Memalloc
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
The ConvexDecomposition library was written by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
|
||||
What is Convex Decomposition?
|
||||
|
||||
Convex Decomposition is when you take an arbitrarily complex triangle mesh and sub-divide it into
|
||||
a collection of discrete compound pieces (each represented as a convex hull) to approximate
|
||||
the original shape of the objet.
|
||||
|
||||
This is required since few physics engines can treat aribtrary triangle mesh objects as dynamic
|
||||
objects. Even those engines which can handle this use case incurr a huge performance and memory
|
||||
penalty to do so.
|
||||
|
||||
By breaking a complex triangle mesh up into a discrete number of convex components you can greatly
|
||||
improve performance for dynamic simulations.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This code is released under the MIT license.
|
||||
|
||||
The code is functional but could use the following improvements:
|
||||
|
||||
(1) The convex hull generator, originally written by Stan Melax, could use some major code cleanup.
|
||||
|
||||
(2) The code to remove T-junctions appears to have a bug in it. This code was working fine before,
|
||||
but I haven't had time to debug why it stopped working.
|
||||
|
||||
(3) Island generation once the mesh has been split is currently disabled due to the fact that the
|
||||
Remove Tjunctions functionality has a bug in it.
|
||||
|
||||
(4) The code to perform a raycast against a triangle mesh does not currently use any acceleration
|
||||
data structures.
|
||||
|
||||
(5) When a split is performed, the surface that got split is not 'capped'. This causes a problem
|
||||
if you use a high recursion depth on your convex decomposition. It will cause the object to
|
||||
be modelled as if it had a hollow interior. A lot of work was done to solve this problem, but
|
||||
it hasn't been integrated into this code drop yet.
|
||||
|
||||
|
||||
|
|
@ -1,852 +0,0 @@
|
|||
/*
|
||||
|
||||
wavefront.cpp : A very small code snippet to read a Wavefront OBJ file into memory.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#ifndef __PPCGEKKO__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "wavefront.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef std::vector< NxI32 > IntVector;
|
||||
typedef std::vector< NxF32 > FloatVector;
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
namespace WAVEFRONT
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
/******************** InParser.h ********************************/
|
||||
/*******************************************************************/
|
||||
class InPlaceParserInterface
|
||||
{
|
||||
public:
|
||||
virtual NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv) =0; // return TRUE to continue parsing, return FALSE to abort parsing process
|
||||
};
|
||||
|
||||
enum SeparatorType
|
||||
{
|
||||
ST_DATA, // is data
|
||||
ST_HARD, // is a hard separator
|
||||
ST_SOFT, // is a soft separator
|
||||
ST_EOS // is a comment symbol, and everything past this character should be ignored
|
||||
};
|
||||
|
||||
class InPlaceParser
|
||||
{
|
||||
public:
|
||||
InPlaceParser(void)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
InPlaceParser(char *data,NxI32 len)
|
||||
{
|
||||
Init();
|
||||
SetSourceData(data,len);
|
||||
}
|
||||
|
||||
InPlaceParser(const char *fname)
|
||||
{
|
||||
Init();
|
||||
SetFile(fname);
|
||||
}
|
||||
|
||||
~InPlaceParser(void);
|
||||
|
||||
void Init(void)
|
||||
{
|
||||
mQuoteChar = 34;
|
||||
mData = 0;
|
||||
mLen = 0;
|
||||
mMyAlloc = false;
|
||||
for (NxI32 i=0; i<256; i++)
|
||||
{
|
||||
mHard[i] = ST_DATA;
|
||||
mHardString[i*2] = i;
|
||||
mHardString[i*2+1] = 0;
|
||||
}
|
||||
mHard[0] = ST_EOS;
|
||||
mHard[32] = ST_SOFT;
|
||||
mHard[9] = ST_SOFT;
|
||||
mHard[13] = ST_SOFT;
|
||||
mHard[10] = ST_SOFT;
|
||||
}
|
||||
|
||||
void SetFile(const char *fname); // use this file as source data to parse.
|
||||
|
||||
void SetSourceData(char *data,NxI32 len)
|
||||
{
|
||||
mData = data;
|
||||
mLen = len;
|
||||
mMyAlloc = false;
|
||||
};
|
||||
|
||||
NxI32 Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason
|
||||
|
||||
NxI32 ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback);
|
||||
|
||||
const char ** GetArglist(char *source,NxI32 &count); // convert source string into an arg list, this is a destructive parse.
|
||||
|
||||
void SetHardSeparator(char c) // add a hard separator
|
||||
{
|
||||
mHard[c] = ST_HARD;
|
||||
}
|
||||
|
||||
void SetHard(char c) // add a hard separator
|
||||
{
|
||||
mHard[c] = ST_HARD;
|
||||
}
|
||||
|
||||
|
||||
void SetCommentSymbol(char c) // comment character, treated as 'end of string'
|
||||
{
|
||||
mHard[c] = ST_EOS;
|
||||
}
|
||||
|
||||
void ClearHardSeparator(char c)
|
||||
{
|
||||
mHard[c] = ST_DATA;
|
||||
}
|
||||
|
||||
|
||||
void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character.
|
||||
|
||||
bool EOS(char c)
|
||||
{
|
||||
if ( mHard[c] == ST_EOS )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetQuoteChar(char c)
|
||||
{
|
||||
mQuoteChar = c;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
inline char * AddHard(NxI32 &argc,const char **argv,char *foo);
|
||||
inline bool IsHard(char c);
|
||||
inline char * SkipSpaces(char *foo);
|
||||
inline bool IsWhiteSpace(char c);
|
||||
inline bool IsNonSeparator(char c); // non seperator,neither hard nor soft
|
||||
|
||||
bool mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it.
|
||||
char *mData; // ascii data to parse.
|
||||
NxI32 mLen; // length of data
|
||||
SeparatorType mHard[256];
|
||||
char mHardString[256*2];
|
||||
char mQuoteChar;
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
/******************** InParser.cpp ********************************/
|
||||
/*******************************************************************/
|
||||
void InPlaceParser::SetFile(const char *fname)
|
||||
{
|
||||
if ( mMyAlloc )
|
||||
{
|
||||
free(mData);
|
||||
}
|
||||
mData = 0;
|
||||
mLen = 0;
|
||||
mMyAlloc = false;
|
||||
|
||||
FILE *fph = fopen(fname,"rb");
|
||||
if ( fph )
|
||||
{
|
||||
fseek(fph,0L,SEEK_END);
|
||||
mLen = ftell(fph);
|
||||
fseek(fph,0L,SEEK_SET);
|
||||
if ( mLen )
|
||||
{
|
||||
mData = (char *) malloc(sizeof(char)*(mLen+1));
|
||||
size_t ok = fread(mData, mLen, 1, fph);
|
||||
if ( !ok )
|
||||
{
|
||||
free(mData);
|
||||
mData = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mData[mLen] = 0; // zero byte terminate end of file marker.
|
||||
mMyAlloc = true;
|
||||
}
|
||||
}
|
||||
fclose(fph);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InPlaceParser::~InPlaceParser(void)
|
||||
{
|
||||
if ( mMyAlloc )
|
||||
{
|
||||
free(mData);
|
||||
}
|
||||
}
|
||||
|
||||
#define MAXARGS 512
|
||||
|
||||
bool InPlaceParser::IsHard(char c)
|
||||
{
|
||||
return mHard[c] == ST_HARD;
|
||||
}
|
||||
|
||||
char * InPlaceParser::AddHard(NxI32 &argc,const char **argv,char *foo)
|
||||
{
|
||||
while ( IsHard(*foo) )
|
||||
{
|
||||
const char *hard = &mHardString[*foo*2];
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = hard;
|
||||
}
|
||||
foo++;
|
||||
}
|
||||
return foo;
|
||||
}
|
||||
|
||||
bool InPlaceParser::IsWhiteSpace(char c)
|
||||
{
|
||||
return mHard[c] == ST_SOFT;
|
||||
}
|
||||
|
||||
char * InPlaceParser::SkipSpaces(char *foo)
|
||||
{
|
||||
while ( !EOS(*foo) && IsWhiteSpace(*foo) ) foo++;
|
||||
return foo;
|
||||
}
|
||||
|
||||
bool InPlaceParser::IsNonSeparator(char c)
|
||||
{
|
||||
if ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
NxI32 InPlaceParser::ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback)
|
||||
{
|
||||
NxI32 ret = 0;
|
||||
|
||||
const char *argv[MAXARGS];
|
||||
NxI32 argc = 0;
|
||||
|
||||
char *foo = line;
|
||||
|
||||
while ( !EOS(*foo) && argc < MAXARGS )
|
||||
{
|
||||
|
||||
foo = SkipSpaces(foo); // skip any leading spaces
|
||||
|
||||
if ( EOS(*foo) ) break;
|
||||
|
||||
if ( *foo == mQuoteChar ) // if it is an open quote
|
||||
{
|
||||
foo++;
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = foo;
|
||||
}
|
||||
while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
|
||||
if ( !EOS(*foo) )
|
||||
{
|
||||
*foo = 0; // replace close quote with zero byte EOS
|
||||
foo++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
|
||||
|
||||
if ( IsNonSeparator(*foo) ) // add non-hard argument.
|
||||
{
|
||||
bool quote = false;
|
||||
if ( *foo == mQuoteChar )
|
||||
{
|
||||
foo++;
|
||||
quote = true;
|
||||
}
|
||||
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = foo;
|
||||
}
|
||||
|
||||
if ( quote )
|
||||
{
|
||||
while (*foo && *foo != mQuoteChar ) foo++;
|
||||
if ( *foo ) *foo = 32;
|
||||
}
|
||||
|
||||
// continue..until we hit an eos ..
|
||||
while ( !EOS(*foo) ) // until we hit EOS
|
||||
{
|
||||
if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
|
||||
{
|
||||
*foo = 0;
|
||||
foo++;
|
||||
break;
|
||||
}
|
||||
else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
|
||||
{
|
||||
const char *hard = &mHardString[*foo*2];
|
||||
*foo = 0;
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = hard;
|
||||
}
|
||||
foo++;
|
||||
break;
|
||||
}
|
||||
foo++;
|
||||
} // end of while loop...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc )
|
||||
{
|
||||
ret = callback->ParseLine(lineno, argc, argv );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NxI32 InPlaceParser::Parse(InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason
|
||||
{
|
||||
assert( callback );
|
||||
if ( !mData ) return 0;
|
||||
|
||||
NxI32 ret = 0;
|
||||
|
||||
NxI32 lineno = 0;
|
||||
|
||||
char *foo = mData;
|
||||
char *begin = foo;
|
||||
|
||||
|
||||
while ( *foo )
|
||||
{
|
||||
if ( *foo == 10 || *foo == 13 )
|
||||
{
|
||||
lineno++;
|
||||
*foo = 0;
|
||||
|
||||
if ( *begin ) // if there is any data to parse at all...
|
||||
{
|
||||
NxI32 v = ProcessLine(lineno,begin,callback);
|
||||
if ( v ) ret = v;
|
||||
}
|
||||
|
||||
foo++;
|
||||
if ( *foo == 10 ) foo++; // skip line feed, if it is in the carraige-return line-feed format...
|
||||
begin = foo;
|
||||
}
|
||||
else
|
||||
{
|
||||
foo++;
|
||||
}
|
||||
}
|
||||
|
||||
lineno++; // lasst line.
|
||||
|
||||
NxI32 v = ProcessLine(lineno,begin,callback);
|
||||
if ( v ) ret = v;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void InPlaceParser::DefaultSymbols(void)
|
||||
{
|
||||
SetHardSeparator(',');
|
||||
SetHardSeparator('(');
|
||||
SetHardSeparator(')');
|
||||
SetHardSeparator('=');
|
||||
SetHardSeparator('[');
|
||||
SetHardSeparator(']');
|
||||
SetHardSeparator('{');
|
||||
SetHardSeparator('}');
|
||||
SetCommentSymbol('#');
|
||||
}
|
||||
|
||||
|
||||
const char ** InPlaceParser::GetArglist(char *line,NxI32 &count) // convert source string into an arg list, this is a destructive parse.
|
||||
{
|
||||
const char **ret = 0;
|
||||
|
||||
static const char *argv[MAXARGS];
|
||||
NxI32 argc = 0;
|
||||
|
||||
char *foo = line;
|
||||
|
||||
while ( !EOS(*foo) && argc < MAXARGS )
|
||||
{
|
||||
|
||||
foo = SkipSpaces(foo); // skip any leading spaces
|
||||
|
||||
if ( EOS(*foo) ) break;
|
||||
|
||||
if ( *foo == mQuoteChar ) // if it is an open quote
|
||||
{
|
||||
foo++;
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = foo;
|
||||
}
|
||||
while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
|
||||
if ( !EOS(*foo) )
|
||||
{
|
||||
*foo = 0; // replace close quote with zero byte EOS
|
||||
foo++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
|
||||
|
||||
if ( IsNonSeparator(*foo) ) // add non-hard argument.
|
||||
{
|
||||
bool quote = false;
|
||||
if ( *foo == mQuoteChar )
|
||||
{
|
||||
foo++;
|
||||
quote = true;
|
||||
}
|
||||
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = foo;
|
||||
}
|
||||
|
||||
if ( quote )
|
||||
{
|
||||
while (*foo && *foo != mQuoteChar ) foo++;
|
||||
if ( *foo ) *foo = 32;
|
||||
}
|
||||
|
||||
// continue..until we hit an eos ..
|
||||
while ( !EOS(*foo) ) // until we hit EOS
|
||||
{
|
||||
if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
|
||||
{
|
||||
*foo = 0;
|
||||
foo++;
|
||||
break;
|
||||
}
|
||||
else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
|
||||
{
|
||||
const char *hard = &mHardString[*foo*2];
|
||||
*foo = 0;
|
||||
if ( argc < MAXARGS )
|
||||
{
|
||||
argv[argc++] = hard;
|
||||
}
|
||||
foo++;
|
||||
break;
|
||||
}
|
||||
foo++;
|
||||
} // end of while loop...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = argc;
|
||||
if ( argc )
|
||||
{
|
||||
ret = argv;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/******************** Geometry.h ********************************/
|
||||
/*******************************************************************/
|
||||
|
||||
class GeometryVertex
|
||||
{
|
||||
public:
|
||||
NxF32 mPos[3];
|
||||
NxF32 mNormal[3];
|
||||
NxF32 mTexel[2];
|
||||
};
|
||||
|
||||
|
||||
class GeometryInterface
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void NodeTriangle(const GeometryVertex *v1,const GeometryVertex *v2,const GeometryVertex *v3, bool textured)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
/******************** Obj.h ********************************/
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
class OBJ : public InPlaceParserInterface
|
||||
{
|
||||
public:
|
||||
NxI32 LoadMesh(const char *fname,GeometryInterface *callback, bool textured);
|
||||
NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv); // return TRUE to continue parsing, return FALSE to abort parsing process
|
||||
private:
|
||||
|
||||
void GetVertex(GeometryVertex &v,const char *face) const;
|
||||
|
||||
FloatVector mVerts;
|
||||
FloatVector mTexels;
|
||||
FloatVector mNormals;
|
||||
|
||||
bool mTextured;
|
||||
|
||||
GeometryInterface *mCallback;
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
/******************** Obj.cpp ********************************/
|
||||
/*******************************************************************/
|
||||
|
||||
NxI32 OBJ::LoadMesh(const char *fname,GeometryInterface *iface, bool textured)
|
||||
{
|
||||
mTextured = textured;
|
||||
NxI32 ret = 0;
|
||||
|
||||
mVerts.clear();
|
||||
mTexels.clear();
|
||||
mNormals.clear();
|
||||
|
||||
mCallback = iface;
|
||||
|
||||
InPlaceParser ipp(fname);
|
||||
|
||||
ipp.Parse(this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * GetArg(const char **argv,NxI32 i,NxI32 argc)
|
||||
{
|
||||
const char * ret = 0;
|
||||
if ( i < argc ) ret = argv[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OBJ::GetVertex(GeometryVertex &v,const char *face) const
|
||||
{
|
||||
v.mPos[0] = 0;
|
||||
v.mPos[1] = 0;
|
||||
v.mPos[2] = 0;
|
||||
|
||||
v.mTexel[0] = 0;
|
||||
v.mTexel[1] = 0;
|
||||
|
||||
v.mNormal[0] = 0;
|
||||
v.mNormal[1] = 1;
|
||||
v.mNormal[2] = 0;
|
||||
|
||||
NxI32 index = atoi( face )-1;
|
||||
|
||||
const char *texel = strstr(face,"/");
|
||||
|
||||
if ( texel )
|
||||
{
|
||||
NxI32 tindex = atoi( texel+1) - 1;
|
||||
|
||||
if ( tindex >=0 && tindex < (NxI32)(mTexels.size()/2) )
|
||||
{
|
||||
const NxF32 *t = &mTexels[tindex*2];
|
||||
|
||||
v.mTexel[0] = t[0];
|
||||
v.mTexel[1] = t[1];
|
||||
|
||||
}
|
||||
|
||||
const char *normal = strstr(texel+1,"/");
|
||||
if ( normal )
|
||||
{
|
||||
NxI32 nindex = atoi( normal+1 ) - 1;
|
||||
|
||||
if (nindex >= 0 && nindex < (NxI32)(mNormals.size()/3) )
|
||||
{
|
||||
const NxF32 *n = &mNormals[nindex*3];
|
||||
|
||||
v.mNormal[0] = n[0];
|
||||
v.mNormal[1] = n[1];
|
||||
v.mNormal[2] = n[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( index >= 0 && index < (NxI32)(mVerts.size()/3) )
|
||||
{
|
||||
|
||||
const NxF32 *p = &mVerts[index*3];
|
||||
|
||||
v.mPos[0] = p[0];
|
||||
v.mPos[1] = p[1];
|
||||
v.mPos[2] = p[2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NxI32 OBJ::ParseLine(NxI32 lineno,NxI32 argc,const char **argv) // return TRUE to continue parsing, return FALSE to abort parsing process
|
||||
{
|
||||
NxI32 ret = 0;
|
||||
|
||||
if ( argc >= 1 )
|
||||
{
|
||||
const char *foo = argv[0];
|
||||
if ( *foo != '#' )
|
||||
{
|
||||
if ( _stricmp(argv[0],"v") == 0 && argc == 4 )
|
||||
{
|
||||
NxF32 vx = (NxF32) atof( argv[1] );
|
||||
NxF32 vy = (NxF32) atof( argv[2] );
|
||||
NxF32 vz = (NxF32) atof( argv[3] );
|
||||
mVerts.push_back(vx);
|
||||
mVerts.push_back(vy);
|
||||
mVerts.push_back(vz);
|
||||
}
|
||||
else if ( _stricmp(argv[0],"vt") == 0 && (argc == 3 || argc == 4))
|
||||
{
|
||||
// ignore 4rd component if present
|
||||
NxF32 tx = (NxF32) atof( argv[1] );
|
||||
NxF32 ty = (NxF32) atof( argv[2] );
|
||||
mTexels.push_back(tx);
|
||||
mTexels.push_back(ty);
|
||||
}
|
||||
else if ( _stricmp(argv[0],"vn") == 0 && argc == 4 )
|
||||
{
|
||||
NxF32 normalx = (NxF32) atof(argv[1]);
|
||||
NxF32 normaly = (NxF32) atof(argv[2]);
|
||||
NxF32 normalz = (NxF32) atof(argv[3]);
|
||||
mNormals.push_back(normalx);
|
||||
mNormals.push_back(normaly);
|
||||
mNormals.push_back(normalz);
|
||||
}
|
||||
else if ( _stricmp(argv[0],"f") == 0 && argc >= 4 )
|
||||
{
|
||||
GeometryVertex v[32];
|
||||
|
||||
NxI32 vcount = argc-1;
|
||||
|
||||
for (NxI32 i=1; i<argc; i++)
|
||||
{
|
||||
GetVertex(v[i-1],argv[i] );
|
||||
}
|
||||
|
||||
mCallback->NodeTriangle(&v[0],&v[1],&v[2], mTextured);
|
||||
|
||||
if ( vcount >=3 ) // do the fan
|
||||
{
|
||||
for (NxI32 i=2; i<(vcount-1); i++)
|
||||
{
|
||||
mCallback->NodeTriangle(&v[0],&v[i],&v[i+1], mTextured);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class BuildMesh : public GeometryInterface
|
||||
{
|
||||
public:
|
||||
|
||||
NxI32 GetIndex(const NxF32 *p, const NxF32 *texCoord)
|
||||
{
|
||||
|
||||
NxI32 vcount = (NxI32)mVertices.size()/3;
|
||||
|
||||
if(vcount>0)
|
||||
{
|
||||
//New MS STL library checks indices in debug build, so zero causes an assert if it is empty.
|
||||
const NxF32 *v = &mVertices[0];
|
||||
const NxF32 *t = texCoord != NULL ? &mTexCoords[0] : NULL;
|
||||
|
||||
for (NxI32 i=0; i<vcount; i++)
|
||||
{
|
||||
if ( v[0] == p[0] && v[1] == p[1] && v[2] == p[2] )
|
||||
{
|
||||
if (texCoord == NULL || (t[0] == texCoord[0] && t[1] == texCoord[1]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
v+=3;
|
||||
if (t != NULL)
|
||||
t += 2;
|
||||
}
|
||||
}
|
||||
|
||||
mVertices.push_back( p[0] );
|
||||
mVertices.push_back( p[1] );
|
||||
mVertices.push_back( p[2] );
|
||||
|
||||
if (texCoord != NULL)
|
||||
{
|
||||
mTexCoords.push_back( texCoord[0] );
|
||||
mTexCoords.push_back( texCoord[1] );
|
||||
}
|
||||
|
||||
return vcount;
|
||||
}
|
||||
|
||||
virtual void NodeTriangle(const GeometryVertex *v1,const GeometryVertex *v2,const GeometryVertex *v3, bool textured)
|
||||
{
|
||||
mIndices.push_back( GetIndex(v1->mPos, textured ? v1->mTexel : NULL) );
|
||||
mIndices.push_back( GetIndex(v2->mPos, textured ? v2->mTexel : NULL) );
|
||||
mIndices.push_back( GetIndex(v3->mPos, textured ? v3->mTexel : NULL) );
|
||||
}
|
||||
|
||||
const FloatVector& GetVertices(void) const { return mVertices; };
|
||||
const FloatVector& GetTexCoords(void) const { return mTexCoords; };
|
||||
const IntVector& GetIndices(void) const { return mIndices; };
|
||||
|
||||
private:
|
||||
FloatVector mVertices;
|
||||
FloatVector mTexCoords;
|
||||
IntVector mIndices;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace WAVEFRONT;
|
||||
|
||||
WavefrontObj::WavefrontObj(void)
|
||||
{
|
||||
mVertexCount = 0;
|
||||
mTriCount = 0;
|
||||
mIndices = 0;
|
||||
mVertices = NULL;
|
||||
mTexCoords = NULL;
|
||||
}
|
||||
|
||||
WavefrontObj::~WavefrontObj(void)
|
||||
{
|
||||
delete mIndices;
|
||||
delete mVertices;
|
||||
}
|
||||
|
||||
NxU32 WavefrontObj::loadObj(const char *fname, bool textured) // load a wavefront obj returns number of triangles that were loaded. Data is persists until the class is destructed.
|
||||
{
|
||||
|
||||
NxU32 ret = 0;
|
||||
|
||||
delete mVertices;
|
||||
mVertices = 0;
|
||||
delete mIndices;
|
||||
mIndices = 0;
|
||||
mVertexCount = 0;
|
||||
mTriCount = 0;
|
||||
|
||||
|
||||
BuildMesh bm;
|
||||
|
||||
OBJ obj;
|
||||
|
||||
obj.LoadMesh(fname,&bm, textured);
|
||||
|
||||
|
||||
const FloatVector &vlist = bm.GetVertices();
|
||||
const IntVector &indices = bm.GetIndices();
|
||||
if ( vlist.size() )
|
||||
{
|
||||
mVertexCount = (NxI32)vlist.size()/3;
|
||||
mVertices = new NxF32[mVertexCount*3];
|
||||
memcpy( mVertices, &vlist[0], sizeof(NxF32)*mVertexCount*3 );
|
||||
|
||||
if (textured)
|
||||
{
|
||||
mTexCoords = new NxF32[mVertexCount * 2];
|
||||
const FloatVector& tList = bm.GetTexCoords();
|
||||
memcpy( mTexCoords, &tList[0], sizeof(NxF32) * mVertexCount * 2);
|
||||
}
|
||||
|
||||
mTriCount = (NxI32)indices.size()/3;
|
||||
mIndices = new NxU32[mTriCount*3*sizeof(NxU32)];
|
||||
memcpy(mIndices, &indices[0], sizeof(NxU32)*mTriCount*3);
|
||||
ret = mTriCount;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#ifndef WAVEFRONT_OBJ_H
|
||||
|
||||
#define WAVEFRONT_OBJ_H
|
||||
|
||||
/*
|
||||
|
||||
wavefront.h : A very small code snippet to read a Wavefront OBJ file into memory.
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
**
|
||||
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
|
||||
**
|
||||
** Portions of this source has been released with the PhysXViewer application, as well as
|
||||
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
|
||||
**
|
||||
** If you find this code useful or you are feeling particularily generous I would
|
||||
** ask that you please go to http://www.amillionpixels.us and make a donation
|
||||
** to Troy DeMolay.
|
||||
**
|
||||
** DeMolay is a youth group for young men between the ages of 12 and 21.
|
||||
** It teaches strong moral principles, as well as leadership skills and
|
||||
** public speaking. The donations page uses the 'pay for pixels' paradigm
|
||||
** where, in this case, a pixel is only a single penny. Donations can be
|
||||
** made for as small as $4 or as high as a $100 block. Each person who donates
|
||||
** will get a link to their own site as well as acknowledgement on the
|
||||
** donations blog located here http://www.amillionpixels.blogspot.com/
|
||||
**
|
||||
** If you wish to contact me you can use the following methods:
|
||||
**
|
||||
** Skype ID: jratcliff63367
|
||||
** Yahoo: jratcliff63367
|
||||
** AOL: jratcliff1961
|
||||
** email: jratcliffscarab@gmail.com
|
||||
**
|
||||
**
|
||||
** The MIT license:
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is furnished
|
||||
** to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in all
|
||||
** copies or substantial portions of the Software.
|
||||
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "NvUserMemAlloc.h"
|
||||
|
||||
class WavefrontObj
|
||||
{
|
||||
public:
|
||||
|
||||
WavefrontObj(void);
|
||||
~WavefrontObj(void);
|
||||
|
||||
NxU32 loadObj(const char *fname, bool textured); // load a wavefront obj returns number of triangles that were loaded. Data is persists until the class is destructed.
|
||||
|
||||
NxU32 mVertexCount;
|
||||
NxU32 mTriCount;
|
||||
NxU32 *mIndices;
|
||||
NxF32 *mVertices;
|
||||
NxF32 *mTexCoords;
|
||||
};
|
||||
|
||||
#endif
|
||||
3
Engine/lib/convexMath/CMakeLists.txt
Normal file
3
Engine/lib/convexMath/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
file(GLOB CONVEX_DECOMP_SOURCES "*.cpp")
|
||||
add_library(convexMath STATIC ${CONVEX_DECOMP_SOURCES})
|
||||
target_include_directories(convexMath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
17
Engine/lib/convexMath/FloatMath.cpp
Normal file
17
Engine/lib/convexMath/FloatMath.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include "FloatMath.h"
|
||||
#include <vector>
|
||||
|
||||
#define REAL float
|
||||
|
||||
#include "FloatMath.inl"
|
||||
|
||||
#undef REAL
|
||||
#define REAL double
|
||||
|
||||
#include "FloatMath.inl"
|
||||
525
Engine/lib/convexMath/FloatMath.h
Normal file
525
Engine/lib/convexMath/FloatMath.h
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
#ifndef FLOAT_MATH_LIB_H
|
||||
|
||||
#define FLOAT_MATH_LIB_H
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace FLOAT_MATH
|
||||
{
|
||||
|
||||
enum FM_ClipState
|
||||
{
|
||||
FMCS_XMIN = (1<<0),
|
||||
FMCS_XMAX = (1<<1),
|
||||
FMCS_YMIN = (1<<2),
|
||||
FMCS_YMAX = (1<<3),
|
||||
FMCS_ZMIN = (1<<4),
|
||||
FMCS_ZMAX = (1<<5),
|
||||
};
|
||||
|
||||
enum FM_Axis
|
||||
{
|
||||
FM_XAXIS = (1<<0),
|
||||
FM_YAXIS = (1<<1),
|
||||
FM_ZAXIS = (1<<2)
|
||||
};
|
||||
|
||||
enum LineSegmentType
|
||||
{
|
||||
LS_START,
|
||||
LS_MIDDLE,
|
||||
LS_END
|
||||
};
|
||||
|
||||
|
||||
const float FM_PI = 3.1415926535897932384626433832795028841971693993751f;
|
||||
const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
|
||||
const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
|
||||
|
||||
//***************** Float versions
|
||||
//***
|
||||
//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z
|
||||
//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W
|
||||
//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix
|
||||
//*** bounding volumes are expressed as two sets of 3 floats/double representing bmin(x,y,z) and bmax(x,y,z)
|
||||
//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D
|
||||
|
||||
FM_Axis fm_getDominantAxis(const float normal[3]);
|
||||
FM_Axis fm_getDominantAxis(const double normal[3]);
|
||||
|
||||
void fm_decomposeTransform(const float local_transform[16],float trans[3],float rot[4],float scale[3]);
|
||||
void fm_decomposeTransform(const double local_transform[16],double trans[3],double rot[4],double scale[3]);
|
||||
|
||||
void fm_multiplyTransform(const float *pA,const float *pB,float *pM);
|
||||
void fm_multiplyTransform(const double *pA,const double *pB,double *pM);
|
||||
|
||||
void fm_inverseTransform(const float matrix[16],float inverse_matrix[16]);
|
||||
void fm_inverseTransform(const double matrix[16],double inverse_matrix[16]);
|
||||
|
||||
void fm_identity(float matrix[16]); // set 4x4 matrix to identity.
|
||||
void fm_identity(double matrix[16]); // set 4x4 matrix to identity.
|
||||
|
||||
void fm_inverseRT(const float matrix[16], const float pos[3], float t[3]); // inverse rotate translate the point.
|
||||
void fm_inverseRT(const double matrix[16],const double pos[3],double t[3]); // inverse rotate translate the point.
|
||||
|
||||
void fm_transform(const float matrix[16], const float pos[3], float t[3]); // rotate and translate this point.
|
||||
void fm_transform(const double matrix[16],const double pos[3],double t[3]); // rotate and translate this point.
|
||||
|
||||
float fm_getDeterminant(const float matrix[16]);
|
||||
double fm_getDeterminant(const double matrix[16]);
|
||||
|
||||
void fm_getSubMatrix(int32_t ki,int32_t kj,float pDst[16],const float matrix[16]);
|
||||
void fm_getSubMatrix(int32_t ki,int32_t kj,double pDst[16],const float matrix[16]);
|
||||
|
||||
void fm_rotate(const float matrix[16],const float pos[3],float t[3]); // only rotate the point by a 4x4 matrix, don't translate.
|
||||
void fm_rotate(const double matrix[16],const double pos[3],double t[3]); // only rotate the point by a 4x4 matrix, don't translate.
|
||||
|
||||
void fm_eulerToMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
void fm_eulerToMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
|
||||
void fm_getAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]);
|
||||
void fm_getAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]);
|
||||
|
||||
void fm_getAABBCenter(const float bmin[3],const float bmax[3],float center[3]);
|
||||
void fm_getAABBCenter(const double bmin[3],const double bmax[3],double center[3]);
|
||||
|
||||
void fm_transformAABB(const float bmin[3],const float bmax[3],const float matrix[16],float tbmin[3],float tbmax[3]);
|
||||
void fm_transformAABB(const double bmin[3],const double bmax[3],const double matrix[16],double tbmin[3],double tbmax[3]);
|
||||
|
||||
void fm_eulerToQuat(float x,float y,float z,float quat[4]); // convert euler angles to quaternion.
|
||||
void fm_eulerToQuat(double x,double y,double z,double quat[4]); // convert euler angles to quaternion.
|
||||
|
||||
void fm_quatToEuler(const float quat[4],float &ax,float &ay,float &az);
|
||||
void fm_quatToEuler(const double quat[4],double &ax,double &ay,double &az);
|
||||
|
||||
void fm_eulerToQuat(const float euler[3],float quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees!
|
||||
void fm_eulerToQuat(const double euler[3],double quat[4]); // convert euler angles to quaternion.
|
||||
|
||||
void fm_scale(float x,float y,float z,float matrix[16]); // apply scale to the matrix.
|
||||
void fm_scale(double x,double y,double z,double matrix[16]); // apply scale to the matrix.
|
||||
|
||||
void fm_eulerToQuatDX(float x,float y,float z,float quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
|
||||
void fm_eulerToQuatDX(double x,double y,double z,double quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
|
||||
|
||||
void fm_eulerToMatrixDX(float x,float y,float z,float matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
|
||||
void fm_eulerToMatrixDX(double x,double y,double z,double matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
|
||||
|
||||
void fm_quatToMatrix(const float quat[4],float matrix[16]); // convert quaternion rotation to matrix, translation set to zero.
|
||||
void fm_quatToMatrix(const double quat[4],double matrix[16]); // convert quaternion rotation to matrix, translation set to zero.
|
||||
|
||||
void fm_quatRotate(const float quat[4],const float v[3],float r[3]); // rotate a vector directly by a quaternion.
|
||||
void fm_quatRotate(const double quat[4],const double v[3],double r[3]); // rotate a vector directly by a quaternion.
|
||||
|
||||
void fm_getTranslation(const float matrix[16],float t[3]);
|
||||
void fm_getTranslation(const double matrix[16],double t[3]);
|
||||
|
||||
void fm_setTranslation(const float *translation,float matrix[16]);
|
||||
void fm_setTranslation(const double *translation,double matrix[16]);
|
||||
|
||||
void fm_multiplyQuat(const float *qa,const float *qb,float *quat);
|
||||
void fm_multiplyQuat(const double *qa,const double *qb,double *quat);
|
||||
|
||||
void fm_matrixToQuat(const float matrix[16],float quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaternion as x,y,z,w
|
||||
void fm_matrixToQuat(const double matrix[16],double quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaternion as x,y,z,w
|
||||
|
||||
float fm_sphereVolume(float radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
|
||||
double fm_sphereVolume(double radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
|
||||
|
||||
float fm_cylinderVolume(float radius,float h);
|
||||
double fm_cylinderVolume(double radius,double h);
|
||||
|
||||
float fm_capsuleVolume(float radius,float h);
|
||||
double fm_capsuleVolume(double radius,double h);
|
||||
|
||||
float fm_distance(const float p1[3],const float p2[3]);
|
||||
double fm_distance(const double p1[3],const double p2[3]);
|
||||
|
||||
float fm_distanceSquared(const float p1[3],const float p2[3]);
|
||||
double fm_distanceSquared(const double p1[3],const double p2[3]);
|
||||
|
||||
float fm_distanceSquaredXZ(const float p1[3],const float p2[3]);
|
||||
double fm_distanceSquaredXZ(const double p1[3],const double p2[3]);
|
||||
|
||||
float fm_computePlane(const float p1[3],const float p2[3],const float p3[3],float *n); // return D
|
||||
double fm_computePlane(const double p1[3],const double p2[3],const double p3[3],double *n); // return D
|
||||
|
||||
float fm_distToPlane(const float plane[4],const float pos[3]); // computes the distance of this point from the plane.
|
||||
double fm_distToPlane(const double plane[4],const double pos[3]); // computes the distance of this point from the plane.
|
||||
|
||||
float fm_dot(const float p1[3],const float p2[3]);
|
||||
double fm_dot(const double p1[3],const double p2[3]);
|
||||
|
||||
void fm_cross(float cross[3],const float a[3],const float b[3]);
|
||||
void fm_cross(double cross[3],const double a[3],const double b[3]);
|
||||
|
||||
float fm_computeNormalVector(float n[3],const float p1[3],const float p2[3]); // as P2-P1 normalized.
|
||||
double fm_computeNormalVector(double n[3],const double p1[3],const double p2[3]); // as P2-P1 normalized.
|
||||
|
||||
bool fm_computeWindingOrder(const float p1[3],const float p2[3],const float p3[3]); // returns true if the triangle is clockwise.
|
||||
bool fm_computeWindingOrder(const double p1[3],const double p2[3],const double p3[3]); // returns true if the triangle is clockwise.
|
||||
|
||||
float fm_normalize(float n[3]); // normalize this vector and return the distance
|
||||
double fm_normalize(double n[3]); // normalize this vector and return the distance
|
||||
|
||||
float fm_normalizeQuat(float n[4]); // normalize this quat
|
||||
double fm_normalizeQuat(double n[4]); // normalize this quat
|
||||
|
||||
void fm_matrixMultiply(const float A[16],const float B[16],float dest[16]);
|
||||
void fm_matrixMultiply(const double A[16],const double B[16],double dest[16]);
|
||||
|
||||
void fm_composeTransform(const float position[3],const float quat[4],const float scale[3],float matrix[16]);
|
||||
void fm_composeTransform(const double position[3],const double quat[4],const double scale[3],double matrix[16]);
|
||||
|
||||
float fm_computeArea(const float p1[3],const float p2[3],const float p3[3]);
|
||||
double fm_computeArea(const double p1[3],const double p2[3],const double p3[3]);
|
||||
|
||||
void fm_lerp(const float p1[3],const float p2[3],float dest[3],float lerpValue);
|
||||
void fm_lerp(const double p1[3],const double p2[3],double dest[3],double lerpValue);
|
||||
|
||||
bool fm_insideTriangleXZ(const float test[3],const float p1[3],const float p2[3],const float p3[3]);
|
||||
bool fm_insideTriangleXZ(const double test[3],const double p1[3],const double p2[3],const double p3[3]);
|
||||
|
||||
bool fm_insideAABB(const float pos[3],const float bmin[3],const float bmax[3]);
|
||||
bool fm_insideAABB(const double pos[3],const double bmin[3],const double bmax[3]);
|
||||
|
||||
bool fm_insideAABB(const float obmin[3],const float obmax[3],const float tbmin[3],const float tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
|
||||
bool fm_insideAABB(const double obmin[3],const double obmax[3],const double tbmin[3],const double tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
|
||||
|
||||
uint32_t fm_clipTestPoint(const float bmin[3],const float bmax[3],const float pos[3]);
|
||||
uint32_t fm_clipTestPoint(const double bmin[3],const double bmax[3],const double pos[3]);
|
||||
|
||||
uint32_t fm_clipTestPointXZ(const float bmin[3],const float bmax[3],const float pos[3]); // only tests X and Z, not Y
|
||||
uint32_t fm_clipTestPointXZ(const double bmin[3],const double bmax[3],const double pos[3]); // only tests X and Z, not Y
|
||||
|
||||
|
||||
uint32_t fm_clipTestAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],const float p3[3],uint32_t &andCode);
|
||||
uint32_t fm_clipTestAABB(const double bmin[3],const double bmax[3],const double p1[3],const double p2[3],const double p3[3],uint32_t &andCode);
|
||||
|
||||
|
||||
bool fm_lineTestAABBXZ(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
|
||||
bool fm_lineTestAABBXZ(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
|
||||
|
||||
bool fm_lineTestAABB(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
|
||||
bool fm_lineTestAABB(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
|
||||
|
||||
|
||||
void fm_initMinMax(const float p[3],float bmin[3],float bmax[3]);
|
||||
void fm_initMinMax(const double p[3],double bmin[3],double bmax[3]);
|
||||
|
||||
void fm_initMinMax(float bmin[3],float bmax[3]);
|
||||
void fm_initMinMax(double bmin[3],double bmax[3]);
|
||||
|
||||
void fm_minmax(const float p[3],float bmin[3],float bmax[3]); // accumulate to a min-max value
|
||||
void fm_minmax(const double p[3],double bmin[3],double bmax[3]); // accumulate to a min-max value
|
||||
|
||||
// Computes the diagonal length of the bounding box and then inflates the bounding box on all sides
|
||||
// by the ratio provided.
|
||||
void fm_inflateMinMax(float bmin[3], float bmax[3], float ratio);
|
||||
void fm_inflateMinMax(double bmin[3], double bmax[3], double ratio);
|
||||
|
||||
float fm_solveX(const float plane[4],float y,float z); // solve for X given this plane equation and the other two components.
|
||||
double fm_solveX(const double plane[4],double y,double z); // solve for X given this plane equation and the other two components.
|
||||
|
||||
float fm_solveY(const float plane[4],float x,float z); // solve for Y given this plane equation and the other two components.
|
||||
double fm_solveY(const double plane[4],double x,double z); // solve for Y given this plane equation and the other two components.
|
||||
|
||||
float fm_solveZ(const float plane[4],float x,float y); // solve for Z given this plane equation and the other two components.
|
||||
double fm_solveZ(const double plane[4],double x,double y); // solve for Z given this plane equation and the other two components.
|
||||
|
||||
bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points
|
||||
const float *points, // starting address of points array.
|
||||
uint32_t vstride, // stride between input points.
|
||||
const float *weights, // *optional point weighting values.
|
||||
uint32_t wstride, // weight stride for each vertex.
|
||||
float plane[4], // Best fit plane equation
|
||||
float center[3]); // Best fit weighted center of input points
|
||||
|
||||
bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points
|
||||
const double *points, // starting address of points array.
|
||||
uint32_t vstride, // stride between input points.
|
||||
const double *weights, // *optional point weighting values.
|
||||
uint32_t wstride, // weight stride for each vertex.
|
||||
double plane[4],
|
||||
double center[3]);
|
||||
|
||||
// Computes the average center of a set of data points
|
||||
bool fm_computeCentroid(uint32_t vcount, // number of input data points
|
||||
const float *points, // starting address of points array.
|
||||
float *center);
|
||||
|
||||
bool fm_computeCentroid(uint32_t vcount, // number of input data points
|
||||
const double *points, // starting address of points array.
|
||||
double *center);
|
||||
|
||||
// Compute centroid of a triangle mesh; takes area of each triangle into account
|
||||
// weighted average
|
||||
bool fm_computeCentroid(uint32_t vcount, // number of input data points
|
||||
const float *points, // starting address of points array.
|
||||
uint32_t triangleCount,
|
||||
const uint32_t *indices,
|
||||
float *center);
|
||||
|
||||
// Compute centroid of a triangle mesh; takes area of each triangle into account
|
||||
// weighted average
|
||||
bool fm_computeCentroid(uint32_t vcount, // number of input data points
|
||||
const double *points, // starting address of points array.
|
||||
uint32_t triangleCount,
|
||||
const uint32_t *indices,
|
||||
double *center);
|
||||
|
||||
|
||||
float fm_computeBestFitAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); // returns the diagonal distance
|
||||
double fm_computeBestFitAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); // returns the diagonal distance
|
||||
|
||||
float fm_computeBestFitSphere(uint32_t vcount,const float *points,uint32_t pstride,float center[3]);
|
||||
double fm_computeBestFitSphere(uint32_t vcount,const double *points,uint32_t pstride,double center[3]);
|
||||
|
||||
bool fm_lineSphereIntersect(const float center[3],float radius,const float p1[3],const float p2[3],float intersect[3]);
|
||||
bool fm_lineSphereIntersect(const double center[3],double radius,const double p1[3],const double p2[3],double intersect[3]);
|
||||
|
||||
bool fm_intersectRayAABB(const float bmin[3],const float bmax[3],const float pos[3],const float dir[3],float intersect[3]);
|
||||
bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]);
|
||||
|
||||
bool fm_lineIntersectsTriangle(const float rayStart[3],const float rayEnd[3],const float p1[3],const float p2[3],const float p3[3],float sect[3]);
|
||||
bool fm_lineIntersectsTriangle(const double rayStart[3],const double rayEnd[3],const double p1[3],const double p2[3],const double p3[3],double sect[3]);
|
||||
|
||||
bool fm_rayIntersectsTriangle(const float origin[3],const float dir[3],const float v0[3],const float v1[3],const float v2[3],float &t);
|
||||
bool fm_rayIntersectsTriangle(const double origin[3],const double dir[3],const double v0[3],const double v1[3],const double v2[3],double &t);
|
||||
|
||||
bool fm_raySphereIntersect(const float center[3],float radius,const float pos[3],const float dir[3],float distance,float intersect[3]);
|
||||
bool fm_raySphereIntersect(const double center[3],double radius,const double pos[3],const double dir[3],double distance,double intersect[3]);
|
||||
|
||||
void fm_catmullRom(float out_vector[3],const float p1[3],const float p2[3],const float p3[3],const float *p4, const float s);
|
||||
void fm_catmullRom(double out_vector[3],const double p1[3],const double p2[3],const double p3[3],const double *p4, const double s);
|
||||
|
||||
bool fm_intersectAABB(const float bmin1[3],const float bmax1[3],const float bmin2[3],const float bmax2[3]);
|
||||
bool fm_intersectAABB(const double bmin1[3],const double bmax1[3],const double bmin2[3],const double bmax2[3]);
|
||||
|
||||
|
||||
// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1
|
||||
void fm_rotationArc(const float v0[3],const float v1[3],float quat[4]);
|
||||
void fm_rotationArc(const double v0[3],const double v1[3],double quat[4]);
|
||||
|
||||
float fm_distancePointLineSegment(const float Point[3],const float LineStart[3],const float LineEnd[3],float intersection[3],LineSegmentType &type,float epsilon);
|
||||
double fm_distancePointLineSegment(const double Point[3],const double LineStart[3],const double LineEnd[3],double intersection[3],LineSegmentType &type,double epsilon);
|
||||
|
||||
|
||||
bool fm_colinear(const double p1[3],const double p2[3],const double p3[3],double epsilon=0.999); // true if these three points in a row are co-linear
|
||||
bool fm_colinear(const float p1[3],const float p2[3],const float p3[3],float epsilon=0.999f);
|
||||
|
||||
bool fm_colinear(const float a1[3],const float a2[3],const float b1[3],const float b2[3],float epsilon=0.999f); // true if these two line segments are co-linear.
|
||||
bool fm_colinear(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double epsilon=0.999); // true if these two line segments are co-linear.
|
||||
|
||||
enum IntersectResult
|
||||
{
|
||||
IR_DONT_INTERSECT,
|
||||
IR_DO_INTERSECT,
|
||||
IR_COINCIDENT,
|
||||
IR_PARALLEL,
|
||||
};
|
||||
|
||||
IntersectResult fm_intersectLineSegments2d(const float a1[3], const float a2[3], const float b1[3], const float b2[3], float intersectionPoint[3]);
|
||||
IntersectResult fm_intersectLineSegments2d(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double intersectionPoint[3]);
|
||||
|
||||
IntersectResult fm_intersectLineSegments2dTime(const float a1[3], const float a2[3], const float b1[3], const float b2[3],float &t1,float &t2);
|
||||
IntersectResult fm_intersectLineSegments2dTime(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double &t1,double &t2);
|
||||
|
||||
// Plane-Triangle splitting
|
||||
|
||||
enum PlaneTriResult
|
||||
{
|
||||
PTR_ON_PLANE,
|
||||
PTR_FRONT,
|
||||
PTR_BACK,
|
||||
PTR_SPLIT,
|
||||
};
|
||||
|
||||
PlaneTriResult fm_planeTriIntersection(const float plane[4], // the plane equation in Ax+By+Cz+D format
|
||||
const float *triangle, // the source triangle.
|
||||
uint32_t tstride, // stride in bytes of the input and output *vertices*
|
||||
float epsilon, // the co-planer epsilon value.
|
||||
float *front, // the triangle in front of the
|
||||
uint32_t &fcount, // number of vertices in the 'front' triangle
|
||||
float *back, // the triangle in back of the plane
|
||||
uint32_t &bcount); // the number of vertices in the 'back' triangle.
|
||||
|
||||
|
||||
PlaneTriResult fm_planeTriIntersection(const double plane[4], // the plane equation in Ax+By+Cz+D format
|
||||
const double *triangle, // the source triangle.
|
||||
uint32_t tstride, // stride in bytes of the input and output *vertices*
|
||||
double epsilon, // the co-planer epsilon value.
|
||||
double *front, // the triangle in front of the
|
||||
uint32_t &fcount, // number of vertices in the 'front' triangle
|
||||
double *back, // the triangle in back of the plane
|
||||
uint32_t &bcount); // the number of vertices in the 'back' triangle.
|
||||
|
||||
|
||||
bool fm_intersectPointPlane(const float p1[3],const float p2[3],float *split,const float plane[4]);
|
||||
bool fm_intersectPointPlane(const double p1[3],const double p2[3],double *split,const double plane[4]);
|
||||
|
||||
PlaneTriResult fm_getSidePlane(const float p[3],const float plane[4],float epsilon);
|
||||
PlaneTriResult fm_getSidePlane(const double p[3],const double plane[4],double epsilon);
|
||||
|
||||
|
||||
void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float matrix[16],bool bruteForce=true);
|
||||
void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double matrix[16],bool bruteForce=true);
|
||||
|
||||
void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3],float quat[4],bool bruteForce=true);
|
||||
void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3],double quat[4],bool bruteForce=true);
|
||||
|
||||
void fm_computeBestFitABB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3]);
|
||||
void fm_computeBestFitABB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3]);
|
||||
|
||||
|
||||
//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius.
|
||||
void fm_computeBestFitCapsule(uint32_t vcount,const float *points,uint32_t pstride,float &radius,float &height,float matrix[16],bool bruteForce=true);
|
||||
void fm_computeBestFitCapsule(uint32_t vcount,const double *points,uint32_t pstride,float &radius,float &height,double matrix[16],bool bruteForce=true);
|
||||
|
||||
|
||||
void fm_planeToMatrix(const float plane[4],float matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0
|
||||
void fm_planeToQuat(const float plane[4],float quat[4],float pos[3]); // convert a plane equation to a quaternion and translation
|
||||
|
||||
void fm_planeToMatrix(const double plane[4],double matrix[16]); // convert a plane equation to a 4x4 rotation matrix
|
||||
void fm_planeToQuat(const double plane[4],double quat[4],double pos[3]); // convert a plane equation to a quaternion and translation
|
||||
|
||||
inline void fm_doubleToFloat3(const double p[3],float t[3]) { t[0] = (float) p[0]; t[1] = (float)p[1]; t[2] = (float)p[2]; };
|
||||
inline void fm_floatToDouble3(const float p[3],double t[3]) { t[0] = (double)p[0]; t[1] = (double)p[1]; t[2] = (double)p[2]; };
|
||||
|
||||
|
||||
void fm_eulerMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
void fm_eulerMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
|
||||
|
||||
|
||||
float fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices);
|
||||
double fm_computeMeshVolume(const double *vertices,uint32_t tcount,const uint32_t *indices);
|
||||
|
||||
|
||||
#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity
|
||||
|
||||
class fm_VertexIndex
|
||||
{
|
||||
public:
|
||||
virtual uint32_t getIndex(const float pos[3],bool &newPos) = 0; // get welded index for this float vector[3]
|
||||
virtual uint32_t getIndex(const double pos[3],bool &newPos) = 0; // get welded index for this double vector[3]
|
||||
virtual const float * getVerticesFloat(void) const = 0;
|
||||
virtual const double * getVerticesDouble(void) const = 0;
|
||||
virtual const float * getVertexFloat(uint32_t index) const = 0;
|
||||
virtual const double * getVertexDouble(uint32_t index) const = 0;
|
||||
virtual uint32_t getVcount(void) const = 0;
|
||||
virtual bool isDouble(void) const = 0;
|
||||
virtual bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0;
|
||||
};
|
||||
|
||||
fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles
|
||||
fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid); // create an indexed vertex system for floats
|
||||
void fm_releaseVertexIndex(fm_VertexIndex *vindex);
|
||||
|
||||
|
||||
class fm_Triangulate
|
||||
{
|
||||
public:
|
||||
virtual const double * triangulate3d(uint32_t pcount,
|
||||
const double *points,
|
||||
uint32_t vstride,
|
||||
uint32_t &tcount,
|
||||
bool consolidate,
|
||||
double epsilon) = 0;
|
||||
|
||||
virtual const float * triangulate3d(uint32_t pcount,
|
||||
const float *points,
|
||||
uint32_t vstride,
|
||||
uint32_t &tcount,
|
||||
bool consolidate,
|
||||
float epsilon) = 0;
|
||||
};
|
||||
|
||||
fm_Triangulate * fm_createTriangulate(void);
|
||||
void fm_releaseTriangulate(fm_Triangulate *t);
|
||||
|
||||
|
||||
const float * fm_getPoint(const float *points,uint32_t pstride,uint32_t index);
|
||||
const double * fm_getPoint(const double *points,uint32_t pstride,uint32_t index);
|
||||
|
||||
bool fm_insideTriangle(float Ax, float Ay,float Bx, float By,float Cx, float Cy,float Px, float Py);
|
||||
bool fm_insideTriangle(double Ax, double Ay,double Bx, double By,double Cx, double Cy,double Px, double Py);
|
||||
float fm_areaPolygon2d(uint32_t pcount,const float *points,uint32_t pstride);
|
||||
double fm_areaPolygon2d(uint32_t pcount,const double *points,uint32_t pstride);
|
||||
|
||||
bool fm_pointInsidePolygon2d(uint32_t pcount,const float *points,uint32_t pstride,const float *point,uint32_t xindex=0,uint32_t yindex=1);
|
||||
bool fm_pointInsidePolygon2d(uint32_t pcount,const double *points,uint32_t pstride,const double *point,uint32_t xindex=0,uint32_t yindex=1);
|
||||
|
||||
uint32_t fm_consolidatePolygon(uint32_t pcount,const float *points,uint32_t pstride,float *dest,float epsilon=0.999999f); // collapses co-linear edges.
|
||||
uint32_t fm_consolidatePolygon(uint32_t pcount,const double *points,uint32_t pstride,double *dest,double epsilon=0.999999); // collapses co-linear edges.
|
||||
|
||||
|
||||
bool fm_computeSplitPlane(uint32_t vcount,const double *vertices,uint32_t tcount,const uint32_t *indices,double *plane);
|
||||
bool fm_computeSplitPlane(uint32_t vcount,const float *vertices,uint32_t tcount,const uint32_t *indices,float *plane);
|
||||
|
||||
void fm_nearestPointInTriangle(const float *pos,const float *p1,const float *p2,const float *p3,float *nearest);
|
||||
void fm_nearestPointInTriangle(const double *pos,const double *p1,const double *p2,const double *p3,double *nearest);
|
||||
|
||||
float fm_areaTriangle(const float *p1,const float *p2,const float *p3);
|
||||
double fm_areaTriangle(const double *p1,const double *p2,const double *p3);
|
||||
|
||||
void fm_subtract(const float *A,const float *B,float *diff); // compute A-B and store the result in 'diff'
|
||||
void fm_subtract(const double *A,const double *B,double *diff); // compute A-B and store the result in 'diff'
|
||||
|
||||
void fm_multiply(float *A,float scalar);
|
||||
void fm_multiply(double *A,double scalar);
|
||||
|
||||
void fm_add(const float *A,const float *B,float *sum);
|
||||
void fm_add(const double *A,const double *B,double *sum);
|
||||
|
||||
void fm_copy3(const float *source,float *dest);
|
||||
void fm_copy3(const double *source,double *dest);
|
||||
|
||||
// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices.
|
||||
// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size
|
||||
// is the input buffer. The routine returns the new vertex count after re-indexing.
|
||||
uint32_t fm_copyUniqueVertices(uint32_t vcount,const float *input_vertices,float *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
|
||||
uint32_t fm_copyUniqueVertices(uint32_t vcount,const double *input_vertices,double *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
|
||||
|
||||
bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
|
||||
bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const double *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
|
||||
|
||||
bool fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon=0.01f,float dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon
|
||||
bool fm_samePlane(const double p1[4],const double p2[4],double normalEpsilon=0.01,double dEpsilon=0.001,bool doubleSided=false);
|
||||
|
||||
void fm_OBBtoAABB(const float obmin[3],const float obmax[3],const float matrix[16],float abmin[3],float abmax[3]);
|
||||
|
||||
// a utility class that will tessellate a mesh.
|
||||
class fm_Tesselate
|
||||
{
|
||||
public:
|
||||
virtual const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) = 0;
|
||||
};
|
||||
|
||||
fm_Tesselate * fm_createTesselate(void);
|
||||
void fm_releaseTesselate(fm_Tesselate *t);
|
||||
|
||||
void fm_computeMeanNormals(uint32_t vcount, // the number of vertices
|
||||
const float *vertices, // the base address of the vertex position data.
|
||||
uint32_t vstride, // the stride between position data.
|
||||
float *normals, // the base address of the destination for mean vector normals
|
||||
uint32_t nstride, // the stride between normals
|
||||
uint32_t tcount, // the number of triangles
|
||||
const uint32_t *indices); // the triangle indices
|
||||
|
||||
void fm_computeMeanNormals(uint32_t vcount, // the number of vertices
|
||||
const double *vertices, // the base address of the vertex position data.
|
||||
uint32_t vstride, // the stride between position data.
|
||||
double *normals, // the base address of the destination for mean vector normals
|
||||
uint32_t nstride, // the stride between normals
|
||||
uint32_t tcount, // the number of triangles
|
||||
const uint32_t *indices); // the triangle indices
|
||||
|
||||
|
||||
bool fm_isValidTriangle(const float *p1,const float *p2,const float *p3,float epsilon=0.00001f);
|
||||
bool fm_isValidTriangle(const double *p1,const double *p2,const double *p3,double epsilon=0.00001f);
|
||||
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
#endif
|
||||
5280
Engine/lib/convexMath/FloatMath.inl
Normal file
5280
Engine/lib/convexMath/FloatMath.inl
Normal file
File diff suppressed because it is too large
Load diff
46
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
46
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
name: CI
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -14,6 +14,7 @@ jobs:
|
|||
name: "Win32-Release",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A Win32 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -24,6 +25,7 @@ jobs:
|
|||
name: "Win32-Debug",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A Win32 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -34,6 +36,7 @@ jobs:
|
|||
name: "Win64-Release",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -44,16 +47,42 @@ jobs:
|
|||
name: "Win64-Debug",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||
build_type: "Debug"
|
||||
}
|
||||
- {
|
||||
name: "Win64-UWP",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=WindowsStore \
|
||||
\"-DCMAKE_SYSTEM_VERSION=10.0\" \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "macOS-Release",
|
||||
os: macos-latest,
|
||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
|
||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_TESTS=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "iOS-Release",
|
||||
os: macos-latest,
|
||||
cmake_opts: "-GXcode \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_UTILS=OFF \
|
||||
-DALSOFT_EXAMPLES=OFF \
|
||||
-DALSOFT_TESTS=OFF \
|
||||
-DALSOFT_INSTALL=OFF \
|
||||
\"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
|
|
@ -65,7 +94,8 @@ jobs:
|
|||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_JACK=ON \
|
||||
-DALSOFT_REQUIRE_PIPEWIRE=ON",
|
||||
-DALSOFT_REQUIRE_PIPEWIRE=ON \
|
||||
-DALSOFT_TESTS=ON",
|
||||
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
|
||||
libpulse-dev \
|
||||
portaudio19-dev \
|
||||
|
|
@ -78,7 +108,7 @@ jobs:
|
|||
}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
|
|
@ -97,6 +127,12 @@ jobs:
|
|||
run: |
|
||||
cmake --build build --config ${{matrix.config.build_type}}
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd build
|
||||
ctest
|
||||
|
||||
- name: Create Archive
|
||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||
shell: bash
|
||||
|
|
@ -109,7 +145,7 @@ jobs:
|
|||
|
||||
- name: Upload Archive
|
||||
# Upload package as an artifact of this workflow.
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||
with:
|
||||
name: soft_oal-${{matrix.config.name}}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
|
||||
|
||||
- name: Upload makemhr artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: makemhr
|
||||
path: "Artifacts/"
|
||||
|
|
|
|||
1
Engine/lib/openal-soft/.gitignore
vendored
1
Engine/lib/openal-soft/.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
|||
build*/
|
||||
winbuild
|
||||
win64build
|
||||
.vs/
|
||||
|
||||
## kdevelop
|
||||
*.kdev4
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# CMake build file list for OpenAL
|
||||
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
enable_testing()
|
||||
|
||||
if(APPLE)
|
||||
# The workaround for try_compile failing with code signing
|
||||
|
|
@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
|||
FORCE)
|
||||
endif()
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(ALSOFT_UWP TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
if(COMMAND CMAKE_POLICY)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
cmake_policy(SET CMP0005 NEW)
|
||||
|
|
@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX)
|
|||
endif()
|
||||
|
||||
set(DEFAULT_TARGET_PROPS
|
||||
# Require C++14.
|
||||
CXX_STANDARD 14
|
||||
# Require C++17.
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED TRUE
|
||||
# Prefer C11, but support C99 and earlier when possible.
|
||||
C_STANDARD 11)
|
||||
|
|
@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs" ON)
|
|||
option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
|
||||
|
||||
option(ALSOFT_EXAMPLES "Build example programs" ON)
|
||||
option(ALSOFT_TESTS "Build test programs" OFF)
|
||||
|
||||
option(ALSOFT_INSTALL "Install main library" ON)
|
||||
option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
|
||||
|
|
@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++
|
|||
set(INC_PATHS )
|
||||
set(C_FLAGS )
|
||||
set(LINKER_FLAGS )
|
||||
set(LINKER_FLAGS_DEBUG )
|
||||
set(LINKER_FLAGS_RELEASE )
|
||||
set(EXTRA_LIBS )
|
||||
|
||||
if(WIN32)
|
||||
|
|
@ -165,7 +168,7 @@ elseif(APPLE)
|
|||
endif()
|
||||
|
||||
|
||||
# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
|
||||
# QNX's gcc do not uses /usr/include and /usr/lib paths by default
|
||||
if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
|
||||
set(INC_PATHS ${INC_PATHS} /usr/include)
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
|
||||
|
|
@ -186,29 +189,18 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
|
|||
set(EXPORT_DECL "")
|
||||
|
||||
|
||||
if(NOT WIN32)
|
||||
# Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
|
||||
if(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
|
||||
set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
|
||||
if(NOT HAVE_POSIX_MEMALIGN_POSIX)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
|
||||
else()
|
||||
set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600)
|
||||
endif()
|
||||
endif()
|
||||
unset(OLD_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
# C99 has restrict, but C++ does not, so we can only utilize __restrict.
|
||||
check_cxx_source_compiles("int *__restrict foo;
|
||||
int main() { return 0; }" HAVE___RESTRICT)
|
||||
if(HAVE___RESTRICT)
|
||||
set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict)
|
||||
else()
|
||||
set(CPP_DEFS ${CPP_DEFS} "RESTRICT=")
|
||||
# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
|
||||
# to get the fixed-width integer type formatter macros.
|
||||
check_cxx_source_compiles("#include <cinttypes>
|
||||
#include <cstdio>
|
||||
int main()
|
||||
{
|
||||
int64_t i64{};
|
||||
std::printf(\"%\" PRId64, i64);
|
||||
}"
|
||||
HAVE_STDC_FORMAT_MACROS)
|
||||
if(NOT HAVE_STDC_FORMAT_MACROS)
|
||||
set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
|
||||
endif()
|
||||
|
||||
# Some systems may need libatomic for atomic functions to work
|
||||
|
|
@ -278,6 +270,11 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
|
||||
if(HAVE_WNO_INTERFERENCE_SIZE)
|
||||
set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
|
||||
endif()
|
||||
|
||||
if(ALSOFT_WERROR)
|
||||
set(C_FLAGS ${C_FLAGS} -Werror)
|
||||
endif()
|
||||
|
|
@ -312,7 +309,7 @@ else()
|
|||
option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
|
||||
if(ALSOFT_STATIC_STDCXX)
|
||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++")
|
||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||
unset(OLD_REQUIRED_LIBRARIES)
|
||||
|
|
@ -320,14 +317,14 @@ else()
|
|||
if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||
message(FATAL_ERROR "Cannot static link libstdc++")
|
||||
endif()
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
|
||||
if(ALSOFT_STATIC_WINPTHREAD)
|
||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||
unset(OLD_REQUIRED_LIBRARIES)
|
||||
|
|
@ -335,38 +332,38 @@ else()
|
|||
if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||
message(FATAL_ERROR "Cannot static link libwinpthread")
|
||||
endif()
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set visibility/export options if available
|
||||
if(WIN32)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
if(WIN32)
|
||||
set(EXPORT_DECL "__declspec(dllexport)")
|
||||
endif()
|
||||
else()
|
||||
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
# Yes GCC, really don't accept visibility modes you don't support
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
||||
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
||||
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
||||
endif()
|
||||
else()
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
||||
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
# Yes GCC, really don't accept visibility modes you don't support
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
||||
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
||||
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
||||
else()
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
||||
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
|
@ -403,7 +400,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H)
|
|||
set(HAVE_SSE 1)
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
||||
message(FATAL_ERROR "Failed to enable required SSE CPU extensions")
|
||||
endif()
|
||||
|
||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||
|
|
@ -448,10 +445,11 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
|
||||
message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions")
|
||||
message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions")
|
||||
endif()
|
||||
|
||||
|
||||
set(ALSOFT_FORCE_ALIGN )
|
||||
set(SSE_FLAGS )
|
||||
set(FPMATH_SET "0")
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
||||
|
|
@ -476,6 +474,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
|||
# OSs don't guarantee this on 32-bit, so externally-callable
|
||||
# functions need to ensure an aligned stack.
|
||||
set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
|
||||
set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -493,13 +492,9 @@ if(HAVE_SSE2)
|
|||
endif()
|
||||
|
||||
|
||||
check_include_file(malloc.h HAVE_MALLOC_H)
|
||||
check_include_file(cpuid.h HAVE_CPUID_H)
|
||||
check_include_file(intrin.h HAVE_INTRIN_H)
|
||||
check_include_file(guiddef.h HAVE_GUIDDEF_H)
|
||||
if(NOT HAVE_GUIDDEF_H)
|
||||
check_include_file(initguid.h HAVE_INITGUID_H)
|
||||
endif()
|
||||
|
||||
# Some systems need libm for some math functions to work
|
||||
set(MATH_LIB )
|
||||
|
|
@ -517,7 +512,7 @@ if(HAVE_LIBRT)
|
|||
set(RT_LIB rt)
|
||||
endif()
|
||||
|
||||
# Check for the dlopen API (for dynamicly loading backend libs)
|
||||
# Check for the dlopen API (for dynamically loading backend libs)
|
||||
if(ALSOFT_DLOPEN)
|
||||
check_include_file(dlfcn.h HAVE_DLFCN_H)
|
||||
check_library_exists(dl dlopen "" HAVE_LIBDL)
|
||||
|
|
@ -546,8 +541,6 @@ if(HAVE_INTRIN_H)
|
|||
}" HAVE_CPUID_INTRINSIC)
|
||||
endif()
|
||||
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN)
|
||||
check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC)
|
||||
check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH)
|
||||
|
||||
if(NOT WIN32)
|
||||
|
|
@ -582,34 +575,36 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
check_symbol_exists(getopt unistd.h HAVE_GETOPT)
|
||||
|
||||
|
||||
# Common sources used by both the OpenAL implementation library, the OpenAL
|
||||
# router, and certain tools and examples.
|
||||
set(COMMON_OBJS
|
||||
common/alassert.cpp
|
||||
common/alassert.h
|
||||
common/albit.h
|
||||
common/albyte.h
|
||||
common/alcomplex.cpp
|
||||
common/alcomplex.h
|
||||
common/aldeque.h
|
||||
common/alfstream.cpp
|
||||
common/alfstream.h
|
||||
common/almalloc.cpp
|
||||
common/almalloc.h
|
||||
common/alnumbers.h
|
||||
common/alnumeric.h
|
||||
common/aloptional.h
|
||||
common/alsem.cpp
|
||||
common/alsem.h
|
||||
common/alspan.h
|
||||
common/alstring.cpp
|
||||
common/alstring.h
|
||||
common/althrd_setname.cpp
|
||||
common/althrd_setname.h
|
||||
common/althreads.h
|
||||
common/altraits.h
|
||||
common/atomic.h
|
||||
common/comptr.h
|
||||
common/dynload.cpp
|
||||
common/dynload.h
|
||||
common/flexarray.h
|
||||
common/intrusive_ptr.h
|
||||
common/opthelpers.h
|
||||
common/pffft.cpp
|
||||
common/pffft.h
|
||||
common/phase_shifter.h
|
||||
common/polyphase_resampler.cpp
|
||||
common/polyphase_resampler.h
|
||||
|
|
@ -618,8 +613,6 @@ set(COMMON_OBJS
|
|||
common/ringbuffer.h
|
||||
common/strutils.cpp
|
||||
common/strutils.h
|
||||
common/threads.cpp
|
||||
common/threads.h
|
||||
common/vecmat.h
|
||||
common/vector.h)
|
||||
|
||||
|
|
@ -680,6 +673,8 @@ set(CORE_OBJS
|
|||
core/mixer.cpp
|
||||
core/mixer.h
|
||||
core/resampler_limits.h
|
||||
core/storage_formats.cpp
|
||||
core/storage_formats.h
|
||||
core/uhjfilter.cpp
|
||||
core/uhjfilter.h
|
||||
core/uiddefs.cpp
|
||||
|
|
@ -726,7 +721,7 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
||||
message(FATAL_ERROR "Failed to enabled required RTKit support")
|
||||
message(FATAL_ERROR "Failed to enable required RTKit support")
|
||||
endif()
|
||||
|
||||
# Default mixers, always available
|
||||
|
|
@ -742,6 +737,9 @@ set(OPENAL_OBJS
|
|||
al/auxeffectslot.h
|
||||
al/buffer.cpp
|
||||
al/buffer.h
|
||||
al/debug.cpp
|
||||
al/debug.h
|
||||
al/direct_defs.h
|
||||
al/effect.cpp
|
||||
al/effect.h
|
||||
al/effects/autowah.cpp
|
||||
|
|
@ -761,6 +759,7 @@ set(OPENAL_OBJS
|
|||
al/effects/reverb.cpp
|
||||
al/effects/vmorpher.cpp
|
||||
al/error.cpp
|
||||
al/error.h
|
||||
al/event.cpp
|
||||
al/event.h
|
||||
al/extension.cpp
|
||||
|
|
@ -798,6 +797,9 @@ set(ALC_OBJS
|
|||
alc/effects/pshifter.cpp
|
||||
alc/effects/reverb.cpp
|
||||
alc/effects/vmorpher.cpp
|
||||
alc/events.cpp
|
||||
alc/events.h
|
||||
alc/export_list.h
|
||||
alc/inprogext.h
|
||||
alc/panning.cpp)
|
||||
|
||||
|
|
@ -815,7 +817,6 @@ if(ALSOFT_EAX)
|
|||
al/eax/fx_slot_index.h
|
||||
al/eax/fx_slots.cpp
|
||||
al/eax/fx_slots.h
|
||||
al/eax/globals.cpp
|
||||
al/eax/globals.h
|
||||
al/eax/utils.cpp
|
||||
al/eax/utils.h
|
||||
|
|
@ -899,7 +900,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
|
||||
message(FATAL_ERROR "Failed to enabled required PipeWire backend")
|
||||
message(FATAL_ERROR "Failed to enable required PipeWire backend")
|
||||
endif()
|
||||
|
||||
# Check PulseAudio backend
|
||||
|
|
@ -916,7 +917,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required PulseAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required PulseAudio backend")
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
|
|
@ -963,66 +964,72 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Check SndIO backend
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
||||
# Check SndIO backend (disabled by default on non-BSDs)
|
||||
if(BSD)
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
||||
else()
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF)
|
||||
endif()
|
||||
option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
|
||||
if(ALSOFT_BACKEND_SNDIO)
|
||||
find_package(SoundIO)
|
||||
if(SOUNDIO_FOUND)
|
||||
find_package(SndIO)
|
||||
if(SNDIO_FOUND)
|
||||
set(HAVE_SNDIO 1)
|
||||
set(BACKENDS "${BACKENDS} SndIO (linked),")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h)
|
||||
set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS})
|
||||
set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
|
||||
message(FATAL_ERROR "Failed to enabled required ALSA backend")
|
||||
message(FATAL_ERROR "Failed to enable required ALSA backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
|
||||
message(FATAL_ERROR "Failed to enabled required OSS backend")
|
||||
message(FATAL_ERROR "Failed to enable required OSS backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
|
||||
message(FATAL_ERROR "Failed to enabled required Solaris backend")
|
||||
message(FATAL_ERROR "Failed to enable required Solaris backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required SndIO backend")
|
||||
message(FATAL_ERROR "Failed to enable required SndIO backend")
|
||||
endif()
|
||||
|
||||
# Check Windows-only backends
|
||||
if(WIN32)
|
||||
# Check MMSystem backend
|
||||
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
||||
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
||||
if(ALSOFT_BACKEND_WINMM)
|
||||
set(HAVE_WINMM 1)
|
||||
set(BACKENDS "${BACKENDS} WinMM,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
||||
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
||||
# find_library doesn't find it without being told to look in a specific
|
||||
# place in the WindowsSDK, but it links anyway. If there ends up being
|
||||
# Windows targets without this, another means to detect it is needed.
|
||||
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
||||
endif()
|
||||
|
||||
# Check DSound backend
|
||||
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
||||
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
||||
if(ALSOFT_BACKEND_DSOUND)
|
||||
check_include_file(dsound.h HAVE_DSOUND_H)
|
||||
if(DXSDK_DIR)
|
||||
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
||||
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
||||
DOC "The DirectSound include directory")
|
||||
if (NOT ALSOFT_UWP)
|
||||
# Check MMSystem backend
|
||||
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
||||
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
||||
if(ALSOFT_BACKEND_WINMM)
|
||||
set(HAVE_WINMM 1)
|
||||
set(BACKENDS "${BACKENDS} WinMM,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
||||
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
||||
# find_library doesn't find it without being told to look in a specific
|
||||
# place in the WindowsSDK, but it links anyway. If there ends up being
|
||||
# Windows targets without this, another means to detect it is needed.
|
||||
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
||||
endif()
|
||||
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
||||
set(HAVE_DSOUND 1)
|
||||
set(BACKENDS "${BACKENDS} DirectSound,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
||||
|
||||
if(NOT HAVE_DSOUND_H)
|
||||
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
||||
# Check DSound backend
|
||||
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
||||
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
||||
if(ALSOFT_BACKEND_DSOUND)
|
||||
check_include_file(dsound.h HAVE_DSOUND_H)
|
||||
if(DXSDK_DIR)
|
||||
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
||||
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
||||
DOC "The DirectSound include directory")
|
||||
endif()
|
||||
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
||||
set(HAVE_DSOUND 1)
|
||||
set(BACKENDS "${BACKENDS} DirectSound,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
||||
|
||||
if(NOT HAVE_DSOUND_H)
|
||||
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1040,13 +1047,13 @@ if(WIN32)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
|
||||
message(FATAL_ERROR "Failed to enabled required WinMM backend")
|
||||
message(FATAL_ERROR "Failed to enable required WinMM backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
|
||||
message(FATAL_ERROR "Failed to enabled required DSound backend")
|
||||
message(FATAL_ERROR "Failed to enable required DSound backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
|
||||
message(FATAL_ERROR "Failed to enabled required WASAPI backend")
|
||||
message(FATAL_ERROR "Failed to enable required WASAPI backend")
|
||||
endif()
|
||||
|
||||
# Check JACK backend
|
||||
|
|
@ -1063,7 +1070,7 @@ if(ALSOFT_BACKEND_JACK)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
|
||||
message(FATAL_ERROR "Failed to enabled required JACK backend")
|
||||
message(FATAL_ERROR "Failed to enable required JACK backend")
|
||||
endif()
|
||||
|
||||
# Check CoreAudio backend
|
||||
|
|
@ -1098,7 +1105,7 @@ if(ALSOFT_BACKEND_COREAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required CoreAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required CoreAudio backend")
|
||||
endif()
|
||||
|
||||
# Check for Oboe (Android) backend
|
||||
|
|
@ -1130,7 +1137,7 @@ if(ALSOFT_BACKEND_OBOE)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
|
||||
message(FATAL_ERROR "Failed to enabled required Oboe backend")
|
||||
message(FATAL_ERROR "Failed to enable required Oboe backend")
|
||||
endif()
|
||||
|
||||
# Check for OpenSL (Android) backend
|
||||
|
|
@ -1142,11 +1149,12 @@ if(ALSOFT_BACKEND_OPENSL)
|
|||
set(HAVE_OPENSL 1)
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
|
||||
set(BACKENDS "${BACKENDS} OpenSL,")
|
||||
set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS})
|
||||
set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
|
||||
message(FATAL_ERROR "Failed to enabled required OpenSL backend")
|
||||
message(FATAL_ERROR "Failed to enable required OpenSL backend")
|
||||
endif()
|
||||
|
||||
# Check PortAudio backend
|
||||
|
|
@ -1163,7 +1171,7 @@ if(ALSOFT_BACKEND_PORTAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required PortAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required PortAudio backend")
|
||||
endif()
|
||||
|
||||
# Check for SDL2 backend
|
||||
|
|
@ -1181,7 +1189,7 @@ if(ALSOFT_BACKEND_SDL2)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
|
||||
message(FATAL_ERROR "Failed to enabled required SDL2 backend")
|
||||
message(FATAL_ERROR "Failed to enable required SDL2 backend")
|
||||
endif()
|
||||
|
||||
# Optionally enable the Wave Writer backend
|
||||
|
|
@ -1309,11 +1317,12 @@ configure_file(
|
|||
@ONLY)
|
||||
|
||||
|
||||
add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||
target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include)
|
||||
target_compile_definitions(common PRIVATE ${CPP_DEFS})
|
||||
target_compile_options(common PRIVATE ${C_FLAGS})
|
||||
set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
||||
add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||
target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
|
||||
PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
|
||||
target_compile_options(alcommon PRIVATE ${C_FLAGS})
|
||||
set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
||||
|
||||
|
||||
unset(HAS_ROUTER)
|
||||
|
|
@ -1348,7 +1357,7 @@ else()
|
|||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
|
||||
"AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||
target_compile_options(OpenAL PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
||||
target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
|
||||
target_include_directories(OpenAL
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
||||
|
|
@ -1379,13 +1388,31 @@ else()
|
|||
if(WIN32)
|
||||
set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
|
||||
endif()
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
|
||||
if(ALSOFT_UWP)
|
||||
set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
|
||||
|
||||
find_program(NUGET_EXE NAMES nuget)
|
||||
if(NOT NUGET_EXE)
|
||||
message("NUGET.EXE not found.")
|
||||
message(FATAL_ERROR "Please install this executable, and run CMake again.")
|
||||
endif()
|
||||
|
||||
exec_program(${NUGET_EXE}
|
||||
ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
|
||||
|
||||
set_target_properties(${IMPL_TARGET} PROPERTIES
|
||||
VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
|
||||
)
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
# FIXME: This doesn't put a dependency on the version script. Changing
|
||||
# the version script will not cause a relink as it should.
|
||||
set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
||||
target_link_options(${IMPL_TARGET} PRIVATE
|
||||
"-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
||||
endif()
|
||||
|
||||
if(APPLE AND ALSOFT_OSX_FRAMEWORK)
|
||||
|
|
@ -1443,8 +1470,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS}
|
|||
SOVERSION ${LIB_MAJOR_VERSION}
|
||||
)
|
||||
target_compile_definitions(${IMPL_TARGET}
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
||||
${CPP_DEFS})
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
|
||||
"ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||
|
||||
if(TARGET build_version)
|
||||
|
|
@ -1462,8 +1489,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC"
|
|||
message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
||||
endif()
|
||||
else()
|
||||
set_property(TARGET OpenAL APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,--output-def,OpenAL32.def")
|
||||
target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
|
||||
add_custom_command(TARGET OpenAL POST_BUILD
|
||||
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
||||
COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
||||
|
|
@ -1494,7 +1520,10 @@ if(FPMATH_SET)
|
|||
message(STATUS "Building with SSE${FPMATH_SET} codegen")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(ALSOFT_UWP)
|
||||
message(STATUS "Building with UWP support")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
if(ALSOFT_EAX)
|
||||
message(STATUS "Building with legacy EAX extension support")
|
||||
message(STATUS "")
|
||||
|
|
@ -1580,7 +1609,7 @@ if(ALSOFT_UTILS)
|
|||
target_include_directories(uhjdecoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjdecoder PUBLIC common
|
||||
target_link_libraries(uhjdecoder PUBLIC alcommon
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
|
|
@ -1589,7 +1618,7 @@ if(ALSOFT_UTILS)
|
|||
target_include_directories(uhjencoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjencoder PUBLIC common
|
||||
target_link_libraries(uhjencoder PUBLIC alcommon
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
endif()
|
||||
|
|
@ -1602,7 +1631,7 @@ if(ALSOFT_UTILS)
|
|||
target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(sofa-support PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
||||
target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
||||
set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
set(MAKEMHR_SRCS
|
||||
|
|
@ -1612,9 +1641,6 @@ if(ALSOFT_UTILS)
|
|||
utils/makemhr/loadsofa.h
|
||||
utils/makemhr/makemhr.cpp
|
||||
utils/makemhr/makemhr.h)
|
||||
if(NOT HAVE_GETOPT)
|
||||
set(MAKEMHR_SRCS ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h)
|
||||
endif()
|
||||
add_executable(makemhr ${MAKEMHR_SRCS})
|
||||
target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(makemhr
|
||||
|
|
@ -1644,22 +1670,22 @@ endif()
|
|||
|
||||
|
||||
# Add a static library with common functions used by multiple example targets
|
||||
add_library(ex-common STATIC EXCLUDE_FROM_ALL
|
||||
add_library(al-excommon STATIC EXCLUDE_FROM_ALL
|
||||
examples/common/alhelpers.c
|
||||
examples/common/alhelpers.h)
|
||||
target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
|
||||
target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(ex-common PUBLIC ${C_FLAGS})
|
||||
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||
set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
|
||||
target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(al-excommon PUBLIC ${C_FLAGS})
|
||||
target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||
set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_EXAMPLES)
|
||||
add_executable(altonegen examples/altonegen.c)
|
||||
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG})
|
||||
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alrecord examples/alrecord.c)
|
||||
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG})
|
||||
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1670,48 +1696,53 @@ if(ALSOFT_EXAMPLES)
|
|||
|
||||
if(SNDFILE_FOUND)
|
||||
add_executable(alplay examples/alplay.c)
|
||||
target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alstream examples/alstream.c)
|
||||
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alreverb examples/alreverb.c)
|
||||
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(almultireverb examples/almultireverb.c)
|
||||
target_link_libraries(almultireverb
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||
set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(allatency examples/allatency.c)
|
||||
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alhrtf examples/alhrtf.c)
|
||||
target_link_libraries(alhrtf
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||
set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alstreamcb examples/alstreamcb.cpp)
|
||||
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alconvolve examples/alconvolve.c)
|
||||
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common
|
||||
add_executable(aldirect examples/aldirect.cpp)
|
||||
target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alconvolve examples/alconvolve.c)
|
||||
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile
|
||||
al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
|
||||
alhrtf)
|
||||
alhrtf aldirect)
|
||||
endif()
|
||||
|
||||
message(STATUS "Building SndFile example programs")
|
||||
|
|
@ -1720,7 +1751,7 @@ if(ALSOFT_EXAMPLES)
|
|||
if(SDL2_FOUND)
|
||||
add_executable(alloopback examples/alloopback.c)
|
||||
target_link_libraries(alloopback
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB})
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
|
||||
set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1757,7 +1788,7 @@ if(ALSOFT_EXAMPLES)
|
|||
add_executable(alffplay examples/alffplay.cpp)
|
||||
target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
|
||||
target_link_libraries(alffplay
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common)
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
|
||||
set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1769,6 +1800,10 @@ if(ALSOFT_EXAMPLES)
|
|||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if (ALSOFT_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(EXTRA_INSTALLS)
|
||||
install(TARGETS ${EXTRA_INSTALLS}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
|
|
|
|||
38
Engine/lib/openal-soft/LICENSE-pffft
Normal file
38
Engine/lib/openal-soft/LICENSE-pffft
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
A modified PFFFT is included, with the following license.
|
||||
|
||||
Copyright (c) 2023 Christopher Robinson
|
||||
|
||||
Copyright (c) 2013 Julien Pommier ( pommier@modartt.com )
|
||||
|
||||
Copyright (c) 2004 the University Corporation for Atmospheric
|
||||
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
||||
Computational and Information Systems Laboratory, UCAR,
|
||||
www.cisl.ucar.edu.
|
||||
|
||||
Redistribution and use of the Software in source and binary forms,
|
||||
with or without modification, is permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
- Neither the names of NCAR's Computational and Information Systems
|
||||
Laboratory, the University Corporation for Atmospheric Research,
|
||||
nor the names of its sponsors or contributors may be used to
|
||||
endorse or promote products derived from this Software without
|
||||
specific prior written permission.
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notices, this list of conditions, and the disclaimer below.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions, and the disclaimer below in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
|
|||
available settings.
|
||||
|
||||
|
||||
Language Bindings
|
||||
-----------------
|
||||
|
||||
As a C API, OpenAL Soft can be used directly by any language that can use
|
||||
functions with C linkage. For languages that can't directly use C-style
|
||||
headers, bindings may be developed to allow code written in that language to
|
||||
call into the library. Some bindings for some languages are listed here.
|
||||
|
||||
C# Bindings:
|
||||
* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
|
||||
API, including some extensions. It also includes utility libraries for math and
|
||||
linear algebra, which can be useful for 3D calculations.
|
||||
|
||||
Java Bindings:
|
||||
* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
|
||||
Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
|
||||
higher level Sound3D Toolkit API and utility functions to make easier use of
|
||||
OpenAL features and capabilities.
|
||||
|
||||
Python Bindings:
|
||||
* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
|
||||
wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
|
||||
|
||||
Other bindings for these and other languages also exist. This list will grow as
|
||||
more bindings are found.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,23 +1,24 @@
|
|||
#ifndef AL_AUXEFFECTSLOT_H
|
||||
#define AL_AUXEFFECTSLOT_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/effectslot.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <memory>
|
||||
#include "eax/api.h"
|
||||
#include "eax/call.h"
|
||||
#include "eax/effect.h"
|
||||
#include "eax/exception.h"
|
||||
|
|
@ -26,8 +27,6 @@
|
|||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffect;
|
||||
struct WetBuffer;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
class EaxFxSlotException : public EaxException {
|
||||
|
|
@ -38,30 +37,30 @@ public:
|
|||
};
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
enum class SlotState : ALenum {
|
||||
Initial = AL_INITIAL,
|
||||
Playing = AL_PLAYING,
|
||||
Stopped = AL_STOPPED,
|
||||
enum class SlotState : bool {
|
||||
Initial, Playing,
|
||||
};
|
||||
|
||||
struct ALeffectslot {
|
||||
ALuint EffectId{};
|
||||
float Gain{1.0f};
|
||||
bool AuxSendAuto{true};
|
||||
ALeffectslot *Target{nullptr};
|
||||
ALbuffer *Buffer{nullptr};
|
||||
|
||||
struct {
|
||||
struct EffectData {
|
||||
EffectSlotType Type{EffectSlotType::None};
|
||||
EffectProps Props{};
|
||||
|
||||
al::intrusive_ptr<EffectState> State;
|
||||
} Effect;
|
||||
};
|
||||
EffectData Effect;
|
||||
|
||||
bool mPropsDirty{true};
|
||||
|
||||
SlotState mState{SlotState::Initial};
|
||||
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
EffectSlot *mSlot{nullptr};
|
||||
|
||||
|
|
@ -73,23 +72,23 @@ struct ALeffectslot {
|
|||
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
||||
~ALeffectslot();
|
||||
|
||||
ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
|
||||
void updateProps(ALCcontext *context);
|
||||
ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
|
||||
ALCcontext *context);
|
||||
void updateProps(ALCcontext *context) const;
|
||||
|
||||
/* This can be new'd for the context's default effect slot. */
|
||||
DEF_NEWDEL(ALeffectslot)
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
|
||||
|
||||
EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; }
|
||||
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept
|
||||
[[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; }
|
||||
[[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
|
||||
{ return eax_; }
|
||||
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_dispatch(const EaxCall& call)
|
||||
[[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
|
||||
{ return call.is_get() ? eax_get(call) : eax_set(call); }
|
||||
|
||||
void eax_commit();
|
||||
|
|
@ -193,6 +192,17 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct Eax5FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX50FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5OcclusionValidator {
|
||||
void operator()(long lOcclusion) const
|
||||
{
|
||||
|
|
@ -215,21 +225,13 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct Eax5FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX50FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5AllValidator {
|
||||
void operator()(const EAX50FXSLOTPROPERTIES& all) const
|
||||
{
|
||||
Eax4AllValidator{}(static_cast<const EAX40FXSLOTPROPERTIES&>(all));
|
||||
Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
|
||||
Eax4VolumeValidator{}(all.lVolume);
|
||||
Eax4LockValidator{}(all.lLock);
|
||||
Eax5FlagsValidator{}(all.ulFlags);
|
||||
Eax5OcclusionValidator{}(all.lOcclusion);
|
||||
Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
|
||||
}
|
||||
|
|
@ -277,14 +279,14 @@ private:
|
|||
dst = src;
|
||||
}
|
||||
|
||||
constexpr bool eax4_fx_slot_is_legacy() const noexcept
|
||||
[[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
|
||||
{ return eax_fx_slot_index_ < 2; }
|
||||
|
||||
void eax4_fx_slot_ensure_unlocked() const;
|
||||
|
||||
static ALenum eax_get_efx_effect_type(const GUID& guid);
|
||||
const GUID& eax_get_eax_default_effect_guid() const noexcept;
|
||||
long eax_get_eax_default_lock() const noexcept;
|
||||
[[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum;
|
||||
[[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
|
||||
[[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long;
|
||||
|
||||
void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept;
|
||||
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
||||
|
|
@ -293,8 +295,8 @@ private:
|
|||
void eax_fx_slot_set_current_defaults();
|
||||
void eax_fx_slot_set_defaults();
|
||||
|
||||
void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const;
|
||||
void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const;
|
||||
static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
|
||||
static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props);
|
||||
void eax_fx_slot_get(const EaxCall& call) const;
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_get(const EaxCall& call);
|
||||
|
|
@ -307,7 +309,7 @@ private:
|
|||
void eax4_fx_slot_set_all(const EaxCall& call);
|
||||
void eax5_fx_slot_set_all(const EaxCall& call);
|
||||
|
||||
bool eax_fx_slot_should_update_sources() const noexcept;
|
||||
[[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool;
|
||||
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax4_fx_slot_set(const EaxCall& call);
|
||||
|
|
@ -365,4 +367,20 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
|
|||
void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct EffectSlotSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr};
|
||||
|
||||
EffectSlotSubList() noexcept = default;
|
||||
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||
~EffectSlotSubList();
|
||||
|
||||
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,23 @@
|
|||
#ifndef AL_BUFFER_H
|
||||
#define AL_BUFFER_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/buffer_storage.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax/x_ram.h"
|
||||
|
||||
enum class EaxStorage : uint8_t {
|
||||
Automatic,
|
||||
Accessible,
|
||||
|
|
@ -26,7 +29,7 @@ enum class EaxStorage : uint8_t {
|
|||
struct ALbuffer : public BufferStorage {
|
||||
ALbitfieldSOFT Access{0u};
|
||||
|
||||
al::vector<al::byte,16> mDataStorage;
|
||||
al::vector<std::byte,16> mDataStorage;
|
||||
|
||||
ALuint OriginalSize{0};
|
||||
|
||||
|
|
@ -42,12 +45,14 @@ struct ALbuffer : public BufferStorage {
|
|||
ALuint mLoopEnd{0u};
|
||||
|
||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
|
||||
|
|
@ -55,4 +60,19 @@ struct ALbuffer : public BufferStorage {
|
|||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
struct BufferSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr};
|
||||
|
||||
BufferSubList() noexcept = default;
|
||||
BufferSubList(const BufferSubList&) = delete;
|
||||
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||
~BufferSubList();
|
||||
|
||||
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
618
Engine/lib/openal-soft/al/debug.cpp
Normal file
618
Engine/lib/openal-soft/al/debug.cpp
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "auxeffectslot.h"
|
||||
#include "buffer.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "direct_defs.h"
|
||||
#include "effect.h"
|
||||
#include "error.h"
|
||||
#include "filter.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "source.h"
|
||||
|
||||
|
||||
/* Declared here to prevent compilers from thinking it should be inlined, which
|
||||
* GCC warns about increasing code size.
|
||||
*/
|
||||
DebugGroup::~DebugGroup() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
|
||||
|
||||
template<typename T, T ...Vals>
|
||||
constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
|
||||
{ return std::array<T,sizeof...(Vals)>{Vals...}; }
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr auto make_array_sequence()
|
||||
{ return make_array_sequence(std::make_integer_sequence<T,N>{}); }
|
||||
|
||||
|
||||
constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
|
||||
case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
|
||||
case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
|
||||
case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
|
||||
case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
|
||||
case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
|
||||
case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
|
||||
case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
|
||||
case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
|
||||
case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
|
||||
case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
|
||||
case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
|
||||
case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
|
||||
case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
|
||||
case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
|
||||
case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
|
||||
case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
|
||||
case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
|
||||
case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
|
||||
case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
|
||||
}
|
||||
|
||||
constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
|
||||
case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
|
||||
case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
|
||||
case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
|
||||
case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
|
||||
case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
|
||||
case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
|
||||
case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
|
||||
case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
|
||||
case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
|
||||
case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
|
||||
case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
|
||||
}
|
||||
throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
|
||||
}
|
||||
|
||||
|
||||
constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case DebugSource::API: return "API";
|
||||
case DebugSource::System: return "Audio System";
|
||||
case DebugSource::ThirdParty: return "Third Party";
|
||||
case DebugSource::Application: return "Application";
|
||||
case DebugSource::Other: return "Other";
|
||||
}
|
||||
return "<invalid source>";
|
||||
}
|
||||
|
||||
constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case DebugType::Error: return "Error";
|
||||
case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
|
||||
case DebugType::UndefinedBehavior: return "Undefined Behavior";
|
||||
case DebugType::Portability: return "Portability";
|
||||
case DebugType::Performance: return "Performance";
|
||||
case DebugType::Marker: return "Marker";
|
||||
case DebugType::PushGroup: return "Push Group";
|
||||
case DebugType::PopGroup: return "Pop Group";
|
||||
case DebugType::Other: return "Other";
|
||||
}
|
||||
return "<invalid type>";
|
||||
}
|
||||
|
||||
constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
|
||||
{
|
||||
switch(severity)
|
||||
{
|
||||
case DebugSeverity::High: return "High";
|
||||
case DebugSeverity::Medium: return "Medium";
|
||||
case DebugSeverity::Low: return "Low";
|
||||
case DebugSeverity::Notification: return "Notification";
|
||||
}
|
||||
return "<invalid severity>";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
|
||||
DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
|
||||
{
|
||||
if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
|
||||
return;
|
||||
|
||||
if(message.length() >= MaxDebugMessageLength) UNLIKELY
|
||||
{
|
||||
ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
|
||||
MaxDebugMessageLength, al::sizei(message), message.data());
|
||||
return;
|
||||
}
|
||||
|
||||
DebugGroup &debug = mDebugGroups.back();
|
||||
|
||||
const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
|
||||
| (1_u64 << (DebugTypeBase+al::to_underlying(type)))
|
||||
| (uint64_t{id} << 32)};
|
||||
auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
|
||||
if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
|
||||
return;
|
||||
|
||||
const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
|
||||
| (1u << (DebugTypeBase+al::to_underlying(type)))
|
||||
| (1u << (DebugSeverityBase+al::to_underlying(severity)))};
|
||||
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||
if(iter != debug.mFilters.cend() && *iter == filter)
|
||||
return;
|
||||
|
||||
if(mDebugCb)
|
||||
{
|
||||
auto callback = mDebugCb;
|
||||
auto param = mDebugParam;
|
||||
debuglock.unlock();
|
||||
callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
|
||||
GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
|
||||
param);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mDebugLog.size() < MaxDebugLoggedMessages)
|
||||
mDebugLog.emplace_back(source, type, id, severity, message);
|
||||
else UNLIKELY
|
||||
ERR("Debug message log overflow. Lost message:\n"
|
||||
" Source: %s\n"
|
||||
" Type: %s\n"
|
||||
" ID: %u\n"
|
||||
" Severity: %s\n"
|
||||
" Message: \"%.*s\"\n",
|
||||
GetDebugSourceName(source), GetDebugTypeName(type), id,
|
||||
GetDebugSeverityName(severity), al::sizei(message), message.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
|
||||
ALDEBUGPROCEXT callback, void *userParam) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
context->mDebugCb = callback;
|
||||
context->mDebugParam = userParam;
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
|
||||
try {
|
||||
if(!context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
return;
|
||||
|
||||
if(!message)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
|
||||
|
||||
auto msgview = (length < 0) ? std::string_view{message}
|
||||
: std::string_view{message, static_cast<uint>(length)};
|
||||
if(msgview.size() >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
|
||||
msgview.size(), MaxDebugMessageLength};
|
||||
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||
|
||||
auto dtype = GetDebugType(type);
|
||||
if(!dtype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||
|
||||
auto dseverity = GetDebugSeverity(severity);
|
||||
if(!dseverity)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||
|
||||
context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
|
||||
FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
|
||||
try {
|
||||
if(count > 0)
|
||||
{
|
||||
if(!ids)
|
||||
throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
|
||||
if(source == AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug source cannot be AL_DONT_CARE_EXT with IDs"};
|
||||
if(type == AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug type cannot be AL_DONT_CARE_EXT with IDs"};
|
||||
if(severity != AL_DONT_CARE_EXT)
|
||||
throw al::context_error{AL_INVALID_OPERATION,
|
||||
"Debug severity must be AL_DONT_CARE_EXT with IDs"};
|
||||
}
|
||||
|
||||
if(enable != AL_TRUE && enable != AL_FALSE)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
|
||||
|
||||
static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
|
||||
static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
|
||||
|
||||
auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
|
||||
if(source != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
|
||||
}
|
||||
|
||||
auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
|
||||
if(type != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dtype = GetDebugType(type);
|
||||
if(!dtype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||
typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
|
||||
}
|
||||
|
||||
auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
|
||||
if(severity != AL_DONT_CARE_EXT)
|
||||
{
|
||||
auto dseverity = GetDebugSeverity(severity);
|
||||
if(!dseverity)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||
svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
DebugGroup &debug = context->mDebugGroups.back();
|
||||
if(count > 0)
|
||||
{
|
||||
const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
|
||||
|
||||
for(const uint id : al::span{ids, static_cast<uint>(count)})
|
||||
{
|
||||
const uint64_t filter{filterbase | (uint64_t{id} << 32)};
|
||||
|
||||
auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
|
||||
filter);
|
||||
if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
|
||||
debug.mIdFilters.insert(iter, filter);
|
||||
else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
|
||||
debug.mIdFilters.erase(iter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto apply_filter = [enable,&debug](const uint filter)
|
||||
{
|
||||
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||
if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
|
||||
debug.mFilters.insert(iter, filter);
|
||||
else if(enable && iter != debug.mFilters.cend() && *iter == filter)
|
||||
debug.mFilters.erase(iter);
|
||||
};
|
||||
auto apply_severity = [apply_filter,svrIndices](const uint filter)
|
||||
{
|
||||
std::for_each(svrIndices.cbegin(), svrIndices.cend(),
|
||||
[apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
|
||||
};
|
||||
auto apply_type = [apply_severity,typeIndices](const uint filter)
|
||||
{
|
||||
std::for_each(typeIndices.cbegin(), typeIndices.cend(),
|
||||
[apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
|
||||
};
|
||||
std::for_each(srcIndices.cbegin(), srcIndices.cend(),
|
||||
[apply_type](const uint idx){ apply_type(1<<idx); });
|
||||
}
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
|
||||
FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
|
||||
ALuint id, ALsizei length, const ALchar *message) noexcept
|
||||
try {
|
||||
if(length < 0)
|
||||
{
|
||||
size_t newlen{std::strlen(message)};
|
||||
if(newlen >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
|
||||
MaxDebugMessageLength};
|
||||
length = static_cast<ALsizei>(newlen);
|
||||
}
|
||||
else if(length >= MaxDebugMessageLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
|
||||
MaxDebugMessageLength};
|
||||
|
||||
auto dsource = GetDebugSource(source);
|
||||
if(!dsource)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||
|
||||
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||
if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
|
||||
throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
|
||||
|
||||
context->mDebugGroups.emplace_back(*dsource, id,
|
||||
std::string_view{message, static_cast<uint>(length)});
|
||||
auto &oldback = *(context->mDebugGroups.end()-2);
|
||||
auto &newback = context->mDebugGroups.back();
|
||||
|
||||
newback.mFilters = oldback.mFilters;
|
||||
newback.mIdFilters = oldback.mIdFilters;
|
||||
|
||||
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
|
||||
DebugSeverity::Notification, newback.mMessage);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
|
||||
FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
|
||||
try {
|
||||
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||
if(context->mDebugGroups.size() <= 1)
|
||||
throw al::context_error{AL_STACK_UNDERFLOW_EXT,
|
||||
"Attempting to pop the default debug group"};
|
||||
|
||||
DebugGroup &debug = context->mDebugGroups.back();
|
||||
const auto source = debug.mSource;
|
||||
const auto id = debug.mId;
|
||||
std::string message{std::move(debug.mMessage)};
|
||||
|
||||
context->mDebugGroups.pop_back();
|
||||
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||
context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
|
||||
DebugSeverity::Notification, message);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
|
||||
FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
|
||||
ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
|
||||
ALsizei *lengths, ALchar *logBuf) noexcept
|
||||
try {
|
||||
if(logBufSize < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
|
||||
|
||||
auto sourcesOut = al::span{sources, sources ? count : 0u};
|
||||
auto typesOut = al::span{types, types ? count : 0u};
|
||||
auto idsOut = al::span{ids, ids ? count : 0u};
|
||||
auto severitiesOut = al::span{severities, severities ? count : 0u};
|
||||
auto lengthsOut = al::span{lengths, lengths ? count : 0u};
|
||||
auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
|
||||
|
||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||
for(ALuint i{0};i < count;++i)
|
||||
{
|
||||
if(context->mDebugLog.empty())
|
||||
return i;
|
||||
|
||||
auto &entry = context->mDebugLog.front();
|
||||
const size_t tocopy{entry.mMessage.size() + 1};
|
||||
if(logOut.data() != nullptr)
|
||||
{
|
||||
if(logOut.size() < tocopy)
|
||||
return i;
|
||||
auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
|
||||
*oiter = '\0';
|
||||
logOut = {oiter+1, logOut.end()};
|
||||
}
|
||||
|
||||
if(!sourcesOut.empty())
|
||||
{
|
||||
sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
|
||||
sourcesOut = sourcesOut.subspan<1>();
|
||||
}
|
||||
if(!typesOut.empty())
|
||||
{
|
||||
typesOut.front() = GetDebugTypeEnum(entry.mType);
|
||||
typesOut = typesOut.subspan<1>();
|
||||
}
|
||||
if(!idsOut.empty())
|
||||
{
|
||||
idsOut.front() = entry.mId;
|
||||
idsOut = idsOut.subspan<1>();
|
||||
}
|
||||
if(!severitiesOut.empty())
|
||||
{
|
||||
severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
|
||||
severitiesOut = severitiesOut.subspan<1>();
|
||||
}
|
||||
if(!lengthsOut.empty())
|
||||
{
|
||||
lengthsOut.front() = static_cast<ALsizei>(tocopy);
|
||||
lengthsOut = lengthsOut.subspan<1>();
|
||||
}
|
||||
|
||||
context->mDebugLog.pop_front();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
|
||||
FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||
ALuint name, ALsizei length, const ALchar *label) noexcept
|
||||
try {
|
||||
if(!label && length != 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
|
||||
|
||||
auto objname = (length < 0) ? std::string_view{label}
|
||||
: std::string_view{label, static_cast<uint>(length)};
|
||||
if(objname.size() >= MaxObjectLabelLength)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
|
||||
objname.size(), MaxObjectLabelLength};
|
||||
|
||||
switch(identifier)
|
||||
{
|
||||
case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return;
|
||||
case AL_BUFFER: ALbuffer::SetName(context, name, objname); return;
|
||||
case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return;
|
||||
case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return;
|
||||
case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return;
|
||||
}
|
||||
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||
ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
|
||||
try {
|
||||
if(bufSize < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
|
||||
|
||||
if(!label && !length)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
|
||||
if(label && bufSize == 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
|
||||
|
||||
const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
|
||||
auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
|
||||
{
|
||||
std::string_view objname;
|
||||
|
||||
auto iter = names.find(name);
|
||||
if(iter != names.end())
|
||||
objname = iter->second;
|
||||
|
||||
if(labelOut.empty())
|
||||
*length = static_cast<ALsizei>(objname.size());
|
||||
else
|
||||
{
|
||||
const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
|
||||
auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
|
||||
*oiter = '\0';
|
||||
if(length)
|
||||
*length = static_cast<ALsizei>(tocopy);
|
||||
}
|
||||
};
|
||||
|
||||
if(identifier == AL_SOURCE_EXT)
|
||||
{
|
||||
std::lock_guard srclock{context->mSourceLock};
|
||||
copy_name(context->mSourceNames);
|
||||
}
|
||||
else if(identifier == AL_BUFFER)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard buflock{device->BufferLock};
|
||||
copy_name(device->mBufferNames);
|
||||
}
|
||||
else if(identifier == AL_FILTER_EXT)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard filterlock{device->FilterLock};
|
||||
copy_name(device->mFilterNames);
|
||||
}
|
||||
else if(identifier == AL_EFFECT_EXT)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard effectlock{device->EffectLock};
|
||||
copy_name(device->mEffectNames);
|
||||
}
|
||||
else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
|
||||
{
|
||||
std::lock_guard slotlock{context->mEffectSlotLock};
|
||||
copy_name(context->mEffectSlotNames);
|
||||
}
|
||||
else
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
70
Engine/lib/openal-soft/al/debug.h
Normal file
70
Engine/lib/openal-soft/al/debug.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef AL_DEBUG_H
|
||||
#define AL_DEBUG_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
|
||||
/* Somewhat arbitrary. Avoid letting it get out of control if the app enables
|
||||
* logging but never reads it.
|
||||
*/
|
||||
inline constexpr std::uint8_t MaxDebugLoggedMessages{64};
|
||||
inline constexpr std::uint16_t MaxDebugMessageLength{1024};
|
||||
inline constexpr std::uint8_t MaxDebugGroupDepth{64};
|
||||
inline constexpr std::uint16_t MaxObjectLabelLength{1024};
|
||||
|
||||
|
||||
inline constexpr uint DebugSourceBase{0};
|
||||
enum class DebugSource : std::uint8_t {
|
||||
API = 0,
|
||||
System,
|
||||
ThirdParty,
|
||||
Application,
|
||||
Other,
|
||||
};
|
||||
inline constexpr uint DebugSourceCount{5};
|
||||
|
||||
inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
|
||||
enum class DebugType : std::uint8_t {
|
||||
Error = 0,
|
||||
DeprecatedBehavior,
|
||||
UndefinedBehavior,
|
||||
Portability,
|
||||
Performance,
|
||||
Marker,
|
||||
PushGroup,
|
||||
PopGroup,
|
||||
Other,
|
||||
};
|
||||
inline constexpr uint DebugTypeCount{9};
|
||||
|
||||
inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount};
|
||||
enum class DebugSeverity : std::uint8_t {
|
||||
High = 0,
|
||||
Medium,
|
||||
Low,
|
||||
Notification,
|
||||
};
|
||||
inline constexpr uint DebugSeverityCount{4};
|
||||
|
||||
struct DebugGroup {
|
||||
const uint mId;
|
||||
const DebugSource mSource;
|
||||
std::string mMessage;
|
||||
std::vector<uint> mFilters;
|
||||
std::vector<std::uint64_t> mIdFilters;
|
||||
|
||||
template<typename T>
|
||||
DebugGroup(DebugSource source, uint id, T&& message)
|
||||
: mId{id}, mSource{source}, mMessage{std::forward<T>(message)}
|
||||
{ }
|
||||
DebugGroup(const DebugGroup&) = default;
|
||||
DebugGroup(DebugGroup&&) = default;
|
||||
~DebugGroup();
|
||||
};
|
||||
|
||||
#endif /* AL_DEBUG_H */
|
||||
127
Engine/lib/openal-soft/al/direct_defs.h
Normal file
127
Engine/lib/openal-soft/al/direct_defs.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#ifndef AL_DIRECT_DEFS_H
|
||||
#define AL_DIRECT_DEFS_H
|
||||
|
||||
namespace detail_ {
|
||||
|
||||
template<typename T>
|
||||
constexpr T DefaultVal() noexcept { return T{}; }
|
||||
|
||||
template<>
|
||||
constexpr void DefaultVal() noexcept { }
|
||||
|
||||
} // namespace detail_
|
||||
|
||||
#define DECL_FUNC(R, Name) \
|
||||
auto AL_APIENTRY Name() noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get()); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC1(R, Name, T1,n1) \
|
||||
auto AL_APIENTRY Name(T1 n1) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC2(R, Name, T1,n1, T2,n2) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3, n4); \
|
||||
}
|
||||
|
||||
#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct(context.get(), n1, n2, n3, n4, n5); \
|
||||
}
|
||||
|
||||
|
||||
#define DECL_FUNCEXT(R, Name,Ext) \
|
||||
auto AL_APIENTRY Name##Ext() noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get()); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT1(R, Name,Ext, T1,n1) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6); \
|
||||
}
|
||||
|
||||
#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \
|
||||
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \
|
||||
{ \
|
||||
auto context = GetContextRef(); \
|
||||
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8); \
|
||||
}
|
||||
|
||||
#endif /* AL_DIRECT_DEFS_H */
|
||||
|
|
@ -10,34 +10,39 @@
|
|||
//
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <cfloat>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#ifdef _WIN32
|
||||
#include <guiddef.h>
|
||||
#endif
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
|
||||
#ifndef GUID_DEFINED
|
||||
#define GUID_DEFINED
|
||||
typedef struct _GUID {
|
||||
#ifndef _WIN32
|
||||
using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
|
||||
std::uint32_t Data1;
|
||||
std::uint16_t Data2;
|
||||
std::uint16_t Data3;
|
||||
std::uint8_t Data4[8];
|
||||
} GUID;
|
||||
std::array<std::uint8_t,8> Data4;
|
||||
};
|
||||
|
||||
#ifndef _SYS_GUID_OPERATOR_EQ_
|
||||
#define _SYS_GUID_OPERATOR_EQ_
|
||||
inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
|
||||
{ return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
|
||||
|
||||
inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
|
||||
{ return !(lhs == rhs); }
|
||||
#endif // _SYS_GUID_OPERATOR_EQ_
|
||||
#endif // GUID_DEFINED
|
||||
#endif // _WIN32
|
||||
|
||||
#define DECL_EQOP(T, ...) \
|
||||
[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
|
||||
[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \
|
||||
{ return lhs.get_members() == rhs.get_members(); } \
|
||||
[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept \
|
||||
{ return !(lhs == rhs); }
|
||||
|
||||
extern const GUID DSPROPSETID_EAX_ReverbProperties;
|
||||
|
||||
|
|
@ -276,11 +281,15 @@ struct EAXVECTOR {
|
|||
float x;
|
||||
float y;
|
||||
float z;
|
||||
[[nodiscard]]
|
||||
auto get_members() const noexcept { return std::forward_as_tuple(x, y, z); }
|
||||
}; // EAXVECTOR
|
||||
|
||||
[[nodiscard]]
|
||||
inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||
{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; }
|
||||
{ return lhs.get_members() == rhs.get_members(); }
|
||||
|
||||
[[nodiscard]]
|
||||
inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||
{ return !(lhs == rhs); }
|
||||
|
||||
|
|
@ -361,6 +370,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F;
|
|||
constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F;
|
||||
constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
|
||||
|
||||
constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
|
||||
|
||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
|
||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
||||
|
|
@ -613,7 +623,7 @@ struct EAX30SOURCEPROPERTIES {
|
|||
float flOcclusionLFRatio; // occlusion low-frequency level re. main control
|
||||
float flOcclusionRoomRatio; // relative occlusion control for room effect
|
||||
float flOcclusionDirectRatio; // relative occlusion control for direct path
|
||||
long lExclusion; // main exlusion control (attenuation at high frequencies)
|
||||
long lExclusion; // main exclusion control (attenuation at high frequencies)
|
||||
float flExclusionLFRatio; // exclusion low-frequency level re. main control
|
||||
long lOutsideVolumeHF; // outside sound cone level at high frequencies
|
||||
float flDopplerFactor; // like DS3D flDopplerFactor but per source
|
||||
|
|
@ -653,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES {
|
|||
}; // EAXSPEAKERLEVELPROPERTIES
|
||||
|
||||
struct EAX40ACTIVEFXSLOTS {
|
||||
GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS];
|
||||
std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||
}; // EAX40ACTIVEFXSLOTS
|
||||
|
||||
struct EAX50ACTIVEFXSLOTS {
|
||||
GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS];
|
||||
std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||
}; // EAX50ACTIVEFXSLOTS
|
||||
|
||||
// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
|
||||
|
|
@ -836,6 +846,11 @@ struct EAXREVERBPROPERTIES {
|
|||
float flLFReference; // reference low frequency
|
||||
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
||||
unsigned long ulFlags; // modifies the behavior of properties
|
||||
DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom,
|
||||
lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections,
|
||||
flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime,
|
||||
flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference,
|
||||
flLFReference, flRoomRolloffFactor, ulFlags)
|
||||
}; // EAXREVERBPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -965,6 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
|
|||
|
||||
struct EAXAGCCOMPRESSORPROPERTIES {
|
||||
unsigned long ulOnOff; // Switch Compressor on or off
|
||||
DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff)
|
||||
}; // EAXAGCCOMPRESSORPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -991,6 +1007,7 @@ struct EAXAUTOWAHPROPERTIES {
|
|||
float flReleaseTime; // Release time (seconds)
|
||||
long lResonance; // Resonance (mB)
|
||||
long lPeakLevel; // Peak level (mB)
|
||||
DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel)
|
||||
}; // EAXAUTOWAHPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1038,6 +1055,7 @@ struct EAXCHORUSPROPERTIES {
|
|||
float flDepth; // Depth (0 to 1)
|
||||
float flFeedback; // Feedback (-1 to 1)
|
||||
float flDelay; // Delay (seconds)
|
||||
DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||
}; // EAXCHORUSPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1086,6 +1104,7 @@ struct EAXDISTORTIONPROPERTIES {
|
|||
float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz)
|
||||
float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz)
|
||||
float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz)
|
||||
DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth)
|
||||
}; // EAXDISTORTIONPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1130,6 +1149,7 @@ struct EAXECHOPROPERTIES {
|
|||
float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1)
|
||||
float flFeedback; // Controls the duration of echo repetition (0 to 1)
|
||||
float flSpread; // Controls the left-right spread of the echoes
|
||||
DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread)
|
||||
}; // EAXECHOPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1184,6 +1204,8 @@ struct EAXEQUALIZERPROPERTIES {
|
|||
float flMid2Width; // (octaves)
|
||||
long lHighGain; // (mB)
|
||||
float flHighCutOff; // (Hz)
|
||||
DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width,
|
||||
lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff)
|
||||
}; // EAXEQUALIZERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1255,6 +1277,7 @@ struct EAXFLANGERPROPERTIES {
|
|||
float flDepth; // Depth (0 to 1)
|
||||
float flFeedback; // Feedback (0 to 1)
|
||||
float flDelay; // Delay (seconds)
|
||||
DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||
}; // EAXFLANGERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1305,6 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
|
|||
float flFrequency; // (Hz)
|
||||
unsigned long ulLeftDirection; // see enum above
|
||||
unsigned long ulRightDirection; // see enum above
|
||||
DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection)
|
||||
}; // EAXFREQUENCYSHIFTERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1383,6 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES {
|
|||
long lPhonemeBCoarseTuning; // (semitones)
|
||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||
float flRate; // (Hz)
|
||||
DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB,
|
||||
lPhonemeBCoarseTuning, ulWaveform, flRate)
|
||||
}; // EAXVOCALMORPHERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1425,6 +1451,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
|
|||
struct EAXPITCHSHIFTERPROPERTIES {
|
||||
long lCoarseTune; // Amount of pitch shift (semitones)
|
||||
long lFineTune; // Amount of pitch shift (cents)
|
||||
DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune)
|
||||
}; // EAXPITCHSHIFTERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1460,6 +1487,7 @@ struct EAXRINGMODULATORPROPERTIES {
|
|||
float flFrequency; // Frequency of modulation (Hz)
|
||||
float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz)
|
||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||
DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform)
|
||||
}; // EAXRINGMODULATORPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1490,4 +1518,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
|
|||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
#undef DECL_EQOP
|
||||
#endif // !EAX_API_INCLUDED
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ EaxCall::EaxCall(
|
|||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
: mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
|
||||
, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||
: mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||
, mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
|
||||
, mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,16 +31,16 @@ public:
|
|||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
bool is_get() const noexcept { return mCallType == EaxCallType::get; }
|
||||
bool is_deferred() const noexcept { return mIsDeferred; }
|
||||
int get_version() const noexcept { return mVersion; }
|
||||
EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; }
|
||||
ALuint get_property_id() const noexcept { return mPropertyId; }
|
||||
ALuint get_property_al_name() const noexcept { return mPropertySourceId; }
|
||||
EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; }
|
||||
[[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; }
|
||||
[[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; }
|
||||
[[nodiscard]] auto get_version() const noexcept -> int { return mVersion; }
|
||||
[[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; }
|
||||
[[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; }
|
||||
[[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; }
|
||||
[[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; }
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
TValue& get_value() const
|
||||
[[nodiscard]] auto get_value() const -> TValue&
|
||||
{
|
||||
if(mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
|
@ -49,32 +49,32 @@ public:
|
|||
}
|
||||
|
||||
template<typename TValue>
|
||||
al::span<TValue> get_values(size_t max_count) const
|
||||
[[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
|
||||
{
|
||||
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
||||
const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
|
||||
return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
|
||||
const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
|
||||
return {static_cast<TValue*>(mPropertyBuffer), count};
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
al::span<TValue> get_values() const
|
||||
[[nodiscard]] auto get_values() const -> al::span<TValue>
|
||||
{
|
||||
return get_values<TValue>(~size_t{});
|
||||
return get_values<TValue>(~0_uz);
|
||||
}
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
void set_value(const TValue& value) const
|
||||
auto set_value(const TValue& value) const -> void
|
||||
{
|
||||
get_value<TException, TValue>() = value;
|
||||
}
|
||||
|
||||
private:
|
||||
const EaxCallType mCallType;
|
||||
int mVersion;
|
||||
EaxFxSlotIndex mFxSlotIndex;
|
||||
EaxCallPropertySetId mPropertySetId;
|
||||
int mVersion{};
|
||||
EaxFxSlotIndex mFxSlotIndex{};
|
||||
EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
|
||||
bool mIsDeferred;
|
||||
|
||||
const ALuint mPropertyId;
|
||||
|
|
|
|||
|
|
@ -4,60 +4,56 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "call.h"
|
||||
|
||||
struct EaxEffectErrorMessages
|
||||
{
|
||||
struct EaxEffectErrorMessages {
|
||||
static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
|
||||
static constexpr auto unknown_version() noexcept { return "Unknown version."; }
|
||||
}; // EaxEffectErrorMessages
|
||||
|
||||
/* TODO: Use std::variant (C++17). */
|
||||
enum class EaxEffectType {
|
||||
None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
|
||||
FrequencyShifter, Modulator, PitchShifter, VocalMorpher
|
||||
};
|
||||
struct EaxEffectProps {
|
||||
EaxEffectType mType;
|
||||
union {
|
||||
EAXREVERBPROPERTIES mReverb;
|
||||
EAXCHORUSPROPERTIES mChorus;
|
||||
EAXAUTOWAHPROPERTIES mAutowah;
|
||||
EAXAGCCOMPRESSORPROPERTIES mCompressor;
|
||||
EAXDISTORTIONPROPERTIES mDistortion;
|
||||
EAXECHOPROPERTIES mEcho;
|
||||
EAXEQUALIZERPROPERTIES mEqualizer;
|
||||
EAXFLANGERPROPERTIES mFlanger;
|
||||
EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
|
||||
EAXRINGMODULATORPROPERTIES mModulator;
|
||||
EAXPITCHSHIFTERPROPERTIES mPitchShifter;
|
||||
EAXVOCALMORPHERPROPERTIES mVocalMorpher;
|
||||
};
|
||||
};
|
||||
using EaxEffectProps = std::variant<std::monostate,
|
||||
EAXREVERBPROPERTIES,
|
||||
EAXCHORUSPROPERTIES,
|
||||
EAXAUTOWAHPROPERTIES,
|
||||
EAXAGCCOMPRESSORPROPERTIES,
|
||||
EAXDISTORTIONPROPERTIES,
|
||||
EAXECHOPROPERTIES,
|
||||
EAXEQUALIZERPROPERTIES,
|
||||
EAXFLANGERPROPERTIES,
|
||||
EAXFREQUENCYSHIFTERPROPERTIES,
|
||||
EAXRINGMODULATORPROPERTIES,
|
||||
EAXPITCHSHIFTERPROPERTIES,
|
||||
EAXVOCALMORPHERPROPERTIES>;
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
|
||||
{
|
||||
switch(props.mType)
|
||||
{
|
||||
case EaxEffectType::None: break;
|
||||
case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
|
||||
case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
|
||||
case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
|
||||
case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
|
||||
case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
|
||||
case EaxEffectType::Echo: return AL_EFFECT_ECHO;
|
||||
case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
|
||||
case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
|
||||
case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
|
||||
case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
|
||||
case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
|
||||
case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
|
||||
}
|
||||
return AL_EFFECT_NULL;
|
||||
return std::visit(overloaded{
|
||||
[](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
|
||||
[](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
|
||||
[](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
|
||||
[](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
|
||||
[](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
|
||||
[](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
|
||||
[](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
|
||||
[](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
|
||||
[](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
|
||||
[](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
|
||||
[](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
|
||||
[](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
|
||||
[](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
|
||||
}, props);
|
||||
}
|
||||
|
||||
struct EaxReverbCommitter {
|
||||
|
|
@ -105,7 +101,6 @@ struct EaxReverbCommitter {
|
|||
bool commit(const EAX_REVERBPROPERTIES &props);
|
||||
bool commit(const EAX20LISTENERPROPERTIES &props);
|
||||
bool commit(const EAXREVERBPROPERTIES &props);
|
||||
bool commit(const EaxEffectProps &props);
|
||||
|
||||
static void SetDefaults(EAX_REVERBPROPERTIES &props);
|
||||
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
|
||||
|
|
@ -115,16 +110,13 @@ struct EaxReverbCommitter {
|
|||
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
||||
|
||||
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
||||
|
||||
static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -149,51 +141,137 @@ struct EaxCommitter {
|
|||
[[noreturn]] static void fail(const char *message);
|
||||
[[noreturn]] static void fail_unknown_property_id()
|
||||
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
|
||||
|
||||
bool commit(const EaxEffectProps &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
||||
};
|
||||
|
||||
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
|
||||
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAUTOWAHPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
|
||||
};
|
||||
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
|
||||
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXCHORUSPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
|
||||
};
|
||||
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
|
||||
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
};
|
||||
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
|
||||
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXDISTORTIONPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
|
||||
};
|
||||
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
|
||||
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXECHOPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
|
||||
};
|
||||
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
|
||||
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXEQUALIZERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
|
||||
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFLANGERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
|
||||
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
|
||||
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXRINGMODULATORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
|
||||
};
|
||||
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
|
||||
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
|
||||
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXVOCALMORPHERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
|
||||
};
|
||||
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
|
||||
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const std::monostate &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const std::monostate &props);
|
||||
static void Set(const EaxCall &call, std::monostate &props);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CommitterFromProps { };
|
||||
|
||||
template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
|
||||
template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
|
||||
template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
|
||||
template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
|
||||
template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
|
||||
template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
|
||||
|
||||
template<typename T>
|
||||
using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
|
||||
|
||||
|
||||
class EaxEffect {
|
||||
public:
|
||||
|
|
@ -238,51 +316,39 @@ public:
|
|||
State4 state5_{};
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
void call_set_defaults(Args&& ...args)
|
||||
{ return T::SetDefaults(std::forward<Args>(args)...); }
|
||||
|
||||
void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||
static void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||
{
|
||||
if(altype == AL_EFFECT_EAXREVERB)
|
||||
return call_set_defaults<EaxReverbCommitter>(props);
|
||||
if(altype == AL_EFFECT_CHORUS)
|
||||
return call_set_defaults<EaxChorusCommitter>(props);
|
||||
if(altype == AL_EFFECT_AUTOWAH)
|
||||
return call_set_defaults<EaxAutowahCommitter>(props);
|
||||
if(altype == AL_EFFECT_COMPRESSOR)
|
||||
return call_set_defaults<EaxCompressorCommitter>(props);
|
||||
if(altype == AL_EFFECT_DISTORTION)
|
||||
return call_set_defaults<EaxDistortionCommitter>(props);
|
||||
if(altype == AL_EFFECT_ECHO)
|
||||
return call_set_defaults<EaxEchoCommitter>(props);
|
||||
if(altype == AL_EFFECT_EQUALIZER)
|
||||
return call_set_defaults<EaxEqualizerCommitter>(props);
|
||||
if(altype == AL_EFFECT_FLANGER)
|
||||
return call_set_defaults<EaxFlangerCommitter>(props);
|
||||
if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
|
||||
return call_set_defaults<EaxFrequencyShifterCommitter>(props);
|
||||
if(altype == AL_EFFECT_RING_MODULATOR)
|
||||
return call_set_defaults<EaxModulatorCommitter>(props);
|
||||
if(altype == AL_EFFECT_PITCH_SHIFTER)
|
||||
return call_set_defaults<EaxPitchShifterCommitter>(props);
|
||||
if(altype == AL_EFFECT_VOCAL_MORPHER)
|
||||
return call_set_defaults<EaxVocalMorpherCommitter>(props);
|
||||
return call_set_defaults<EaxNullCommitter>(props);
|
||||
switch(altype)
|
||||
{
|
||||
case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_NULL: break;
|
||||
}
|
||||
return EaxNullCommitter::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void init()
|
||||
{
|
||||
call_set_defaults<EaxReverbCommitter>(state1_.d);
|
||||
EaxReverbCommitter::SetDefaults(state1_.d);
|
||||
state1_.i = state1_.d;
|
||||
call_set_defaults<EaxReverbCommitter>(state2_.d);
|
||||
EaxReverbCommitter::SetDefaults(state2_.d);
|
||||
state2_.i = state2_.d;
|
||||
call_set_defaults<EaxReverbCommitter>(state3_.d);
|
||||
EaxReverbCommitter::SetDefaults(state3_.d);
|
||||
state3_.i = state3_.d;
|
||||
call_set_defaults<T>(state4_.d);
|
||||
T::SetDefaults(state4_.d);
|
||||
state4_.i = state4_.d;
|
||||
call_set_defaults<T>(state5_.d);
|
||||
T::SetDefaults(state5_.d);
|
||||
state5_.i = state5_.d;
|
||||
}
|
||||
|
||||
|
|
@ -290,9 +356,9 @@ public:
|
|||
{
|
||||
switch(eax_version)
|
||||
{
|
||||
case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
|
||||
case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
|
||||
case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
|
||||
case 1: EaxReverbCommitter::SetDefaults(state1_.d); break;
|
||||
case 2: EaxReverbCommitter::SetDefaults(state2_.d); break;
|
||||
case 3: EaxReverbCommitter::SetDefaults(state3_.d); break;
|
||||
case 4: call_set_defaults(altype, state4_.d); break;
|
||||
case 5: call_set_defaults(altype, state5_.d); break;
|
||||
}
|
||||
|
|
@ -300,47 +366,20 @@ public:
|
|||
}
|
||||
|
||||
|
||||
#define EAXCALL(T, Callable, ...) \
|
||||
if(T == EaxEffectType::Reverb) \
|
||||
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Chorus) \
|
||||
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Autowah) \
|
||||
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Compressor) \
|
||||
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Distortion) \
|
||||
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Echo) \
|
||||
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Equalizer) \
|
||||
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Flanger) \
|
||||
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::FrequencyShifter) \
|
||||
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Modulator) \
|
||||
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::PitchShifter) \
|
||||
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::VocalMorpher) \
|
||||
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
|
||||
return Callable<EaxNullCommitter>(__VA_ARGS__)
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
static void call_set(Args&& ...args)
|
||||
{ return T::Set(std::forward<Args>(args)...); }
|
||||
|
||||
static void call_set(const EaxCall &call, EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_set, call, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Set(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void set(const EaxCall &call)
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
|
||||
case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
|
||||
case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
|
||||
case 1: EaxReverbCommitter::Set(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Set(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Set(call, state3_.d); break;
|
||||
case 4: call_set(call, state4_.d); break;
|
||||
case 5: call_set(call, state5_.d); break;
|
||||
}
|
||||
|
|
@ -348,32 +387,32 @@ public:
|
|||
}
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
static void call_get(Args&& ...args)
|
||||
{ return T::Get(std::forward<Args>(args)...); }
|
||||
|
||||
static void call_get(const EaxCall &call, const EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_get, call, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Get(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void get(const EaxCall &call)
|
||||
void get(const EaxCall &call) const
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
|
||||
case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
|
||||
case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
|
||||
case 1: EaxReverbCommitter::Get(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Get(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Get(call, state3_.d); break;
|
||||
case 4: call_get(call, state4_.d); break;
|
||||
case 5: call_get(call, state5_.d); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
bool call_commit(Args&& ...args)
|
||||
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
|
||||
|
||||
bool call_commit(const EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_commit, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
bool commit(int eax_version)
|
||||
{
|
||||
|
|
@ -388,15 +427,15 @@ public:
|
|||
{
|
||||
case 1:
|
||||
state1_.i = state1_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state1_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
|
||||
break;
|
||||
case 2:
|
||||
state2_.i = state2_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state2_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
|
||||
break;
|
||||
case 3:
|
||||
state3_.i = state3_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state3_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
|
||||
break;
|
||||
case 4:
|
||||
state4_.i = state4_.d;
|
||||
|
|
|
|||
|
|
@ -6,54 +6,27 @@
|
|||
#include <string>
|
||||
|
||||
|
||||
EaxException::EaxException(const char *context, const char *message)
|
||||
EaxException::EaxException(std::string_view context, std::string_view message)
|
||||
: std::runtime_error{make_message(context, message)}
|
||||
{
|
||||
}
|
||||
EaxException::~EaxException() = default;
|
||||
|
||||
|
||||
std::string EaxException::make_message(const char *context, const char *message)
|
||||
std::string EaxException::make_message(std::string_view context, std::string_view message)
|
||||
{
|
||||
const auto context_size = (context ? std::string::traits_type::length(context) : 0);
|
||||
const auto has_contex = (context_size > 0);
|
||||
|
||||
const auto message_size = (message ? std::string::traits_type::length(message) : 0);
|
||||
const auto has_message = (message_size > 0);
|
||||
|
||||
if (!has_contex && !has_message)
|
||||
{
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
static constexpr char left_prefix[] = "[";
|
||||
const auto left_prefix_size = std::string::traits_type::length(left_prefix);
|
||||
|
||||
static constexpr char right_prefix[] = "] ";
|
||||
const auto right_prefix_size = std::string::traits_type::length(right_prefix);
|
||||
|
||||
const auto what_size =
|
||||
(
|
||||
has_contex ?
|
||||
left_prefix_size + context_size + right_prefix_size :
|
||||
0) +
|
||||
message_size +
|
||||
1;
|
||||
|
||||
auto what = std::string{};
|
||||
what.reserve(what_size);
|
||||
if(context.empty() && message.empty())
|
||||
return what;
|
||||
|
||||
if (has_contex)
|
||||
what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
|
||||
if(!context.empty())
|
||||
{
|
||||
what.append(left_prefix, left_prefix_size);
|
||||
what.append(context, context_size);
|
||||
what.append(right_prefix, right_prefix_size);
|
||||
}
|
||||
|
||||
if (has_message)
|
||||
{
|
||||
what.append(message, message_size);
|
||||
what += "[";
|
||||
what += context;
|
||||
what += "] ";
|
||||
}
|
||||
what += message;
|
||||
|
||||
return what;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
#ifndef EAX_EXCEPTION_INCLUDED
|
||||
#define EAX_EXCEPTION_INCLUDED
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
class EaxException : public std::runtime_error {
|
||||
static std::string make_message(const char *context, const char *message);
|
||||
static std::string make_message(std::string_view context, std::string_view message);
|
||||
|
||||
public:
|
||||
EaxException(const char *context, const char *message);
|
||||
EaxException() = delete;
|
||||
EaxException(const EaxException&) = default;
|
||||
EaxException(EaxException&&) = default;
|
||||
EaxException(std::string_view context, std::string_view message);
|
||||
~EaxException() override;
|
||||
}; // EaxException
|
||||
|
||||
auto operator=(const EaxException&) -> EaxException& = default;
|
||||
auto operator=(EaxException&&) -> EaxException& = default;
|
||||
};
|
||||
|
||||
#endif // !EAX_EXCEPTION_INCLUDED
|
||||
#endif /* EAX_EXCEPTION_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include "api.h"
|
||||
|
||||
|
||||
using EaxFxSlotIndexValue = std::size_t;
|
||||
|
||||
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
|
||||
{
|
||||
class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
|
||||
public:
|
||||
using al::optional<EaxFxSlotIndexValue>::optional;
|
||||
using std::optional<EaxFxSlotIndexValue>::optional;
|
||||
|
||||
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
||||
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@
|
|||
|
||||
#include "al/auxeffectslot.h"
|
||||
|
||||
#include "api.h"
|
||||
#include "call.h"
|
||||
#include "fx_slot_index.h"
|
||||
|
||||
|
||||
class EaxFxSlots
|
||||
{
|
||||
class EaxFxSlots {
|
||||
public:
|
||||
void initialize(ALCcontext& al_context);
|
||||
|
||||
|
|
@ -25,11 +22,9 @@ public:
|
|||
}
|
||||
|
||||
|
||||
const ALeffectslot& get(
|
||||
EaxFxSlotIndex index) const;
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&;
|
||||
|
||||
ALeffectslot& get(
|
||||
EaxFxSlotIndex index);
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&;
|
||||
|
||||
private:
|
||||
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
||||
|
|
@ -39,8 +34,7 @@ private:
|
|||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
static void fail(const char* message);
|
||||
|
||||
void initialize_fx_slots(ALCcontext& al_context);
|
||||
}; // EaxFxSlots
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
|
||||
bool eax_g_is_enabled = true;
|
||||
|
||||
|
||||
const char eax1_ext_name[] = "EAX";
|
||||
const char eax2_ext_name[] = "EAX2.0";
|
||||
const char eax3_ext_name[] = "EAX3.0";
|
||||
const char eax4_ext_name[] = "EAX4.0";
|
||||
const char eax5_ext_name[] = "EAX5.0";
|
||||
|
||||
const char eax_x_ram_ext_name[] = "EAX-RAM";
|
||||
|
||||
const char eax_eax_set_func_name[] = "EAXSet";
|
||||
const char eax_eax_get_func_name[] = "EAXGet";
|
||||
|
||||
const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
|
||||
const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";
|
||||
|
|
@ -1,22 +1,6 @@
|
|||
#ifndef EAX_GLOBALS_INCLUDED
|
||||
#define EAX_GLOBALS_INCLUDED
|
||||
|
||||
inline bool eax_g_is_enabled{true};
|
||||
|
||||
extern bool eax_g_is_enabled;
|
||||
|
||||
|
||||
extern const char eax1_ext_name[];
|
||||
extern const char eax2_ext_name[];
|
||||
extern const char eax3_ext_name[];
|
||||
extern const char eax4_ext_name[];
|
||||
extern const char eax5_ext_name[];
|
||||
|
||||
extern const char eax_x_ram_ext_name[];
|
||||
|
||||
extern const char eax_eax_set_func_name[];
|
||||
extern const char eax_eax_get_func_name[];
|
||||
|
||||
extern const char eax_eax_set_buffer_mode_func_name[];
|
||||
extern const char eax_eax_get_buffer_mode_func_name[];
|
||||
|
||||
#endif // !EAX_GLOBALS_INCLUDED
|
||||
#endif /* EAX_GLOBALS_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@
|
|||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "alstring.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
void eax_log_exception(const char *message) noexcept
|
||||
void eax_log_exception(std::string_view message) noexcept
|
||||
{
|
||||
const auto exception_ptr = std::current_exception();
|
||||
assert(exception_ptr);
|
||||
|
|
@ -17,10 +18,9 @@ void eax_log_exception(const char *message) noexcept
|
|||
std::rethrow_exception(exception_ptr);
|
||||
}
|
||||
catch(const std::exception& ex) {
|
||||
const auto ex_message = ex.what();
|
||||
ERR("%s %s\n", message ? message : "", ex_message);
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
|
||||
}
|
||||
catch(...) {
|
||||
ERR("%s %s\n", message ? message : "", "Generic exception.");
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "opthelpers.h"
|
||||
|
||||
using EaxDirtyFlags = unsigned int;
|
||||
|
||||
struct EaxAlLowPassParam {
|
||||
|
|
@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
|
|||
float gain_hf;
|
||||
};
|
||||
|
||||
void eax_log_exception(const char *message) noexcept;
|
||||
void eax_log_exception(std::string_view message) noexcept;
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
void eax_validate_range(
|
||||
const char* value_name,
|
||||
const TValue& value,
|
||||
const TValue& min_value,
|
||||
void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
|
||||
const TValue& max_value)
|
||||
{
|
||||
if (value >= min_value && value <= max_value)
|
||||
if(value >= min_value && value <= max_value) LIKELY
|
||||
return;
|
||||
|
||||
const auto message =
|
||||
|
|
|
|||
|
|
@ -24,15 +24,7 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
|
|||
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
||||
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
||||
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(
|
||||
ALsizei n,
|
||||
const ALuint* buffers,
|
||||
ALint value);
|
||||
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(
|
||||
ALuint buffer,
|
||||
ALint* pReserved);
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
|
||||
|
||||
#endif // !EAX_X_RAM_INCLUDED
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@
|
|||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
|
@ -38,26 +42,23 @@
|
|||
#include "AL/efx-presets.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "albit.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "eax/exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const EffectList gEffectList[16]{
|
||||
const std::array<EffectList,16> gEffectList{{
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||
|
|
@ -73,95 +74,74 @@ const EffectList gEffectList[16]{
|
|||
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
|
||||
};
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
|
||||
}};
|
||||
|
||||
bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
|
||||
effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
effect_exception::~effect_exception() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
struct EffectPropsItem {
|
||||
ALenum Type;
|
||||
const EffectProps &DefaultProps;
|
||||
const EffectVtable &Vtable;
|
||||
};
|
||||
constexpr EffectPropsItem EffectPropsList[] = {
|
||||
{ AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
|
||||
{ AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
|
||||
{ AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
|
||||
{ AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
|
||||
{ AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
|
||||
{ AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
|
||||
{ AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
|
||||
{ AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
|
||||
{ AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
|
||||
{ AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
|
||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
|
||||
{ AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
|
||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
|
||||
{ AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
|
||||
};
|
||||
using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
|
||||
|
||||
|
||||
void ALeffect_setParami(ALeffect *effect, ALenum param, int value)
|
||||
{ effect->vtab->setParami(&effect->Props, param, value); }
|
||||
void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values)
|
||||
{ effect->vtab->setParamiv(&effect->Props, param, values); }
|
||||
void ALeffect_setParamf(ALeffect *effect, ALenum param, float value)
|
||||
{ effect->vtab->setParamf(&effect->Props, param, value); }
|
||||
void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values)
|
||||
{ effect->vtab->setParamfv(&effect->Props, param, values); }
|
||||
|
||||
void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value)
|
||||
{ effect->vtab->getParami(&effect->Props, param, value); }
|
||||
void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values)
|
||||
{ effect->vtab->getParamiv(&effect->Props, param, values); }
|
||||
void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value)
|
||||
{ effect->vtab->getParamf(&effect->Props, param, value); }
|
||||
void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values)
|
||||
{ effect->vtab->getParamfv(&effect->Props, param, values); }
|
||||
|
||||
|
||||
const EffectPropsItem *getEffectPropsItemByType(ALenum type)
|
||||
constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
|
||||
{
|
||||
auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
|
||||
[type](const EffectPropsItem &item) noexcept -> bool
|
||||
{ return item.Type == type; });
|
||||
return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr;
|
||||
switch(type)
|
||||
{
|
||||
case AL_EFFECT_NULL: return NullEffectProps;
|
||||
case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
|
||||
case AL_EFFECT_REVERB: return StdReverbEffectProps;
|
||||
case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
|
||||
case AL_EFFECT_CHORUS: return ChorusEffectProps;
|
||||
case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
|
||||
case AL_EFFECT_DISTORTION: return DistortionEffectProps;
|
||||
case AL_EFFECT_ECHO: return EchoEffectProps;
|
||||
case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
|
||||
case AL_EFFECT_FLANGER: return FlangerEffectProps;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
|
||||
case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
|
||||
case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
|
||||
case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
|
||||
case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
|
||||
}
|
||||
return NullEffectProps;
|
||||
}
|
||||
|
||||
void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
||||
{
|
||||
const EffectPropsItem *item{getEffectPropsItemByType(type)};
|
||||
if(item)
|
||||
switch(type)
|
||||
{
|
||||
effect->Props = item->DefaultProps;
|
||||
effect->vtab = &item->Vtable;
|
||||
}
|
||||
else
|
||||
{
|
||||
effect->Props = EffectProps{};
|
||||
effect->vtab = &NullEffectVtable;
|
||||
case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
|
||||
case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
|
||||
case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
|
||||
case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
|
||||
case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||
case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
|
||||
case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
|
||||
case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
|
||||
case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
|
||||
case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
|
||||
case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
|
||||
case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
|
||||
case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE:
|
||||
effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
|
||||
break;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
|
||||
effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
|
||||
break;
|
||||
case AL_EFFECT_CONVOLUTION_SOFT:
|
||||
effect->PropsVariant.emplace<ConvolutionEffectHandler>();
|
||||
break;
|
||||
}
|
||||
effect->Props = GetDefaultProps(type);
|
||||
effect->type = type;
|
||||
}
|
||||
|
||||
bool EnsureEffects(ALCdevice *device, size_t needed)
|
||||
{
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
|
||||
auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
|
||||
try {
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
|
||||
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
||||
|
||||
|
|
@ -170,21 +150,19 @@ bool EnsureEffects(ALCdevice *device, size_t needed)
|
|||
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
||||
return false;
|
||||
|
||||
device->EffectList.emplace_back();
|
||||
auto sublist = device->EffectList.end() - 1;
|
||||
sublist->FreeMask = ~0_u64;
|
||||
sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
|
||||
if(!sublist->Effects) UNLIKELY
|
||||
{
|
||||
device->EffectList.pop_back();
|
||||
return false;
|
||||
}
|
||||
count += 64;
|
||||
EffectSubList sublist{};
|
||||
sublist.FreeMask = ~0_u64;
|
||||
sublist.Effects = SubListAllocator{}.allocate(1);
|
||||
device->EffectList.emplace_back(std::move(sublist));
|
||||
count += std::tuple_size_v<SubListAllocator::value_type>;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ALeffect *AllocEffect(ALCdevice *device)
|
||||
ALeffect *AllocEffect(ALCdevice *device) noexcept
|
||||
{
|
||||
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
||||
[](const EffectSubList &entry) noexcept -> bool
|
||||
|
|
@ -193,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device)
|
|||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
|
||||
ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
/* Add 1 to avoid effect ID 0. */
|
||||
|
|
@ -206,16 +184,18 @@ ALeffect *AllocEffect(ALCdevice *device)
|
|||
|
||||
void FreeEffect(ALCdevice *device, ALeffect *effect)
|
||||
{
|
||||
device->mEffectNames.erase(effect->id);
|
||||
|
||||
const ALuint id{effect->id - 1};
|
||||
const size_t lidx{id >> 6};
|
||||
const ALuint slidx{id & 0x3f};
|
||||
|
||||
al::destroy_at(effect);
|
||||
std::destroy_at(effect);
|
||||
|
||||
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
||||
}
|
||||
|
||||
inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
|
||||
{
|
||||
const size_t lidx{(id-1) >> 6};
|
||||
const ALuint slidx{(id-1) & 0x3f};
|
||||
|
|
@ -225,320 +205,294 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
|||
EffectSubList &sublist = device->EffectList[lidx];
|
||||
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
||||
return nullptr;
|
||||
return sublist.Effects + slidx;
|
||||
return al::to_address(sublist.Effects->begin() + slidx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(n < 0) UNLIKELY
|
||||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
if(n == 1) LIKELY
|
||||
{
|
||||
/* Special handling for the easy and normal case. */
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
effects[0] = effect->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Store the allocated buffer IDs in a separate local list, to avoid
|
||||
* modifying the user storage in case of failure.
|
||||
*/
|
||||
al::vector<ALuint> ids;
|
||||
ids.reserve(static_cast<ALuint>(n));
|
||||
do {
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
ids.emplace_back(effect->id);
|
||||
} while(--n);
|
||||
std::copy(ids.cbegin(), ids.cend(), effects);
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
if(!EnsureEffects(device, eids.size()))
|
||||
throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
|
||||
(n == 1) ? "" : "s"};
|
||||
|
||||
std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(n < 0) UNLIKELY
|
||||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||
AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
|
||||
const ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
/* First try to find any effects that are invalid. */
|
||||
auto validate_effect = [device](const ALuint eid) -> bool
|
||||
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
||||
|
||||
const ALuint *effects_end = effects + n;
|
||||
auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
|
||||
if(inveffect != effects_end) UNLIKELY
|
||||
{
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
|
||||
return;
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
|
||||
if(inveffect != eids.end())
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
|
||||
|
||||
/* All good. Delete non-0 effect IDs. */
|
||||
auto delete_effect = [device](ALuint eid) -> void
|
||||
{
|
||||
ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
|
||||
if(effect) FreeEffect(device, effect);
|
||||
if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
|
||||
FreeEffect(device, effect);
|
||||
};
|
||||
std::for_each(effects, effects_end, delete_effect);
|
||||
std::for_each(eids.begin(), eids.end(), delete_effect);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(context) LIKELY
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
}
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
bool isOk{value == AL_EFFECT_NULL};
|
||||
if(!isOk)
|
||||
case AL_EFFECT_TYPE:
|
||||
if(value != AL_EFFECT_NULL)
|
||||
{
|
||||
for(const EffectList &effectitem : gEffectList)
|
||||
{
|
||||
if(value == effectitem.val && !DisabledEffects[effectitem.type])
|
||||
{
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto check_effect = [value](const EffectList &item) -> bool
|
||||
{ return value == item.val && !DisabledEffects.test(item.type); };
|
||||
if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
|
||||
throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
|
||||
value};
|
||||
}
|
||||
|
||||
if(isOk)
|
||||
InitEffectParams(aleffect, value);
|
||||
else
|
||||
context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
|
||||
}
|
||||
else try
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParami(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alEffecti(effect, param, values[0]);
|
||||
InitEffectParams(aleffect, value);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
const ALint *values) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alEffectiDirect(context, effect, param, *values);
|
||||
return;
|
||||
}
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
const ALfloat *values) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamfv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
*value = aleffect->type;
|
||||
else try
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParami(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alGetEffecti(effect, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint *values) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_TYPE:
|
||||
alGetEffectiDirect(context, effect, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamfv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
void InitEffect(ALeffect *effect)
|
||||
|
|
@ -546,26 +500,43 @@ void InitEffect(ALeffect *effect)
|
|||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
}
|
||||
|
||||
void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
auto effect = LookupEffect(device, id);
|
||||
if(!effect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
|
||||
|
||||
device->mEffectNames.insert_or_assign(id, name);
|
||||
}
|
||||
|
||||
|
||||
EffectSubList::~EffectSubList()
|
||||
{
|
||||
if(!Effects)
|
||||
return;
|
||||
|
||||
uint64_t usemask{~FreeMask};
|
||||
while(usemask)
|
||||
{
|
||||
const int idx{al::countr_zero(usemask)};
|
||||
al::destroy_at(Effects+idx);
|
||||
std::destroy_at(al::to_address(Effects->begin()+idx));
|
||||
usemask &= ~(1_u64 << idx);
|
||||
}
|
||||
FreeMask = ~usemask;
|
||||
al_free(Effects);
|
||||
SubListAllocator{}.deallocate(Effects, 1);
|
||||
Effects = nullptr;
|
||||
}
|
||||
|
||||
|
||||
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
|
||||
static const struct {
|
||||
const char name[32];
|
||||
struct EffectPreset {
|
||||
const char name[32]; /* NOLINT(*-avoid-c-arrays) */
|
||||
EFXEAXREVERBPROPERTIES props;
|
||||
} reverblist[] = {
|
||||
};
|
||||
#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
|
||||
static constexpr std::array reverblist{
|
||||
DECL(GENERIC),
|
||||
DECL(PADDEDCELL),
|
||||
DECL(ROOM),
|
||||
|
|
@ -695,61 +666,62 @@ static const struct {
|
|||
};
|
||||
#undef DECL
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect)
|
||||
{
|
||||
if(al::strcasecmp(name, "NONE") == 0)
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if(al::case_compare(name, "NONE"sv) == 0)
|
||||
{
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
TRACE("Loading reverb '%s'\n", "NONE");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!DisabledEffects[EAXREVERB_EFFECT])
|
||||
if(!DisabledEffects.test(EAXREVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
||||
else if(!DisabledEffects[REVERB_EFFECT])
|
||||
else if(!DisabledEffects.test(REVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_REVERB);
|
||||
else
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
for(const auto &reverbitem : reverblist)
|
||||
{
|
||||
const EFXEAXREVERBPROPERTIES *props;
|
||||
|
||||
if(al::strcasecmp(name, reverbitem.name) != 0)
|
||||
if(al::case_compare(name, std::data(reverbitem.name)) != 0)
|
||||
continue;
|
||||
|
||||
TRACE("Loading reverb '%s'\n", reverbitem.name);
|
||||
props = &reverbitem.props;
|
||||
effect->Props.Reverb.Density = props->flDensity;
|
||||
effect->Props.Reverb.Diffusion = props->flDiffusion;
|
||||
effect->Props.Reverb.Gain = props->flGain;
|
||||
effect->Props.Reverb.GainHF = props->flGainHF;
|
||||
effect->Props.Reverb.GainLF = props->flGainLF;
|
||||
effect->Props.Reverb.DecayTime = props->flDecayTime;
|
||||
effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
|
||||
effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
|
||||
effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain;
|
||||
effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay;
|
||||
effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
|
||||
effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
|
||||
effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
|
||||
effect->Props.Reverb.LateReverbGain = props->flLateReverbGain;
|
||||
effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay;
|
||||
effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
|
||||
effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
|
||||
effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
|
||||
effect->Props.Reverb.EchoTime = props->flEchoTime;
|
||||
effect->Props.Reverb.EchoDepth = props->flEchoDepth;
|
||||
effect->Props.Reverb.ModulationTime = props->flModulationTime;
|
||||
effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
|
||||
effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
|
||||
effect->Props.Reverb.HFReference = props->flHFReference;
|
||||
effect->Props.Reverb.LFReference = props->flLFReference;
|
||||
effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
|
||||
effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
|
||||
const auto &props = reverbitem.props;
|
||||
auto &dst = std::get<ReverbProps>(effect->Props);
|
||||
dst.Density = props.flDensity;
|
||||
dst.Diffusion = props.flDiffusion;
|
||||
dst.Gain = props.flGain;
|
||||
dst.GainHF = props.flGainHF;
|
||||
dst.GainLF = props.flGainLF;
|
||||
dst.DecayTime = props.flDecayTime;
|
||||
dst.DecayHFRatio = props.flDecayHFRatio;
|
||||
dst.DecayLFRatio = props.flDecayLFRatio;
|
||||
dst.ReflectionsGain = props.flReflectionsGain;
|
||||
dst.ReflectionsDelay = props.flReflectionsDelay;
|
||||
dst.ReflectionsPan[0] = props.flReflectionsPan[0];
|
||||
dst.ReflectionsPan[1] = props.flReflectionsPan[1];
|
||||
dst.ReflectionsPan[2] = props.flReflectionsPan[2];
|
||||
dst.LateReverbGain = props.flLateReverbGain;
|
||||
dst.LateReverbDelay = props.flLateReverbDelay;
|
||||
dst.LateReverbPan[0] = props.flLateReverbPan[0];
|
||||
dst.LateReverbPan[1] = props.flLateReverbPan[1];
|
||||
dst.LateReverbPan[2] = props.flLateReverbPan[2];
|
||||
dst.EchoTime = props.flEchoTime;
|
||||
dst.EchoDepth = props.flEchoDepth;
|
||||
dst.ModulationTime = props.flModulationTime;
|
||||
dst.ModulationDepth = props.flModulationDepth;
|
||||
dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
|
||||
dst.HFReference = props.flHFReference;
|
||||
dst.LFReference = props.flLFReference;
|
||||
dst.RoomRolloffFactor = props.flRoomRolloffFactor;
|
||||
dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
WARN("Reverb preset '%s' not found\n", name);
|
||||
WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
|
||||
}
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept
|
||||
|
|
@ -757,10 +729,7 @@ bool IsValidEffectType(ALenum type) noexcept
|
|||
if(type == AL_EFFECT_NULL)
|
||||
return true;
|
||||
|
||||
for(const auto &effect_item : gEffectList)
|
||||
{
|
||||
if(type == effect_item.val && !DisabledEffects[effect_item.type])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
auto check_effect = [type](const EffectList &item) noexcept -> bool
|
||||
{ return type == item.val && !DisabledEffects.test(item.type); };
|
||||
return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
#ifndef AL_EFFECT_H
|
||||
#define AL_EFFECT_H
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects/effects.h"
|
||||
|
||||
|
||||
enum {
|
||||
|
|
@ -27,36 +36,55 @@ enum {
|
|||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern float ReverbBoost;
|
||||
inline std::bitset<MAX_EFFECTS> DisabledEffects;
|
||||
|
||||
struct EffectList {
|
||||
const char name[16];
|
||||
int type;
|
||||
const char name[16]; /* NOLINT(*-avoid-c-arrays) */
|
||||
ALuint type;
|
||||
ALenum val;
|
||||
};
|
||||
extern const EffectList gEffectList[16];
|
||||
extern const std::array<EffectList,16> gEffectList;
|
||||
|
||||
using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
|
||||
StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
|
||||
DistortionEffectHandler,EchoEffectHandler,EqualizerEffectHandler,FlangerEffectHandler,
|
||||
FshifterEffectHandler,ModulatorEffectHandler,PshifterEffectHandler,VmorpherEffectHandler,
|
||||
DedicatedDialogEffectHandler,DedicatedLfeEffectHandler,ConvolutionEffectHandler>;
|
||||
|
||||
struct ALeffect {
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type{AL_EFFECT_NULL};
|
||||
|
||||
EffectHandlerVariant PropsVariant;
|
||||
EffectProps Props{};
|
||||
|
||||
const EffectVtable *vtab{nullptr};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0u};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
void InitEffect(ALeffect *effect);
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect);
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept;
|
||||
|
||||
struct EffectSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */
|
||||
|
||||
EffectSubList() noexcept = default;
|
||||
EffectSubList(const EffectSubList&) = delete;
|
||||
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||
~EffectSubList();
|
||||
|
||||
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,100 +21,87 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
AutowahProps props{};
|
||||
props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
|
||||
void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void AutowahEffectHandler::SetParamf(AutowahProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
|
||||
props->Autowah.AttackTime = val;
|
||||
props.AttackTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
|
||||
props->Autowah.ReleaseTime = val;
|
||||
props.ReleaseTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
|
||||
props->Autowah.Resonance = val;
|
||||
props.Resonance = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
|
||||
props->Autowah.PeakGain = val;
|
||||
props.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Autowah_setParamf(props, param, vals[0]); }
|
||||
void AutowahEffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Autowah_setParami(EffectProps*, ALenum param, int)
|
||||
void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void AutowahEffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break;
|
||||
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
|
||||
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
|
||||
case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||
}
|
||||
|
||||
}
|
||||
void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Autowah_getParamf(props, param, vals); }
|
||||
|
||||
void Autowah_getParami(const EffectProps*, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Autowah);
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -189,62 +177,62 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool AutowahCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
|
||||
&& mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
|
||||
&& mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
|
||||
&& mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
|
||||
if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
|
||||
mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
|
||||
mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
|
||||
mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
|
||||
mAlProps = [&]{
|
||||
AutowahProps ret{};
|
||||
ret.AttackTime = props.flAttackTime;
|
||||
ret.ReleaseTime = props.flReleaseTime;
|
||||
ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
|
||||
ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Autowah;
|
||||
props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
static constexpr EAXAUTOWAHPROPERTIES defprops{[]
|
||||
{
|
||||
EAXAUTOWAHPROPERTIES ret{};
|
||||
ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -27,16 +25,16 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm
|
|||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||
|
||||
inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
|
||||
constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
inline ALenum EnumFromWaveform(ChorusWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(ChorusWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -46,13 +44,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type)
|
|||
throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultChorusProps() noexcept
|
||||
{
|
||||
ChorusProps props{};
|
||||
props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value();
|
||||
props.Phase = AL_CHORUS_DEFAULT_PHASE;
|
||||
props.Rate = AL_CHORUS_DEFAULT_RATE;
|
||||
props.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
||||
props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
||||
props.Delay = AL_CHORUS_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
constexpr EffectProps genDefaultFlangerProps() noexcept
|
||||
{
|
||||
ChorusProps props{};
|
||||
props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
|
||||
props.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
props.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
props.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
props.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||
|
||||
void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEnum(val))
|
||||
props->Chorus.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -60,115 +86,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_CHORUS_PHASE:
|
||||
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
|
||||
props->Chorus.Phase = val;
|
||||
props.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Chorus_setParami(props, param, vals[0]); }
|
||||
void Chorus_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void ChorusEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
|
||||
props->Chorus.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
|
||||
props->Chorus.Depth = val;
|
||||
props.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
|
||||
props->Chorus.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
|
||||
props->Chorus.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Chorus_setParamf(props, param, vals[0]); }
|
||||
void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
case AL_CHORUS_PHASE: *val = props.Phase; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Chorus_getParami(props, param, vals); }
|
||||
void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
case AL_CHORUS_RATE: *val = props.Rate; break;
|
||||
case AL_CHORUS_DEPTH: *val = props.Depth; break;
|
||||
case AL_CHORUS_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_CHORUS_DELAY: *val = props.Delay; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Chorus_getParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultChorusProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
|
||||
props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
|
||||
props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
|
||||
props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
||||
props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
||||
props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
void ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||
|
||||
void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEnum(val))
|
||||
props->Chorus.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -176,127 +176,87 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
|
||||
props->Chorus.Phase = val;
|
||||
props.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Flanger_setParami(props, param, vals[0]); }
|
||||
void Flanger_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void FlangerEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
|
||||
props->Chorus.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
|
||||
props->Chorus.Depth = val;
|
||||
props.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
|
||||
props->Chorus.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
|
||||
props->Chorus.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Flanger_setParamf(props, param, vals[0]); }
|
||||
void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
case AL_FLANGER_PHASE: *val = props.Phase; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Flanger_getParami(props, param, vals); }
|
||||
void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
case AL_FLANGER_RATE: *val = props.Rate; break;
|
||||
case AL_FLANGER_DEPTH: *val = props.Depth; break;
|
||||
case AL_FLANGER_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_FLANGER_DELAY: *val = props.Delay; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Flanger_getParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultFlangerProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
|
||||
props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Chorus);
|
||||
|
||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Flanger);
|
||||
|
||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||
void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
struct EaxChorusTraits {
|
||||
using Props = EAXCHORUSPROPERTIES;
|
||||
using EaxProps = EAXCHORUSPROPERTIES;
|
||||
using Committer = EaxChorusCommitter;
|
||||
static constexpr auto Field = &EaxEffectProps::mChorus;
|
||||
|
||||
static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
|
||||
static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
|
||||
|
||||
static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
|
||||
|
|
@ -359,11 +319,9 @@ struct EaxChorusTraits {
|
|||
}; // EaxChorusTraits
|
||||
|
||||
struct EaxFlangerTraits {
|
||||
using Props = EAXFLANGERPROPERTIES;
|
||||
using EaxProps = EAXFLANGERPROPERTIES;
|
||||
using Committer = EaxFlangerCommitter;
|
||||
static constexpr auto Field = &EaxEffectProps::mFlanger;
|
||||
|
||||
static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
|
||||
static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
|
||||
|
||||
static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
|
||||
|
|
@ -428,11 +386,10 @@ struct EaxFlangerTraits {
|
|||
template<typename TTraits>
|
||||
struct ChorusFlangerEffect {
|
||||
using Traits = TTraits;
|
||||
using EaxProps = typename Traits::EaxProps;
|
||||
using Committer = typename Traits::Committer;
|
||||
using Exception = typename Committer::Exception;
|
||||
|
||||
static constexpr auto Field = Traits::Field;
|
||||
|
||||
struct WaveformValidator {
|
||||
void operator()(unsigned long ulWaveform) const
|
||||
{
|
||||
|
|
@ -500,7 +457,7 @@ struct ChorusFlangerEffect {
|
|||
}; // DelayValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const typename Traits::Props& all) const
|
||||
void operator()(const EaxProps& all) const
|
||||
{
|
||||
WaveformValidator{}(all.ulWaveform);
|
||||
PhaseValidator{}(all.lPhase);
|
||||
|
|
@ -514,8 +471,7 @@ struct ChorusFlangerEffect {
|
|||
public:
|
||||
static void SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
props.mType = Traits::eax_effect_type();
|
||||
auto&& all = props.emplace<EaxProps>();
|
||||
all.ulWaveform = Traits::eax_default_waveform();
|
||||
all.lPhase = Traits::eax_default_phase();
|
||||
all.flRate = Traits::eax_default_rate();
|
||||
|
|
@ -525,109 +481,83 @@ public:
|
|||
}
|
||||
|
||||
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
static void Get(const EaxCall &call, const EaxProps &all)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case Traits::eax_none_param_id():
|
||||
break;
|
||||
|
||||
case Traits::eax_allparameters_param_id():
|
||||
call.template set_value<Exception>(all);
|
||||
break;
|
||||
|
||||
case Traits::eax_waveform_param_id():
|
||||
call.template set_value<Exception>(all.ulWaveform);
|
||||
break;
|
||||
|
||||
case Traits::eax_phase_param_id():
|
||||
call.template set_value<Exception>(all.lPhase);
|
||||
break;
|
||||
|
||||
case Traits::eax_rate_param_id():
|
||||
call.template set_value<Exception>(all.flRate);
|
||||
break;
|
||||
|
||||
case Traits::eax_depth_param_id():
|
||||
call.template set_value<Exception>(all.flDepth);
|
||||
break;
|
||||
|
||||
case Traits::eax_feedback_param_id():
|
||||
call.template set_value<Exception>(all.flFeedback);
|
||||
break;
|
||||
|
||||
case Traits::eax_delay_param_id():
|
||||
call.template set_value<Exception>(all.flDelay);
|
||||
break;
|
||||
|
||||
default:
|
||||
Committer::fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props)
|
||||
static void Set(const EaxCall &call, EaxProps &all)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case Traits::eax_none_param_id():
|
||||
break;
|
||||
|
||||
case Traits::eax_allparameters_param_id():
|
||||
Committer::template defer<AllValidator>(call, all);
|
||||
break;
|
||||
|
||||
case Traits::eax_waveform_param_id():
|
||||
Committer::template defer<WaveformValidator>(call, all.ulWaveform);
|
||||
break;
|
||||
|
||||
case Traits::eax_phase_param_id():
|
||||
Committer::template defer<PhaseValidator>(call, all.lPhase);
|
||||
break;
|
||||
|
||||
case Traits::eax_rate_param_id():
|
||||
Committer::template defer<RateValidator>(call, all.flRate);
|
||||
break;
|
||||
|
||||
case Traits::eax_depth_param_id():
|
||||
Committer::template defer<DepthValidator>(call, all.flDepth);
|
||||
break;
|
||||
|
||||
case Traits::eax_feedback_param_id():
|
||||
Committer::template defer<FeedbackValidator>(call, all.flFeedback);
|
||||
break;
|
||||
|
||||
case Traits::eax_delay_param_id():
|
||||
Committer::template defer<DelayValidator>(call, all.flDelay);
|
||||
break;
|
||||
|
||||
default:
|
||||
Committer::fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
|
||||
static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
|
||||
{
|
||||
if(props.mType == props_.mType)
|
||||
{
|
||||
auto&& src = props_.*Field;
|
||||
auto&& dst = props.*Field;
|
||||
if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
|
||||
&& dst.flRate == src.flRate && dst.flDepth == src.flDepth
|
||||
&& dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
|
||||
return false;
|
||||
}
|
||||
if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
props_ = props;
|
||||
auto&& dst = props.*Field;
|
||||
|
||||
al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
|
||||
al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
|
||||
al_props_.Chorus.Rate = dst.flRate;
|
||||
al_props_.Chorus.Depth = dst.flDepth;
|
||||
al_props_.Chorus.Feedback = dst.flFeedback;
|
||||
al_props_.Chorus.Delay = dst.flDelay;
|
||||
al_props_.Waveform = Traits::eax_waveform(props.ulWaveform);
|
||||
al_props_.Phase = static_cast<int>(props.lPhase);
|
||||
al_props_.Rate = props.flRate;
|
||||
al_props_.Depth = props.flDepth;
|
||||
al_props_.Feedback = props.flFeedback;
|
||||
al_props_.Delay = props.flDelay;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -652,29 +582,25 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool ChorusCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
||||
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::Get(call, props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::Set(call, props);
|
||||
|
|
@ -693,29 +619,25 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FlangerCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
||||
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::Get(call, props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::Set(call, props);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,14 +17,25 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
CompressorProps props{};
|
||||
props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
void CompressorEffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
|
||||
props->Compressor.OnOff = (val != AL_FALSE);
|
||||
props.OnOff = (val != AL_FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -31,51 +43,36 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Compressor_setParami(props, param, vals[0]); }
|
||||
void Compressor_setParamf(EffectProps*, ALenum param, float)
|
||||
void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void CompressorEffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
break;
|
||||
|
||||
case AL_COMPRESSOR_ONOFF: *val = props.OnOff; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Compressor_getParami(props, param, vals); }
|
||||
void Compressor_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Compressor);
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -115,46 +112,40 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool CompressorCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
|
||||
if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
mAlProps = CompressorProps{props.ulOnOff != 0};
|
||||
|
||||
mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Compressor;
|
||||
props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
|
||||
props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +1,117 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_setParami(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_setParamf(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_getParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Convolution_getParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
ConvolutionProps props{};
|
||||
props.OrientAt = {0.0f, 0.0f, -1.0f};
|
||||
props.OrientUp = {0.0f, 1.0f, 0.0f};
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Convolution);
|
||||
|
||||
const EffectProps ConvolutionEffectProps{genDefaultProps()};
|
||||
|
||||
void ConvolutionEffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParami(props, param, *vals);
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values)
|
||||
{
|
||||
static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); };
|
||||
al::span<const float> vals;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||
vals = {values, 6_uz};
|
||||
if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
|
||||
|
||||
std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
|
||||
std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
|
||||
break;
|
||||
|
||||
default:
|
||||
SetParamf(props, param, *values);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionEffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void ConvolutionEffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values)
|
||||
{
|
||||
al::span<float> vals;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||
vals = {values, 6_uz};
|
||||
std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
|
||||
std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
|
||||
break;
|
||||
|
||||
default:
|
||||
GetParamf(props, param, values);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,61 +12,111 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Dedicated_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultDialogProps() noexcept
|
||||
{
|
||||
DedicatedProps props{};
|
||||
props.Target = DedicatedProps::Dialog;
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
constexpr EffectProps genDefaultLfeProps() noexcept
|
||||
{
|
||||
DedicatedProps props{};
|
||||
props.Target = DedicatedProps::Lfe;
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
||||
|
||||
void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void DedicatedDialogEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props->Dedicated.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Dedicated_setParamf(props, param, vals[0]); }
|
||||
void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Dedicated_getParami(const EffectProps*, ALenum param, int*)
|
||||
void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
||||
|
||||
void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void DedicatedLfeEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
*val = props->Dedicated.Gain;
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Dedicated_getParamf(props, param, vals); }
|
||||
void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Dedicated.Gain = 1.0f;
|
||||
return props;
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Dedicated);
|
||||
|
||||
const EffectProps DedicatedEffectProps{genDefaultProps()};
|
||||
void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,108 +17,93 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Distortion_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
DistortionProps props{};
|
||||
props.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
props.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||
props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void DistortionEffectHandler::SetParamf(DistortionProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
|
||||
props->Distortion.Edge = val;
|
||||
props.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
|
||||
props->Distortion.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
props.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
|
||||
props->Distortion.EQCenter = val;
|
||||
props.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
|
||||
props->Distortion.EQBandwidth = val;
|
||||
props.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Distortion_setParamf(props, param, vals[0]); }
|
||||
void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Distortion_getParami(const EffectProps*, ALenum param, int*)
|
||||
void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void DistortionEffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
*val = props->Distortion.Edge;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
*val = props->Distortion.Gain;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
*val = props->Distortion.LowpassCutoff;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
*val = props->Distortion.EQCenter;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
*val = props->Distortion.EQBandwidth;
|
||||
break;
|
||||
case AL_DISTORTION_EDGE: *val = props.Edge; break;
|
||||
case AL_DISTORTION_GAIN: *val = props.Gain; break;
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
|
||||
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
|
||||
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Distortion_getParamf(props, param, vals); }
|
||||
void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||
props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Distortion);
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -204,66 +190,66 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool DistortionCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
|
||||
&& mEaxProps.mDistortion.lGain == props.mDistortion.lGain
|
||||
&& mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
|
||||
&& mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
|
||||
&& mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
|
||||
if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Distortion.Edge = props.mDistortion.flEdge;
|
||||
mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
|
||||
mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
|
||||
mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
|
||||
mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
|
||||
mAlProps = [&]{
|
||||
DistortionProps ret{};
|
||||
ret.Edge = props.flEdge;
|
||||
ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain));
|
||||
ret.LowpassCutoff = props.flLowPassCutOff;
|
||||
ret.EQCenter = props.flEQCenter;
|
||||
ret.EQBandwidth = props.flEdge;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Distortion;
|
||||
props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
static constexpr EAXDISTORTIONPROPERTIES defprops{[]
|
||||
{
|
||||
EAXDISTORTIONPROPERTIES ret{};
|
||||
ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
ret.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
|
||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
|
||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -19,102 +20,87 @@ namespace {
|
|||
static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
|
||||
static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
|
||||
|
||||
void Echo_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EchoProps props{};
|
||||
props.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||
props.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
||||
props.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||
props.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||
props.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||
|
||||
void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||
void Echo_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
|
||||
props->Echo.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
|
||||
props->Echo.LRDelay = val;
|
||||
props.LRDelay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
|
||||
props->Echo.Damping = val;
|
||||
props.Damping = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
|
||||
props->Echo.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
|
||||
props->Echo.Spread = val;
|
||||
props.Spread = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Echo_setParamf(props, param, vals[0]); }
|
||||
void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Echo_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||
void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
*val = props->Echo.Delay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
*val = props->Echo.LRDelay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
*val = props->Echo.Damping;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
*val = props->Echo.Feedback;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
*val = props->Echo.Spread;
|
||||
break;
|
||||
case AL_ECHO_DELAY: *val = props.Delay; break;
|
||||
case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
|
||||
case AL_ECHO_DAMPING: *val = props.Damping; break;
|
||||
case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_ECHO_SPREAD: *val = props.Spread; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Echo_getParamf(props, param, vals); }
|
||||
void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||
props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
||||
props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||
props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||
props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Echo);
|
||||
|
||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -201,66 +187,66 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool EchoCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
|
||||
&& mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
|
||||
&& mEaxProps.mEcho.flDamping == props.mEcho.flDamping
|
||||
&& mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
|
||||
&& mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
|
||||
if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Echo.Delay = props.mEcho.flDelay;
|
||||
mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
|
||||
mAlProps.Echo.Damping = props.mEcho.flDamping;
|
||||
mAlProps.Echo.Feedback = props.mEcho.flFeedback;
|
||||
mAlProps.Echo.Spread = props.mEcho.flSpread;
|
||||
mAlProps = [&]{
|
||||
EchoProps ret{};
|
||||
ret.Delay = props.flDelay;
|
||||
ret.LRDelay = props.flLRDelay;
|
||||
ret.Damping = props.flDamping;
|
||||
ret.Feedback = props.flFeedback;
|
||||
ret.Spread = props.flSpread;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxEchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Echo;
|
||||
props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
static constexpr EAXECHOPROPERTIES defprops{[]
|
||||
{
|
||||
EAXECHOPROPERTIES ret{};
|
||||
ret.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
ret.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
ret.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
|
||||
case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
|
||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
|
||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
|
||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
|
||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
|
||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
|
||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
|
||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
#include <cassert>
|
||||
#include "AL/efx.h"
|
||||
#include "effects.h"
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -1,52 +1,47 @@
|
|||
#ifndef AL_EFFECTS_EFFECTS_H
|
||||
#define AL_EFFECTS_EFFECTS_H
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "core/except.h"
|
||||
#include "al/error.h"
|
||||
#include "core/effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax/effect.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
union EffectProps;
|
||||
|
||||
|
||||
class effect_exception final : public al::base_exception {
|
||||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
effect_exception(ALenum code, const char *msg, ...);
|
||||
~effect_exception() override;
|
||||
|
||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
#define DECL_HANDLER(N, T) \
|
||||
struct N { \
|
||||
using prop_type = T; \
|
||||
\
|
||||
static void SetParami(prop_type &props, ALenum param, int val); \
|
||||
static void SetParamiv(prop_type &props, ALenum param, const int *vals); \
|
||||
static void SetParamf(prop_type &props, ALenum param, float val); \
|
||||
static void SetParamfv(prop_type &props, ALenum param, const float *vals);\
|
||||
static void GetParami(const prop_type &props, ALenum param, int *val); \
|
||||
static void GetParamiv(const prop_type &props, ALenum param, int *vals); \
|
||||
static void GetParamf(const prop_type &props, ALenum param, float *val); \
|
||||
static void GetParamfv(const prop_type &props, ALenum param, float *vals);\
|
||||
};
|
||||
DECL_HANDLER(NullEffectHandler, std::monostate)
|
||||
DECL_HANDLER(ReverbEffectHandler, ReverbProps)
|
||||
DECL_HANDLER(StdReverbEffectHandler, ReverbProps)
|
||||
DECL_HANDLER(AutowahEffectHandler, AutowahProps)
|
||||
DECL_HANDLER(ChorusEffectHandler, ChorusProps)
|
||||
DECL_HANDLER(CompressorEffectHandler, CompressorProps)
|
||||
DECL_HANDLER(DistortionEffectHandler, DistortionProps)
|
||||
DECL_HANDLER(EchoEffectHandler, EchoProps)
|
||||
DECL_HANDLER(EqualizerEffectHandler, EqualizerProps)
|
||||
DECL_HANDLER(FlangerEffectHandler, ChorusProps)
|
||||
DECL_HANDLER(FshifterEffectHandler, FshifterProps)
|
||||
DECL_HANDLER(ModulatorEffectHandler, ModulatorProps)
|
||||
DECL_HANDLER(PshifterEffectHandler, PshifterProps)
|
||||
DECL_HANDLER(VmorpherEffectHandler, VmorpherProps)
|
||||
DECL_HANDLER(DedicatedDialogEffectHandler, DedicatedProps)
|
||||
DECL_HANDLER(DedicatedLfeEffectHandler, DedicatedProps)
|
||||
DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps)
|
||||
#undef DECL_HANDLER
|
||||
|
||||
|
||||
struct EffectVtable {
|
||||
void (*const setParami)(EffectProps *props, ALenum param, int val);
|
||||
void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals);
|
||||
void (*const setParamf)(EffectProps *props, ALenum param, float val);
|
||||
void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals);
|
||||
|
||||
void (*const getParami)(const EffectProps *props, ALenum param, int *val);
|
||||
void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals);
|
||||
void (*const getParamf)(const EffectProps *props, ALenum param, float *val);
|
||||
void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECT_VTABLE(T) \
|
||||
const EffectVtable T##EffectVtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
using effect_exception = al::context_error;
|
||||
|
||||
|
||||
/* Default properties for the given effect types. */
|
||||
|
|
@ -64,25 +59,8 @@ extern const EffectProps FshifterEffectProps;
|
|||
extern const EffectProps ModulatorEffectProps;
|
||||
extern const EffectProps PshifterEffectProps;
|
||||
extern const EffectProps VmorpherEffectProps;
|
||||
extern const EffectProps DedicatedEffectProps;
|
||||
extern const EffectProps DedicatedDialogEffectProps;
|
||||
extern const EffectProps DedicatedLfeEffectProps;
|
||||
extern const EffectProps ConvolutionEffectProps;
|
||||
|
||||
/* Vtables to get/set properties for the given effect types. */
|
||||
extern const EffectVtable NullEffectVtable;
|
||||
extern const EffectVtable ReverbEffectVtable;
|
||||
extern const EffectVtable StdReverbEffectVtable;
|
||||
extern const EffectVtable AutowahEffectVtable;
|
||||
extern const EffectVtable ChorusEffectVtable;
|
||||
extern const EffectVtable CompressorEffectVtable;
|
||||
extern const EffectVtable DistortionEffectVtable;
|
||||
extern const EffectVtable EchoEffectVtable;
|
||||
extern const EffectVtable EqualizerEffectVtable;
|
||||
extern const EffectVtable FlangerEffectVtable;
|
||||
extern const EffectVtable FshifterEffectVtable;
|
||||
extern const EffectVtable ModulatorEffectVtable;
|
||||
extern const EffectVtable PshifterEffectVtable;
|
||||
extern const EffectVtable VmorpherEffectVtable;
|
||||
extern const EffectVtable DedicatedEffectVtable;
|
||||
extern const EffectVtable ConvolutionEffectVtable;
|
||||
|
||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,163 +17,133 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Equalizer_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EqualizerProps props{};
|
||||
props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||
props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
||||
props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
||||
props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
||||
props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
||||
props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
||||
props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
||||
props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||
props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||
props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||
|
||||
void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||
void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EqualizerEffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
|
||||
props->Equalizer.LowGain = val;
|
||||
props.LowGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
|
||||
props->Equalizer.LowCutoff = val;
|
||||
props.LowCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
|
||||
props->Equalizer.Mid1Gain = val;
|
||||
props.Mid1Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
|
||||
props->Equalizer.Mid1Center = val;
|
||||
props.Mid1Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
|
||||
props->Equalizer.Mid1Width = val;
|
||||
props.Mid1Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
|
||||
props->Equalizer.Mid2Gain = val;
|
||||
props.Mid2Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
|
||||
props->Equalizer.Mid2Center = val;
|
||||
props.Mid2Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
|
||||
props->Equalizer.Mid2Width = val;
|
||||
props.Mid2Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
|
||||
props->Equalizer.HighGain = val;
|
||||
props.HighGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
|
||||
props->Equalizer.HighCutoff = val;
|
||||
props.HighCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Equalizer_setParamf(props, param, vals[0]); }
|
||||
void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Equalizer_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||
void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EqualizerEffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
*val = props->Equalizer.LowGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
*val = props->Equalizer.LowCutoff;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
*val = props->Equalizer.Mid1Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
*val = props->Equalizer.Mid1Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
*val = props->Equalizer.Mid1Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
*val = props->Equalizer.Mid2Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
*val = props->Equalizer.Mid2Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
*val = props->Equalizer.Mid2Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
*val = props->Equalizer.HighGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
*val = props->Equalizer.HighCutoff;
|
||||
break;
|
||||
case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break;
|
||||
case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break;
|
||||
case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break;
|
||||
case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break;
|
||||
case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break;
|
||||
case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break;
|
||||
case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break;
|
||||
case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break;
|
||||
case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break;
|
||||
case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Equalizer_getParamf(props, param, vals); }
|
||||
void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||
props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
||||
props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
||||
props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
||||
props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
||||
props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
||||
props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
||||
props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||
props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||
props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Equalizer);
|
||||
|
||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -319,91 +290,86 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool EqualizerCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
|
||||
&& mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
|
||||
&& mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
|
||||
&& mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
|
||||
&& mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
|
||||
&& mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
|
||||
&& mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
|
||||
&& mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
|
||||
&& mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
|
||||
&& mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
|
||||
if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
|
||||
mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
|
||||
mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
|
||||
mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
|
||||
mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
|
||||
mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
|
||||
mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
|
||||
mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
|
||||
mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
|
||||
mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
|
||||
mAlProps = [&]{
|
||||
EqualizerProps ret{};
|
||||
ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain));
|
||||
ret.LowCutoff = props.flLowCutOff;
|
||||
ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain));
|
||||
ret.Mid1Center = props.flMid1Center;
|
||||
ret.Mid1Width = props.flMid1Width;
|
||||
ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain));
|
||||
ret.Mid2Center = props.flMid2Center;
|
||||
ret.Mid2Width = props.flMid2Width;
|
||||
ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain));
|
||||
ret.HighCutoff = props.flHighCutOff;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Equalizer;
|
||||
props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||
props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||
props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||
props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||
props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||
props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||
props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||
props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||
props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||
props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||
static constexpr EAXEQUALIZERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXEQUALIZERPROPERTIES ret{};
|
||||
ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||
ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||
ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||
ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||
ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||
ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||
ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||
ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||
ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||
ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE: break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
|
||||
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE: break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
|
||||
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
||||
constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -28,9 +29,9 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
|||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromDirection(FShifterDirection dir)
|
||||
constexpr ALenum EnumFromDirection(FShifterDirection dir)
|
||||
{
|
||||
switch(dir)
|
||||
{
|
||||
|
|
@ -41,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir)
|
|||
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
|
||||
}
|
||||
|
||||
void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||
props->Fshifter.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
FshifterProps props{};
|
||||
props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value();
|
||||
props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value();
|
||||
return props;
|
||||
}
|
||||
void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Fshifter_setParamf(props, param, vals[0]); }
|
||||
|
||||
void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.LeftDirection = *diropt;
|
||||
props.LeftDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter left direction: 0x%04x", val};
|
||||
|
|
@ -73,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.RightDirection = *diropt;
|
||||
props.RightDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter right direction: 0x%04x", val};
|
||||
|
|
@ -84,33 +80,17 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Fshifter_setParami(props, param, vals[0]); }
|
||||
void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = EnumFromDirection(props->Fshifter.LeftDirection);
|
||||
break;
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
*val = EnumFromDirection(props->Fshifter.RightDirection);
|
||||
break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM,
|
||||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Fshifter_getParami(props, param, vals); }
|
||||
|
||||
void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
*val = props->Fshifter.Frequency;
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||
props.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -118,23 +98,44 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Fshifter_getParamf(props, param, vals); }
|
||||
void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
|
||||
props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = EnumFromDirection(props.LeftDirection);
|
||||
break;
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
*val = EnumFromDirection(props.RightDirection);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM,
|
||||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void FshifterEffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
|
||||
} // namespace
|
||||
void FshifterEffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
*val = props.Frequency;
|
||||
break;
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Fshifter);
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void FshifterEffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -197,13 +198,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
|
||||
&& mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
|
||||
&& mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
|
||||
if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -217,46 +214,52 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
|||
return FShifterDirection::Off;
|
||||
};
|
||||
|
||||
mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
|
||||
mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
|
||||
mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
|
||||
mAlProps = [&]{
|
||||
FshifterProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.LeftDirection = get_direction(props.ulLeftDirection);
|
||||
ret.RightDirection = get_direction(props.ulRightDirection);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::FrequencyShifter;
|
||||
props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXFREQUENCYSHIFTERPROPERTIES ret{};
|
||||
ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -28,9 +29,9 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
|||
case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
|
||||
case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
|
|||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Modulator_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
||||
props->Modulator.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
ModulatorProps props{};
|
||||
props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Modulator_setParamf(props, param, vals[0]); }
|
||||
void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
void ModulatorEffectHandler::SetParami(ModulatorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
Modulator_setParamf(props, param, static_cast<float>(val));
|
||||
SetParamf(props, param, static_cast<float>(val));
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Modulator.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -85,62 +77,61 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Modulator_setParami(props, param, vals[0]); }
|
||||
void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = static_cast<int>(props->Modulator.Frequency);
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = static_cast<int>(props->Modulator.HighPassCutoff);
|
||||
break;
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Modulator.Waveform);
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Modulator_getParami(props, param, vals); }
|
||||
void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = props->Modulator.HighPassCutoff;
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
||||
props.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Modulator_getParamf(props, param, vals); }
|
||||
void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
|
||||
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void ModulatorEffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void ModulatorEffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break;
|
||||
|
||||
} // namespace
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Modulator);
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -203,13 +194,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
|
||||
&& mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
|
||||
&& mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
|
||||
if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -225,46 +212,52 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
|||
return ModulatorWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
|
||||
mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
|
||||
mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
|
||||
mAlProps = [&]{
|
||||
ModulatorProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.HighPassCutoff = props.flHighPassCutOff;
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Modulator;
|
||||
props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
|
||||
{
|
||||
EAXRINGMODULATORPROPERTIES ret{};
|
||||
ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
switch (call.get_property_id())
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,94 +8,92 @@
|
|||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_setParami(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_setParamf(props, param, vals[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_getParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Null_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
Null_getParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
return props;
|
||||
return std::monostate{};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Null);
|
||||
|
||||
const EffectProps NullEffectProps{genDefaultProps()};
|
||||
|
||||
void NullEffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParami(props, param, *vals);
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SetParamf(props, param, *vals);
|
||||
}
|
||||
}
|
||||
|
||||
void NullEffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParami(props, param, vals);
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void NullEffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
GetParamf(props, param, vals);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -117,30 +115,26 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool NullCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxNullCommitter::commit(const std::monostate &props)
|
||||
{
|
||||
const bool ret{props.mType != mEaxProps.mType};
|
||||
const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
|
||||
mEaxProps = props;
|
||||
mAlProps = std::monostate{};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props = EaxEffectProps{};
|
||||
props.mType = EaxEffectType::None;
|
||||
props = std::monostate{};
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
|
||||
void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
|
||||
void EaxNullCommitter::Set(const EaxCall &call, std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,28 +17,32 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Pshifter_setParamf(EffectProps*, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
PshifterProps props{};
|
||||
props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void PshifterEffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
|
||||
props->Pshifter.CoarseTune = val;
|
||||
props.CoarseTune = val;
|
||||
break;
|
||||
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
|
||||
props->Pshifter.FineTune = val;
|
||||
props.FineTune = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -45,49 +50,40 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Pshifter_setParami(props, param, vals[0]); }
|
||||
void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void PshifterEffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
*val = props->Pshifter.CoarseTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
*val = props->Pshifter.FineTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Pshifter_getParami(props, param, vals); }
|
||||
void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
|
||||
void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Pshifter);
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -138,52 +134,48 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool PitchShifterCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
|
||||
&& mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
|
||||
if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
|
||||
mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
|
||||
mAlProps = [&]{
|
||||
PshifterProps ret{};
|
||||
ret.CoarseTune = static_cast<int>(props.lCoarseTune);
|
||||
ret.FineTune = static_cast<int>(props.lFineTune);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::PitchShifter;
|
||||
props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
|
||||
props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
|
||||
props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
|
||||
EAXPITCHSHIFTER_DEFAULTFINETUNE};
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
||||
constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
|
||||
return VMorpherPhenome::x
|
||||
|
|
@ -57,10 +57,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
|||
HANDLE_PHENOME(V);
|
||||
HANDLE_PHENOME(Z);
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
#undef HANDLE_PHENOME
|
||||
}
|
||||
ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
|
||||
switch(phenome)
|
||||
|
|
@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
|||
#undef HANDLE_PHENOME
|
||||
}
|
||||
|
||||
al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -108,9 +108,9 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
|||
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
|
|||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
VmorpherProps props{};
|
||||
props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value();
|
||||
props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value();
|
||||
props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeA = *phenomeopt;
|
||||
props.PhonemeA = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeACoarseTuning = val;
|
||||
props.PhonemeACoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeB = *phenomeopt;
|
||||
props.PhonemeB = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeBCoarseTuning = val;
|
||||
props.PhonemeBCoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Vmorpher.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void VmorpherEffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
|
||||
props->Vmorpher.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Vmorpher_setParamf(props, param, vals[0]); }
|
||||
void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
|
||||
void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeA);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeACoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeB);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeBCoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Vmorpher.Waveform);
|
||||
break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void VmorpherEffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
*val = props->Vmorpher.Rate;
|
||||
*val = props.Rate;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -234,26 +236,9 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Vmorpher_getParamf(props, param, vals); }
|
||||
void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
|
||||
props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
|
||||
props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Vmorpher);
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -352,16 +337,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
|
||||
&& mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
|
||||
&& mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
|
||||
&& mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
|
||||
&& mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
|
||||
&& mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
|
||||
if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -413,107 +391,65 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
|||
return VMorpherWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
|
||||
mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
|
||||
mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
|
||||
mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
|
||||
mAlProps = [&]{
|
||||
VmorpherProps ret{};
|
||||
ret.PhonemeA = get_phoneme(props.ulPhonemeA);
|
||||
ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
|
||||
ret.PhonemeB = get_phoneme(props.ulPhonemeB);
|
||||
ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning);
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
ret.Rate = props.flRate;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::VocalMorpher;
|
||||
props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXVOCALMORPHERPROPERTIES ret{};
|
||||
ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
call.set_value<Exception>(props.mVocalMorpher);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
call.set_value<Exception>(props.mVocalMorpher.flRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
fail_unknown_property_id();
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
defer<AllValidator>(call, props.mVocalMorpher);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
defer<RateValidator>(call, props.mVocalMorpher.flRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
fail_unknown_property_id();
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
|
@ -29,27 +31,45 @@
|
|||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "al/debug.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
#include "strutils.h"
|
||||
|
||||
|
||||
bool TrapALError{false};
|
||||
namespace al {
|
||||
context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
}
|
||||
context_error::~context_error() = default;
|
||||
} /* namespace al */
|
||||
|
||||
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||
{
|
||||
auto message = al::vector<char>(256);
|
||||
auto message = std::vector<char>(256);
|
||||
|
||||
va_list args, args2;
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args, args2;
|
||||
va_start(args, msg);
|
||||
va_copy(args2, args);
|
||||
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
|
||||
|
|
@ -60,9 +80,15 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
|||
}
|
||||
va_end(args2);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
|
||||
if(msglen >= 0) msg = message.data();
|
||||
else msg = "<internal error constructing message>";
|
||||
if(msglen >= 0)
|
||||
msg = message.data();
|
||||
else
|
||||
{
|
||||
msg = "<internal error constructing message>";
|
||||
msglen = static_cast<int>(strlen(msg));
|
||||
}
|
||||
|
||||
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
|
||||
decltype(std::declval<void*>()){this}, errorCode, msg);
|
||||
|
|
@ -77,30 +103,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
|||
#endif
|
||||
}
|
||||
|
||||
ALenum curerr{AL_NO_ERROR};
|
||||
mLastError.compare_exchange_strong(curerr, errorCode);
|
||||
if(mLastThreadError.get() == AL_NO_ERROR)
|
||||
mLastThreadError.set(errorCode);
|
||||
|
||||
debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
|
||||
{msg, static_cast<uint>(msglen)});
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
START_API_FUNC
|
||||
/* Special-case alGetError since it (potentially) raises a debug signal and
|
||||
* returns a non-default value for a null context.
|
||||
*/
|
||||
AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY
|
||||
{
|
||||
static constexpr ALenum deferror{AL_INVALID_OPERATION};
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return deferror;
|
||||
}
|
||||
if(auto context = GetContextRef()) LIKELY
|
||||
return alGetErrorDirect(context.get());
|
||||
|
||||
return context->mLastError.exchange(AL_NO_ERROR);
|
||||
auto get_value = [](const char *envname, const char *optname) -> ALenum
|
||||
{
|
||||
auto optstr = al::getenv(envname);
|
||||
if(!optstr)
|
||||
optstr = ConfigValueStr({}, "game_compat", optname);
|
||||
if(optstr)
|
||||
{
|
||||
char *end{};
|
||||
auto value = std::strtoul(optstr->c_str(), &end, 0);
|
||||
if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max())
|
||||
return static_cast<ALenum>(value);
|
||||
ERR("Invalid default error value: \"%s\"", optstr->c_str());
|
||||
}
|
||||
return AL_INVALID_OPERATION;
|
||||
};
|
||||
static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
|
||||
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return deferror;
|
||||
}
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
|
||||
{
|
||||
ALenum ret{context->mLastThreadError.get()};
|
||||
if(ret != AL_NO_ERROR) UNLIKELY
|
||||
context->mLastThreadError.set(AL_NO_ERROR);
|
||||
return ret;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
27
Engine/lib/openal-soft/al/error.h
Normal file
27
Engine/lib/openal-soft/al/error.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef AL_ERROR_H
|
||||
#define AL_ERROR_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "core/except.h"
|
||||
|
||||
namespace al {
|
||||
|
||||
class context_error final : public al::base_exception {
|
||||
ALenum mErrorCode{};
|
||||
|
||||
public:
|
||||
#ifdef __MINGW32__
|
||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
context_error(ALenum code, const char *msg, ...);
|
||||
~context_error() final;
|
||||
|
||||
[[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; }
|
||||
};
|
||||
|
||||
} /* namespace al */
|
||||
|
||||
#endif /* AL_ERROR_H */
|
||||
|
|
@ -3,35 +3,49 @@
|
|||
|
||||
#include "event.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alsem.h"
|
||||
#include "alspan.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/except.h"
|
||||
#include "core/context.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "debug.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
static int EventThread(ALCcontext *context)
|
||||
namespace {
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
int EventThread(ALCcontext *context)
|
||||
{
|
||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||
bool quitnow{false};
|
||||
|
|
@ -44,76 +58,98 @@ static int EventThread(ALCcontext *context)
|
|||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
do {
|
||||
auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
|
||||
evt_data.buf += sizeof(AsyncEvent);
|
||||
evt_data.len -= 1;
|
||||
|
||||
AsyncEvent evt{*evt_ptr};
|
||||
al::destroy_at(evt_ptr);
|
||||
ring->readAdvance(1);
|
||||
|
||||
quitnow = evt.EnumType == AsyncEvent::KillThread;
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
|
||||
evt_data.len};
|
||||
for(auto &event : evt_span)
|
||||
{
|
||||
quitnow = std::holds_alternative<AsyncKillThread>(event);
|
||||
if(quitnow) UNLIKELY break;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
|
||||
{
|
||||
al::intrusive_ptr<EffectState>{evt.u.mEffectState};
|
||||
continue;
|
||||
}
|
||||
|
||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
|
||||
if(!context->mEventCb || !enabledevts.test(evt.EnumType))
|
||||
continue;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::SourceStateChange)
|
||||
auto proc_killthread = [](AsyncKillThread&) { };
|
||||
auto proc_release = [](AsyncEffectReleaseEvent &evt)
|
||||
{
|
||||
al::intrusive_ptr<EffectState>{evt.mEffectState};
|
||||
};
|
||||
auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
|
||||
{
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
|
||||
return;
|
||||
|
||||
ALuint state{};
|
||||
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
|
||||
std::string msg{"Source ID " + std::to_string(evt.mId)};
|
||||
msg += " state has changed to ";
|
||||
switch(evt.u.srcstate.state)
|
||||
switch(evt.mState)
|
||||
{
|
||||
case AsyncEvent::SrcState::Reset:
|
||||
case AsyncSrcState::Reset:
|
||||
msg += "AL_INITIAL";
|
||||
state = AL_INITIAL;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Stop:
|
||||
case AsyncSrcState::Stop:
|
||||
msg += "AL_STOPPED";
|
||||
state = AL_STOPPED;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Play:
|
||||
case AsyncSrcState::Play:
|
||||
msg += "AL_PLAYING";
|
||||
state = AL_PLAYING;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Pause:
|
||||
case AsyncSrcState::Pause:
|
||||
msg += "AL_PAUSED";
|
||||
state = AL_PAUSED;
|
||||
break;
|
||||
}
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::BufferCompleted)
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
|
||||
{
|
||||
std::string msg{std::to_string(evt.u.bufcomp.count)};
|
||||
if(evt.u.bufcomp.count == 1) msg += " buffer completed";
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
|
||||
return;
|
||||
|
||||
std::string msg{std::to_string(evt.mCount)};
|
||||
if(evt.mCount == 1) msg += " buffer completed";
|
||||
else msg += " buffers completed";
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
|
||||
evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
|
||||
context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::Disconnected)
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
|
||||
{
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
|
||||
context->mEventParam);
|
||||
}
|
||||
} while(evt_data.len != 0);
|
||||
context->debugMessage(DebugSource::System, DebugType::Error, 0,
|
||||
DebugSeverity::High, evt.msg);
|
||||
|
||||
if(context->mEventCb
|
||||
&& enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
|
||||
context->mEventParam);
|
||||
};
|
||||
|
||||
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
||||
proc_killthread}, event);
|
||||
}
|
||||
std::destroy(evt_span.begin(), evt_span.end());
|
||||
ring->readAdvance(evt_span.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
|
||||
{
|
||||
switch(etype)
|
||||
{
|
||||
case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
|
||||
case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
|
||||
case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void StartEventThrd(ALCcontext *ctx)
|
||||
{
|
||||
try {
|
||||
|
|
@ -138,7 +174,7 @@ void StopEventThrd(ALCcontext *ctx)
|
|||
evt_data = ring->getWriteVector().first;
|
||||
} while(evt_data.len == 0);
|
||||
}
|
||||
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
|
||||
std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
|
||||
ring->writeAdvance(1);
|
||||
|
||||
ctx->mEventSem.post();
|
||||
|
|
@ -146,34 +182,25 @@ void StopEventThrd(ALCcontext *ctx)
|
|||
ctx->mEventThread.join();
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
|
||||
const ALenum *types, ALboolean enable) noexcept
|
||||
try {
|
||||
if(count < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
|
||||
if(count <= 0) UNLIKELY return;
|
||||
|
||||
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
|
||||
if(count <= 0) return;
|
||||
if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
if(!types)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ContextBase::AsyncEventBitset flags{};
|
||||
const ALenum *types_end = types+count;
|
||||
auto bad_type = std::find_if_not(types, types_end,
|
||||
[&flags](ALenum type) noexcept -> bool
|
||||
{
|
||||
if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
|
||||
flags.set(AsyncEvent::BufferCompleted);
|
||||
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
|
||||
flags.set(AsyncEvent::SourceStateChange);
|
||||
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
|
||||
flags.set(AsyncEvent::Disconnected);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
if(bad_type != types_end)
|
||||
return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
|
||||
for(ALenum evttype : al::span{types, static_cast<uint>(count)})
|
||||
{
|
||||
auto etype = GetEventType(evttype);
|
||||
if(!etype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
|
||||
flags.set(al::to_underlying(*etype));
|
||||
}
|
||||
|
||||
if(enable)
|
||||
{
|
||||
|
|
@ -196,20 +223,18 @@ START_API_FUNC
|
|||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
|
||||
ALEVENTPROCSOFT callback, void *userParam) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> __{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
context->mEventCb = callback;
|
||||
context->mEventParam = userParam;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
|
|
@ -20,63 +20,59 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "direct_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return AL_FALSE;
|
||||
|
||||
if(!extName) UNLIKELY
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
size_t len{strlen(extName)};
|
||||
const char *ptr{context->mExtensionList};
|
||||
while(ptr && *ptr)
|
||||
const std::string_view tofind{extName};
|
||||
for(std::string_view ext : context->mExtensions)
|
||||
{
|
||||
if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
|
||||
if(al::case_compare(ext, tofind) == 0)
|
||||
return AL_TRUE;
|
||||
|
||||
if((ptr=strchr(ptr, ' ')) != nullptr)
|
||||
{
|
||||
do {
|
||||
++ptr;
|
||||
} while(isspace(*ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
|
||||
START_API_FUNC
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!funcName) return nullptr;
|
||||
return alcGetProcAddress(nullptr, funcName);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
|
||||
START_API_FUNC
|
||||
FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!enumName) return static_cast<ALenum>(0);
|
||||
if(!funcName) return nullptr;
|
||||
return alcGetProcAddress(nullptr, funcName);
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
|
||||
{
|
||||
if(!enumName) return ALenum{0};
|
||||
return alcGetEnumValue(nullptr, enumName);
|
||||
}
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept
|
||||
{
|
||||
if(!enumName) return ALenum{0};
|
||||
return alcGetEnumValue(nullptr, enumName);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,40 @@
|
|||
#ifndef AL_FILTER_H
|
||||
#define AL_FILTER_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
|
||||
#define LOWPASSFREQREF 5000.0f
|
||||
#define HIGHPASSFREQREF 250.0f
|
||||
|
||||
inline constexpr float LowPassFreqRef{5000.0f};
|
||||
inline constexpr float HighPassFreqRef{250.0f};
|
||||
|
||||
template<typename T>
|
||||
struct FilterTable {
|
||||
static void setParami(struct ALfilter*, ALenum, int);
|
||||
static void setParamiv(struct ALfilter*, ALenum, const int*);
|
||||
static void setParamf(struct ALfilter*, ALenum, float);
|
||||
static void setParamfv(struct ALfilter*, ALenum, const float*);
|
||||
|
||||
static void getParami(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamiv(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamf(const struct ALfilter*, ALenum, float*);
|
||||
static void getParamfv(const struct ALfilter*, ALenum, float*);
|
||||
};
|
||||
|
||||
struct NullFilterTable : public FilterTable<NullFilterTable> { };
|
||||
struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
|
||||
struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
|
||||
struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
|
||||
|
||||
|
||||
struct ALfilter {
|
||||
|
|
@ -17,36 +42,35 @@ struct ALfilter {
|
|||
|
||||
float Gain{1.0f};
|
||||
float GainHF{1.0f};
|
||||
float HFReference{LOWPASSFREQREF};
|
||||
float HFReference{LowPassFreqRef};
|
||||
float GainLF{1.0f};
|
||||
float LFReference{HIGHPASSFREQREF};
|
||||
float LFReference{HighPassFreqRef};
|
||||
|
||||
struct Vtable {
|
||||
void (*const setParami )(ALfilter *filter, ALenum param, int val);
|
||||
void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
|
||||
void (*const setParamf )(ALfilter *filter, ALenum param, float val);
|
||||
void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
|
||||
|
||||
void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
|
||||
void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
|
||||
void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
|
||||
void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
|
||||
};
|
||||
const Vtable *vtab{nullptr};
|
||||
using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
|
||||
BandpassFilterTable>;
|
||||
TableTypes mTypeVariant;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
|
||||
void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
|
||||
void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
|
||||
void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
|
||||
void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
|
||||
void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
|
||||
void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
|
||||
void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
struct FilterSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALfilter,64>*> Filters{nullptr};
|
||||
|
||||
FilterSubList() noexcept = default;
|
||||
FilterSubList(const FilterSubList&) = delete;
|
||||
FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
|
||||
~FilterSubList();
|
||||
|
||||
FilterSubList& operator=(const FilterSubList&) = delete;
|
||||
FilterSubList& operator=(FilterSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "listener.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
|
||||
|
|
@ -30,9 +31,10 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alspan.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
|
@ -68,377 +70,333 @@ inline void CommitAndUpdateProps(ALCcontext *context)
|
|||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(!(value >= 0.0f && std::isfinite(value)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
|
||||
listener.Gain = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
UpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
|
||||
listener.mMetersPerUnit = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
||||
UpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
|
||||
ALfloat value2, ALfloat value3) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener position out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
|
||||
listener.Position[0] = value1;
|
||||
listener.Position[1] = value2;
|
||||
listener.Position[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener velocity out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
|
||||
listener.Velocity[0] = value1;
|
||||
listener.Velocity[1] = value2;
|
||||
listener.Velocity[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
const ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerf(param, values[0]);
|
||||
return;
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerfDirect(context, param, *values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, values[0], values[1], values[2]);
|
||||
return;
|
||||
}
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
auto vals = al::span<const float,3>{values, 3_uz};
|
||||
alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(!values) UNLIKELY
|
||||
return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
|
||||
std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])))
|
||||
auto vals = al::span<const float,6>{values, 6_uz};
|
||||
if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); }))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener orientation out of range");
|
||||
/* AT then UP */
|
||||
listener.OrientAt[0] = values[0];
|
||||
listener.OrientAt[1] = values[1];
|
||||
listener.OrientAt[2] = values[2];
|
||||
listener.OrientUp[0] = values[3];
|
||||
listener.OrientUp[1] = values[4];
|
||||
listener.OrientUp[2] = values[5];
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
|
||||
std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
|
||||
AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
|
||||
try {
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1,
|
||||
ALint value2, ALint value3) noexcept
|
||||
try {
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
|
||||
static_cast<ALfloat>(value3));
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(value1),
|
||||
static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param,
|
||||
const ALint *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
al::span<const ALint> vals;
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
vals = {values, 3_uz};
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
|
||||
static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
|
||||
return;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
vals = {values, 6_uz};
|
||||
const std::array fvals{static_cast<ALfloat>(vals[0]), static_cast<ALfloat>(vals[1]),
|
||||
static_cast<ALfloat>(vals[2]), static_cast<ALfloat>(vals[3]),
|
||||
static_cast<ALfloat>(vals[4]), static_cast<ALfloat>(vals[5]),
|
||||
};
|
||||
alListenerfvDirect(context, param, fvals.data());
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||
param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
{
|
||||
ALfloat fvals[6];
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]),
|
||||
static_cast<ALfloat>(values[2]));
|
||||
return;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
fvals[0] = static_cast<ALfloat>(values[0]);
|
||||
fvals[1] = static_cast<ALfloat>(values[1]);
|
||||
fvals[2] = static_cast<ALfloat>(values[2]);
|
||||
fvals[3] = static_cast<ALfloat>(values[3]);
|
||||
fvals[4] = static_cast<ALfloat>(values[4]);
|
||||
fvals[5] = static_cast<ALfloat>(values[5]);
|
||||
alListenerfv(param, fvals);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values) UNLIKELY
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
if(!value)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
*value = listener.Gain;
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
*value = listener.mMetersPerUnit;
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN: *value = listener.Gain; return;
|
||||
case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
|
||||
try {
|
||||
if(!value1 || !value2 || !value3)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = listener.Position[0];
|
||||
*value2 = listener.Position[1];
|
||||
*value3 = listener.Position[2];
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = listener.Velocity[0];
|
||||
*value2 = listener.Velocity[1];
|
||||
*value3 = listener.Velocity[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alGetListenerf(param, values);
|
||||
alGetListenerfDirect(context, param, values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3f(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALfloat,3>{values, 3_uz};
|
||||
alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
al::span<ALfloat,6> vals{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = listener.OrientAt[0];
|
||||
values[1] = listener.OrientAt[1];
|
||||
values[2] = listener.OrientAt[2];
|
||||
values[3] = listener.OrientUp[0];
|
||||
values[4] = listener.OrientUp[1];
|
||||
values[5] = listener.OrientUp[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin());
|
||||
std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
|
||||
try {
|
||||
if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
|
||||
ALint *value1, ALint *value2, ALint *value3) noexcept
|
||||
try {
|
||||
if(!value1 || !value2 || !value3)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value1 || !value2 || !value3)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = static_cast<ALint>(listener.Position[0]);
|
||||
*value2 = static_cast<ALint>(listener.Position[1]);
|
||||
*value3 = static_cast<ALint>(listener.Position[2]);
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = static_cast<ALint>(listener.Velocity[0]);
|
||||
*value2 = static_cast<ALint>(listener.Velocity[1]);
|
||||
*value3 = static_cast<ALint>(listener.Velocity[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
|
||||
ALint *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3i(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALint,3>{values, 3_uz};
|
||||
alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
|
||||
static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
auto vals = al::span<ALint,6>{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = static_cast<ALint>(listener.OrientAt[0]);
|
||||
values[1] = static_cast<ALint>(listener.OrientAt[1]);
|
||||
values[2] = static_cast<ALint>(listener.OrientAt[2]);
|
||||
values[3] = static_cast<ALint>(listener.OrientUp[0]);
|
||||
values[4] = static_cast<ALint>(listener.OrientUp[1]);
|
||||
values[5] = static_cast<ALint>(listener.OrientUp[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i);
|
||||
std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||
param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
|
|
@ -18,7 +16,7 @@ struct ALlistener {
|
|||
float Gain{1.0f};
|
||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,26 +2,26 @@
|
|||
#define AL_SOURCE_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "aldeque.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alnumeric.h"
|
||||
#include "atomic.h"
|
||||
#include "alspan.h"
|
||||
#include "core/context.h"
|
||||
#include "core/voice.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax/api.h"
|
||||
#include "eax/call.h"
|
||||
#include "eax/exception.h"
|
||||
#include "eax/fx_slot_index.h"
|
||||
|
|
@ -30,23 +30,23 @@
|
|||
|
||||
struct ALbuffer;
|
||||
struct ALeffectslot;
|
||||
|
||||
enum class Resampler : uint8_t;
|
||||
|
||||
enum class SourceStereo : bool {
|
||||
Normal = AL_NORMAL_SOFT,
|
||||
Enhanced = AL_SUPER_STEREO_SOFT
|
||||
};
|
||||
|
||||
#define DEFAULT_SENDS 2
|
||||
inline constexpr size_t DefaultSendCount{2};
|
||||
|
||||
#define INVALID_VOICE_IDX static_cast<ALuint>(-1)
|
||||
inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()};
|
||||
|
||||
extern bool sBufferSubDataCompat;
|
||||
inline bool sBufferSubDataCompat{false};
|
||||
|
||||
struct ALbufferQueueItem : public VoiceBufferItem {
|
||||
ALbuffer *mBuffer{nullptr};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -88,6 +88,7 @@ struct ALsource {
|
|||
DirectMode DirectChannels{DirectMode::Off};
|
||||
SpatializeMode mSpatialize{SpatializeMode::Auto};
|
||||
SourceStereo mStereoMode{SourceStereo::Normal};
|
||||
bool mPanningEnabled{false};
|
||||
|
||||
bool DryGainHFAuto{true};
|
||||
bool WetGainAuto{true};
|
||||
|
|
@ -105,24 +106,27 @@ struct ALsource {
|
|||
|
||||
float Radius{0.0f};
|
||||
float EnhWidth{0.593f};
|
||||
float mPan{0.0f};
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
float Gain;
|
||||
float GainHF;
|
||||
float HFReference;
|
||||
float GainLF;
|
||||
float LFReference;
|
||||
} Direct;
|
||||
struct SendData {
|
||||
ALeffectslot *Slot;
|
||||
float Gain;
|
||||
float GainHF;
|
||||
float HFReference;
|
||||
float GainLF;
|
||||
float LFReference;
|
||||
struct DirectData {
|
||||
float Gain{};
|
||||
float GainHF{};
|
||||
float HFReference{};
|
||||
float GainLF{};
|
||||
float LFReference{};
|
||||
};
|
||||
std::array<SendData,MAX_SENDS> Send;
|
||||
DirectData Direct;
|
||||
|
||||
struct SendData {
|
||||
ALeffectslot *Slot{};
|
||||
float Gain{};
|
||||
float GainHF{};
|
||||
float HFReference{};
|
||||
float GainLF{};
|
||||
float LFReference{};
|
||||
};
|
||||
std::array<SendData,MaxSendCount> Send;
|
||||
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
|
|
@ -138,26 +142,28 @@ struct ALsource {
|
|||
ALenum state{AL_INITIAL};
|
||||
|
||||
/** Source Buffer Queue head. */
|
||||
al::deque<ALbufferQueueItem> mQueue;
|
||||
std::deque<ALbufferQueueItem> mQueue;
|
||||
|
||||
bool mPropsDirty{true};
|
||||
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
*/
|
||||
ALuint VoiceIdx{INVALID_VOICE_IDX};
|
||||
ALuint VoiceIdx{InvalidVoiceIndex};
|
||||
|
||||
/** Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
|
||||
ALsource();
|
||||
ALsource() noexcept;
|
||||
~ALsource();
|
||||
|
||||
ALsource(const ALsource&) = delete;
|
||||
ALsource& operator=(const ALsource&) = delete;
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
|
|
@ -171,18 +177,18 @@ public:
|
|||
private:
|
||||
using Exception = EaxSourceException;
|
||||
|
||||
static constexpr auto eax_max_speakers = 9;
|
||||
static constexpr auto eax_max_speakers{9u};
|
||||
|
||||
using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS];
|
||||
using EaxFxSlotIds = std::array<const GUID*,EAX_MAX_FXSLOTS>;
|
||||
|
||||
static constexpr const EaxFxSlotIds eax4_fx_slot_ids = {
|
||||
static constexpr const EaxFxSlotIds eax4_fx_slot_ids{
|
||||
&EAXPROPERTYID_EAX40_FXSlot0,
|
||||
&EAXPROPERTYID_EAX40_FXSlot1,
|
||||
&EAXPROPERTYID_EAX40_FXSlot2,
|
||||
&EAXPROPERTYID_EAX40_FXSlot3,
|
||||
};
|
||||
|
||||
static constexpr const EaxFxSlotIds eax5_fx_slot_ids = {
|
||||
static constexpr const EaxFxSlotIds eax5_fx_slot_ids{
|
||||
&EAXPROPERTYID_EAX50_FXSlot0,
|
||||
&EAXPROPERTYID_EAX50_FXSlot1,
|
||||
&EAXPROPERTYID_EAX50_FXSlot2,
|
||||
|
|
@ -215,11 +221,6 @@ private:
|
|||
Eax3Props source;
|
||||
EaxSends sends;
|
||||
EAX40ACTIVEFXSLOTS active_fx_slots;
|
||||
|
||||
bool operator==(const Eax4Props& rhs) noexcept
|
||||
{
|
||||
return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4State {
|
||||
|
|
@ -232,11 +233,6 @@ private:
|
|||
EaxSends sends;
|
||||
EAX50ACTIVEFXSLOTS active_fx_slots;
|
||||
EaxSpeakerLevels speaker_levels;
|
||||
|
||||
bool operator==(const Eax5Props& rhs) noexcept
|
||||
{
|
||||
return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5State {
|
||||
|
|
@ -546,7 +542,24 @@ private:
|
|||
struct Eax5SourceAllValidator {
|
||||
void operator()(const EAX50SOURCEPROPERTIES& props) const
|
||||
{
|
||||
Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(props));
|
||||
Eax2SourceDirectValidator{}(props.lDirect);
|
||||
Eax2SourceDirectHfValidator{}(props.lDirectHF);
|
||||
Eax2SourceRoomValidator{}(props.lRoom);
|
||||
Eax2SourceRoomHfValidator{}(props.lRoomHF);
|
||||
Eax2SourceObstructionValidator{}(props.lObstruction);
|
||||
Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio);
|
||||
Eax2SourceOcclusionValidator{}(props.lOcclusion);
|
||||
Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio);
|
||||
Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio);
|
||||
Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio);
|
||||
Eax3SourceExclusionValidator{}(props.lExclusion);
|
||||
Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio);
|
||||
Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF);
|
||||
Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor);
|
||||
Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor);
|
||||
Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor);
|
||||
Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor);
|
||||
Eax5SourceFlagsValidator{}(props.ulFlags);
|
||||
Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
|
||||
}
|
||||
};
|
||||
|
|
@ -809,39 +822,38 @@ private:
|
|||
[[noreturn]] static void eax_fail_unknown_active_fx_slot_id();
|
||||
[[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
|
||||
|
||||
void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
||||
void eax1_set_defaults(Eax1Props& props) noexcept;
|
||||
static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
||||
static void eax1_set_defaults(Eax1Props& props) noexcept;
|
||||
void eax1_set_defaults() noexcept;
|
||||
void eax2_set_defaults(Eax2Props& props) noexcept;
|
||||
static void eax2_set_defaults(Eax2Props& props) noexcept;
|
||||
void eax2_set_defaults() noexcept;
|
||||
void eax3_set_defaults(Eax3Props& props) noexcept;
|
||||
static void eax3_set_defaults(Eax3Props& props) noexcept;
|
||||
void eax3_set_defaults() noexcept;
|
||||
void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
||||
static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
||||
void eax4_set_defaults() noexcept;
|
||||
void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
||||
void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
||||
void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
||||
void eax5_set_defaults(Eax5Props& props) noexcept;
|
||||
static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
||||
static void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
||||
static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
||||
static void eax5_set_defaults(Eax5Props& props) noexcept;
|
||||
void eax5_set_defaults() noexcept;
|
||||
void eax_set_defaults() noexcept;
|
||||
|
||||
void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
||||
void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
||||
void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
||||
void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
|
||||
|
||||
static float eax_calculate_dst_occlusion_mb(
|
||||
long src_occlusion_mb,
|
||||
float path_ratio,
|
||||
float lf_ratio) noexcept;
|
||||
|
||||
EaxAlLowPassParam eax_create_direct_filter_param() const noexcept;
|
||||
[[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam;
|
||||
|
||||
EaxAlLowPassParam eax_create_room_filter_param(
|
||||
const ALeffectslot& fx_slot,
|
||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept;
|
||||
[[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot,
|
||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam;
|
||||
|
||||
void eax_update_direct_filter();
|
||||
void eax_update_room_filters();
|
||||
|
|
@ -894,16 +906,16 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count);
|
||||
void eax1_get(const EaxCall& call, const Eax1Props& props);
|
||||
void eax2_get(const EaxCall& call, const Eax2Props& props);
|
||||
void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
|
||||
static void eax1_get(const EaxCall& call, const Eax1Props& props);
|
||||
static void eax2_get(const EaxCall& call, const Eax2Props& props);
|
||||
static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get(const EaxCall& call, const Eax3Props& props);
|
||||
void eax4_get(const EaxCall& call, const Eax4Props& props);
|
||||
void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
||||
void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
||||
static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
||||
static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
||||
void eax5_get(const EaxCall& call, const Eax5Props& props);
|
||||
void eax_get(const EaxCall& call);
|
||||
|
||||
|
|
@ -976,21 +988,21 @@ private:
|
|||
}
|
||||
|
||||
template<typename TValidator, size_t TIdCount>
|
||||
void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
const auto src_ids = call.get_values<const GUID>(TIdCount);
|
||||
std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{});
|
||||
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids);
|
||||
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin());
|
||||
}
|
||||
|
||||
template<size_t TIdCount>
|
||||
void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
|
||||
}
|
||||
|
||||
template<size_t TIdCount>
|
||||
void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
|
||||
}
|
||||
|
|
@ -1022,12 +1034,12 @@ private:
|
|||
void eax_set_efx_wet_gain_auto();
|
||||
void eax_set_efx_wet_gain_hf_auto();
|
||||
|
||||
void eax1_set(const EaxCall& call, Eax1Props& props);
|
||||
void eax2_set(const EaxCall& call, Eax2Props& props);
|
||||
static void eax1_set(const EaxCall& call, Eax1Props& props);
|
||||
static void eax2_set(const EaxCall& call, Eax2Props& props);
|
||||
void eax3_set(const EaxCall& call, Eax3Props& props);
|
||||
void eax4_set(const EaxCall& call, Eax4Props& props);
|
||||
void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
||||
void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
||||
static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
||||
static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
||||
void eax5_set(const EaxCall& call, Eax5Props& props);
|
||||
void eax_set(const EaxCall& call);
|
||||
|
||||
|
|
@ -1041,4 +1053,19 @@ private:
|
|||
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
||||
struct SourceSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALsource,64>*> Sources{nullptr};
|
||||
|
||||
SourceSubList() noexcept = default;
|
||||
SourceSubList(const SourceSubList&) = delete;
|
||||
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||
~SourceSubList();
|
||||
|
||||
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -22,9 +22,6 @@
|
|||
|
||||
#include "alconfig.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
|
|
@ -34,25 +31,47 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "alfstream.h"
|
||||
#include "almalloc.h"
|
||||
#include "alstring.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#if defined(ALSOFT_UWP)
|
||||
#include <winrt/Windows.Media.Core.h> // !!This is important!!
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
using namespace winrt;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
|
||||
struct CoTaskMemDeleter {
|
||||
void operator()(void *mem) const { CoTaskMemFree(mem); }
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ConfigEntry {
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
al::vector<ConfigEntry> ConfOpts;
|
||||
std::vector<ConfigEntry> ConfOpts;
|
||||
|
||||
|
||||
std::string &lstrip(std::string &line)
|
||||
|
|
@ -72,57 +91,48 @@ bool readline(std::istream &f, std::string &output)
|
|||
return std::getline(f, output) && !output.empty();
|
||||
}
|
||||
|
||||
std::string expdup(const char *str)
|
||||
std::string expdup(std::string_view str)
|
||||
{
|
||||
std::string output;
|
||||
|
||||
std::string envval;
|
||||
while(*str != '\0')
|
||||
while(!str.empty())
|
||||
{
|
||||
const char *addstr;
|
||||
size_t addstrlen;
|
||||
|
||||
if(str[0] != '$')
|
||||
if(auto nextpos = str.find('$'))
|
||||
{
|
||||
const char *next = std::strchr(str, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
||||
output += str.substr(0, nextpos);
|
||||
if(nextpos == std::string_view::npos)
|
||||
break;
|
||||
|
||||
str += addstrlen;
|
||||
str.remove_prefix(nextpos);
|
||||
}
|
||||
else
|
||||
|
||||
str.remove_prefix(1);
|
||||
if(str.empty())
|
||||
{
|
||||
str++;
|
||||
if(*str == '$')
|
||||
{
|
||||
const char *next = std::strchr(str+1, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool hasbraces{(*str == '{')};
|
||||
|
||||
if(hasbraces) str++;
|
||||
const char *envstart = str;
|
||||
while(std::isalnum(*str) || *str == '_')
|
||||
++str;
|
||||
if(hasbraces && *str != '}')
|
||||
continue;
|
||||
const std::string envname{envstart, str};
|
||||
if(hasbraces) str++;
|
||||
|
||||
envval = al::getenv(envname.c_str()).value_or(std::string{});
|
||||
addstr = envval.data();
|
||||
addstrlen = envval.length();
|
||||
}
|
||||
output += '$';
|
||||
break;
|
||||
}
|
||||
if(addstrlen == 0)
|
||||
if(str.front() == '$')
|
||||
{
|
||||
output += '$';
|
||||
str.remove_prefix(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
output.append(addstr, addstrlen);
|
||||
const bool hasbraces{str.front() == '{'};
|
||||
if(hasbraces) str.remove_prefix(1);
|
||||
|
||||
size_t envend{0};
|
||||
while(envend < str.size() && (std::isalnum(str[envend]) || str[envend] == '_'))
|
||||
++envend;
|
||||
if(hasbraces && (envend == str.size() || str[envend] != '}'))
|
||||
continue;
|
||||
const std::string envname{str.substr(0, envend)};
|
||||
if(hasbraces) ++envend;
|
||||
str.remove_prefix(envend);
|
||||
|
||||
if(auto envval = al::getenv(envname.c_str()))
|
||||
output += *envval;
|
||||
}
|
||||
|
||||
return output;
|
||||
|
|
@ -140,44 +150,43 @@ void LoadConfigFromFile(std::istream &f)
|
|||
|
||||
if(buffer[0] == '[')
|
||||
{
|
||||
auto line = const_cast<char*>(buffer.data());
|
||||
char *section = line+1;
|
||||
char *endsection;
|
||||
|
||||
endsection = std::strchr(section, ']');
|
||||
if(!endsection || section == endsection)
|
||||
auto endpos = buffer.find(']', 1);
|
||||
if(endpos == 1 || endpos == std::string::npos)
|
||||
{
|
||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
||||
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
if(endsection[1] != 0)
|
||||
if(buffer[endpos+1] != '\0')
|
||||
{
|
||||
char *end = endsection+1;
|
||||
while(std::isspace(*end))
|
||||
++end;
|
||||
if(*end != 0 && *end != '#')
|
||||
size_t last{endpos+1};
|
||||
while(last < buffer.size() && std::isspace(buffer[last]))
|
||||
++last;
|
||||
|
||||
if(last < buffer.size() && buffer[last] != '#')
|
||||
{
|
||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
||||
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*endsection = 0;
|
||||
|
||||
auto section = std::string_view{buffer}.substr(1, endpos-1);
|
||||
|
||||
curSection.clear();
|
||||
if(al::strcasecmp(section, "general") != 0)
|
||||
if(al::case_compare(section, "general"sv) != 0)
|
||||
{
|
||||
do {
|
||||
char *nextp = std::strchr(section, '%');
|
||||
if(!nextp)
|
||||
auto nextp = section.find('%');
|
||||
if(nextp == std::string_view::npos)
|
||||
{
|
||||
curSection += section;
|
||||
break;
|
||||
}
|
||||
|
||||
curSection.append(section, nextp);
|
||||
section = nextp;
|
||||
curSection += section.substr(0, nextp);
|
||||
section.remove_prefix(nextp);
|
||||
|
||||
if(((section[1] >= '0' && section[1] <= '9') ||
|
||||
if(section.size() > 2 &&
|
||||
((section[1] >= '0' && section[1] <= '9') ||
|
||||
(section[1] >= 'a' && section[1] <= 'f') ||
|
||||
(section[1] >= 'A' && section[1] <= 'F')) &&
|
||||
((section[2] >= '0' && section[2] <= '9') ||
|
||||
|
|
@ -198,19 +207,19 @@ void LoadConfigFromFile(std::istream &f)
|
|||
else if(section[2] >= 'A' && section[2] <= 'F')
|
||||
b |= (section[2]-'A'+0x0a);
|
||||
curSection += static_cast<char>(b);
|
||||
section += 3;
|
||||
section.remove_prefix(3);
|
||||
}
|
||||
else if(section[1] == '%')
|
||||
else if(section.size() > 1 && section[1] == '%')
|
||||
{
|
||||
curSection += '%';
|
||||
section += 2;
|
||||
section.remove_prefix(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
curSection += '%';
|
||||
section += 1;
|
||||
section.remove_prefix(1);
|
||||
}
|
||||
} while(*section != 0);
|
||||
} while(!section.empty());
|
||||
}
|
||||
|
||||
continue;
|
||||
|
|
@ -228,16 +237,17 @@ void LoadConfigFromFile(std::istream &f)
|
|||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
auto keyend = sep++;
|
||||
while(keyend > 0 && std::isspace(buffer[keyend-1]))
|
||||
--keyend;
|
||||
if(!keyend)
|
||||
auto keypart = std::string_view{buffer}.substr(0, sep++);
|
||||
while(!keypart.empty() && std::isspace(keypart.back()))
|
||||
keypart.remove_suffix(1);
|
||||
if(keypart.empty())
|
||||
{
|
||||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
while(sep < buffer.size() && std::isspace(buffer[sep]))
|
||||
sep++;
|
||||
auto valpart = std::string_view{buffer}.substr(sep);
|
||||
while(!valpart.empty() && std::isspace(valpart.front()))
|
||||
valpart.remove_prefix(1);
|
||||
|
||||
std::string fullKey;
|
||||
if(!curSection.empty())
|
||||
|
|
@ -245,20 +255,24 @@ void LoadConfigFromFile(std::istream &f)
|
|||
fullKey += curSection;
|
||||
fullKey += '/';
|
||||
}
|
||||
fullKey += buffer.substr(0u, keyend);
|
||||
fullKey += keypart;
|
||||
|
||||
std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}};
|
||||
if(value.size() > 1)
|
||||
if(valpart.size() > size_t{std::numeric_limits<int>::max()})
|
||||
{
|
||||
if((value.front() == '"' && value.back() == '"')
|
||||
|| (value.front() == '\'' && value.back() == '\''))
|
||||
ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
if(valpart.size() > 1)
|
||||
{
|
||||
if((valpart.front() == '"' && valpart.back() == '"')
|
||||
|| (valpart.front() == '\'' && valpart.back() == '\''))
|
||||
{
|
||||
value.pop_back();
|
||||
value.erase(value.begin());
|
||||
valpart.remove_prefix(1);
|
||||
valpart.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str());
|
||||
TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data());
|
||||
|
||||
/* Check if we already have this option set */
|
||||
auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
|
||||
|
|
@ -266,61 +280,49 @@ void LoadConfigFromFile(std::istream &f)
|
|||
auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key);
|
||||
if(ent != ConfOpts.end())
|
||||
{
|
||||
if(!value.empty())
|
||||
ent->value = expdup(value.c_str());
|
||||
if(!valpart.empty())
|
||||
ent->value = expdup(valpart);
|
||||
else
|
||||
ConfOpts.erase(ent);
|
||||
}
|
||||
else if(!value.empty())
|
||||
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())});
|
||||
else if(!valpart.empty())
|
||||
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)});
|
||||
}
|
||||
ConfOpts.shrink_to_fit();
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName)
|
||||
const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName)
|
||||
{
|
||||
if(!keyName)
|
||||
if(keyName.empty())
|
||||
return nullptr;
|
||||
|
||||
std::string key;
|
||||
if(blockName && al::strcasecmp(blockName, "general") != 0)
|
||||
if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
|
||||
{
|
||||
key = blockName;
|
||||
if(devName)
|
||||
{
|
||||
key += '/';
|
||||
key += devName;
|
||||
}
|
||||
key += '/';
|
||||
key += keyName;
|
||||
}
|
||||
else
|
||||
if(!devName.empty())
|
||||
{
|
||||
if(devName)
|
||||
{
|
||||
key = devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
key += devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
|
||||
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
||||
[&key](const ConfigEntry &entry) -> bool
|
||||
{ return entry.key == key; });
|
||||
[&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
|
||||
if(iter != ConfOpts.cend())
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
if(!iter->value.empty())
|
||||
return iter->value.c_str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key.c_str());
|
||||
if(devName.empty())
|
||||
return nullptr;
|
||||
}
|
||||
return GetConfigValue(nullptr, blockName, keyName);
|
||||
return GetConfigValue({}, blockName, keyName);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -329,33 +331,48 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
|
|||
#ifdef _WIN32
|
||||
void ReadALConfig()
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
std::string filepath{wstr_to_utf8(buffer)};
|
||||
filepath += "\\alsoft.ini";
|
||||
namespace fs = std::filesystem;
|
||||
fs::path path;
|
||||
|
||||
TRACE("Loading config %s...\n", filepath.c_str());
|
||||
al::ifstream f{filepath};
|
||||
if(f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
#if !defined(_GAMING_XBOX)
|
||||
{
|
||||
#if !defined(ALSOFT_UWP)
|
||||
std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
|
||||
const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
|
||||
nullptr, al::out_ptr(bufstore))};
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
const std::wstring_view buffer{bufstore.get()};
|
||||
#else
|
||||
winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings();
|
||||
auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
|
||||
std::wstring_view buffer{bufstore};
|
||||
{
|
||||
#endif
|
||||
path = fs::path{buffer};
|
||||
path /= L"alsoft.ini";
|
||||
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ppath{GetProcBinary().path};
|
||||
if(!ppath.empty())
|
||||
path = fs::u8path(GetProcBinary().path);
|
||||
if(!path.empty())
|
||||
{
|
||||
ppath += "\\alsoft.ini";
|
||||
TRACE("Loading config %s...\n", ppath.c_str());
|
||||
al::ifstream f{ppath};
|
||||
if(f.is_open())
|
||||
path /= "alsoft.ini";
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto confpath = al::getenv(L"ALSOFT_CONF"))
|
||||
{
|
||||
TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str());
|
||||
al::ifstream f{*confpath};
|
||||
if(f.is_open())
|
||||
path = *confpath;
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
|
|
@ -364,13 +381,12 @@ void ReadALConfig()
|
|||
|
||||
void ReadALConfig()
|
||||
{
|
||||
const char *str{"/etc/openal/alsoft.conf"};
|
||||
namespace fs = std::filesystem;
|
||||
fs::path path{"/etc/openal/alsoft.conf"};
|
||||
|
||||
TRACE("Loading config %s...\n", str);
|
||||
al::ifstream f{str};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
f.close();
|
||||
|
||||
std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
|
||||
/* Go through the list in reverse, since "the order of base directories
|
||||
|
|
@ -378,48 +394,43 @@ void ReadALConfig()
|
|||
* important". Ergo, we need to load the settings from the later dirs
|
||||
* first so that the settings in the earlier dirs override them.
|
||||
*/
|
||||
std::string fname;
|
||||
while(!confpaths.empty())
|
||||
{
|
||||
auto next = confpaths.find_last_of(':');
|
||||
auto next = confpaths.rfind(':');
|
||||
if(next < confpaths.length())
|
||||
{
|
||||
fname = confpaths.substr(next+1);
|
||||
path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal();
|
||||
confpaths.erase(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
fname = confpaths;
|
||||
path = fs::path{confpaths}.lexically_normal();
|
||||
confpaths.clear();
|
||||
}
|
||||
|
||||
if(fname.empty() || fname.front() != '/')
|
||||
WARN("Ignoring XDG config dir: %s\n", fname.c_str());
|
||||
if(!path.is_absolute())
|
||||
WARN("Ignoring XDG config dir: %s\n", path.u8string().c_str());
|
||||
else
|
||||
{
|
||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
||||
else fname += "alsoft.conf";
|
||||
path /= "alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
fname.clear();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
if(mainBundle)
|
||||
{
|
||||
unsigned char fileName[PATH_MAX];
|
||||
CFURLRef configURL;
|
||||
CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""),
|
||||
nullptr)};
|
||||
|
||||
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) &&
|
||||
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
|
||||
std::array<unsigned char,PATH_MAX> fileName{};
|
||||
if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size()))
|
||||
{
|
||||
f = al::ifstream{reinterpret_cast<char*>(fileName)};
|
||||
if(f.is_open())
|
||||
if(std::ifstream f{reinterpret_cast<char*>(fileName.data())}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,102 +438,100 @@ void ReadALConfig()
|
|||
|
||||
if(auto homedir = al::getenv("HOME"))
|
||||
{
|
||||
fname = *homedir;
|
||||
if(fname.back() != '/') fname += "/.alsoftrc";
|
||||
else fname += ".alsoftrc";
|
||||
path = *homedir;
|
||||
path /= ".alsoftrc";
|
||||
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto configdir = al::getenv("XDG_CONFIG_HOME"))
|
||||
{
|
||||
fname = *configdir;
|
||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
||||
else fname += "alsoft.conf";
|
||||
path = *configdir;
|
||||
path /= "alsoft.conf";
|
||||
}
|
||||
else
|
||||
{
|
||||
fname.clear();
|
||||
path.clear();
|
||||
if(auto homedir = al::getenv("HOME"))
|
||||
{
|
||||
fname = *homedir;
|
||||
if(fname.back() != '/') fname += "/.config/alsoft.conf";
|
||||
else fname += ".config/alsoft.conf";
|
||||
path = *homedir;
|
||||
path /= ".config/alsoft.conf";
|
||||
}
|
||||
}
|
||||
if(!fname.empty())
|
||||
if(!path.empty())
|
||||
{
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
std::string ppath{GetProcBinary().path};
|
||||
if(!ppath.empty())
|
||||
path = GetProcBinary().path;
|
||||
if(!path.empty())
|
||||
{
|
||||
if(ppath.back() != '/') ppath += "/alsoft.conf";
|
||||
else ppath += "alsoft.conf";
|
||||
path /= "alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", ppath.c_str());
|
||||
f = al::ifstream{ppath};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto confname = al::getenv("ALSOFT_CONF"))
|
||||
{
|
||||
TRACE("Loading config %s...\n", confname->c_str());
|
||||
f = al::ifstream{*confname};
|
||||
if(f.is_open())
|
||||
if(std::ifstream f{*confname}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return val;
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return static_cast<int>(std::strtol(val, nullptr, 0));
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return std::strtof(val, nullptr);
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true")==0 || atoi(val) != 0;
|
||||
return al::nullopt;
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
|
||||
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName, bool def)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
|
||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||
return def;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
#ifndef ALCONFIG_H
|
||||
#define ALCONFIG_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "aloptional.h"
|
||||
|
||||
void ReadALConfig();
|
||||
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def);
|
||||
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName, bool def);
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
|
||||
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName);
|
||||
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
|
||||
#endif /* ALCONFIG_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,20 +2,20 @@
|
|||
#define ALU_H
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
struct ALCcontext;
|
||||
struct ALCdevice;
|
||||
struct EffectSlot;
|
||||
|
||||
enum class StereoEncoding : unsigned char;
|
||||
enum class StereoEncoding : std::uint8_t;
|
||||
|
||||
|
||||
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
||||
|
||||
|
||||
enum CompatFlags : uint8_t {
|
||||
enum CompatFlags : std::uint8_t {
|
||||
ReverseX,
|
||||
ReverseY,
|
||||
ReverseZ,
|
||||
|
|
@ -31,7 +31,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale);
|
|||
* Set up the appropriate panning method and mixing method given the device
|
||||
* properties.
|
||||
*/
|
||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode);
|
||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode);
|
||||
|
||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,29 +31,33 @@
|
|||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "albit.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char alsaDevice[] = "ALSA Default";
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
|
|
@ -248,59 +252,97 @@ struct DevMap {
|
|||
{ }
|
||||
};
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
al::vector<DevMap> CaptureDevices;
|
||||
std::vector<DevMap> PlaybackDevices;
|
||||
std::vector<DevMap> CaptureDevices;
|
||||
|
||||
|
||||
const char *prefix_name(snd_pcm_stream_t stream)
|
||||
std::string_view prefix_name(snd_pcm_stream_t stream) noexcept
|
||||
{
|
||||
assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
|
||||
return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
|
||||
if(stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return "device-prefix"sv;
|
||||
return "capture-prefix"sv;
|
||||
}
|
||||
|
||||
al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||
struct SndCtlCardInfo {
|
||||
snd_ctl_card_info_t *mInfo{};
|
||||
|
||||
SndCtlCardInfo() { snd_ctl_card_info_malloc(&mInfo); }
|
||||
~SndCtlCardInfo() { if(mInfo) snd_ctl_card_info_free(mInfo); }
|
||||
SndCtlCardInfo(const SndCtlCardInfo&) = delete;
|
||||
SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_ctl_card_info_t*() const noexcept { return mInfo; }
|
||||
};
|
||||
|
||||
struct SndPcmInfo {
|
||||
snd_pcm_info_t *mInfo{};
|
||||
|
||||
SndPcmInfo() { snd_pcm_info_malloc(&mInfo); }
|
||||
~SndPcmInfo() { if(mInfo) snd_pcm_info_free(mInfo); }
|
||||
SndPcmInfo(const SndPcmInfo&) = delete;
|
||||
SndPcmInfo& operator=(const SndPcmInfo&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_pcm_info_t*() const noexcept { return mInfo; }
|
||||
};
|
||||
|
||||
struct SndCtl {
|
||||
snd_ctl_t *mHandle{};
|
||||
|
||||
SndCtl() = default;
|
||||
~SndCtl() { if(mHandle) snd_ctl_close(mHandle); }
|
||||
SndCtl(const SndCtl&) = delete;
|
||||
SndCtl& operator=(const SndCtl&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); }
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_ctl_t*() const noexcept { return mHandle; }
|
||||
};
|
||||
|
||||
|
||||
std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||
{
|
||||
al::vector<DevMap> devlist;
|
||||
std::vector<DevMap> devlist;
|
||||
|
||||
snd_ctl_card_info_t *info;
|
||||
snd_ctl_card_info_malloc(&info);
|
||||
snd_pcm_info_t *pcminfo;
|
||||
snd_pcm_info_malloc(&pcminfo);
|
||||
SndCtlCardInfo info;
|
||||
SndPcmInfo pcminfo;
|
||||
|
||||
auto defname = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture");
|
||||
devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default");
|
||||
auto defname = ConfigValueStr({}, "alsa"sv,
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device"sv : "capture"sv);
|
||||
devlist.emplace_back(GetDefaultName(), defname ? std::string_view{*defname} : "default"sv);
|
||||
|
||||
if(auto customdevs = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures"))
|
||||
if(auto customdevs = ConfigValueStr({}, "alsa"sv,
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices"sv : "custom-captures"sv))
|
||||
{
|
||||
size_t nextpos{customdevs->find_first_not_of(';')};
|
||||
size_t curpos;
|
||||
while((curpos=nextpos) < customdevs->length())
|
||||
size_t curpos{customdevs->find_first_not_of(';')};
|
||||
while(curpos < customdevs->length())
|
||||
{
|
||||
nextpos = customdevs->find_first_of(';', curpos+1);
|
||||
|
||||
size_t seppos{customdevs->find_first_of('=', curpos)};
|
||||
size_t nextpos{customdevs->find(';', curpos+1)};
|
||||
const size_t seppos{customdevs->find('=', curpos)};
|
||||
if(seppos == curpos || seppos >= nextpos)
|
||||
{
|
||||
std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||
const std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
devlist.emplace_back(customdevs->substr(curpos, seppos-curpos),
|
||||
customdevs->substr(seppos+1, nextpos-seppos-1));
|
||||
const auto &entry = devlist.back();
|
||||
const std::string_view strview{*customdevs};
|
||||
const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos),
|
||||
strview.substr(seppos+1, nextpos-seppos-1));
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
|
||||
if(nextpos < customdevs->length())
|
||||
nextpos = customdevs->find_first_not_of(';', nextpos+1);
|
||||
curpos = nextpos;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string main_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
|
||||
const std::string main_prefix{ConfigValueStr({}, "alsa"sv, prefix_name(stream))
|
||||
.value_or("plughw:")};
|
||||
|
||||
int card{-1};
|
||||
int err{snd_card_next(&card)};
|
||||
|
|
@ -308,16 +350,17 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
{
|
||||
std::string name{"hw:" + std::to_string(card)};
|
||||
|
||||
snd_ctl_t *handle;
|
||||
if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
|
||||
SndCtl handle;
|
||||
err = handle.open(name.c_str(), 0);
|
||||
if(err < 0)
|
||||
{
|
||||
ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
|
||||
continue;
|
||||
}
|
||||
if((err=snd_ctl_card_info(handle, info)) < 0)
|
||||
err = snd_ctl_card_info(handle, info);
|
||||
if(err < 0)
|
||||
{
|
||||
ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
|
||||
snd_ctl_close(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -326,8 +369,7 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
name = prefix_name(stream);
|
||||
name += '-';
|
||||
name += cardid;
|
||||
const std::string card_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
|
||||
const std::string card_prefix{ConfigValueStr({}, "alsa"sv, name).value_or(main_prefix)};
|
||||
|
||||
int dev{-1};
|
||||
while(true)
|
||||
|
|
@ -339,7 +381,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
|
||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
snd_pcm_info_set_stream(pcminfo, stream);
|
||||
if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
|
||||
err = snd_ctl_pcm_info(handle, pcminfo);
|
||||
if(err < 0)
|
||||
{
|
||||
if(err != -ENOENT)
|
||||
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
|
||||
|
|
@ -352,8 +395,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
name += cardid;
|
||||
name += '-';
|
||||
name += std::to_string(dev);
|
||||
const std::string device_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
|
||||
const std::string device_prefix{ConfigValueStr({}, "alsa"sv, name)
|
||||
.value_or(card_prefix)};
|
||||
|
||||
/* "CardName, PcmName (CARD=cardid,DEV=dev)" */
|
||||
name = cardname;
|
||||
|
|
@ -372,18 +415,13 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
device += ",DEV=";
|
||||
device += std::to_string(dev);
|
||||
|
||||
devlist.emplace_back(std::move(name), std::move(device));
|
||||
const auto &entry = devlist.back();
|
||||
const auto &entry = devlist.emplace_back(std::move(name), std::move(device));
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
snd_ctl_close(handle);
|
||||
}
|
||||
if(err < 0)
|
||||
ERR("snd_card_next failed: %s\n", snd_strerror(err));
|
||||
|
||||
snd_pcm_info_free(pcminfo);
|
||||
snd_ctl_card_info_free(info);
|
||||
|
||||
return devlist;
|
||||
}
|
||||
|
||||
|
|
@ -392,7 +430,6 @@ int verify_state(snd_pcm_t *handle)
|
|||
{
|
||||
snd_pcm_state_t state{snd_pcm_state(handle)};
|
||||
|
||||
int err;
|
||||
switch(state)
|
||||
{
|
||||
case SND_PCM_STATE_OPEN:
|
||||
|
|
@ -405,15 +442,27 @@ int verify_state(snd_pcm_t *handle)
|
|||
break;
|
||||
|
||||
case SND_PCM_STATE_XRUN:
|
||||
if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
|
||||
if(int err{snd_pcm_recover(handle, -EPIPE, 1)}; err < 0)
|
||||
return err;
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
|
||||
if(int err{snd_pcm_recover(handle, -ESTRPIPE, 1)}; err < 0)
|
||||
return err;
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_DISCONNECTED:
|
||||
return -ENODEV;
|
||||
|
||||
/* ALSA headers have made this enum public, leaving us in a bind: use
|
||||
* the enum despite being private and internal to the libasound, or
|
||||
* ignore when an enum value isn't handled. We can't rely on it being
|
||||
* declared either, since older headers don't have it and it could be
|
||||
* removed in the future. We can't even really rely on its value, since
|
||||
* being private/internal means it's subject to change, but this is the
|
||||
* best we can do.
|
||||
*/
|
||||
case 1024 /*SND_PCM_STATE_PRIVATE1*/:
|
||||
assert(state != 1024);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
@ -427,7 +476,7 @@ struct AlsaPlayback final : public BackendBase {
|
|||
int mixerProc();
|
||||
int mixerNoMMapProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -439,12 +488,10 @@ struct AlsaPlayback final : public BackendBase {
|
|||
std::mutex mMutex;
|
||||
|
||||
uint mFrameStep{};
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(AlsaPlayback)
|
||||
};
|
||||
|
||||
AlsaPlayback::~AlsaPlayback()
|
||||
|
|
@ -458,7 +505,7 @@ AlsaPlayback::~AlsaPlayback()
|
|||
int AlsaPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
|
|
@ -506,7 +553,7 @@ int AlsaPlayback::mixerProc()
|
|||
avail -= avail%update_size;
|
||||
|
||||
// it is possible that contiguous areas are smaller, thus we use a loop
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
while(avail > 0)
|
||||
{
|
||||
snd_pcm_uframes_t frames{avail};
|
||||
|
|
@ -520,6 +567,7 @@ int AlsaPlayback::mixerProc()
|
|||
break;
|
||||
}
|
||||
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
|
||||
char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
|
||||
|
||||
|
|
@ -541,7 +589,7 @@ int AlsaPlayback::mixerProc()
|
|||
int AlsaPlayback::mixerNoMMapProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
|
|
@ -585,13 +633,13 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
continue;
|
||||
}
|
||||
|
||||
al::byte *WritePtr{mBuffer.data()};
|
||||
auto WritePtr = mBuffer.begin();
|
||||
avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
mDevice->renderSamples(al::to_address(WritePtr), static_cast<uint>(avail), mFrameStep);
|
||||
while(avail > 0)
|
||||
{
|
||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr,
|
||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, al::to_address(WritePtr),
|
||||
static_cast<snd_pcm_uframes_t>(avail))};
|
||||
switch(ret)
|
||||
{
|
||||
|
|
@ -626,10 +674,10 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
}
|
||||
|
||||
|
||||
void AlsaPlayback::open(const char *name)
|
||||
void AlsaPlayback::open(std::string_view name)
|
||||
{
|
||||
std::string driver{"default"};
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||
|
|
@ -638,13 +686,13 @@ void AlsaPlayback::open(const char *name)
|
|||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
driver = iter->device_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device"))
|
||||
name = GetDefaultName();
|
||||
if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv))
|
||||
driver = std::move(driveropt).value();
|
||||
}
|
||||
TRACE("Opening device \"%s\"\n", driver.c_str());
|
||||
|
|
@ -692,15 +740,14 @@ bool AlsaPlayback::reset()
|
|||
break;
|
||||
}
|
||||
|
||||
bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)};
|
||||
bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa"sv, "mmap"sv, true)};
|
||||
uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
|
||||
uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
|
||||
uint rate{mDevice->Frequency};
|
||||
|
||||
int err{};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -715,17 +762,18 @@ bool AlsaPlayback::reset()
|
|||
/* test and set format (implicitly sets sample bits) */
|
||||
if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0)
|
||||
{
|
||||
static const struct {
|
||||
struct FormatMap {
|
||||
snd_pcm_format_t format;
|
||||
DevFmtType fmttype;
|
||||
} formatlist[] = {
|
||||
{ SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
||||
{ SND_PCM_FORMAT_S32, DevFmtInt },
|
||||
{ SND_PCM_FORMAT_U32, DevFmtUInt },
|
||||
{ SND_PCM_FORMAT_S16, DevFmtShort },
|
||||
{ SND_PCM_FORMAT_U16, DevFmtUShort },
|
||||
{ SND_PCM_FORMAT_S8, DevFmtByte },
|
||||
{ SND_PCM_FORMAT_U8, DevFmtUByte },
|
||||
};
|
||||
static constexpr std::array formatlist{
|
||||
FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
||||
FormatMap{SND_PCM_FORMAT_S32, DevFmtInt },
|
||||
FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt },
|
||||
FormatMap{SND_PCM_FORMAT_S16, DevFmtShort },
|
||||
FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort},
|
||||
FormatMap{SND_PCM_FORMAT_S8, DevFmtByte },
|
||||
FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte },
|
||||
};
|
||||
|
||||
for(const auto &fmt : formatlist)
|
||||
|
|
@ -750,7 +798,7 @@ bool AlsaPlayback::reset()
|
|||
else mDevice->FmtChans = DevFmtStereo;
|
||||
}
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false)
|
||||
if(!GetConfigValueBool(mDevice->DeviceName, "alsa", "allow-resampler", false)
|
||||
|| !mDevice->Flags.test(FrequencyRequest))
|
||||
{
|
||||
if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
|
||||
|
|
@ -760,10 +808,10 @@ bool AlsaPlayback::reset()
|
|||
WARN("Failed to enable ALSA resampler\n");
|
||||
CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
|
||||
/* set period time (implicitly constrains period/buffer parameters) */
|
||||
if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0)
|
||||
if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 0)
|
||||
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
|
||||
/* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
|
||||
if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0)
|
||||
if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 0)
|
||||
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
|
||||
/* install and prepare hardware configuration */
|
||||
CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
|
||||
|
|
@ -798,11 +846,10 @@ bool AlsaPlayback::reset()
|
|||
|
||||
void AlsaPlayback::start()
|
||||
{
|
||||
int err{};
|
||||
snd_pcm_access_t access{};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -849,10 +896,9 @@ void AlsaPlayback::stop()
|
|||
|
||||
ClockLatency AlsaPlayback::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
ClockLatency ret{};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
snd_pcm_sframes_t delay{};
|
||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||
if(err < 0)
|
||||
|
|
@ -871,23 +917,21 @@ struct AlsaCapture final : public BackendBase {
|
|||
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
ClockLatency getClockLatency() override;
|
||||
|
||||
snd_pcm_t *mPcmHandle{nullptr};
|
||||
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
bool mDoCapture{false};
|
||||
RingBufferPtr mRing{nullptr};
|
||||
|
||||
snd_pcm_sframes_t mLastAvail{0};
|
||||
|
||||
DEF_NEWDEL(AlsaCapture)
|
||||
};
|
||||
|
||||
AlsaCapture::~AlsaCapture()
|
||||
|
|
@ -898,10 +942,10 @@ AlsaCapture::~AlsaCapture()
|
|||
}
|
||||
|
||||
|
||||
void AlsaCapture::open(const char *name)
|
||||
void AlsaCapture::open(std::string_view name)
|
||||
{
|
||||
std::string driver{"default"};
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
{
|
||||
if(CaptureDevices.empty())
|
||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||
|
|
@ -910,19 +954,18 @@ void AlsaCapture::open(const char *name)
|
|||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
driver = iter->device_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture"))
|
||||
name = GetDefaultName();
|
||||
if(auto driveropt = ConfigValueStr({}, "alsa"sv, "capture"sv))
|
||||
driver = std::move(driveropt).value();
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver.c_str());
|
||||
int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not open ALSA device \"%s\"", driver.c_str()};
|
||||
|
||||
|
|
@ -955,13 +998,15 @@ void AlsaCapture::open(const char *name)
|
|||
break;
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
|
||||
snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)};
|
||||
snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->BufferSize,
|
||||
100u*mDevice->Frequency/1000u)};
|
||||
snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->BufferSize,
|
||||
25u*mDevice->Frequency/1000u)};
|
||||
|
||||
bool needring{false};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -999,13 +1044,11 @@ void AlsaCapture::open(const char *name)
|
|||
|
||||
void AlsaCapture::start()
|
||||
{
|
||||
int err{snd_pcm_prepare(mPcmHandle)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s",
|
||||
snd_strerror(err)};
|
||||
|
||||
err = snd_pcm_start(mPcmHandle);
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_start(mPcmHandle)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s",
|
||||
snd_strerror(err)};
|
||||
|
||||
|
|
@ -1024,25 +1067,27 @@ void AlsaCapture::stop()
|
|||
/* The ring buffer implicitly captures when checking availability.
|
||||
* Direct access needs to explicitly capture it into temp storage.
|
||||
*/
|
||||
auto temp = al::vector<al::byte>(
|
||||
auto temp = std::vector<std::byte>(
|
||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
|
||||
captureSamples(temp.data(), avail);
|
||||
mBuffer = std::move(temp);
|
||||
}
|
||||
int err{snd_pcm_drop(mPcmHandle)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_drop(mPcmHandle)}; err < 0)
|
||||
ERR("drop failed: %s\n", snd_strerror(err));
|
||||
mDoCapture = false;
|
||||
}
|
||||
|
||||
void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{
|
||||
if(mRing)
|
||||
{
|
||||
mRing->read(buffer, samples);
|
||||
std::ignore = mRing->read(buffer, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto outspan = al::span{buffer,
|
||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, samples))};
|
||||
auto outiter = outspan.begin();
|
||||
mLastAvail -= samples;
|
||||
while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
|
||||
{
|
||||
|
|
@ -1055,20 +1100,21 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
|
||||
|
||||
amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
|
||||
std::copy_n(mBuffer.begin(), amt, buffer);
|
||||
std::copy_n(mBuffer.begin(), amt, outiter);
|
||||
|
||||
mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
|
||||
amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
|
||||
}
|
||||
else if(mDoCapture)
|
||||
amt = snd_pcm_readi(mPcmHandle, buffer, samples);
|
||||
amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples);
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
|
||||
|
||||
if(amt == -EAGAIN)
|
||||
continue;
|
||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
||||
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||
if(amt >= 0)
|
||||
{
|
||||
amt = snd_pcm_start(mPcmHandle);
|
||||
if(amt >= 0)
|
||||
|
|
@ -1088,12 +1134,12 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
continue;
|
||||
}
|
||||
|
||||
buffer = buffer + amt;
|
||||
outiter += amt;
|
||||
samples -= static_cast<uint>(amt);
|
||||
}
|
||||
if(samples > 0)
|
||||
std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
||||
al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
||||
std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
||||
std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
||||
}
|
||||
|
||||
uint AlsaCapture::availableSamples()
|
||||
|
|
@ -1105,7 +1151,8 @@ uint AlsaCapture::availableSamples()
|
|||
{
|
||||
ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
|
||||
|
||||
if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0)
|
||||
avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
|
||||
if(avail >= 0)
|
||||
{
|
||||
if(mDoCapture)
|
||||
avail = snd_pcm_start(mPcmHandle);
|
||||
|
|
@ -1141,7 +1188,8 @@ uint AlsaCapture::availableSamples()
|
|||
|
||||
if(amt == -EAGAIN)
|
||||
continue;
|
||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
||||
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||
if(amt >= 0)
|
||||
{
|
||||
if(mDoCapture)
|
||||
amt = snd_pcm_start(mPcmHandle);
|
||||
|
|
@ -1168,9 +1216,8 @@ uint AlsaCapture::availableSamples()
|
|||
|
||||
ClockLatency AlsaCapture::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
ClockLatency ret{};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
snd_pcm_sframes_t delay{};
|
||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||
if(err < 0)
|
||||
|
|
@ -1189,13 +1236,9 @@ ClockLatency AlsaCapture::getClockLatency()
|
|||
|
||||
bool AlsaBackendFactory::init()
|
||||
{
|
||||
bool error{false};
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!alsa_handle)
|
||||
{
|
||||
std::string missing_funcs;
|
||||
|
||||
alsa_handle = LoadLib("libasound.so.2");
|
||||
if(!alsa_handle)
|
||||
{
|
||||
|
|
@ -1203,52 +1246,47 @@ bool AlsaBackendFactory::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
error = false;
|
||||
std::string missing_funcs;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
|
||||
if(p##f == nullptr) { \
|
||||
error = true; \
|
||||
missing_funcs += "\n" #f; \
|
||||
} \
|
||||
if(p##f == nullptr) missing_funcs += "\n" #f; \
|
||||
} while(0)
|
||||
ALSA_FUNCS(LOAD_FUNC);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if(error)
|
||||
if(!missing_funcs.empty())
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
||||
CloseLib(alsa_handle);
|
||||
alsa_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return !error;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AlsaBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string AlsaBackendFactory::probe(BackendType type)
|
||||
auto AlsaBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||
{
|
||||
/* +1 to also append the null char (to ensure a null-separated list and
|
||||
* double-null terminated list).
|
||||
*/
|
||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.name); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct AlsaBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_ALSA_H */
|
||||
|
|
|
|||
|
|
@ -7,17 +7,6 @@
|
|||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
#include "albit.h"
|
||||
#include "core/logging.h"
|
||||
#include "aloptional.h"
|
||||
#endif
|
||||
|
||||
#include "atomic.h"
|
||||
#include "core/devformat.h"
|
||||
|
||||
|
||||
|
|
@ -25,10 +14,12 @@ namespace al {
|
|||
|
||||
backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
}
|
||||
backend_exception::~backend_exception() = default;
|
||||
|
||||
|
|
@ -38,7 +29,7 @@ backend_exception::~backend_exception() = default;
|
|||
bool BackendBase::reset()
|
||||
{ throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; }
|
||||
|
||||
void BackendBase::captureSamples(al::byte*, uint)
|
||||
void BackendBase::captureSamples(std::byte*, uint)
|
||||
{ }
|
||||
|
||||
uint BackendBase::availableSamples()
|
||||
|
|
@ -46,27 +37,26 @@ uint BackendBase::availableSamples()
|
|||
|
||||
ClockLatency BackendBase::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
ClockLatency ret{};
|
||||
|
||||
uint refcount;
|
||||
do {
|
||||
refcount = mDevice->waitForMix();
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
} while(refcount != ReadRef(mDevice->MixCount));
|
||||
} while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed));
|
||||
|
||||
/* NOTE: The device will generally have about all but one periods filled at
|
||||
* any given time during playback. Without a more accurate measurement from
|
||||
* the output, this is an okay approximation.
|
||||
*/
|
||||
ret.Latency = std::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize},
|
||||
std::chrono::seconds::zero());
|
||||
ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize};
|
||||
ret.Latency /= mDevice->Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BackendBase::setDefaultWFXChannelOrder()
|
||||
void BackendBase::setDefaultWFXChannelOrder() const
|
||||
{
|
||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||
|
||||
|
|
@ -126,6 +116,24 @@ void BackendBase::setDefaultWFXChannelOrder()
|
|||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
break;
|
||||
case DevFmtX7144:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 2;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 3;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 4;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 5;
|
||||
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||
break;
|
||||
case DevFmtX3D71:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
|
|
@ -141,7 +149,7 @@ void BackendBase::setDefaultWFXChannelOrder()
|
|||
}
|
||||
}
|
||||
|
||||
void BackendBase::setDefaultChannelOrder()
|
||||
void BackendBase::setDefaultChannelOrder() const
|
||||
{
|
||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||
|
||||
|
|
@ -179,6 +187,24 @@ void BackendBase::setDefaultChannelOrder()
|
|||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
break;
|
||||
case DevFmtX7144:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 2;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 3;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 4;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 5;
|
||||
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||
break;
|
||||
case DevFmtX3D71:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "core/device.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/events.h"
|
||||
|
||||
|
||||
using uint = unsigned int;
|
||||
|
|
@ -20,27 +23,33 @@ struct ClockLatency {
|
|||
};
|
||||
|
||||
struct BackendBase {
|
||||
virtual void open(const char *name) = 0;
|
||||
virtual void open(std::string_view name) = 0;
|
||||
|
||||
virtual bool reset();
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void captureSamples(al::byte *buffer, uint samples);
|
||||
virtual void captureSamples(std::byte *buffer, uint samples);
|
||||
virtual uint availableSamples();
|
||||
|
||||
virtual ClockLatency getClockLatency();
|
||||
|
||||
DeviceBase *const mDevice;
|
||||
|
||||
BackendBase() = delete;
|
||||
BackendBase(const BackendBase&) = delete;
|
||||
BackendBase(BackendBase&&) = delete;
|
||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||
virtual ~BackendBase() = default;
|
||||
|
||||
void operator=(const BackendBase&) = delete;
|
||||
void operator=(BackendBase&&) = delete;
|
||||
|
||||
protected:
|
||||
/** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
|
||||
void setDefaultChannelOrder();
|
||||
void setDefaultChannelOrder() const;
|
||||
/** Sets the default channel order used by WaveFormatEx. */
|
||||
void setDefaultWFXChannelOrder();
|
||||
void setDefaultWFXChannelOrder() const;
|
||||
};
|
||||
using BackendPtr = std::unique_ptr<BackendBase>;
|
||||
|
||||
|
|
@ -50,18 +59,6 @@ enum class BackendType {
|
|||
};
|
||||
|
||||
|
||||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
||||
{
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency;
|
||||
return device->ClockBase + ns;
|
||||
}
|
||||
|
||||
/* Helper to get the device latency from the backend, including any fixed
|
||||
* latency from post-processing.
|
||||
*/
|
||||
|
|
@ -74,16 +71,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend)
|
|||
|
||||
|
||||
struct BackendFactory {
|
||||
virtual bool init() = 0;
|
||||
|
||||
virtual bool querySupport(BackendType type) = 0;
|
||||
|
||||
virtual std::string probe(BackendType type) = 0;
|
||||
|
||||
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
||||
|
||||
protected:
|
||||
BackendFactory() = default;
|
||||
BackendFactory(const BackendFactory&) = delete;
|
||||
BackendFactory(BackendFactory&&) = delete;
|
||||
virtual ~BackendFactory() = default;
|
||||
|
||||
void operator=(const BackendFactory&) = delete;
|
||||
void operator=(BackendFactory&&) = delete;
|
||||
|
||||
virtual auto init() -> bool = 0;
|
||||
|
||||
virtual auto querySupport(BackendType type) -> bool = 0;
|
||||
|
||||
virtual auto queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport
|
||||
{ return alc::EventSupport::NoSupport; }
|
||||
|
||||
virtual auto enumerate(BackendType type) -> std::vector<std::string> = 0;
|
||||
|
||||
virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0;
|
||||
};
|
||||
|
||||
namespace al {
|
||||
|
|
@ -98,15 +103,15 @@ class backend_exception final : public base_exception {
|
|||
backend_error mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#ifdef __MINGW32__
|
||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
backend_exception(backend_error code, const char *msg, ...);
|
||||
~backend_exception() override;
|
||||
|
||||
backend_error errorCode() const noexcept { return mErrorCode; }
|
||||
[[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; }
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
|
|
|||
|
|
@ -22,18 +22,20 @@
|
|||
|
||||
#include "coreaudio.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
|
@ -42,18 +44,40 @@
|
|||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#define CAN_ENUMERATE 0
|
||||
#else
|
||||
#include <IOKit/audio/IOAudioTypes.h>
|
||||
#define CAN_ENUMERATE 1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto OutputElement = 0;
|
||||
constexpr auto InputElement = 1;
|
||||
|
||||
struct FourCCPrinter {
|
||||
char mString[sizeof(UInt32) + 1]{};
|
||||
|
||||
constexpr FourCCPrinter(UInt32 code) noexcept
|
||||
{
|
||||
for(size_t i{0};i < sizeof(UInt32);++i)
|
||||
{
|
||||
const auto ch = static_cast<char>(code & 0xff);
|
||||
/* If this breaks early it'll leave the first byte null, to get
|
||||
* read as a 0-length string.
|
||||
*/
|
||||
if(ch <= 0x1f || ch >= 0x7f)
|
||||
break;
|
||||
mString[sizeof(UInt32)-1-i] = ch;
|
||||
code >>= 8;
|
||||
}
|
||||
}
|
||||
constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
|
||||
|
||||
constexpr const char *c_str() const noexcept { return mString; }
|
||||
};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
struct DeviceEntry {
|
||||
AudioDeviceID mId;
|
||||
|
|
@ -147,7 +171,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
|||
&propSize);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +183,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
|||
buflist);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +208,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
|||
auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
|
||||
if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
|
||||
{
|
||||
ERR("Failed to get device list: %u\n", err);
|
||||
ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -247,6 +273,48 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
|||
newdevs.swap(list);
|
||||
}
|
||||
|
||||
struct DeviceHelper {
|
||||
DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectAddPropertyListener fail: %d", status);
|
||||
}
|
||||
~DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectRemovePropertyListener fail: %d", status);
|
||||
}
|
||||
|
||||
static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
|
||||
const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/)
|
||||
{
|
||||
for(UInt32 i = 0; i < inNumberAddresses; ++i)
|
||||
{
|
||||
switch(inAddresses[i].mSelector)
|
||||
{
|
||||
case kAudioHardwarePropertyDefaultOutputDevice:
|
||||
case kAudioHardwarePropertyDefaultSystemOutputDevice:
|
||||
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
|
||||
"Default playback device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||
break;
|
||||
case kAudioHardwarePropertyDefaultInputDevice:
|
||||
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
|
||||
"Default capture device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return noErr;
|
||||
}
|
||||
};
|
||||
|
||||
static std::optional<DeviceHelper> sDeviceHelper;
|
||||
|
||||
#else
|
||||
|
||||
static constexpr char ca_device[] = "CoreAudio Default";
|
||||
|
|
@ -260,15 +328,8 @@ struct CoreAudioPlayback final : public BackendBase {
|
|||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept;
|
||||
static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept
|
||||
{
|
||||
return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
|
||||
inBusNumber, inNumberFrames, ioData);
|
||||
}
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -277,8 +338,6 @@ struct CoreAudioPlayback final : public BackendBase {
|
|||
|
||||
uint mFrameSize{0u};
|
||||
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
DEF_NEWDEL(CoreAudioPlayback)
|
||||
};
|
||||
|
||||
CoreAudioPlayback::~CoreAudioPlayback()
|
||||
|
|
@ -301,11 +360,11 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi
|
|||
}
|
||||
|
||||
|
||||
void CoreAudioPlayback::open(const char *name)
|
||||
void CoreAudioPlayback::open(std::string_view name)
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
|
|
@ -318,16 +377,16 @@ void CoreAudioPlayback::open(const char *name)
|
|||
auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
|
||||
if(devmatch == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
#endif
|
||||
|
||||
/* open the default output unit */
|
||||
|
|
@ -351,7 +410,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
|
|
@ -362,7 +421,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
err = AudioUnitInitialize(audioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: %u", err};
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
||||
* non-0. If not, this logic is broken.
|
||||
|
|
@ -375,7 +434,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
mAudioUnit = audioUnit;
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
|
|
@ -388,6 +447,21 @@ void CoreAudioPlayback::open(const char *name)
|
|||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
}
|
||||
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
{
|
||||
UInt32 type{};
|
||||
err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
|
||||
kAudioObjectPropertyElementMaster, sizeof(type), &type);
|
||||
if(err != noErr)
|
||||
ERR("Failed to get audio device type: %u\n", err);
|
||||
else
|
||||
{
|
||||
TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
|
||||
mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
#endif
|
||||
|
|
@ -397,7 +471,7 @@ bool CoreAudioPlayback::reset()
|
|||
{
|
||||
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("-- AudioUnitUninitialize failed.\n");
|
||||
ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
AudioStreamBasicDescription streamFormat{};
|
||||
|
|
@ -406,7 +480,8 @@ bool CoreAudioPlayback::reset()
|
|||
OutputElement, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(streamFormat))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +548,8 @@ bool CoreAudioPlayback::reset()
|
|||
OutputElement, &streamFormat, sizeof(streamFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -482,14 +558,16 @@ bool CoreAudioPlayback::reset()
|
|||
/* setup callback */
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
AURenderCallbackStruct input{};
|
||||
input.inputProc = CoreAudioPlayback::MixerProcC;
|
||||
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||
{ return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||
input.inputProcRefCon = this;
|
||||
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +575,7 @@ bool CoreAudioPlayback::reset()
|
|||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -509,14 +587,14 @@ void CoreAudioPlayback::start()
|
|||
const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: %d", err};
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioPlayback::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -527,18 +605,11 @@ struct CoreAudioCapture final : public BackendBase {
|
|||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
|
||||
static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept
|
||||
{
|
||||
return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
|
||||
inBusNumber, inNumberFrames, ioData);
|
||||
}
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
AudioUnit mAudioUnit{0};
|
||||
|
|
@ -548,11 +619,9 @@ struct CoreAudioCapture final : public BackendBase {
|
|||
|
||||
SampleConverterPtr mConverter;
|
||||
|
||||
al::vector<char> mCaptureData;
|
||||
std::vector<char> mCaptureData;
|
||||
|
||||
RingBufferPtr mRing{nullptr};
|
||||
|
||||
DEF_NEWDEL(CoreAudioCapture)
|
||||
};
|
||||
|
||||
CoreAudioCapture::~CoreAudioCapture()
|
||||
|
|
@ -568,7 +637,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
|||
AudioBufferList*) noexcept
|
||||
{
|
||||
union {
|
||||
al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
||||
std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
||||
AudioBufferList list;
|
||||
} audiobuf{};
|
||||
|
||||
|
|
@ -581,20 +650,20 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
|||
inNumberFrames, &audiobuf.list)};
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender capture error: %d\n", err);
|
||||
ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
mRing->write(mCaptureData.data(), inNumberFrames);
|
||||
std::ignore = mRing->write(mCaptureData.data(), inNumberFrames);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
void CoreAudioCapture::open(const char *name)
|
||||
void CoreAudioCapture::open(std::string_view name)
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
|
|
@ -607,16 +676,16 @@ void CoreAudioCapture::open(const char *name)
|
|||
auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
|
||||
if(devmatch == CaptureList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
#endif
|
||||
|
||||
AudioComponentDescription desc{};
|
||||
|
|
@ -640,7 +709,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Turn off AudioUnit output
|
||||
UInt32 enableIO{0};
|
||||
|
|
@ -648,7 +717,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable audio unit output property: %u", err};
|
||||
"Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Turn on AudioUnit input
|
||||
enableIO = 1;
|
||||
|
|
@ -656,7 +726,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not enable audio unit input property: %u", err};
|
||||
"Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
|
|
@ -666,14 +737,15 @@ void CoreAudioCapture::open(const char *name)
|
|||
|
||||
// set capture callback
|
||||
AURenderCallbackStruct input{};
|
||||
input.inputProc = CoreAudioCapture::RecordProcC;
|
||||
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||
{ return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||
input.inputProcRefCon = this;
|
||||
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
|
||||
kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set capture callback: %u", err};
|
||||
"Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Disable buffer allocation for capture
|
||||
UInt32 flag{0};
|
||||
|
|
@ -681,13 +753,14 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable buffer allocation property: %u", err};
|
||||
"Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: %u", err};
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Get the hardware format
|
||||
AudioStreamBasicDescription hardwareFormat{};
|
||||
|
|
@ -696,7 +769,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
InputElement, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(hardwareFormat))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input format: %u", err};
|
||||
"Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Set up the requested format description
|
||||
AudioStreamBasicDescription requestedFormat{};
|
||||
|
|
@ -749,6 +822,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtX714:
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
|
||||
|
|
@ -777,14 +851,14 @@ void CoreAudioCapture::open(const char *name)
|
|||
InputElement, &outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set input format: %u", err};
|
||||
"Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* Calculate the minimum AudioUnit output format frame count for the pre-
|
||||
* conversion ring buffer. Ensure at least 100ms for the total buffer.
|
||||
*/
|
||||
double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
|
||||
auto FrameCount64 = maxu64(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||
static_cast<UInt32>(outputFormat.mSampleRate)/10);
|
||||
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||
static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
|
||||
FrameCount64 += MaxResamplerPadding;
|
||||
if(FrameCount64 > std::numeric_limits<int32_t>::max())
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
|
|
@ -796,11 +870,11 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(outputFrameCount))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input frame count: %u", err};
|
||||
"Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
mCaptureData.resize(outputFrameCount * mFrameSize);
|
||||
|
||||
outputFrameCount = static_cast<UInt32>(maxu64(outputFrameCount, FrameCount64));
|
||||
outputFrameCount = static_cast<UInt32>(std::max(uint64_t{outputFrameCount}, FrameCount64));
|
||||
mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
|
||||
|
||||
/* Set up sample converter if needed */
|
||||
|
|
@ -810,7 +884,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
mDevice->Frequency, Resampler::FastBSinc24);
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
|
|
@ -834,21 +908,21 @@ void CoreAudioCapture::start()
|
|||
OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: %d", err};
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioCapture::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{
|
||||
if(!mConverter)
|
||||
{
|
||||
mRing->read(buffer, samples);
|
||||
std::ignore = mRing->read(buffer, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -882,28 +956,34 @@ BackendFactory &CoreAudioBackendFactory::getFactory()
|
|||
return factory;
|
||||
}
|
||||
|
||||
bool CoreAudioBackendFactory::init() { return true; }
|
||||
bool CoreAudioBackendFactory::init()
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
sDeviceHelper.emplace();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreAudioBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||
auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
#if CAN_ENUMERATE
|
||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.mName); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
EnumerateDevices(PlaybackList, false);
|
||||
outnames.reserve(PlaybackList.size());
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
EnumerateDevices(CaptureList, true);
|
||||
outnames.reserve(CaptureList.size());
|
||||
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
||||
break;
|
||||
}
|
||||
|
|
@ -914,8 +994,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
|||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(ca_device, sizeof(ca_device));
|
||||
outnames.emplace_back(ca_device);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -930,3 +1009,18 @@ BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendTyp
|
|||
return BackendPtr{new CoreAudioCapture{device}};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
|
||||
{
|
||||
switch(eventType)
|
||||
{
|
||||
case alc::EventType::DefaultDeviceChanged:
|
||||
return alc::EventSupport::FullSupport;
|
||||
|
||||
case alc::EventType::DeviceAdded:
|
||||
case alc::EventType::DeviceRemoved:
|
||||
case alc::EventType::Count:
|
||||
break;
|
||||
}
|
||||
return alc::EventSupport::NoSupport;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,17 @@
|
|||
|
||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_COREAUDIO_H */
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <cguid.h>
|
||||
#include <mmreg.h>
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
|
|
@ -36,15 +32,21 @@
|
|||
#include <ksmedia.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "comptr.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
|
|
@ -52,7 +54,6 @@
|
|||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "strutils.h"
|
||||
#include "threads.h"
|
||||
|
||||
/* MinGW-w64 needs this for some unknown reason now. */
|
||||
using LPCWAVEFORMATEX = const WAVEFORMATEX*;
|
||||
|
|
@ -129,10 +130,10 @@ struct DevMap {
|
|||
{ }
|
||||
};
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
al::vector<DevMap> CaptureDevices;
|
||||
std::vector<DevMap> PlaybackDevices;
|
||||
std::vector<DevMap> CaptureDevices;
|
||||
|
||||
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
||||
bool checkName(const al::span<DevMap> list, const std::string &name)
|
||||
{
|
||||
auto match_name = [&name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; };
|
||||
|
|
@ -144,7 +145,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
|||
if(!guid)
|
||||
return TRUE;
|
||||
|
||||
auto& devices = *static_cast<al::vector<DevMap>*>(data);
|
||||
auto& devices = *static_cast<std::vector<DevMap>*>(data);
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
|
||||
|
||||
int count{1};
|
||||
|
|
@ -176,7 +177,7 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -189,8 +190,6 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(DSoundPlayback)
|
||||
};
|
||||
|
||||
DSoundPlayback::~DSoundPlayback()
|
||||
|
|
@ -209,7 +208,7 @@ DSoundPlayback::~DSoundPlayback()
|
|||
FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
DSBCAPS DSBCaps{};
|
||||
DSBCaps.dwSize = sizeof(DSBCaps);
|
||||
|
|
@ -299,24 +298,22 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DSoundPlayback::open(const char *name)
|
||||
void DSoundPlayback::open(std::string_view name)
|
||||
{
|
||||
HRESULT hr;
|
||||
if(PlaybackDevices.empty())
|
||||
{
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const GUID *guid{nullptr};
|
||||
if(!name && !PlaybackDevices.empty())
|
||||
if(name.empty() && !PlaybackDevices.empty())
|
||||
{
|
||||
name = PlaybackDevices[0].name.c_str();
|
||||
name = PlaybackDevices[0].name;
|
||||
guid = &PlaybackDevices[0].guid;
|
||||
}
|
||||
else
|
||||
|
|
@ -332,7 +329,7 @@ void DSoundPlayback::open(const char *name)
|
|||
[&id](const DevMap &entry) -> bool { return entry.guid == id; });
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
}
|
||||
guid = &iter->guid;
|
||||
}
|
||||
|
|
@ -347,7 +344,7 @@ void DSoundPlayback::open(const char *name)
|
|||
//DirectSound Init code
|
||||
ComPtr<IDirectSound> ds;
|
||||
if(SUCCEEDED(hr))
|
||||
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
||||
hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
if(FAILED(hr))
|
||||
|
|
@ -425,49 +422,53 @@ bool DSoundPlayback::reset()
|
|||
case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
|
||||
case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
|
||||
case DevFmtX7144: mDevice->FmtChans = DevFmtX714;
|
||||
/* fall-through */
|
||||
case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
|
||||
case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
hr = S_OK;
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
||||
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
||||
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
||||
OutputType.Format.wBitsPerSample / 8);
|
||||
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||
OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = 0;
|
||||
do {
|
||||
hr = S_OK;
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
||||
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
||||
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
||||
OutputType.Format.wBitsPerSample / 8);
|
||||
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||
OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = 0;
|
||||
|
||||
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
mPrimaryBuffer = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SUCCEEDED(hr) && !mPrimaryBuffer)
|
||||
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
}
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
mPrimaryBuffer = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SUCCEEDED(hr) && !mPrimaryBuffer)
|
||||
{
|
||||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
break;
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
if(num_updates > MAX_UPDATES)
|
||||
num_updates = MAX_UPDATES;
|
||||
|
|
@ -480,26 +481,21 @@ retry_open:
|
|||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
goto retry_open;
|
||||
}
|
||||
}
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
|
||||
if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
|
||||
break;
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
} while(FAILED(hr));
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
void *ptr;
|
||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
||||
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
assert(num_updates <= MAX_UPDATES);
|
||||
|
||||
std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
|
||||
std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots{};
|
||||
for(uint i{0};i < num_updates;++i)
|
||||
{
|
||||
nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
|
||||
|
|
@ -550,10 +546,10 @@ struct DSoundCapture final : public BackendBase {
|
|||
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
ComPtr<IDirectSoundCapture> mDSC;
|
||||
|
|
@ -562,8 +558,6 @@ struct DSoundCapture final : public BackendBase {
|
|||
DWORD mCursor{0u};
|
||||
|
||||
RingBufferPtr mRing;
|
||||
|
||||
DEF_NEWDEL(DSoundCapture)
|
||||
};
|
||||
|
||||
DSoundCapture::~DSoundCapture()
|
||||
|
|
@ -577,24 +571,22 @@ DSoundCapture::~DSoundCapture()
|
|||
}
|
||||
|
||||
|
||||
void DSoundCapture::open(const char *name)
|
||||
void DSoundCapture::open(std::string_view name)
|
||||
{
|
||||
HRESULT hr;
|
||||
if(CaptureDevices.empty())
|
||||
{
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const GUID *guid{nullptr};
|
||||
if(!name && !CaptureDevices.empty())
|
||||
if(name.empty() && !CaptureDevices.empty())
|
||||
{
|
||||
name = CaptureDevices[0].name.c_str();
|
||||
name = CaptureDevices[0].name;
|
||||
guid = &CaptureDevices[0].guid;
|
||||
}
|
||||
else
|
||||
|
|
@ -610,7 +602,7 @@ void DSoundCapture::open(const char *name)
|
|||
[&id](const DevMap &entry) -> bool { return entry.guid == id; });
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
}
|
||||
guid = &iter->guid;
|
||||
}
|
||||
|
|
@ -641,6 +633,7 @@ void DSoundCapture::open(const char *name)
|
|||
case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
|
||||
case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
|
||||
|
|
@ -657,6 +650,7 @@ void DSoundCapture::open(const char *name)
|
|||
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
|
||||
InputType.Format.nBlockAlign;
|
||||
InputType.Format.cbSize = 0;
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
|
@ -669,8 +663,7 @@ void DSoundCapture::open(const char *name)
|
|||
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
}
|
||||
|
||||
uint samples{mDevice->BufferSize};
|
||||
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
|
||||
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||
|
||||
DSCBUFFERDESC DSCBDescription{};
|
||||
DSCBDescription.dwSize = sizeof(DSCBDescription);
|
||||
|
|
@ -679,9 +672,9 @@ void DSoundCapture::open(const char *name)
|
|||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
//DirectSoundCapture Init code
|
||||
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
||||
hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
||||
|
||||
|
|
@ -719,8 +712,8 @@ void DSoundCapture::stop()
|
|||
}
|
||||
}
|
||||
|
||||
void DSoundCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
uint DSoundCapture::availableSamples()
|
||||
{
|
||||
|
|
@ -743,9 +736,9 @@ uint DSoundCapture::availableSamples()
|
|||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
||||
std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
||||
if(ReadPtr2 != nullptr && ReadCnt2 > 0)
|
||||
mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
||||
std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
||||
hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
|
||||
mCursor = ReadCursor;
|
||||
}
|
||||
|
|
@ -802,40 +795,32 @@ bool DSoundBackendFactory::init()
|
|||
bool DSoundBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string DSoundBackendFactory::probe(BackendType type)
|
||||
auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||
{
|
||||
/* +1 to also append the null char (to ensure a null-separated list and
|
||||
* double-null terminated list).
|
||||
*/
|
||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.name); };
|
||||
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hr;
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
PlaybackDevices.clear();
|
||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
||||
if(FAILED(hr))
|
||||
if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
|
||||
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
CaptureDevices.clear();
|
||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
||||
if(FAILED(hr))
|
||||
if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
|
||||
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
|
||||
return outnames;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue