Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions OpenUtau.Core/PlaybackManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
using Serilog;
using System.Collections.Concurrent;

namespace OpenUtau.Core {
public class SineGenerator : ISampleProvider {
Expand Down Expand Up @@ -265,6 +264,7 @@ private PlaybackManager() {

double startMs;
public int StartTick => DocManager.Inst.Project.timeAxis.MsPosToTickPos(startMs);
public int returnPosition = 0;
// One cancellation source per render lane so starting playback, pre-rendering
// or exporting no longer cancel each other in flight.
CancellationTokenSource renderCancellation;
Expand Down Expand Up @@ -435,6 +435,11 @@ public void StopPlayback() {
}

public void PausePlayback() {
if (Preferences.Default.LockStartTime != 0) {
StopPlayback();
DocManager.Inst.ExecuteCmd(new SetPlayPosTickNotification(returnPosition, pause: true));
return;
}
AudioOutput.Pause();
StartingToPlay = false;
PlayingMaster = false;
Expand Down Expand Up @@ -468,14 +473,19 @@ private void Render(UProject project, int tick, int endTick, int trackNo) {
RenderEngine engine = new RenderEngine(project, startTick: tick, endTick: endTick, trackNo: trackNo);
var result = engine.RenderMixdown(DocManager.Inst.MainScheduler, ref renderCancellation, wait: false, applyMixFx: true, planner: MixPlanner);
playbackMix = new PlaybackMix(result.Item1, metronomeEngine);
this.returnPosition = tick;
var startTick = tick;
if (Preferences.Default.PreRoll) {
startTick = Math.Max(0, tick - (480 * 4 * Preferences.Default.PreRollMeasure));
}
var playbackAdapter = new MasterAdapter(playbackMix);
// Hold mode: wait for pending phrases (today's behaviour), except
// in loop mode where a pending phrase must not stall the clock.
// Failed phrases never hold in either mode.
playbackAdapter.HoldWhenUnready = !LoopPlayback;
playbackAdapter.SetPosition((int)(project.timeAxis.TickPosToMsPos(tick) * 44100 / 1000) * 2);
playbackAdapter.SetPosition((int)(project.timeAxis.TickPosToMsPos(startTick) * 44100 / 1000) * 2);
faders = result.Item2;
StartPlayback(project.timeAxis.TickPosToMsPos(tick), playbackAdapter);
StartPlayback(project.timeAxis.TickPosToMsPos(startTick), playbackAdapter);

Task.Factory.StartNew(() => {
DocManager.Inst.ExecuteCmd(new WaveformReadyNotification());
Expand Down
4 changes: 4 additions & 0 deletions OpenUtau.Core/Util/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ public class SerializablePreferences {
public int MetronomeVolume = 60;
public int MetronomeHighFrequency = 2200;
public int MetronomeLowFrequency = 1320;
public bool AutoMovePlayhead = false;
public bool PreRoll = false;
public int PreRollMeasure = 1;
/// <summary>On Pausing; 0 = Do nothing, 1 = Move cursor and scroll, 2 = Move cursor only</summary>
public int LockStartTime = 0;
public int PlaybackAutoScroll = 2;
public bool ReverseLogOrder = true;
Expand Down
5 changes: 5 additions & 0 deletions OpenUtau/Controls/PianoRoll.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="{DynamicResource menu.view.moveplayhead}" Click="OnMenuAutoMovePlayhead">
<MenuItem.Icon>
<CheckBox IsChecked="{Binding AutoMovePlayhead}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource prefs.playback.lockstarttime}">
<MenuItem Header="{DynamicResource prefs.playback.lockstarttime.off}" Click="OnMenuLockStartTime" Tag="0">
<MenuItem.Icon>
Expand Down
5 changes: 5 additions & 0 deletions OpenUtau/Controls/PianoRoll.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ void OnMenuDegreeStyle(object sender, RoutedEventArgs args) {
MessageBus.Current.SendMessage(new PianorollRefreshEvent("Part"));
}
}
void OnMenuAutoMovePlayhead(object sender, RoutedEventArgs args) {
Preferences.Default.AutoMovePlayhead = !Preferences.Default.AutoMovePlayhead;
Preferences.Save();
ViewModel.RaisePropertyChanged(nameof(ViewModel.AutoMovePlayhead));
}
void OnMenuLockStartTime(object sender, RoutedEventArgs args) {
if (sender is MenuItem menu && int.TryParse(menu.Tag?.ToString(), out int tag)) {
Preferences.Default.LockStartTime = tag;
Expand Down
3 changes: 3 additions & 0 deletions OpenUtau/OpenUtau.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@
<Compile Update="Views\TrackColorDialog.axaml.cs">
<DependentUpon>TrackColorDialog.axaml</DependentUpon>
</Compile>
<Compile Update="Views\ComboBoxDialog.axaml.cs">
<DependentUpon>ComboBoxDialog.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Resources.resx">
Expand Down
3 changes: 3 additions & 0 deletions OpenUtau/Strings/Strings.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ OpenUtau aims to be an open source editing environment for UTAU community, with
<system:String x:Key="dialogs.messagebox.copy">Copy error to clipboard</system:String>
<system:String x:Key="dialogs.noresampler.caption">No resampler</system:String>
<system:String x:Key="dialogs.noresampler.message">No resampler! Put your favourite resampler exe or dll in the Resamplers folder and choose it in Preferences!</system:String>
<system:String x:Key="dialogs.preroll.caption">Pre-roll Measures</system:String>
<system:String x:Key="dialogs.recovery">Previously, OpenUtau terminated abnormally. Click here to recover.</system:String>
<system:String x:Key="dialogs.recovery.caption">Recovery project</system:String>
<system:String x:Key="dialogs.remaptimeaxis.message">This tool will change the tempo of the project without changing the actual positions and durations (in seconds) of notes.
Expand Down Expand Up @@ -368,6 +369,7 @@ Do you want to continue by splitting at the nearest position after current playh
<system:String x:Key="menu.tools.singers">Singers...</system:String>
<system:String x:Key="menu.tools.wavtoolresampler.install">Install Wavtool/Resampler (.exe)...</system:String>
<system:String x:Key="menu.view">View</system:String>
<system:String x:Key="menu.view.moveplayhead">Move the playhead when selecting notes</system:String>

<system:String x:Key="mergevoicebank.back">Back</system:String>
<system:String x:Key="mergevoicebank.error">Failed to merge singer.</system:String>
Expand Down Expand Up @@ -870,6 +872,7 @@ General
<system:String x:Key="tip.notes.zoomx">◀ Scroll here to zoom horizontally ▶</system:String>
<system:String x:Key="tip.notes.zoomy">Scroll here to zoom vertically ▶</system:String>
<system:String x:Key="tip.playback.loop">Loop playback</system:String>
<system:String x:Key="tip.playback.preroll">Pre-roll</system:String>

<system:String x:Key="tracks.duplicate">Duplicate Track</system:String>
<system:String x:Key="tracks.duplicatesettings">Duplicate Track Settings</system:String>
Expand Down
6 changes: 6 additions & 0 deletions OpenUtau/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public string AppVersion {
public ReactiveCommand<int, RxVoid>? DelTempoChangeCmd { get; set; }
public ReactiveCommand<int, RxVoid>? AddTimeSigChangeCmd { get; set; }
public ReactiveCommand<int, RxVoid>? DelTimeSigChangeCmd { get; set; }
[Reactive] public partial bool PreRoll { get; set; } = Preferences.Default.PreRoll;
[Reactive] public partial bool CanUndo { get; set; } = false;
[Reactive] public partial bool CanRedo { get; set; } = false;
[Reactive] public partial string UndoText { get; set; } = ThemeManager.GetString("menu.edit.undo");
Expand Down Expand Up @@ -143,6 +144,11 @@ public MainWindowViewModel() {
PianoRollMaxHeight = x ? double.PositiveInfinity : 0.01;
PianoRollMinHeight = x ? ViewConstants.PianoRollMinHeight : 0.01;
});
this.WhenAnyValue(vm => vm.PreRoll)
.Subscribe(x => {
Preferences.Default.PreRoll = x;
Preferences.Save();
});
}

public void Undo() {
Expand Down
29 changes: 16 additions & 13 deletions OpenUtau/ViewModels/NotePropertiesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Media;
using OpenUtau.Api;
using OpenUtau.Core;
using OpenUtau.Core.Format;
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
using ReactiveUI;
using ReactiveUI.Primitives;
using ReactiveUI.SourceGenerators;
using SharpCompress;
using OpenUtau.Api;
using ReactiveUI.Primitives;

namespace OpenUtau.App.ViewModels {
public partial class NotePropertiesViewModel : ViewModelBase, ICmdSubscriber {
Expand Down Expand Up @@ -141,19 +141,22 @@ public NotePropertiesViewModel() {
});

SubscribeExtensions.Subscribe(MessageBus.Current.Listen<NotesSelectionEvent>(), e => {
if (PanelControlPressed) {
PanelControlPressed = false;
DocManager.Inst.EndUndoGroup();
}
NoteLoading = true;
if (PanelControlPressed) {
PanelControlPressed = false;
DocManager.Inst.EndUndoGroup();
}

selectedNotes.Clear();
selectedNotes.UnionWith(e.selectedNotes);
selectedNotes.UnionWith(e.tempSelectedNotes);
OnSelectNotes();
NoteLoading = true;
selectedNotes.Clear();
selectedNotes.UnionWith(e.selectedNotes);
selectedNotes.UnionWith(e.tempSelectedNotes);
OnSelectNotes();
NoteLoading = false;

NoteLoading = false;
});
if (Preferences.Default.AutoMovePlayhead && selectedNotes.Count > 0 && Part != null) {
DocManager.Inst.ExecuteCmd(new SetPlayPosTickNotification(Part.position + selectedNotes.First().position));
}
});

DocManager.Inst.AddSubscriber(this);
}
Expand Down
5 changes: 3 additions & 2 deletions OpenUtau/ViewModels/PianoRollViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Input;
Expand Down Expand Up @@ -61,6 +61,7 @@ public partial class PianoRollViewModel : ViewModelBase, ICmdSubscriber {
public bool DegreeStyle0 { get => Preferences.Default.DegreeStyle == 0 ? true : false; }
public bool DegreeStyle1 { get => Preferences.Default.DegreeStyle == 1 ? true : false; }
public bool DegreeStyle2 { get => Preferences.Default.DegreeStyle == 2 ? true : false; }
public bool AutoMovePlayhead { get => Preferences.Default.AutoMovePlayhead; }
public bool LockStartTime0 { get => Preferences.Default.LockStartTime == 0 ? true : false; }
public bool LockStartTime1 { get => Preferences.Default.LockStartTime == 1 ? true : false; }
public bool LockStartTime2 { get => Preferences.Default.LockStartTime == 2 ? true : false; }
Expand Down Expand Up @@ -320,4 +321,4 @@ public void OnNext(UCommand cmd, bool isUndo) {

#endregion
}
}
}
5 changes: 0 additions & 5 deletions OpenUtau/ViewModels/PlaybackViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using OpenUtau.Core;
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
using ReactiveUI;

namespace OpenUtau.App.ViewModels {
Expand Down Expand Up @@ -52,10 +51,6 @@ public void SeekEnd() {
}
public void PlayOrPause(int tick = -1, int endTick = -1, int trackNo = -1) {
PlaybackManager.Inst.PlayOrPause(tick: tick, endTick: endTick, trackNo: trackNo);
var lockStartTime = Convert.ToBoolean(Preferences.Default.LockStartTime);
if (!PlaybackManager.Inst.OutputActive && !PlaybackManager.Inst.StartingToPlay && lockStartTime) {
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(PlaybackManager.Inst.StartTick, true));
}
}
public void Pause() {
PlaybackManager.Inst.PausePlayback();
Expand Down
14 changes: 14 additions & 0 deletions OpenUtau/Views/ComboBoxDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Window xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d"
x:Class="OpenUtau.App.Views.ComboBoxDialog"
Icon="/Assets/open-utau.ico"
Title="ComboBoxDialog" Height="120" Width="320" WindowStartupLocation="CenterOwner">
<StackPanel VerticalAlignment="Center" Margin="{Binding $parent.WindowDecorationMargin}">
<TextBlock Margin="20" Name="Prompt" IsVisible="False" TextWrapping="Wrap" MaxWidth="560"/>
<ComboBox Name="ComboBox" Margin="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Name="OkButton" Content="OK" Margin="4" HorizontalAlignment="Stretch"/>
</StackPanel>
</Window>
48 changes: 48 additions & 0 deletions OpenUtau/Views/ComboBoxDialog.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;

namespace OpenUtau.App.Views {
public partial class ComboBoxDialog : Window {
public Action<int>? onFinish;

public ComboBoxDialog() {
InitializeComponent();
OkButton.Click += OkButtonClick;
}

public void Initialize(string title, string[] items, int selectedIndex) {
Title = title;
ComboBox.ItemsSource = items;
ComboBox.SelectedIndex = selectedIndex;
}
public void SetPrompt(string prompt) {
Prompt.IsVisible = true;
Prompt.Text = prompt;
}

private void OkButtonClick(object? sender, RoutedEventArgs e) {
Finish();
}

private void Finish() {
if (onFinish != null) {
onFinish.Invoke(ComboBox.SelectedIndex);
}
Close();
}

protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key == Key.Escape) {
e.Handled = true;
Close();
} else if (e.Key == Key.Enter) {
e.Handled = true;
Finish();
} else {
base.OnKeyDown(e);
}
}
}
}
15 changes: 10 additions & 5 deletions OpenUtau/Views/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@
<RowDefinition Height="4"/>
</Grid.RowDefinitions>
<Panel Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="24">
<Border Classes="playback" Width="72" HorizontalAlignment="Left" Margin="4,0">
<Border Classes="playback" Width="68" HorizontalAlignment="Left" Margin="5,0">
<Grid>
<TextBlock Width="28" HorizontalAlignment="Left"
<TextBlock Width="24" HorizontalAlignment="Left"
Background="Transparent" PointerPressed="OnEditTimeSignature">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}/{1}">
Expand All @@ -242,7 +242,7 @@
</TextBlock>
</Grid>
</Border>
<Border Classes="playback" Width="127" HorizontalAlignment="Center">
<Border Classes="playback" Width="148" HorizontalAlignment="Center" Margin="5,0,0,0">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding PlaybackViewModel.SeekStart}" Height="18">
<Path Fill="{Binding $parent.Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
Expand All @@ -261,18 +261,23 @@
Data="M 0 0 L 5 4.5 L 0 9 Z M 5 0 L 7 0 L 7 9 L 5 9 Z"/>
</Button>
<ToggleButton IsChecked="{Binding PlaybackViewModel.LoopPlayback}"
ToolTip.Tip="{DynamicResource tip.playback.loop}" Height="18">
ToolTip.Tip="{DynamicResource tip.playback.loop}">
<Path Fill="{Binding $parent.Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="M 0.86 1.21 H 6.58 v -2 l 2.45 3 -2.45 3 v -2 H 1.68 v 2 H 0.04 v -2 c 0 -1.1 0.33 -2 0.82 -2 z m 7.36 8 h -5.73 v 2 l -2.45 -3 2.45 -3 v 2 h 4.91 v -2 H 9.04 v 2 c 0 1.1 -0.33 2 -0.82 2 z"/>
</ToggleButton>
<ToggleButton IsChecked="{Binding PreRoll}" PointerPressed="OnClickPreRoll"
ToolTip.Tip="{DynamicResource tip.playback.preroll}">
<Path Fill="{Binding $parent.Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="12" Height="12" Stretch="Uniform"
Data="M760-200v-160q0-50-35-85t-85-35H273l144 144-57 56-240-240 240-240 57 56-144 144h367q83 0 141.5 58.5T840-360v160h-80Z"/>
</ToggleButton>
<ToggleButton IsChecked="{Binding PlaybackViewModel.Metronome}"
ToolTip.Tip="{DynamicResource prefs.playback.metronome}">
<Path Fill="{Binding $parent.Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="M 5 1 C 6 1 7 1 8 1 L 11 5 L 11 13 L 2 13 L 2 5 Z Z M 6.5 4 A 1.5 1.5 0 1 1 6.5 7 A 1.5 1.5 0 1 1 6.5 4 Z M 6.5 5 L 12.5 0 L 12.5 1 L 6.5 6 Z"/>
</ToggleButton>
</StackPanel>
</Border>
<Border Classes="playback" Width="72" HorizontalAlignment="Right" Margin="4,0">
<Border Classes="playback" Width="64" HorizontalAlignment="Right" Margin="5,0">
<Grid>
<TextBlock Text="{Binding Path=PlaybackViewModel.PlayPosTime, StringFormat={}{0:mm\\:ss\\.fff}}"/>
</Grid>
Expand Down
18 changes: 18 additions & 0 deletions OpenUtau/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,24 @@ private void LayoutSplit(double? x, double? y) {
}
}

void OnClickPreRoll(object sender, PointerPressedEventArgs arg) {
var point = arg.GetCurrentPoint(sender as Control);
if (point.Properties.IsRightButtonPressed) {
var dialog = new ComboBoxDialog();
dialog.Initialize(ThemeManager.GetString("dialogs.preroll.caption"),
new[] { "1", "2", "3", "4" },
Preferences.Default.PreRollMeasure - 1);
dialog.onFinish = index => {
if (0 <= index && index < 4) {
Preferences.Default.PreRollMeasure = index + 1;
Preferences.Save();
}
};
dialog.ShowDialog(this);
}
arg.Handled = true;
}

void OnKeyDown(object sender, KeyEventArgs args) {
if (PianoRollContainer.IsKeyboardFocusWithin) {
args.Handled = false;
Expand Down
Loading