diff --git a/OpenUtau.Core/Editing/NoteBatchEdits.cs b/OpenUtau.Core/Editing/NoteBatchEdits.cs index 06de97770..93f390f1c 100644 --- a/OpenUtau.Core/Editing/NoteBatchEdits.cs +++ b/OpenUtau.Core/Editing/NoteBatchEdits.cs @@ -332,6 +332,8 @@ PitchPointShape DetermineShape(Point start, Point middle, Point end){ * 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 * */ + //perpendicularDistance is replaced with deltaY, because the units of X and Y are different. + //result doesn't contain the last point to enhance performance in recursion List simplifyShape(List pointList, Double epsilon) { if (pointList.Count <= 2) { return pointList; @@ -362,15 +364,14 @@ List simplifyShape(List pointList, Double 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(recResults1); results.AddRange(recResults2); if (results.Count < 2) { throw new Exception("Problem assembling output"); } } else { - //Just return start and end points + //Just return the start point results.Add(pointList[0].ChangeShape(shape)); - results.Add(pointList[end]); } return results; } @@ -424,7 +425,18 @@ public void Run(UProject project, UVoicePart part, List selectedNotes, Do ).ToList(); //Reduce pitch point - points = simplifyShape(points, 10); + var mustIncludeIndices = phrase.notes + .SelectMany(n => new[] { + n.position, + n.duration>160 ? n.end-80 : n.position+n.duration/2 }) + .Select(t=>(t-pitchStart)/pitchInterval) + .Prepend(0) + .Append(points.Count-1) + .ToList(); + //pairwise(mustIncludePointIndices) + points = mustIncludeIndices.Zip(mustIncludeIndices.Skip(1), + (a, b) => simplifyShape(points.GetRange(a,b-a),10)) + .SelectMany(x=>x).Append(points[^1]).ToList(); //determine where to distribute pitch point int idx = 0; @@ -459,7 +471,7 @@ public void Run(UProject project, UVoicePart part, List selectedNotes, Do } } adjusted_boundaries[^1] = note_boundaries[^1]; - //distribute pitch point + //distribute pitch point to each note 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)) @@ -476,7 +488,8 @@ public void Run(UProject project, UVoicePart part, List selectedNotes, Do } } docManager.StartUndoGroup(true); - foreach(var note in selectedNotes) { + //Apply pitch points to notes + foreach(var note in notes) { if (pitchPointsPerNote.TryGetValue(note.position, out var tickRangeAndPitch)) { var pitch = tickRangeAndPitch.Item3; docManager.ExecuteCmd(new ResetPitchPointsCommand(part, note)); @@ -492,21 +505,23 @@ public void Run(UProject project, UVoicePart part, List selectedNotes, Do } } - foreach(var note in selectedNotes) { + //Erase PITD curve that has been converted to pitch points + foreach(var note in notes) { if (pitchPointsPerNote.TryGetValue(note.position, out var tickRangeAndPitch)) { + var start = tickRangeAndPitch.Item1 - part.position; + var end = tickRangeAndPitch.Item2 - part.position; docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD, - tickRangeAndPitch.Item1, 0, - tickRangeAndPitch.Item1, 0)); + start, 0, + start, 0)); docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD, - tickRangeAndPitch.Item2, 0, - tickRangeAndPitch.Item2, 0)); + end, 0, + end, 0)); docManager.ExecuteCmd(new SetCurveCommand(project, part, Format.Ustx.PITD, - tickRangeAndPitch.Item1, 0, - tickRangeAndPitch.Item2, 0)); + start, 0, + end, 0)); } } docManager.EndUndoGroup(); - } } }