Skip to content

Commit 43d6c43

Browse files
committed
Add streaming camera listener in userspace
1 parent ba90187 commit 43d6c43

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

ShowmanTools/LinuxStreamingAudioFix.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,52 @@
22
using FrooxEngine;
33
using HarmonyLib;
44
using MonkeyLoader.Resonite;
5+
using System.Runtime.CompilerServices;
56

67
namespace ShowmanTools
78
{
89
[HarmonyPatch]
910
[HarmonyPatchCategory(nameof(LinuxStreamingAudioFix))]
1011
internal sealed class LinuxStreamingAudioFix : ResoniteMonkey<LinuxStreamingAudioFix>
1112
{
13+
private static readonly ConditionalWeakTable<InteractiveCamera, AudioListener> _secondaryListenersByCamera = [];
14+
private static AudioListener? _secondaryUserspaceListener;
15+
1216
public override bool CanBeDisabled => true;
1317

18+
[HarmonyPostfix]
19+
[HarmonyPatch(typeof(InteractiveCamera), nameof(InteractiveCamera.OnCommonUpdate))]
20+
private static void OnCommonUpdatePostfix(InteractiveCamera __instance)
21+
{
22+
if (!Enabled || __instance.Control is null || __instance.LocalUserRoot is null)
23+
return;
24+
25+
_secondaryListenersByCamera.TryGetValue(__instance, out var secondaryListener);
26+
27+
if (secondaryListener is not null && (__instance.Control.IsDestroyed || __instance.Control.AudioFromCameraViewpoint || __instance.AudioSystem.StreamingOutput.Device is null))
28+
{
29+
secondaryListener.Slot.Destroy();
30+
_secondaryListenersByCamera.Remove(__instance);
31+
32+
return;
33+
}
34+
35+
if (__instance.Control.IsDestroyed || __instance.Control.AudioFromCameraViewpoint || __instance.AudioSystem.StreamingOutput.Device is null)
36+
return;
37+
38+
if (secondaryListener is null)
39+
{
40+
var secondarySlot = __instance.LocalUserRoot.PrimaryListener.Target.Slot.Duplicate();
41+
secondarySlot.Name = "Camera Listener";
42+
43+
secondaryListener = secondarySlot.GetComponent<AudioListener>();
44+
secondaryListener.TargetOutput.Value = AudioListener.ListenerTarget.StreamingCamera;
45+
secondaryListener.Enabled = true;
46+
47+
_secondaryListenersByCamera.Add(__instance, secondaryListener);
48+
}
49+
}
50+
1451
[HarmonyPrefix]
1552
[HarmonyPatch(typeof(AudioSystem), nameof(AudioSystem.UpdateDefaultAudioOutput))]
1653
private static bool UpdateDefaultAudioOutputPrefix(AudioSystem __instance, Action<AudioOutputDriver>? ___DefaultAudioOutputChanged)
@@ -71,6 +108,7 @@ private static bool UpdateDefaultAudioOutputPrefix(AudioSystem __instance, Actio
71108
newDefault.RenderAudio = __instance.RenderAudio;
72109
newDefault.Start("Default Output");
73110

111+
// newStreaming will never be the same as newDefault
74112
if (newStreaming is not null)
75113
{
76114
newStreaming.RenderAudio = __instance.RenderAudio;
@@ -80,6 +118,40 @@ private static bool UpdateDefaultAudioOutputPrefix(AudioSystem __instance, Actio
80118
__instance.PrimaryOutput.Device = newDefault;
81119
__instance.StreamingOutput.Device = newStreaming;
82120

121+
Userspace.Current.RunSynchronously(() =>
122+
{
123+
if (newStreaming is not null && _secondaryUserspaceListener is null)
124+
{
125+
var secondarySlot = Userspace.Current.LocalUserRoot.PrimaryListener.Target.Slot.Parent.AddSlot("Camera Listener");
126+
127+
_secondaryUserspaceListener = secondarySlot.AttachComponent<AudioListener>(false);
128+
_secondaryUserspaceListener.ActiveUser.Target = Userspace.Current.LocalUser;
129+
_secondaryUserspaceListener.TargetOutput.Value = AudioListener.ListenerTarget.StreamingCamera;
130+
_secondaryUserspaceListener.Enabled = true;
131+
132+
// Add 'reverb' effect that mutes everything
133+
var reverb = secondarySlot.AttachComponent<AudioZitaReverb>(false);
134+
reverb.InDelay.Value = 0;
135+
reverb.Crossover.Value = 20;
136+
reverb.RT60Low.Value = .05f;
137+
reverb.RT60Mid.Value = .05f;
138+
reverb.HighFrequencyDamping.Value = 20000;
139+
reverb.EQ1Frequency.Value = 250;
140+
reverb.EQ1Level.Value = 0;
141+
reverb.EQ2Frequency.Value = 20000;
142+
reverb.EQ2Level.Value = 0;
143+
reverb.Mix.Value = 1;
144+
reverb.Level.Value = -90;
145+
146+
_secondaryUserspaceListener.Effects.Add(reverb);
147+
}
148+
else
149+
{
150+
_secondaryUserspaceListener?.Slot.Destroy();
151+
_secondaryUserspaceListener = null;
152+
}
153+
});
154+
83155
___DefaultAudioOutputChanged?.Invoke(newDefault);
84156
return false;
85157
}

ShowmanTools/ShowMustGoOn.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ private static bool MuteCheck(Component audioStream)
3232
.GetValue<ISyncRef>()
3333
.Target;
3434

35+
if (stream is null)
36+
return false;
37+
3538
var isAudioStream = _audioStreams.TryGetValue(stream, out _);
3639

3740
return world.Focus == World.WorldFocus.Focused

0 commit comments

Comments
 (0)