diff --git a/src/api/api.h b/src/api/api.h index d0d090f5..559e3c78 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -100,6 +100,7 @@ extern StringEntry lovrPermission[]; extern StringEntry lovrSampleFormat[]; extern StringEntry lovrShaderType[]; extern StringEntry lovrShapeType[]; +extern StringEntry lovrSourceInterpolation[]; extern StringEntry lovrStencilAction[]; extern StringEntry lovrTextureFormat[]; extern StringEntry lovrTextureType[]; diff --git a/src/api/l_audio.c b/src/api/l_audio.c index 5f8a2f8b..badbb164 100644 --- a/src/api/l_audio.c +++ b/src/api/l_audio.c @@ -18,6 +18,12 @@ StringEntry lovrTimeUnit[] = { { 0 } }; +StringEntry lovrSourceInterpolation[] = { + [SOURCE_NEAREST] = ENTRY("nearest"), + [SOURCE_BILINEAR] = ENTRY("bilinear"), + { 0 } +}; + static void onDevice(const void* id, size_t size, const char* name, bool isDefault, void* userdata) { lua_State* L = userdata; lua_createtable(L, 0, 3); @@ -134,22 +140,32 @@ static int l_lovrAudioGetCaptureStream(lua_State* L) { static int l_lovrAudioNewSource(lua_State* L) { Sound* sound = luax_totype(L, 1, Sound); + bool spatial = true; + bool shared = false; + bool decode = false; + if (lua_istable(L, 2)) { + lua_getfield(L, 2, "spatial"); + spatial = lua_isnil(L, -1) || lua_toboolean(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 2, "shared"); + spatial = lua_toboolean(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 2, "decode"); + decode = lua_toboolean(L, -1); + lua_pop(L, 1); + } + if (!sound) { Blob* blob = luax_readblob(L, 1, "Source"); - sound = lovrSoundCreateFromFile(blob, false); + sound = lovrSoundCreateFromFile(blob, decode); lovrRelease(blob, lovrBlobDestroy); } else { lovrRetain(sound); } - bool spatial = true; - if (lua_istable(L, 2)) { - lua_getfield(L, 2, "spatial"); - spatial = lua_isnil(L, -1) || lua_toboolean(L, -1); - lua_pop(L, 1); - } - - Source* source = lovrSourceCreate(sound, spatial); + Source* source = lovrSourceCreate(sound, spatial, shared); luax_pushtype(L, Source, source); lovrRelease(sound, lovrSoundDestroy); lovrRelease(source, lovrSourceDestroy); diff --git a/src/api/l_audio_source.c b/src/api/l_audio_source.c index ab20119e..a43b8028 100644 --- a/src/api/l_audio_source.c +++ b/src/api/l_audio_source.c @@ -90,10 +90,46 @@ static int l_lovrSourceSetTime(lua_State* L) { static int l_lovrSourceIsSpatial(lua_State* L) { Source* source = luax_checktype(L, 1, Source); - lua_pushboolean(L, lovrSourceIsSpatial(source)); + bool spatial = lovrSourceIsSpatial(source); + lua_pushboolean(L, spatial); return 1; } +static int l_lovrSourceIsShared(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool shared = lovrSourceIsShared(source); + lua_pushboolean(L, shared); + return 1; +} + +static int l_lovrSourceGetSpatialBlend(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + float blend = lovrSourceGetSpatialBlend(source); + lua_pushnumber(L, blend); + return 1; +} + +static int l_lovrSourceSetSpatialBlend(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + float blend = luax_checkfloat(L, 2); + lovrSourceSetSpatialBlend(source, blend); + return 0; +} + +static int l_lovrSourceGetInterpolation(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + SourceInterpolation interpolation = lovrSourceGetInterpolation(source); + luax_pushenum(L, SourceInterpolation, interpolation); + return 1; +} + +static int l_lovrSourceSetInterpolation(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + SourceInterpolation interpolation = luax_checkenum(L, 2, SourceInterpolation, NULL); + lovrSourceSetInterpolation(source, interpolation); + return 0; +} + static int l_lovrSourceGetPose(lua_State* L) { Source* source = luax_checktype(L, 1, Source); float position[4], orientation[4], angle, ax, ay, az; @@ -136,6 +172,20 @@ static int l_lovrSourceSetDirectivity(lua_State* L) { return 0; } +static int l_lovrSourceGetRadius(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + float radius = lovrSourceGetRadius(source); + lua_pushnumber(L, radius); + return 1; +} + +static int l_lovrSourceSetRadius(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + float radius = luax_checkfloat(L, 2); + lovrSourceSetRadius(source, radius); + return 0; +} + static int l_lovrSourceIsAbsorptionEnabled(lua_State* L) { Source* source = luax_checktype(L, 1, Source); bool enabled = lovrSourceIsAbsorptionEnabled(source); @@ -164,6 +214,48 @@ static int l_lovrSourceSetFalloffEnabled(lua_State* L) { return 0; } +static int l_lovrSourceIsOcclusionEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lovrSourceIsOcclusionEnabled(source); + lua_pushboolean(L, enabled); + return 1; +} + +static int l_lovrSourceSetOcclusionEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lua_toboolean(L, 2); + lovrSourceSetOcclusionEnabled(source, enabled); + return 0; +} + +static int l_lovrSourceIsReverbEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lovrSourceIsReverbEnabled(source); + lua_pushboolean(L, enabled); + return 1; +} + +static int l_lovrSourceSetReverbEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lua_toboolean(L, 2); + lovrSourceSetReverbEnabled(source, enabled); + return 0; +} + +static int l_lovrSourceIsTransmissionEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lovrSourceIsTransmissionEnabled(source); + lua_pushboolean(L, enabled); + return 1; +} + +static int l_lovrSourceSetTransmissionEnabled(lua_State* L) { + Source* source = luax_checktype(L, 1, Source); + bool enabled = lua_toboolean(L, 2); + lovrSourceSetTransmissionEnabled(source, enabled); + return 0; +} + const luaL_Reg lovrSource[] = { { "clone", l_lovrSourceClone }, { "play", l_lovrSourcePlay }, @@ -178,13 +270,26 @@ const luaL_Reg lovrSource[] = { { "getTime", l_lovrSourceGetTime }, { "setTime", l_lovrSourceSetTime }, { "isSpatial", l_lovrSourceIsSpatial }, + { "isShared", l_lovrSourceIsShared }, + { "getSpatialBlend", l_lovrSourceGetSpatialBlend }, + { "setSpatialBlend", l_lovrSourceSetSpatialBlend }, + { "getInterpolation", l_lovrSourceGetInterpolation }, + { "setInterpolation", l_lovrSourceSetInterpolation }, { "getPose", l_lovrSourceGetPose }, { "setPose", l_lovrSourceSetPose }, + { "getRadius", l_lovrSourceGetRadius }, + { "setRadius", l_lovrSourceSetRadius }, { "getDirectivity", l_lovrSourceGetDirectivity }, { "setDirectivity", l_lovrSourceSetDirectivity }, { "isAbsorptionEnabled", l_lovrSourceIsAbsorptionEnabled }, { "setAbsorptionEnabled", l_lovrSourceSetAbsorptionEnabled }, { "isFalloffEnabled", l_lovrSourceIsFalloffEnabled }, { "setFalloffEnabled", l_lovrSourceSetFalloffEnabled }, + { "isOcclusionEnabled", l_lovrSourceIsOcclusionEnabled }, + { "setOcclusionEnabled", l_lovrSourceSetOcclusionEnabled }, + { "isReverbEnabled", l_lovrSourceIsReverbEnabled }, + { "setReverbEnabled", l_lovrSourceSetReverbEnabled }, + { "isTransmissionEnabled", l_lovrSourceIsTransmissionEnabled }, + { "setTransmissionEnabled", l_lovrSourceSetTransmissionEnabled }, { NULL, NULL } }; diff --git a/src/modules/audio/audio.c b/src/modules/audio/audio.c index 77119917..df5c2d22 100644 --- a/src/modules/audio/audio.c +++ b/src/modules/audio/audio.c @@ -32,15 +32,22 @@ struct Source { uint32_t converter; uint32_t offset; float volume; + float blend; float position[4]; float orientation[4]; + float radius; float dipoleWeight; float dipolePower; bool absorption; bool falloff; + bool occlusion; + bool reverb; + bool transmission; bool playing; bool looping; bool spatial; + bool shared; + bool bilinear; }; static struct { @@ -97,8 +104,8 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co if (!source->playing) { state.sources[source->index] = NULL; state.sourceMask &= ~(1ull << source->index); - source->index = ~0u; state.spatializer->sourceDestroy(source); + source->index = ~0u; lovrRelease(source, lovrSourceDestroy); continue; } @@ -342,7 +349,7 @@ Sound* lovrAudioGetCaptureStream() { // Source -Source* lovrSourceCreate(Sound* sound, bool spatial) { +Source* lovrSourceCreate(Sound* sound, bool spatial, bool shared) { lovrAssert(lovrSoundGetChannelLayout(sound) != CHANNEL_AMBISONIC, "Ambisonic Sources are not currently supported"); Source* source = calloc(1, sizeof(Source)); lovrAssert(source, "Out of memory"); @@ -353,6 +360,8 @@ Source* lovrSourceCreate(Sound* sound, bool spatial) { source->volume = 1.f; source->spatial = spatial; + source->shared = shared; + source->converter = ~0u; ma_data_converter_config config = ma_data_converter_config_init_default(); config.formatIn = miniaudioFormats[lovrSoundGetFormat(sound)]; @@ -362,8 +371,6 @@ Source* lovrSourceCreate(Sound* sound, bool spatial) { config.sampleRateIn = lovrSoundGetSampleRate(sound); config.sampleRateOut = PLAYBACK_SAMPLE_RATE; - source->converter = ~0u; - if (config.formatIn != config.formatOut || config.channelsIn != config.channelsOut || config.sampleRateIn != config.sampleRateOut) { for (size_t i = 0; i < state.converters.length; i++) { ma_data_converter* converter = &state.converters.data[i]; @@ -479,6 +486,26 @@ bool lovrSourceIsSpatial(Source *source) { return source->spatial; } +bool lovrSourceIsShared(Source* source) { + return source->shared; +} + +float lovrSourceGetSpatialBlend(Source* source) { + return source->blend; +} + +void lovrSourceSetSpatialBlend(Source* source, float blend) { + source->blend = blend; +} + +SourceInterpolation lovrSourceGetInterpolation(Source* source) { + return source->bilinear ? SOURCE_BILINEAR : SOURCE_NEAREST; +} + +void lovrSourceSetInterpolation(Source* source, SourceInterpolation interpolation) { + source->bilinear = interpolation == SOURCE_BILINEAR; +} + void lovrSourceGetPose(Source *source, float position[4], float orientation[4]) { memcpy(position, source->position, sizeof(source->position)); memcpy(orientation, source->orientation, sizeof(source->orientation)); @@ -491,6 +518,14 @@ void lovrSourceSetPose(Source *source, float position[4], float orientation[4]) ma_mutex_unlock(&state.lock); } +float lovrSourceGetRadius(Source* source) { + return source->radius; +} + +void lovrSourceSetRadius(Source* source, float radius) { + source->radius = radius; +} + void lovrSourceGetDirectivity(Source* source, float* weight, float* power) { *weight = source->dipoleWeight; *power = source->dipolePower; @@ -513,10 +548,38 @@ bool lovrSourceIsFalloffEnabled(Source* source) { return source->falloff; } -void lovrSourceSetFalloffEnabled(Source* source, bool falloff) { - source->falloff = falloff; +void lovrSourceSetFalloffEnabled(Source* source, bool enabled) { + source->falloff = enabled; +} + +bool lovrSourceIsOcclusionEnabled(Source* source) { + return source->occlusion; +} + +void lovrSourceSetOcclusionEnabled(Source* source, bool enabled) { + source->occlusion = enabled; +} + +bool lovrSourceIsReverbEnabled(Source* source) { + return source->reverb; +} + +void lovrSourceSetReverbEnabled(Source* source, bool enabled) { + source->reverb = enabled; +} + +bool lovrSourceIsTransmissionEnabled(Source* source) { + return source->transmission; +} + +void lovrSourceSetTransmissionEnabled(Source* source, bool enabled) { + source->transmission = enabled; } intptr_t* lovrSourceGetSpatializerMemoField(Source* source) { return &source->spatializerMemo; } + +uint32_t lovrSourceGetIndex(Source* source) { + return source->index; +} diff --git a/src/modules/audio/audio.h b/src/modules/audio/audio.h index 25533efa..80f53750 100644 --- a/src/modules/audio/audio.h +++ b/src/modules/audio/audio.h @@ -21,6 +21,11 @@ typedef enum { UNIT_FRAMES } TimeUnit; +typedef enum { + SOURCE_NEAREST, + SOURCE_BILINEAR +} SourceInterpolation; + typedef void AudioDeviceCallback(const void* id, size_t size, const char* name, bool isDefault, void* userdata); bool lovrAudioInit(const char* spatializer); @@ -40,7 +45,7 @@ struct Sound* lovrAudioGetCaptureStream(void); // Source -Source* lovrSourceCreate(struct Sound* sound, bool spatial); +Source* lovrSourceCreate(struct Sound* sound, bool spatial, bool shared); Source* lovrSourceClone(Source* source); void lovrSourceDestroy(void* ref); bool lovrSourcePlay(Source* source); @@ -55,12 +60,26 @@ double lovrSourceGetDuration(Source* source, TimeUnit units); double lovrSourceGetTime(Source* source, TimeUnit units); void lovrSourceSetTime(Source* source, double time, TimeUnit units); bool lovrSourceIsSpatial(Source* source); +bool lovrSourceIsShared(Source* source); +float lovrSourceGetSpatialBlend(Source* source); +void lovrSourceSetSpatialBlend(Source* source, float blend); +SourceInterpolation lovrSourceGetInterpolation(Source* source); +void lovrSourceSetInterpolation(Source* source, SourceInterpolation interpolation); void lovrSourceGetPose(Source* source, float position[4], float orientation[4]); void lovrSourceSetPose(Source* source, float position[4], float orientation[4]); +float lovrSourceGetRadius(Source* source); +void lovrSourceSetRadius(Source* source, float radius); void lovrSourceGetDirectivity(Source* source, float* weight, float* power); void lovrSourceSetDirectivity(Source* source, float weight, float power); bool lovrSourceIsAbsorptionEnabled(Source* source); void lovrSourceSetAbsorptionEnabled(Source* source, bool enabled); bool lovrSourceIsFalloffEnabled(Source* source); void lovrSourceSetFalloffEnabled(Source* source, bool enabled); +bool lovrSourceIsOcclusionEnabled(Source* source); +void lovrSourceSetOcclusionEnabled(Source* source, bool enabled); +bool lovrSourceIsReverbEnabled(Source* source); +void lovrSourceSetReverbEnabled(Source* source, bool enabled); +bool lovrSourceIsTransmissionEnabled(Source* source); +void lovrSourceSetTransmissionEnabled(Source* source, bool enabled); intptr_t* lovrSourceGetSpatializerMemoField(Source* source); +uint32_t lovrSourceGetIndex(Source* source); diff --git a/src/modules/audio/spatializer_phonon.c b/src/modules/audio/spatializer_phonon.c index 7bdd6ac5..629ffd0a 100644 --- a/src/modules/audio/spatializer_phonon.c +++ b/src/modules/audio/spatializer_phonon.c @@ -4,6 +4,9 @@ #include #include +#define MONO (IPLAudioFormat) { IPL_CHANNELLAYOUTTYPE_SPEAKERS, IPL_CHANNELLAYOUT_MONO, .channelOrder = IPL_CHANNELORDER_INTERLEAVED } +#define STEREO (IPLAudioFormat) { IPL_CHANNELLAYOUTTYPE_SPEAKERS, IPL_CHANNELLAYOUT_STEREO, .channelOrder = IPL_CHANNELORDER_INTERLEAVED } + #ifdef _WIN32 #include #define PHONON_LIBRARY "phonon.dll" @@ -32,15 +35,24 @@ typedef IPLerror fn_iplCreateStaticMesh(IPLhandle scene, IPLint32 numVertices, I typedef IPLerror fn_iplDestroyStaticMesh(IPLhandle* staticMesh); typedef IPLerror fn_iplCreateEnvironment(IPLhandle context, IPLhandle computeDevice, IPLSimulationSettings simulationSettings, IPLhandle scene, IPLhandle probeManager, IPLhandle* environment); typedef IPLvoid fn_iplDestroyEnvironment(IPLhandle* environment); +typedef IPLerror fn_iplCreateEnvironmentalRenderer(IPLhandle context, IPLhandle environment, IPLRenderingSettings renderingSettings, IPLAudioFormat outputFormat, IPLSimulationThreadCreateCallback threadCreateCallback, IPLSimulationThreadDestroyCallback threadDestroyCallback, IPLhandle* renderer); +typedef IPLvoid fn_iplDestroyEnvironmentalRenderer(IPLhandle* renderer); typedef IPLerror fn_iplCreateDirectSoundEffect(IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLRenderingSettings renderingSettings, IPLhandle* effect); typedef IPLvoid fn_iplDestroyDirectSoundEffect(IPLhandle* effect); typedef IPLvoid fn_iplApplyDirectSoundEffect(IPLhandle effect, IPLAudioBuffer inputAudio, IPLDirectSoundPath directSoundPath, IPLDirectSoundEffectOptions options, IPLAudioBuffer outputAudio); +typedef IPLvoid fn_iplFlushDirectSoundEffect(IPLhandle effect); typedef IPLDirectSoundPath fn_iplGetDirectSoundPath(IPLhandle environment, IPLVector3 listenerPosition, IPLVector3 listenerAhead, IPLVector3 listenerUp, IPLSource source, IPLfloat32 sourceRadius, IPLint32 numSamples, IPLDirectOcclusionMode occlusionMode, IPLDirectOcclusionMethod occlusionMethod); typedef IPLerror fn_iplCreateBinauralRenderer(IPLhandle context, IPLRenderingSettings renderingSettings, IPLHrtfParams params, IPLhandle* renderer); typedef IPLvoid fn_iplDestroyBinauralRenderer(IPLhandle* renderer); typedef IPLerror fn_iplCreateBinauralEffect(IPLhandle renderer, IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLhandle* effect); typedef IPLvoid fn_iplDestroyBinauralEffect(IPLhandle* effect); typedef IPLvoid fn_iplApplyBinauralEffect(IPLhandle effect, IPLhandle binauralRenderer, IPLAudioBuffer inputAudio, IPLVector3 direction, IPLHrtfInterpolation interpolation, IPLfloat32 spatialBlend, IPLAudioBuffer outputAudio); +typedef IPLvoid fn_iplFlushBinauralEffect(IPLhandle effect); +typedef IPLerror fn_iplCreateConvolutionEffect(IPLhandle renderer, IPLBakedDataIdentifier identifier, IPLSimulationType simulationType, IPLAudioFormat inputFormat, IPLAudioFormat outputFormat, IPLhandle* effect); +typedef IPLvoid fn_iplDestroyConvolutionEffect(IPLhandle* effect); +typedef IPLvoid fn_iplSetDryAudioForConvolutionEffect(IPLhandle effect, IPLSource source, IPLAudioBuffer dryAudio); +typedef IPLvoid fn_iplGetMixedEnvironmentalAudio(IPLhandle renderer, IPLVector3 listenerPosition, IPLVector3 listenerAhead, IPLVector3 listenerUp, IPLAudioBuffer mixedWetAudio); +typedef IPLvoid fn_iplFlushConvolutionEffect(IPLhandle effect); #define PHONON_DECLARE(f) static fn_##f* phonon_##f; #define PHONON_LOAD(f) phonon_##f = (fn_##f*) phonon_dlsym(state.library, #f); @@ -55,15 +67,24 @@ typedef IPLvoid fn_iplApplyBinauralEffect(IPLhandle effect, IPLhandle binauralRe X(iplDestroyStaticMesh)\ X(iplCreateEnvironment)\ X(iplDestroyEnvironment)\ + X(iplCreateEnvironmentalRenderer)\ + X(iplDestroyEnvironmentalRenderer)\ X(iplCreateDirectSoundEffect)\ X(iplDestroyDirectSoundEffect)\ X(iplApplyDirectSoundEffect)\ + X(iplFlushDirectSoundEffect)\ X(iplGetDirectSoundPath)\ X(iplCreateBinauralRenderer)\ X(iplDestroyBinauralRenderer)\ X(iplCreateBinauralEffect)\ X(iplDestroyBinauralEffect)\ - X(iplApplyBinauralEffect) + X(iplApplyBinauralEffect)\ + X(iplFlushBinauralEffect)\ + X(iplCreateConvolutionEffect)\ + X(iplDestroyConvolutionEffect)\ + X(iplSetDryAudioForConvolutionEffect)\ + X(iplGetMixedEnvironmentalAudio)\ + X(iplFlushConvolutionEffect) PHONON_FOREACH(PHONON_DECLARE) @@ -73,9 +94,12 @@ static struct { IPLhandle scene; IPLhandle mesh; IPLhandle environment; - IPLhandle directSoundEffect; + IPLhandle environmentalRenderer; IPLhandle binauralRenderer; - IPLhandle binauralEffect; + IPLhandle binauralEffect[MAX_SOURCES]; + IPLhandle directSoundEffect[MAX_SOURCES]; + IPLhandle convolutionEffect[MAX_SOURCES]; + IPLRenderingSettings renderingSettings; float listenerPosition[4]; float listenerOrientation[4]; float* scratchpad; @@ -98,37 +122,18 @@ bool phonon_init(SpatializerConfig config) { status = phonon_iplCreateEnvironment(state.context, NULL, simulationSettings, NULL, NULL, &state.environment); if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false; - IPLAudioFormat mono = { - .channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS, - .channelLayout = IPL_CHANNELLAYOUT_MONO, - .channelOrder = IPL_CHANNELORDER_INTERLEAVED - }; - - IPLAudioFormat stereo = { - .channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS, - .channelLayout = IPL_CHANNELLAYOUT_STEREO, - .channelOrder = IPL_CHANNELORDER_INTERLEAVED - }; - - IPLRenderingSettings renderingSettings = { - .samplingRate = config.sampleRate, - .frameSize = config.fixedBufferSize - }; + state.renderingSettings.samplingRate = config.sampleRate; + state.renderingSettings.frameSize = config.fixedBufferSize; + state.renderingSettings.convolutionType = IPL_CONVOLUTIONTYPE_PHONON; state.scratchpad = malloc(config.fixedBufferSize * sizeof(float)); if (!state.scratchpad) return phonon_destroy(), false; - status = phonon_iplCreateDirectSoundEffect(mono, mono, renderingSettings, &state.directSoundEffect); - if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false; - IPLHrtfParams hrtfParams = { .type = IPL_HRTFDATABASETYPE_DEFAULT }; - status = phonon_iplCreateBinauralRenderer(state.context, renderingSettings, hrtfParams, &state.binauralRenderer); - if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false; - - status = phonon_iplCreateBinauralEffect(state.binauralRenderer, mono, stereo, &state.binauralEffect); + status = phonon_iplCreateBinauralRenderer(state.context, state.renderingSettings, hrtfParams, &state.binauralRenderer); if (status != IPL_STATUS_SUCCESS) return phonon_destroy(), false; return true; @@ -136,9 +141,13 @@ bool phonon_init(SpatializerConfig config) { void phonon_destroy() { if (state.scratchpad) free(state.scratchpad); - if (state.binauralEffect) phonon_iplDestroyBinauralEffect(&state.binauralEffect); + for (size_t i = 0; i < MAX_SOURCES; i++) { + if (state.binauralEffect[i]) phonon_iplDestroyBinauralEffect(&state.binauralEffect[i]); + if (state.directSoundEffect[i]) phonon_iplDestroyDirectSoundEffect(&state.directSoundEffect[i]); + if (state.convolutionEffect[i]) phonon_iplDestroyConvolutionEffect(&state.convolutionEffect[i]); + } if (state.binauralRenderer) phonon_iplDestroyBinauralRenderer(&state.binauralRenderer); - if (state.directSoundEffect) phonon_iplDestroyDirectSoundEffect(&state.directSoundEffect); + if (state.environmentalRenderer) phonon_iplDestroyEnvironmentalRenderer(&state.environmentalRenderer); if (state.environment) phonon_iplDestroyEnvironment(&state.environment); if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh); if (state.scene) phonon_iplDestroyStaticMesh(&state.scene); @@ -149,85 +158,83 @@ void phonon_destroy() { } uint32_t phonon_apply(Source* source, const float* input, float* output, uint32_t frames, uint32_t why) { - float forward[4] = { 0.f, 0.f, -1.f }; - float up[4] = { 0.f, 1.f, 0.f }; + IPLAudioBuffer in = { .format = MONO, .numSamples = frames, .interleavedBuffer = (float*) input }; + IPLAudioBuffer tmp = { .format = MONO, .numSamples = frames, .interleavedBuffer = state.scratchpad }; + IPLAudioBuffer out = { .format = STEREO, .numSamples = frames, .interleavedBuffer = output }; - quat_rotate(state.listenerOrientation, forward); - quat_rotate(state.listenerOrientation, up); + uint32_t index = lovrSourceGetIndex(source); - IPLVector3 listenerPosition = { state.listenerPosition[0], state.listenerPosition[1], state.listenerPosition[2] }; - IPLVector3 listenerForward = { forward[0], forward[1], forward[2] }; - IPLVector3 listenerUp = { up[0], up[1], up[2] }; + float x[4], y[4], z[4]; - // Using a matrix may be more effective here? - float position[4], orientation[4], right[4]; + vec3_set(y, 0.f, 1.f, 0.f); + vec3_set(z, 0.f, 0.f, -1.f); + quat_rotate(state.listenerOrientation, y); + quat_rotate(state.listenerOrientation, z); + IPLVector3 listener = { state.listenerPosition[0], state.listenerPosition[1], state.listenerPosition[2] }; + IPLVector3 forward = { z[0], z[1], z[2] }; + IPLVector3 up = { y[0], y[1], y[2] }; + + // TODO maybe this should use a matrix + float position[4], orientation[4]; lovrSourceGetPose(source, position, orientation); - vec3_set(forward, 0.f, 0.f, -1.f); - vec3_set(up, 0.f, 1.f, 0.f); - vec3_set(right, 1.f, 0.f, 0.f); - quat_rotate(orientation, forward); - quat_rotate(orientation, up); - quat_rotate(orientation, right); + vec3_set(x, 1.f, 0.f, 0.f); + vec3_set(y, 0.f, 1.f, 0.f); + vec3_set(z, 0.f, 0.f, -1.f); + quat_rotate(orientation, x); + quat_rotate(orientation, y); + quat_rotate(orientation, z); - float weight; - float power; + float weight, power; lovrSourceGetDirectivity(source, &weight, &power); IPLSource iplSource = { .position = (IPLVector3) { position[0], position[1], position[2] }, - .ahead = (IPLVector3) { forward[0], forward[1], forward[2] }, - .up = (IPLVector3) { up[0], up[1], up[2] }, - .right = (IPLVector3) { right[0], right[1], right[2] }, + .ahead = (IPLVector3) { z[0], z[1], z[2] }, + .up = (IPLVector3) { y[0], y[1], y[2] }, + .right = (IPLVector3) { x[0], x[1], x[2] }, .directivity.dipoleWeight = weight, .directivity.dipolePower = power }; - IPLDirectOcclusionMode occlusionMode = IPL_DIRECTOCCLUSION_TRANSMISSIONBYFREQUENCY; - IPLDirectOcclusionMethod occlusionMethod = IPL_DIRECTOCCLUSION_VOLUMETRIC; - IPLDirectSoundPath path = phonon_iplGetDirectSoundPath(state.environment, listenerPosition, listenerForward, listenerUp, iplSource, .5f, 32, occlusionMode, occlusionMethod); + IPLDirectOcclusionMode occlusion = IPL_DIRECTOCCLUSION_NONE; + IPLDirectOcclusionMethod volumetric = IPL_DIRECTOCCLUSION_RAYCAST; + float radius = 0.f; + IPLint32 rays = 0; + + if (lovrSourceIsOcclusionEnabled(source)) { + occlusion = lovrSourceIsTransmissionEnabled(source) ? IPL_DIRECTOCCLUSION_TRANSMISSIONBYFREQUENCY : IPL_DIRECTOCCLUSION_NOTRANSMISSION; + radius = lovrSourceGetRadius(source); + + if (radius > 0.f) { + volumetric = IPL_DIRECTOCCLUSION_VOLUMETRIC; + rays = 32; + } + } + + IPLDirectSoundPath path = phonon_iplGetDirectSoundPath(state.environment, listener, forward, up, iplSource, radius, rays, occlusion, volumetric); IPLDirectSoundEffectOptions options = { .applyDistanceAttenuation = lovrSourceIsFalloffEnabled(source) ? IPL_TRUE : IPL_FALSE, .applyAirAbsorption = lovrSourceIsAbsorptionEnabled(source) ? IPL_TRUE : IPL_FALSE, .applyDirectivity = weight > 0.f && power > 0.f ? IPL_TRUE : IPL_FALSE, - .directOcclusionMode = occlusionMode + .directOcclusionMode = occlusion }; - IPLAudioFormat mono = { - .channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS, - .channelLayout = IPL_CHANNELLAYOUT_MONO, - .channelOrder = IPL_CHANNELORDER_INTERLEAVED - }; + phonon_iplApplyDirectSoundEffect(state.directSoundEffect[index], in, path, options, tmp); - IPLAudioFormat stereo = { - .channelLayoutType = IPL_CHANNELLAYOUTTYPE_SPEAKERS, - .channelLayout = IPL_CHANNELLAYOUT_STEREO, - .channelOrder = IPL_CHANNELORDER_INTERLEAVED - }; + if (lovrSourceIsShared(source)) { + memset(output, 0, frames * 2 * sizeof(float)); + // ambisonic panning effect + } else { + float blend = lovrSourceGetSpatialBlend(source); + IPLHrtfInterpolation interpolation = lovrSourceGetInterpolation(source) == SOURCE_BILINEAR ? IPL_HRTFINTERPOLATION_BILINEAR : IPL_HRTFINTERPOLATION_NEAREST; + phonon_iplApplyBinauralEffect(state.binauralEffect[index], state.binauralRenderer, tmp, path.direction, interpolation, blend, out); + } - IPLAudioBuffer inputBuffer = { - .format = mono, - .numSamples = frames, - .interleavedBuffer = (float*) input - }; + if (lovrSourceIsReverbEnabled(source)) { + phonon_iplSetDryAudioForConvolutionEffect(state.convolutionEffect[index], iplSource, in); + } - IPLAudioBuffer scratchBuffer = { - .format = mono, - .numSamples = frames, - .interleavedBuffer = state.scratchpad - }; - - IPLAudioBuffer outputBuffer = { - .format = stereo, - .numSamples = frames, - .interleavedBuffer = output - }; - - IPLHrtfInterpolation interpolation = IPL_HRTFINTERPOLATION_NEAREST; - float spatialBlend = 1.f; - - phonon_iplApplyDirectSoundEffect(state.directSoundEffect, inputBuffer, path, options, scratchBuffer); - phonon_iplApplyBinauralEffect(state.binauralEffect, state.binauralRenderer, scratchBuffer, path.direction, interpolation, spatialBlend, outputBuffer); return frames; } @@ -235,7 +242,6 @@ uint32_t phonon_tail(float* scratch, float* output, uint32_t frames) { return 0; } -// TODO lock/transact void phonon_setListenerPose(float position[4], float orientation[4]) { memcpy(state.listenerPosition, position, sizeof(state.listenerPosition)); memcpy(state.listenerOrientation, orientation, sizeof(state.listenerOrientation)); @@ -245,6 +251,7 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh); if (state.scene) phonon_iplDestroyScene(&state.scene); if (state.environment) phonon_iplDestroyEnvironment(&state.environment); + if (state.environmentalRenderer) phonon_iplDestroyEnvironment(&state.environmentalRenderer); IPLMaterial material = (IPLMaterial) { .lowFreqAbsorption = .1f, @@ -265,7 +272,7 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount .numThreads = 1, .irDuration = 1.f, .ambisonicsOrder = 1, - .maxConvolutionSources = 64, + .maxConvolutionSources = MAX_SOURCES, .bakingBatchSize = 1, .irradianceMinDistance = .1f }; @@ -284,6 +291,9 @@ bool phonon_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount status = phonon_iplCreateEnvironment(state.context, NULL, settings, state.scene, NULL, &state.environment); if (status != IPL_STATUS_SUCCESS) goto fail; + status = phonon_iplCreateEnvironmentalRenderer(state.context, state.environment, state.renderingSettings, STEREO, NULL, NULL, &state.environmentalRenderer); + if (status != IPL_STATUS_SUCCESS) goto fail; + free(materials); return true; @@ -292,16 +302,41 @@ fail: if (state.mesh) phonon_iplDestroyStaticMesh(&state.mesh); if (state.scene) phonon_iplDestroyScene(&state.scene); if (state.environment) phonon_iplDestroyEnvironment(&state.environment); + if (state.environmentalRenderer) phonon_iplDestroyEnvironmentalRenderer(&state.environmentalRenderer); phonon_iplCreateEnvironment(state.context, NULL, settings, NULL, NULL, &state.environment); + phonon_iplCreateEnvironmentalRenderer(state.context, state.environment, state.renderingSettings, STEREO, NULL, NULL, &state.environmentalRenderer); return false; } void phonon_sourceCreate(Source* source) { - // + uint32_t index = lovrSourceGetIndex(source); + + if (!state.binauralEffect[index] && !lovrSourceIsShared(source)) { + phonon_iplCreateBinauralEffect(state.binauralRenderer, MONO, STEREO, &state.binauralEffect[index]); + } + + if (!state.directSoundEffect[index]) { + phonon_iplCreateDirectSoundEffect(MONO, MONO, state.renderingSettings, &state.directSoundEffect[index]); + } + + if (!state.convolutionEffect[index]) { + IPLBakedDataIdentifier id = { 0 }; + IPLAudioFormat ambisonic = (IPLAudioFormat) { + .channelLayoutType = IPL_CHANNELLAYOUTTYPE_AMBISONICS, + .ambisonicsOrder = 1, + .ambisonicsOrdering = IPL_AMBISONICSORDERING_FURSEMALHAM, + .ambisonicsNormalization = IPL_AMBISONICSNORMALIZATION_FURSEMALHAM, + .channelOrder = IPL_CHANNELORDER_INTERLEAVED + }; + phonon_iplCreateConvolutionEffect(state.environmentalRenderer, id, IPL_SIMTYPE_REALTIME, MONO, ambisonic, &state.convolutionEffect[index]); + } } void phonon_sourceDestroy(Source* source) { - // + uint32_t index = lovrSourceGetIndex(source); + if (state.binauralEffect[index]) phonon_iplFlushBinauralEffect(state.binauralEffect[index]); + if (state.directSoundEffect[index]) phonon_iplFlushDirectSoundEffect(state.directSoundEffect[index]); + if (state.convolutionEffect[index]) phonon_iplFlushConvolutionEffect(state.convolutionEffect[index]); } Spatializer phononSpatializer = {