public void SetExpression(UProject project, string abbr, float value) { var descriptor = project.expressions[abbr]; var note = Parent.Extends ?? Parent; int index = Parent.PhonemeOffset + Index; if (descriptor.defaultValue == value) { note.phonemeExpressions.RemoveAll(exp => exp.descriptor == descriptor && exp.index == index); return; } var expression = note.phonemeExpressions.FirstOrDefault(exp => exp.descriptor == descriptor && exp.index == index); if (expression != null) { expression.value = value; } else { note.phonemeExpressions.Add(new UExpression(descriptor) { descriptor = descriptor, index = index, value = value, }); } }
public override void BeforeSave(UProject project, UTrack track) { foreach (var note in notes) { note.BeforeSave(project, track, this); } }
public string GetResamplerFlags(UProject project) { StringBuilder builder = new StringBuilder(); foreach (var descriptor in project.expressions.Values) { if (descriptor.type == UExpressionType.Numerical) { if (!string.IsNullOrEmpty(descriptor.flag)) { builder.Append(descriptor.flag); int value = (int)GetExpression(project, descriptor.abbr).Item1; builder.Append(value); } } if (descriptor.type == UExpressionType.Options) { if (descriptor.isFlag) { int value = (int)GetExpression(project, descriptor.abbr).Item1; builder.Append(descriptor.options[value]); } } } return(builder.ToString()); }
public override void AfterLoad(UProject project, UTrack track) { foreach (var note in notes) { note.AfterLoad(project, track, this); } Duration = GetBarDurTick(project); }
public void Validate(UProject project, UTrack track, UVoicePart part, UNote note) { Error = note.Error; ValidateDuration(note); ValidateOto(track, note); ValidateOverlap(project, note); ValidateEnvelope(project, note); }
public void BeforeSave(UProject project, UTrack track, UVoicePart part) { noteExpressions.ForEach(exp => exp.index = null); noteExpressions = noteExpressions .OrderBy(exp => exp.abbr) .ToList(); phonemeExpressions = phonemeExpressions .OrderBy(exp => exp.index) .ThenBy(exp => exp.abbr) .ToList(); }
public void AfterLoad(UProject project, UTrack track, UVoicePart part) { foreach (var exp in noteExpressions) { exp.descriptor = project.expressions[exp.abbr]; } foreach (var exp in phonemeExpressions) { exp.descriptor = project.expressions[exp.abbr]; } }
public override void Validate(UProject project, UTrack track) { UNote lastNote = null; foreach (UNote note in notes) { note.Prev = lastNote; note.Next = null; if (lastNote != null) { lastNote.Next = note; } lastNote = note; } foreach (UNote note in notes) { note.ExtendedDuration = note.duration; if (note.Prev != null && note.Prev.End == note.position && note.lyric.StartsWith("...")) { note.Extends = note.Prev.Extends ?? note.Prev; note.Extends.ExtendedDuration = note.End - note.Extends.position; } else { note.Extends = null; } } foreach (UNote note in notes.Reverse()) { note.Phonemize(project, track); } UPhoneme lastPhoneme = null; foreach (UNote note in notes) { foreach (var phoneme in note.phonemes) { phoneme.Parent = note; phoneme.Prev = lastPhoneme; phoneme.Next = null; if (lastPhoneme != null) { lastPhoneme.Next = phoneme; } lastPhoneme = phoneme; } } foreach (UNote note in notes) { note.Validate(project, track, this); } }
public Tuple <float, bool> GetExpression(UProject project, string abbr) { var descriptor = project.expressions[abbr]; var note = Parent.Extends ?? Parent; int index = Parent.PhonemeOffset + Index; var expression = note.phonemeExpressions.FirstOrDefault(exp => exp.descriptor == descriptor && exp.index == index); if (expression != null) { return(Tuple.Create(expression.value, true)); } else { return(Tuple.Create(descriptor.defaultValue, false)); } }
void ValidateOverlap(UProject project, UNote note) { if (Error) { return; } float consonantStretch = (float)Math.Pow(2f, 1.0f - GetExpression(project, "vel").Item1 / 100f); overlap = (float)oto.Overlap * consonantStretch * (overlapScale ?? 1); preutter = (float)oto.Preutter * consonantStretch * (preutterScale ?? 1); overlapped = false; if (Prev == null) { return; } int gapTick = Parent.position + position - (Prev.Parent.position + Prev.End); float gapMs = (float)project.TickToMillisecond(gapTick); float maxPreutter = preutter; if (gapMs <= 0) { // Keep at least half of last phoneme, or 10% if preutterScale is set. overlapped = true; maxPreutter = (float)project.TickToMillisecond(Prev.Duration) * (preutterScale == null ? 0.5f : 0.9f); } else if (gapMs < preutter) { maxPreutter = gapMs; } if (preutter > maxPreutter) { float ratio = maxPreutter / preutter; preutter = maxPreutter; overlap *= ratio; } preutter = Math.Max(0, preutter); overlap = Math.Min(overlap, preutter); Prev.tailIntrude = overlapped ? preutter : 0; Prev.tailOverlap = overlapped ? overlap : 0; Prev.ValidateEnvelope(project, Prev.Parent); if (Next == null) { tailIntrude = 0; tailOverlap = 0; } }
void ValidateEnvelope(UProject project, UNote note) { if (Error) { return; } var vol = GetExpression(project, "vol").Item1; var atk = GetExpression(project, "atk").Item1; var dec = GetExpression(project, "dec").Item1; Vector2 p0, p1, p2, p3, p4; p0.X = -preutter; p1.X = p0.X + (overlapped ? overlap : 5f); p2.X = Math.Max(0f, p1.X); p3.X = (float)project.TickToMillisecond(Duration) - (float)tailIntrude; p4.X = p3.X + (float)tailOverlap; if (p3.X == p4.X) { p3.X = Math.Max(p2.X, p3.X - 25f); } p0.Y = 0f; p1.Y = vol; p1.X = p0.X + (overlapped ? overlap : 5f); p1.Y = atk * vol / 100f; p2.Y = vol; p3.Y = vol * (1f - dec / 100f); p4.Y = 0f; envelope.data[0] = p0; envelope.data[1] = p1; envelope.data[2] = p2; envelope.data[3] = p3; envelope.data[4] = p4; }
public abstract int GetMinDurTick(UProject project);
public virtual void AfterLoad(UProject project, UTrack track) { }
public virtual void Validate(UProject project, UTrack track) { }
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); }
public void Validate(UProject project, UTrack track, UVoicePart part) { duration = Math.Max(10, duration); if (Prev != null && Prev.End > position) { Error = true; OverlapError = true; return; } Error = false; OverlapError = false; if (track.Singer == null || !track.Singer.Loaded) { Error |= true; } if (pitch.snapFirst) { if (Prev != null && Prev.End == position) { pitch.data[0].Y = (Prev.tone - tone) * 10; } else { pitch.data[0].Y = 0; } } for (var i = 0; i < phonemes.Count; i++) { var phoneme = phonemes[i]; phoneme.Parent = this; phoneme.Index = i; } foreach (var phoneme in phonemes) { phoneme.Validate(project, track, part, this); Error |= phoneme.Error; } // Update has override bits. foreach (var phoneme in phonemes) { phoneme.HasPhonemeOverride = false; phoneme.HasOffsetOverride = false; phoneme.preutterScale = null; phoneme.overlapScale = null; } foreach (var o in (Extends ?? this).phonemeOverrides) { int index = o.index - PhonemeOffset; if (index >= 0 && index < phonemes.Count) { if (o.phoneme != null) { phonemes[index].HasPhonemeOverride = true; } if (o.offset != null) { phonemes[index].HasOffsetOverride = true; } phonemes[index].preutterScale = o.preutterScale; phonemes[index].overlapScale = o.overlapScale; } } }
public virtual void BeforeSave(UProject project, UTrack track) { }
public override int GetMinDurTick(UProject project) { return(notes.Count > 0 ? Math.Max(project.BarTicks, notes.Last().End) : project.BarTicks); }
public int GetBarDurTick(UProject project) { int barTicks = project.BarTicks; return((int)Math.Ceiling((double)GetMinDurTick(project) / barTicks) * barTicks); }