Skip to content

Commit

Permalink
发音修改
Browse files Browse the repository at this point in the history
  • Loading branch information
LiuYunPlayer committed May 19, 2024
1 parent 3a32915 commit 00a8ee5
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 29 deletions.
1 change: 1 addition & 0 deletions TuneLab.Extensions.Formats/DataInfo/NoteInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class NoteInfo
public required double Dur { get; set; }
public required int Pitch { get; set; }
public required string Lyric { get; set; }
public string Pronunciation { get; set; } = string.Empty;
public PropertyObject Properties { get; set; } = PropertyObject.Empty;
public List<PhonemeInfo> Phonemes { get; set; } = new();
}
Expand Down
11 changes: 11 additions & 0 deletions TuneLab/Data/INote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ internal interface INote : IDataObject<NoteInfo>, ISelectable, ILinkedNode<INote
IDataProperty<double> Dur { get; }
new IDataProperty<int> Pitch { get; }
new IDataProperty<string> Lyric { get; }
IDataProperty<string> Pronunciation { get; }
new DataPropertyObject Properties { get; }
new IDataObjectList<IPhoneme> Phonemes { get; }
SynthesizedPhoneme[]? SynthesizedPhonemes { get; set; }
IReadOnlyCollection<string> Pronunciations { get; }

INote? NextInSegment { get; set; }
INote? LastInSegment { get; set; }

int ISynthesisNote.Pitch => Pitch.Value;
string ISynthesisNote.Lyric => this.FinalPronunciation() ?? Lyric.Value;
PropertyObject ISynthesisNote.Properties => new(Properties);
IReadOnlyList<SynthesizedPhoneme> ISynthesisNote.Phonemes => Phonemes.Convert(GetPhoneme);
ISynthesisNote? ISynthesisNote.Next => NextInSegment;
Expand Down Expand Up @@ -106,4 +109,12 @@ public static void LockPhonemes(this INote note)
note.Phonemes.Add(Phoneme.Create(new PhonemeInfo() { StartTime = phoneme.StartTime - startTime, EndTime = phoneme.EndTime - startTime, Symbol = phoneme.Symbol }));
}
}

public static string? FinalPronunciation(this INote note)
{
if (!string.IsNullOrEmpty(note.Pronunciation.Value))
return note.Pronunciation.Value;

return note.Pronunciations.FirstOrDefault();
}
}
2 changes: 2 additions & 0 deletions TuneLab/Data/MidiPart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ public SynthesisPiece(MidiPart part, SynthesisSegment<INote> segment)
note.Dur.Modified.Subscribe(SetDirtyAndResegment);
note.Pitch.Modified.Subscribe(SetDirtyAndResegment);
note.Lyric.Modified.Subscribe(SetDirtyAndResegment);
note.Pronunciation.Modified.Subscribe(SetDirtyAndResegment);
note.Properties.PropertyModified.Subscribe(OnNotePropertyModified);
note.Phonemes.Modified.Subscribe(OnPhonemeChanged);
}
Expand Down Expand Up @@ -787,6 +788,7 @@ public void Dispose()
note.Dur.Modified.Unsubscribe(SetDirtyAndResegment);
note.Pitch.Modified.Unsubscribe(SetDirtyAndResegment);
note.Lyric.Modified.Unsubscribe(SetDirtyAndResegment);
note.Pronunciation.Modified.Unsubscribe(SetDirtyAndResegment);
note.Properties.PropertyModified.Unsubscribe(OnNotePropertyModified);
note.Phonemes.Modified.Unsubscribe(OnPhonemeChanged);
}
Expand Down
18 changes: 10 additions & 8 deletions TuneLab/Data/Note.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class Note : DataObject, INote
public DataStruct<double> Dur { get; }
public DataStruct<int> Pitch { get; }
DataLyric Lyric { get; }
DataString Pronunciation { get; }
public DataPropertyObject Properties { get; }
public DataObjectList<IPhoneme> Phonemes { get; } = new();
public bool IsSelected { get => mIsSelected; set { if (mIsSelected == value) return; mIsSelected = value; mSelectionChanged.Invoke(); } }
Expand All @@ -35,15 +36,15 @@ internal class Note : DataObject, INote
public double EndTime => mPart.TempoManager.GetTime(Next == null ? this.GlobalEndPos() : Math.Min(this.GlobalEndPos(), Next.GlobalStartPos()));

public SynthesizedPhoneme[]? SynthesizedPhonemes { get; set; }
public IReadOnlyCollection<string> Pronunciations => Lyric.Pronunciations;

IDataProperty<double> INote.Pos => Pos;
IDataProperty<double> INote.Dur => Dur;
IDataProperty<int> INote.Pitch => Pitch;
IDataProperty<string> INote.Lyric => Lyric;
IDataProperty<string> INote.Pronunciation => Pronunciation;
IDataObjectList<IPhoneme> INote.Phonemes => Phonemes;

string ISynthesisNote.Lyric => Lyric.Pronunciation ?? Lyric.Value;

INote? ILinkedNode<INote>.Next { get; set; } = null;
INote? ILinkedNode<INote>.Last { get; set; } = null;
ILinkedList<INote>? ILinkedNode<INote>.LinkedList { get; set; }
Expand All @@ -56,6 +57,7 @@ public Note(IMidiPart part, NoteInfo info)
Dur = new(this);
Pitch = new(this);
Lyric = new(this);
Pronunciation = new(this);
Properties = new(this);
Phonemes.Attach(this);
mPart = part;
Expand All @@ -70,8 +72,9 @@ public NoteInfo GetInfo()
Dur = Dur,
Pitch = Pitch,
Lyric = Lyric,
Pronunciation = Pronunciation,
Properties = Properties.GetInfo(),
Phonemes = Phonemes.GetInfo().ToInfo()
Phonemes = Phonemes.GetInfo().ToInfo(),
};

return info;
Expand All @@ -83,32 +86,31 @@ void IDataObject<NoteInfo>.SetInfo(NoteInfo info)
IDataObject<NoteInfo>.SetInfo(Dur, info.Dur);
IDataObject<NoteInfo>.SetInfo(Pitch, info.Pitch);
IDataObject<NoteInfo>.SetInfo(Lyric, info.Lyric);
IDataObject<NoteInfo>.SetInfo(Pronunciation, info.Pronunciation);
IDataObject<NoteInfo>.SetInfo(Properties, info.Properties);
IDataObject<NoteInfo>.SetInfo(Phonemes, info.Phonemes.Convert(Phoneme.Create).ToArray());
}

class DataLyric : DataString
{
public string? Pronunciation => mPronunciation;
public IReadOnlyCollection<string> Pronunciations { get; private set; } = [];

public DataLyric(Note note) : base(note)
{
mNote = note;
Modified.Subscribe(() =>
{
var pronunciations = LyricUtils.GetPronunciations(Value);
if (!pronunciations.IsEmpty())
mPronunciation = pronunciations.First();
Pronunciations = LyricUtils.GetPronunciations(Value);
});
}

public override void Set(string value)
{
base.Set(value);
mNote.Phonemes.Clear();
mNote.Pronunciation.Set(string.Empty);
}

string? mPronunciation;
Note mNote;
}

Expand Down
2 changes: 2 additions & 0 deletions TuneLab/Extensions/Formats/TLP/TuneLabProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public ProjectInfo Deserialize(Stream streamToRead)
Dur = (int)note["dur"],
Pitch = (int)note["pitch"],
Lyric = (string)note["lyric"],
Pronunciation = (string)note["pronunciation"],
Properties = FromJson(note["properties"])
};

Expand Down Expand Up @@ -293,6 +294,7 @@ public Stream Serialize(ProjectInfo projectInfo)
note.Add("dur", noteInfo.Dur);
note.Add("pitch", noteInfo.Pitch);
note.Add("lyric", noteInfo.Lyric);
note.Add("pronunciation", noteInfo.Pronunciation);
note.Add("properties", ToJson(noteInfo.Properties));
if (!noteInfo.Phonemes.IsEmpty())
{
Expand Down
2 changes: 1 addition & 1 deletion TuneLab/Utils/LyricUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static IReadOnlyCollection<string> GetPronunciations(string lyric)
if (ChineseChar.IsValidChar(c))
{
var chineseChar = new ChineseChar(c);
return chineseChar.Pinyins.Take(chineseChar.PinyinCount).Convert(ToPinyin).ToArray();
return chineseChar.Pinyins.Take(chineseChar.PinyinCount).Convert(ToPinyin).ToHashSet();
}
}

Expand Down
12 changes: 8 additions & 4 deletions TuneLab/Views/PianoGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ protected override void OnRender(DrawingContext context)
IBrush noteBrush = NoteColor.ToBrush();
IBrush selectedNoteBrush = SelectedNoteColor.ToBrush();
IBrush lyricBrush = Colors.White.Opacity(0.7).ToBrush();
//IBrush selectedLyricBrush = GUI.Style.INTERFACE.ToBrush();
IBrush pronunciationBrush = Style.LIGHT_WHITE.ToBrush();
foreach (var note in Part.Notes)
{
if (note.GlobalEndPos() < minVisibleTick)
Expand All @@ -240,13 +240,17 @@ protected override void OnRender(DrawingContext context)
var rect = this.NoteRect(note);
context.FillRectangle(note.IsSelected ? selectedNoteBrush : noteBrush, rect, (float)round);

rect = rect.Adjusted(8, 0, -8, 0);
rect = rect.Adjusted(8, -28, -8, 0);
if (rect.Width <= 0)
continue;

var clip = context.PushClip(rect);
string text = note.Lyric.Value;
context.DrawString(text, rect, lyricBrush, 12, Alignment.LeftCenter, Alignment.LeftCenter);
context.DrawString(note.Lyric.Value, rect, lyricBrush, 12, Alignment.LeftCenter, Alignment.LeftCenter, new(0, 14));
var pronunciation = note.FinalPronunciation();
if (!string.IsNullOrEmpty(pronunciation))
{
context.DrawString(pronunciation, rect, pronunciationBrush, 12, Alignment.LeftTop, Alignment.LeftCenter, new(0, 14));
}
clip.Dispose();
}

Expand Down
35 changes: 25 additions & 10 deletions TuneLab/Views/PianoGridItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using TuneLab.Utils;
using TuneLab.Extensions.Voices;
using TuneLab.Base.Utils;
using Avalonia.Media;

namespace TuneLab.Views;

Expand All @@ -22,7 +23,7 @@ class PianoGridItem(PianoGrid pianoGrid) : Item

class NoteItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid)
{
public INote Note;
public required INote Note;

public Rect Rect()
{
Expand All @@ -37,7 +38,7 @@ public override bool Raycast(Avalonia.Point point)

class NoteStartResizeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid)
{
public INote Note;
public required INote Note;

public override bool Raycast(Avalonia.Point point)
{
Expand All @@ -58,7 +59,7 @@ public override bool Raycast(Avalonia.Point point)

class NoteEndResizeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid)
{
public INote Note;
public required INote Note;

public override bool Raycast(Avalonia.Point point)
{
Expand All @@ -71,6 +72,20 @@ public override bool Raycast(Avalonia.Point point)
}
}

class NotePronunciationItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid)
{
public required INote Note;

public override bool Raycast(Point point)
{
double left = PianoGrid.TickAxis.Tick2X(Note.GlobalStartPos());
double width = new FormattedText(Note.FinalPronunciation() ?? string.Empty, System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, 12, null).Width + 16;
double bottom = PianoGrid.PitchAxis.Pitch2Y(Note.Pitch.Value + 1);
double top = bottom - 28;
return new Rect(left, top, width, bottom - top).Contains(point);
}
}

interface IVibratoItem
{
Vibrato Vibrato { get; }
Expand Down Expand Up @@ -100,7 +115,7 @@ public Point PhasePosition()

class VibratoItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public override bool Raycast(Point point)
{
Expand All @@ -112,7 +127,7 @@ public override bool Raycast(Point point)

class VibratoAmplitudeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public override bool Raycast(Point point)
{
Expand All @@ -130,7 +145,7 @@ public override bool Raycast(Point point)

class VibratoStartResizeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public override bool Raycast(Point point)
{
Expand All @@ -141,7 +156,7 @@ public override bool Raycast(Point point)

class VibratoEndResizeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public override bool Raycast(Point point)
{
Expand All @@ -152,7 +167,7 @@ public override bool Raycast(Point point)

class VibratoFrequencyItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public Point Position()
{
Expand All @@ -171,7 +186,7 @@ public override bool Raycast(Point point)

class VibratoPhaseItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid), IVibratoItem
{
public Vibrato Vibrato { get; set; }
public required Vibrato Vibrato { get; set; }

public Point Position()
{
Expand Down Expand Up @@ -215,7 +230,7 @@ public override bool Raycast(Point point)

class WaveformPhonemeResizeItem(PianoGrid pianoGrid) : PianoGridItem(pianoGrid)
{
public INote Note;
public required INote Note;
public int PhonemeIndex;

public override bool Raycast(Point point)
Expand Down
40 changes: 34 additions & 6 deletions TuneLab/Views/PianoGridOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
using ContextMenu = Avalonia.Controls.ContextMenu;
using MenuItem = Avalonia.Controls.MenuItem;
using Avalonia.Input;
using NAudio.CoreAudioApi;
using System.Reflection;
using TuneLab.Extensions.Voices;
using TuneLab.Utils;
using TuneLab.Base.Science;
Expand Down Expand Up @@ -123,6 +121,24 @@ bool DetectWaveformPrimaryButton()
{
mNoteStartResizeOperation.Down(e.Position.X, noteStartResizeItem.Note);
}
else if (item is NotePronunciationItem notePronunciationItem)
{
var note = notePronunciationItem.Note;
if (!note.Pronunciations.IsEmpty())
{
var menu = new ContextMenu();
foreach (var pronunciation in note.Pronunciations)
{
var menuItem = new MenuItem().SetName(pronunciation).SetAction(() =>
{
note.Pronunciation.Set(pronunciation);
note.Commit();
});
menu.Items.Add(menuItem);
}
menu.Open(this);
}
}
else
{
if (e.IsDoubleClick)
Expand Down Expand Up @@ -163,6 +179,8 @@ bool DetectWaveformPrimaryButton()
var menuItem = new MenuItem().SetName("Cut").SetAction(Cut).SetInputGesture(new KeyGesture(Key.X, KeyModifiers.Control));
menu.Items.Add(menuItem);
}

menu.Items.Add(new Avalonia.Controls.Separator());
var position = e.Position;
var splitPos = TickAxis.X2Tick(position.X);
if (!alt) splitPos = GetQuantizedTick(splitPos);
Expand Down Expand Up @@ -218,10 +236,8 @@ bool DetectWaveformPrimaryButton()
});
menu.Items.Add(menuItem);
}
{
var menuItem = new MenuItem().SetName("Input Lyrics").SetAction(() => { LyricInput.EnterInput(Part.Notes.AllSelectedItems()); });
menu.Items.Add(menuItem);
}

menu.Items.Add(new Avalonia.Controls.Separator());
{
var menuItem = new MenuItem().SetName("Octave Up").SetAction(OctaveUp).SetInputGesture(new KeyGesture(Key.Up, KeyModifiers.Shift));
menu.Items.Add(menuItem);
Expand All @@ -230,6 +246,8 @@ bool DetectWaveformPrimaryButton()
var menuItem = new MenuItem().SetName("Octave Down").SetAction(OctaveDown).SetInputGesture(new KeyGesture(Key.Down, KeyModifiers.Shift));
menu.Items.Add(menuItem);
}

menu.Items.Add(new Avalonia.Controls.Separator());
if (note.Next != null)
{
var menuItem = new MenuItem().SetName("Move Lyrics Forward").SetAction(() =>
Expand Down Expand Up @@ -265,6 +283,12 @@ bool DetectWaveformPrimaryButton()
});
menu.Items.Add(menuItem);
}
{
var menuItem = new MenuItem().SetName("Input Lyrics").SetAction(() => { LyricInput.EnterInput(Part.Notes.AllSelectedItems()); });
menu.Items.Add(menuItem);
}

menu.Items.Add(new Avalonia.Controls.Separator());
{
var menuItem = new MenuItem().SetName("Delete").SetAction(Delete).SetInputGesture(new KeyGesture(Key.Delete));
menu.Items.Add(menuItem);
Expand Down Expand Up @@ -803,6 +827,10 @@ protected override void UpdateItems(IItemCollection items)
break;

items.Add(new NoteItem(this) { Note = note });
if (!string.IsNullOrEmpty(note.FinalPronunciation()))
{
items.Add(new NotePronunciationItem(this) { Note = note });
}
items.Add(new NoteEndResizeItem(this) { Note = note });
items.Add(new NoteStartResizeItem(this) { Note = note });
}
Expand Down

0 comments on commit 00a8ee5

Please sign in to comment.