Skip to content

Commit

Permalink
patch on audio preview load
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Apr 3, 2024
1 parent 6bfcdd4 commit afdaf79
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Osu.Utils.Extensions;
using static System.Reflection.Emit.OpCodes;

// ReSharper disable InconsistentNaming

namespace Osu.Patcher.Hook.Patches.Mods.AudioPreview;

/// <summary>
Expand Down
61 changes: 61 additions & 0 deletions Osu.Patcher.Hook/Patches/Mods/AudioPreview/ModAudioEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using Osu.Stubs.Audio;
using Osu.Stubs.Scoring;
using static Osu.Stubs.Other.Mods;

namespace Osu.Patcher.Hook.Patches.Mods.AudioPreview;

/// <summary>
/// Handles applying and resetting the audio effects on the AudioEngine.
/// </summary>
internal static class ModAudioEffects
{
/// <summary>
/// Applies the audio changes to the AudioEngine based on the current global mods.
/// </summary>
internal static void ApplyModEffects()
{
ResetChanges();

var mods = ModManager.ModStatus.Get();

// NC always comes with DT
if ((mods & Nightcore) > None)
mods &= ~DoubleTime;

switch (mods & (DoubleTime | Nightcore | HalfTime))
{
case DoubleTime:
UpdateAudioRate(rate => rate * 1.5);
break;
case Nightcore:
AudioEngine.Nightcore.Set(true);
UpdateAudioRate(rate => rate * 1.5);
break;
case HalfTime:
UpdateAudioRate(rate => rate * 0.75);
break;
}
}

/// <summary>
/// Resets the audio stream effects back to default.
/// </summary>
private static void ResetChanges()
{
AudioEngine.Nightcore.Set(false);
UpdateAudioRate(_ => 100);
}

/// <summary>
/// Gets, modifies, and writes back the CurrentPlaybackRate on the AudioEngine.
/// </summary>
/// <param name="onModify">Rate transformer.</param>
private static void UpdateAudioRate(Func<double, double> onModify)
{
var currentRate = AudioEngine.GetCurrentPlaybackRate.Invoke();
var newRate = onModify.Invoke(currentRate);

AudioEngine.SetCurrentPlaybackRate.Invoke(parameters: [newRate]);
}
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,24 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading.Tasks;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs.Audio;
using Osu.Stubs.SongSelect;
using static Osu.Stubs.Other.Mods;

namespace Osu.Patcher.Hook.Patches.Mods.AudioPreview;

/// <summary>
/// Hooks the place where ModButtons get updates in the mod selection menu to apply audio effects.
/// </summary>
[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
internal class ModSelectAudioPreview
{
[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => ModButton.SetStatus.Reference;
private static MethodBase Target() => ModSelection.UpdateMods.Reference;

[UsedImplicitly]
[HarmonyPostfix]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private static void After(
object __instance, // typeof(ModButton)
[HarmonyArgument(1)] int mod,
[HarmonyArgument(2)] bool playSound)
{
// These calls happen for all mods on any mod update
// and don't actually indicate a ModButton being pressed
if (!playSound) return;

// var availableModStates = ModButton.AvailableStates.Get(__instance);
//
// // Check that this is the DT+NC or HF button
// if (availableModStates[0] is not (DoubleTime or HalfTime))
// return;

ApplyChanges(mod);
}

internal static void ApplyChanges(int mods)
{
ResetChanges();

switch (mods & (DoubleTime | Nightcore | HalfTime))
{
case DoubleTime:
UpdateAudioRate(rate => rate * 1.5);
break;
case Nightcore:
AudioEngine.Nightcore.Set(true);
UpdateAudioRate(rate => rate * 1.5);
break;
case HalfTime:
UpdateAudioRate(rate => rate * 0.75);
break;
}
}

/// <summary>
/// Resets the audio stream effects back to default.
/// </summary>
private static void ResetChanges()
{
AudioEngine.Nightcore.Set(false);
UpdateAudioRate(_ => 100);
}

private static void UpdateAudioRate(Func<double, double> onModify)
{
var currentRate = AudioEngine.GetCurrentPlaybackRate.Invoke();
var newRate = onModify.Invoke(currentRate);

AudioEngine.SetCurrentPlaybackRate.Invoke(parameters: [newRate]);
}
private static void After() => Task.Run(ModAudioEffects.ApplyModEffects);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Reflection;
using System.Threading.Tasks;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs.Audio;

namespace Osu.Patcher.Hook.Patches.Mods.AudioPreview;

/// <summary>
/// Hooks the place where preview audio gets loaded to apply our mod audio effects.
/// </summary>
[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
public class TrackUpdatePreviewMusic
{
[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => AudioEngine.LoadAudioForPreview.Reference;

[HarmonyPostfix]
[UsedImplicitly]
private static void After() => Task.Run(ModAudioEffects.ApplyModEffects);
}
19 changes: 19 additions & 0 deletions Osu.Stubs/Audio/AudioEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,25 @@ namespace Osu.Stubs.Audio;
[PublicAPI]
public class AudioEngine
{
/// <summary>
/// Original: <c>LoadAudioForPreview(Beatmap beatmap, bool continuePlayback, bool previewPoint, bool quick)</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod<bool> LoadAudioForPreview = LazyMethod<bool>.ByPartialSignature(
"osu.Audio.AudioEngine::LoadAudioForPreview(Beatmap, bool, bool, bool)",
[
Call,
Ldc_I4_0,
Ceq,
Stloc_0,
Leave_S,
Pop,
Ldc_I4,
Call,
]
);

/// <summary>
/// Original: <c>get_CurrentPlaybackRate()</c> (property getter)
/// b20240123: <c></c>
Expand Down
58 changes: 58 additions & 0 deletions Osu.Stubs/Scoring/ModManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Linq;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs.Other;
using Osu.Utils.Lazy;
using static System.Reflection.Emit.OpCodes;

namespace Osu.Stubs.Scoring;

[PublicAPI]
public class ModManager
{
/// <summary>
/// Original: <c>osu.GameplayElements.Scoring.ModManager</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyType Class = new(
"osu.GameplayElements.Scoring.ModManager",
() => AllowRanking!.Reference.DeclaringType!
);

/// <summary>
/// Original: <c>AllowRanking(Mods enabledMods)</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod AllowRanking = LazyMethod.ByPartialSignature(
"osu.GameplayElements.Scoring.ModManager::AllowRanking(Mods)",
[
Ldloc_2,
And,
Ldc_I4_0,
Cgt,
Brfalse_S,
Ldc_I4_0,
Ret,
Ldc_I4,
Ldarg_0,
Stloc_2,
Stloc_1,
Ldloc_1,
Ldloc_2,
]
);

/// <summary>
/// Original: <c>ActiveMods</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyField<int> ModStatus = new(
"osu.GameplayElements.Scoring.ModManager::ModStatus",
() => Class.Reference
.GetDeclaredFields()
.Single(field => field.FieldType == Mods.Type.Reference)
);
}
61 changes: 0 additions & 61 deletions Osu.Stubs/SongSelect/ModButton.cs

This file was deleted.

30 changes: 30 additions & 0 deletions Osu.Stubs/SongSelect/ModSelection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using JetBrains.Annotations;
using Osu.Utils.Lazy;
using static System.Reflection.Emit.OpCodes;

namespace Osu.Stubs.SongSelect;

/// <summary>
/// Original: <c>osu.GameModes.Select.ModSelection</c>
/// b20240123: <c></c>
/// </summary>
[PublicAPI]
public class ModSelection
{
/// <summary>
/// Original: <c>updateMods()</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod UpdateMods = LazyMethod.ByPartialSignature(
"osu.GameModes.Select.ModSelection::updateMods()",
[
Ldc_I4_1,
Conv_I8,
Stloc_0,
Br_S,
Ldloc_0,
Conv_I4,
]
);
}
2 changes: 1 addition & 1 deletion Osu.Stubs/SongSelect/SongSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static class SongSelection
/// </summary>
[Stub]
public static readonly LazyMethod BeatmapTreeManagerOnRightClicked = LazyMethod.ByPartialSignature(
"osu.GameModes.Select.SongSelection::beatmapTreeManager_OnRightClicked",
"osu.GameModes.Select.SongSelection::beatmapTreeManager_OnRightClicked(object, BeatmapTreeItem)",
[
Ldarg_2,
Isinst,
Expand Down

0 comments on commit afdaf79

Please sign in to comment.