diff --git a/OpenUtau.Core/DocManager.cs b/OpenUtau.Core/DocManager.cs index 0ef989a35..8a309a28e 100644 --- a/OpenUtau.Core/DocManager.cs +++ b/OpenUtau.Core/DocManager.cs @@ -41,14 +41,14 @@ public class DocManager : SingletonBase { public List NotesClipboard { get; set; } internal PhonemizerRunner PhonemizerRunner { get; private set; } - public void Initialize() { + public void Initialize(Thread mainThread, TaskScheduler mainScheduler) { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler((sender, args) => { CrashSave(); }); SearchAllPlugins(); SearchAllLegacyPlugins(); - mainThread = Thread.CurrentThread; - mainScheduler = TaskScheduler.FromCurrentSynchronizationContext(); + this.mainThread = mainThread; + this.mainScheduler = mainScheduler; PhonemizerRunner = new PhonemizerRunner(mainScheduler); } diff --git a/OpenUtau.Core/SingerManager.cs b/OpenUtau.Core/SingerManager.cs index a25455585..829155144 100644 --- a/OpenUtau.Core/SingerManager.cs +++ b/OpenUtau.Core/SingerManager.cs @@ -15,44 +15,31 @@ namespace OpenUtau.Core { public class SingerManager : SingletonBase { public Dictionary Singers { get; private set; } = new Dictionary(); public Dictionary> SingerGroups { get; private set; } = new Dictionary>(); - public Task? InitializationTask = null; private readonly ConcurrentQueue reloadQueue = new ConcurrentQueue(); private CancellationTokenSource reloadCancellation; - + private HashSet singersUsed = new HashSet(); public void Initialize() { - InitializationTask = Task.Run(() => { - SearchAllSingers(); - }); + SearchAllSingers(); } public void SearchAllSingers() { - try { - Log.Information("Searching singers."); - Directory.CreateDirectory(PathManager.Inst.SingersPath); - var stopWatch = Stopwatch.StartNew(); - var singers = ClassicSingerLoader.FindAllSingers() - .Concat(Vogen.VogenSingerLoader.FindAllSingers()) - .Distinct(); - Singers = singers - .ToLookup(s => s.Id) - .ToDictionary(g => g.Key, g => g.First()); - SingerGroups = singers - .GroupBy(s => s.SingerType) - .ToDictionary(s => s.Key, s => s.LocalizedOrderBy(singer => singer.LocalizedName).ToList()); - stopWatch.Stop(); - Log.Information($"Search all singers: {stopWatch.Elapsed}"); - } catch (Exception e) { - if (InitializationTask.Status == TaskStatus.Running) { - Log.Error(e, "Failed to search singers."); - } else { - var customEx = new MessageCustomizableException("Failed to search singers.", "", e); - DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(customEx)); - } - Singers = new Dictionary(); - } + Log.Information("Searching singers."); + Directory.CreateDirectory(PathManager.Inst.SingersPath); + var stopWatch = Stopwatch.StartNew(); + var singers = ClassicSingerLoader.FindAllSingers() + .Concat(Vogen.VogenSingerLoader.FindAllSingers()) + .Distinct(); + Singers = singers + .ToLookup(s => s.Id) + .ToDictionary(g => g.Key, g => g.First()); + SingerGroups = singers + .GroupBy(s => s.SingerType) + .ToDictionary(s => s.Key, s => s.LocalizedOrderBy(singer => singer.LocalizedName).ToList()); + stopWatch.Stop(); + Log.Information($"Search all singers: {stopWatch.Elapsed}"); } public USinger GetSinger(string name) { @@ -122,15 +109,15 @@ private void Refresh() { public void ReleaseSingersNotInUse(UProject project) { //Check which singers are in use var singersInUse = new HashSet(); - foreach(var track in project.tracks){ + foreach (var track in project.tracks) { var singer = track.Singer; - if(singer != null && singer.Found && !singersInUse.Contains(singer)) { + if (singer != null && singer.Found && !singersInUse.Contains(singer)) { singersInUse.Add(singer); } } //Release singers that are no longer in use - foreach(var singer in singersUsed){ - if(!singersInUse.Contains(singer)){ + foreach (var singer in singersUsed) { + if (!singersInUse.Contains(singer)) { singer.FreeMemory(); } } diff --git a/OpenUtau/App.axaml.cs b/OpenUtau/App.axaml.cs index 2d0c89141..280d4558f 100644 --- a/OpenUtau/App.axaml.cs +++ b/OpenUtau/App.axaml.cs @@ -23,15 +23,13 @@ public override void Initialize() { AvaloniaXamlLoader.Load(this); InitializeCulture(); InitializeTheme(); - InitOpenUtau(); - InitAudio(); Log.Information("Initialized application."); } public override void OnFrameworkInitializationCompleted() { Log.Information("Framework initialization completed."); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow(); + desktop.MainWindow = new SplashWindow(); } base.OnFrameworkInitializationCompleted(); @@ -112,32 +110,5 @@ public static void SetTheme() { } ThemeManager.LoadTheme(); } - - public static void InitOpenUtau() { - Log.Information("Initializing OpenUtau."); - ToolsManager.Inst.Initialize(); - SingerManager.Inst.Initialize(); - DocManager.Inst.Initialize(); - DocManager.Inst.PostOnUIThread = action => Avalonia.Threading.Dispatcher.UIThread.Post(action); - Log.Information("Initialized OpenUtau."); - } - - public static void InitAudio() { - Log.Information("Initializing audio."); - if (!OS.IsWindows() || Core.Util.Preferences.Default.PreferPortAudio) { - try { - PlaybackManager.Inst.AudioOutput = new Audio.MiniAudioOutput(); - } catch (Exception e1) { - Log.Error(e1, "Failed to init MiniAudio"); - } - } else { - try { - PlaybackManager.Inst.AudioOutput = new Audio.NAudioOutput(); - } catch (Exception e2) { - Log.Error(e2, "Failed to init NAudio"); - } - } - Log.Information("Initialized audio."); - } } } diff --git a/OpenUtau/ViewModels/MainWindowViewModel.cs b/OpenUtau/ViewModels/MainWindowViewModel.cs index 0115350fb..b7b177ace 100644 --- a/OpenUtau/ViewModels/MainWindowViewModel.cs +++ b/OpenUtau/ViewModels/MainWindowViewModel.cs @@ -90,10 +90,6 @@ public void Redo() { DocManager.Inst.Redo(); } - public Task? GetInitSingerTask() { - return SingerManager.Inst.InitializationTask; - } - public void InitProject() { var args = Environment.GetCommandLineArgs(); if (args.Length == 2 && File.Exists(args[1])) { diff --git a/OpenUtau/Views/MainWindow.axaml b/OpenUtau/Views/MainWindow.axaml index b5e38c1c7..12d48bc8c 100644 --- a/OpenUtau/Views/MainWindow.axaml +++ b/OpenUtau/Views/MainWindow.axaml @@ -18,7 +18,7 @@ - + @@ -269,8 +269,5 @@ - - - diff --git a/OpenUtau/Views/MainWindow.axaml.cs b/OpenUtau/Views/MainWindow.axaml.cs index 7180f6f07..68eaea8f7 100644 --- a/OpenUtau/Views/MainWindow.axaml.cs +++ b/OpenUtau/Views/MainWindow.axaml.cs @@ -32,8 +32,6 @@ public partial class MainWindow : Window, ICmdSubscriber { OS.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; private readonly MainWindowViewModel viewModel; - private bool splashDone = false; - private PianoRollWindow? pianoRollWindow; private bool openPianoRollWindow; @@ -54,20 +52,12 @@ public MainWindow() { InitializeComponent(); Log.Information("Initialized main window component."); DataContext = viewModel = new MainWindowViewModel(); - var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); - viewModel.GetInitSingerTask()!.ContinueWith(_ => { - viewModel.InitProject(); - viewModel.AddTempoChangeCmd = ReactiveCommand.Create(tick => AddTempoChange(tick)); - viewModel.DelTempoChangeCmd = ReactiveCommand.Create(tick => DelTempoChange(tick)); - viewModel.AddTimeSigChangeCmd = ReactiveCommand.Create(bar => AddTimeSigChange(bar)); - viewModel.DelTimeSigChangeCmd = ReactiveCommand.Create(bar => DelTimeSigChange(bar)); - - Splash.IsEnabled = false; - Splash.IsVisible = false; - MainGrid.IsEnabled = true; - MainGrid.IsVisible = true; - splashDone = true; - }, CancellationToken.None, TaskContinuationOptions.None, scheduler); + + viewModel.InitProject(); + viewModel.AddTempoChangeCmd = ReactiveCommand.Create(tick => AddTempoChange(tick)); + viewModel.DelTempoChangeCmd = ReactiveCommand.Create(tick => DelTempoChange(tick)); + viewModel.AddTimeSigChangeCmd = ReactiveCommand.Create(bar => AddTimeSigChange(bar)); + viewModel.DelTimeSigChangeCmd = ReactiveCommand.Create(bar => DelTimeSigChange(bar)); timer = new DispatcherTimer( TimeSpan.FromMilliseconds(15), @@ -149,9 +139,9 @@ private void DelTempoChange(int tick) { DocManager.Inst.EndUndoGroup(); } - - - void OnMenuRemapTimeaxis(object sender, RoutedEventArgs e){ + + + void OnMenuRemapTimeaxis(object sender, RoutedEventArgs e) { var project = DocManager.Inst.Project; var dialog = new TypeInDialog { Title = ThemeManager.GetString("menu.project.remaptimeaxis") @@ -160,13 +150,13 @@ void OnMenuRemapTimeaxis(object sender, RoutedEventArgs e){ dialog.SetPrompt(ThemeManager.GetString("dialogs.remaptimeaxis.message")); dialog.SetText(project.tempos[0].bpm.ToString()); dialog.onFinish = s => { - try{ + try { if (double.TryParse(s, out double bpm)) { DocManager.Inst.StartUndoGroup(); var oldTimeAxis = project.timeAxis.Clone(); DocManager.Inst.ExecuteCmd(new BpmCommand( project, bpm)); - foreach(var tempo in project.tempos.Skip(1)){ + foreach (var tempo in project.tempos.Skip(1)) { DocManager.Inst.ExecuteCmd(new DelTempoChangeCommand( project, tempo.position)); } @@ -321,20 +311,20 @@ async void OnMenuImportTracks(object sender, RoutedEventArgs args) { } try { var loadedProjects = Formats.ReadProjects(files); - if(loadedProjects == null || loadedProjects.Length == 0){ + if (loadedProjects == null || loadedProjects.Length == 0) { return; - } + } bool importTempo = true; - switch(Preferences.Default.ImportTempo){ + switch (Preferences.Default.ImportTempo) { case 1: importTempo = false; break; case 2: - if(loadedProjects[0].tempos.Count == 0){ + if (loadedProjects[0].tempos.Count == 0) { importTempo = false; break; } - var tempoString = String.Join("\n", + var tempoString = String.Join("\n", loadedProjects[0].tempos .Select(tempo => $"position: {tempo.position}, tempo: {tempo.bpm}") ); @@ -344,7 +334,7 @@ async void OnMenuImportTracks(object sender, RoutedEventArgs args) { ThemeManager.GetString("dialogs.importtracks.importtempo") + "\n" + tempoString, ThemeManager.GetString("dialogs.importtracks.caption"), MessageBox.MessageBoxButtons.YesNo); - if(result == MessageBox.MessageBoxResult.No){ + if (result == MessageBox.MessageBoxResult.No) { importTempo = false; } break; @@ -421,7 +411,7 @@ async void OnMenuExportDsTo(object sender, RoutedEventArgs e) { for (var i = 0; i < project.parts.Count; i++) { var part = project.parts[i]; if (part is UVoicePart voicePart) { - var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4]+".ds"; + var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4] + ".ds"; DiffSingerScript.SavePart(project, voicePart, savePath); DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, $"{savePath}.")); } @@ -437,7 +427,7 @@ async void OnMenuExportDsV2To(object sender, RoutedEventArgs e) { for (var i = 0; i < project.parts.Count; i++) { var part = project.parts[i]; if (part is UVoicePart voicePart) { - var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4]+".ds"; + var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4] + ".ds"; DiffSingerScript.SavePart(project, voicePart, savePath, true); DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, $"{savePath}.")); } @@ -453,7 +443,7 @@ async void OnMenuExportDsV2WithoutPitchTo(object sender, RoutedEventArgs e) { for (var i = 0; i < project.parts.Count; i++) { var part = project.parts[i]; if (part is UVoicePart voicePart) { - var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4]+".ds"; + var savePath = PathManager.Inst.GetPartSavePath(file, voicePart.DisplayName, i)[..^4] + ".ds"; DiffSingerScript.SavePart(project, voicePart, savePath, true, false); DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, $"{savePath}.")); } @@ -537,8 +527,8 @@ void OnMenuSingers(object sender, RoutedEventArgs args) { /// If the user haven't selected a singer for the track, or the singer specified in ustx project doesn't exist, return null. /// Otherwise, return the singer. /// - public USinger? TrackSingerIfFound(UTrack track){ - if(track.Singer?.Found ?? false){ + public USinger? TrackSingerIfFound(UTrack track) { + if (track.Singer?.Found ?? false) { return track.Singer; } return null; @@ -609,7 +599,7 @@ async void OnMenuInstallSinger(object sender, RoutedEventArgs args) { } catch (Exception e) { Log.Error(e, $"Failed to install singer {file}"); MessageCustomizableException mce; - if(e is MessageCustomizableException){ + if (e is MessageCustomizableException) { mce = (MessageCustomizableException)e; } else { mce = new MessageCustomizableException($"Failed to install singer {file}", $": {file}", e); @@ -618,7 +608,7 @@ async void OnMenuInstallSinger(object sender, RoutedEventArgs args) { } } - async void OnMenuInstallDependency(object sender, RoutedEventArgs args){ + async void OnMenuInstallDependency(object sender, RoutedEventArgs args) { var file = await FilePicker.OpenFile( this, "menu.tools.dependency.install", FilePicker.OUDEP); if (file == null) { @@ -734,18 +724,15 @@ private void LayoutSplit(double? x, double? y) { Width = x != null ? wa.Size.Width * x.Value : wa.Size.Width; Height = (y != null ? wa.Size.Height * y.Value : wa.Size.Height) - titleBarHeight; if (pianoRollWindow != null) { - pianoRollWindow.Position = new PixelPoint(x != null ? (int) Width : 0, y != null ? (int) (Height + (OS.IsMacOS() ? 25 : titleBarHeight)) : 0); + pianoRollWindow.Position = new PixelPoint(x != null ? (int)Width : 0, y != null ? (int)(Height + (OS.IsMacOS() ? 25 : titleBarHeight)) : 0); pianoRollWindow.Width = x != null ? wa.Size.Width - Width : wa.Size.Width; pianoRollWindow.Height = (y != null ? wa.Size.Height - (Height + titleBarHeight) : wa.Size.Height) - titleBarHeight; } } void OnKeyDown(object sender, KeyEventArgs args) { - if (!splashDone) { - return; - } var tracksVm = viewModel.TracksViewModel; - if (args.KeyModifiers == KeyModifiers.None){ + if (args.KeyModifiers == KeyModifiers.None) { args.Handled = true; switch (args.Key) { case Key.Delete: viewModel.TracksViewModel.DeleteSelectedParts(); break; @@ -834,7 +821,7 @@ async void OnDrop(object? sender, DragEventArgs args) { } string file = storageItem.Path.LocalPath; var ext = Path.GetExtension(file); - if (ext == ".ustx" || ext == ".ust" || ext == ".vsqx" || ext==".ufdata") { + if (ext == ".ustx" || ext == ".ust" || ext == ".vsqx" || ext == ".ufdata") { if (!DocManager.Inst.ChangesSaved && !await AskIfSaveAndContinue()) { return; } @@ -852,7 +839,7 @@ async void OnDrop(object? sender, DragEventArgs args) { _ = await MessageBox.ShowError(this, new MessageCustomizableException("Failed to import midi", "", e)); } } else if (ext == ".zip" || ext == ".rar" || ext == ".uar") { - try{ + try { var setup = new SingerSetupDialog() { DataContext = new SingerSetupViewModel() { ArchiveFilePath = file, @@ -865,7 +852,7 @@ async void OnDrop(object? sender, DragEventArgs args) { } catch (Exception e) { Log.Error(e, $"Failed to install singer {file}"); MessageCustomizableException mce; - if(e is MessageCustomizableException){ + if (e is MessageCustomizableException) { mce = (MessageCustomizableException)e; } else { mce = new MessageCustomizableException($"Failed to install singer {file}", $": {file}", e); @@ -936,7 +923,7 @@ public void VScrollPointerWheelChanged(object sender, PointerWheelEventArgs args public void TimelinePointerWheelChanged(object sender, PointerWheelEventArgs args) { var control = (Control)sender; - var position = args.GetCurrentPoint((Visual) sender).Position; + var position = args.GetCurrentPoint((Visual)sender).Position; var size = control.Bounds.Size; position = position.WithX(position.X / size.Width).WithY(position.Y / size.Height); viewModel.TracksViewModel.OnXZoomed(position, 0.1 * args.Delta.Y); @@ -1039,7 +1026,7 @@ public void PartsCanvasPointerPressed(object sender, PointerPressedEventArgs arg } public void PartsCanvasPointerMoved(object sender, PointerEventArgs args) { - var control = (Control) sender; + var control = (Control)sender; var point = args.GetCurrentPoint(control); if (partEditState != null) { partEditState.Update(point.Pointer, point.Position); @@ -1068,7 +1055,7 @@ public void PartsCanvasPointerReleased(object sender, PointerReleasedEventArgs a if (partEditState.MouseButton != args.InitialPressMouseButton) { return; } - var control = (Control) sender; + var control = (Control)sender; var point = args.GetCurrentPoint(control); partEditState.Update(point.Pointer, point.Position); partEditState.End(point.Pointer, point.Position); @@ -1187,20 +1174,20 @@ async void ReplaceAudio(UPart part) { DocManager.Inst.EndUndoGroup(); } - void Transcribe(UPart part){ + void Transcribe(UPart part) { //Convert audio to notes - if(part is UWavePart wavePart){ - try{ + if (part is UWavePart wavePart) { + try { string text = ThemeManager.GetString("context.part.transcribing"); var msgbox = MessageBox.ShowModal(this, $"{text} {part.name}", text); //Duration of the wave file in seconds int wavDurS = (int)(wavePart.fileDurationMs / 1000.0); var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); - var transcribeTask = Task.Run(()=>{ - using(var some = new Some()){ - return some.Transcribe(DocManager.Inst.Project, wavePart, wavPosS =>{ + var transcribeTask = Task.Run(() => { + using (var some = new Some()) { + return some.Transcribe(DocManager.Inst.Project, wavePart, wavPosS => { //msgbox?.SetText($"{text} {part.name}\n{wavPosS}/{wavDurS}"); - msgbox.SetText(string.Format("{0} {1}\n{2}s / {3}s",text, part.name, wavPosS, wavDurS)); + msgbox.SetText(string.Format("{0} {1}\n{2}s / {3}s", text, part.name, wavPosS, wavDurS)); }); } }); @@ -1213,7 +1200,7 @@ void Transcribe(UPart part){ } var voicePart = task.Result; //Add voicePart into project - if(voicePart != null){ + if (voicePart != null) { var project = DocManager.Inst.Project; var track = new UTrack(project); track.TrackNo = project.tracks.Count; @@ -1350,7 +1337,7 @@ public void OnNext(UCommand cmd, bool isUndo) { MessageBox.CloseLoading(); } } else if (cmd is VoiceColorRemappingNotification voicecolorNotif) { - if(voicecolorNotif.TrackNo < 0 || DocManager.Inst.Project.tracks.Count <= voicecolorNotif.TrackNo) { + if (voicecolorNotif.TrackNo < 0 || DocManager.Inst.Project.tracks.Count <= voicecolorNotif.TrackNo) { ValidateTracksVoiceColor(); } else { UTrack track = DocManager.Inst.Project.tracks[voicecolorNotif.TrackNo]; diff --git a/OpenUtau/Views/SplashWindow.axaml b/OpenUtau/Views/SplashWindow.axaml new file mode 100644 index 000000000..76adbdda5 --- /dev/null +++ b/OpenUtau/Views/SplashWindow.axaml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/OpenUtau/Views/SplashWindow.axaml.cs b/OpenUtau/Views/SplashWindow.axaml.cs new file mode 100644 index 000000000..edc90f2f4 --- /dev/null +++ b/OpenUtau/Views/SplashWindow.axaml.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using OpenUtau.Classic; +using OpenUtau.Core; +using Serilog; + +namespace OpenUtau.App.Views { + public partial class SplashWindow : Window { + public SplashWindow() { + InitializeComponent(); + if (ThemeManager.IsDarkMode) { + LogoTypeLight.IsVisible = false; + LogoTypeDark.IsVisible = true; + } else { + LogoTypeLight.IsVisible = true; + LogoTypeDark.IsVisible = false; + } + this.Opened += SplashWindow_Opened; + } + + private void SplashWindow_Opened(object? sender, System.EventArgs e) { + if (Screens.Primary == null) { + return; + } + var wa = Screens.Primary.WorkingArea; + int x = wa.Size.Width / 2 - (int)Width / 2; + int y = wa.Size.Height / 2 - (int)Height / 2; + Position = new Avalonia.PixelPoint(x, y); + + Start(); + } + + private void Start() { + var mainThread = Thread.CurrentThread; + var mainScheduler = TaskScheduler.FromCurrentSynchronizationContext(); + Task.Run(() => { + Log.Information("Initializing OpenUtau."); + ToolsManager.Inst.Initialize(); + SingerManager.Inst.Initialize(); + DocManager.Inst.Initialize(mainThread, mainScheduler); + DocManager.Inst.PostOnUIThread = action => Avalonia.Threading.Dispatcher.UIThread.Post(action); + Log.Information("Initialized OpenUtau."); + InitAudio(); + }).ContinueWith(t => { + if (t.IsFaulted) { + Log.Error(t.Exception?.Flatten(), "Failed to Start."); + MessageBox.ShowError(this, t.Exception, "Failed to Start OpenUtau").ContinueWith(t1 => { Close(); }); + return; + } + if (App.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + var mainWindow = new MainWindow(); + mainWindow.Show(); + desktop.MainWindow = mainWindow; + Close(); + } + }, CancellationToken.None, TaskContinuationOptions.None, mainScheduler); + } + + private static void InitAudio() { + Log.Information("Initializing audio."); + if (!OS.IsWindows() || Core.Util.Preferences.Default.PreferPortAudio) { + try { + PlaybackManager.Inst.AudioOutput = new Audio.MiniAudioOutput(); + } catch (Exception e1) { + Log.Error(e1, "Failed to init MiniAudio"); + } + } else { + try { + PlaybackManager.Inst.AudioOutput = new Audio.NAudioOutput(); + } catch (Exception e2) { + Log.Error(e2, "Failed to init NAudio"); + } + } + Log.Information("Initialized audio."); + } + } +}