Skip to content

Commit

Permalink
Merge pull request stakira#874 from oxygen-dioxide/solfege
Browse files Browse the repository at this point in the history
Scale degree display
  • Loading branch information
stakira authored Oct 22, 2023
2 parents 649fd69 + 8cddfbb commit 282a583
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 5 deletions.
12 changes: 12 additions & 0 deletions OpenUtau.Core/Commands/ProjectCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ public override void Unexecute() {
}
}

public class KeyCommand : ProjectCommand{
public readonly int oldKey;
public readonly int newKey;
public KeyCommand(UProject project, int key) : base(project) {
oldKey = project.key;
newKey = key;
}
public override string ToString() => $"Change key from {oldKey} to {newKey}";
public override void Execute() => project.key = newKey;
public override void Unexecute() => project.key = oldKey;
}

public class ConfigureExpressionsCommand : ProjectCommand {
readonly UExpressionDescriptor[] oldDescriptors;
readonly UExpressionDescriptor[] newDescriptors;
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Core/Ustx/UProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class UProject {
public string[] expSelectors = new string[] { Format.Ustx.DYN, Format.Ustx.PITD, Format.Ustx.CLR, Format.Ustx.ENG, Format.Ustx.VEL };
public int expPrimary = 0;
public int expSecondary = 1;
public int key = 0;//Music key of the project, 0 = C, 1 = C#, 2 = D, ..., 11 = B
public List<UTimeSignature> timeSignatures;
public List<UTempo> tempos;
public List<UTrack> tracks;
Expand Down
30 changes: 30 additions & 0 deletions OpenUtau.Core/Util/MusicMath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,36 @@ public enum KeyColor { White, Black }
{ "B", 11 },
};

public static readonly string[] Solfeges = {
"do",
"",
"re",
"",
"mi",
"fa",
"",
"sol",
"",
"la",
"",
"ti",
};

public static readonly string[] NumberedNotations = {
"1",
"",
"2",
"",
"3",
"4",
"",
"5",
"",
"6",
"",
"7",
};

public static string GetToneName(int noteNum) {
return noteNum < 0 ? string.Empty : KeysInOctave[noteNum % 12].Item1 + (noteNum / 12 - 1).ToString();
}
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Core/Util/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public class SerializablePreferences {
public bool ShowPrefs = true;
public bool ShowTips = true;
public int Theme;
public int DegreeStyle;
public bool UseTrackColor = false;
public bool ClearCacheOnQuit = false;
public bool PreRender = true;
Expand Down
38 changes: 33 additions & 5 deletions OpenUtau/Controls/TrackBackground.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using OpenUtau.Core;
using OpenUtau.Core.Util;
using ReactiveUI;

namespace OpenUtau.App.Controls {
Expand Down Expand Up @@ -65,12 +67,29 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
}
}

int mod(int a, int b){
return (a % b + b) % b;
}

public override void Render(DrawingContext context) {
if (TrackHeight == 0) {
return;
}
int track = (int)TrackOffset;
double top = TrackHeight * (track - TrackOffset);
int key = DocManager.Inst.Project == null ? 0 : DocManager.Inst.Project.key;
string[] degreeNames;
switch(Preferences.Default.DegreeStyle){
case 1:
degreeNames = MusicMath.Solfeges;
break;
case 2:
degreeNames = MusicMath.NumberedNotations;
break;
default:
degreeNames = Enumerable.Repeat("", 12).ToArray();
break;
}
while (top < Bounds.Height) {
bool isAltTrack = IsAltTrack(track) ^ (ThemeManager.IsDarkMode && !IsKeyboard);
bool isCenterKey = IsKeyboard && IsCenterKey(track);
Expand All @@ -85,11 +104,20 @@ public override void Render(DrawingContext context) {
brush = isCenterKey ? ThemeManager.CenterKeyNameBrush
: isAltTrack ? ThemeManager.BlackKeyNameBrush
: ThemeManager.WhiteKeyNameBrush;
string toneName = MusicMath.GetToneName(ViewConstants.MaxTone - 1 - track);
var textLayout = TextLayoutCache.Get(toneName, brush, 12);
var textPosition = new Point(Bounds.Width - 4 - (int)textLayout.Width, (int)(top + (TrackHeight - textLayout.Height) / 2));
using (var state = context.PushTransform(Matrix.CreateTranslation(textPosition))) {
textLayout.Draw(context, new Point());
int tone = ViewConstants.MaxTone - 1 - track;
string toneName = MusicMath.GetToneName(tone);
var toneTextLayout = TextLayoutCache.Get(toneName, brush, 12);
var toneTextPosition = new Point(Bounds.Width - 4 - (int)toneTextLayout.Width, (int)(top + (TrackHeight - toneTextLayout.Height) / 2));
using (var state = context.PushTransform(Matrix.CreateTranslation(toneTextPosition))) {
toneTextLayout.Draw(context, new Point());
}
//scale degree display
int degree = mod(tone - key, 12);
string degreeName = degreeNames[degree];
var degreeTextLayout = TextLayoutCache.Get(degreeName, brush, 12);
var degreeTextPosition = new Point(4, (int)(top + (TrackHeight - degreeTextLayout.Height) / 2));
using (var state = context.PushTransform(Matrix.CreateTranslation(degreeTextPosition))) {
degreeTextLayout.Draw(context, new Point());
}
}
track++;
Expand Down
5 changes: 5 additions & 0 deletions OpenUtau/Strings/Strings.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<system:String x:Key="dialogs.installdependency.message">Installing </system:String>
<system:String x:Key="dialogs.installdll.caption">Installing phonemizer</system:String>
<system:String x:Key="dialogs.installdll.message">Installing </system:String>
<system:String x:Key="dialogs.key.caption">Key</system:String>
<system:String x:Key="dialogs.messagebox.cancel">Cancel</system:String>
<system:String x:Key="dialogs.messagebox.no">No</system:String>
<system:String x:Key="dialogs.messagebox.ok">Ok</system:String>
Expand Down Expand Up @@ -290,6 +291,10 @@
<system:String x:Key="prefs.advanced.stable">Stable</system:String>
<system:String x:Key="prefs.advanced.vlabelerpath">vLabeler Path</system:String>
<system:String x:Key="prefs.appearance">Appearance</system:String>
<system:String x:Key="prefs.appearance.degree">Scale degree display style</system:String>
<system:String x:Key="prefs.appearance.degree.numbered">Numbered (1 2 3 4 5 6 7)</system:String>
<system:String x:Key="prefs.appearance.degree.off">Off</system:String>
<system:String x:Key="prefs.appearance.degree.solfege">Solfège (do re mi fa sol la ti)</system:String>
<system:String x:Key="prefs.appearance.lang">Language</system:String>
<system:String x:Key="prefs.appearance.showghostnotes">Show other tracks' notes on piano roll</system:String>
<system:String x:Key="prefs.appearance.showportrait">Show portrait on piano roll</system:String>
Expand Down
25 changes: 25 additions & 0 deletions OpenUtau/ViewModels/NotesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class NotesViewModel : ViewModelBase, ICmdSubscriber {
[Reactive] public bool ShowNoteParams { get; set; }
[Reactive] public bool IsSnapOn { get; set; }
[Reactive] public string SnapDivText { get; set; }
[Reactive] public string KeyText { get; set; }
[Reactive] public Rect ExpBounds { get; set; }
[Reactive] public string PrimaryKey { get; set; }
[Reactive] public bool PrimaryKeyNotSupported { get; set; }
Expand All @@ -85,8 +86,10 @@ public class NotesViewModel : ViewModelBase, ICmdSubscriber {
public double VScrollBarMax => Math.Max(0, TrackCount - ViewportTracks);
public UProject Project => DocManager.Inst.Project;
[Reactive] public List<MenuItemViewModel> SnapDivs { get; set; }
[Reactive] public List<MenuItemViewModel> Keys { get; set; }

public ReactiveCommand<int, Unit> SetSnapUnitCommand { get; set; }
public ReactiveCommand<int, Unit> SetKeyCommand { get; set; }

// See the comments on TracksViewModel.playPosXToTickOffset
private double playPosXToTickOffset => ViewportTicks / Bounds.Width;
Expand All @@ -111,6 +114,14 @@ public NotesViewModel() {
UpdateSnapDiv();
});

Keys = new List<MenuItemViewModel>();
SetKeyCommand = ReactiveCommand.Create<int>(key => {
DocManager.Inst.StartUndoGroup();
DocManager.Inst.ExecuteCmd(new KeyCommand(Project, key));
DocManager.Inst.EndUndoGroup();
UpdateKey();
});

viewportTicks = this.WhenAnyValue(x => x.Bounds, x => x.TickWidth)
.Select(v => v.Item1.Width / Math.Max(v.Item2, ViewConstants.TickWidthMin))
.ToProperty(this, x => x.ViewportTicks);
Expand Down Expand Up @@ -173,6 +184,13 @@ public NotesViewModel() {
Command = SetSnapUnitCommand,
CommandParameter = div,
}));
Keys.Clear();
Keys.AddRange(MusicMath.KeysInOctave
.Select((key, index) => new MenuItemViewModel {
Header = $"1={key.Item1}",
Command = SetKeyCommand,
CommandParameter = index,
}));
});

CursorTool = false;
Expand All @@ -193,6 +211,7 @@ public NotesViewModel() {
ShowTips = Preferences.Default.ShowTips;
IsSnapOn = true;
SnapDivText = string.Empty;
KeyText = string.Empty;

PlayTone = Preferences.Default.PlayTone;
this.WhenAnyValue(x => x.PlayTone)
Expand Down Expand Up @@ -267,6 +286,11 @@ private void UpdateSnapDiv() {
SnapDivText = $"(1/{div})";
}

private void UpdateKey(){
int key = Project.key;
KeyText = "1="+MusicMath.KeysInOctave[key].Item1;
}

public void OnXZoomed(Point position, double delta) {
bool recenter = true;
if (TickOffset == 0 && position.X < 0.1) {
Expand Down Expand Up @@ -378,6 +402,7 @@ private void LoadPart(UPart part, UProject project) {
LoadPortrait(part, project);
LoadWindowTitle(part, project);
LoadTrackColor(part, project);
UpdateKey();
}

//If PortraitHeight is 0, the default behaviour is resizing any image taller than 800px to 800px,
Expand Down
13 changes: 13 additions & 0 deletions OpenUtau/ViewModels/PlaybackViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class PlaybackViewModel : ViewModelBase, ICmdSubscriber {
public int BeatPerBar => Project.timeSignatures[0].beatPerBar;
public int BeatUnit => Project.timeSignatures[0].beatUnit;
public double Bpm => Project.tempos[0].bpm;
public int Key => Project.key;
public string KeyName => MusicMath.KeysInOctave[Key].Item1;
public int Resolution => Project.resolution;
public int PlayPosTick => DocManager.Inst.playPosTick;
public TimeSpan PlayPosTime => TimeSpan.FromMilliseconds((int)Project.timeAxis.TickPosToMsPos(DocManager.Inst.playPosTick));
Expand Down Expand Up @@ -61,17 +63,28 @@ public void SetBpm(double bpm) {
DocManager.Inst.EndUndoGroup();
}

public void SetKey(int key) {
if (key == DocManager.Inst.Project.key) {
return;
}
DocManager.Inst.StartUndoGroup();
DocManager.Inst.ExecuteCmd(new KeyCommand(Project, key));
DocManager.Inst.EndUndoGroup();
}

public void OnNext(UCommand cmd, bool isUndo) {
if (cmd is BpmCommand ||
cmd is TimeSignatureCommand ||
cmd is AddTempoChangeCommand ||
cmd is DelTempoChangeCommand ||
cmd is AddTimeSigCommand ||
cmd is DelTimeSigCommand ||
cmd is KeyCommand ||
cmd is LoadProjectNotification) {
this.RaisePropertyChanged(nameof(BeatPerBar));
this.RaisePropertyChanged(nameof(BeatUnit));
this.RaisePropertyChanged(nameof(Bpm));
this.RaisePropertyChanged(nameof(KeyName));
MessageBus.Current.SendMessage(new TimeAxisChangedEvent());
if (cmd is LoadProjectNotification) {
DocManager.Inst.ExecuteCmd(new SetPlayPosTickNotification(0));
Expand Down
7 changes: 7 additions & 0 deletions OpenUtau/ViewModels/PreferencesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public AudioOutputDevice? AudioOutputDevice {
[Reactive] public int DiffsingerSpeedup { get; set; }
[Reactive] public bool HighThreads { get; set; }
[Reactive] public int Theme { get; set; }
[Reactive] public int DegreeStyle { get; set; }
[Reactive] public bool UseTrackColor { get; set; }
[Reactive] public bool ShowPortrait { get; set; }
[Reactive] public bool ShowGhostNotes { get; set; }
Expand Down Expand Up @@ -136,6 +137,7 @@ public PreferencesViewModel() {
DiffSingerDepth = Preferences.Default.DiffSingerDepth;
DiffsingerSpeedup = Preferences.Default.DiffsingerSpeedup;
Theme = Preferences.Default.Theme;
DegreeStyle = Preferences.Default.DegreeStyle;
UseTrackColor = Preferences.Default.UseTrackColor;
ShowPortrait = Preferences.Default.ShowPortrait;
ShowGhostNotes = Preferences.Default.ShowGhostNotes;
Expand Down Expand Up @@ -207,6 +209,11 @@ public PreferencesViewModel() {
Preferences.Save();
App.SetTheme();
});
this.WhenAnyValue(vm => vm.DegreeStyle)
.Subscribe(degreeStyle => {
Preferences.Default.DegreeStyle = degreeStyle;
Preferences.Save();
});
this.WhenAnyValue(vm => vm.UseTrackColor)
.Subscribe(trackColor => {
Preferences.Default.UseTrackColor = trackColor;
Expand Down
14 changes: 14 additions & 0 deletions OpenUtau/Views/PianoRollWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,20 @@
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Margin="0" Padding="0,1" Height="20" Width="40" Background="Transparent" Click="OnKeyMenuButton">
<TextBlock Grid.Column="0" Text="{Binding NotesViewModel.KeyText}"
TextAlignment="Right" FontSize="10" FontFamily="monospace"
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
<Button.ContextMenu>
<ContextMenu Name="KeyMenu" PlacementMode="Bottom" ItemsSource="{Binding NotesViewModel.Keys}" KeyDown="OnKeyKeyDown">
<ContextMenu.DataTemplates>
<DataTemplate DataType="vm:MenuItemViewModel">
<MenuItem Header="{Binding Header}" Command="{Binding Command, FallbackValue={x:Null}}" CommandParameter="{Binding CommandParameter}"/>
</DataTemplate>
</ContextMenu.DataTemplates>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Border>
</StackPanel>
Expand Down
13 changes: 13 additions & 0 deletions OpenUtau/Views/PianoRollWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,19 @@ void OnSnapDivKeyDown(object sender, KeyEventArgs e) {
}
}

public void OnKeyMenuButton(object sender, RoutedEventArgs args) {
KeyMenu.PlacementTarget = sender as Button;
KeyMenu.Open();
}

void OnKeyKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter && e.KeyModifiers == KeyModifiers.None) {
if (sender is ContextMenu menu && menu.SelectedItem is MenuItemViewModel item) {
item.Command?.Execute(item.CommandParameter);
}
}
}

#region value tip

void IValueTip.ShowValueTip() {
Expand Down
6 changes: 6 additions & 0 deletions OpenUtau/Views/PreferencesDialog.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@
<ComboBoxItem Content="{DynamicResource prefs.appearance.theme.dark}"/>
</ComboBox>
<TextBlock Classes="restart"/>
<TextBlock Text="{DynamicResource prefs.appearance.degree}" Margin="0,10,0,0"/>
<ComboBox SelectedIndex="{Binding DegreeStyle}">
<ComboBoxItem Content="{DynamicResource prefs.appearance.degree.off}"/>
<ComboBoxItem Content="{DynamicResource prefs.appearance.degree.solfege}"/>
<ComboBoxItem Content="{DynamicResource prefs.appearance.degree.numbered}"/>
</ComboBox>
<Grid Margin="0,5,0,0">
<TextBlock Text="{DynamicResource prefs.appearance.trackcolor}" HorizontalAlignment="Left"/>
<ToggleSwitch IsChecked="{Binding UseTrackColor}"/>
Expand Down

0 comments on commit 282a583

Please sign in to comment.