public void Phonemize(UProject project, UTrack track) { if (track.Singer == null || !track.Singer.Loaded) { return; } if (Extends != null) { return; } List <UNote> notes = new List <UNote> { this }; while (notes.Last().Next != null && notes.Last().Next.Extends == this) { notes.Add(notes.Last().Next); } int endOffset = 0; if (notes.Last().Next != null && notes.Last().Next.phonemes.Count > 0) { endOffset = Math.Min(0, notes.Last().Next.position - notes.Last().End + notes.Last().Next.phonemes[0].position); } var prev = Prev?.ToProcessorNote(); var next = notes.Last().Next?.ToProcessorNote(); bool prevIsNeighbour = Prev?.End >= position; if (Prev?.Extends != null) { prev = Prev.Extends.ToProcessorNote(); var phoneme = prev.Value; phoneme.duration = Prev.ExtendedDuration; prev = phoneme; } bool nextIsNeighbour = notes.Last().End >= notes.Last().Next?.position; track.Phonemizer.SetTiming(project.bpm, project.beatUnit, project.resolution); var phonemizerNotes = notes.Select(note => note.ToProcessorNote()).ToArray(); phonemizerNotes[phonemizerNotes.Length - 1].duration += endOffset; if (string.IsNullOrEmpty(phonemizerNotes[0].lyric) && string.IsNullOrEmpty(phonemizerNotes[0].phoneticHint)) { phonemes.Clear(); return; } Phonemizer.Result phonemizerResult; try { phonemizerResult = track.Phonemizer.Process( phonemizerNotes, prev, next, prevIsNeighbour ? prev : null, nextIsNeighbour ? next : null); } catch (Exception e) { Log.Error(e, "phonemizer error"); phonemizerResult = new Phonemizer.Result() { phonemes = new Phonemizer.Phoneme[] { new Phonemizer.Phoneme { phoneme = "error" } } }; } var newPhonemes = phonemizerResult.phonemes; // Apply overrides. for (int i = phonemeOverrides.Count - 1; i >= 0; --i) { if (phonemeOverrides[i].IsEmpty) { phonemeOverrides.RemoveAt(i); } } foreach (var o in phonemeOverrides) { if (o.index >= 0 && o.index < newPhonemes.Length) { var p = newPhonemes[o.index]; if (o.phoneme != null) { p.phoneme = o.phoneme; } if (o.offset != null) { p.position += o.offset.Value; } newPhonemes[o.index] = p; } } // Safety treatment after phonemizer output and phoneme overrides. int maxPostion = notes.Last().End - notes.First().position + endOffset - 10; for (int i = newPhonemes.Length - 1; i >= 0; --i) { var p = newPhonemes[i]; p.position = Math.Min(p.position, maxPostion); newPhonemes[i] = p; maxPostion = p.position - 10; } DistributePhonemes(notes, newPhonemes); }