Skip to content

Commit 126ce93

Browse files
authored
Merge pull request #144 from winnerspiros/fix-oboe-and-build-warnings-955977207888231247
Fix Oboe audio redirection and Android build warnings
2 parents 3e8b3f1 + 103b925 commit 126ce93

3 files changed

Lines changed: 62 additions & 42 deletions

File tree

osu.Android/AndroidNativeBridgeManager.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public void StartOboeBridge(Scheduler scheduler, Action<double> onLatencyMeasure
3030
}
3131

3232
Debug.WriteLine($"[osu!] Starting Oboe bridge (sampleRate={sampleRate}, hasProvider={provider != IntPtr.Zero})");
33+
cachedOboeStatus = null;
3334

3435
try
3536
{
@@ -83,6 +84,7 @@ public void StopOboeBridge()
8384
Debug.WriteLine("[osu!] Stopping Oboe bridge...");
8485
(oboeBridge as OboeAudioBridge)?.Dispose();
8586
oboeBridge = null;
87+
cachedOboeStatus = null;
8688
Debug.WriteLine("[osu!] Oboe bridge stopped");
8789
}
8890

@@ -96,7 +98,7 @@ public void StopOboeBridge()
9698
public string GetOboeStatus()
9799
{
98100
if (oboeBridge is not OboeAudioBridge bridge) return string.Empty;
99-
return $"{(bridge.IsAAudio ? "AAudio" : "OpenSLES")} [{(bridge.IsMMap ? "MMAP" : "Legacy")}]";
101+
return cachedOboeStatus ??= $"{(bridge.IsAAudio ? "AAudio" : "OpenSLES")} [{(bridge.IsMMap ? "MMAP" : "Legacy")}]";
100102
}
101103

102104
public double GetMeasuredAudioLatencyMs()
@@ -110,6 +112,7 @@ public void StartVulkanProbe()
110112
if (vulkanProbe != null) return;
111113

112114
Debug.WriteLine("[osu!] Starting Vulkan probe...");
115+
cachedVulkanStatus = null;
113116

114117
try
115118
{
@@ -140,13 +143,14 @@ public void StartVulkanProbe()
140143
public string GetVulkanStatus()
141144
{
142145
if (vulkanProbe is not VulkanProbe probe) return string.Empty;
143-
return $"{(probe.SupportsMailboxPresentMode ? "MAILBOX" : "FIFO")}{(probe.DisablePresentId ? " [NoID]" : "")}{(probe.DisablePresentWait ? " [NoWait]" : "")}{(probe.DisableGraphicsPipelineLibrary ? " [NoGPL]" : "")}";
146+
return cachedVulkanStatus ??= $"{(probe.SupportsMailboxPresentMode ? "MAILBOX" : "FIFO")}{(probe.DisablePresentId ? " [NoID]" : "")}{(probe.DisablePresentWait ? " [NoWait]" : "")}{(probe.DisableGraphicsPipelineLibrary ? " [NoGPL]" : "")}";
144147
}
145148

146149
public void StopVulkanProbe()
147150
{
148151
(vulkanProbe as VulkanProbe)?.Dispose();
149152
vulkanProbe = null;
153+
cachedVulkanStatus = null;
150154
Debug.WriteLine("[osu!] Vulkan probe stopped");
151155
}
152156

osu.Android/OboeAudioRedirector.cs

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,20 @@ public void RefreshMixers(int hardwareSampleRate)
4343
lastHardwareSampleRate = hardwareSampleRate;
4444

4545
Console.WriteLine($"[osu!] Oboe redirector: Refreshing mixers with rate {lastHardwareSampleRate}Hz");
46-
restoreDefaultAudio();
46+
47+
// Clean up previous state but keep the silencing if we are already silenced.
48+
ActiveMasterMixer = 0;
49+
if (masterMixer != 0)
50+
{
51+
Bass.StreamFree(masterMixer);
52+
masterMixer = 0;
53+
}
54+
55+
restoreToParents();
4756
mixerHandles.Clear();
4857
originalParents.Clear();
4958

5059
sampleRate = lastHardwareSampleRate;
51-
mixerHandles.Clear();
5260

5361
addRootMixer(audioManager.TrackMixer);
5462
addRootMixer(audioManager.SampleMixer);
@@ -81,8 +89,6 @@ public void RefreshMixers(int hardwareSampleRate)
8189
{
8290
Console.WriteLine("[osu!] Oboe redirector: Failed to setup master mixer, restoring default audio.");
8391
restoreDefaultAudio();
84-
mixerHandles.Clear();
85-
originalParents.Clear();
8692
return;
8793
}
8894

@@ -92,19 +98,28 @@ public void RefreshMixers(int hardwareSampleRate)
9298

9399
private IEnumerable<AudioMixer> getActiveMixers()
94100
{
95-
// Use reflection to access the internal activeMixers list in AudioManager.
96-
// In the official framework, it is an internal BindableList<AudioMixer> activeMixers.
97-
FieldInfo? field = typeof(AudioManager).GetField("activeMixers", BindingFlags.Instance | BindingFlags.NonPublic);
98-
if (field == null) yield break;
101+
// Exhaustive search for the activeMixers list in AudioManager.
102+
Type type = typeof(AudioManager);
99103

100-
object? val = field.GetValue(audioManager);
101-
if (val is IEnumerable enumerable)
104+
while (type != null && type != typeof(object))
102105
{
103-
foreach (var item in enumerable)
106+
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
104107
{
105-
if (item is AudioMixer mixer)
106-
yield return mixer;
108+
if (field.FieldType.IsGenericType && field.FieldType.GetGenericArguments().Contains(typeof(AudioMixer)))
109+
{
110+
object? val = field.GetValue(audioManager);
111+
if (val is IEnumerable enumerable)
112+
{
113+
foreach (var item in enumerable)
114+
{
115+
if (item is AudioMixer mixer)
116+
yield return mixer;
117+
}
118+
yield break;
119+
}
120+
}
107121
}
122+
type = type.BaseType!;
108123
}
109124
}
110125

@@ -129,6 +144,7 @@ private bool setupMasterMixer()
129144
return false;
130145
}
131146

147+
// Disable BASS-internal buffering for the lowest possible latency.
132148
Bass.ChannelSetAttribute(masterMixer, ChannelAttribute.Buffer, 0);
133149

134150
int successfullyAdded = 0;
@@ -137,7 +153,7 @@ private bool setupMasterMixer()
137153
{
138154
int parent = BassMix.ChannelGetMixer(handle);
139155

140-
if (parent != 0)
156+
if (parent != 0 && parent != masterMixer)
141157
{
142158
originalParents[handle] = parent;
143159
BassMix.MixerRemoveChannel(handle);
@@ -149,7 +165,7 @@ private bool setupMasterMixer()
149165
if (!Bass.ChannelSetDevice(handle, 0))
150166
{
151167
Console.WriteLine($"[osu!] Failed to move source mixer {handle} to silent device: {Bass.LastError}");
152-
continue;
168+
// Try to proceed anyway, as some devices might behave strangely with device 0.
153169
}
154170
}
155171

@@ -186,6 +202,20 @@ private bool silenceDefaultAudio()
186202
}
187203
}
188204

205+
private void restoreToParents()
206+
{
207+
foreach (var kvp in originalParents)
208+
{
209+
int handle = kvp.Key;
210+
int parent = kvp.Value;
211+
212+
BassMix.MixerRemoveChannel(handle);
213+
Bass.ChannelSetDevice(handle, 1);
214+
BassMix.MixerAddChannel(parent, handle, BassFlags.MixerChanNoRampin);
215+
}
216+
originalParents.Clear();
217+
}
218+
189219
private void restoreDefaultAudio()
190220
{
191221
ActiveMasterMixer = 0;
@@ -198,16 +228,7 @@ private void restoreDefaultAudio()
198228
masterMixer = 0;
199229
}
200230

201-
// Restore hijacked mixers to their original parents.
202-
foreach (var kvp in originalParents)
203-
{
204-
int handle = kvp.Key;
205-
int parent = kvp.Value;
206-
207-
BassMix.MixerRemoveChannel(handle);
208-
Bass.ChannelSetDevice(handle, 1);
209-
BassMix.MixerAddChannel(parent, handle, BassFlags.MixerChanNoRampin);
210-
}
231+
restoreToParents();
211232

212233
// Restore any other discovered mixers to the default device.
213234
foreach (int handle in mixerHandles)
@@ -218,7 +239,6 @@ private void restoreDefaultAudio()
218239
Bass.ChannelSetDevice(handle, 1);
219240
}
220241

221-
originalParents.Clear();
222242
devicesSilenced = false;
223243
}
224244
catch (Exception e)
@@ -261,31 +281,29 @@ private int findHandle(object obj)
261281

262282
while (type != null && type != typeof(object))
263283
{
284+
// Broad search for anything that looks like a BASS handle.
285+
// We check int, long, and IntPtr as different wrappers use different types.
264286
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
265287
{
266-
if (field.FieldType == typeof(int) || field.FieldType == typeof(IntPtr))
288+
if (isHandleType(field.FieldType))
267289
{
268290
string name = field.Name.ToLowerInvariant();
269-
270-
if (name.Contains("handle") || name.Contains("mixer"))
291+
if (name.Contains("handle") || name.Contains("mixer") || name.Contains("id") || name.Contains("stream") || name.Contains("channel"))
271292
{
272293
int h = convertToHandle(field.GetValue(obj));
273-
274294
if (h != 0) return h;
275295
}
276296
}
277297
}
278298

279299
foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
280300
{
281-
if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(IntPtr))
301+
if (isHandleType(prop.PropertyType))
282302
{
283303
string name = prop.Name.ToLowerInvariant();
284-
285-
if (name.Contains("handle") || name.Contains("mixer"))
304+
if (name.Contains("handle") || name.Contains("mixer") || name.Contains("id") || name.Contains("stream") || name.Contains("channel"))
286305
{
287306
int h = convertToHandle(prop.GetValue(obj));
288-
289307
if (h != 0) return h;
290308
}
291309
}
@@ -297,16 +315,14 @@ private int findHandle(object obj)
297315
return 0;
298316
}
299317

318+
private bool isHandleType(Type type) => type == typeof(int) || type == typeof(IntPtr) || type == typeof(long);
319+
300320
private int convertToHandle(object? val)
301321
{
302322
if (val == null) return 0;
303-
304323
if (val is int ih) return ih;
305-
306324
if (val is long lh) return (int)lh;
307-
308325
if (val is IntPtr ph) return (int)ph.ToInt64();
309-
310326
return 0;
311327
}
312328

@@ -317,7 +333,7 @@ private static int provideAudio(IntPtr audioData, int numFrames)
317333

318334
if (mixer == 0) return 0;
319335

320-
int bytesToRead = numFrames * 8;
336+
int bytesToRead = numFrames * 8; // 2 channels * 4 bytes (float)
321337
int bytesRead = Bass.ChannelGetData(mixer, audioData, bytesToRead);
322338

323339
if (bytesRead <= 0) return 0;

osu.Android/OsuGameAndroid.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ protected override void LoadComplete()
256256
{
257257
// Only redirect audio once the Oboe stream has successfully started.
258258
// This prevents silence if the bridge fails to initialize.
259-
audioRedirector?.RefreshMixers(sampleRate);
259+
audioRedirector?.RefreshMixers(sampleRate > 0 ? sampleRate : hardwareSampleRate);
260260
Debug.WriteLine("[osu!] Audio redirector refreshed with hardware sample rate: " + sampleRate);
261261
});
262262
}

0 commit comments

Comments
 (0)