Skip to content

Commit

Permalink
make audio track work
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Apr 3, 2024
1 parent f72c253 commit 561dd60
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 53 deletions.
127 changes: 90 additions & 37 deletions Osu.Patcher.Hook/Patches/Mods/AudioPreview/FixUpdatePlaybackRate.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using JetBrains.Annotations;
using Osu.Stubs.Audio;
Expand All @@ -9,58 +11,109 @@
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
/// Apply various fixes to <c>AudioTrackBass</c> to make <c>BASS_ATTRIB_TEMPO</c> work again on
/// "Preview" audio streams. We use this in combination with <see cref="ModSelectAudioPreview" />
/// to reapply the constant pitch speed modifier (for DoubleTime).
/// </summary>
[OsuPatch]
[HarmonyPatch]
[UsedImplicitly]
internal static class FixUpdatePlaybackRate
internal 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;
private static MethodBase Target() => AudioTrackBass.Constructor.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)
private static IEnumerable<CodeInstruction> Transpiler(
IEnumerable<CodeInstruction> instructions,
ILGenerator generator)
{
instructions = instructions.ManipulatorReplace(
// Find inst that loads 0f as the parameter "value" to BASS_ChannelSetAttribute
inst => inst.Is(Ldc_R4, 0f),
inst => new CodeInstruction[]
// Remove the conditional & block that checks for this.Preview and always do the full initialization
// Otherwise, the "quick" initialization never uses BASS_FX_TempoCreate, which makes setting
// BASSAttribute.BASS_ATTRIB_TEMPO impossible (what's used for DoubleTime).
// instructions = instructions.NoopSignature(
instructions = instructions.NoopSignature(
// if (Preview) { audioStream = audioStreamForwards = audioStreamPrefilter; }
[
Ldarg_0,
Callvirt,
Brfalse_S,
Ldarg_0,
Ldarg_0,
Ldarg_0,
Ldfld,
Dup,
Stloc_1,
Stfld,
Ldloc_1,
Call,
Br_S,
]
);

// Change this "BASSFlag flags = Preview ? 0 : (BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_STREAM_PRESCAN);"
// into "BASSFlag flags = Preview ? BASSFlag.BASS_STREAM_DECODE : (BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_STREAM_PRESCAN);"
// This is so BASS_FX_TempoCreate still works when called later
var foundFlags = false;
const int BASS_STREAM_DECODE = 0x200000;
const int BASS_STREAM_PRESCAN = 0x20000;
instructions = instructions.Manipulator(
inst => foundFlags || inst.Is(Ldc_I4, BASS_STREAM_DECODE | BASS_STREAM_PRESCAN),
inst =>
{
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
if (inst.OperandIs(BASS_STREAM_DECODE | BASS_STREAM_PRESCAN))
{
foundFlags = true;
return;
}

// This is the "? 0"
if (inst.opcode != Ldc_I4_0)
return;

inst.opcode = Ldc_I4;
inst.operand = BASS_STREAM_DECODE;
foundFlags = false;
}
);

// Load speed optimization: disable BASS_FX_ReverseCreate when the "quick" parameter is true (aka. Preview)
var found = false;
instructions = instructions.Reverse().ManipulatorReplace(
inst => found || inst.Is(Stfld, AudioTrackBass.AudioStreamBackwardsHandle.Reference),
inst =>
{
// Only targeting the Call instruction before Stfld
if (inst.opcode != Call)
{
found = true;
return [inst];
}

found = false;
var labelTrue = generator.DefineLabel();
var labelFalse = generator.DefineLabel();

return new[]
{
new(Ldarg_2), // "bool quick"
new(Brfalse_S, labelFalse),

// Clean up the 3 values to the Call on stack
new(Pop),
new(Pop),
new(Pop),
new(Ldc_I4_0), // Load a "0" to be used for Stfld AudioStreamBackwardsHandle
new(Br_S, labelTrue),

inst.WithLabels([labelFalse]),
new(Nop) { labels = [labelTrue] },
}.Reverse();
}
).Reverse();

return instructions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ private static void After(
[HarmonyArgument(1)] int mod,
[HarmonyArgument(2)] bool playSound)
{
// These calls happen for all mods on any mod update and don't actually do anything
// 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);
Expand Down
83 changes: 68 additions & 15 deletions Osu.Stubs/Audio/AudioTrackBass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,91 @@ public class AudioTrackBass
[Stub]
public static readonly LazyType Class = new(
"osu.Audio.AudioTrackBass",
() => UpdatePlaybackRate!.Reference.DeclaringType!
() => Constructor!.Reference.DeclaringType!
);

/// <summary>
/// Original: <c>updatePlaybackRate()</c>
/// Original: <c>AudioTrackBass(Stream data, bool quick = false, bool loop = false)</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyMethod UpdatePlaybackRate = LazyMethod.ByPartialSignature(
"osu.Audio.AudioTrackBass::updatePlaybackRate()",
public static readonly LazyConstructor Constructor = LazyConstructor.ByPartialSignature(
"osu.Audio.AudioTrackBass::AudioTrackBass(Stream, bool, bool)",
[
Conv_R8,
Newobj,
Throw,
Ldarg_0,
Ldarg_1,
Isinst,
Stfld,
Ldarg_0,
Ldfld,
Mul,
Ldc_R8,
Div,
Conv_R4,
Call,
Pop,
]
);

/// <summary>
/// Original: <c>playbackRate</c>
/// Original: <c>audioStreamBackwards</c>
/// b20240123: <c></c>
/// </summary>
[Stub]
public static readonly LazyField<double> PlaybackRate = new(
"osu.Audio.AudioTrackBass::playbackRate",
public static readonly LazyField<int> AudioStreamBackwardsHandle = new(
"osu.Audio.AudioTrackBass::audioStreamBackwards",
() => Class.Reference
.GetDeclaredFields()
.Single(field => field.FieldType == typeof(double))
.Where(field => field.FieldType == typeof(int))
.Skip(1)
.First()
);

// /// <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>Play(bool restart = true)</c>
// /// b20240123: <c></c>
// /// </summary>
// [Stub]
// public static readonly LazyMethod Play = LazyMethod.BySignature(
// "osu.Audio.AudioTrackBass::Play(bool)",
// [
// Ldarg_0,
// Ldc_I4_1,
// Stfld,
// Ldarg_0,
// Call,
// Ldarg_1,
// Call,
// Pop,
// Ret,
// ]
// );

// /// <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))
// );
}

0 comments on commit 561dd60

Please sign in to comment.