@@ -17,77 +17,87 @@ internal sealed class AndroidNativeBridgeManager : IDisposable
1717 private object ? oboeBridge ;
1818 private object ? vulkanProbe ;
1919 private volatile bool disposed ;
20- private string ? cachedOboeStatus ;
21- private string ? cachedVulkanStatus ;
20+ private volatile string ? cachedOboeStatus ;
21+ private volatile string ? cachedVulkanStatus ;
22+ private readonly object oboeLock = new object ( ) ;
2223
2324 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
2425 public void StartOboeBridge ( Scheduler scheduler , Action < double > onLatencyMeasured , IntPtr provider , int sampleRate = 0 , Action < int > ? onStarted = null )
2526 {
26- if ( oboeBridge != null )
27+ lock ( oboeLock )
2728 {
28- Debug . WriteLine ( "[osu!] Oboe bridge already started, ignoring request" ) ;
29- return ;
30- }
31-
32- Debug . WriteLine ( $ "[osu!] Starting Oboe bridge (sampleRate={ sampleRate } , hasProvider={ provider != IntPtr . Zero } )") ;
33- cachedOboeStatus = null ;
29+ if ( oboeBridge != null )
30+ {
31+ Debug . WriteLine ( "[osu!] Oboe bridge already started, ignoring request" ) ;
32+ return ;
33+ }
3434
35- try
36- {
37- var bridge = OboeAudioBridge . Create ( sampleRate ) ;
35+ Debug . WriteLine ( $ "[osu!] Starting Oboe bridge (sampleRate={ sampleRate } , hasProvider={ provider != IntPtr . Zero } )") ;
36+ cachedOboeStatus = null ;
3837
39- if ( bridge != null )
38+ try
4039 {
41- oboeBridge = bridge ;
40+ var bridge = OboeAudioBridge . Create ( sampleRate ) ;
4241
43- if ( provider != IntPtr . Zero )
44- bridge . SetProvider ( provider ) ;
42+ if ( bridge != null )
43+ {
44+ oboeBridge = bridge ;
4545
46- try { SetThreadAffinity ( 0xF8 ) ; } catch { }
47- bool started = bridge . Start ( ) ;
48- if ( ! started ) { System . Threading . Thread . Sleep ( 100 ) ; started = bridge . Start ( ) ; }
46+ if ( provider != IntPtr . Zero )
47+ bridge . SetProvider ( provider ) ;
4948
50- if ( started )
51- {
52- Debug . WriteLine ( "[osu!] Oboe bridge started successfully" ) ;
53- logOboeInfo ( bridge ) ;
49+ try { SetThreadAffinity ( Environment . ProcessorCount > 4 ? 0xF0 : 0x0C ) ; }
50+ catch ( Exception e ) { Debug . WriteLine ( $ "[osu!] Audio thread affinity failed: { e . Message } ") ; }
5451
55- onStarted ? . Invoke ( bridge . SampleRate ) ;
52+ bool started = bridge . Start ( ) ;
53+ if ( ! started ) { System . Threading . Thread . Sleep ( 100 ) ; started = bridge . Start ( ) ; }
5654
57- scheduler . Add ( new ScheduledDelegate ( ( ) =>
55+ if ( started )
5856 {
59- if ( oboeBridge is not OboeAudioBridge b ) return ;
57+ Debug . WriteLine ( "[osu!] Oboe bridge started successfully" ) ;
58+ logOboeInfo ( bridge ) ;
59+
60+ onStarted ? . Invoke ( bridge . SampleRate ) ;
6061
61- double latency = b . GetOutputLatencyMs ( ) ;
62+ scheduler . Add ( new ScheduledDelegate ( ( ) =>
63+ {
64+ if ( oboeBridge is not OboeAudioBridge b ) return ;
6265
63- if ( latency > 0 )
64- onLatencyMeasured ( latency ) ;
65- } , 2000 , 5000 ) ) ;
66+ double latency = b . GetOutputLatencyMs ( ) ;
67+
68+ if ( latency > 0 )
69+ onLatencyMeasured ( latency ) ;
70+ } , 2000 , 5000 ) ) ;
71+ }
72+ else
73+ {
74+ string error = bridge . GetLastErrorMessage ( ) ?? "Unknown" ;
75+ Debug . WriteLine ( $ "[osu!] Oboe bridge created but failed to start: { error } ") ;
76+ }
6677 }
6778 else
6879 {
69- Debug . WriteLine ( "[osu!] Oboe bridge created but failed to start (Start() returned false) " ) ;
80+ Debug . WriteLine ( "[osu!] Oboe bridge creation failed — native library not loaded or stream open failed " ) ;
7081 }
7182 }
72- else
83+ catch ( Exception e )
7384 {
74- Debug . WriteLine ( "[osu!] Oboe bridge creation failed (Create() returned null) " ) ;
85+ Debug . WriteLine ( $ "[osu!] Oboe bridge init failed with exception: { e . Message } ") ;
7586 }
7687 }
77- catch ( Exception e )
78- {
79- Debug . WriteLine ( $ "[osu!] Oboe bridge init failed with exception: { e . Message } ") ;
80- }
8188 }
8289
8390 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
8491 public void StopOboeBridge ( )
8592 {
86- Debug . WriteLine ( "[osu!] Stopping Oboe bridge..." ) ;
87- ( oboeBridge as OboeAudioBridge ) ? . Dispose ( ) ;
88- oboeBridge = null ;
89- cachedOboeStatus = null ;
90- Debug . WriteLine ( "[osu!] Oboe bridge stopped" ) ;
93+ lock ( oboeLock )
94+ {
95+ Debug . WriteLine ( "[osu!] Stopping Oboe bridge..." ) ;
96+ ( oboeBridge as OboeAudioBridge ) ? . Dispose ( ) ;
97+ oboeBridge = null ;
98+ cachedOboeStatus = null ;
99+ Debug . WriteLine ( "[osu!] Oboe bridge stopped" ) ;
100+ }
91101 }
92102
93103 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
@@ -100,7 +110,13 @@ public void StopOboeBridge()
100110 public string GetOboeStatus ( )
101111 {
102112 if ( oboeBridge is not OboeAudioBridge bridge ) return "Not Created" ;
103- if ( ! bridge . IsActive ) return "Failed: " + bridge . GetLastErrorMessage ( ) ;
113+
114+ if ( ! bridge . IsActive )
115+ {
116+ try { return "Failed: " + ( bridge . GetLastErrorMessage ( ) ?? "Unknown" ) ; }
117+ catch { return "Failed: Unknown" ; }
118+ }
119+
104120 return cachedOboeStatus ??= $ "{ ( bridge . IsAAudio ? "AAudio" : "OpenSLES" ) } [{ ( bridge . IsMMap ? "MMAP" : "Legacy" ) } ]";
105121 }
106122
0 commit comments