private List <int> BuildPitchData(UPhoneme phoneme, UVoicePart part, UProject project) { int leftBound = phoneme.Parent.position + phoneme.position - project.MillisecondToTick(phoneme.preutter); int rightBound = phoneme.Parent.position + phoneme.position + phoneme.Duration - project.MillisecondToTick(phoneme.tailIntrude - phoneme.tailOverlap); var leftNote = phoneme.Parent; var rightNote = phoneme.Parent; bool oneMore = true; while ((leftBound < leftNote.RightBound || oneMore) && leftNote.Prev != null && leftNote.Prev.End == leftNote.position) { leftNote = leftNote.Prev; if (leftBound >= leftNote.RightBound) { oneMore = false; } } oneMore = true; while ((rightBound > rightNote.LeftBound || oneMore) && rightNote.Next != null && rightNote.Next.position == rightNote.End) { rightNote = rightNote.Next; if (rightBound <= rightNote.LeftBound) { oneMore = false; } } // Collect pitch curve and vibratos. var points = new List <PitchPoint>(); var vibratos = new List <Tuple <double, double, UVibrato> >(); var note = leftNote; float vel = Velocity; var strechRatio = Math.Pow(2, 1.0 - vel / 100); float correction = (float)(phoneme.oto.Preutter * (strechRatio - 1)); while (true) { var offsetMs = (float)project.TickToMillisecond(note.position - phoneme.Parent.position); foreach (var point in note.pitch.data) { var newpp = point.Clone(); newpp.X += offsetMs + correction; newpp.Y -= (phoneme.Parent.tone - note.tone) * 10; points.Add(newpp); } if (note.vibrato.length != 0) { double vibratoStartMs = project.TickToMillisecond(note.position + note.duration * (1 - note.vibrato.length / 100) - phoneme.Parent.position); double vibratoEndMs = project.TickToMillisecond(note.End - phoneme.Parent.position); vibratos.Add(Tuple.Create(vibratoStartMs, vibratoEndMs, note.vibrato)); } if (note == rightNote) { break; } note = note.Next; } // Expand curve if necessary. float startMs = (float)(project.TickToMillisecond(phoneme.position) - phoneme.oto.Preutter); float endMs = (float)(project.TickToMillisecond(phoneme.End) - phoneme.tailIntrude + phoneme.tailOverlap); if (points.First().X > startMs) { points.Insert(0, new PitchPoint(startMs, points.First().Y)); } if (points.Last().X < endMs) { points.Add(new PitchPoint(endMs, points.Last().Y)); } // Interpolation. var pitches = new List <int>(); const int intervalTick = 5; float intervalMs = (float)project.TickToMillisecond(intervalTick); float currMs = startMs; int i = 0; int vibrato = 0; while (currMs < endMs) { while (points[i + 1].X < currMs) { i++; } var pit = MusicMath.InterpolateShape(points[i].X, points[i + 1].X, points[i].Y, points[i + 1].Y, currMs, points[i].shape) * 10; while (vibrato < vibratos.Count - 1 && vibratos[vibrato].Item2 < currMs) { vibrato++; } if (vibrato < vibratos.Count && vibratos[vibrato].Item1 <= currMs && currMs < vibratos[vibrato].Item2) { pit += InterpolateVibrato(vibratos[vibrato].Item3, currMs - vibratos[vibrato].Item1, vibratos[vibrato].Item2 - vibratos[vibrato].Item1, project); } pitches.Add((int)pit); currMs += intervalMs; } return(pitches); }