diff --git a/OpenUtau.Core/Editing/NoteBatchEdits.cs b/OpenUtau.Core/Editing/NoteBatchEdits.cs index d6cf1298c..d38972a14 100644 --- a/OpenUtau.Core/Editing/NoteBatchEdits.cs +++ b/OpenUtau.Core/Editing/NoteBatchEdits.cs @@ -282,6 +282,51 @@ public void Run(UProject project, UVoicePart part, List selectedNotes, Do } } + public class LengthenCrossfade : BatchEdit { + public virtual string Name => name; + private string name; + private double ratio; + + public LengthenCrossfade(double ratio) { + name = "pianoroll.menu.notes.lengthencrossfade"; + this.ratio = ratio; + } + + public void Run(UProject project, UVoicePart part, List selectedNotes, DocManager docManager) { + var notes = selectedNotes.Count > 0 ? selectedNotes : part.notes.ToList(); + if (notes.Count == 0) { + return; + } + docManager.StartUndoGroup(true); + var track = project.tracks[part.trackNo]; + foreach (var note in notes) { + foreach (UPhoneme phoneme in part.phonemes) { + if (phoneme.Parent == note && phoneme.Prev != null && phoneme.PositionMs == phoneme.Prev.EndMs) { + + double consonantStretch = Math.Pow(2f, 1.0f - phoneme.GetExpression(project, track, Format.Ustx.VEL).Item1 / 100f); + double maxPreutter = phoneme.oto.Preutter * consonantStretch; + double prevDur = phoneme.Prev.DurationMs; + double preutter = phoneme.preutter; + + if (maxPreutter > prevDur * 0.9f) { + maxPreutter = prevDur * 0.9f; + } + if(maxPreutter > phoneme.preutter) { + docManager.ExecuteCmd(new PhonemePreutterCommand(part, note, phoneme.index, (float)(maxPreutter - phoneme.autoPreutter))); + preutter = maxPreutter; + } + + var overlap = preutter * ratio; + if (overlap > phoneme.autoOverlap) { + docManager.ExecuteCmd(new PhonemeOverlapCommand(part, note, phoneme.index, (float)(overlap - phoneme.autoOverlap))); + } + } + } + } + docManager.EndUndoGroup(); + } + } + public class LoadRenderedPitch : BatchEdit { public virtual string Name => name; diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml index 4d03239d8..254096ddf 100644 --- a/OpenUtau/Strings/Strings.axaml +++ b/OpenUtau/Strings/Strings.axaml @@ -210,6 +210,7 @@ Warning: this option removes custom presets. Convert PITD to pitch control points Clear vibratos Hanzi to pinyin + Lengthen crossfades Load rendered pitch Move an octave down Move an octave up diff --git a/OpenUtau/Strings/Strings.ja-JP.axaml b/OpenUtau/Strings/Strings.ja-JP.axaml index f301a6b06..4a3701881 100644 --- a/OpenUtau/Strings/Strings.ja-JP.axaml +++ b/OpenUtau/Strings/Strings.ja-JP.axaml @@ -210,6 +210,7 @@ ピッチ曲線(PITD)をピッチ点に変換 ビブラートを削除 漢字をピンインへ + クロスフェードを長くする レンダリング済みピッチの読み込み 1オクターブ下げる 1オクターブ上げる diff --git a/OpenUtau/Views/PianoRollWindow.axaml.cs b/OpenUtau/Views/PianoRollWindow.axaml.cs index aa9c5535f..76d0979fc 100644 --- a/OpenUtau/Views/PianoRollWindow.axaml.cs +++ b/OpenUtau/Views/PianoRollWindow.axaml.cs @@ -40,6 +40,12 @@ public PianoRollWindow() { DataContext = ViewModel = new PianoRollViewModel(); ValueTip.IsVisible = false; + ViewModel.NoteBatchEdits.Add(new MenuItemViewModel() { + Header = ThemeManager.GetString("pianoroll.menu.notes.lengthencrossfade"), + Command = ReactiveCommand.Create(() => { + LengthenCrossfade(); + }) + }); ViewModel.LyricBatchEdits.Add(new MenuItemViewModel() { Header = ThemeManager.GetString("lyricsreplace.replace"), Command = ReactiveCommand.Create(() => { @@ -139,6 +145,31 @@ void EditNoteDefaults() { } } + void LengthenCrossfade() { + var notesVM = ViewModel.NotesViewModel; + if (notesVM.Part == null) { + return; + } + if (notesVM.Selection.IsEmpty) { + _ = MessageBox.Show( + this, + ThemeManager.GetString("lyrics.selectnotes"), + ThemeManager.GetString("lyrics.caption"), + MessageBox.MessageBoxButtons.Ok); + return; + } + var dialog = new SliderDialog(ThemeManager.GetString("pianoroll.menu.notes.lengthencrossfade"), 0.5, 0, 1, 0.1); + dialog.onFinish = value => { + var edit = new Core.Editing.LengthenCrossfade(value); + try { + edit.Run(notesVM.Project, notesVM.Part, notesVM.Selection.ToList(), DocManager.Inst); + } catch (Exception e) { + DocManager.Inst.ExecuteCmd(new ErrorMessageNotification("Failed to run editing macro", e)); + } + }; + dialog.ShowDialog(this); + } + public void OnExpButtonClick(object sender, RoutedEventArgs args) { var dialog = new ExpressionsDialog() { DataContext = new ExpressionsViewModel(), diff --git a/OpenUtau/Views/SliderDialog.axaml b/OpenUtau/Views/SliderDialog.axaml new file mode 100644 index 000000000..b29706333 --- /dev/null +++ b/OpenUtau/Views/SliderDialog.axaml @@ -0,0 +1,19 @@ + + + + + + +