diff --git a/Mirivoice/Assets/Lang/strings-en-US.axaml b/Mirivoice/Assets/Lang/strings-en-US.axaml index dc296a0..e7e003f 100644 --- a/Mirivoice/Assets/Lang/strings-en-US.axaml +++ b/Mirivoice/Assets/Lang/strings-en-US.axaml @@ -40,7 +40,12 @@ Export Export audios per lines (.wav) + + An Error occured during export. + Please Check Log file for more informations. + Select Directory to export Export audio merged (.wav) + Set Filename to export diff --git a/Mirivoice/Mirivoice.Core/Format/RecentFIles.cs b/Mirivoice/Mirivoice.Core/Format/RecentFIles.cs index 0f1b559..a85fbd3 100644 --- a/Mirivoice/Mirivoice.Core/Format/RecentFIles.cs +++ b/Mirivoice/Mirivoice.Core/Format/RecentFIles.cs @@ -69,7 +69,7 @@ public void AddRecentFile(string filePath, MainViewModel v) { Files.RemoveAt(10); } - + Files.Reverse(); Save(); UpdateUI(v); } diff --git a/Mirivoice/Mirivoice.Core/Managers/AudioManager.cs b/Mirivoice/Mirivoice.Core/Managers/AudioManager.cs index 19fecc4..2954788 100644 --- a/Mirivoice/Mirivoice.Core/Managers/AudioManager.cs +++ b/Mirivoice/Mirivoice.Core/Managers/AudioManager.cs @@ -45,6 +45,21 @@ public string GetUniqueCachePath() return cacheFilePath; } + string SetSuffixToUnique(string filepath, int suffix) + { + if (File.Exists(filepath)) + { + string dirPath = Path.GetDirectoryName(filepath); + string changedPath = Path.Combine(dirPath, Path.GetFileNameWithoutExtension(filepath) + $"({suffix})" + Path.GetExtension(filepath)); + if (File.Exists(changedPath)) + { + return SetSuffixToUnique(filepath, suffix + 1); // recursive call + } + else return changedPath; + } + else return filepath; + } + List GetAllCacheFiles() { return Directory.GetFiles(MainManager.Instance.PathM.CachePath, "*.wav").ToList(); @@ -53,7 +68,15 @@ List GetAllCacheFiles() private double OffsetBeforePlay; private int offset = 0; private int currentLine; - public async void PlayAllCacheFiles(int startIndex) + /// + /// Note that startIndex is same as lineNo (starts from 1, not 0) + /// + /// + /// + /// + /// + /// + public async void PlayAllCacheFiles(int startIndex, bool exportOnlyAndDoNotPlay=false, bool exportPerTrack=true, string fileName="", string DirPath="") { if ( _waveOut != null && _waveOut.PlaybackState == PlaybackState.Paused) { @@ -61,21 +84,21 @@ public async void PlayAllCacheFiles(int startIndex) return; } List caches = new List(); - ObservableCollection lineBoxViews = v.LineBoxCollection; + int index = 0; v.SingleTextBoxEditorEnabled = false; v.CurrentEdit.IsEnabled = false; var tasks = new List(); caches.Clear(); - for (int i = startIndex - 1; i < lineBoxViews.Count; ++i) + for (int i = startIndex - 1; i < v.LineBoxCollection.Count; ++i) { - caches.Add(lineBoxViews[i].CurrentCacheName); + caches.Add(v.LineBoxCollection[i].CurrentCacheName); } - - for (int i = 0; i < lineBoxViews.Count; ++i) + v.MainWindowGetInput = false; + for (int i = 0; i < v.LineBoxCollection.Count; ++i) { - LineBoxView l = lineBoxViews[i]; + LineBoxView l = v.LineBoxCollection[i]; if (i < startIndex - 1) { @@ -85,7 +108,7 @@ public async void PlayAllCacheFiles(int startIndex) if (i == startIndex - 1) { - l.v.StartProgress(0, lineBoxViews.Count - startIndex + 1, "Inference"); + l.v.StartProgress(0, v.LineBoxCollection.Count - startIndex + 1, "Inference"); } Log.Debug($"[Generating Cache]"); @@ -103,15 +126,74 @@ public async void PlayAllCacheFiles(int startIndex) await Task.WhenAll(tasks); // Finalize progress - if (lineBoxViews.Count - 1 >= startIndex - 1) + if (v.LineBoxCollection.Count - 1 >= startIndex - 1) { - lineBoxViews[lineBoxViews.Count - 1].v.EndProgress(); + v.LineBoxCollection[v.LineBoxCollection.Count - 1].v.EndProgress(); + } + + if (exportOnlyAndDoNotPlay) + { + + Log.Information("Exporting cache files."); + if (exportPerTrack) + { + // export per track + int no = 1; + foreach (string cacheName in caches) + { + string exportPath = Path.Combine(DirPath, $"{no}_{fileName}.wav"); + + exportPath = SetSuffixToUnique(exportPath, 1); + Log.Debug($"Exporting {cacheName} to {exportPath}"); + // resample to 48000kHz + using (var reader = new AudioFileReader(cacheName)) + { + using (var resampler = new MediaFoundationResampler(reader, new WaveFormat(48000, reader.WaveFormat.Channels))) + { + resampler.ResamplerQuality = 60; + + WaveFileWriter.CreateWaveFile(exportPath, resampler); + } + } + no++; + } + + } + else + { + // export mixdown + string exportPath = Path.Combine(DirPath, $"{fileName}"); // no suffix, because file extension is already included + exportPath = SetSuffixToUnique(exportPath, 1); + using (var outputWaveFile = new WaveFileWriter(exportPath, new WaveFormat(48000, 1))) + { + foreach (string cacheName in caches) + { + using (var reader = new AudioFileReader(cacheName)) + { + // resample to 48000kHz + using (var resampler = new MediaFoundationResampler(reader, new WaveFormat(48000, reader.WaveFormat.Channels))) + { + resampler.ResamplerQuality = 60; + byte[] buffer = new byte[8192]; + int read; + while ((read = resampler + .Read(buffer, 0, buffer.Length)) > 0) + { + outputWaveFile.Write(buffer, 0, read); + } + + } + } + } + } + } + v.MainWindowGetInput = true; + return; } - foreach (string cacheName in caches) { - Log.Debug($"[Playing Cache] {cacheName}"); + //Log.Debug($"[Playing Cache] {cacheName}"); var reader = new AudioFileReader(cacheName); _audioReaders.Add(reader); @@ -124,7 +206,7 @@ public async void PlayAllCacheFiles(int startIndex) SelectedBtnIndexBeforePlay = startIndex - 1; v.LinesViewerOffset = new Avalonia.Vector(0, 104 * (startIndex - 1)); currentLine = startIndex - 1; - v.MainWindowGetInput = false; + PlayNextFile(); } diff --git a/Mirivoice/ViewModels/MainViewModel.cs b/Mirivoice/ViewModels/MainViewModel.cs index 6be320b..c29b7c0 100644 --- a/Mirivoice/ViewModels/MainViewModel.cs +++ b/Mirivoice/ViewModels/MainViewModel.cs @@ -34,7 +34,10 @@ public class MainViewModel : VoicerSelectingViewModelBase MimeTypes = new[] { "MiriVoiceProject/*" } }; - + public static FilePickerFileType MiriVoiceExportAudio { get; } = new("Wav File") + { + Patterns = new[] { "*.wav" } + }; Mrp initMrp; Version version = System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version; @@ -387,15 +390,87 @@ public void OnPlayButtonClick() Log.Information("Pause Button Clicked"); } } - - public void OnExportAudioPerLineClick() + IStorageFolder LastExportPath; + public async void OnExportAudioPerLineClick() { + if (LineBoxCollection.Count == 0) + { + return; + } + + var topLevel = TopLevel.GetTopLevel(mainWindow); + if (LastExportPath is null) + { + LastExportPath = topLevel.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Downloads).Result; + } + var directory = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions + { + Title = (string)mainWindow.FindResource("menu.files.export.perLine.desc"), + AllowMultiple = false, + SuggestedStartLocation = LastExportPath, + SuggestedFileName = LastExportPath.Path.LocalPath + + }); + + + if (directory.Count > 0) + { + if (directory[0] is null) + { + return; + } + string path = directory[0].Path.LocalPath; + if (path == string.Empty) + { + path = LastExportPath.Path.LocalPath; + } + try + { + Log.Information($"Exporting audio per line to {path}"); + string filename = CurrentProjectPath.Split(System.IO.Path.PathSeparator).Last(); + MainManager.Instance.AudioM.PlayAllCacheFiles(1, true, true, System.IO.Path.GetFileNameWithoutExtension(filename) , path); + LastExportPath = directory[0]; + } + catch (Exception e) + { + Log.Error($"[Failed to export audio per line]{e.ToString}: {e.Message} \n>> traceback: \n\t{e.StackTrace}"); + var res = await ShowConfirmWindow("menu.files.export.failed"); + } + } + + } - public void OnExportAudioMergedClick() + public async void OnExportAudioMergedClick() { + if (LineBoxCollection.Count == 0) + { + return; + } + var topLevel = TopLevel.GetTopLevel(mainWindow); + var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + { + Title = (string)mainWindow.FindResource("menu.files.export.merged.desc"), + DefaultExtension = "wav", + ShowOverwritePrompt = true, + FileTypeChoices = new[] { MiriVoiceExportAudio }, + SuggestedFileName = System.IO.Path.GetFileNameWithoutExtension(CurrentProjectPath) + ".wav" + }); + if (file is not null) + { + string path = file.Path.LocalPath; + try + { + MainManager.Instance.AudioM.PlayAllCacheFiles(1, true, false, System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetDirectoryName(path) ); + } + catch (Exception e) + { + Log.Error($"[Failed to export audio merged]{e.ToString}: {e.Message} \n>> traceback: \n\t{e.StackTrace}"); + var res = await ShowConfirmWindow("menu.files.export.failed"); + } + } } public void OnGlobalSettingButtonClick() diff --git a/Mirivoice/Views/GlobalSettingWindow.axaml b/Mirivoice/Views/GlobalSettingWindow.axaml index ad17230..b975406 100644 --- a/Mirivoice/Views/GlobalSettingWindow.axaml +++ b/Mirivoice/Views/GlobalSettingWindow.axaml @@ -5,6 +5,7 @@ xmlns:vm="using:Mirivoice.ViewModels" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" x:Class="Mirivoice.GlobalSettingWindow" + Icon="/Assets/mirivoice-logo.ico" WindowStartupLocation="CenterScreen" MinWidth="500" MinHeight="600" Width="500" Height="450" diff --git a/Mirivoice/Views/SingleLineEditorView.axaml.cs b/Mirivoice/Views/SingleLineEditorView.axaml.cs index d99a17e..5417f84 100644 --- a/Mirivoice/Views/SingleLineEditorView.axaml.cs +++ b/Mirivoice/Views/SingleLineEditorView.axaml.cs @@ -38,17 +38,17 @@ private async void LineTextChanged(object sender, TextChangedEventArgs e) string textChanged = textBox.Text; if (l is null) { - Log.Debug("LineBoxView is null"); + //Log.Debug("LineBoxView is null"); return; } if (l.viewModel.LineText == textBox.Text) { - Log.Debug($"No need to phonemize ---- SingleLineTBox '{textBox.Text}' // linePreview '{l.viewModel.LineText}' "); ; + //Log.Debug($"No need to phonemize ---- SingleLineTBox '{textBox.Text}' // linePreview '{l.viewModel.LineText}' "); ; l.DeActivatePhonemizer = true; // no need to phonemize } else { - Log.Debug($"SingleLineTBox '{textBox.Text}' // linePreview '{l.viewModel.LineText}' "); ; + //Log.Debug($"SingleLineTBox '{textBox.Text}' // linePreview '{l.viewModel.LineText}' "); ; l.viewModel.LineText = textChanged; l.DeActivatePhonemizer = false; if (FirstUpdate)