Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions osu.Android/OboeAudioRedirector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ public void RefreshMixers(int hardwareSampleRate)
}

// User requested Low-Latency Oboe: we MUST use Oboe.
// Silence the default device immediately to prevent duplicated audio.
// Silence the default device and setup routing.
// Order is critical: Device init -> Create Master -> Move Sources -> Add to Master.
silenceDefaultAudio();
setupMasterMixer();

Expand Down Expand Up @@ -93,6 +94,11 @@ private void setupMasterMixer()
return;
}

// Move the master mixer to the silent device immediately.
// This ensures all following operations happen on the same device context.
if (!Bass.ChannelSetDevice(masterMixer, 0))
Debug.WriteLine($"[osu!] Failed to move master mixer to silent device: {Bass.LastError}");

// Disable BASS-internal buffering for the master mixer.
// This ensures BASS renders as fast as possible when we call ChannelGetData.
// This is key for "lowest possible latency" as requested.
Expand All @@ -112,6 +118,11 @@ private void setupMasterMixer()
Debug.WriteLine($"[osu!] Failed to hijack mixer {handle} from parent {parent}: {Bass.LastError}");
}

// Move source mixer to the silent device before adding to master.
// Changing device automatically removes it from any existing mixer.
if (!Bass.ChannelSetDevice(handle, 0))
Debug.WriteLine($"[osu!] Failed to move source mixer {handle} to silent device: {Bass.LastError}");

// Add redirected mixers as sources to our master mixer.
// We remove BASS_MIXER_BUFFER to eliminate internal BASS buffering latency,
// relying entirely on the Oboe callback timing for rock-solid sync.
Expand All @@ -120,9 +131,6 @@ private void setupMasterMixer()
Debug.WriteLine($"[osu!] Failed to add mixer {handle} to master mixer: {Bass.LastError}");
}
}

// Move the master mixer to the silent device too.
Bass.ChannelSetDevice(masterMixer, 0);
}

private void silenceDefaultAudio()
Expand All @@ -137,22 +145,7 @@ private void silenceDefaultAudio()
return;
}

bool allSuccess = true;

// Move all redirected mixers to the silent device.
foreach (int handle in mixerHandles)
{
if (!Bass.ChannelSetDevice(handle, 0))
{
Debug.WriteLine($"[osu!] Failed to move mixer {handle} to silent device: {Bass.LastError}");
allSuccess = false;
}
}

devicesSilenced = allSuccess;

if (allSuccess && mixerHandles.Count > 0)
Debug.WriteLine($"[osu!] BASS mixers ({mixerHandles.Count}) moved to silent device 0 (Oboe active)");
devicesSilenced = true;
}
catch (Exception e)
{
Expand Down Expand Up @@ -180,14 +173,20 @@ private void restoreDefaultAudio()
// Restore to framework's original parent mixer if we hijacked it.
if (originalParents.TryGetValue(handle, out int parent))
{
// MUST move back to default device (1) before re-adding to framework parent.
if (!Bass.ChannelSetDevice(handle, 1))
Debug.WriteLine($"[osu!] Failed to restore mixer {handle} to default device: {Bass.LastError}");

if (BassMix.MixerAddChannel(parent, handle, BassFlags.MixerChanNoRampin))
Debug.WriteLine($"[osu!] Restored mixer {handle} to framework parent {parent}");
else
Debug.WriteLine($"[osu!] Failed to restore mixer {handle} to framework parent {parent}: {Bass.LastError}");
}

if (!Bass.ChannelSetDevice(handle, 1))
Debug.WriteLine($"[osu!] Failed to restore mixer {handle} to default device: {Bass.LastError}");
else
{
// Even if no parent, restore to default device.
Bass.ChannelSetDevice(handle, 1);
}
}

originalParents.Clear();
Expand Down
Loading