diff --git a/assets/images/editors/charter/audio-time-empty.png b/assets/images/editors/charter/audio-time-empty.png new file mode 100644 index 00000000..24157874 Binary files /dev/null and b/assets/images/editors/charter/audio-time-empty.png differ diff --git a/assets/images/editors/charter/audio-time-full.png b/assets/images/editors/charter/audio-time-full.png new file mode 100644 index 00000000..22073536 Binary files /dev/null and b/assets/images/editors/charter/audio-time-full.png differ diff --git a/source/funkin/editors/charter/CharterWaveForms.hx b/source/funkin/editors/charter/CharterWaveForms.hx deleted file mode 100644 index 6ad2a549..00000000 --- a/source/funkin/editors/charter/CharterWaveForms.hx +++ /dev/null @@ -1,110 +0,0 @@ -package funkin.editors.charter; - -import flixel.util.FlxColor; -import funkin.backend.system.Conductor; -import openfl.geom.Rectangle; -import openfl.media.Sound; -import flixel.system.FlxSound; -import lime.media.AudioBuffer; -import flixel.FlxSprite; - -class CharterWaveForms extends FlxTypedGroup { - public var sound:FlxSound; - public var strumLineID:Int = 0; - - public function new(sound:FlxSound) { - this.sound = sound; - super(); - - for (measure in 0...Std.int(Conductor.getMeasuresLength())) { - var waveFormMeasure:WaveformSprite = new WaveformSprite(0, (40 * Conductor.getMeasureLength()) * measure, sound, 40 * 4, Std.int(40 * Conductor.getMeasureLength())); - waveFormMeasure.generateFlixel( - Conductor.getTimeForStep(measure * Conductor.getMeasureLength()), - Conductor.getTimeForStep((measure+1) * Conductor.getMeasureLength()) - ); - trace( - Conductor.getTimeForStep(measure * Conductor.getMeasureLength()), - Conductor.getTimeForStep((measure+1) * Conductor.getMeasureLength()) - ); - add(waveFormMeasure); - } - } - - public override function update(elapsed:Float) { - for (mem in members) mem.x = strumLineID * 40 * 4; - super.update(elapsed); - } -} - -class WaveformSprite extends FlxSprite { - public var buffer:AudioBuffer; - public var sound:Sound; - public var peak:Float = 0; - public var valid:Bool = true; - - public override function destroy() { - super.destroy(); - if (buffer != null) { - buffer.data.buffer = null; - buffer.dispose(); - } - } - public function new(x:Float, y:Float, buffer:Dynamic, w:Int, h:Int) { - super(x,y); - this.buffer = null; - if (Std.isOfType(buffer, FlxSound)) { - @:privateAccess - this.buffer = cast(buffer, FlxSound)._sound.__buffer; - @:privateAccess - this.sound = cast(buffer, FlxSound)._sound; - } else if (Std.isOfType(buffer, Sound)) { - @:privateAccess - this.buffer = cast(buffer, Sound).__buffer; - this.sound = cast(buffer, Sound); - } else if (Std.isOfType(buffer, AudioBuffer)) { - @:privateAccess - this.buffer = cast(buffer, AudioBuffer); - } else { - valid = false; - return; - } - peak = Math.pow(2, buffer.bitsPerSample-1)-1; // max positive value of a bitsPerSample bits integer - makeGraphic(w, h, FlxColor.WHITE); // transparent - } - - public function generate(startPos:Int, endPos:Int) { - if (!valid) return; - startPos -= startPos % buffer.bitsPerSample; - endPos -= endPos % buffer.bitsPerSample; - pixels.lock(); - pixels.fillRect(new Rectangle(0, 0, pixels.width, pixels.height), 0); - var diff = endPos - startPos; - var diffRange = Math.floor(diff / pixels.height); - for(y in 0...pixels.height) { - var d = Math.floor(diff * (y / pixels.height)); - d -= d % buffer.bitsPerSample; - var pos = startPos + d; - var max:Int = 0; - for(i in 0...Math.floor(diffRange / buffer.bitsPerSample)) { - var thing = buffer.data.buffer.get(pos + (i * buffer.bitsPerSample)) | (buffer.data.buffer.get(pos + (i * buffer.bitsPerSample) + 1) << 8); - if (thing > 256 * 128) - thing -= 256 * 256; - if (max < thing) max = thing; - } - var thing = max; - var w = (thing) / pixels.width; - pixels.fillRect(new Rectangle((pixels.width / 2) - (w / 2), y, w, 1), FlxColor.RED); - } - pixels.unlock(); - } - - public function generateFlixel(startPos:Float, endPos:Float) { - if (!valid) return; - var rateFrequency = (1 / buffer.sampleRate); - var multiplicator = 1 / rateFrequency; // 1 hz/s - multiplicator *= buffer.bitsPerSample; - multiplicator -= multiplicator % buffer.bitsPerSample; - - generate(Math.floor(startPos * multiplicator / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample, Math.floor(endPos * multiplicator / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample); - } -} \ No newline at end of file diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 8ccd90fb..c8b2a89b 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -5,7 +5,7 @@ import funkin.backend.chart.ChartData; import haxe.io.Bytes; typedef SongCreationData = { - var song:ChartData; + var meta:ChartMetaData; var instBytes:Bytes; var voicesBytes:Bytes; } @@ -81,16 +81,16 @@ class SongCreationScreen extends UISubstateWindow { addLabelOn(needsVoicesCheckbox, "Needs Voices"); needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4; - instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, #if html5 "mp3" #else "ogg" #end, function (res) { - var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 16, instExplorer.y + 8, res); + instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, Paths.SOUND_EXT, function (res) { + var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res); instExplorer.members.push(audioPlayer); instExplorer.uiElement = audioPlayer; }); songDataGroup.add(instExplorer); addLabelOn(instExplorer, "Inst Audio File"); - voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, #if html5 "mp3" #else "ogg" #end, function (res) { - var audioPlayer:UIAudioPlayer = new UIAudioPlayer(voicesExplorer.x + 16, voicesExplorer.y + 8, res); + voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, Paths.SOUND_EXT, function (res) { + var audioPlayer:UIAudioPlayer = new UIAudioPlayer(voicesExplorer.x + 8, voicesExplorer.y + 8, res); voicesExplorer.members.push(audioPlayer); voicesExplorer.uiElement = audioPlayer; }); diff --git a/source/funkin/editors/ui/UIAudioPlayer.hx b/source/funkin/editors/ui/UIAudioPlayer.hx index 797cfa52..a63404ab 100644 --- a/source/funkin/editors/ui/UIAudioPlayer.hx +++ b/source/funkin/editors/ui/UIAudioPlayer.hx @@ -1,39 +1,91 @@ package funkin.editors.ui; +import flixel.util.FlxColor; +import flixel.ui.FlxBar; +import openfl.geom.Rectangle; import flixel.sound.FlxSound; import haxe.io.Bytes; import lime.media.AudioBuffer; import openfl.media.Sound; +import flixel.util.FlxStringUtil; + +using flixel.util.FlxSpriteUtil; class UIAudioPlayer extends UIButton { public var sound:FlxSound; public var bytes:Bytes; public var playingSprite:FlxSprite; + public var timeBar:FlxBar; public var timeText:UIText; + public var timeBarSpr:UISprite; // VISUAL FEEDBACK !11!11! + public function new(x:Float, y:Float, bytes:Bytes) { sound = FlxG.sound.load(Sound.fromAudioBuffer(AudioBuffer.fromBytes(bytes))); super(x, y, "", function () { if (sound.playing) sound.pause(); - else sound.play(); - }, 58 - 18, 58 - 18); + else sound.play(false, sound.time); + }, 58 - 16, 58 - 16); + trace(x,y); - playingSprite = new FlxSprite(x + ((58 - 18)/2) - 8, y + ((58 - 18)/2) - 8).loadGraphic(Paths.image('editors/charter/audio-buttons'), true, 16, 16); + playingSprite = new FlxSprite(x + ((58 - 16)/2) - 8, y + ((58 - 16)/2) - 8).loadGraphic(Paths.image('editors/charter/audio-buttons'), true, 16, 16); playingSprite.animation.add("paused", [0]); playingSprite.animation.add("playing", [1]); playingSprite.antialiasing = false; playingSprite.updateHitbox(); members.push(playingSprite); + timeText = new UIText(x + bWidth + 8, y + 4, 0, "", 12); + timeText.alignment = RIGHT; + members.push(timeText); + + timeBar = new FlxBar(x + bWidth + 8, y + (58 - 16) - ((58 - 16)/3) - 4, LEFT_TO_RIGHT, 202, Std.int((58 - 16)/3), sound, "time", 0, sound.length); + timeBar.createImageBar(Paths.image('editors/charter/audio-time-empty'), Paths.image('editors/charter/audio-time-full')); + timeBar.numDivisions = timeBar.barWidth; + members.push(timeBar); + + timeBarSpr = cast new UISprite(timeBar.x, timeBar.y).makeGraphic(timeBar.barWidth, timeBar.barHeight, 0xFFFFFF); + timeBarSpr.alpha = 0; + members.push(timeBarSpr); } + public var dragging:Bool = false; + public var wasPlaying:Bool = false; + public override function update(elapsed:Float) { super.update(elapsed); - if (sound != null) + if (sound != null) { playingSprite.animation.play(sound.playing ? "playing" : "paused"); + timeText.text = '${FlxStringUtil.formatTime(sound.time/1000, true)} / ${FlxStringUtil.formatTime(sound.length/1000)}'; + } + + var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); + var spritePos:FlxPoint = timeBar.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]); + + if (((mousePos.x > (spritePos.x)) && (mousePos.x < (spritePos.x) + timeBar.barWidth)) + && ((mousePos.y > (spritePos.y)) && (mousePos.y < (spritePos.y) + timeBar.barHeight))) { + timeBarSpr.cursor = BUTTON; + timeBarSpr.alpha = 0.3; + if (FlxG.mouse.justPressed) { + dragging = true; + wasPlaying = sound.playing; + } + + if (FlxG.mouse.pressed && dragging) { + if (sound.playing) sound.pause(); + sound.time = FlxMath.remapToRange(mousePos.x - spritePos.x, 0, timeBar.barWidth, 0, sound.length); + } + } else { + timeBarSpr.cursor = ARROW; + timeBarSpr.alpha = 0; + } + + if (FlxG.mouse.released && dragging && wasPlaying) { + sound.play(dragging = wasPlaying = false, sound.time); + } } public override function destroy() { diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx index d4880400..3098f8d9 100644 --- a/source/funkin/editors/ui/UIFileExplorer.hx +++ b/source/funkin/editors/ui/UIFileExplorer.hx @@ -29,14 +29,14 @@ class UIFileExplorer extends UISliceSprite { if (this.onFile != null) this.onFile(file); }); fileDialog.open(fileType); - }, 320 - 16, 58 - 18); + }, 320 - 16, 58 - 16); members.push(uploadButton); - uploadIcon = new FlxSprite(uploadButton.x + (uploadButton.bWidth / 2) - 8, uploadButton.y + ((58-18)/2) - 8).loadGraphic(Paths.image('editors/charter/upload-button')); + uploadIcon = new FlxSprite(uploadButton.x + (uploadButton.bWidth / 2) - 8, uploadButton.y + ((58-16)/2) - 8).loadGraphic(Paths.image('editors/charter/upload-button')); uploadIcon.antialiasing = false; uploadButton.members.push(uploadIcon); - deleteButton = new UIButton(x + 320 - (58 - 18) - 16, y + 8, "", function () { + deleteButton = new UIButton(x + 320 - (58 - 16) - 8, y + 8, "", function () { if (uiElement != null) { members.remove(uiElement); uiElement.destroy(); @@ -46,11 +46,11 @@ class UIFileExplorer extends UISliceSprite { MemoryUtil.clearMajor(); deleteButton.visible = deleteButton.selectable = deleteIcon.visible = !(uploadButton.visible = uploadButton.selectable = true); - }, 58 - 18, 58 - 18); + }, 58 - 16, 58 - 16); deleteButton.color = 0xFFFF0000; members.push(deleteButton); - deleteIcon = new FlxSprite(deleteButton.x + ((58 - 18)/2) - 8, deleteButton.y + ((58 - 18)/2) - 8).loadGraphic(Paths.image('editors/character/delete-button')); + deleteIcon = new FlxSprite(deleteButton.x + ((58 - 16)/2) - 8, deleteButton.y + ((58 - 16)/2) - 8).loadGraphic(Paths.image('editors/character/delete-button')); deleteIcon.antialiasing = false; members.push(deleteIcon);