22using FrooxEngine ;
33using HarmonyLib ;
44using MonkeyLoader . Resonite ;
5+ using System . Runtime . CompilerServices ;
56
67namespace 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 }
0 commit comments