Skip to content

Commit

Permalink
wip(patch): mod audio preview
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Apr 2, 2024
1 parent 79487d9 commit f72c253
Show file tree
Hide file tree
Showing 8 changed files with 409 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs.Audio;
using Osu.Utils.Extensions;
using static System.Reflection.Emit.OpCodes;

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

/// <summary>
/// <c>AudioTrackBass::updatePlaybackRate()</c> forcibly resets the tempo to
/// normal if a pitch isn't applied. This forces the tempo to be set if pitch isn't applied,
/// to be used with the <see cref="ModSelectAudioPreview" /> patch
/// </summary>
[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
internal static class FixUpdatePlaybackRate
{
// static FixUpdatePlaybackRate()
// {
// Task.Run(async () =>
// {
// await Task.Delay(2000);
// try
// {
// var mtd = AccessTools.Method(AudioTrackBass.Class.Reference.Name + ":" +
// AudioTrackBass.UpdatePlaybackRate.Reference.Name);
// foreach (var instruction in MethodReader.GetInstructions(mtd))
// {
// Console.WriteLine($"{instruction.Opcode} {instruction.Operand}");
// }
// }
// catch (Exception e)
// {
// Console.WriteLine(e);
// }
// });
// }

[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => AudioTrackBass.UpdatePlaybackRate.Reference;

// TODO: WHY THE FUCK ISN'T THIS TRANSPILER APPLYING CHANGES????? PRE/POST PATCHES WORK FINE BUT THIS DOESN'T????????
[UsedImplicitly]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
instructions = instructions.ManipulatorReplace(
// Find inst that loads 0f as the parameter "value" to BASS_ChannelSetAttribute
inst => inst.Is(Ldc_R4, 0f),
inst => new CodeInstruction[]
{
new(Ldarg_0) { labels = inst.labels }, // Load "this"
new(Ldfld, AudioTrackBass.PlaybackRate.Reference), // Load the float64 "playbackRate"
new(Ldc_R8, 100.0), // Load the float64 "100.0"
new(Sub), // Subtract 100.0 from playbackRate
new(Conv_R4), // Convert to float32
}
);

return instructions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
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;

[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
internal class ModSelectAudioPreview
{
[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => ModButton.SetStatus.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 do anything
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]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using JetBrains.Annotations;
using Osu.Stubs.Rulesets;
using static System.Reflection.Emit.OpCodes;
using static Osu.Stubs.Other.Mods;

namespace Osu.Patcher.Hook.Patches.Mods;

Expand All @@ -24,11 +25,8 @@ namespace Osu.Patcher.Hook.Patches.Mods;
[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
internal static class PatchSuddenDeathAutoRetry
internal static class SuddenDeathAutoRetry
{
private const int ModPerfect = 1 << 14;
private const int ModSuddenDeath = 1 << 5;

[UsedImplicitly]
[HarmonyTargetMethod]
private static MethodBase Target() => Ruleset.Fail.Reference;
Expand All @@ -42,8 +40,8 @@ internal static class PatchSuddenDeathAutoRetry
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
instructions = instructions.Manipulator(
inst => inst.opcode == Ldc_I4 && inst.OperandIs(ModPerfect),
inst => inst.operand = ModPerfect | ModSuddenDeath
inst => inst.opcode == Ldc_I4 && inst.OperandIs(Perfect),
inst => inst.operand = Perfect | SuddenDeath
);

return instructions;
Expand Down
73 changes: 73 additions & 0 deletions Osu.Stubs/Audio/AudioEngine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Osu.Utils.IL;
using Osu.Utils.Lazy;
using static System.Reflection.Emit.OpCodes;

namespace Osu.Stubs.Audio;

/// <summary>
/// Original: <c>osu.Audio.AudioEngine</c>
/// b20240123: <c></c>
/// </summary>
[PublicAPI]
public class AudioEngine
{
/// <summary>
/// Original: <c>get_CurrentPlaybackRate()</c> (property getter)
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod<double> GetCurrentPlaybackRate = LazyMethod<double>.BySignature(
"osu.Audio.AudioEngine::get_CurrentPlaybackRate()",
[
Ldsfld,
Dup,
Brtrue_S,
Pop,
Ldc_R8,
Ret,
Callvirt,
Ret,
]
);

/// <summary>
/// Original: <c>set_CurrentPlaybackRate()</c> (property setter)
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod SetCurrentPlaybackRate = LazyMethod.ByPartialSignature(
"osu.Audio.AudioEngine::set_CurrentPlaybackRate()",
[
Ldsfld, // Reference to AudioEngine::Nightcore
Ldc_I4_0,
Ceq,
Callvirt,
Ldloc_0,
Ldarg_0,
Callvirt,
Ret,
]
);

/// <summary>
/// Original: <c>Nightcore</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyField<bool> Nightcore = new(
"osu.Audio.AudioEngine::Nightcore",
() =>
{
// Last Ldsfld in get_CurrentPlaybackRate() is a reference to AudioEngine::Nightcore
var instruction = MethodReader
.GetInstructions(SetCurrentPlaybackRate.Reference)
.Reverse()
.First(inst => inst.Opcode == Ldsfld);

return (FieldInfo)instruction.Operand;
}
);
}
53 changes: 53 additions & 0 deletions Osu.Stubs/Audio/AudioTrackBass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Linq;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Utils.Lazy;
using static System.Reflection.Emit.OpCodes;

namespace Osu.Stubs.Audio;

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

/// <summary>
/// Original: <c>updatePlaybackRate()</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod UpdatePlaybackRate = LazyMethod.ByPartialSignature(
"osu.Audio.AudioTrackBass::updatePlaybackRate()",
[
Conv_R8,
Ldarg_0,
Ldfld,
Mul,
Ldc_R8,
Div,
Conv_R4,
Call,
Pop,
]
);

/// <summary>
/// Original: <c>playbackRate</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyField<double> PlaybackRate = new(
"osu.Audio.AudioTrackBass::playbackRate",
() => Class.Reference
.GetDeclaredFields()
.Single(field => field.FieldType == typeof(double))
);
}
45 changes: 45 additions & 0 deletions Osu.Stubs/Other/Mods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using JetBrains.Annotations;
using Osu.Utils.Lazy;

namespace Osu.Stubs.Other;

[PublicAPI]
public class Mods
{
public const int None = 0;
public const int NoFail = 1 << 0;
public const int Easy = 1 << 1;
public const int Hidden = 1 << 3;
public const int HardRock = 1 << 4;
public const int SuddenDeath = 1 << 5;
public const int DoubleTime = 1 << 6;
public const int Relax = 1 << 7;
public const int HalfTime = 1 << 8;
public const int Nightcore = 1 << 9;
public const int Flashlight = 1 << 10;
public const int Autoplay = 1 << 11;
public const int SpunOut = 1 << 12;
public const int Relax2 = 1 << 13;
public const int Perfect = 1 << 14;
public const int Key4 = 1 << 15;
public const int Key5 = 1 << 16;
public const int Key6 = 1 << 17;
public const int Key7 = 1 << 18;
public const int Key8 = 1 << 19;
public const int FadeIn = 1 << 20;
public const int Random = 1 << 21;
public const int Cinema = 1 << 22;
public const int Target = 1 << 23;
public const int Key9 = 1 << 24;
public const int KeyCoop = 1 << 25;
public const int Key1 = 1 << 26;
public const int Key3 = 1 << 27;
public const int Key2 = 1 << 28;

/// <summary>
/// Original: <c>osu_common.Mods</c>
/// b20240123: <c>osu_common.Mods</c>
/// </summary>
[Stub]
public static readonly LazyType Type = LazyType.ByName("osu_common.Mods");
}
Loading

0 comments on commit f72c253

Please sign in to comment.