diff --git a/OngekiFumenEditor/App.config b/OngekiFumenEditor/App.config
index 4e3911d4..8fd90805 100644
--- a/OngekiFumenEditor/App.config
+++ b/OngekiFumenEditor/App.config
@@ -173,6 +173,9 @@
136, 3, 152
+
+ -1
+
@@ -190,6 +193,9 @@
True
+
+ -1
+
\ No newline at end of file
diff --git a/OngekiFumenEditor/Kernel/Graphics/IDrawingContext.cs b/OngekiFumenEditor/Kernel/Graphics/IDrawingContext.cs
index 9b882535..02c60d16 100644
--- a/OngekiFumenEditor/Kernel/Graphics/IDrawingContext.cs
+++ b/OngekiFumenEditor/Kernel/Graphics/IDrawingContext.cs
@@ -46,7 +46,7 @@ public VisibleRect(Vector2 buttomRight, Vector2 topLeft)
IPerfomenceMonitor PerfomenceMonitor { get; }
- void PrepareEditorLoop(GLWpfControl glView);
+ void PrepareRenderLoop(GLWpfControl glView);
void OnRenderSizeChanged(GLWpfControl glView, SizeChangedEventArgs e);
void Render(TimeSpan ts);
diff --git a/OngekiFumenEditor/Kernel/SettingPages/FumenVisualEditor/Views/FumenVisualEditorGlobalSettingView.xaml b/OngekiFumenEditor/Kernel/SettingPages/FumenVisualEditor/Views/FumenVisualEditorGlobalSettingView.xaml
index c71fe90d..c7c35176 100644
--- a/OngekiFumenEditor/Kernel/SettingPages/FumenVisualEditor/Views/FumenVisualEditorGlobalSettingView.xaml
+++ b/OngekiFumenEditor/Kernel/SettingPages/FumenVisualEditor/Views/FumenVisualEditorGlobalSettingView.xaml
@@ -2,6 +2,7 @@
x:Class="OngekiFumenEditor.Kernel.SettingPages.FumenVisualEditor.Views.FumenVisualEditorGlobalSettingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
xmlns:cal="http://caliburnmicro.com"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:markup="clr-namespace:OngekiFumenEditor.UI.Markup"
@@ -100,56 +101,91 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Graphics/WaveformDrawing/DefaultImpls/DefaultWaveformDrawing.cs b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Graphics/WaveformDrawing/DefaultImpls/DefaultWaveformDrawing.cs
index f451e747..1d30663d 100644
--- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Graphics/WaveformDrawing/DefaultImpls/DefaultWaveformDrawing.cs
+++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Graphics/WaveformDrawing/DefaultImpls/DefaultWaveformDrawing.cs
@@ -74,7 +74,8 @@ public override void Draw(IWaveformDrawingContext target, PeakPointCollection pe
lineDrawing.Begin(target, 1);
{
var prevX = 0f;
- lineDrawing.PostPoint(new(-width / 2, 0), WhiteColor, InvailedLineDash);
+
+ lineDrawing.PostPoint(new(-width / 2, 0), WhiteColor, InvailedLineDash);
for (int i = minIndex; i < maxIndex; i++)
{
var peakPoint = peakData[i];
diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.WaveformDrawing.cs b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.WaveformDrawing.cs
index 0117b04a..f9d8c64b 100644
--- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.WaveformDrawing.cs
+++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.WaveformDrawing.cs
@@ -1,4 +1,5 @@
using Caliburn.Micro;
+using ControlzEx.Standard;
using OngekiFumenEditor.Kernel.Audio;
using OngekiFumenEditor.Kernel.Graphics;
using OngekiFumenEditor.Kernel.Graphics.Performence;
@@ -11,6 +12,7 @@
using OpenTK.Mathematics;
using OpenTK.Wpf;
using System;
+using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -19,236 +21,269 @@
namespace OngekiFumenEditor.Modules.AudioPlayerToolViewer.ViewModels
{
- public partial class AudioPlayerToolViewerViewModel : IWaveformDrawingContext
- {
- private float viewWidth;
- private float viewHeight;
- private int renderViewWidth;
- private int renderViewHeight;
- private IPerfomenceMonitor performenceMonitor;
- private ISamplePeak samplePeak;
- private CancellationTokenSource loadWaveformTask;
- private CancellationTokenSource resampleTaskCancelTokenSource;
- private TaskCompletionSource initTask = new TaskCompletionSource();
-
- private PeakPointCollection rawPeakData;
- private PeakPointCollection usingPeakData;
-
- public VisibleRect Rect { get; private set; }
-
- public Matrix4 ProjectionMatrix { get; private set; }
- public Matrix4 ViewMatrix { get; private set; }
- public Matrix4 ViewProjectionMatrix { get; private set; }
-
- public IPerfomenceMonitor PerfomenceMonitor => performenceMonitor;
-
- public TimeSpan CurrentTime { get; private set; }
- public TimeSpan AudioTotalDuration => AudioPlayer?.Duration ?? default;
-
- private IWaveformDrawing waveformDrawing;
- public IWaveformDrawing WaveformDrawing
- {
- get => waveformDrawing;
- set
- {
- Set(ref waveformDrawing, value);
- }
- }
-
- private int resampleSize = Properties.AudioPlayerToolViewerSetting.Default.ResampleSize;
- public int ResampleSize
- {
- get => resampleSize;
- set
- {
- Set(ref resampleSize, value);
- ResamplePeak();
- Properties.AudioPlayerToolViewerSetting.Default.ResampleSize = value;
- Properties.AudioPlayerToolViewerSetting.Default.Save();
- }
- }
-
- private float waveformVecticalScale = Properties.AudioPlayerToolViewerSetting.Default.WaveformVecticalScale;
- public float WaveformVecticalScale
- {
- get => waveformVecticalScale;
- set
- {
- Set(ref waveformVecticalScale, value);
- Properties.AudioPlayerToolViewerSetting.Default.WaveformVecticalScale = value;
- Properties.AudioPlayerToolViewerSetting.Default.Save();
- }
- }
-
- private float durationMsPerPixel = Properties.AudioPlayerToolViewerSetting.Default.DurationMsPerPixel;
- public float DurationMsPerPixel
- {
- get => durationMsPerPixel;
- set
- {
- Set(ref durationMsPerPixel, value);
- Properties.AudioPlayerToolViewerSetting.Default.DurationMsPerPixel = value;
- Properties.AudioPlayerToolViewerSetting.Default.Save();
- }
- }
-
- private float currentTimeXOffset = Properties.AudioPlayerToolViewerSetting.Default.CurrentTimeXOffset;
- public float CurrentTimeXOffset
- {
- get => currentTimeXOffset;
- set
- {
- Set(ref currentTimeXOffset, value);
- Properties.AudioPlayerToolViewerSetting.Default.CurrentTimeXOffset = value;
- Properties.AudioPlayerToolViewerSetting.Default.Save();
- }
- }
-
- private bool isShowWaveform = true;
- public bool IsShowWaveform
- {
- get => isShowWaveform && Properties.AudioPlayerToolViewerSetting.Default.EnableWaveformDisplay;
- set => Set(ref isShowWaveform, value);
- }
-
- public FumenVisualEditorViewModel EditorViewModel => Editor;
-
- private void RecalcViewProjectionMatrix()
- {
- ProjectionMatrix =
- Matrix4.CreateOrthographic(viewWidth, viewHeight, -1, 1);
- ViewMatrix =
- Matrix4.CreateTranslation(new Vector3(0, 0, 0)); //todo
-
- ViewProjectionMatrix = ViewMatrix * ProjectionMatrix;
- }
-
- public void OnRenderSizeChanged(GLWpfControl glView, SizeChangedEventArgs sizeArg)
- {
- Log.LogDebug($"new size: {sizeArg.NewSize} , glView.RenderSize = {glView.RenderSize}");
- var dpiX = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleX;
- var dpiY = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleY;
-
- viewWidth = (float)sizeArg.NewSize.Width;
- viewHeight = (float)sizeArg.NewSize.Height;
- renderViewWidth = (int)(sizeArg.NewSize.Width * dpiX);
- renderViewHeight = (int)(sizeArg.NewSize.Height * dpiY);
-
- RecalcViewProjectionMatrix();
- }
-
- public async void PrepareEditorLoop(GLWpfControl glView)
- {
- Log.LogDebug($"ready.");
- await IoC.Get().CheckOrInitGraphics();
-
- InitRender();
- var dpiX = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleX;
- var dpiY = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleY;
-
- viewWidth = (float)glView.ActualWidth;
- viewHeight = (float)glView.ActualHeight;
- renderViewWidth = (int)(glView.ActualWidth * dpiX);
- renderViewHeight = (int)(glView.ActualHeight * dpiY);
-
- RecalcViewProjectionMatrix();
-
- //暂时没有需要显示检测的必要?
- //performenceMonitor = IoC.Get();
- performenceMonitor = new DummyPerformenceMonitor();
-
- glView.Render += Render;
- }
-
- private void InitRender()
- {
- samplePeak = IoC.Get();
- WaveformDrawing = IoC.Get();
- initTask.SetResult();
- }
-
- private void PrepareWaveform(IAudioPlayer player)
- {
- CleanWaveform();
- loadWaveformTask = new CancellationTokenSource();
- var cancelToken = loadWaveformTask.Token;
-
- Task.Run(() => OnPrepareWaveform(player, cancelToken), cancelToken);
- }
-
- private async void OnPrepareWaveform(IAudioPlayer player, CancellationToken cancelToken)
- {
- await initTask.Task;
- if (cancelToken.IsCancellationRequested || player is null || samplePeak is null)
- return;
- var sampleData = await player.GetSamplesAsync();
- rawPeakData = sampleData is not null ? samplePeak.GetPeakValues(sampleData) : null;
- ResamplePeak();
- }
-
- private async void ResamplePeak()
- {
- resampleTaskCancelTokenSource?.Cancel();
- var tokenSource = new CancellationTokenSource();
- resampleTaskCancelTokenSource = tokenSource;
-
- if (ResampleSize == 0)
- usingPeakData = rawPeakData;
- else
- {
- var newPeakData = rawPeakData is null ? default : await rawPeakData?.GenerateSimplfiedAsync(ResampleSize, tokenSource.Token);
- if (!tokenSource.IsCancellationRequested)
- usingPeakData = newPeakData;
- }
- }
-
- private void CleanWaveform()
- {
- loadWaveformTask?.Cancel();
- loadWaveformTask = null;
- rawPeakData = null;
- usingPeakData = null;
- }
-
- public void Render(TimeSpan ts)
- {
+ public partial class AudioPlayerToolViewerViewModel : IWaveformDrawingContext
+ {
+ private float viewWidth;
+ private float viewHeight;
+ private int renderViewWidth;
+ private int renderViewHeight;
+ private IPerfomenceMonitor performenceMonitor;
+ private Stopwatch sw;
+ private ISamplePeak samplePeak;
+ private CancellationTokenSource loadWaveformTask;
+ private CancellationTokenSource resampleTaskCancelTokenSource;
+ private TaskCompletionSource initTask = new TaskCompletionSource();
+
+ private PeakPointCollection rawPeakData;
+ private PeakPointCollection usingPeakData;
+
+ public VisibleRect Rect { get; private set; }
+
+ public Matrix4 ProjectionMatrix { get; private set; }
+ public Matrix4 ViewMatrix { get; private set; }
+ public Matrix4 ViewProjectionMatrix { get; private set; }
+
+ public IPerfomenceMonitor PerfomenceMonitor => performenceMonitor;
+
+ public TimeSpan CurrentTime { get; private set; }
+ public TimeSpan AudioTotalDuration => AudioPlayer?.Duration ?? default;
+
+ private IWaveformDrawing waveformDrawing;
+ public IWaveformDrawing WaveformDrawing
+ {
+ get => waveformDrawing;
+ set
+ {
+ Set(ref waveformDrawing, value);
+ }
+ }
+
+ private int resampleSize = Properties.AudioPlayerToolViewerSetting.Default.ResampleSize;
+ public int ResampleSize
+ {
+ get => resampleSize;
+ set
+ {
+ Set(ref resampleSize, value);
+ ResamplePeak();
+ Properties.AudioPlayerToolViewerSetting.Default.ResampleSize = value;
+ Properties.AudioPlayerToolViewerSetting.Default.Save();
+ }
+ }
+
+ private float waveformVecticalScale = Properties.AudioPlayerToolViewerSetting.Default.WaveformVecticalScale;
+ public float WaveformVecticalScale
+ {
+ get => waveformVecticalScale;
+ set
+ {
+ Set(ref waveformVecticalScale, value);
+ Properties.AudioPlayerToolViewerSetting.Default.WaveformVecticalScale = value;
+ Properties.AudioPlayerToolViewerSetting.Default.Save();
+ }
+ }
+
+ private float durationMsPerPixel = Properties.AudioPlayerToolViewerSetting.Default.DurationMsPerPixel;
+ public float DurationMsPerPixel
+ {
+ get => durationMsPerPixel;
+ set
+ {
+ Set(ref durationMsPerPixel, value);
+ Properties.AudioPlayerToolViewerSetting.Default.DurationMsPerPixel = value;
+ Properties.AudioPlayerToolViewerSetting.Default.Save();
+ }
+ }
+
+ private float currentTimeXOffset = Properties.AudioPlayerToolViewerSetting.Default.CurrentTimeXOffset;
+ public float CurrentTimeXOffset
+ {
+ get => currentTimeXOffset;
+ set
+ {
+ Set(ref currentTimeXOffset, value);
+ Properties.AudioPlayerToolViewerSetting.Default.CurrentTimeXOffset = value;
+ Properties.AudioPlayerToolViewerSetting.Default.Save();
+ }
+ }
+
+ private bool isShowWaveform = true;
+ public bool IsShowWaveform
+ {
+ get => isShowWaveform && Properties.AudioPlayerToolViewerSetting.Default.EnableWaveformDisplay;
+ set => Set(ref isShowWaveform, value);
+ }
+
+ private float actualRenderInterval = float.MaxValue;
+ private int limitFPS = Properties.AudioPlayerToolViewerSetting.Default.LimitFPS;
+ public int LimitFPS
+ {
+ get => limitFPS;
+ set
+ {
+ Set(ref limitFPS, value);
+ Properties.AudioPlayerToolViewerSetting.Default.CurrentTimeXOffset = value;
+ Properties.AudioPlayerToolViewerSetting.Default.Save();
+ UpdateActualRenderInterval();
+ }
+ }
+
+ public FumenVisualEditorViewModel EditorViewModel => Editor;
+
+ private void UpdateActualRenderInterval()
+ {
+ actualRenderInterval = LimitFPS switch
+ {
+ <= 0 => float.MaxValue,
+ _ => 1000.0F / LimitFPS
+ };
+ }
+
+ private void RecalcViewProjectionMatrix()
+ {
+ ProjectionMatrix =
+ Matrix4.CreateOrthographic(viewWidth, viewHeight, -1, 1);
+ ViewMatrix =
+ Matrix4.CreateTranslation(new Vector3(0, 0, 0)); //todo
+
+ ViewProjectionMatrix = ViewMatrix * ProjectionMatrix;
+ }
+
+ public void OnRenderSizeChanged(GLWpfControl glView, SizeChangedEventArgs sizeArg)
+ {
+ Log.LogDebug($"new size: {sizeArg.NewSize} , glView.RenderSize = {glView.RenderSize}");
+ var dpiX = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleX;
+ var dpiY = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleY;
+
+ viewWidth = (float)sizeArg.NewSize.Width;
+ viewHeight = (float)sizeArg.NewSize.Height;
+ renderViewWidth = (int)(sizeArg.NewSize.Width * dpiX);
+ renderViewHeight = (int)(sizeArg.NewSize.Height * dpiY);
+
+ RecalcViewProjectionMatrix();
+ }
+
+ public async void PrepareRenderLoop(GLWpfControl glView)
+ {
+ Log.LogDebug($"ready.");
+ await IoC.Get().CheckOrInitGraphics();
+
+ InitRender();
+ var dpiX = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleX;
+ var dpiY = VisualTreeHelper.GetDpi(Application.Current.MainWindow).DpiScaleY;
+
+ viewWidth = (float)glView.ActualWidth;
+ viewHeight = (float)glView.ActualHeight;
+ renderViewWidth = (int)(glView.ActualWidth * dpiX);
+ renderViewHeight = (int)(glView.ActualHeight * dpiY);
+
+ RecalcViewProjectionMatrix();
+
+ //暂时没有需要显示检测的必要?
+ //performenceMonitor = IoC.Get();
+ performenceMonitor = new DummyPerformenceMonitor();
+
+ sw = new Stopwatch();
+ sw.Start();
+ glView.Render += Render;
+ }
+
+ private void InitRender()
+ {
+ samplePeak = IoC.Get();
+ WaveformDrawing = IoC.Get();
+ initTask.SetResult();
+ }
+
+ private void PrepareWaveform(IAudioPlayer player)
+ {
+ CleanWaveform();
+ loadWaveformTask = new CancellationTokenSource();
+ var cancelToken = loadWaveformTask.Token;
+
+ Task.Run(() => OnPrepareWaveform(player, cancelToken), cancelToken);
+ }
+
+ private async void OnPrepareWaveform(IAudioPlayer player, CancellationToken cancelToken)
+ {
+ await initTask.Task;
+ if (cancelToken.IsCancellationRequested || player is null || samplePeak is null)
+ return;
+ var sampleData = await player.GetSamplesAsync();
+ rawPeakData = sampleData is not null ? samplePeak.GetPeakValues(sampleData) : null;
+ ResamplePeak();
+ }
+
+ private async void ResamplePeak()
+ {
+ resampleTaskCancelTokenSource?.Cancel();
+ var tokenSource = new CancellationTokenSource();
+ resampleTaskCancelTokenSource = tokenSource;
+
+ if (ResampleSize == 0)
+ usingPeakData = rawPeakData;
+ else
+ {
+ var newPeakData = rawPeakData is null ? default : await rawPeakData?.GenerateSimplfiedAsync(ResampleSize, tokenSource.Token);
+ if (!tokenSource.IsCancellationRequested)
+ usingPeakData = newPeakData;
+ }
+ }
+
+ private void CleanWaveform()
+ {
+ loadWaveformTask?.Cancel();
+ loadWaveformTask = null;
+ rawPeakData = null;
+ usingPeakData = null;
+ }
+
+ public void Render(TimeSpan ts)
+ {
#if DEBUG
- GLUtility.CheckError();
+ GLUtility.CheckError();
#endif
-
- GL.ClearColor(16 / 255f, 16 / 255f, 16 / 255f, 1f);
- GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
-
- if (Editor is null || !IsShowWaveform)
- return;
-
- PerfomenceMonitor.PostUIRenderTime(ts);
- PerfomenceMonitor.OnBeforeRender();
-
- GL.Viewport(0, 0, renderViewWidth, renderViewHeight);
-
- UpdateDrawingContext();
-
- if (usingPeakData is not null)
- WaveformDrawing.Draw(this, usingPeakData);
-
- PerfomenceMonitor.OnAfterRender();
- }
-
- private void UpdateDrawingContext()
- {
- Rect = new VisibleRect(new(0 + viewWidth, 0), new(0, 0 + viewHeight));
-
- if (AudioPlayer?.IsPlaying ?? false)
- CurrentTime = AudioPlayer.CurrentTime;
- else
- {
- var tGrid = Editor?.GetCurrentTGrid();
- if (tGrid is null)
- return;
- var editorAudioTime = TGridCalculator.ConvertTGridToAudioTime(tGrid, Editor);
- CurrentTime = editorAudioTime;
- }
- }
- }
+ //limit
+ if (LimitFPS > 0)
+ {
+ if (sw.ElapsedMilliseconds < actualRenderInterval)
+ return;
+ sw.Restart();
+ }
+
+ GL.ClearColor(16 / 255f, 16 / 255f, 16 / 255f, 1f);
+ GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
+
+ if (Editor is null || !IsShowWaveform)
+ return;
+
+ PerfomenceMonitor.PostUIRenderTime(ts);
+ PerfomenceMonitor.OnBeforeRender();
+
+ GL.Viewport(0, 0, renderViewWidth, renderViewHeight);
+
+ UpdateDrawingContext();
+
+ if (usingPeakData is not null)
+ WaveformDrawing.Draw(this, usingPeakData);
+
+ PerfomenceMonitor.OnAfterRender();
+ }
+
+ private void UpdateDrawingContext()
+ {
+ Rect = new VisibleRect(new(0 + viewWidth, 0), new(0, 0 + viewHeight));
+
+ if (AudioPlayer?.IsPlaying ?? false)
+ CurrentTime = AudioPlayer.CurrentTime;
+ else
+ {
+ var tGrid = Editor?.GetCurrentTGrid();
+ if (tGrid is null)
+ return;
+ var editorAudioTime = TGridCalculator.ConvertTGridToAudioTime(tGrid, Editor);
+ CurrentTime = editorAudioTime;
+ }
+ }
+ }
}
diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs
index c0f88c70..1e9ba646 100644
--- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs
+++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs
@@ -134,8 +134,9 @@ public AudioPlayerToolViewerViewModel()
IoC.Get().OnActivateEditorChanged += OnActivateEditorChanged;
Editor = IoC.Get().CurrentActivatedEditor;
- CompositionTarget.Rendering += CompositionTarget_Rendering;
- }
+ UpdateActualRenderInterval();
+ CompositionTarget.Rendering += CompositionTarget_Rendering;
+ }
private void OnActivateEditorChanged(FumenVisualEditorViewModel @new, FumenVisualEditorViewModel old)
{
@@ -167,7 +168,6 @@ private void CompositionTarget_Rendering(object sender, EventArgs e)
Process(AudioPlayer.CurrentTime);
}
-
private void Process(TimeSpan time)
{
if (Editor is null)
diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml
index 40fb1f44..15bfc9e6 100644
--- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml
+++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml
@@ -2,6 +2,7 @@
x:Class="OngekiFumenEditor.Modules.AudioPlayerToolViewer.Views.AudioPlayerToolViewerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
xmlns:cal="http://caliburnmicro.com"
xmlns:controls="clr-namespace:OngekiFumenEditor.UI.Controls"
xmlns:converters1="clr-namespace:OngekiFumenEditor.Modules.AudioPlayerToolViewer.Converters"
@@ -9,9 +10,10 @@
xmlns:gemini="http://schemas.timjones.io/gemini"
xmlns:local="clr-namespace:OngekiFumenEditor.Modules.FumenMetaInfoBrowser.Views"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+ xmlns:markup="clr-namespace:OngekiFumenEditor.UI.Markup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:objectinspector="clr-namespace:OngekiFumenEditor.UI.Controls.ObjectInspector"
- xmlns:res="clr-namespace:OngekiFumenEditor.Properties" xmlns:markup="clr-namespace:OngekiFumenEditor.UI.Markup"
+ xmlns:res="clr-namespace:OngekiFumenEditor.Properties"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:vm="clr-namespace:OngekiFumenEditor.Modules.AudioPlayerToolViewer.ViewModels"
xmlns:wpf="clr-namespace:OpenTK.Wpf;assembly=GLWpfControl"
@@ -193,6 +195,39 @@
Foreground="{StaticResource EnvironmentToolWindowText}"
Header="{markup:Translate [SwitchControl]}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
displayFPS;
@@ -140,7 +145,7 @@ public void OnRenderSizeChanged(GLWpfControl glView, SizeChangedEventArgs sizeAr
renderViewHeight = (int)(sizeArg.NewSize.Height * dpiY);
}
- public async void PrepareEditorLoop(GLWpfControl openGLView)
+ public async void PrepareRenderLoop(GLWpfControl openGLView)
{
Log.LogDebug("ready.");
await IoC.Get().CheckOrInitGraphics();
@@ -173,6 +178,10 @@ public async void PrepareEditorLoop(GLWpfControl openGLView)
actualPerformenceMonitor = IoC.Get();
IsDisplayFPS = IsDisplayFPS;
+ UpdateActualRenderInterval();
+ sw = new Stopwatch();
+ sw.Start();
+
openGLView.Render += OnEditorLoop;
}
@@ -186,14 +195,32 @@ private void OnEditorLoop(TimeSpan ts)
public void Render(TimeSpan ts)
=> OnEditorRender(ts);
- private void OnEditorRender(TimeSpan ts)
+ private void UpdateActualRenderInterval()
{
- PerfomenceMonitor.PostUIRenderTime(ts);
- PerfomenceMonitor.OnBeforeRender();
+ actualRenderInterval = EditorGlobalSetting.Default.LimitFPS switch
+ {
+ <= 0 => 0,
+ _ => 1000.0F / EditorGlobalSetting.Default.LimitFPS
+ };
+ }
+ private void OnEditorRender(TimeSpan ts)
+ {
#if DEBUG
GLUtility.CheckError();
#endif
+ //limit
+ if (actualRenderInterval > 0)
+ {
+ var ms = sw.ElapsedMilliseconds;
+ if (ms < actualRenderInterval)
+ goto End;
+ ts = TimeSpan.FromMilliseconds(ms);
+ sw.Restart();
+ }
+
+ PerfomenceMonitor.PostUIRenderTime(ts);
+ PerfomenceMonitor.OnBeforeRender();
CleanRender();
GL.Viewport(0, 0, renderViewWidth, renderViewHeight);
@@ -202,7 +229,7 @@ private void OnEditorRender(TimeSpan ts)
var fumen = Fumen;
if (fumen is null)
- return;
+ goto End;
var tGrid = GetCurrentTGrid();
@@ -218,7 +245,7 @@ private void OnEditorRender(TimeSpan ts)
var maxTGrid = TGridCalculator.ConvertYToTGrid_DesignMode(maxY, this);
if (maxTGrid is null || minTGrid is null)
- return;
+ goto End;
visibleTGridRanges.Add((minTGrid, maxTGrid));
}
else
@@ -231,7 +258,7 @@ private void OnEditorRender(TimeSpan ts)
foreach (var x in ranges)
{
if (x.maxTGrid is null || x.minTGrid is null)
- return;
+ goto End;
visibleTGridRanges.Add((x.minTGrid, x.maxTGrid));
}
}
@@ -312,13 +339,13 @@ private void OnEditorRender(TimeSpan ts)
playerLocationHelper.Draw(this);
selectingRangeHelper.Draw(this);
-
//clean up
- drawMap.Clear();
foreach (var list in map.Values)
ObjectPool>.Return(list);
ObjectPool>>.Return(map);
+ End:
+ drawMap.Clear();
PerfomenceMonitor.OnAfterRender();
}
diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs
index d59524db..bc4d0d04 100644
--- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs
+++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.UserInteractionActions.cs
@@ -753,6 +753,27 @@ public void KeyboardAction_FastSetObjectIsCritical(ActionExecutionContext e)
}));
}
+ public void KeyboardAction_FastSwitchFlickDirection(ActionExecutionContext e)
+ {
+ var selectedFlicks = SelectObjects.OfType().ToList();
+
+ if (selectedFlicks.Count == 0)
+ {
+ ToastNotify(Resources.NoFlickCouldBeSwitched);
+ return;
+ }
+
+ UndoRedoManager.ExecuteAction(LambdaUndoAction.Create(Resources.BatchSwitchFlickDirection, ChangeFlicks, ChangeFlicks));
+
+ void ChangeFlicks()
+ {
+ foreach (var flick in selectedFlicks)
+ flick.Direction = flick.Direction == Flick.FlickDirection.Left
+ ? Flick.FlickDirection.Right
+ : Flick.FlickDirection.Left;
+ }
+ }
+
public bool CheckAndNotifyIfPlaceBeyondDuration(Point placePoint)
{
if (placePoint.Y > TotalDurationHeight || placePoint.Y < 0)
diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs
index 3c1e32cc..dd8c0e98 100644
--- a/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs
+++ b/OngekiFumenEditor/Modules/FumenVisualEditor/ViewModels/FumenVisualEditorViewModel.cs
@@ -105,6 +105,9 @@ private void OnSettingPropertyChanged(object sender, PropertyChangedEventArgs e)
enableShowPlayerLocation = EditorGlobalSetting.Default.EnableShowPlayerLocation;
PlayerLocationRecorder.Clear();
break;
+ case nameof(EditorGlobalSetting.LimitFPS):
+ UpdateActualRenderInterval();
+ break;
case nameof(EditorGlobalSetting.XGridUnitSpace):
case nameof(EditorGlobalSetting.DisplayTimeFormat):
case nameof(EditorGlobalSetting.BeatSplit):
@@ -177,7 +180,7 @@ public FumenVisualEditorViewModel() : base()
//replace owned impl
UndoRedoManager = new DefaultEditorUndoManager(this);
- Properties.EditorGlobalSetting.Default.PropertyChanged += OnSettingPropertyChanged;
+ EditorGlobalSetting.Default.PropertyChanged += OnSettingPropertyChanged;
DisplayName = default;
}
diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml b/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml
index 1ddd594a..eac012d7 100644
--- a/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml
+++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml
@@ -16,7 +16,7 @@
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:valueconverters="clr-namespace:OngekiFumenEditor.UI.ValueConverters"
xmlns:viewmodels="clr-namespace:OngekiFumenEditor.Modules.FumenVisualEditor.ViewModels"
- cal:Message.Attach="[Key OemTilde] = [Action KeyboardAction_FastPlaceDockableObjectToWallLeft]; [Key D4] = [Action KeyboardAction_FastPlaceDockableObjectToWallRight]; [Key D3] = [Action KeyboardAction_FastPlaceDockableObjectToRight]; [Key D2] = [Action KeyboardAction_FastPlaceDockableObjectToCenter]; [Key D1] = [Action KeyboardAction_FastPlaceDockableObjectToLeft]; [Event DragEnter] = [Action Grid_DragEnter($executionContext)]; [Event Drop] = [Action Grid_Drop($executionContext)]; [Event FocusableChanged] = [Action OnFocusableChanged($executionContext)]; [Key Delete] = [Action KeyboardAction_DeleteSelectingObjects]; [Gesture Ctrl+A] = [Action KeyboardAction_SelectAllObjects]; [Key Escape] = [Action KeyboardAction_CancelSelectingObjects]; [Key C] = [Action KeyboardAction_FastSetObjectIsCritical($executionContext)]; [Key Q] = [Action KeyboardAction_HideOrShow];[Key A] = [Action KeyboardAction_FastAddConnectableChild($executionContext)]; [Gesture Ctrl+C]=[Action MenuItemAction_CopySelectedObjects]; [Gesture Ctrl+V]=[Action MenuItemAction_PasteCopiesObjects]; "
+ cal:Message.Attach="[Key OemTilde] = [Action KeyboardAction_FastPlaceDockableObjectToWallLeft]; [Key D4] = [Action KeyboardAction_FastPlaceDockableObjectToWallRight]; [Key D3] = [Action KeyboardAction_FastPlaceDockableObjectToRight]; [Key D2] = [Action KeyboardAction_FastPlaceDockableObjectToCenter]; [Key D1] = [Action KeyboardAction_FastPlaceDockableObjectToLeft]; [Event DragEnter] = [Action Grid_DragEnter($executionContext)]; [Event Drop] = [Action Grid_Drop($executionContext)]; [Event FocusableChanged] = [Action OnFocusableChanged($executionContext)]; [Key Delete] = [Action KeyboardAction_DeleteSelectingObjects]; [Gesture Ctrl+A] = [Action KeyboardAction_SelectAllObjects]; [Key Escape] = [Action KeyboardAction_CancelSelectingObjects]; [Key C] = [Action KeyboardAction_FastSetObjectIsCritical($executionContext)]; [Key Q] = [Action KeyboardAction_HideOrShow];[Key A] = [Action KeyboardAction_FastAddConnectableChild($executionContext)]; [Key F] = [Action KeyboardAction_FastSwitchFlickDirection($executionContext)]; [Gesture Ctrl+C]=[Action MenuItemAction_CopySelectedObjects]; [Gesture Ctrl+V]=[Action MenuItemAction_PasteCopiesObjects]; "
d:DataContext="{d:DesignInstance Type=viewmodels:FumenVisualEditorViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
diff --git a/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml.cs b/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml.cs
index 6e35c7cc..ca558966 100644
--- a/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml.cs
+++ b/OngekiFumenEditor/Modules/FumenVisualEditor/Views/FumenVisualEditorView.xaml.cs
@@ -23,7 +23,7 @@ private void glView_Ready()
{
if (DataContext is IDrawingContext fumenPreviewer)
{
- fumenPreviewer.PrepareEditorLoop(glView);
+ fumenPreviewer.PrepareRenderLoop(glView);
}
});
}
diff --git a/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.Designer.cs b/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.Designer.cs
index b8eba317..1636fc46 100644
--- a/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.Designer.cs
+++ b/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.Designer.cs
@@ -12,7 +12,7 @@ namespace OngekiFumenEditor.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")]
public sealed partial class AudioPlayerToolViewerSetting : global::System.Configuration.ApplicationSettingsBase {
private static AudioPlayerToolViewerSetting defaultInstance = ((AudioPlayerToolViewerSetting)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AudioPlayerToolViewerSetting())));
@@ -82,5 +82,17 @@ public bool EnableWaveformDisplay {
this["EnableWaveformDisplay"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("-1")]
+ public int LimitFPS {
+ get {
+ return ((int)(this["LimitFPS"]));
+ }
+ set {
+ this["LimitFPS"] = value;
+ }
+ }
}
}
diff --git a/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.settings b/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.settings
index b4ebd8f9..838037c4 100644
--- a/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.settings
+++ b/OngekiFumenEditor/Properties/AudioPlayerToolViewerSetting.settings
@@ -17,5 +17,8 @@
True
+
+ -1
+
\ No newline at end of file
diff --git a/OngekiFumenEditor/Properties/EditorGlobalSetting.Designer.cs b/OngekiFumenEditor/Properties/EditorGlobalSetting.Designer.cs
index 7c4f40d5..611533a7 100644
--- a/OngekiFumenEditor/Properties/EditorGlobalSetting.Designer.cs
+++ b/OngekiFumenEditor/Properties/EditorGlobalSetting.Designer.cs
@@ -454,5 +454,17 @@ public bool EnableShowPlayerLocation {
this["ColorHoldWallLeft"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("-1")]
+ public int LimitFPS {
+ get {
+ return ((int)(this["LimitFPS"]));
+ }
+ set {
+ this["LimitFPS"] = value;
+ }
+ }
}
}
diff --git a/OngekiFumenEditor/Properties/EditorGlobalSetting.settings b/OngekiFumenEditor/Properties/EditorGlobalSetting.settings
index 08848239..9290be03 100644
--- a/OngekiFumenEditor/Properties/EditorGlobalSetting.settings
+++ b/OngekiFumenEditor/Properties/EditorGlobalSetting.settings
@@ -110,5 +110,8 @@
136, 3, 152
+
+ -1
+
\ No newline at end of file
diff --git a/OngekiFumenEditor/Properties/Resources.Designer.cs b/OngekiFumenEditor/Properties/Resources.Designer.cs
index e6e4e7b9..88207716 100644
--- a/OngekiFumenEditor/Properties/Resources.Designer.cs
+++ b/OngekiFumenEditor/Properties/Resources.Designer.cs
@@ -357,6 +357,15 @@ public static string BatchSetIsCritical {
}
}
+ ///
+ /// 查找类似 Batch switch flick direction 的本地化字符串。
+ ///
+ public static string BatchSwitchFlickDirection {
+ get {
+ return ResourceManager.GetString("BatchSwitchFlickDirection", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Subdivision multiplier for the meter: 的本地化字符串。
///
@@ -2765,6 +2774,15 @@ public static string NoEditorTarget {
}
}
+ ///
+ /// 查找类似 No flicks to switch in selection 的本地化字符串。
+ ///
+ public static string NoFlickCouldBeSwitched {
+ get {
+ return ResourceManager.GetString("NoFlickCouldBeSwitched", resourceCulture);
+ }
+ }
+
///
/// 查找类似 No fumen input 的本地化字符串。
///
@@ -2774,6 +2792,15 @@ public static string NoFumenInput {
}
}
+ ///
+ /// 查找类似 (No Limit) 的本地化字符串。
+ ///
+ public static string NoLimit {
+ get {
+ return ResourceManager.GetString("NoLimit", resourceCulture);
+ }
+ }
+
///
/// 查找类似 No suitable objects to setting property IsCritical 的本地化字符串。
///
diff --git a/OngekiFumenEditor/Properties/Resources.ja.resx b/OngekiFumenEditor/Properties/Resources.ja.resx
index eda75f85..22b48a6f 100644
--- a/OngekiFumenEditor/Properties/Resources.ja.resx
+++ b/OngekiFumenEditor/Properties/Resources.ja.resx
@@ -216,6 +216,9 @@
一括でクリティカル設定
+
+ Flickの向きを一括変更する
+
拍の分割数:
@@ -999,6 +1002,9 @@
IsCritical属性を一括設定できるオブジェクトがありません
+
+ 方向を変更する前にFlickを選択してください
+
曲線レーンが補完されていません
@@ -1539,4 +1545,7 @@
このアプリケーションのすべての構成オプションをリセットしますか?
+
+ (無制限)
+
\ No newline at end of file
diff --git a/OngekiFumenEditor/Properties/Resources.resx b/OngekiFumenEditor/Properties/Resources.resx
index 5066ed26..38f301e7 100644
--- a/OngekiFumenEditor/Properties/Resources.resx
+++ b/OngekiFumenEditor/Properties/Resources.resx
@@ -216,6 +216,9 @@
Batch set property IsCritical
+
+ Batch switch flick direction
+
Subdivision multiplier for the meter:
@@ -1014,6 +1017,9 @@
No suitable objects to setting property IsCritical
+
+ No flicks to switch in selection
+
The curve lane has not been interpolated yet
@@ -1566,4 +1572,7 @@
Mirror lane colors
+
+ (No Limit)
+
\ No newline at end of file
diff --git a/OngekiFumenEditor/Properties/Resources.zh-Hans.resx b/OngekiFumenEditor/Properties/Resources.zh-Hans.resx
index d6fccf76..5a674199 100644
--- a/OngekiFumenEditor/Properties/Resources.zh-Hans.resx
+++ b/OngekiFumenEditor/Properties/Resources.zh-Hans.resx
@@ -216,6 +216,9 @@
快速批量设置IsCritical
+
+ 批量改变Flick物件方向
+
节奏线细分倍率:
@@ -1005,6 +1008,9 @@
无合适物件批量设置IsCritical属性
+
+ 请先选择Flick物件再更改方向
+
轨道物件曲线还没被插值
@@ -1548,4 +1554,7 @@
是否重置本应用所有配置选项?
+
+ (无限制)
+
\ No newline at end of file