mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 10:05:41 -05:00
202 lines
6 KiB
Haxe
202 lines
6 KiB
Haxe
package funkin.ui.debug;
|
|
|
|
import flixel.FlxSprite;
|
|
import flixel.util.FlxColor;
|
|
import funkin.audio.FunkinSound;
|
|
import funkin.audio.waveform.WaveformData;
|
|
import funkin.audio.waveform.WaveformDataParser;
|
|
import funkin.graphics.rendering.MeshRender;
|
|
|
|
class WaveformTestState extends MusicBeatState
|
|
{
|
|
public function new()
|
|
{
|
|
super();
|
|
}
|
|
|
|
var waveformData:WaveformData;
|
|
|
|
var waveformAudio:FunkinSound;
|
|
|
|
var meshRender:MeshRender;
|
|
|
|
var timeMarker:FlxSprite;
|
|
|
|
public override function create():Void
|
|
{
|
|
super.create();
|
|
|
|
waveformData = WaveformDataParser.parseWaveformData(Paths.json("waveform/dadbattle-erect/dadbattle-erect.waveform"));
|
|
|
|
waveformAudio = FunkinSound.load(Paths.music('dadbattle-erect/dadbattle-erect'));
|
|
|
|
var lightBlue:FlxColor = FlxColor.fromString("#ADD8E6");
|
|
meshRender = new MeshRender(0, 0, lightBlue);
|
|
add(meshRender);
|
|
|
|
timeMarker = new FlxSprite(0, FlxG.height * 1 / 6);
|
|
timeMarker.makeGraphic(1, Std.int(FlxG.height * 2 / 3), FlxColor.RED);
|
|
add(timeMarker);
|
|
|
|
drawWaveform(time, duration);
|
|
}
|
|
|
|
/**
|
|
* @param offsetX Horizontal offset to draw the waveform at, in samples.
|
|
*/
|
|
function drawWaveform(timeSeconds:Float, duration:Float):Void
|
|
{
|
|
meshRender.clear();
|
|
|
|
var offsetX:Int = waveformData.secondsToIndex(timeSeconds);
|
|
|
|
var waveformHeight:Int = Std.int(FlxG.height * (2 / 3));
|
|
var waveformWidth:Int = FlxG.width;
|
|
var waveformCenterPos:Int = Std.int(FlxG.height / 2);
|
|
|
|
var oneSecondInIndices:Int = waveformData.secondsToIndex(1);
|
|
|
|
var startTime:Float = -1.0;
|
|
var endTime:Float = startTime + duration;
|
|
|
|
var startIndex:Int = Std.int(offsetX + (oneSecondInIndices * startTime));
|
|
var endIndex:Int = Std.int(offsetX + (oneSecondInIndices * (startTime + duration)));
|
|
|
|
var pixelsPerIndex:Float = waveformWidth / (endIndex - startIndex);
|
|
var indexesPerPixel:Float = (endIndex - startIndex) / waveformWidth;
|
|
|
|
if (pixelsPerIndex >= 1.0)
|
|
{
|
|
// Each index is at least one pixel wide, so we render each index.
|
|
var prevVertexTopIndex:Int = -1;
|
|
var prevVertexBottomIndex:Int = -1;
|
|
for (i in startIndex...endIndex)
|
|
{
|
|
var pixelPos:Int = Std.int((i - startIndex) * pixelsPerIndex);
|
|
|
|
var vertexTopY:Int = Std.int(waveformCenterPos - (waveformData.channel(0).maxSampleMapped(i) * waveformHeight / 2));
|
|
var vertexBottomY:Int = Std.int(waveformCenterPos + (-waveformData.channel(0).minSampleMapped(i) * waveformHeight / 2));
|
|
|
|
var vertexTopIndex:Int = meshRender.build_vertex(pixelPos, vertexTopY);
|
|
var vertexBottomIndex:Int = meshRender.build_vertex(pixelPos, vertexBottomY);
|
|
|
|
if (prevVertexTopIndex != -1 && prevVertexBottomIndex != -1)
|
|
{
|
|
meshRender.add_quad(prevVertexTopIndex, vertexTopIndex, vertexBottomIndex, prevVertexBottomIndex);
|
|
}
|
|
else
|
|
{
|
|
trace('Skipping quad at index ${i}');
|
|
}
|
|
|
|
prevVertexTopIndex = vertexTopIndex;
|
|
prevVertexBottomIndex = vertexBottomIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Indexes are less than one pixel wide, so for each pixel we render the maximum of the samples that fall within it.
|
|
var prevVertexTopIndex:Int = -1;
|
|
var prevVertexBottomIndex:Int = -1;
|
|
for (i in 0...waveformWidth)
|
|
{
|
|
// 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);
|
|
var rangeEnd:Int = Std.int((i + 1) * indexesPerPixel + startIndex);
|
|
|
|
var vertexTopY:Int = Std.int(waveformCenterPos - (waveformData.channel(0).maxSampleRangeMapped(rangeStart, rangeEnd) * waveformHeight / 2));
|
|
var vertexBottomY:Int = Std.int(waveformCenterPos + (-waveformData.channel(0).minSampleRangeMapped(rangeStart, rangeEnd) * waveformHeight / 2));
|
|
|
|
// trace('Drawing index ${rangeStart} at pixel ${i} with MAX ${vertexTopY} and MIN ${vertexBottomY}');
|
|
|
|
var vertexTopIndex:Int = meshRender.build_vertex(i, vertexTopY);
|
|
var vertexBottomIndex:Int = meshRender.build_vertex(i, vertexBottomY);
|
|
|
|
if (prevVertexTopIndex != -1 && prevVertexBottomIndex != -1)
|
|
{
|
|
meshRender.add_quad(prevVertexTopIndex, vertexTopIndex, vertexBottomIndex, prevVertexBottomIndex);
|
|
}
|
|
else
|
|
{
|
|
trace('Skipping quad at index ${i}');
|
|
}
|
|
|
|
prevVertexTopIndex = vertexTopIndex;
|
|
prevVertexBottomIndex = vertexBottomIndex;
|
|
}
|
|
}
|
|
|
|
trace('Drawing ${duration} seconds of waveform with ${meshRender.vertex_count} vertices');
|
|
|
|
var oneSecondInPixels:Float = waveformWidth / duration;
|
|
|
|
timeMarker.x = Std.int(oneSecondInPixels);
|
|
|
|
// For each sample in the waveform...
|
|
// Add a MAX vertex and a MIN vertex.
|
|
// If previous MAX/MIN is empty, store.
|
|
// If previous MAX/MIN is not empty, draw a quad using current and previous MAX/MIN. Then store current MAX/MIN.
|
|
// Continue until end of waveform.
|
|
}
|
|
|
|
public override function update(elapsed:Float):Void
|
|
{
|
|
super.update(elapsed);
|
|
|
|
if (FlxG.keys.justPressed.SPACE)
|
|
{
|
|
if (waveformAudio.isPlaying)
|
|
{
|
|
waveformAudio.stop();
|
|
}
|
|
else
|
|
{
|
|
waveformAudio.play();
|
|
}
|
|
}
|
|
|
|
if (waveformAudio.isPlaying)
|
|
{
|
|
var songTimeSeconds:Float = waveformAudio.time / 1000;
|
|
drawWaveform(songTimeSeconds, duration);
|
|
}
|
|
|
|
if (FlxG.keys.justPressed.UP)
|
|
{
|
|
trace('Zooming out');
|
|
duration += 1.0;
|
|
drawTheWaveform();
|
|
}
|
|
if (FlxG.keys.justPressed.DOWN)
|
|
{
|
|
trace('Zooming in');
|
|
duration -= 1.0;
|
|
drawTheWaveform();
|
|
}
|
|
if (FlxG.keys.justPressed.LEFT)
|
|
{
|
|
trace('Seeking back');
|
|
time -= 1.0;
|
|
drawTheWaveform();
|
|
}
|
|
if (FlxG.keys.justPressed.RIGHT)
|
|
{
|
|
trace('Seeking forward');
|
|
time += 1.0;
|
|
drawTheWaveform();
|
|
}
|
|
}
|
|
|
|
var time:Float = 0.0;
|
|
var duration:Float = 5.0;
|
|
|
|
function drawTheWaveform():Void
|
|
{
|
|
drawWaveform(time, duration);
|
|
}
|
|
|
|
public override function destroy():Void
|
|
{
|
|
super.destroy();
|
|
}
|
|
}
|