diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2c6db57..d0f70ca 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,7 +12,7 @@ jobs: - name: Set version number shell: pwsh run: | - $version = "0.0.8" + $version = "0.1.0" echo $version echo "VERSION_NUMBER=$version" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append - uses: actions/checkout@v4 diff --git a/KaddaOK.AvaloniaApp/App.axaml.cs b/KaddaOK.AvaloniaApp/App.axaml.cs index 105fef9..4aae9a0 100644 --- a/KaddaOK.AvaloniaApp/App.axaml.cs +++ b/KaddaOK.AvaloniaApp/App.axaml.cs @@ -43,6 +43,7 @@ public override void Initialize() services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/KaddaOK.AvaloniaApp/Models/KaraokeProcess.cs b/KaddaOK.AvaloniaApp/Models/KaraokeProcess.cs index 6a09321..f7d6dac 100644 --- a/KaddaOK.AvaloniaApp/Models/KaraokeProcess.cs +++ b/KaddaOK.AvaloniaApp/Models/KaraokeProcess.cs @@ -13,7 +13,8 @@ public enum InitialKaraokeSource { AzureSpeechService, RzlrcImport, - KbpImport + KbpImport, + CtmImport } public partial class KaraokeProcess : ObservableBase @@ -360,7 +361,8 @@ public string? ReasonEditTabIsDisabled get { if (KaraokeSource == InitialKaraokeSource.RzlrcImport - || KaraokeSource == InitialKaraokeSource.KbpImport) + || KaraokeSource == InitialKaraokeSource.KbpImport + || KaraokeSource == InitialKaraokeSource.CtmImport) { return null; } diff --git a/KaddaOK.AvaloniaApp/ViewModels/DesignTime/DesignTimeRecognizeViewModel.cs b/KaddaOK.AvaloniaApp/ViewModels/DesignTime/DesignTimeRecognizeViewModel.cs index f1e7386..6d21b4b 100644 --- a/KaddaOK.AvaloniaApp/ViewModels/DesignTime/DesignTimeRecognizeViewModel.cs +++ b/KaddaOK.AvaloniaApp/ViewModels/DesignTime/DesignTimeRecognizeViewModel.cs @@ -31,7 +31,7 @@ public Task CancelRecognition(Action reportProgress) } public class DesignTimeRecognizeViewModel : RecognizeViewModel { - public DesignTimeRecognizeViewModel() : base(new DummyAzureRecognizer(), new KaddaOKSettingsPersistor(), DesignTimeKaraokeProcess.Get()) + public DesignTimeRecognizeViewModel() : base(new DummyAzureRecognizer(), new KaddaOKSettingsPersistor(), new NfaCtmImporter(), DesignTimeKaraokeProcess.Get()) { HasEverBeenStarted = true; LogContents = new ObservableCollection(new[] diff --git a/KaddaOK.AvaloniaApp/ViewModels/RecognizeViewModel.cs b/KaddaOK.AvaloniaApp/ViewModels/RecognizeViewModel.cs index 76b05a5..64d9a7f 100644 --- a/KaddaOK.AvaloniaApp/ViewModels/RecognizeViewModel.cs +++ b/KaddaOK.AvaloniaApp/ViewModels/RecognizeViewModel.cs @@ -1,12 +1,16 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Threading; using CommunityToolkit.Mvvm.Input; using KaddaOK.Library; using System.Windows.Input; +using Avalonia.Controls.Notifications; +using Avalonia.Platform.Storage; using KaddaOK.AvaloniaApp.Models; using KaddaOK.AvaloniaApp.Services; @@ -35,13 +39,23 @@ public KaddaOKSettings RecognizeSettings set => SetProperty(ref recognizeSettings, value); } + private bool _gettingFile; + public bool GettingFile + { + get => _gettingFile; + set => SetProperty(ref _gettingFile, value); + } + public IAzureRecognizer AzureRecognizer { get; } private IKaddaOKSettingsPersistor SettingsPersistor { get; } - public RecognizeViewModel(IAzureRecognizer recognizer, IKaddaOKSettingsPersistor settingsPersistor, KaraokeProcess karaokeProcess) : base(karaokeProcess) + private INfaCtmImporter NfaCtmImporter { get; } + public WindowNotificationManager? NotificationManager { get; set; } + public RecognizeViewModel(IAzureRecognizer recognizer, IKaddaOKSettingsPersistor settingsPersistor, INfaCtmImporter cfmImporter, KaraokeProcess karaokeProcess) : base(karaokeProcess) { LogContents = new ObservableCollection(); AzureRecognizer = recognizer; SettingsPersistor = settingsPersistor; + NfaCtmImporter = cfmImporter; FullLengthVocalsDraw = new WaveformDraw { DesiredPeakHeight = 200 @@ -143,159 +157,77 @@ public void GoToNextStep(object? parameter) CurrentProcess!.SelectedTabIndex = 3; } + [RelayCommand] + public async Task ImportCtm() + { + if (App.MainWindow == null) + { + throw new InvalidOperationException( + "Couldn't find the reference to MainWindow in order to show a dialog"); + } + + var options = new FilePickerOpenOptions + { + AllowMultiple = false, + Title = "Select a .CTM file to import", + FileTypeFilter = new FilePickerFileType[] { new ("NeMo Forced Aligner tokens .ctm file") + { + Patterns = new[] { "*.ctm" } + } } + }; + + try + { + Dispatcher.UIThread.Invoke(() => { GettingFile = true; }); + var results = await App.MainWindow.StorageProvider.OpenFilePickerAsync(options); + var result = results?.FirstOrDefault(); + if (result != null) + { + + var ctmFilePath = result.TryGetLocalPath(); + + if (!string.IsNullOrWhiteSpace(ctmFilePath)) + { + var ctmLines = File.ReadAllLines(ctmFilePath).ToList(); + + var lyricLines = + NfaCtmImporter.ImportNfaCtmAndLyrics(ctmLines, CurrentProcess.KnownOriginalLyrics?.Lyrics); + + CurrentProcess.ChosenLines = + new ObservableCollection(lyricLines); + + + CurrentProcess.KaraokeSource = InitialKaraokeSource.CtmImport; + CurrentProcess.RaiseChosenLinesChanged(); + CurrentProcess.NarrowingStepCompletenessChanged(); + CurrentProcess.CanExportFactorsChanged(); + CurrentProcess!.SelectedTabIndex = 4; + } + } + GettingFile = false; + } + catch (Exception e) + { + // The user canceled or something went wrong + if (NotificationManager != null) + { + NotificationManager.Position = NotificationPosition.BottomRight; + NotificationManager.Show(new Notification("Error", $"An error occurred: {e.Message}", NotificationType.Error, TimeSpan.Zero)); + } + GettingFile = false; + } + } + [RelayCommand] private void LinkToAzureSpeechService() { UrlOpener.OpenUrl("https://azure.microsoft.com/en-us/products/ai-services/speech-to-text"); } - public List SupportedLanguages = - //public List<(string bcp47, string displayName)> SupportedLanguages = - new() - { - /*("af-ZA", "Afrikaans (South Africa)"), - ("am-ET", "Amharic (Ethiopia)"), - ("ar-AE", "Arabic (United Arab Emirates)"), - ("ar-BH", "Arabic (Bahrain)"), - ("ar-DZ", "Arabic (Algeria)"), - ("ar-EG", "Arabic (Egypt)"), - ("ar-IL", "Arabic (Israel)"), - ("ar-IQ", "Arabic (Iraq)"), - ("ar-JO", "Arabic (Jordan)"), - ("ar-KW", "Arabic (Kuwait)"), - ("ar-LB", "Arabic (Lebanon)"), - ("ar-LY", "Arabic (Libya)"), - ("ar-MA", "Arabic (Morocco)"), - ("ar-OM", "Arabic (Oman)"), - ("ar-PS", "Arabic (Palestinian Authority)"), - ("ar-QA", "Arabic (Qatar)"), - ("ar-SA", "Arabic (Saudi Arabia)"), - ("ar-SY", "Arabic (Syria)"), - ("ar-TN", "Arabic (Tunisia)"), - ("ar-YE", "Arabic (Yemen)"), - ("az-AZ", "Azerbaijani (Latin, Azerbaijan)"), - ("bg-BG", "Bulgarian (Bulgaria)"), - ("bn-IN", "Bengali (India)"), - ("bs-BA", "Bosnian (Bosnia and Herzegovina)"), - ("ca-ES", "Catalan"), - ("cs-CZ", "Czech (Czechia)"), - ("cy-GB", "Welsh (United Kingdom)"), - ("da-DK", "Danish (Denmark)"), - ("de-AT", "German (Austria)"), - ("de-CH", "German (Switzerland)"), - ("de-DE", "German (Germany)"), - ("el-GR", "Greek (Greece)"), - ("en-AU", "English (Australia)"), - ("en-CA", "English (Canada)"), - ("en-GB", "English (United Kingdom)"), - ("en-GH", "English (Ghana)"), - ("en-HK", "English (Hong Kong SAR)"), - ("en-IE", "English (Ireland)"), - ("en-IN", "English (India)"), - ("en-KE", "English (Kenya)"), - ("en-NG", "English (Nigeria)"), - ("en-NZ", "English (New Zealand)"), - ("en-PH", "English (Philippines)"), - ("en-SG", "English (Singapore)"), - ("en-TZ", "English (Tanzania)"), - ("en-US", "English (United States)"), - ("en-ZA", "English (South Africa)"), - ("es-AR", "Spanish (Argentina)"), - ("es-BO", "Spanish (Bolivia)"), - ("es-CL", "Spanish (Chile)"), - ("es-CO", "Spanish (Colombia)"), - ("es-CR", "Spanish (Costa Rica)"), - ("es-CU", "Spanish (Cuba)"), - ("es-DO", "Spanish (Dominican Republic)"), - ("es-EC", "Spanish (Ecuador)"), - ("es-ES", "Spanish (Spain)"), - ("es-GQ", "Spanish (Equatorial Guinea)"), - ("es-GT", "Spanish (Guatemala)"), - ("es-HN", "Spanish (Honduras)"), - ("es-MX", "Spanish (Mexico)"), - ("es-NI", "Spanish (Nicaragua)"), - ("es-PA", "Spanish (Panama)"), - ("es-PE", "Spanish (Peru)"), - ("es-PR", "Spanish (Puerto Rico)"), - ("es-PY", "Spanish (Paraguay)"), - ("es-SV", "Spanish (El Salvador)"), - ("es-US", "Spanish (United States)"), - ("es-UY", "Spanish (Uruguay)"), - ("es-VE", "Spanish (Venezuela)"), - ("et-EE", "Estonian (Estonia)"), - ("eu-ES", "Basque"), - ("fa-IR", "Persian (Iran)"), - ("fi-FI", "Finnish (Finland)"), - ("fil-PH", "Filipino (Philippines)"), - ("fr-BE", "French (Belgium)"), - ("fr-CA", "French (Canada)"), - ("fr-CH", "French (Switzerland)"), - ("fr-FR", "French (France)"), - ("ga-IE", "Irish (Ireland)"), - ("gl-ES", "Galician"), - ("gu-IN", "Gujarati (India)"), - ("he-IL", "Hebrew (Israel)"), - ("hi-IN", "Hindi (India)"), - ("hr-HR", "Croatian (Croatia)"), - ("hu-HU", "Hungarian (Hungary)"), - ("hy-AM", "Armenian (Armenia)"), - ("id-ID", "Indonesian (Indonesia)"), - ("is-IS", "Icelandic (Iceland)"), - ("it-CH", "Italian (Switzerland)"), - ("it-IT", "Italian (Italy)"), - ("ja-JP", "Japanese (Japan)"), - ("jv-ID", "Javanese (Latin, Indonesia)"), - ("ka-GE", "Georgian (Georgia)"), - ("kk-KZ", "Kazakh (Kazakhstan)"), - ("km-KH", "Khmer (Cambodia)"), - ("kn-IN", "Kannada (India)"), - ("ko-KR", "Korean (Korea)"), - ("lo-LA", "Lao (Laos)"), - ("lt-LT", "Lithuanian (Lithuania)"), - ("lv-LV", "Latvian (Latvia)"), - ("mk-MK", "Macedonian (North Macedonia)"), - ("ml-IN", "Malayalam (India)"), - ("mn-MN", "Mongolian (Mongolia)"), - ("mr-IN", "Marathi (India)"), - ("ms-MY", "Malay (Malaysia)"), - ("mt-MT", "Maltese (Malta)"), - ("my-MM", "Burmese (Myanmar)"), - ("nb-NO", "Norwegian Bokmål (Norway)"), - ("ne-NP", "Nepali (Nepal)"), - ("nl-BE", "Dutch (Belgium)"), - ("nl-NL", "Dutch (Netherlands)"), - ("pa-IN", "Punjabi (India)"), - ("pl-PL", "Polish (Poland)"), - ("ps-AF", "Pashto (Afghanistan)"), - ("pt-BR", "Portuguese (Brazil)"), - ("pt-PT", "Portuguese (Portugal)"), - ("ro-RO", "Romanian (Romania)"), - ("ru-RU", "Russian (Russia)"), - ("si-LK", "Sinhala (Sri Lanka)"), - ("sk-SK", "Slovak (Slovakia)"), - ("sl-SI", "Slovenian (Slovenia)"), - ("so-SO", "Somali (Somalia)"), - ("sq-AL", "Albanian (Albania)"), - ("sr-RS", "Serbian (Cyrillic, Serbia)"), - ("sv-SE", "Swedish (Sweden)"), - ("sw-KE", "Swahili (Kenya)"), - ("sw-TZ", "Swahili (Tanzania)"), - ("ta-IN", "Tamil (India)"), - ("te-IN", "Telugu (India)"), - ("th-TH", "Thai (Thailand)"), - ("tr-TR", "Turkish (Türkiye)"), - ("uk-UA", "Ukrainian (Ukraine)"), - ("ur-IN", "Urdu (India)"), - ("uz-UZ", "Uzbek (Latin, Uzbekistan)"), - ("vi-VN", "Vietnamese (Vietnam)"), - ("wuu-CN", "Chinese (Wu, Simplified)"), - ("yue-CN", "Chinese (Cantonese, Simplified)"), - ("zh-CN", "Chinese (Mandarin, Simplified)"), - ("zh-CN-shandong", "Chinese (Jilu Mandarin, Simplified)"), - ("zh-CN-sichuan", "Chinese (Southwestern Mandarin, Simplified)"), - ("zh-HK", "Chinese (Cantonese, Traditional)"), - ("zh-TW", "Chinese (Taiwanese Mandarin, Traditional)"), - ("zu-ZA", "Zulu (South Africa)"),*/ - }; + [RelayCommand] + private void LinkToForcedAligner() + { + UrlOpener.OpenUrl("https://github.com/KaddaOK/Forced-Aligner-for-Karaoke"); + } } } diff --git a/KaddaOK.AvaloniaApp/Views/MainWindow.axaml b/KaddaOK.AvaloniaApp/Views/MainWindow.axaml index fd55bb5..72a4af0 100644 --- a/KaddaOK.AvaloniaApp/Views/MainWindow.axaml +++ b/KaddaOK.AvaloniaApp/Views/MainWindow.axaml @@ -7,7 +7,7 @@ xmlns:views="clr-namespace:KaddaOK.AvaloniaApp.Views" mc:Ignorable="d" d:DesignWidth="1000" - d:DesignHeight="800" + d:DesignHeight="860" x:Class="KaddaOK.AvaloniaApp.Views.MainWindow" Icon="/Assets/KaddaOKIcon.png" Title="KaddaOK Tools" @@ -16,7 +16,7 @@ Position="0,0" Width="1000" MinWidth="800" - Height="800" + Height="860" WindowState="Normal" WindowStartupLocation="CenterScreen" x:Name="Main" diff --git a/KaddaOK.AvaloniaApp/Views/RecognizeView.axaml b/KaddaOK.AvaloniaApp/Views/RecognizeView.axaml index ab1ed01..b1da8ea 100644 --- a/KaddaOK.AvaloniaApp/Views/RecognizeView.axaml +++ b/KaddaOK.AvaloniaApp/Views/RecognizeView.axaml @@ -49,20 +49,57 @@ - + RowDefinitions="85,60,80,60,35,200,25,*"> + + + + There are two ways to proceed from here. + + + For the best result, I now recommend you use +