mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-23 08:07:54 -05:00
Work in progress on offsets toolbox (working dragging!)
This commit is contained in:
parent
8085b20a67
commit
f6e4bc863d
10 changed files with 395 additions and 60 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit ae904d08df53712a582d5a3c26de40ef552422d1
|
||||
Subproject commit 384a99d732456b2b3d35fa9bd2e10aa5f747afa6
|
|
@ -7,6 +7,7 @@ import flash.utils.ByteArray;
|
|||
import flixel.sound.FlxSound;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.system.FlxAssets.FlxSoundAsset;
|
||||
import funkin.util.tools.ICloneable;
|
||||
import openfl.Assets;
|
||||
#if (openfl >= "8.0.0")
|
||||
import openfl.utils.AssetType;
|
||||
|
@ -17,10 +18,17 @@ import openfl.utils.AssetType;
|
|||
* - Delayed playback via negative song position.
|
||||
*/
|
||||
@:nullSafety
|
||||
class FunkinSound extends FlxSound
|
||||
class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
||||
{
|
||||
static var cache(default, null):FlxTypedGroup<FunkinSound> = new FlxTypedGroup<FunkinSound>();
|
||||
|
||||
public var paused(get, never):Bool;
|
||||
|
||||
function get_paused():Bool
|
||||
{
|
||||
return this._paused;
|
||||
}
|
||||
|
||||
public var isPlaying(get, never):Bool;
|
||||
|
||||
function get_isPlaying():Bool
|
||||
|
@ -153,6 +161,21 @@ class FunkinSound extends FlxSound
|
|||
return this;
|
||||
}
|
||||
|
||||
public function clone():FunkinSound
|
||||
{
|
||||
var sound:FunkinSound = new FunkinSound();
|
||||
|
||||
// Clone the sound by creating one with the same data buffer.
|
||||
// Reusing the `Sound` object directly causes issues with playback.
|
||||
@:privateAccess
|
||||
sound._sound = openfl.media.Sound.fromAudioBuffer(this._sound.__buffer);
|
||||
|
||||
// Call init to ensure the FlxSound is properly initialized.
|
||||
sound.init(this.looped, this.autoDestroy, this.onComplete);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new `FunkinSound` object.
|
||||
*
|
||||
|
|
|
@ -16,6 +16,8 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
|||
|
||||
public var pitch(get, set):Float;
|
||||
|
||||
public var playing(get, never):Bool;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
@ -165,6 +167,13 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
|
|||
return time;
|
||||
}
|
||||
|
||||
function get_playing():Bool
|
||||
{
|
||||
if (getFirstAlive != null) return getFirstAlive().playing;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_volume():Float
|
||||
{
|
||||
if (getFirstAlive() != null) return getFirstAlive().volume;
|
||||
|
|
|
@ -106,6 +106,16 @@ class VoicesGroup extends SoundGroup
|
|||
return opponentVolume = volume;
|
||||
}
|
||||
|
||||
public function getPlayerVoice(index:Int = 0):Null<FunkinSound>
|
||||
{
|
||||
return playerVoices.members[index];
|
||||
}
|
||||
|
||||
public function getOpponentVoice(index:Int = 0):Null<FunkinSound>
|
||||
{
|
||||
return opponentVoices.members[index];
|
||||
}
|
||||
|
||||
public function buildPlayerVoiceWaveform():Null<WaveformData>
|
||||
{
|
||||
if (playerVoices.members.length == 0) return null;
|
||||
|
|
|
@ -145,6 +145,14 @@ class WaveformSprite extends MeshRender
|
|||
this.forceUpdate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually tell the waveform to rebuild itself, even if none of its properties have changed.
|
||||
*/
|
||||
public function markDirty():Void
|
||||
{
|
||||
isWaveformDirty = true;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
|
|
@ -1438,19 +1438,28 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
return value;
|
||||
}
|
||||
|
||||
var currentVocalOffset(get, set):Float;
|
||||
var currentVocalOffsetPlayer(get, set):Float;
|
||||
|
||||
function get_currentVocalOffset():Float
|
||||
function get_currentVocalOffsetPlayer():Float
|
||||
{
|
||||
// Currently there's only one vocal offset, so we just grab the player's offset since both should be the same.
|
||||
// Should probably make it so we can set offsets for player + opponent individually, though.
|
||||
return currentSongOffsets.getVocalOffset(currentPlayerChar);
|
||||
}
|
||||
|
||||
function set_currentVocalOffset(value:Float):Float
|
||||
function set_currentVocalOffsetPlayer(value:Float):Float
|
||||
{
|
||||
// Currently there's only one vocal offset, so we just apply it to both characters.
|
||||
currentSongOffsets.setVocalOffset(currentPlayerChar, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
var currentVocalOffsetOpponent(get, set):Float;
|
||||
|
||||
function get_currentVocalOffsetOpponent():Float
|
||||
{
|
||||
return currentSongOffsets.getVocalOffset(currentOpponentChar);
|
||||
}
|
||||
|
||||
function set_currentVocalOffsetOpponent(value:Float):Float
|
||||
{
|
||||
currentSongOffsets.setVocalOffset(currentOpponentChar, value);
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -204,7 +204,7 @@ class ChartEditorAudioHandler
|
|||
trace('[WARN] Failed to parse waveform data for vocal track.');
|
||||
}
|
||||
|
||||
state.audioVocalTrackGroup.playerVoicesOffset = state.currentVocalOffset;
|
||||
state.audioVocalTrackGroup.playerVoicesOffset = state.currentVocalOffsetPlayer;
|
||||
return true;
|
||||
case DAD:
|
||||
state.audioVocalTrackGroup.addOpponentVoice(vocalTrack);
|
||||
|
@ -227,7 +227,7 @@ class ChartEditorAudioHandler
|
|||
trace('[WARN] Failed to parse waveform data for vocal track.');
|
||||
}
|
||||
|
||||
state.audioVocalTrackGroup.opponentVoicesOffset = state.currentVocalOffset;
|
||||
state.audioVocalTrackGroup.opponentVoicesOffset = state.currentVocalOffsetOpponent;
|
||||
|
||||
return true;
|
||||
case OTHER:
|
||||
|
@ -248,9 +248,9 @@ class ChartEditorAudioHandler
|
|||
{
|
||||
state.audioVocalTrackGroup.clear();
|
||||
}
|
||||
if (state.audioVisGroup != null)
|
||||
if (state.audioWaveforms != null)
|
||||
{
|
||||
state.audioVisGroup.clearAllVis();
|
||||
state.audioWaveforms.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,6 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
var buttonCharacterGirlfriend:Button;
|
||||
var buttonCharacterOpponent:Button;
|
||||
var inputBPM:NumberStepper;
|
||||
var inputOffsetInst:NumberStepper;
|
||||
var inputOffsetVocal:NumberStepper;
|
||||
var labelScrollSpeed:Label;
|
||||
var inputScrollSpeed:Slider;
|
||||
var frameVariation:Frame;
|
||||
|
@ -138,25 +136,6 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
chartEditorState.updateTimeSignature();
|
||||
};
|
||||
|
||||
inputOffsetInst.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
chartEditorState.currentInstrumentalOffset = event.value;
|
||||
Conductor.instance.instrumentalOffset = event.value;
|
||||
// Update song length.
|
||||
chartEditorState.songLengthInMs = (chartEditorState.audioInstTrack?.length ?? 1000.0) + Conductor.instance.instrumentalOffset;
|
||||
};
|
||||
|
||||
inputOffsetVocal.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
chartEditorState.currentVocalOffset = event.value;
|
||||
if (chartEditorState.audioVocalTrackGroup != null)
|
||||
{
|
||||
chartEditorState.audioVocalTrackGroup.playerVoicesOffset = event.value;
|
||||
chartEditorState.audioVocalTrackGroup.opponentVoicesOffset = event.value;
|
||||
}
|
||||
};
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.value != null && event.target.value > 0;
|
||||
|
||||
|
@ -196,8 +175,6 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
||||
inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle;
|
||||
inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
|
||||
inputOffsetInst.value = chartEditorState.currentSongMetadata.offsets.getInstrumentalOffset();
|
||||
inputOffsetVocal.value = chartEditorState.currentSongMetadata.offsets.getVocalOffset(chartEditorState.currentSongMetadata.playData.characters.player);
|
||||
inputScrollSpeed.value = chartEditorState.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x';
|
||||
frameVariation.text = 'Variation: ${chartEditorState.selectedVariation.toTitleCase()}';
|
||||
|
|
|
@ -1,42 +1,54 @@
|
|||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import funkin.audio.SoundGroup;
|
||||
import haxe.ui.components.Button;
|
||||
import haxe.ui.components.CheckBox;
|
||||
import haxe.ui.components.DropDown;
|
||||
import haxe.ui.components.HorizontalSlider;
|
||||
import haxe.ui.components.Label;
|
||||
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.VBox;
|
||||
import haxe.ui.containers.ScrollView;
|
||||
import haxe.ui.containers.Frame;
|
||||
import haxe.ui.core.Screen;
|
||||
import haxe.ui.events.DragEvent;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
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.
|
||||
*/
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/offsets.xml"))
|
||||
class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
||||
{
|
||||
var waveformContainer:VBox;
|
||||
var waveformScrollview:ScrollView;
|
||||
var waveformPlayer:WaveformPlayer;
|
||||
var waveformOpponent:WaveformPlayer;
|
||||
var waveformInstrumental:WaveformPlayer;
|
||||
var offsetButtonZoomIn:Button;
|
||||
var offsetButtonZoomOut:Button;
|
||||
var offsetButtonPause:Button;
|
||||
var offsetButtonPlay:Button;
|
||||
var offsetButtonStop:Button;
|
||||
var offsetStepperPlayer:NumberStepper;
|
||||
var offsetStepperOpponent:NumberStepper;
|
||||
var offsetStepperInstrumental:NumberStepper;
|
||||
|
||||
var waveformScale:Int = 64;
|
||||
static final BASE_SCALE:Int = 64;
|
||||
|
||||
var waveformScale:Int = BASE_SCALE;
|
||||
|
||||
var audioPreviewTracks:SoundGroup;
|
||||
|
||||
// Local store of the audio offsets, so we can detect when they change.
|
||||
var audioPreviewPlayerOffset:Float = 0;
|
||||
var audioPreviewOpponentOffset:Float = 0;
|
||||
var audioPreviewInstrumentalOffset:Float = 0;
|
||||
|
||||
public function new(chartEditorState2:ChartEditorState)
|
||||
{
|
||||
|
@ -65,24 +77,223 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
offsetButtonZoomOut.onClick = (_) -> {
|
||||
zoomWaveformOut();
|
||||
};
|
||||
offsetButtonPause.onClick = (_) -> {
|
||||
pauseAudioPreview();
|
||||
};
|
||||
offsetButtonPlay.onClick = (_) -> {
|
||||
playAudioPreview();
|
||||
};
|
||||
offsetButtonStop.onClick = (_) -> {
|
||||
stopAudioPreview();
|
||||
};
|
||||
offsetStepperPlayer.onChange = (event:UIEvent) -> {
|
||||
chartEditorState.currentVocalOffsetPlayer = event.value;
|
||||
refresh();
|
||||
}
|
||||
offsetStepperOpponent.onChange = (event:UIEvent) -> {
|
||||
chartEditorState.currentVocalOffsetOpponent = event.value;
|
||||
refresh();
|
||||
}
|
||||
offsetStepperInstrumental.onChange = (event:UIEvent) -> {
|
||||
chartEditorState.currentInstrumentalOffset = event.value;
|
||||
refresh();
|
||||
}
|
||||
waveformScrollview.onScroll = (_) -> {
|
||||
if (!audioPreviewTracks.playing)
|
||||
{
|
||||
// We have to change the song position to match.
|
||||
var currentWaveformIndex:Int = Std.int(waveformScrollview.hscrollPos / BASE_SCALE * waveformScale);
|
||||
var targetSongTimeSeconds:Float = waveformPlayer.waveform.waveformData.indexToSeconds(currentWaveformIndex);
|
||||
audioPreviewTracks.time = targetSongTimeSeconds * Constants.MS_PER_SEC;
|
||||
addOffsetsToAudioPreview();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The scrollview probably changed because the song position changed.
|
||||
// If we try to move the song now it will glitch.
|
||||
}
|
||||
|
||||
// Either way, clipRect has changed, so we need to refresh the waveforms.
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Build player waveform.
|
||||
waveformPlayer.waveform.forceUpdate = true;
|
||||
// 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;
|
||||
waveformPlayer.waveform.duration = chartEditorState.audioVocalTrackGroup.getPlayerVoiceLength() / Constants.MS_PER_SEC;
|
||||
|
||||
// Build opponent waveform.
|
||||
waveformOpponent.waveform.forceUpdate = true;
|
||||
// waveformOpponent.waveform.forceUpdate = true;
|
||||
waveformOpponent.waveform.waveformData = chartEditorState.audioVocalTrackGroup.buildOpponentVoiceWaveform();
|
||||
waveformOpponent.waveform.duration = 5.0; // chartEditorState.audioVocalTrackGroup.getOpponentVoiceLength() / 1000;
|
||||
waveformOpponent.waveform.duration = chartEditorState.audioVocalTrackGroup.getOpponentVoiceLength() / Constants.MS_PER_SEC;
|
||||
|
||||
// Build instrumental waveform.
|
||||
waveformInstrumental.waveform.forceUpdate = true;
|
||||
// waveformInstrumental.waveform.forceUpdate = true;
|
||||
waveformInstrumental.waveform.waveformData = WaveformDataParser.interpretFlxSound(chartEditorState.audioInstTrack);
|
||||
waveformInstrumental.waveform.duration = 5.0; // chartEditorState.audioInstTrack.length / 1000;
|
||||
waveformInstrumental.waveform.duration = chartEditorState.audioInstTrack.length / Constants.MS_PER_SEC;
|
||||
|
||||
refresh();
|
||||
refreshAudioPreview();
|
||||
|
||||
waveformPlayer.registerEvent(MouseEvent.MOUSE_DOWN, (_) -> {
|
||||
onStartDragWaveform(PLAYER);
|
||||
});
|
||||
waveformOpponent.registerEvent(MouseEvent.MOUSE_DOWN, (_) -> {
|
||||
onStartDragWaveform(OPPONENT);
|
||||
});
|
||||
waveformInstrumental.registerEvent(MouseEvent.MOUSE_DOWN, (_) -> {
|
||||
onStartDragWaveform(INSTRUMENTAL);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull the audio tracks from the chart editor state and create copies of them to play in the Offsets Toolbox.
|
||||
* These must be DEEP CLONES or else the editor will affect the audio preview!
|
||||
*/
|
||||
public function refreshAudioPreview():Void
|
||||
{
|
||||
if (audioPreviewTracks == null)
|
||||
{
|
||||
audioPreviewTracks = new SoundGroup();
|
||||
// Make sure audioPreviewTracks (and all its children) receives update() calls.
|
||||
chartEditorState.add(audioPreviewTracks);
|
||||
}
|
||||
else
|
||||
{
|
||||
audioPreviewTracks.stop();
|
||||
audioPreviewTracks.clear();
|
||||
}
|
||||
|
||||
audioPreviewTracks.add(chartEditorState.audioInstTrack.clone());
|
||||
audioPreviewTracks.add(chartEditorState.audioVocalTrackGroup.getPlayerVoice().clone());
|
||||
audioPreviewTracks.add(chartEditorState.audioVocalTrackGroup.getOpponentVoice().clone());
|
||||
|
||||
addOffsetsToAudioPreview();
|
||||
}
|
||||
|
||||
var dragMousePosition:Float = 0;
|
||||
var dragWaveform:Waveform = null;
|
||||
|
||||
public function onStartDragWaveform(waveform:Waveform):Void
|
||||
{
|
||||
dragMousePosition = FlxG.mouse.x;
|
||||
dragWaveform = waveform;
|
||||
|
||||
Screen.instance.registerEvent(MouseEvent.MOUSE_MOVE, onDragWaveform);
|
||||
Screen.instance.registerEvent(MouseEvent.MOUSE_UP, onStopDragWaveform);
|
||||
}
|
||||
|
||||
public function onDragWaveform(event:MouseEvent):Void
|
||||
{
|
||||
var newDragMousePosition = FlxG.mouse.x;
|
||||
var deltaMousePosition = newDragMousePosition - dragMousePosition;
|
||||
|
||||
if (deltaMousePosition == 0) return;
|
||||
|
||||
var deltaPixels:Float = deltaMousePosition / BASE_SCALE * waveformScale;
|
||||
var deltaMilliseconds:Float = switch (dragWaveform)
|
||||
{
|
||||
case PLAYER:
|
||||
deltaPixels / waveformPlayer.waveform.waveformData.pointsPerSecond() * Constants.MS_PER_SEC;
|
||||
case OPPONENT:
|
||||
deltaPixels / waveformOpponent.waveform.waveformData.pointsPerSecond() * Constants.MS_PER_SEC;
|
||||
case INSTRUMENTAL:
|
||||
deltaPixels / waveformInstrumental.waveform.waveformData.pointsPerSecond() * Constants.MS_PER_SEC;
|
||||
};
|
||||
|
||||
trace('Moving waveform by ${deltaMousePosition} -> ${deltaPixels} -> ${deltaMilliseconds} milliseconds.');
|
||||
|
||||
switch (dragWaveform)
|
||||
{
|
||||
case PLAYER:
|
||||
chartEditorState.currentVocalOffsetPlayer += deltaMilliseconds;
|
||||
case OPPONENT:
|
||||
chartEditorState.currentVocalOffsetOpponent += deltaMilliseconds;
|
||||
case INSTRUMENTAL:
|
||||
chartEditorState.currentInstrumentalOffset += deltaMilliseconds;
|
||||
}
|
||||
|
||||
dragMousePosition = newDragMousePosition;
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public function onStopDragWaveform(event:MouseEvent):Void
|
||||
{
|
||||
// Stop dragging.
|
||||
Screen.instance.unregisterEvent(MouseEvent.MOUSE_MOVE, onDragWaveform);
|
||||
Screen.instance.unregisterEvent(MouseEvent.MOUSE_UP, onStopDragWaveform);
|
||||
|
||||
dragMousePosition = 0;
|
||||
dragWaveform = null;
|
||||
}
|
||||
|
||||
public function playAudioPreview():Void
|
||||
{
|
||||
// chartEditorState.stopAudioPlayback();
|
||||
|
||||
audioPreviewTracks.resume();
|
||||
}
|
||||
|
||||
public function addOffsetsToAudioPreview():Void
|
||||
{
|
||||
var trackInst = audioPreviewTracks.members[0];
|
||||
if (trackInst != null)
|
||||
{
|
||||
audioPreviewInstrumentalOffset = chartEditorState.currentInstrumentalOffset;
|
||||
trackInst.time -= audioPreviewInstrumentalOffset;
|
||||
}
|
||||
|
||||
var trackPlayer = audioPreviewTracks.members[1];
|
||||
if (trackPlayer != null)
|
||||
{
|
||||
audioPreviewPlayerOffset = chartEditorState.currentVocalOffsetPlayer;
|
||||
trackPlayer.time -= audioPreviewPlayerOffset;
|
||||
}
|
||||
|
||||
var trackOpponent = audioPreviewTracks.members[2];
|
||||
if (trackOpponent != null)
|
||||
{
|
||||
audioPreviewOpponentOffset = chartEditorState.currentVocalOffsetOpponent;
|
||||
trackOpponent.time -= audioPreviewOpponentOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public function pauseAudioPreview():Void
|
||||
{
|
||||
audioPreviewTracks.pause();
|
||||
}
|
||||
|
||||
public function stopAudioPreview():Void
|
||||
{
|
||||
audioPreviewTracks.stop();
|
||||
|
||||
audioPreviewTracks.time = 0;
|
||||
|
||||
var trackInst = audioPreviewTracks.members[0];
|
||||
if (trackInst != null)
|
||||
{
|
||||
audioPreviewInstrumentalOffset = chartEditorState.currentInstrumentalOffset;
|
||||
trackInst.time = -audioPreviewInstrumentalOffset;
|
||||
}
|
||||
|
||||
var trackPlayer = audioPreviewTracks.members[1];
|
||||
if (trackPlayer != null)
|
||||
{
|
||||
audioPreviewPlayerOffset = chartEditorState.currentVocalOffsetPlayer;
|
||||
trackPlayer.time = -audioPreviewPlayerOffset;
|
||||
}
|
||||
|
||||
var trackOpponent = audioPreviewTracks.members[2];
|
||||
if (trackOpponent != null)
|
||||
{
|
||||
audioPreviewOpponentOffset = chartEditorState.currentVocalOffsetOpponent;
|
||||
trackOpponent.time = -audioPreviewOpponentOffset;
|
||||
}
|
||||
|
||||
waveformScrollview.hscrollPos = 0;
|
||||
refresh();
|
||||
}
|
||||
|
||||
public function zoomWaveformIn():Void
|
||||
|
@ -96,6 +307,8 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
waveformScale = 1;
|
||||
}
|
||||
|
||||
trace('Zooming in, scale: ${waveformScale}');
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
@ -103,23 +316,84 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
{
|
||||
waveformScale = Std.int(waveformScale * 2);
|
||||
|
||||
trace('Zooming out, scale: ${waveformScale}');
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (audioPreviewTracks.playing)
|
||||
{
|
||||
trace('Playback time: ${audioPreviewTracks.time}');
|
||||
|
||||
var targetScrollPos:Float = waveformPlayer.waveform.waveformData.secondsToIndex(audioPreviewTracks.time / Constants.MS_PER_SEC) / waveformScale * BASE_SCALE;
|
||||
waveformScrollview.hscrollPos = targetScrollPos;
|
||||
}
|
||||
|
||||
if (chartEditorState.currentInstrumentalOffset != audioPreviewInstrumentalOffset)
|
||||
{
|
||||
var track = audioPreviewTracks.members[0];
|
||||
if (track != null)
|
||||
{
|
||||
track.time += audioPreviewInstrumentalOffset;
|
||||
track.time -= chartEditorState.currentInstrumentalOffset;
|
||||
audioPreviewInstrumentalOffset = chartEditorState.currentInstrumentalOffset;
|
||||
}
|
||||
}
|
||||
if (chartEditorState.currentVocalOffsetPlayer != audioPreviewPlayerOffset)
|
||||
{
|
||||
var track = audioPreviewTracks.members[1];
|
||||
if (track != null)
|
||||
{
|
||||
track.time += audioPreviewPlayerOffset;
|
||||
track.time -= chartEditorState.currentVocalOffsetPlayer;
|
||||
audioPreviewPlayerOffset = chartEditorState.currentVocalOffsetPlayer;
|
||||
}
|
||||
}
|
||||
if (chartEditorState.currentVocalOffsetOpponent != audioPreviewOpponentOffset)
|
||||
{
|
||||
var track = audioPreviewTracks.members[2];
|
||||
if (track != null)
|
||||
{
|
||||
track.time += audioPreviewOpponentOffset;
|
||||
track.time -= chartEditorState.currentVocalOffsetOpponent;
|
||||
audioPreviewOpponentOffset = chartEditorState.currentVocalOffsetOpponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}');
|
||||
var maxWidth:Int = -1;
|
||||
|
||||
waveformOpponent.waveform.width = waveformOpponent.waveform.waveformData.length / waveformScale;
|
||||
trace('Opponent duration: ${waveformOpponent.waveform.duration}, width: ${waveformOpponent.waveform.width}');
|
||||
offsetStepperPlayer.value = chartEditorState.currentVocalOffsetPlayer;
|
||||
offsetStepperOpponent.value = chartEditorState.currentVocalOffsetOpponent;
|
||||
offsetStepperInstrumental.value = chartEditorState.currentInstrumentalOffset;
|
||||
|
||||
waveformInstrumental.waveform.width = waveformInstrumental.waveform.waveformData.length / waveformScale;
|
||||
trace('Instrumental duration: ${waveformInstrumental.waveform.duration}, width: ${waveformInstrumental.waveform.width}');
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
waveformPlayer.waveform.markDirty();
|
||||
waveformOpponent.waveform.markDirty();
|
||||
waveformInstrumental.waveform.markDirty();
|
||||
|
||||
waveformContainer.width = maxWidth;
|
||||
}
|
||||
|
||||
public static function build(chartEditorState:ChartEditorState):ChartEditorOffsetsToolbox
|
||||
|
@ -127,3 +401,10 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox
|
|||
return new ChartEditorOffsetsToolbox(chartEditorState);
|
||||
}
|
||||
}
|
||||
|
||||
enum Waveform
|
||||
{
|
||||
PLAYER;
|
||||
OPPONENT;
|
||||
INSTRUMENTAL;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,24 @@ class CrashHandler
|
|||
throw "This is an example of an uncaught exception.";
|
||||
}
|
||||
|
||||
public static function induceNullObjectReference():Void
|
||||
{
|
||||
var obj:Dynamic = null;
|
||||
var value = obj.test;
|
||||
}
|
||||
|
||||
public static function induceNullObjectReference2():Void
|
||||
{
|
||||
var obj:Dynamic = null;
|
||||
var value = obj.test();
|
||||
}
|
||||
|
||||
public static function induceNullObjectReference3():Void
|
||||
{
|
||||
var obj:Dynamic = null;
|
||||
var value = obj();
|
||||
}
|
||||
|
||||
static function renderMethod():String
|
||||
{
|
||||
return switch (FlxG.renderMethod)
|
||||
|
|
Loading…
Reference in a new issue