mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2025-03-22 21:06:19 -04:00
Initial attempt at rendering the waveform inside the offsets toolbox.
This commit is contained in:
parent
25aff02784
commit
c9163986d5
6 changed files with 141 additions and 23 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 3d92b497682727d34eaa55e564e0bd9faea1c9d7
|
||||
Subproject commit 685c9481c26020563333852d9401fe4c6ee10257
|
4
hmm.json
4
hmm.json
|
@ -54,14 +54,14 @@
|
|||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "bb904f8b4b205755a310c23ff25219f9dcd62711",
|
||||
"ref": "5b2d5b8e7e470cf637953e1369c80a1f42016a75",
|
||||
"url": "https://github.com/haxeui/haxeui-core"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "1ec470c297afd7758a90dc9399aa1e3a4ea6ca0b",
|
||||
"ref": "e9f880522e27134b29df4067f82df7d7e5237b70",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@ package funkin.audio;
|
|||
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import funkin.audio.waveform.WaveformData;
|
||||
import funkin.audio.waveform.WaveformDataParser;
|
||||
|
||||
class VoicesGroup extends SoundGroup
|
||||
{
|
||||
|
@ -104,6 +106,40 @@ class VoicesGroup extends SoundGroup
|
|||
return opponentVolume = volume;
|
||||
}
|
||||
|
||||
public function buildPlayerVoiceWaveform():Null<WaveformData>
|
||||
{
|
||||
if (playerVoices.members.length == 0) return null;
|
||||
|
||||
return WaveformDataParser.interpretFlxSound(playerVoices.members[0]);
|
||||
}
|
||||
|
||||
public function buildOpponentVoiceWaveform():Null<WaveformData>
|
||||
{
|
||||
if (opponentVoices.members.length == 0) return null;
|
||||
|
||||
return WaveformDataParser.interpretFlxSound(opponentVoices.members[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the player's vocal track, in milliseconds.
|
||||
*/
|
||||
public function getPlayerVoiceLength():Float
|
||||
{
|
||||
if (playerVoices.members.length == 0) return 0.0;
|
||||
|
||||
return playerVoices.members[0].length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the opponent's vocal track, in milliseconds.
|
||||
*/
|
||||
public function getOpponentVoiceLength():Float
|
||||
{
|
||||
if (opponentVoices.members.length == 0) return 0.0;
|
||||
|
||||
return opponentVoices.members[0].length;
|
||||
}
|
||||
|
||||
public override function clear():Void
|
||||
{
|
||||
playerVoices.clear();
|
||||
|
|
|
@ -215,17 +215,17 @@ class WaveformSprite extends MeshRender
|
|||
{
|
||||
var pixelPos:Int = Std.int((i - startIndex) * pixelsPerIndex);
|
||||
|
||||
var isOutsideClipRect:Bool = (clipRect != null) && if (orientation == HORIZONTAL)
|
||||
{
|
||||
pixelPos < clipRect.x
|
||||
|| pixelPos > (clipRect.x + clipRect.width);
|
||||
} else
|
||||
{
|
||||
pixelPos < clipRect.y || pixelPos > (clipRect.y + clipRect.height);
|
||||
};
|
||||
var isBeforeClipRect:Bool = (clipRect != null) && ((orientation == HORIZONTAL) ? pixelPos < clipRect.x : pixelPos < clipRect.y);
|
||||
|
||||
// This index is outside the clipRect, so we can just skip rendering it. Fantastic!
|
||||
if (isOutsideClipRect) continue;
|
||||
if (isBeforeClipRect) continue;
|
||||
|
||||
var isAfterClipRect:Bool = (clipRect != null)
|
||||
&& ((orientation == HORIZONTAL) ? pixelPos > (clipRect.x + clipRect.width) : pixelPos > (clipRect.y + clipRect.height));
|
||||
|
||||
if (isAfterClipRect)
|
||||
{
|
||||
break;
|
||||
};
|
||||
|
||||
var sampleMax:Float = Math.min(waveformData.channel(0).maxSampleMapped(i) * amplitude, 1.0);
|
||||
var sampleMin:Float = Math.max(waveformData.channel(0).minSampleMapped(i) * amplitude, -1.0);
|
||||
|
@ -299,16 +299,17 @@ class WaveformSprite extends MeshRender
|
|||
{
|
||||
var pixelPos:Int = i;
|
||||
|
||||
var isOutsideClipRect:Bool = (clipRect != null) && if (orientation == HORIZONTAL)
|
||||
var isBeforeClipRect:Bool = (clipRect != null) && ((orientation == HORIZONTAL) ? pixelPos < clipRect.x : pixelPos < clipRect.y);
|
||||
|
||||
if (isBeforeClipRect) continue;
|
||||
|
||||
var isAfterClipRect:Bool = (clipRect != null)
|
||||
&& ((orientation == HORIZONTAL) ? pixelPos > (clipRect.x + clipRect.width) : pixelPos > (clipRect.y + clipRect.height));
|
||||
|
||||
if (isAfterClipRect)
|
||||
{
|
||||
pixelPos < clipRect.x
|
||||
|| pixelPos > (clipRect.x + clipRect.width);
|
||||
} else
|
||||
{
|
||||
pixelPos < clipRect.y || pixelPos > (clipRect.y + clipRect.height);
|
||||
break;
|
||||
};
|
||||
// This index is outside the clipRect, so we can just skip rendering it. Fantastic!
|
||||
if (isOutsideClipRect) continue;
|
||||
|
||||
// Wrap Std.int around the whole range calculation, not just indexesPerPixel, otherwise you get weird issues with zooming.
|
||||
var rangeStart:Int = Std.int(i * indexesPerPixel + startIndex);
|
||||
|
@ -369,8 +370,6 @@ class WaveformSprite extends MeshRender
|
|||
prevVertexBottomIndex = vertexBottomIndex;
|
||||
}
|
||||
}
|
||||
|
||||
trace('Rendering waveform of ${duration} seconds with ${this.vertex_count} vertices.');
|
||||
}
|
||||
|
||||
function buildClippedVertex(x:Int, y:Int, topLeftVertexIndex:Int, topRightVertexIndex:Int, bottomLeftVertexIndex:Int, bottomRightVertexIndex:Int):Int
|
||||
|
|
|
@ -15,9 +15,12 @@ import haxe.ui.components.NumberStepper;
|
|||
import haxe.ui.components.Slider;
|
||||
import haxe.ui.components.TextField;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.ui.haxeui.components.WaveformPlayer;
|
||||
import funkin.audio.waveform.WaveformDataParser;
|
||||
import haxe.ui.containers.Box;
|
||||
import haxe.ui.containers.Frame;
|
||||
import haxe.ui.events.UIEvent;
|
||||
import funkin.audio.waveform.WaveformData;
|
||||
|
||||
/**
|
||||
* The toolbox which allows modifying information like Song Title, Scroll Speed, Characters/Stages, and starting BPM.
|
||||
|
@ -27,6 +30,14 @@ import haxe.ui.events.UIEvent;
|
|||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/offsets.xml"))
|
||||
class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
||||
{
|
||||
var waveformPlayer:WaveformPlayer;
|
||||
var waveformOpponent:WaveformPlayer;
|
||||
var waveformInstrumental:WaveformPlayer;
|
||||
var offsetButtonZoomIn:Button;
|
||||
var offsetButtonZoomOut:Button;
|
||||
|
||||
var waveformScale:Int = 64;
|
||||
|
||||
public function new(chartEditorState2:ChartEditorState)
|
||||
{
|
||||
super(chartEditorState2);
|
||||
|
@ -48,12 +59,67 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
this.x = 150;
|
||||
this.y = 250;
|
||||
|
||||
offsetButtonZoomIn.onClick = (_) -> {
|
||||
zoomWaveformIn();
|
||||
};
|
||||
offsetButtonZoomOut.onClick = (_) -> {
|
||||
zoomWaveformOut();
|
||||
};
|
||||
|
||||
// Build player waveform.
|
||||
waveformPlayer.waveform.forceUpdate = true;
|
||||
waveformPlayer.waveform.waveformData = chartEditorState.audioVocalTrackGroup.buildPlayerVoiceWaveform();
|
||||
// Set the width and duration to render the full waveform, with the clipRect applied we only render a segment of it.
|
||||
waveformPlayer.waveform.duration = 5.0; // chartEditorState.audioVocalTrackGroup.getPlayerVoiceLength() / 1000;
|
||||
|
||||
// Build opponent waveform.
|
||||
waveformOpponent.waveform.forceUpdate = true;
|
||||
waveformOpponent.waveform.waveformData = chartEditorState.audioVocalTrackGroup.buildOpponentVoiceWaveform();
|
||||
waveformOpponent.waveform.duration = 5.0; // chartEditorState.audioVocalTrackGroup.getOpponentVoiceLength() / 1000;
|
||||
|
||||
// Build instrumental waveform.
|
||||
waveformInstrumental.waveform.forceUpdate = true;
|
||||
waveformInstrumental.waveform.waveformData = WaveformDataParser.interpretFlxSound(chartEditorState.audioInstTrack);
|
||||
waveformInstrumental.waveform.duration = 5.0; // chartEditorState.audioInstTrack.length / 1000;
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public function zoomWaveformIn():Void
|
||||
{
|
||||
if (waveformScale > 1)
|
||||
{
|
||||
waveformScale = Std.int(waveformScale / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
waveformScale = 1;
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public function zoomWaveformOut():Void
|
||||
{
|
||||
waveformScale = Std.int(waveformScale * 2);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public override function refresh():Void
|
||||
{
|
||||
super.refresh();
|
||||
|
||||
// Set the width based on the waveformScale value.
|
||||
|
||||
waveformPlayer.waveform.width = waveformPlayer.waveform.waveformData.length / waveformScale;
|
||||
trace('Player duration: ${waveformPlayer.waveform.duration}, width: ${waveformPlayer.waveform.width}');
|
||||
|
||||
waveformOpponent.waveform.width = waveformOpponent.waveform.waveformData.length / waveformScale;
|
||||
trace('Opponent duration: ${waveformOpponent.waveform.duration}, width: ${waveformOpponent.waveform.width}');
|
||||
|
||||
waveformInstrumental.waveform.width = waveformInstrumental.waveform.waveformData.length / waveformScale;
|
||||
trace('Instrumental duration: ${waveformInstrumental.waveform.duration}, width: ${waveformInstrumental.waveform.width}');
|
||||
}
|
||||
|
||||
public static function build(chartEditorState:ChartEditorState):ChartEditorOffsetsToolbox
|
||||
|
|
17
source/funkin/ui/haxeui/components/WaveformPlayer.hx
Normal file
17
source/funkin/ui/haxeui/components/WaveformPlayer.hx
Normal file
|
@ -0,0 +1,17 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.audio.waveform.WaveformSprite;
|
||||
import funkin.audio.waveform.WaveformData;
|
||||
import haxe.ui.backend.flixel.components.SpriteWrapper;
|
||||
|
||||
class WaveformPlayer extends SpriteWrapper
|
||||
{
|
||||
public var waveform(default, null):WaveformSprite;
|
||||
|
||||
public function new(?waveformData:WaveformData)
|
||||
{
|
||||
super();
|
||||
this.waveform = new WaveformSprite(waveformData);
|
||||
this.sprite = waveform;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue