예제 #1
0
        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);
        }