diff --git a/OpenUtau.Core/PlaybackManager.cs b/OpenUtau.Core/PlaybackManager.cs index 996361bc2..2b4a6d053 100644 --- a/OpenUtau.Core/PlaybackManager.cs +++ b/OpenUtau.Core/PlaybackManager.cs @@ -62,6 +62,7 @@ private PlaybackManager() { public Audio.IAudioOutput AudioOutput { get; set; } = new Audio.DummyAudioOutput(); public bool Playing => AudioOutput.PlaybackState == PlaybackState.Playing; public bool StartingToPlay { get; private set; } + public bool SoloTrackExist { get; set; } = false; public void PlayTestSound() { masterMix = null; @@ -170,7 +171,7 @@ public void RenderToFiles(UProject project, string exportPath) { RenderEngine engine = new RenderEngine(project); var trackMixes = engine.RenderTracks(DocManager.Inst.MainScheduler, ref renderCancellation); for (int i = 0; i < trackMixes.Count; ++i) { - if (trackMixes[i] == null || i >= project.tracks.Count || project.tracks[i].Mute) { + if (trackMixes[i] == null || i >= project.tracks.Count || project.tracks[i].Muted) { continue; } var file = PathManager.Inst.GetExportPath(exportPath, project.tracks[i]); diff --git a/OpenUtau.Core/Render/RenderEngine.cs b/OpenUtau.Core/Render/RenderEngine.cs index 97216fd4c..3f3ba17fb 100644 --- a/OpenUtau.Core/Render/RenderEngine.cs +++ b/OpenUtau.Core/Render/RenderEngine.cs @@ -85,7 +85,7 @@ public Tuple> RenderMixdown(int startTick, TaskScheduler ui })); var trackMix = new WaveMix(trackSources); var fader = new Fader(trackMix); - fader.Scale = PlaybackManager.DecibelToVolume(track.Mute ? -24 : track.Volume); + fader.Scale = PlaybackManager.DecibelToVolume(track.Muted ? -24 : track.Volume); fader.Pan = (float)track.Pan; fader.SetScaleToTarget(); faders.Add(fader); diff --git a/OpenUtau.Core/Ustx/UTrack.cs b/OpenUtau.Core/Ustx/UTrack.cs index 4bb0cf9f9..c2e915bb4 100644 --- a/OpenUtau.Core/Ustx/UTrack.cs +++ b/OpenUtau.Core/Ustx/UTrack.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using OpenUtau.Api; using OpenUtau.Core.Render; @@ -89,8 +88,9 @@ public USinger Singer { [YamlIgnore] public string SingerName => Singer != null ? Singer.DisplayName : "[No Singer]"; [YamlIgnore] public int TrackNo { set; get; } public string TrackName { get; set; } = "New Track"; - public bool Mute { set; get; } - public bool Solo { set; get; } + [YamlIgnore] public bool Muted { set; get; } + public bool Mute { get; set; } + public bool Solo { get; set; } public double Volume { set; get; } public double Pan { set; get; } [YamlIgnore] public UExpressionDescriptor VoiceColorExp { set; get; } @@ -179,6 +179,11 @@ public void AfterLoad(UProject project) { }; } TrackNo = project.tracks.IndexOf(this); + if (Solo) { + PlaybackManager.Inst.SoloTrackExist = true; + } else if (Mute) { + Muted = true; + } } } } diff --git a/OpenUtau/Controls/TrackHeader.axaml b/OpenUtau/Controls/TrackHeader.axaml index fd51caee9..6cb2d65aa 100644 --- a/OpenUtau/Controls/TrackHeader.axaml +++ b/OpenUtau/Controls/TrackHeader.axaml @@ -82,7 +82,7 @@ + IsChecked="{Binding Muted, Mode=OneWay}" Command="{Binding ToggleMute}"> () .Subscribe(_ => { foreach (var (track, header) in trackHeaders) { - header.ViewModel?.ManuallyRaise(); + if (header.ViewModel == null) { + continue; + } + if (PlaybackManager.Inst.SoloTrackExist && !track.Solo) { + header.ViewModel.Muted = true; + } + header.ViewModel.ManuallyRaise(); } if (trackAdder != null) { trackAdder.TrackNo = trackHeaders.Count; @@ -61,18 +68,39 @@ public TrackHeaderCanvas() { .Subscribe(e => { foreach (var (track, header) in trackHeaders) { if (header.ViewModel != null) { - if (track.TrackNo == e.trackNo) { - header.ViewModel.Solo = e.solo; - if (e.solo) { - header.ViewModel.Mute = false; + if (e.solo) { + PlaybackManager.Inst.SoloTrackExist = true; + + if (track.TrackNo == e.trackNo) { + header.ViewModel.Solo = true; + header.ViewModel.Muted = false; + } else { + header.ViewModel.Solo = false; + header.ViewModel.Muted = true; } } else { - header.ViewModel.Solo = false; - header.ViewModel.Mute = e.solo; + PlaybackManager.Inst.SoloTrackExist = false; + if (track.TrackNo == e.trackNo) { + header.ViewModel.Solo = false; + } + if (track.Mute) { + header.ViewModel.Muted = true; + } else { + header.ViewModel.Muted = false; + } } header.ViewModel.ManuallyRaise(); } } + }); MessageBus.Current.Listen() + .Subscribe(e => { + foreach (var (track, header) in trackHeaders) { + if (header.ViewModel != null) { + if (track.TrackNo == e.trackNo) { + header.ViewModel.ToggleMute(); + } + } + } }); } diff --git a/OpenUtau/ViewModels/TrackHeaderViewModel.cs b/OpenUtau/ViewModels/TrackHeaderViewModel.cs index 1e6727ac5..7f85a51cb 100644 --- a/OpenUtau/ViewModels/TrackHeaderViewModel.cs +++ b/OpenUtau/ViewModels/TrackHeaderViewModel.cs @@ -33,7 +33,7 @@ public class TrackHeaderViewModel : ViewModelBase, IActivatableViewModel { [Reactive] public string TrackName { get; set; } = string.Empty; [Reactive] public double Volume { get; set; } [Reactive] public double Pan { get; set; } - [Reactive] public bool Mute { get; set; } + [Reactive] public bool Muted { get; set; } [Reactive] public bool Solo { get; set; } [Reactive] public Bitmap? Avatar { get; set; } @@ -130,26 +130,22 @@ public TrackHeaderViewModel(UTrack track) { TrackName = track.TrackName; Volume = track.Volume; Pan = track.Pan; - Mute = track.Mute; + Muted = track.Muted; Solo = track.Solo; - this.WhenAnyValue(x => x.track.TrackName) - .Subscribe(trackName => { - TrackName = trackName; - }); this.WhenAnyValue(x => x.Volume) .Subscribe(volume => { track.Volume = volume; - DocManager.Inst.ExecuteCmd(new VolumeChangeNotification(track.TrackNo, Mute ? -24 : volume)); + DocManager.Inst.ExecuteCmd(new VolumeChangeNotification(track.TrackNo, Muted ? -24 : volume)); }); this.WhenAnyValue(x => x.Pan) .Subscribe(pan => { track.Pan = pan; DocManager.Inst.ExecuteCmd(new PanChangeNotification(track.TrackNo, pan)); }); - this.WhenAnyValue(x => x.Mute) - .Subscribe(mute => { - track.Mute = mute; - DocManager.Inst.ExecuteCmd(new VolumeChangeNotification(track.TrackNo, mute ? -24 : Volume)); + this.WhenAnyValue(x => x.Muted) + .Subscribe(muted => { + track.Muted = muted; + DocManager.Inst.ExecuteCmd(new VolumeChangeNotification(track.TrackNo, muted ? -24 : Volume)); }); this.WhenAnyValue(x => x.Solo) .Subscribe(solo => { @@ -163,6 +159,21 @@ public void ToggleSolo() { MessageBus.Current.SendMessage(new TracksSoloEvent(track.TrackNo, !track.Solo)); } + public void ToggleMute() { + if (!track.Mute) { + track.Mute = true; + Muted = true; + } else { + track.Mute = false; + if (PlaybackManager.Inst.SoloTrackExist) { + Muted = true; + } else { + Muted = false; + } + } + this.RaisePropertyChanged(nameof(Muted)); + } + private bool TryChangePhonemizer(string phonemizerName) { try { var factory = DocManager.Inst.PhonemizerFactories.FirstOrDefault(factory => factory.type.FullName == phonemizerName); @@ -269,7 +280,7 @@ public void ManuallyRaise() { this.RaisePropertyChanged(nameof(Phonemizer)); this.RaisePropertyChanged(nameof(PhonemizerTag)); this.RaisePropertyChanged(nameof(Renderer)); - this.RaisePropertyChanged(nameof(Mute)); + this.RaisePropertyChanged(nameof(Muted)); this.RaisePropertyChanged(nameof(Solo)); this.RaisePropertyChanged(nameof(Volume)); this.RaisePropertyChanged(nameof(Pan)); @@ -326,6 +337,7 @@ public void Duplicate() { Phonemizer = track.Phonemizer, RendererSettings = track.RendererSettings, Mute = track.Mute, + Muted = track.Muted, Solo = track.Solo, Volume = track.Volume, Pan = track.Pan, @@ -350,6 +362,7 @@ public void DuplicateSettings() { Phonemizer = track.Phonemizer, RendererSettings = track.RendererSettings, Mute = track.Mute, + Muted = track.Muted, Solo = track.Solo, Volume = track.Volume, Pan = track.Pan, diff --git a/OpenUtau/ViewModels/TracksViewModel.cs b/OpenUtau/ViewModels/TracksViewModel.cs index f19ba4f59..0cf817a06 100644 --- a/OpenUtau/ViewModels/TracksViewModel.cs +++ b/OpenUtau/ViewModels/TracksViewModel.cs @@ -21,6 +21,12 @@ public TracksSoloEvent(int trackNo, bool solo) { this.solo = solo; } } + public class TracksMuteEvent { + public readonly int trackNo; + public TracksMuteEvent(int trackNo) { + this.trackNo = trackNo; + } + } public class PartsSelectionEvent { public readonly UPart[] selectedParts; public readonly UPart[] tempSelectedParts; diff --git a/OpenUtau/Views/MainWindow.axaml.cs b/OpenUtau/Views/MainWindow.axaml.cs index c46ba961f..e14c3a5f8 100644 --- a/OpenUtau/Views/MainWindow.axaml.cs +++ b/OpenUtau/Views/MainWindow.axaml.cs @@ -616,6 +616,28 @@ void OnKeyDown(object sender, KeyEventArgs args) { args.Handled = false; break; } + } else if (args.KeyModifiers == KeyModifiers.Shift) { + args.Handled = true; + switch (args.Key) { + // solo + case Key.S: + if (viewModel.TracksViewModel.SelectedParts.Count > 0) { + var part = viewModel.TracksViewModel.SelectedParts.First(); + var track = DocManager.Inst.Project.tracks[part.trackNo]; + MessageBus.Current.SendMessage(new TracksSoloEvent(part.trackNo, !track.Solo)); + } + break; + // mute + case Key.M: + if (viewModel.TracksViewModel.SelectedParts.Count > 0) { + var part = viewModel.TracksViewModel.SelectedParts.First(); + MessageBus.Current.SendMessage(new TracksMuteEvent(part.trackNo)); + } + break; + default: + args.Handled = false; + break; + } } else if (args.KeyModifiers == (cmdKey | KeyModifiers.Shift)) { args.Handled = true; switch (args.Key) { diff --git a/OpenUtau/Views/PianoRollWindow.axaml.cs b/OpenUtau/Views/PianoRollWindow.axaml.cs index 0c732f7b0..b51719f59 100644 --- a/OpenUtau/Views/PianoRollWindow.axaml.cs +++ b/OpenUtau/Views/PianoRollWindow.axaml.cs @@ -1190,6 +1190,18 @@ bool OnKeyExtendedHandler(KeyEventArgs args) { _ = MainWindow?.Save(); return true; } + // solo + if(isShift) { + var track = project.tracks[notesVm.Part.trackNo]; + MessageBus.Current.SendMessage(new TracksSoloEvent(notesVm.Part.trackNo, !track.Solo)); + return true; + } + break; + case Key.M: + // mute + if (isShift) { + MessageBus.Current.SendMessage(new TracksMuteEvent(notesVm.Part.trackNo)); + } break; case Key.F: // scroll selection into focus