Skip to content

Commit 2fd63e6

Browse files
committed
Listener-centric reverb;
Not sure about final API yet
1 parent e76a998 commit 2fd63e6

File tree

6 files changed

+154
-19
lines changed

6 files changed

+154
-19
lines changed

etc/boot.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ local conf = {
2323
reverb = {
2424
mode = 'convolution',
2525
rays = 4096,
26-
bounces = 4,
26+
bounces = 16,
2727
duration = 2,
2828
rate = .1
2929
}

src/api/l_audio.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ StringEntry lovrEffect[] = {
1010
[EFFECT_ABSORPTION] = ENTRY("absorption"),
1111
[EFFECT_ATTENUATION] = ENTRY("attenuation"),
1212
[EFFECT_OCCLUSION] = ENTRY("occlusion"),
13-
[EFFECT_REVERB] = ENTRY("reverb"),
1413
[EFFECT_SPATIALIZATION] = ENTRY("spatialization"),
1514
[EFFECT_TRANSMISSION] = ENTRY("transmission"),
1615
{ 0 }
@@ -229,6 +228,18 @@ static int l_lovrAudioSetAbsorption(lua_State* L) {
229228
return 0;
230229
}
231230

231+
static int l_lovrAudioGetReverb(lua_State* L) {
232+
float reverb = lovrAudioGetReverb();
233+
lua_pushnumber(L, reverb);
234+
return 1;
235+
}
236+
237+
static int l_lovrAudioSetReverb(lua_State* L) {
238+
float reverb = luax_optfloat(L, 1, 0.f);
239+
lovrAudioSetReverb(reverb);
240+
return 0;
241+
}
242+
232243
static int l_lovrAudioNewSource(lua_State* L) {
233244
Sound* sound = luax_totype(L, 1, Sound);
234245

@@ -336,6 +347,8 @@ static const luaL_Reg lovrAudio[] = {
336347
{ "getSampleRate", l_lovrAudioGetSampleRate },
337348
{ "getAbsorption", l_lovrAudioGetAbsorption },
338349
{ "setAbsorption", l_lovrAudioSetAbsorption },
350+
{ "getReverb", l_lovrAudioGetReverb },
351+
{ "setReverb", l_lovrAudioSetReverb },
339352
{ "newSource", l_lovrAudioNewSource },
340353
{ "newAudioMesh", l_lovrAudioNewAudioMesh },
341354
{ NULL, NULL }

src/api/l_audio_source.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ static int l_lovrSourceSetVolume(lua_State* L) {
8383
return 0;
8484
}
8585

86+
static int l_lovrSourceGetReverb(lua_State* L) {
87+
Source* source = luax_checktype(L, 1, Source);
88+
float reverb = lovrSourceGetReverb(source);
89+
lua_pushnumber(L, reverb);
90+
return 1;
91+
}
92+
93+
static int l_lovrSourceSetReverb(lua_State* L) {
94+
Source* source = luax_checktype(L, 1, Source);
95+
float reverb = luax_optfloat(L, 2, 0.f);
96+
lovrSourceSetReverb(source, reverb);
97+
return 0;
98+
}
99+
86100
static int l_lovrSourceSeek(lua_State* L) {
87101
Source* source = luax_checktype(L, 1, Source);
88102
double seconds = luaL_checknumber(L, 2);
@@ -237,6 +251,8 @@ const luaL_Reg lovrSource[] = {
237251
{ "setPitch", l_lovrSourceSetPitch },
238252
{ "getVolume", l_lovrSourceGetVolume },
239253
{ "setVolume", l_lovrSourceSetVolume },
254+
{ "getReverb", l_lovrSourceGetReverb },
255+
{ "setReverb", l_lovrSourceSetReverb },
240256
{ "seek", l_lovrSourceSeek },
241257
{ "tell", l_lovrSourceTell },
242258
{ "getDuration", l_lovrSourceGetDuration },

src/core/job.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ bool job_init(uint32_t count, void (*setupWorker)(uint32_t id)) {
7070
}
7171

7272
void job_destroy(void) {
73+
mtx_lock(&state.lock);
7374
state.quit = true;
75+
mtx_unlock(&state.lock);
7476
cnd_broadcast(&state.hasJob);
7577
for (uint32_t i = 0; i < state.workerCount; i++) {
7678
thrd_join(state.workers[i], NULL);

src/modules/audio/audio.c

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

491507
Source* 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+
661686
void 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

883924
static 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

10131082
static 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

10911167
static 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

13351436
static 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

Comments
 (0)