Skip to content

Commit

Permalink
Merge branch 'master' into fix-windows-ink
Browse files Browse the repository at this point in the history
  • Loading branch information
peppy committed Dec 27, 2023
2 parents b29a828 + deae25b commit 23870c3
Show file tree
Hide file tree
Showing 22 changed files with 282 additions and 35 deletions.
3 changes: 3 additions & 0 deletions osu.Framework.Android/Input/AndroidJoystickHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace osu.Framework.Android.Input
{
public class AndroidJoystickHandler : AndroidInputHandler
{
private static readonly GlobalStatistic<ulong> statistic_total_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<AndroidJoystickHandler>(), "Total events");

public BindableFloat DeadzoneThreshold { get; } = new BindableFloat(0.1f)
{
MinValue = 0,
Expand Down Expand Up @@ -223,6 +225,7 @@ private void enqueueInput(IInput input)
{
PendingInputs.Enqueue(input);
FrameStatistics.Increment(StatisticsCounterType.JoystickEvents);
statistic_total_events.Value++;
}
}
}
3 changes: 3 additions & 0 deletions osu.Framework.Android/Input/AndroidKeyboardHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace osu.Framework.Android.Input
{
public class AndroidKeyboardHandler : AndroidInputHandler
{
private static readonly GlobalStatistic<ulong> statistic_total_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<AndroidKeyboardHandler>(), "Total events");

protected override IEnumerable<InputSourceType> HandledEventSources => new[]
{
InputSourceType.Keyboard,
Expand Down Expand Up @@ -209,6 +211,7 @@ private void enqueueInput(IInput input)
{
PendingInputs.Enqueue(input);
FrameStatistics.Increment(StatisticsCounterType.KeyEvents);
statistic_total_events.Value++;
}
}
}
3 changes: 3 additions & 0 deletions osu.Framework.Android/Input/AndroidMouseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace osu.Framework.Android.Input
/// </summary>
public class AndroidMouseHandler : AndroidInputHandler
{
private static readonly GlobalStatistic<ulong> statistic_total_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<AndroidMouseHandler>(), "Total events");

/// <summary>
/// Whether relative mode should be preferred when the window has focus, the cursor is contained and the OS cursor is not visible.
/// </summary>
Expand Down Expand Up @@ -308,6 +310,7 @@ private void enqueueInput(IInput input)
{
PendingInputs.Enqueue(input);
FrameStatistics.Increment(StatisticsCounterType.MouseEvents);
statistic_total_events.Value++;
}
}
}
9 changes: 9 additions & 0 deletions osu.Framework.Android/Input/AndroidTouchHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace osu.Framework.Android.Input
{
public class AndroidTouchHandler : AndroidInputHandler
{
private static readonly GlobalStatistic<ulong> statistic_touch_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<AndroidTouchHandler>(), "Touch events");
private static readonly GlobalStatistic<ulong> statistic_hover_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<AndroidTouchHandler>(), "Hover events");

public override bool IsActive => true;

protected override IEnumerable<InputSourceType> HandledEventSources => new[] { InputSourceType.BluetoothStylus, InputSourceType.Stylus, InputSourceType.Touchscreen };
Expand Down Expand Up @@ -87,14 +90,20 @@ protected override bool OnHover(MotionEvent hoverEvent)
void apply(MotionEvent e, int historyPosition)
{
if (tryGetEventPosition(e, historyPosition, 0, out var position))
{
enqueueInput(new MousePositionAbsoluteInput { Position = position });
statistic_hover_events.Value++;
}
}
}

private void applyTouchInput(MotionEvent touchEvent, int historyPosition, int pointerIndex)
{
if (tryGetEventTouch(touchEvent, historyPosition, pointerIndex, out var touch))
{
enqueueInput(new TouchInput(touch, touchEvent.ActionMasked.IsTouchDownAction()));
statistic_touch_events.Value++;
}
}

private bool tryGetEventTouch(MotionEvent motionEvent, int historyPosition, int pointerIndex, out Touch touch)
Expand Down
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion osu.Framework.Tests/Audio/BassTestComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void Add(params AudioComponent[] component)

internal BassAudioMixer CreateMixer()
{
var mixer = new BassAudioMixer(Mixer, "Test mixer");
var mixer = new BassAudioMixer(null, Mixer, "Test mixer");
mixerComponents.AddItem(mixer);
return mixer;
}
Expand Down
51 changes: 42 additions & 9 deletions osu.Framework/Audio/AudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,30 @@ public class AudioManager : AudioCollectionManager<AudioComponent>
MaxValue = 1
};

/// <summary>
/// Whether a global mixer is being used for audio routing.
/// For now, this is only the case on Windows when using shared mode WASAPI initialisation.
/// </summary>
public IBindable<bool> UsingGlobalMixer => usingGlobalMixer;

private readonly Bindable<bool> usingGlobalMixer = new BindableBool();

/// <summary>
/// If a global mixer is being used, this will be the BASS handle for it.
/// If non-null, all game mixers should be added to this mixer.
/// </summary>
/// <remarks>
/// When this is non-null, all mixers created via <see cref="CreateAudioMixer"/>
/// will themselves be added to the global mixer, which will handle playback itself.
///
/// In this mode of operation, nested mixers will be created with the <see cref="BassFlags.Decode"/>
/// flag, meaning they no longer handle playback directly.
///
/// An eventual goal would be to use a global mixer across all platforms as it can result
/// in more control and better playback performance.
/// </remarks>
internal readonly IBindable<int?> GlobalMixerHandle = new Bindable<int?>();

public override bool IsLoaded => base.IsLoaded &&
// bass default device is a null device (-1), not the actual system default.
Bass.CurrentDevice != Bass.DefaultDevice;
Expand Down Expand Up @@ -146,7 +170,12 @@ public AudioManager(AudioThread audioThread, ResourceStore<byte[]> trackStore, R

thread.RegisterManager(this);

AudioDevice.ValueChanged += onDeviceChanged;
AudioDevice.ValueChanged += _ => onDeviceChanged();
GlobalMixerHandle.ValueChanged += handle =>
{
onDeviceChanged();
usingGlobalMixer.Value = handle.NewValue.HasValue;
};

AddItem(TrackMixer = createAudioMixer(null, nameof(TrackMixer)));
AddItem(SampleMixer = createAudioMixer(null, nameof(SampleMixer)));
Expand All @@ -169,7 +198,8 @@ public AudioManager(AudioThread audioThread, ResourceStore<byte[]> trackStore, R

CancellationToken token = cancelSource.Token;

scheduler.Add(() =>
syncAudioDevices();
scheduler.AddDelayed(() =>
{
// sync audioDevices every 1000ms
new Thread(() =>
Expand All @@ -189,7 +219,7 @@ public AudioManager(AudioThread audioThread, ResourceStore<byte[]> trackStore, R
{
IsBackground = true
}.Start();
});
}, 1000);
}

protected override void Dispose(bool disposing)
Expand All @@ -204,9 +234,9 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}

private void onDeviceChanged(ValueChangedEvent<string> args)
private void onDeviceChanged()
{
scheduler.Add(() => setAudioDevice(args.NewValue));
scheduler.Add(() => setAudioDevice(AudioDevice.Value));
}

private void onDevicesChanged()
Expand All @@ -233,9 +263,9 @@ private void onDevicesChanged()
public AudioMixer CreateAudioMixer(string identifier = default) =>
createAudioMixer(SampleMixer, !string.IsNullOrEmpty(identifier) ? identifier : $"user #{Interlocked.Increment(ref userMixerID)}");

private AudioMixer createAudioMixer(AudioMixer globalMixer, string identifier)
private AudioMixer createAudioMixer(AudioMixer fallbackMixer, string identifier)
{
var mixer = new BassAudioMixer(globalMixer, identifier);
var mixer = new BassAudioMixer(this, fallbackMixer, identifier);
AddItem(mixer);
return mixer;
}
Expand Down Expand Up @@ -311,7 +341,7 @@ private bool setAudioDevice(string deviceName = null)
if (setAudioDevice(Bass.NoSoundDevice))
return true;

//we're fucked. even "No sound" device won't initialise.
// we're boned. even "No sound" device won't initialise.
return false;
}

Expand Down Expand Up @@ -389,7 +419,10 @@ protected virtual bool InitBass(int device)
// See https://www.un4seen.com/forum/?topic=19601 for more information.
Bass.Configure((ManagedBass.Configuration)70, false);

return AudioThread.InitDevice(device);
if (!thread.InitDevice(device))
return false;

return true;
}

private void syncAudioDevices()
Expand Down
14 changes: 7 additions & 7 deletions osu.Framework/Audio/Mixing/AudioMixer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ public abstract class AudioMixer : AudioComponent, IAudioMixer
{
public readonly string Identifier;

private readonly AudioMixer? globalMixer;
private readonly AudioMixer? fallbackMixer;

/// <summary>
/// Creates a new <see cref="AudioMixer"/>.
/// </summary>
/// <param name="globalMixer">The global <see cref="AudioMixer"/>, which <see cref="IAudioChannel"/>s are moved to if removed from this one.
/// A <c>null</c> value indicates this is the global <see cref="AudioMixer"/>.</param>
/// <param name="fallbackMixer">A fallback <see cref="AudioMixer"/>, which <see cref="IAudioChannel"/>s are moved to if removed from this one.
/// A <c>null</c> value indicates this is the fallback <see cref="AudioMixer"/>.</param>
/// <param name="identifier">An identifier displayed on the audio mixer visualiser.</param>
protected AudioMixer(AudioMixer? globalMixer, string identifier)
protected AudioMixer(AudioMixer? fallbackMixer, string identifier)
{
this.globalMixer = globalMixer;
this.fallbackMixer = fallbackMixer;
Identifier = identifier;
}

Expand Down Expand Up @@ -56,7 +56,7 @@ public void Add(IAudioChannel channel)
protected void Remove(IAudioChannel channel, bool returnToDefault)
{
// If this is the default mixer, prevent removal.
if (returnToDefault && globalMixer == null)
if (returnToDefault && fallbackMixer == null)
return;

channel.EnqueueAction(() =>
Expand All @@ -69,7 +69,7 @@ protected void Remove(IAudioChannel channel, bool returnToDefault)
// Add the channel back to the default mixer so audio can always be played.
if (returnToDefault)
globalMixer.AsNonNull().Add(channel);
fallbackMixer.AsNonNull().Add(channel);
});
}

Expand Down
23 changes: 19 additions & 4 deletions osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace osu.Framework.Audio.Mixing.Bass
/// </summary>
internal class BassAudioMixer : AudioMixer, IBassAudio
{
private readonly AudioManager? manager;

/// <summary>
/// The handle for this mixer.
/// </summary>
Expand All @@ -42,11 +44,13 @@ internal class BassAudioMixer : AudioMixer, IBassAudio
/// <summary>
/// Creates a new <see cref="BassAudioMixer"/>.
/// </summary>
/// <param name="globalMixer"><inheritdoc /></param>
/// <param name="manager">The game's audio manager.</param>
/// <param name="fallbackMixer"><inheritdoc /></param>
/// <param name="identifier">An identifier displayed on the audio mixer visualiser.</param>
public BassAudioMixer(AudioMixer? globalMixer, string identifier)
: base(globalMixer, identifier)
public BassAudioMixer(AudioManager? manager, AudioMixer? fallbackMixer, string identifier)
: base(fallbackMixer, identifier)
{
this.manager = manager;
EnqueueAction(createMixer);
}

Expand Down Expand Up @@ -248,7 +252,12 @@ public void UpdateDevice(int deviceIndex)
if (Handle == 0)
createMixer();
else
{
ManagedBass.Bass.ChannelSetDevice(Handle, deviceIndex);

if (manager?.GlobalMixerHandle.Value != null)
BassMix.MixerAddChannel(manager.GlobalMixerHandle.Value.Value, Handle, BassFlags.MixerChanBuffer | BassFlags.MixerChanNoRampin);
}
}

protected override void UpdateState()
Expand Down Expand Up @@ -277,7 +286,10 @@ private void createMixer()
if (!ManagedBass.Bass.GetDeviceInfo(ManagedBass.Bass.CurrentDevice, out var deviceInfo) || !deviceInfo.IsInitialized)
return;

Handle = BassMix.CreateMixerStream(frequency, 2, BassFlags.MixerNonStop);
Handle = manager?.GlobalMixerHandle.Value != null
? BassMix.CreateMixerStream(frequency, 2, BassFlags.MixerNonStop | BassFlags.Decode)
: BassMix.CreateMixerStream(frequency, 2, BassFlags.MixerNonStop);

if (Handle == 0)
return;

Expand All @@ -292,6 +304,9 @@ private void createMixer()

Effects.BindCollectionChanged(onEffectsChanged, true);

if (manager?.GlobalMixerHandle.Value != null)
BassMix.MixerAddChannel(manager.GlobalMixerHandle.Value.Value, Handle, BassFlags.MixerChanBuffer | BassFlags.MixerChanNoRampin);

ManagedBass.Bass.ChannelPlay(Handle);
}

Expand Down
17 changes: 17 additions & 0 deletions osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Platform;
using osuTK;

namespace osu.Framework.Graphics.UserInterface
{
public abstract partial class DropdownSearchBar : VisibilityContainer
{
[Resolved]
private GameHost? host { get; set; }

private TextBox textBox = null!;
private PassThroughInputManager textBoxInputManager = null!;

Expand Down Expand Up @@ -68,6 +72,19 @@ protected override void LoadComplete()

public void ObtainFocus()
{
// On mobile platforms, let's not make the keyboard popup unless the dropdown is intentionally searchable.
// Unfortunately it is not enough to just early-return here,
// as even despite that the text box will receive focus via the text box input manager;
// it is necessary to cut off the text box input manager from parent input entirely.
// TODO: preferably figure out a better way to do this.
bool willShowOverlappingKeyboard = host?.OnScreenKeyboardOverlapsGameWindow == true;

if (willShowOverlappingKeyboard && !AlwaysDisplayOnFocus)
{
textBoxInputManager.UseParentInput = false;
return;
}

textBoxInputManager.ChangeFocus(textBox);
obtainedFocus = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System;
using ManagedBass;
using ManagedBass.Mix;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
Expand All @@ -28,6 +30,7 @@ public partial class AudioChannelDisplay : CompositeDrawable
private float maxPeak = float.MinValue;
private double lastMaxPeakTime;
private readonly bool isOutputChannel;
private IBindable<bool> usingGlobalMixer = null!;

public AudioChannelDisplay(int channelHandle, bool isOutputChannel = false)
{
Expand Down Expand Up @@ -100,13 +103,19 @@ public AudioChannelDisplay(int channelHandle, bool isOutputChannel = false)
};
}

[BackgroundDependencyLoader]
private void load(AudioManager audioManager)
{
usingGlobalMixer = audioManager.UsingGlobalMixer.GetBoundCopy();
}

protected override void Update()
{
base.Update();

float[] levels = new float[2];

if (isOutputChannel)
if (isOutputChannel && !usingGlobalMixer.Value)
Bass.ChannelGetLevel(ChannelHandle, levels, 1 / 1000f * sample_window, LevelRetrievalFlags.Stereo);
else
BassMix.ChannelGetLevel(ChannelHandle, levels, 1 / 1000f * sample_window, LevelRetrievalFlags.Stereo);
Expand Down
Loading

0 comments on commit 23870c3

Please sign in to comment.