From f74604c48ef20e19f33ab7098eef5b8bcff73001 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Tue, 30 Jan 2024 21:50:25 -0500
Subject: [PATCH] Feature changes to Offsets window

---
 assets                                        |   2 +-
 source/funkin/ui/debug/WaveformTestState.hx   | 114 ++++++++---
 .../toolboxes/ChartEditorOffsetsToolbox.hx    | 177 +++++++++++++++++-
 3 files changed, 262 insertions(+), 31 deletions(-)

diff --git a/assets b/assets
index 384a99d73..9350711c4 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 384a99d732456b2b3d35fa9bd2e10aa5f747afa6
+Subproject commit 9350711c41ce30151ff6a46cadaf0703461486d3
diff --git a/source/funkin/ui/debug/WaveformTestState.hx b/source/funkin/ui/debug/WaveformTestState.hx
index 745ac7a6b..f3566db85 100644
--- a/source/funkin/ui/debug/WaveformTestState.hx
+++ b/source/funkin/ui/debug/WaveformTestState.hx
@@ -21,11 +21,13 @@ class WaveformTestState extends MusicBeatState
 
   var waveformAudio:FunkinSound;
 
-  var waveformSprite:WaveformSprite;
-
+  // var waveformSprite:WaveformSprite;
   // var waveformSprite2:WaveformSprite;
   var timeMarker:FlxSprite;
 
+  var polygonSprite:MeshRender;
+  var vertexCount:Int = 3;
+
   public override function create():Void
   {
     super.create();
@@ -34,21 +36,26 @@ class WaveformTestState extends MusicBeatState
     testSprite.loadGraphic(Paths.image('funkay'));
     testSprite.updateHitbox();
     testSprite.clipRect = new FlxRect(0, 0, FlxG.width, FlxG.height);
-    add(testSprite);
+    // add(testSprite);
 
     waveformAudio = FunkinSound.load(Paths.inst('bopeebo', '-erect'));
 
     waveformData = WaveformDataParser.interpretFlxSound(waveformAudio);
 
-    waveformSprite = WaveformSprite.buildFromWaveformData(waveformData, HORIZONTAL, FlxColor.fromString("#ADD8E6"));
-    waveformSprite.duration = 5.0 * 160;
-    waveformSprite.width = FlxG.width * 160;
-    waveformSprite.height = FlxG.height; // / 2;
-    waveformSprite.amplitude = 2.0;
-    waveformSprite.minWaveformSize = 25;
-    waveformSprite.clipRect = new FlxRect(0, 0, FlxG.width, FlxG.height);
-    add(waveformSprite);
+    polygonSprite = new MeshRender(FlxG.width / 2, FlxG.height / 2, FlxColor.WHITE);
 
+    setPolygonVertices(vertexCount);
+    add(polygonSprite);
+
+    // waveformSprite = WaveformSprite.buildFromWaveformData(waveformData, HORIZONTAL, FlxColor.fromString("#ADD8E6"));
+    // waveformSprite.duration = 5.0 * 160;
+    // waveformSprite.width = FlxG.width * 160;
+    // waveformSprite.height = FlxG.height; // / 2;
+    // waveformSprite.amplitude = 2.0;
+    // waveformSprite.minWaveformSize = 25;
+    // waveformSprite.clipRect = new FlxRect(0, 0, FlxG.width, FlxG.height);
+    // add(waveformSprite);
+    //
     // waveformSprite2 = WaveformSprite.buildFromWaveformData(waveformData2, HORIZONTAL, FlxColor.fromString("#FF0000"), 5.0);
     // waveformSprite2.width = FlxG.width;
     // waveformSprite2.height = FlxG.height / 2;
@@ -80,48 +87,103 @@ class WaveformTestState extends MusicBeatState
 
     if (FlxG.keys.justPressed.ENTER)
     {
-      if (waveformSprite.orientation == HORIZONTAL)
-      {
-        waveformSprite.orientation = VERTICAL;
-        // waveformSprite2.orientation = VERTICAL;
-      }
-      else
-      {
-        waveformSprite.orientation = HORIZONTAL;
-        // waveformSprite2.orientation = HORIZONTAL;
-      }
+      // if (waveformSprite.orientation == HORIZONTAL)
+      // {
+      //   // waveformSprite.orientation = VERTICAL;
+      //   // waveformSprite2.orientation = VERTICAL;
+      // }
+      // else
+      // {
+      //   // waveformSprite.orientation = HORIZONTAL;
+      //   // waveformSprite2.orientation = HORIZONTAL;
+      // }
     }
 
     if (waveformAudio.isPlaying)
     {
       // waveformSprite takes a time in fractional seconds, not milliseconds.
       var timeSeconds = waveformAudio.time / 1000;
-      waveformSprite.time = timeSeconds;
+      // waveformSprite.time = timeSeconds;
       // waveformSprite2.time = timeSeconds;
     }
 
     if (FlxG.keys.justPressed.UP)
     {
-      waveformSprite.duration += 1.0;
+      vertexCount += 1;
+      setPolygonVertices(vertexCount);
+      // waveformSprite.duration += 1.0;
       // waveformSprite2.duration += 1.0;
     }
     if (FlxG.keys.justPressed.DOWN)
     {
-      waveformSprite.duration -= 1.0;
+      vertexCount -= 1;
+      setPolygonVertices(vertexCount);
+      // waveformSprite.duration -= 1.0;
       // waveformSprite2.duration -= 1.0;
     }
     if (FlxG.keys.justPressed.LEFT)
     {
-      waveformSprite.time -= 1.0;
+      // waveformSprite.time -= 1.0;
       // waveformSprite2.time -= 1.0;
     }
     if (FlxG.keys.justPressed.RIGHT)
     {
-      waveformSprite.time += 1.0;
+      // waveformSprite.time += 1.0;
       // waveformSprite2.time += 1.0;
     }
   }
 
+  function setPolygonVertices(count:Int)
+  {
+    polygonSprite.clear();
+
+    var size = 100.0;
+
+    // Build a polygon with count vertices.
+
+    var vertices:Array<Array<Float>> = [];
+
+    var angle = 0.0;
+
+    for (i in 0...count)
+    {
+      var x = Math.cos(angle) * size;
+      var y = Math.sin(angle) * size;
+
+      vertices.push([x, y]);
+
+      angle += 2 * Math.PI / count;
+    }
+
+    trace('vertices: ${vertices}');
+
+    var centerVertex = polygonSprite.build_vertex(0, 0);
+    var firstVertex = -1;
+    var lastVertex = -1;
+
+    for (vertex in vertices)
+    {
+      var x = vertex[0];
+      var y = vertex[1];
+
+      var newVertex = polygonSprite.build_vertex(x, y);
+
+      if (firstVertex == -1)
+      {
+        firstVertex = newVertex;
+      }
+
+      if (lastVertex != -1)
+      {
+        polygonSprite.add_tri(centerVertex, lastVertex, newVertex);
+      }
+
+      lastVertex = newVertex;
+    }
+
+    polygonSprite.add_tri(centerVertex, lastVertex, firstVertex);
+  }
+
   public override function destroy():Void
   {
     super.destroy();
diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx
index 56b602758..c72c7fd9a 100644
--- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx
+++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx
@@ -39,9 +39,11 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
   var offsetStepperOpponent:NumberStepper;
   var offsetStepperInstrumental:NumberStepper;
 
-  static final BASE_SCALE:Int = 64;
+  static final BASE_SCALE:Float = 64.0;
+  static final MIN_SCALE:Float = 4.0;
+  static final WAVEFORM_ZOOM_MULT:Float = 1.5;
 
-  var waveformScale:Int = BASE_SCALE;
+  var waveformScale:Float = BASE_SCALE;
 
   var audioPreviewTracks:SoundGroup;
 
@@ -71,6 +73,36 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
     this.x = 150;
     this.y = 250;
 
+    offsetPlayerVolume.onChange = (_) -> {
+      var targetVolume = offsetPlayerVolume.value * 2 / 100;
+      setTrackVolume(PLAYER, targetVolume);
+    };
+    offsetPlayerMute.onClick = (_) -> {
+      toggleMuteTrack(PLAYER);
+    };
+    offsetPlayerSolo.onClick = (_) -> {
+      soloTrack(PLAYER);
+    };
+    offsetOpponentVolume.onChange = (_) -> {
+      var targetVolume = offsetOpponentVolume.value * 2 / 100;
+      setTrackVolume(OPPONENT, targetVolume);
+    };
+    offsetOpponentMute.onClick = (_) -> {
+      toggleMuteTrack(OPPONENT);
+    };
+    offsetOpponentSolo.onClick = (_) -> {
+      soloTrack(OPPONENT);
+    };
+    offsetInstrumentalVolume.onChange = (_) -> {
+      var targetVolume = offsetInstrumentalVolume.value * 2 / 100;
+      setTrackVolume(INSTRUMENTAL, targetVolume);
+    };
+    offsetInstrumentalMute.onClick = (_) -> {
+      toggleMuteTrack(INSTRUMENTAL);
+    };
+    offsetInstrumentalSolo.onClick = (_) -> {
+      soloTrack(INSTRUMENTAL);
+    };
     offsetButtonZoomIn.onClick = (_) -> {
       zoomWaveformIn();
     };
@@ -300,7 +332,8 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
   {
     if (waveformScale > 1)
     {
-      waveformScale = Std.int(waveformScale / 2);
+      waveformScale = waveformScale / WAVEFORM_ZOOM_MULT;
+      if (waveformScale < MIN_SCALE) waveformScale = MIN_SCALE;
     }
     else
     {
@@ -314,13 +347,146 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
 
   public function zoomWaveformOut():Void
   {
-    waveformScale = Std.int(waveformScale * 2);
+    waveformScale = waveformScale * WAVEFORM_ZOOM_MULT;
+    if (waveformScale < MIN_SCALE) waveformScale = MIN_SCALE;
 
     trace('Zooming out, scale: ${waveformScale}');
 
     refresh();
   }
 
+  public function setTrackVolume(target:Waveform, volume:Float):Void
+  {
+    switch (target)
+    {
+      case Waveform.INSTRUMENTAL:
+        var trackInst = audioPreviewTracks.members[0];
+        if (trackInst != null)
+        {
+          trackInst.volume = volume;
+        }
+      case Waveform.PLAYER:
+        var trackPlayer = audioPreviewTracks.members[1];
+        if (trackPlayer != null)
+        {
+          trackPlayer.volume = volume;
+        }
+      case Waveform.OPPONENT:
+        var trackOpponent = audioPreviewTracks.members[2];
+        if (trackOpponent != null)
+        {
+          trackOpponent.volume = volume;
+        }
+    }
+  }
+
+  public function muteTrack(target:Waveform):Void
+  {
+    switch (target)
+    {
+      case Waveform.INSTRUMENTAL:
+        var trackInst = audioPreviewTracks.members[0];
+        if (trackInst != null)
+        {
+          trackInst.muted = true;
+          offsetInstrumentalMute.text = trackInst.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.PLAYER:
+        var trackPlayer = audioPreviewTracks.members[1];
+        if (trackPlayer != null)
+        {
+          trackPlayer.muted = true;
+          offsetPlayerMute.text = trackPlayer.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.OPPONENT:
+        var trackOpponent = audioPreviewTracks.members[2];
+        if (trackOpponent != null)
+        {
+          trackOpponent.muted = true;
+          offsetOpponentMute.text = trackOpponent.muted ? "Unmute" : "Mute";
+        }
+    }
+  }
+
+  public function unmuteTrack(target:Waveform):Void
+  {
+    switch (target)
+    {
+      case Waveform.INSTRUMENTAL:
+        var trackInst = audioPreviewTracks.members[0];
+        if (trackInst != null)
+        {
+          trackInst.muted = false;
+          offsetInstrumentalMute.text = trackInst.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.PLAYER:
+        var trackPlayer = audioPreviewTracks.members[1];
+        if (trackPlayer != null)
+        {
+          trackPlayer.muted = false;
+          offsetPlayerMute.text = trackPlayer.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.OPPONENT:
+        var trackOpponent = audioPreviewTracks.members[2];
+        if (trackOpponent != null)
+        {
+          trackOpponent.muted = false;
+          offsetOpponentMute.text = trackOpponent.muted ? "Unmute" : "Mute";
+        }
+    }
+  }
+
+  public function toggleMuteTrack(target:Waveform):Void
+  {
+    switch (target)
+    {
+      case Waveform.INSTRUMENTAL:
+        var trackInst = audioPreviewTracks.members[0];
+        if (trackInst != null)
+        {
+          trackInst.muted = !trackInst.muted;
+          offsetInstrumentalMute.text = trackInst.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.PLAYER:
+        var trackPlayer = audioPreviewTracks.members[1];
+        if (trackPlayer != null)
+        {
+          trackPlayer.muted = !trackPlayer.muted;
+          offsetPlayerMute.text = trackPlayer.muted ? "Unmute" : "Mute";
+        }
+      case Waveform.OPPONENT:
+        var trackOpponent = audioPreviewTracks.members[2];
+        if (trackOpponent != null)
+        {
+          trackOpponent.muted = !trackOpponent.muted;
+          offsetOpponentMute.text = trackOpponent.muted ? "Unmute" : "Mute";
+        }
+    }
+  }
+
+  /**
+   * Clicking the solo button will unmute the track and mute all other tracks.
+   * @param target
+   */
+  public function soloTrack(target:Waveform):Void
+  {
+    switch (target)
+    {
+      case Waveform.PLAYER:
+        muteTrack(Waveform.OPPONENT);
+        muteTrack(Waveform.INSTRUMENTAL);
+        unmuteTrack(Waveform.PLAYER);
+      case Waveform.OPPONENT:
+        muteTrack(Waveform.PLAYER);
+        muteTrack(Waveform.INSTRUMENTAL);
+        unmuteTrack(Waveform.OPPONENT);
+      case Waveform.INSTRUMENTAL:
+        muteTrack(Waveform.PLAYER);
+        muteTrack(Waveform.OPPONENT);
+        unmuteTrack(Waveform.INSTRUMENTAL);
+    }
+  }
+
   public override function update(elapsed:Float)
   {
     super.update(elapsed);
@@ -380,14 +546,17 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
     waveformPlayer.waveform.time = -chartEditorState.currentVocalOffsetPlayer / Constants.MS_PER_SEC; // Negative offsets make the song start early.
     waveformPlayer.waveform.width = waveformPlayer.waveform.waveformData.length / waveformScale * BASE_SCALE;
     if (waveformPlayer.waveform.width > maxWidth) maxWidth = Std.int(waveformPlayer.waveform.width);
+    waveformPlayer.waveform.height = 65;
 
     waveformOpponent.waveform.time = -chartEditorState.currentVocalOffsetOpponent / Constants.MS_PER_SEC;
     waveformOpponent.waveform.width = waveformOpponent.waveform.waveformData.length / waveformScale * BASE_SCALE;
     if (waveformOpponent.waveform.width > maxWidth) maxWidth = Std.int(waveformOpponent.waveform.width);
+    waveformOpponent.waveform.height = 65;
 
     waveformInstrumental.waveform.time = -chartEditorState.currentInstrumentalOffset / Constants.MS_PER_SEC;
     waveformInstrumental.waveform.width = waveformInstrumental.waveform.waveformData.length / waveformScale * BASE_SCALE;
     if (waveformInstrumental.waveform.width > maxWidth) maxWidth = Std.int(waveformInstrumental.waveform.width);
+    waveformInstrumental.waveform.height = 65;
 
     waveformPlayer.waveform.markDirty();
     waveformOpponent.waveform.markDirty();