@@ -30,8 +30,9 @@ struct Source {
3030 uint32_t slot ;
3131 Sound * sound ;
3232 ma_data_converter * converter ;
33- float volume ;
3433 float pitch ;
34+ float volume ;
35+ float reverb ;
3536 float position [3 ];
3637 float radius ;
3738 float orientation [4 ];
@@ -87,6 +88,7 @@ static struct {
8788 float position [3 ];
8889 float orientation [4 ];
8990 float absorption [3 ];
91+ float reverb ;
9092#ifdef LOVR_USE_PHONON
9193 IPLContext phonon ;
9294 IPLAudioSettings audioSettings ;
@@ -96,9 +98,13 @@ static struct {
9698 IPLScene scene ;
9799 bool sceneDirty ;
98100 IPLHRTF hrtf ;
101+ IPLSource listener ;
102+ bool listenerAdded ;
99103 IPLCoordinateSpace3 listenerBasis [2 ];
104+ IPLReflectionEffect reflectionEffect ;
100105 IPLReflectionMixer reflectionMixer ;
101106 IPLAudioBuffer reflectionBuffer ;
107+ IPLAudioBuffer listenerReverbInput ;
102108 IPLAmbisonicsDecodeEffect ambisonicsDecodeEffect ;
103109 atomic_bool reverbFinished ;
104110 float reverbTimer ;
@@ -287,6 +293,8 @@ bool lovrAudioInit(AudioConfig* config) {
287293 state .absorption [1 ] = .0017f ;
288294 state .absorption [2 ] = .0182f ;
289295
296+ state .reverb = 1.f ;
297+
290298 quat_identity (state .orientation );
291299 return true;
292300}
@@ -486,6 +494,14 @@ void lovrAudioSetAbsorption(float absorption[3]) {
486494 memcpy (state .absorption , absorption , 3 * sizeof (float ));
487495}
488496
497+ float lovrAudioGetReverb (void ) {
498+ return state .reverb ;
499+ }
500+
501+ void lovrAudioSetReverb (float reverb ) {
502+ state .reverb = reverb ;
503+ }
504+
489505// Source
490506
491507Source * lovrSourceCreate (Sound * sound , bool pitchable , bool spatial , uint32_t effects ) {
@@ -494,8 +510,9 @@ Source* lovrSourceCreate(Sound* sound, bool pitchable, bool spatial, uint32_t ef
494510 Source * source = lovrCalloc (sizeof (Source ));
495511 source -> ref = 1 ;
496512 source -> slot = ~0u ;
497- source -> pitch = 1.f ;
498513 source -> volume = 1.f ;
514+ source -> pitch = 1.f ;
515+ source -> reverb = 0.f ;
499516 source -> pitchable = pitchable ;
500517 source -> spatial = spatial ;
501518 source -> effects = spatial ? effects : 0 ;
@@ -658,6 +675,14 @@ void lovrSourceSetVolume(Source* source, float volume, VolumeUnit units) {
658675 source -> volume = CLAMP (volume , 0.f , 1.f );
659676}
660677
678+ float lovrSourceGetReverb (Source * source ) {
679+ return source -> reverb ;
680+ }
681+
682+ void lovrSourceSetReverb (Source * source , float reverb ) {
683+ source -> reverb = reverb ;
684+ }
685+
661686void lovrSourceSeek (Source * source , double time , TimeUnit units ) {
662687 source -> seekRequest = units == UNIT_SECONDS ? (uint32_t ) (time * lovrSoundGetSampleRate (source -> sound ) + .5 ) : (uint32_t ) time ;
663688}
@@ -855,6 +880,18 @@ static bool phonon_init(void) {
855880
856881 iplSimulatorSetScene (state .simulator , state .scene );
857882
883+ IPLSourceSettings sourceSettings = {
884+ .flags = IPL_SIMULATIONFLAGS_REFLECTIONS
885+ };
886+
887+ if (iplSourceCreate (state .simulator , & sourceSettings , & state .listener )) {
888+ return phonon_destroy (), lovrSetError ("Failed to create listener source" );
889+ }
890+
891+ if (iplReflectionEffectCreate (state .phonon , & state .audioSettings , & state .reflectionSettings , & state .reflectionEffect )) {
892+ return phonon_destroy (), lovrSetError ("Failed to create reflection effect" );
893+ }
894+
858895 if (state .config .reverb .mode == REVERB_CONVOLUTION ) {
859896 if (iplReflectionMixerCreate (state .phonon , & state .audioSettings , & state .reflectionSettings , & state .reflectionMixer )) {
860897 return phonon_destroy (), lovrSetError ("Failed to create reverb mixer" );
@@ -875,15 +912,26 @@ static bool phonon_init(void) {
875912 return phonon_destroy (), lovrSetError ("Failed to create reverb buffer" );
876913 }
877914
915+ if (iplAudioBufferAllocate (state .phonon , 1 , BUFFER_SIZE , & state .listenerReverbInput )) {
916+ return phonon_destroy (), lovrSetError ("Failed to create reverb buffer" );
917+ }
918+
878919 atomic_store (& state .reverbFinished , true);
879920
880921 return true;
881922}
882923
883924static void phonon_destroy (void ) {
925+ while (!atomic_load (& state .reverbFinished )) {
926+ job_spin ();
927+ }
928+
884929 iplAmbisonicsDecodeEffectRelease (& state .ambisonicsDecodeEffect ), state .ambisonicsDecodeEffect = NULL ;
930+ iplAudioBufferFree (state .phonon , & state .listenerReverbInput ), state .listenerReverbInput .data = NULL ;
885931 iplAudioBufferFree (state .phonon , & state .reflectionBuffer ), state .reflectionBuffer .data = NULL ;
886932 iplReflectionMixerRelease (& state .reflectionMixer ), state .reflectionMixer = NULL ;
933+ iplReflectionEffectRelease (& state .reflectionEffect ), state .reflectionEffect = NULL ;
934+ iplSourceRelease (& state .listener ), state .listener = NULL ;
887935 iplHRTFRelease (& state .hrtf ), state .hrtf = NULL ;
888936 iplSceneRelease (& state .scene ), state .scene = NULL ;
889937 iplSimulatorRelease (& state .simulator ), state .simulator = NULL ;
@@ -898,6 +946,14 @@ static void phonon_update(float dt) {
898946
899947 // TODO maybe split into 2 simulators so we can have less latency on direct simulation commits
900948 if (atomic_load (& state .reverbFinished )) {
949+ if (!state .listenerAdded && state .reverb > 0.f ) {
950+ iplSourceAdd (state .listener , state .simulator );
951+ state .listenerAdded = true;
952+ } else if (state .listenerAdded && state .reverb <= 0.f ) {
953+ iplSourceRemove (state .listener , state .simulator );
954+ state .listenerAdded = false;
955+ }
956+
901957 iplSimulatorCommit (state .simulator );
902958 }
903959
@@ -927,7 +983,6 @@ static void phonon_update(float dt) {
927983 [EFFECT_ABSORPTION ] = IPL_DIRECTSIMULATIONFLAGS_AIRABSORPTION ,
928984 [EFFECT_ATTENUATION ] = IPL_DIRECTSIMULATIONFLAGS_DISTANCEATTENUATION ,
929985 [EFFECT_OCCLUSION ] = IPL_DIRECTSIMULATIONFLAGS_OCCLUSION ,
930- [EFFECT_REVERB ] = 0 ,
931986 [EFFECT_SPATIALIZATION ] = 0 ,
932987 [EFFECT_TRANSMISSION ] = IPL_DIRECTSIMULATIONFLAGS_TRANSMISSION
933988 };
@@ -940,7 +995,7 @@ static void phonon_update(float dt) {
940995 }
941996 }
942997
943- hasReverb |= !!( source -> effects & ( 1 << EFFECT_REVERB )) ;
998+ hasReverb |= source -> reverb > 0.f ;
944999
9451000 source -> inputs .directivity .dipoleWeight = source -> dipoleWeight ;
9461001 source -> inputs .directivity .dipolePower = source -> dipolePower ;
@@ -960,13 +1015,23 @@ static void phonon_update(float dt) {
9601015
9611016 atomic_fetch_xor (& state .backbuffer , 0x1 );
9621017
963- if (hasReverb ) {
1018+ if (hasReverb || state . reverb > 0.f ) {
9641019 state .reverbTimer -= dt ;
9651020
9661021 if (state .reverbTimer <= 0.f && atomic_load (& state .reverbFinished )) {
9671022 atomic_store (& state .reverbFinished , false);
9681023 state .reverbTimer = state .config .reverb .rate ;
9691024
1025+ if (state .reverb > 0.f ) {
1026+ IPLSimulationInputs inputs = { 0 };
1027+ inputs .flags = IPL_SIMULATIONFLAGS_REFLECTIONS ;
1028+ inputs .source = sharedInputs .listener ;
1029+ inputs .reverbScale [0 ] = 1.f ;
1030+ inputs .reverbScale [1 ] = 1.f ;
1031+ inputs .reverbScale [2 ] = 1.f ;
1032+ iplSourceSetInputs (state .listener , IPL_SIMULATIONFLAGS_REFLECTIONS , & inputs );
1033+ }
1034+
9701035 Source * source ;
9711036 FOREACH_SOURCE (mask , source ) {
9721037 iplSourceSetInputs (source -> handle , IPL_SIMULATIONFLAGS_REFLECTIONS , & source -> inputs );
@@ -1008,6 +1073,10 @@ static void phonon_mix_begin(void) {
10081073 if (state .config .reverb .mode == REVERB_PARAMETRIC ) {
10091074 memset (state .reflectionBuffer .data [0 ], 0 , BUFFER_SIZE * sizeof (float ));
10101075 }
1076+
1077+ if (state .reverb > 0.f ) {
1078+ memset (state .listenerReverbInput .data [0 ], 0 , BUFFER_SIZE * sizeof (float ));
1079+ }
10111080}
10121081
10131082static bool phonon_mix_source (Source * source , float * _src , float * dst , float * _tmp ) {
@@ -1026,9 +1095,9 @@ static bool phonon_mix_source(Source* source, float* _src, float* dst, float* _t
10261095 memset (dst , 0 , 2 * BUFFER_SIZE * sizeof (float ));
10271096 }
10281097
1029- if (( source -> effects & ( 1 << EFFECT_REVERB )) && iplReflectionEffectGetTailSize (source -> reflectionEffect ) > 0 ) {
1098+ if (source -> reverb > 0.f && iplReflectionEffectGetTailSize (source -> reflectionEffect ) > 0 ) {
10301099 if (state .config .reverb .mode == REVERB_CONVOLUTION ) {
1031- tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , NULL , state .reflectionMixer );
1100+ tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & tmp1 , state .reflectionMixer );
10321101 } else {
10331102 tail |= !iplReflectionEffectGetTail (source -> reflectionEffect , & tmp1 , NULL );
10341103 iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
@@ -1040,7 +1109,7 @@ static bool phonon_mix_source(Source* source, float* _src, float* dst, float* _t
10401109
10411110 // Feed raw audio to reflection effect (use reflection mixer for convolution, or mix mono reverb
10421111 // into reflectionBuffer for parametric)
1043- if (source -> effects & ( 1 << EFFECT_REVERB ) ) {
1112+ if (source -> reverb > 0.f ) {
10441113 IPLSimulationOutputs outputs = { 0 };
10451114 iplSourceGetOutputs (source -> handle , IPL_SIMULATIONFLAGS_REFLECTIONS , & outputs );
10461115
@@ -1058,6 +1127,13 @@ static bool phonon_mix_source(Source* source, float* _src, float* dst, float* _t
10581127 tail |= !iplDirectEffectApply (source -> directEffect , params , & src , & src );
10591128 }
10601129
1130+ // Accumulate post-direct-effect mono audio for listener-centric reverb
1131+ if (state .reverb > 0.f && source -> reverb <= 0.f ) {
1132+ for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1133+ state .listenerReverbInput .data [0 ][i ] += _src [i ];
1134+ }
1135+ }
1136+
10611137 // Spatialize to stereo (either binaural, panning, or upmix)
10621138 if (source -> effects & (1 << EFFECT_SPATIALIZATION )) {
10631139 if (source -> hrtf ) {
@@ -1089,18 +1165,43 @@ static bool phonon_mix_source(Source* source, float* _src, float* dst, float* _t
10891165}
10901166
10911167static void phonon_mix_tail (float * dst , float * _tmp ) {
1092- IPLAudioBuffer tmp = { 2 , BUFFER_SIZE , .data = (float * [2 ]) { _tmp , _tmp + BUFFER_SIZE } };
1168+ IPLAudioBuffer tmp1 = { .numChannels = 1 , .numSamples = BUFFER_SIZE , .data = & _tmp };
1169+ IPLAudioBuffer tmp2 = { .numChannels = 2 , .numSamples = BUFFER_SIZE , .data = (float * [2 ]) { _tmp , _tmp + BUFFER_SIZE } };
10931170
1094- // TODO?
10951171 Source * source ;
10961172 bool hasReverb = false;
10971173 FOREACH_SOURCE (state .activeSourceMask , source ) {
1098- if (source -> effects & ( 1 << EFFECT_REVERB ) ) {
1174+ if (source -> reverb > 0.f || iplReflectionEffectGetTailSize ( source -> reflectionEffect ) > 0 ) {
10991175 hasReverb = true;
11001176 break ;
11011177 }
11021178 }
11031179
1180+ // Listener-centric reverb
1181+ if (state .reverb > 0.f ) {
1182+ IPLSimulationOutputs outputs = { 0 };
1183+ iplSourceGetOutputs (state .listener , IPL_SIMULATIONFLAGS_REFLECTIONS , & outputs );
1184+
1185+ if (state .config .reverb .mode == REVERB_CONVOLUTION ) {
1186+ iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & tmp1 , state .reflectionMixer );
1187+ } else {
1188+ iplReflectionEffectApply (state .reflectionEffect , & outputs .reflections , & state .listenerReverbInput , & tmp1 , NULL );
1189+ iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1190+ }
1191+
1192+ hasReverb = true;
1193+ } else if (iplReflectionEffectGetTailSize (state .reflectionEffect ) > 0 ) {
1194+ if (state .config .reverb .mode == REVERB_CONVOLUTION ) {
1195+ iplReflectionEffectGetTail (state .reflectionEffect , & tmp1 , state .reflectionMixer );
1196+ } else {
1197+ iplReflectionEffectGetTail (state .reflectionEffect , & tmp1 , NULL );
1198+ iplAudioBufferMix (state .phonon , & tmp1 , & state .reflectionBuffer );
1199+ }
1200+
1201+ hasReverb = true;
1202+ }
1203+
1204+ // Final reverb mix
11041205 if (state .config .reverb .mode == REVERB_CONVOLUTION ) {
11051206 if (hasReverb ) {
11061207 IPLReflectionEffectParams reflectionMixerParams = {
@@ -1116,15 +1217,15 @@ static void phonon_mix_tail(float* dst, float* _tmp) {
11161217 .binaural = !!state .hrtf
11171218 };
11181219
1119- iplAmbisonicsDecodeEffectApply (state .ambisonicsDecodeEffect , & ambisonicsDecodeParams , & state .reflectionBuffer , & tmp );
1220+ iplAmbisonicsDecodeEffectApply (state .ambisonicsDecodeEffect , & ambisonicsDecodeParams , & state .reflectionBuffer , & tmp2 );
11201221 } else if (iplAmbisonicsDecodeEffectGetTailSize (state .ambisonicsDecodeEffect ) > 0 ) {
1121- iplAmbisonicsDecodeEffectGetTail (state .ambisonicsDecodeEffect , & tmp );
1222+ iplAmbisonicsDecodeEffectGetTail (state .ambisonicsDecodeEffect , & tmp2 );
11221223 }
11231224
11241225 // Interleave and mix
11251226 for (uint32_t i = 0 ; i < BUFFER_SIZE ; i ++ ) {
1126- dst [2 * i + 0 ] += tmp .data [0 ][i ];
1127- dst [2 * i + 1 ] += tmp .data [1 ][i ];
1227+ dst [2 * i + 0 ] += tmp2 .data [0 ][i ];
1228+ dst [2 * i + 1 ] += tmp2 .data [1 ][i ];
11281229 }
11291230 } else if (hasReverb ) {
11301231 // Parametric reverb: just upmix mono reflection buffer to stereo and mix into dst
@@ -1333,7 +1434,7 @@ static void phonon_mesh_destroy(AudioMesh* mesh) {
13331434}
13341435
13351436static void phonon_mesh_set_enabled (AudioMesh * mesh , bool enable ) {
1336- if (mesh -> enabled = = enable ) {
1437+ if (mesh -> enabled ! = enable ) {
13371438 if (enable ) {
13381439 iplInstancedMeshAdd (mesh -> instancedMesh , state .scene );
13391440 } else {
0 commit comments