diff --git a/OpenUtau.Core/Commands/TrackCommands.cs b/OpenUtau.Core/Commands/TrackCommands.cs
index 7d2eb1718..031b42b82 100644
--- a/OpenUtau.Core/Commands/TrackCommands.cs
+++ b/OpenUtau.Core/Commands/TrackCommands.cs
@@ -78,10 +78,16 @@ public MoveTrackCommand(UProject project, UTrack track, bool up) {
}
public override string ToString() => "Move track";
public override void Execute() {
+ if (index < 0 || index + 1 >= project.tracks.Count) {
+ return;
+ }
project.tracks.Reverse(index, 2);
UpdateTrackNo();
}
public override void Unexecute() {
+ if (index < 0 || index + 1 >= project.tracks.Count) {
+ return;
+ }
project.tracks.Reverse(index, 2);
UpdateTrackNo();
}
@@ -113,6 +119,36 @@ public ChangeTrackColorCommand(UProject project, UTrack track, string colorName)
public override void Unexecute() => track.TrackColor = oldName;
}
+ public class TrackChangeSettingsCommand : TrackCommand {
+ readonly bool newMute;
+ readonly bool oldMute;
+ readonly double newVolume;
+ readonly double oldVolume;
+ readonly double newPan;
+ readonly double oldPan;
+ public TrackChangeSettingsCommand(UProject project, UTrack track, bool mute, double volume, double pan) {
+ this.project = project;
+ this.track = track;
+ newMute = mute;
+ newVolume = volume;
+ newPan = pan;
+ oldMute = track.Mute;
+ oldVolume = track.Volume;
+ oldPan = track.Pan;
+ }
+ public override string ToString() => "Change track settings";
+ public override void Execute() {
+ track.Mute = newMute;
+ track.Volume = newVolume;
+ track.Pan = newPan;
+ }
+ public override void Unexecute() {
+ track.Mute = oldMute;
+ track.Volume = oldVolume;
+ track.Pan = oldPan;
+ }
+ }
+
public class TrackChangeSingerCommand : TrackCommand {
readonly USinger newSinger, oldSinger;
public TrackChangeSingerCommand(UProject project, UTrack track, USinger newSinger) {
diff --git a/OpenUtau/Controls/ApplyToAllTracksButton.cs b/OpenUtau/Controls/ApplyToAllTracksButton.cs
new file mode 100644
index 000000000..fd22f6d49
--- /dev/null
+++ b/OpenUtau/Controls/ApplyToAllTracksButton.cs
@@ -0,0 +1,32 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Input;
+using Avalonia.Media;
+
+namespace OpenUtau.App.Controls {
+
+ public class ApplyToAllTracksButton : Button {
+ public ApplyToAllTracksButton() {
+ Padding = new Avalonia.Thickness(0);
+ BorderThickness = new Avalonia.Thickness(0);
+ Background = Brushes.Transparent;
+ Focusable = false;
+ Content = new Path {
+ Stroke = ThemeManager.AccentBrush2,
+ StrokeThickness = 1.75,
+ Data = Geometry.Parse("M3,4 H11 M3,8 H11 M3,12 H11 M10,2 L13,4 L10,6 M10,6 L13,4 L10,2"),
+ };
+ }
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e) {
+ base.OnPointerPressed(e);
+ e.Handled = true;
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e) {
+ base.OnPointerReleased(e);
+ e.Handled = true;
+ }
+ }
+
+}
diff --git a/OpenUtau/Controls/FavouriteToggleButton.cs b/OpenUtau/Controls/FavouriteToggleButton.cs
index e1ffcc931..93e93374d 100644
--- a/OpenUtau/Controls/FavouriteToggleButton.cs
+++ b/OpenUtau/Controls/FavouriteToggleButton.cs
@@ -1,44 +1,54 @@
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
+using Avalonia.Input;
using Avalonia.Media;
using OpenUtau.App;
-public class FavouriteToggleButton : ToggleButton
-{
- private readonly Path _iconPath;
-
- public FavouriteToggleButton()
- {
- //this.Height = 20;
- //this.Width = 20;
- // Create icon Path.
- _iconPath = new Path
- {
- Fill = SolidColorBrush.Parse("#00000000"),
- Stroke = ThemeManager.AccentBrush3,
- StrokeThickness = 2,
- Data = Geometry.Parse("M12,21.35L10.55,20.03C5.4,15.36,2,12.28,2,8.5C2,5.42,4.42,3,7.5,3C9.24,3,10.91,3.81,12,5.09C13.09,3.81,14.76,3,16.5,3C19.58,3,22,5.42,22,8.5C22,12.28,18.6,15.36,13.45,20.04L12,21.35Z"),
- RenderTransform = new ScaleTransform { ScaleX = 0.6,ScaleY = 0.6}
- };
-
- this.Content = _iconPath;
-
- // Change icon on click.
- this.PropertyChanged += (sender, e) =>
- {
- if (e.Property == IsCheckedProperty)
- {
- UpdateIcon(IsChecked ?? false);
+namespace OpenUtau.App.Controls {
+
+ public class FavouriteToggleButton : ToggleButton {
+ private readonly Path _iconPath;
+
+ public FavouriteToggleButton() {
+ Padding = new Avalonia.Thickness(0);
+ //this.Height = 20;
+ //this.Width = 20;
+ // Create icon Path.
+ _iconPath = new Path {
+ Fill = SolidColorBrush.Parse("#00000000"),
+ Stroke = ThemeManager.AccentBrush3,
+ StrokeThickness = 2,
+ Data = Geometry.Parse("M12,21.35L10.55,20.03C5.4,15.36,2,12.28,2,8.5C2,5.42,4.42,3,7.5,3C9.24,3,10.91,3.81,12,5.09C13.09,3.81,14.76,3,16.5,3C19.58,3,22,5.42,22,8.5C22,12.28,18.6,15.36,13.45,20.04L12,21.35Z"),
+ RenderTransform = new ScaleTransform { ScaleX = 0.5, ScaleY = 0.5 }
+ };
+
+ this.Content = _iconPath;
+
+ // Change icon on click.
+ this.PropertyChanged += (sender, e) => {
+ if (e.Property == IsCheckedProperty) {
+ UpdateIcon(IsChecked ?? false);
+ }
+ };
+ }
+
+ private void UpdateIcon(bool isChecked) {
+ if (isChecked) {
+ _iconPath.Fill = ThemeManager.AccentBrush3;
+ } else {
+ _iconPath.Fill = SolidColorBrush.Parse("#00000000");
}
- };
- }
+ }
- private void UpdateIcon(bool isChecked)
- {
- if (isChecked) {
- _iconPath.Fill = ThemeManager.AccentBrush3;
- } else {
- _iconPath.Fill = SolidColorBrush.Parse("#00000000");
+ protected override void OnPointerPressed(PointerPressedEventArgs e) {
+ base.OnPointerPressed(e);
+ e.Handled = true;
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e) {
+ base.OnPointerReleased(e);
+ e.Handled = true;
}
}
+
}
diff --git a/OpenUtau/Controls/TrackHeader.axaml b/OpenUtau/Controls/TrackHeader.axaml
index 24374fb0a..580b8be71 100644
--- a/OpenUtau/Controls/TrackHeader.axaml
+++ b/OpenUtau/Controls/TrackHeader.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:OpenUtau.App.Controls"
xmlns:vm="using:OpenUtau.App.ViewModels"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="104"
x:Class="OpenUtau.App.Controls.TrackHeader" Width="300" Height="104" TrackNo="{Binding TrackNo}">
@@ -77,18 +78,42 @@
HorizontalOffset="-3" ItemsSource="{Binding PhonemizerMenuItems}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenUtau/Controls/TrackHeaderCanvas.cs b/OpenUtau/Controls/TrackHeaderCanvas.cs
index 0d72270a9..e3c2b420f 100644
--- a/OpenUtau/Controls/TrackHeaderCanvas.cs
+++ b/OpenUtau/Controls/TrackHeaderCanvas.cs
@@ -98,6 +98,7 @@ public TrackHeaderCanvas() {
}
}
});
+
MessageBus.Current.Listen()
.Subscribe(e => {
foreach (var (track, header) in trackHeaders) {
diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml
index bfc779b45..a6a86633f 100644
--- a/OpenUtau/Strings/Strings.axaml
+++ b/OpenUtau/Strings/Strings.axaml
@@ -776,7 +776,7 @@ General
Space: Play
◀ Scroll here to zoom horizontally ▶
Scroll here to zoom vertically ▶
-
+
Duplicate Track
Duplicate Track Settings
Favorites
@@ -793,6 +793,7 @@ General
Open singers location
Remove
Rename track
+ Rotate singers across selected tracks
Select Renderer
Select Singer
(Singer default)
@@ -800,6 +801,7 @@ General
Solo additionally (which not removes solo from other tracks)
Solo this only (which removes solo from other tracks)
Unsolo all
+ Standardize Track Settings
Change track color
Track Settings
diff --git a/OpenUtau/ViewModels/MenuItemViewModel.cs b/OpenUtau/ViewModels/MenuItemViewModel.cs
index 701d3e05d..ca847037c 100644
--- a/OpenUtau/ViewModels/MenuItemViewModel.cs
+++ b/OpenUtau/ViewModels/MenuItemViewModel.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Windows.Input;
using Avalonia.Controls.Shapes;
-using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Threading;
using OpenUtau.Core.Ustx;
@@ -10,6 +9,7 @@ namespace OpenUtau.App.ViewModels {
public class MenuItemViewModel {
public string? Header { get; set; }
public ICommand? Command { get; set; }
+ public ICommand? SecondaryCommand { get; set; }
public object? CommandParameter { get; set; }
public IList? Items { get; set; }
public double Height { get; set; } = 24;
@@ -17,6 +17,7 @@ public class MenuItemViewModel {
public KeyGesture? InputGesture { get; set; }
public bool IsEnabled { get; set; } = true;
public object? Icon { get; set; }
+ public virtual object HeaderViewModel => this;
public MenuItemViewModel() { }
public MenuItemViewModel(bool isChecked) {
@@ -44,19 +45,7 @@ public bool IsFavourite {
}
}
}
- private object? _icon;
- public new object? Icon {
- get {
- if(_icon == null) {
- if (CommandParameter is USinger) {
- _icon = new FavouriteToggleButton() {
- [!FavouriteToggleButton.IsCheckedProperty] = new Binding("IsFavourite")
- };
- }
- }
- return _icon;
- }
- }
+
public string? Location {
get {
if (CommandParameter is USinger singer) {
@@ -66,4 +55,7 @@ public string? Location {
}
}
}
+
+ public class PhonemizerMenuItemViewModel : MenuItemViewModel {
+ }
}
diff --git a/OpenUtau/ViewModels/TrackHeaderViewModel.cs b/OpenUtau/ViewModels/TrackHeaderViewModel.cs
index 7bbde4843..7ecc15d8b 100644
--- a/OpenUtau/ViewModels/TrackHeaderViewModel.cs
+++ b/OpenUtau/ViewModels/TrackHeaderViewModel.cs
@@ -26,9 +26,11 @@ public class TrackHeaderViewModel : ViewModelBase, IActivatableViewModel {
public string PhonemizerTag => track.Phonemizer.Tag;
public Core.Render.IRenderer Renderer => track.RendererSettings.Renderer;
public IReadOnlyList? SingerMenuItems { get; set; }
- public ReactiveCommand SelectSingerCommand { get; }
+ public ReactiveCommand SelectSingerCommand { get; }
+ public ReactiveCommand AllSetSingerCommand { get; }
public IReadOnlyList? PhonemizerMenuItems { get; set; }
public ReactiveCommand SelectPhonemizerCommand { get; }
+ public ReactiveCommand AllSetPhonemizerCommand { get; }
public IReadOnlyList? RenderersMenuItems { get; set; }
public ReactiveCommand SelectRendererCommand { get; }
[Reactive] public string TrackName { get; set; } = string.Empty;
@@ -53,8 +55,10 @@ public class TrackHeaderViewModel : ViewModelBase, IActivatableViewModel {
// Parameterless constructor for Avalonia preview only.
public TrackHeaderViewModel() {
- SelectSingerCommand = ReactiveCommand.Create(_ => { });
+ SelectSingerCommand = ReactiveCommand.Create(_ => { });
+ AllSetSingerCommand = ReactiveCommand.Create(_ => { });
SelectPhonemizerCommand = ReactiveCommand.Create(_ => { });
+ AllSetPhonemizerCommand = ReactiveCommand.Create(_ => { });
SelectRendererCommand = ReactiveCommand.Create(_ => { });
Activator = new ViewModelActivator();
track = new UTrack(DocManager.Inst.Project);
@@ -65,7 +69,7 @@ public TrackHeaderViewModel() {
public TrackHeaderViewModel(UTrack track) {
this.track = track;
- SelectSingerCommand = ReactiveCommand.Create(singer => {
+ SelectSingerCommand = ReactiveCommand.Create(singer => {
if (track.Singer != singer) {
DocManager.Inst.StartUndoGroup("command.track.singer");
ApplySingerToTrack(track, singer);
@@ -80,6 +84,28 @@ public TrackHeaderViewModel(UTrack track) {
this.RaisePropertyChanged(nameof(Renderer));
RefreshAvatar();
});
+ AllSetSingerCommand = ReactiveCommand.Create(singer => {
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count <= 1) {
+ targetTracks = DocManager.Inst.Project.tracks;
+ }
+ DocManager.Inst.StartUndoGroup("command.track.singer");
+ foreach (var targetTrack in targetTracks) {
+ ApplySingerToTrack(targetTrack, singer);
+ }
+ DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true));
+ DocManager.Inst.EndUndoGroup();
+ UpdateRecentSingers(singer);
+ Preferences.Save();
+ MessageBus.Current.SendMessage(new PianorollRefreshEvent("Part"));
+ MessageBus.Current.SendMessage(new TracksRefreshEvent());
+ this.RaisePropertyChanged(nameof(Singer));
+ this.RaisePropertyChanged(nameof(Renderer));
+ RefreshAvatar();
+ });
SelectPhonemizerCommand = ReactiveCommand.Create(factory => {
if (track.Phonemizer.GetType() != factory.type) {
DocManager.Inst.StartUndoGroup("command.track.setting");
@@ -102,6 +128,39 @@ public TrackHeaderViewModel(UTrack track) {
this.RaisePropertyChanged(nameof(Phonemizer));
this.RaisePropertyChanged(nameof(PhonemizerTag));
});
+ AllSetPhonemizerCommand = ReactiveCommand.Create(factory => {
+ if (factory == null) {
+ return;
+ }
+ var name = factory.type.FullName!;
+ Log.Information($"Loading Phonemizer: {factory}");
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count <= 1) {
+ targetTracks = DocManager.Inst.Project.tracks;
+ }
+ DocManager.Inst.StartUndoGroup("command.track.setting");
+ foreach (var targetTrack in targetTracks) {
+ var phonemizer = factory.Create();
+ if (phonemizer != null) {
+ DocManager.Inst.ExecuteCmd(new TrackChangePhonemizerCommand(DocManager.Inst.Project, targetTrack, phonemizer));
+ }
+ var targetSingerId = targetTrack.Singer?.Id;
+ if (!string.IsNullOrEmpty(targetSingerId) && targetTrack.Singer?.Found == true && phonemizer != null) {
+ Preferences.Default.SingerPhonemizers[targetSingerId] = name;
+ }
+ }
+ Preferences.Default.RecentPhonemizers.Remove(name);
+ Preferences.Default.RecentPhonemizers.Insert(0, name);
+ DocManager.Inst.EndUndoGroup();
+ Preferences.Save();
+ MessageBus.Current.SendMessage(new PianorollRefreshEvent("Part"));
+ MessageBus.Current.SendMessage(new TracksRefreshEvent());
+ this.RaisePropertyChanged(nameof(Phonemizer));
+ this.RaisePropertyChanged(nameof(PhonemizerTag));
+ });
SelectRendererCommand = ReactiveCommand.Create(name => {
var settings = new URenderSettings {
renderer = name,
@@ -228,6 +287,12 @@ public void JudgeMuted() {
this.RaisePropertyChanged(nameof(Muted));
}
+ private static bool IsTrackSelected(UTrack projectTrack) {
+ var tracksViewModel = (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
+ ?.MainWindow?.DataContext as MainWindowViewModel;
+ return tracksViewModel?.TracksViewModel.SelectedTracks.Contains(projectTrack) == true;
+ }
+
private void ApplySingerToTrack(UTrack targetTrack, USinger? singer) {
if (singer is USinger selectedSinger) {
Log.Information($"Loading Singer: {selectedSinger.Name}");
@@ -269,6 +334,7 @@ private SingerMenuItemViewModel CreateSingerMenuItem(USinger singer) {
return new SingerMenuItemViewModel() {
Header = singer.LocalizedName,
Command = SelectSingerCommand,
+ SecondaryCommand = AllSetSingerCommand,
CommandParameter = singer,
};
}
@@ -415,7 +481,7 @@ public void RefreshPhonemizers() {
if (track != null && track.Singer != null && track.Singer.Found) {
var factory = FindPhonemizerByName(track.Singer.DefaultPhonemizer);
if (factory != null) {
- items.Add(new MenuItemViewModel() {
+ items.Add(new PhonemizerMenuItemViewModel() {
Header = ThemeManager.GetString("tracks.singerdefault") + factory.ToString(),
Command = SelectPhonemizerCommand,
CommandParameter = factory,
@@ -427,9 +493,10 @@ public void RefreshPhonemizers() {
.Select(name => FindPhonemizerByName(name))
.OfType()
.OrderBy(factory => factory.tag)
- .Select(factory => new MenuItemViewModel() {
+ .Select(factory => new PhonemizerMenuItemViewModel() {
Header = factory.ToString(),
Command = SelectPhonemizerCommand,
+ SecondaryCommand = AllSetPhonemizerCommand,
CommandParameter = factory,
}));
//more phonemizers grouped by singing language
@@ -439,9 +506,10 @@ public void RefreshPhonemizers() {
.OrderBy(group => group.Key)
.Select(group => new MenuItemViewModel() {
Header = GetPhonemizerGroupHeader(group.Key),
- Items = group.Select(factory => new MenuItemViewModel() {
+ Items = group.Select(factory => new PhonemizerMenuItemViewModel() {
Header = factory.ToString(),
Command = SelectPhonemizerCommand,
+ SecondaryCommand = AllSetPhonemizerCommand,
CommandParameter = factory,
}).ToArray(),
}).ToArray()
@@ -506,7 +574,16 @@ public void ManuallyRaise() {
public void Remove() {
DocManager.Inst.StartUndoGroup("command.track.delete");
- DocManager.Inst.ExecuteCmd(new RemoveTrackCommand(DocManager.Inst.Project, track));
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ }
+ foreach (var track in targetTracks) {
+ DocManager.Inst.ExecuteCmd(new RemoveTrackCommand(DocManager.Inst.Project, track));
+ }
DocManager.Inst.EndUndoGroup();
}
@@ -515,7 +592,21 @@ public void MoveUp() {
return;
}
DocManager.Inst.StartUndoGroup("command.track.order");
- DocManager.Inst.ExecuteCmd(new MoveTrackCommand(DocManager.Inst.Project, track, true));
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ } else {
+ targetTracks = targetTracks
+ .Where(targetTrack => targetTrack != DocManager.Inst.Project.tracks.First())
+ .OrderBy(targetTrack => targetTrack.TrackNo)
+ .ToList();
+ }
+ foreach (var track in targetTracks) {
+ DocManager.Inst.ExecuteCmd(new MoveTrackCommand(DocManager.Inst.Project, track, true));
+ }
DocManager.Inst.EndUndoGroup();
}
@@ -524,7 +615,21 @@ public void MoveDown() {
return;
}
DocManager.Inst.StartUndoGroup("command.track.order");
- DocManager.Inst.ExecuteCmd(new MoveTrackCommand(DocManager.Inst.Project, track, false));
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ } else {
+ targetTracks = targetTracks
+ .Where(targetTrack => targetTrack != DocManager.Inst.Project.tracks.Last())
+ .OrderByDescending(targetTrack => targetTrack.TrackNo)
+ .ToList();
+ }
+ foreach (var track in targetTracks) {
+ DocManager.Inst.ExecuteCmd(new MoveTrackCommand(DocManager.Inst.Project, track, false));
+ }
DocManager.Inst.EndUndoGroup();
}
@@ -535,8 +640,19 @@ public void Rename() {
dialog.onFinish = name => {
if (!string.IsNullOrWhiteSpace(name) && name != track.TrackName) {
DocManager.Inst.StartUndoGroup("command.track.setting");
- this.TrackName = name;
- DocManager.Inst.ExecuteCmd(new RenameTrackCommand(DocManager.Inst.Project, track, name));
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ this.TrackName = name;
+ DocManager.Inst.ExecuteCmd(new RenameTrackCommand(DocManager.Inst.Project, track, name));
+ } else {
+ for (int i = 0; i < targetTracks.Count; i++) {
+ string name_ = $"{name}_{i:000}";
+ DocManager.Inst.ExecuteCmd(new RenameTrackCommand(DocManager.Inst.Project, targetTracks[i], name_));
+ }
+ }
DocManager.Inst.EndUndoGroup();
}
};
@@ -546,66 +662,188 @@ public void Rename() {
}
public async void SelectTrackColor() {
- var dialog = new TrackColorDialog();
- dialog.DataContext = new TrackColorViewModel(track);
-
- if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null) {
- await dialog.ShowDialog(desktop.MainWindow);
- TrackAccentColor = ThemeManager.GetTrackColor(track.TrackColor).AccentColor;
- TrackColor = Preferences.Default.UseTrackColor
- ? ThemeManager.GetTrackColor(track.TrackColor)
- : ThemeManager.GetTrackColor("Blue");
- RefreshSelectionStyle();
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ var dialogs = new List();
+ if (targetTracks.Count < 2) {
+ var dialog = new TrackColorDialog();
+ dialog.DataContext = new TrackColorViewModel(track);
+ dialogs.Add(dialog);
+ } else {
+ foreach (var track in targetTracks) {
+ var dialog = new TrackColorDialog();
+ dialog.DataContext = new TrackColorViewModel(track);
+ dialogs.Add(dialog);
+ }
+ }
+ foreach (var dialog in dialogs) {
+ if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null) {
+ await dialog.ShowDialog(desktop.MainWindow);
+ TrackAccentColor = ThemeManager.GetTrackColor(track.TrackColor).AccentColor;
+ TrackColor = Preferences.Default.UseTrackColor
+ ? ThemeManager.GetTrackColor(track.TrackColor)
+ : ThemeManager.GetTrackColor("Blue");
+ RefreshSelectionStyle();
+ }
}
}
public void Duplicate() {
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ } else {
+ targetTracks = targetTracks
+ .OrderByDescending(targetTrack => targetTrack.TrackNo)
+ .ToList();
+ }
DocManager.Inst.StartUndoGroup("command.track.duplicate");
- var newTrack = new UTrack(track.TrackName + "_copy") {
- TrackNo = track.TrackNo + 1,
- Singer = track.Singer,
- Phonemizer = track.Phonemizer,
- RendererSettings = track.RendererSettings,
- Mute = track.Mute,
- Muted = track.Muted,
- Solo = false,
- Volume = track.Volume,
- Pan = track.Pan,
- TrackColor = track.TrackColor,
- TrackExpressions = track.TrackExpressions.Select(exp => exp.Clone()).ToList()
- };
- DocManager.Inst.ExecuteCmd(new AddTrackCommand(DocManager.Inst.Project, newTrack));
- var parts = DocManager.Inst.Project.parts
- .Where(part => part.trackNo == track.TrackNo)
- .Select(part => part.Clone()).ToList();
- foreach (var part in parts) {
- part.trackNo = newTrack.TrackNo;
- DocManager.Inst.ExecuteCmd(new AddPartCommand(DocManager.Inst.Project, part));
+ foreach (var track in targetTracks) {
+ var sourceTrackNo = track.TrackNo;
+ var newTrack = new UTrack(track.TrackName + "_copy") {
+ TrackNo = sourceTrackNo + 1,
+ Singer = track.Singer,
+ Phonemizer = track.Phonemizer,
+ RendererSettings = track.RendererSettings,
+ Mute = track.Mute,
+ Muted = track.Muted,
+ Solo = false,
+ Volume = track.Volume,
+ Pan = track.Pan,
+ TrackColor = track.TrackColor,
+ TrackExpressions = track.TrackExpressions.Select(exp => exp.Clone()).ToList()
+ };
+ DocManager.Inst.ExecuteCmd(new AddTrackCommand(DocManager.Inst.Project, newTrack));
+ var parts = DocManager.Inst.Project.parts
+ .Where(part => part.trackNo == sourceTrackNo)
+ .Select(part => part.Clone()).ToList();
+ foreach (var part in parts) {
+ part.trackNo = newTrack.TrackNo;
+ DocManager.Inst.ExecuteCmd(new AddPartCommand(DocManager.Inst.Project, part));
+ }
}
DocManager.Inst.EndUndoGroup();
}
public void DuplicateSettings() {
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ } else {
+ targetTracks = targetTracks
+ .OrderByDescending(targetTrack => targetTrack.TrackNo)
+ .ToList();
+ }
DocManager.Inst.StartUndoGroup("command.track.duplicate");
- DocManager.Inst.ExecuteCmd(new AddTrackCommand(DocManager.Inst.Project, new UTrack(track.TrackName + "_copy") {
- TrackNo = track.TrackNo + 1,
- Singer = track.Singer,
- Phonemizer = track.Phonemizer,
- RendererSettings = track.RendererSettings,
- Mute = track.Mute,
- Muted = track.Muted,
- Solo = false,
- Volume = track.Volume,
- Pan = track.Pan,
- TrackColor = track.TrackColor,
- TrackExpressions = track.TrackExpressions.Select(exp => exp.Clone()).ToList()
- }));
+ foreach (var track in targetTracks) {
+ DocManager.Inst.ExecuteCmd(new AddTrackCommand(DocManager.Inst.Project, new UTrack(track.TrackName + "_copy") {
+ TrackNo = track.TrackNo + 1,
+ Singer = track.Singer,
+ Phonemizer = track.Phonemizer,
+ RendererSettings = track.RendererSettings,
+ Mute = track.Mute,
+ Muted = track.Muted,
+ Solo = false,
+ Volume = track.Volume,
+ Pan = track.Pan,
+ TrackColor = track.TrackColor,
+ TrackExpressions = track.TrackExpressions.Select(exp => exp.Clone()).ToList()
+ }));
+ }
+ DocManager.Inst.EndUndoGroup();
+ }
+
+ public void StandardizeSettings() {
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count <= 1) {
+ targetTracks = DocManager.Inst.Project.tracks;
+ }
+ var phonemizerFactory = PhonemizerFactory.Get(track.Phonemizer.GetType());
+ DocManager.Inst.StartUndoGroup("command.track.setting");
+ foreach (var targetTrack in targetTracks) {
+ if (targetTrack == track) {
+ continue;
+ }
+ if (track.Singer != targetTrack.Singer) {
+ ApplySingerToTrack(targetTrack, track.Singer);
+ }
+ if (targetTrack.Phonemizer.GetType() != track.Phonemizer.GetType()) {
+ var phonemizer = phonemizerFactory?.Create();
+ if (phonemizer != null) {
+ DocManager.Inst.ExecuteCmd(new TrackChangePhonemizerCommand(DocManager.Inst.Project, targetTrack, phonemizer));
+ }
+ }
+ DocManager.Inst.ExecuteCmd(new TrackChangeRenderSettingCommand(
+ DocManager.Inst.Project,
+ targetTrack,
+ track.RendererSettings.Clone()));
+ DocManager.Inst.ExecuteCmd(new TrackChangeSettingsCommand(
+ DocManager.Inst.Project,
+ targetTrack,
+ track.Mute,
+ track.Volume,
+ track.Pan));
+ DocManager.Inst.ExecuteCmd(new ChangeTrackColorCommand(
+ DocManager.Inst.Project,
+ targetTrack,
+ track.TrackColor));
+ DocManager.Inst.ExecuteCmd(new ConfigureExpressionsCommand(
+ DocManager.Inst.Project,
+ DocManager.Inst.Project.expressions.Values.ToArray(),
+ targetTrack,
+ track.TrackExpressions.Select(exp => exp.Clone()).ToArray()));
+ }
+ DocManager.Inst.EndUndoGroup();
+ MessageBus.Current.SendMessage(new TracksRefreshEvent());
+ MessageBus.Current.SendMessage(new PianorollRefreshEvent("TrackColor"));
+ }
+
+ public void RotateSelectedTrackSingers() {
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .OrderBy(targetTrack => targetTrack.TrackNo)
+ .ToList();
+ if (targetTracks.Count <= 1) {
+ targetTracks = DocManager.Inst.Project.tracks;
+ }
+ var singers = targetTracks
+ .Select(targetTrack => targetTrack.Singer)
+ .ToList();
+ DocManager.Inst.StartUndoGroup("command.track.singer");
+ for (int i = 0; i < targetTracks.Count; i++) {
+ var singer = singers[(i + 1) % singers.Count];
+ ApplySingerToTrack(targetTracks[i], singer);
+ }
DocManager.Inst.EndUndoGroup();
+ DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true));
+ MessageBus.Current.SendMessage(new TracksRefreshEvent());
+ MessageBus.Current.SendMessage(new PianorollRefreshEvent("Part"));
}
public void VoiceColorRemapping() {
- if (track.Singer != null && track.Singer.Found && track.VoiceColorExp != null) {
- DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(track.TrackNo, false));
+ var targetTracks = DocManager.Inst.Project.tracks
+ .Where(projectTrack => projectTrack != null)
+ .Where(projectTrack => IsTrackSelected(projectTrack))
+ .ToList();
+ if (targetTracks.Count < 2) {
+ targetTracks = new List() { this.track };
+ }
+ foreach (var track in targetTracks) {
+ if (track.Singer != null && track.Singer.Found && track.VoiceColorExp != null) {
+ DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(track.TrackNo, false));
+ }
}
}