Skip to content

Commit

Permalink
Merge pull request #38 from oxygen-dioxide/diffsinger
Browse files Browse the repository at this point in the history
Diffsinger
  • Loading branch information
oxygen-dioxide authored Apr 25, 2023
2 parents cd23a14 + e3e0562 commit 4d0bde0
Show file tree
Hide file tree
Showing 79 changed files with 37,699 additions and 75 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,4 @@ OpenUtau.Test/Usts/*
*.dmg
appcast.*.xml
*.tar.gz
.vscode/
14 changes: 10 additions & 4 deletions OpenUtau.Core/Api/Phonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,20 @@ public static IList<string> ToUnicodeElements(string lyric) {
return result;
}

public bool Testing { get; set; } = false;

protected void OnAsyncInitStarted() {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, "Initializing phonemizer..."));
if (!Testing) {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, "Initializing phonemizer..."));
}
}

protected void OnAsyncInitFinished() {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, ""));
DocManager.Inst.ExecuteCmd(new ValidateProjectNotification());
DocManager.Inst.ExecuteCmd(new PreRenderNotification());
if (!Testing) {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, ""));
DocManager.Inst.ExecuteCmd(new ValidateProjectNotification());
DocManager.Inst.ExecuteCmd(new PreRenderNotification());
}
}

protected Result MakeSimpleResult(string phoneme) {
Expand Down
15 changes: 15 additions & 0 deletions OpenUtau.Core/Classic/Ini.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,20 @@ public static List<IniBlock> ReadBlocks(StreamReader reader, string file, string
}
return blocks;
}

public static bool TryGetLines(List<IniBlock> blocks, string header, out List<IniLine> lines) {
if (blocks.Any(block => block.header == header)) {
lines = blocks.Find(block => block.header == header).lines;
if (lines == null || lines.Count <= 0) {
return false;
} else {
return true;
}
} else {
lines = null;
return false;
}

}
}
}
700 changes: 700 additions & 0 deletions OpenUtau.Core/Classic/Presamp.cs

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions OpenUtau.Core/Classic/Ust.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.Globalization;
using System.IO;
Expand Down Expand Up @@ -169,7 +169,12 @@ private static void ParseSetting(UProject project, List<IniLine> lines) {
}

private static void ParseNote(UNote note, int lastNotePos, int lastNoteEnd, List<IniLine> iniLines, out float? noteTempo) {
var ustNote = new UstNote();
var ustNote = new UstNote {
lyric = note.lyric,
position = note.position,
duration = note.duration,
noteNum = note.tone
};
ustNote.Parse(lastNotePos, lastNoteEnd, iniLines, out noteTempo);
note.lyric = ustNote.lyric;
note.position = ustNote.position;
Expand Down
20 changes: 16 additions & 4 deletions OpenUtau.Core/Classic/VoicebankLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,16 +207,28 @@ public static void ApplyConfig(Voicebank bank, VoicebankConfig bankConfig) {
public static void LoadPrefixMap(Voicebank voicebank) {
var dir = Path.GetDirectoryName(voicebank.File);
var filePath = Path.Combine(dir, "prefix.map");
if (!File.Exists(filePath)) {
return;
if (File.Exists(filePath)) {
LoadMap(voicebank, filePath, "");
}

// Append.map for presamp
var mapDir = Path.Combine(dir, "prefix");
if (Directory.Exists(mapDir)) {
var maps = Directory.EnumerateFiles(mapDir, "*.map");
foreach (string mapPath in maps) {
LoadMap(voicebank, mapPath, Path.GetFileNameWithoutExtension(mapPath));
}
}
}
public static void LoadMap(Voicebank voicebank, string filePath, string color) {
try {
using (var stream = File.OpenRead(filePath)) {
var map = ParsePrefixMap(stream, voicebank.TextFileEncoding);
foreach (var kv in map) {
var subbank = new Subbank() {
Color = color,
Prefix = kv.Key.Item1,
Suffix = kv.Key.Item2,
Suffix = color + kv.Key.Item2,
};
var toneRanges = new List<string>();
int? rangeStart = null;
Expand Down Expand Up @@ -245,8 +257,8 @@ public static void LoadPrefixMap(Voicebank voicebank) {
} catch (Exception e) {
Log.Error(e, $"Failed to load {filePath}");
}
}

}
public static Dictionary<Tuple<string, string>, SortedSet<int>> ParsePrefixMap(Stream stream, Encoding encoding) {
using (var reader = new StreamReader(stream, encoding)) {
var result = new Dictionary<Tuple<string, string>, SortedSet<int>>();
Expand Down
6 changes: 4 additions & 2 deletions OpenUtau.Core/Commands/ExpCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public override void Execute() {
}
public override void Unexecute() {
phoneme.SetExpression(project, track, Key, oldValue);
}
}
}

public class ResetExpressionsCommand : ExpCommand {
Expand Down Expand Up @@ -245,7 +245,9 @@ public override void Unexecute() {
curve.ys.AddRange(oldYs);
}
}
public override bool Mergeable => true;
public override bool CanMerge(IList<UCommand> commands) {
return commands.All(c => c is SetCurveCommand);
}
public override UCommand Merge(IList<UCommand> commands) {
var first = commands.First() as SetCurveCommand;
var last = commands.Last() as SetCurveCommand;
Expand Down
9 changes: 5 additions & 4 deletions OpenUtau.Core/Commands/UCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;

namespace OpenUtau.Core {
Expand All @@ -7,8 +8,8 @@ public abstract class UCommand {
public virtual ValidateOptions ValidateOptions => default;
public abstract void Execute();
public abstract void Unexecute();
public virtual bool Mergeable => false;
public virtual UCommand Merge(IList<UCommand> commands) => null;
public virtual bool CanMerge(IList<UCommand> commands) => false;
public virtual UCommand Merge(IList<UCommand> commands) => throw new NotImplementedException();
public abstract override string ToString();
}

Expand All @@ -20,7 +21,7 @@ public UCommandGroup(bool deferValidate) {
Commands = new List<UCommand>();
}
public void Merge() {
if (Commands.Count > 0 && Commands.Last().Mergeable) {
if (Commands.Count > 0 && Commands.Last().CanMerge(Commands)) {
var merged = Commands.Last().Merge(Commands);
Commands.Clear();
Commands.Add(merged);
Expand Down
226 changes: 226 additions & 0 deletions OpenUtau.Core/Editing/NoteBatchEdits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,230 @@ public void Run(UProject project, UVoicePart part, List<UNote> selectedNotes, Do
docManager.EndUndoGroup();
}
}

public class BakePitch: BatchEdit {
public virtual string Name => name;
private string name;
public BakePitch() {
name = "pianoroll.menu.notes.bakepitch";
}

struct Point{
public int X;
public double Y;
public PitchPointShape shape;
public Point(int X, double Y, PitchPointShape shape = PitchPointShape.l) {
this.X = X;
this.Y = Y;
this.shape = shape;
}

public Point ChangeShape(PitchPointShape shape) {
return new Point(X, Y, shape);
}
}

double deltaY(Point pt, Point lineStart, Point lineEnd, PitchPointShape shape){
return pt.Y - MusicMath.InterpolateShape(lineStart.X, lineEnd.X, lineStart.Y, lineEnd.Y, pt.X, shape);
}

PitchPointShape DetermineShape(Point start, Point middle, Point end){
if(start.Y==end.Y){
return PitchPointShape.l;
}
var k = (middle.Y-start.Y)/(end.Y-start.Y);
if(k > 0.67){
return PitchPointShape.o;
}
if(k < 0.33){
return PitchPointShape.i;
}
return PitchPointShape.l;
}

//reference: https://github.com/sdercolin/utaformatix3/blob/0f026f7024386ca8362972043c3471c6f2ac9859/src/main/kotlin/process/RdpSimplification.kt#L43
/*
* The Ramer–Douglas–Peucker algorithm is a line simplification algorithm
* for reducing the number of points used to define its shape.
*
* Wikipedia: https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
* Implementation reference: https://rosettacode.org/wiki/Ramer-Douglas-Peucker_line_simplification
* */
List<Point> simplifyShape(List<Point> pointList, Double epsilon) {
if (pointList.Count <= 2) {
return pointList;
}

// Determine line shape
var middlePoint = pointList[pointList.Count / 2];
var startPoint = pointList[0];
var endPoint = pointList[^1];
var shape = DetermineShape(startPoint, middlePoint, endPoint);

// Find the point with the maximum distance from line between start and end
var dmax = 0.0;
var index = 0;
var end = pointList.Count - 1;
for (var i = 1; i < end; i++) {
var d = Math.Abs(deltaY(pointList[i], pointList[0], pointList[end], shape));
if (d > dmax) {
index = i;
dmax = d;
}
}
// If max distance is greater than epsilon, recursively simplify
List<Point> results = new List<Point>();
if (dmax > epsilon) {
// Recursive call
var recResults1 = simplifyShape(pointList.GetRange(0, index + 1), epsilon);
var recResults2 = simplifyShape(pointList.GetRange(index, pointList.Count - index), epsilon);

// Build the result list
results.AddRange(recResults1.GetRange(0, recResults1.Count - 1));
results.AddRange(recResults2);
if (results.Count < 2) {
throw new Exception("Problem assembling output");
}
} else {
//Just return start and end points
results.Add(pointList[0].ChangeShape(shape));
results.Add(pointList[end]);
}
return results;
}

public static int LastIndexOfMin<T>(IList<T> self, Func<T, double> selector, int startIndex, int endIndex)
{
if (self == null) {
throw new ArgumentNullException("self");
}

if (self.Count == 0) {
throw new ArgumentException("List is empty.", "self");
}

var min = selector(self[endIndex-1]);
int minIndex = endIndex - 1;

for (int i = endIndex - 1; i >= startIndex; --i) {
var value = selector(self[i]);
if (value < min) {
min = value;
minIndex = i;
}
}

return minIndex;
}

public void Run(UProject project, UVoicePart part, List<UNote> selectedNotes, DocManager docManager) {
TimeAxis timeAxis = project.timeAxis;
const int pitchInterval = 5;
var notes = selectedNotes.Count > 0 ? selectedNotes : part.notes.ToList();
var positions = notes.Select(n => n.position + part.position).ToHashSet();
var phrases = part.renderPhrases.Where(phrase => phrase.notes.Any(n => positions.Contains(phrase.position + n.position)));
float minPitD = -1200;
if (project.expressions.TryGetValue(Format.Ustx.PITD, out var descriptor)) {
minPitD = descriptor.min;
}
//Dictionary from note start tick to pitch point
//value is a tuple of (starttick, endtick, pitch points)
//Here starttick and endtick are project absolute tick, and pitch points are ms relative to the starttick
var pitchPointsPerNote = new Dictionary<int, Tuple<int,int,List<PitchPoint>>>();
foreach (var phrase in phrases) {
var pitchStart = -phrase.leading;
//var ticks = Enumerable.Range(0, phrase.duration).Select(i => i * 5).ToArray();
var pitches = phrase.pitches;
var points = Enumerable.Zip(
Enumerable.Range(0, pitches.Length),
pitches,
(i, pitch) => new Point(pitchStart + i * pitchInterval, pitch)
).ToList();

//Reduce pitch point
points = simplifyShape(points, 10);

//determine where to distribute pitch point
int idx = 0;
//note_boundary[i] is the index of the first pitch point after the end of note i
var note_boundaries = new int[phrase.notes.Length + 1];
note_boundaries[0] = 2;
foreach(int i in Enumerable.Range(0,phrase.notes.Length)) {
var note = phrase.notes[i];
while(idx<points.Count
&& points[idx].X<note.end){
idx++;
}
note_boundaries[i + 1] = idx;
}
//if there is zero point in the note, adjusted_boundaries is the index of the last zero point
//otherwise, it is the index of the pitch point with minimal y-distance to the note
var adjusted_boundaries = new int[phrase.notes.Length + 1];
adjusted_boundaries[0] = 2;
foreach(int i in Enumerable.Range(0,phrase.notes.Length - 1)){
var note = phrase.notes[i];
var notePitch = note.tone*100;
//var zero_point = points.FindIndex(note_boundaries[i], note_boundaries[i + 1] - note_boundaries[i], p => p.Y == 0);
var zero_point = Enumerable.Range(0,note_boundaries[i + 1] - note_boundaries[i])
.Select(j=>note_boundaries[i+1]-1-j)
.Where(j => (points[j].Y-notePitch) * (points[j-1].Y-notePitch) <= 0)
.DefaultIfEmpty(-1)
.First();
if(zero_point != -1){
adjusted_boundaries[i + 1] = zero_point + 1;
}else{
adjusted_boundaries[i + 1] = LastIndexOfMin(points, p => Math.Abs(p.Y - notePitch), note_boundaries[i], note_boundaries[i + 1]) + 2;
}
}
adjusted_boundaries[^1] = note_boundaries[^1];
//distribute pitch point
foreach(int i in Enumerable.Range(0,phrase.notes.Length)) {
var note = phrase.notes[i];
var pitch = points.GetRange(adjusted_boundaries[i]-2,adjusted_boundaries[i + 1]-(adjusted_boundaries[i]-2))
.Select(p => new PitchPoint(
(float)timeAxis.MsBetweenTickPos(note.position + part.position, p.X + part.position),
(float)(p.Y - note.tone * 100) / 10,
p.shape))
.ToList();
pitchPointsPerNote[note.position + phrase.position - part.position]
= Tuple.Create(
points[adjusted_boundaries[i] - 2].X + phrase.position,
points[adjusted_boundaries[i + 1] - 1].X + phrase.position,
pitch);
}
}
docManager.StartUndoGroup(true);
foreach(var note in selectedNotes) {
if (pitchPointsPerNote.TryGetValue(note.position, out var tickRangeAndPitch)) {
var pitch = tickRangeAndPitch.Item3;
docManager.ExecuteCmd(new ResetPitchPointsCommand(part, note));
int index = 0;
foreach(var point in pitch) {
docManager.ExecuteCmd(new AddPitchPointCommand(part, note, point, index));
index++;
}
docManager.ExecuteCmd(new DeletePitchPointCommand(part, note, index));
docManager.ExecuteCmd(new DeletePitchPointCommand(part, note, index));
var lastPitch = note.pitch.data[^1];
docManager.ExecuteCmd(new MovePitchPointCommand(part, lastPitch ,0, -lastPitch.Y));

}
}
foreach(var note in selectedNotes) {
if (pitchPointsPerNote.TryGetValue(note.position, out var tickRangeAndPitch)) {
docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD,
tickRangeAndPitch.Item1, 0,
tickRangeAndPitch.Item1, 0));
docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD,
tickRangeAndPitch.Item2, 0,
tickRangeAndPitch.Item2, 0));
docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD,
tickRangeAndPitch.Item1, 0,
tickRangeAndPitch.Item2, 0));
}
}
docManager.EndUndoGroup();

}
}
}
Loading

0 comments on commit 4d0bde0

Please sign in to comment.