From 9c07e906faca1cfaeaf1f824fa29fe33a41a041b Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Sat, 4 Oct 2025 15:59:18 +0800 Subject: [PATCH 1/7] Instance-based dictionary --- OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs b/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs index 2d79fa9ca..cda27915b 100644 --- a/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs @@ -233,7 +233,7 @@ public override void SetSinger(USinger singer) { protected bool isDictionaryLoading => dictionaries[GetType()] == null; protected double TransitionBasicLengthMs => 100; - private static Dictionary dictionaries = new Dictionary(); + private Dictionary dictionaries = new Dictionary(); private const string FORCED_ALIAS_SYMBOL = "?"; private string error = ""; private readonly string[] wordSeparators = new[] { " ", "_" }; From ff17204711b29e5ff801833a1585adddaf413de9 Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Thu, 19 Mar 2026 11:43:58 +0800 Subject: [PATCH 2/7] SBP Update --- .../SyllableBasedPhonemizer.cs | 144 +++++++++++++++--- 1 file changed, 120 insertions(+), 24 deletions(-) diff --git a/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs b/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs index cda27915b..1fa367070 100644 --- a/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs @@ -160,14 +160,19 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN } var phonemes = new List(); + int globalPhonemeIndex = 0; // Track the exact index for OpenUtau's UI + foreach (var syllable in syllables) { - phonemes.AddRange(MakePhonemes(ProcessSyllable(syllable), syllable.duration, syllable.position, false)); + var syllablePhonemes = ProcessSyllable(syllable); + phonemes.AddRange(MakePhonemes(syllablePhonemes, syllable.duration, syllable.position, false, syllable.tone, notes[0].phonemeAttributes, globalPhonemeIndex)); + globalPhonemeIndex += syllablePhonemes.Count; } if (!nextNeighbour.HasValue) { var tryEnding = MakeEnding(notes); if (tryEnding.HasValue) { var ending = tryEnding.Value; - phonemes.AddRange(MakePhonemes(ProcessEnding(ending), ending.duration, ending.position, true)); + var endingPhonemes = ProcessEnding(ending); + phonemes.AddRange(MakePhonemes(endingPhonemes, ending.duration, ending.position, true, ending.tone, notes[0].phonemeAttributes, globalPhonemeIndex)); } } @@ -187,11 +192,17 @@ protected virtual Phoneme[] AssignAllAffixes(List phonemes, Note[] note while (noteIndex < notes.Length - 1 && notes[noteIndex].position - notes[0].position < phoneme.position) { noteIndex++; } - var noteStartPosition = notes[noteIndex].position - notes[0].position; - int tone = (prevs != null && prevs.Length > 0 && phoneme.position < noteStartPosition) ? - prevs.Last().tone : (noteIndex > 0 && phoneme.position < noteStartPosition) ? - notes[noteIndex - 1].tone : notes[noteIndex].tone; + var noteStartPosition = notes[noteIndex].position - notes[0].position; + int tone; + if (phoneme.position < noteStartPosition) { + tone = (noteIndex > 0) ? notes[noteIndex - 1].tone : + (prevs != null && prevs.Length > 0) ? prevs.Last().tone : + notes[noteIndex].tone; + } else { + tone = notes[noteIndex].tone; + } + var validatedAlias = phoneme.phoneme; if (validatedAlias != null) { validatedAlias = ValidateAliasIfNeeded(validatedAlias, tone + toneShift); @@ -304,7 +315,7 @@ string[] getSymbolsRaw(string lyrics) { foreach (var subword in note.lyric.Trim().ToLowerInvariant().Split(wordSeparators, StringSplitOptions.RemoveEmptyEntries)) { var subResult = dictionary.Query(subword); if (subResult == null) { - Log.Warning($"Subword '{subword}' from word '{note.lyric}' can't be found in the dictionary"); + //Log.Warning($"Subword '{subword}' from word '{note.lyric}' can't be found in the dictionary"); subResult = HandleWordNotFound(note); if (subResult == null) { return null; @@ -318,6 +329,16 @@ string[] getSymbolsRaw(string lyrics) { } } + /// + /// Defines whether a consonant (like a liquid or semi-vowel etc) should be placed ON the note (anchor) + /// instead of pushing backward. + /// + protected virtual bool IsGlide(string alias) { + return false; + } + + protected virtual bool NoGap => true; + /// /// Instead of changing symbols in cmudict itself for each reclist, /// you may leave it be and provide symbol replacements with this method. @@ -546,6 +567,33 @@ protected double GetTransitionBasicLengthMsByConstant() { return TransitionBasicLengthMs * GetTempoNoteLengthFactor(); } + /// + /// Uses Preutterance length + /// + protected virtual double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + return GetTransitionBasicLengthMs(alias); + } + + /// + /// OTO HELPER: Calculates transition length based on the mapped Oto's Preutterance. + /// + protected double GetTransitionBasicLengthMsByOto(string alias, int tone = 0, PhonemeAttributes attr = default) { + if (string.IsNullOrEmpty(alias)) return GetTransitionBasicLengthMsByConstant(); + + string color = attr.voiceColor ?? string.Empty; + string alt = attr.alternate?.ToString() ?? string.Empty; + int toneShift = attr.toneShift; + + var validatedAlias = ValidateAliasIfNeeded(alias, tone + toneShift); + var mappedAlias = MapPhoneme(validatedAlias, tone + toneShift, color, alt, singer); + + if (singer.TryGetMappedOto(mappedAlias, tone + toneShift, out var oto)) { + return oto.Preutter; + } + + return GetTransitionBasicLengthMsByConstant(); + } + /// /// a note length modifier, from 1 to 0.3. Used to make transition notes shorter on high tempo /// @@ -752,33 +800,76 @@ private List ExtractVowels(string[] symbols) { } return vowelIds; } - - private Phoneme[] MakePhonemes(List phonemeSymbols, int containerLength, int position, bool isEnding) { - + + private Phoneme[] MakePhonemes(List phonemeSymbols, int containerLength, int position, bool isEnding, int tone = 0, PhonemeAttributes[] attributes = null, int globalStartIndex = 0) { var phonemes = new Phoneme[phonemeSymbols.Count]; + + int[] trueLengths = new int[phonemeSymbols.Count]; + for (int i = 1; i < phonemeSymbols.Count; i++) { + var prevPhonemeI = phonemeSymbols.Count - i; + var nextGlobalIndex = globalStartIndex + prevPhonemeI; + var nextPAttr = attributes?.FirstOrDefault(a => a.index == nextGlobalIndex) ?? default; + double nextStretch = nextPAttr.consonantStretchRatio ?? 1.0; + + string nextAlias = phonemeSymbols[prevPhonemeI]; + double baseLengthMs = GetTransitionBasicLengthMs(nextAlias, tone, nextPAttr); + trueLengths[i] = MsToTick(baseLengthMs * nextStretch); + } + + // IsGlide + int anchorI = 0; + if (!isEnding) { + for (int i = 1; i < phonemeSymbols.Count; i++) { + var phonemeI = phonemeSymbols.Count - i - 1; + if (phonemeSymbols[phonemeI] != null && IsGlide(phonemeSymbols[phonemeI])) { + anchorI = i; + } else { + break; + } + } + } + for (var i = 0; i < phonemeSymbols.Count; i++) { var phonemeI = phonemeSymbols.Count - i - 1; - + var globalIndex = globalStartIndex + phonemeI; var validatedAlias = phonemeSymbols[phonemeI]; + if (validatedAlias != null) { phonemes[phonemeI].phoneme = validatedAlias; - var transitionLengthTick = MsToTick(GetTransitionBasicLengthMs(phonemes[phonemeI].phoneme)); + if (i == 0) { - if (!isEnding) { - transitionLengthTick = 0; + if (isEnding) { + var pAttr = attributes?.FirstOrDefault(a => a.index == globalIndex) ?? default; + double baseLengthMs = GetTransitionBasicLengthMs(phonemes[phonemeI].phoneme, tone, pAttr); + + if (NoGap) { + // Snapped mode: Use a visible 50-tick anchor capped at 1/3 of the note + int targetTicks = 50; + int maxAllowed = containerLength / 3; + phonemes[phonemeI].position = System.Math.Min(targetTicks, maxAllowed); + } else { + // Natural mode: Use the full Preutterance (Right Blank space) + // Useful when the endings has a sound like those VC-'s in VCCV + phonemes[phonemeI].position = MsToTick(baseLengthMs); + } } else { - transitionLengthTick *= 2; + int sum = 0; + for (int k = 1; k <= anchorI; k++) { + sum += trueLengths[k]; + } + phonemes[phonemeI].position = -sum; } + } else { + // VC transitions keep their full length. + phonemes[phonemeI].position = trueLengths[i]; } - // yet it's actually a length; will became position in ScalePhonemes - phonemes[phonemeI].position = transitionLengthTick; } else { phonemes[phonemeI].phoneme = null; phonemes[phonemeI].position = 0; } } - - return ScalePhonemes(phonemes, position, isEnding ? phonemeSymbols.Count : phonemeSymbols.Count - 1, containerLength); + + return ScalePhonemes(phonemes, position, isEnding ? phonemeSymbols.Count - 1 : phonemeSymbols.Count - 1, containerLength); } private string ValidateAliasIfNeeded(string alias, int tone) { @@ -790,18 +881,23 @@ private string ValidateAliasIfNeeded(string alias, int tone) { private Phoneme[] ScalePhonemes(Phoneme[] phonemes, int startPosition, int phonemesCount, int containerLengthTick = -1) { var offset = 0; - // reserved length for prev vowel, double length of a transition; - var containerSafeLengthTick = MsToTick(GetTransitionBasicLengthMsByConstant() * 2); var lengthModifier = 1.0; + if (containerLengthTick > 0) { var allTransitionsLengthTick = phonemes.Sum(n => n.position); - if (allTransitionsLengthTick + containerSafeLengthTick > containerLengthTick) { - lengthModifier = (double)containerLengthTick / (allTransitionsLengthTick + containerSafeLengthTick); + + // Instead of a fixed "Constant * 2", use a proportional limit. + // This allows transitions to occupy up to 80% of the note. + var maxAllowedConsonantTick = (int)(containerLengthTick * 0.8); + + if (allTransitionsLengthTick > maxAllowedConsonantTick) { + lengthModifier = (double)maxAllowedConsonantTick / allTransitionsLengthTick; } } for (var i = phonemes.Length - 1; i >= 0; i--) { - var finalLengthTick = (int)(phonemes[i].position * lengthModifier) / 5 * 5; + if (phonemes[i].phoneme == null) continue; + var finalLengthTick = (int)(phonemes[i].position * lengthModifier); phonemes[i].position = startPosition - finalLengthTick - offset; offset += finalLengthTick; } From ed9e3683ede570798b0a18d799044254a7a89f76 Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Thu, 19 Mar 2026 12:41:18 +0800 Subject: [PATCH 3/7] implement GetTransitionBasicLengthMsByOto per child phonemizer --- .../ArpasingPlusPhonemizer.cs | 126 +++-------------- OpenUtau.Plugin.Builtin/ENtoJAPhonemizer.cs | 8 ++ OpenUtau.Plugin.Builtin/EStoJAPhonemizer.cs | 9 ++ OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs | 126 +++-------------- .../EnglishVCCVPhonemizer.cs | 8 ++ OpenUtau.Plugin.Builtin/FILtoJAPhonemizer.cs | 8 ++ OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs | 127 +++--------------- .../FrenchCVVCPhonemizer.cs | 18 +-- .../FrenchVCCVPhonemizer.cs | 18 +-- .../GermanVCCVPhonemizer.cs | 13 +- .../ItalianSyllableBasedPhonemizer.cs | 9 ++ .../PolishCVCPhonemizer.cs | 9 ++ .../RussianCVCPhonemizer.cs | 19 +-- .../RussianVCCVPhonemizer.cs | 19 +-- .../SpanishMakkusanPhonemizer.cs | 9 ++ .../SpanishSyllableBasedPhonemizer.cs | 19 +-- .../SpanishVCCVPhonemizer.cs | 19 +-- 17 files changed, 160 insertions(+), 404 deletions(-) diff --git a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs index 4d487e9ad..8ed6b91b3 100644 --- a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs @@ -2034,123 +2034,33 @@ protected override string ValidateAlias(string alias) { return base.ValidateAlias(alias); } - bool PhonemeIsPresent(string alias, string phoneme) { - if (string.IsNullOrEmpty(alias) || string.IsNullOrEmpty(phoneme)) - return false; + // Endings has 50 ticks gap + protected override bool NoGap => true; - // Exact token match - if (alias == phoneme) - return true; + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - return alias.EndsWith(phoneme); - } - - private bool PhonemeHasEndingSuffix(string alias, string phoneme) { - var escapedPhoneme = Regex.Escape(phoneme); - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b\s*-") || - Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b-")) { - return true; - } - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b R")) { - return true; - } - return false; - } - - protected override double GetTransitionBasicLengthMs(string alias = "") { - //I wish these were automated instead :') - double transitionMultiplier = 1.0; // Default multiplier - - var fricative_def = 2.3; - var aspirate_def = 1.3; - var semivowel_def = 1.2; - var liquid_def = 1.5; - var nasal_def = 1.5; - var stop_def = 1.8; - var tap_def = 0.5; - var affricate_def = 1.5; - - var allConsonants = fricative.Concat(aspirate) - .Concat(semivowel) - .Concat(liquid) - .Concat(nasal) - .Concat(stop) - .Concat(tap) - .Concat(affricate) - .Distinct(); // Ensure no duplicates - - foreach (var c in allConsonants) { - if (PhonemeHasEndingSuffix(alias, c)) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } - - foreach (var v in vowels) { - if (alias.EndsWith("-")) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } - - // consonant timings + var tokens = alias.Split(' ') + .Select(t => t.Trim()) + .Where(t => !string.IsNullOrEmpty(t)) + .ToList(); var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { - var overridePhoneme = kvp.Key; - var overrideValue = kvp.Value; - if (PhonemeIsPresent(alias, overridePhoneme)) { - return base.GetTransitionBasicLengthMs() * overrideValue; - } - } - - foreach (var c in fricative) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * fricative_def; - } - } + var symbol = kvp.Key; + var value = kvp.Value; - foreach (var c in aspirate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * aspirate_def; - } - } - - foreach (var c in semivowel) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * semivowel_def; - } - } - - foreach (var c in liquid) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * liquid_def; - } - } - - foreach (var c in nasal) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * nasal_def; - } - } - - foreach (var c in stop) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * stop_def; - } - } - - foreach (var c in tap) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * tap_def; - } - } - - foreach (var c in affricate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * affricate_def; + if (symbol.Contains(" ")) { + if (alias.Replace("-", "").Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; + } + } + else if (tokens.Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; } } - return base.GetTransitionBasicLengthMs() * transitionMultiplier; + return otoLength; } } } diff --git a/OpenUtau.Plugin.Builtin/ENtoJAPhonemizer.cs b/OpenUtau.Plugin.Builtin/ENtoJAPhonemizer.cs index 911aac120..36973d76a 100644 --- a/OpenUtau.Plugin.Builtin/ENtoJAPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/ENtoJAPhonemizer.cs @@ -460,5 +460,13 @@ private string ToHiragana(string romaji) { hiragana = hiragana.Replace("ゔ", "ヴ"); return hiragana; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/EStoJAPhonemizer.cs b/OpenUtau.Plugin.Builtin/EStoJAPhonemizer.cs index 8fe5cca1d..b6929062b 100644 --- a/OpenUtau.Plugin.Builtin/EStoJAPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EStoJAPhonemizer.cs @@ -669,5 +669,14 @@ private string ToHiragana(string romaji) { hiragana = hiragana.Replace("ゔ", "ヴ"); return hiragana; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs b/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs index 79e6c3f41..eebe9ca8e 100644 --- a/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs @@ -1199,123 +1199,33 @@ protected override string ValidateAlias(string alias) { return alias; } - bool PhonemeIsPresent(string alias, string phoneme) { - if (string.IsNullOrEmpty(alias) || string.IsNullOrEmpty(phoneme)) - return false; + // Endings has 50 ticks gap + protected override bool NoGap => true; - // Exact token match - if (alias == phoneme) - return true; + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - return alias.EndsWith(phoneme); - } - - private bool PhonemeHasEndingSuffix(string alias, string phoneme) { - var escapedPhoneme = Regex.Escape(phoneme); - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b\s*-") || - Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b-")) { - return true; - } - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b R")) { - return true; - } - return false; - } - - protected override double GetTransitionBasicLengthMs(string alias = "") { - //I wish these were automated instead :') - double transitionMultiplier = 1.0; // Default multiplier - - var fricative_def = 2.3; - var aspirate_def = 1.3; - var semivowel_def = 1.2; - var liquid_def = 1.5; - var nasal_def = 1.5; - var stop_def = 1.8; - var tap_def = 0.5; - var affricate_def = 1.5; - - var allConsonants = fricative.Concat(aspirate) - .Concat(semivowel) - .Concat(liquid) - .Concat(nasal) - .Concat(stop) - .Concat(tap) - .Concat(affricate) - .Distinct(); // Ensure no duplicates - - foreach (var c in allConsonants) { - if (PhonemeHasEndingSuffix(alias, c)) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } - - foreach (var v in vowels) { - if (alias.EndsWith("-")) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } - - // consonant timings + var tokens = alias.Split(' ') + .Select(t => t.Trim()) + .Where(t => !string.IsNullOrEmpty(t)) + .ToList(); var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { - var overridePhoneme = kvp.Key; - var overrideValue = kvp.Value; - if (PhonemeIsPresent(alias, overridePhoneme)) { - return base.GetTransitionBasicLengthMs() * overrideValue; - } - } + var symbol = kvp.Key; + var value = kvp.Value; - foreach (var c in fricative) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * fricative_def; - } - } - - foreach (var c in aspirate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * aspirate_def; - } - } - - foreach (var c in semivowel) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * semivowel_def; - } - } - - foreach (var c in liquid) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * liquid_def; - } - } - - foreach (var c in nasal) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * nasal_def; - } - } - - foreach (var c in stop) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * stop_def; - } - } - - foreach (var c in tap) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * tap_def; - } - } - - foreach (var c in affricate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * affricate_def; + if (symbol.Contains(" ")) { + if (alias.Replace("-", "").Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; + } + } + else if (tokens.Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; } } - return base.GetTransitionBasicLengthMs() * transitionMultiplier; + return otoLength; } } } diff --git a/OpenUtau.Plugin.Builtin/EnglishVCCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/EnglishVCCVPhonemizer.cs index 46ff7d527..bbebc5f61 100644 --- a/OpenUtau.Plugin.Builtin/EnglishVCCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EnglishVCCVPhonemizer.cs @@ -1024,5 +1024,13 @@ protected override string ValidateAlias(string alias) { return alias; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/FILtoJAPhonemizer.cs b/OpenUtau.Plugin.Builtin/FILtoJAPhonemizer.cs index 02e2e62b1..8927f4395 100644 --- a/OpenUtau.Plugin.Builtin/FILtoJAPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FILtoJAPhonemizer.cs @@ -513,5 +513,13 @@ private string ToHiragana(string romaji) { hiragana = hiragana.Replace("ゔ", "ヴ"); return hiragana; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs index 08bd83d05..c09ed0fa5 100644 --- a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs @@ -1237,128 +1237,37 @@ protected override string ValidateAlias(string alias) { alias = alias.Replace(fb.Key, fb.Value); } } - return alias; return base.ValidateAlias(alias); } - bool PhonemeIsPresent(string alias, string phoneme) { - if (string.IsNullOrEmpty(alias) || string.IsNullOrEmpty(phoneme)) - return false; - - // Exact token match - if (alias == phoneme) - return true; - - return alias.EndsWith(phoneme); - } - - private bool PhonemeHasEndingSuffix(string alias, string phoneme) { - var escapedPhoneme = Regex.Escape(phoneme); - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b\s*-") || - Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b-")) { - return true; - } - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b R")) { - return true; - } - return false; - } - - protected override double GetTransitionBasicLengthMs(string alias = "") { - //I wish these were automated instead :') - double transitionMultiplier = 1.0; // Default multiplier - - var fricative_def = 2.3; - var aspirate_def = 1.3; - var semivowel_def = 1.2; - var liquid_def = 1.5; - var nasal_def = 1.5; - var stop_def = 1.8; - var tap_def = 0.5; - var affricate_def = 1.5; - - var allConsonants = fricative.Concat(aspirate) - .Concat(semivowel) - .Concat(liquid) - .Concat(nasal) - .Concat(stop) - .Concat(tap) - .Concat(affricate) - .Distinct(); // Ensure no duplicates - - foreach (var c in allConsonants) { - if (PhonemeHasEndingSuffix(alias, c)) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } + // Endings has 50 ticks gap + protected override bool NoGap => true; - foreach (var v in vowels) { - if (alias.EndsWith("-")) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - // consonant timings + var tokens = alias.Split(' ') + .Select(t => t.Trim()) + .Where(t => !string.IsNullOrEmpty(t)) + .ToList(); var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { - var overridePhoneme = kvp.Key; - var overrideValue = kvp.Value; - if (PhonemeIsPresent(alias, overridePhoneme)) { - return base.GetTransitionBasicLengthMs() * overrideValue; - } - } - - foreach (var c in fricative) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * fricative_def; - } - } + var symbol = kvp.Key; + var value = kvp.Value; - foreach (var c in aspirate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * aspirate_def; - } - } - - foreach (var c in semivowel) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * semivowel_def; - } - } - - foreach (var c in liquid) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * liquid_def; - } - } - - foreach (var c in nasal) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * nasal_def; - } - } - - foreach (var c in stop) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * stop_def; - } - } - - foreach (var c in tap) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * tap_def; - } - } - - foreach (var c in affricate) { - if (PhonemeIsPresent(alias, c)) { - return base.GetTransitionBasicLengthMs() * affricate_def; + if (symbol.Contains(" ")) { + if (alias.Replace("-", "").Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; + } + } + else if (tokens.Contains(symbol)) { + return GetTransitionBasicLengthMsByConstant() * value; } } - return base.GetTransitionBasicLengthMs() * transitionMultiplier; + return otoLength; } } } diff --git a/OpenUtau.Plugin.Builtin/FrenchCVVCPhonemizer.cs b/OpenUtau.Plugin.Builtin/FrenchCVVCPhonemizer.cs index 973907a1f..135e76349 100644 --- a/OpenUtau.Plugin.Builtin/FrenchCVVCPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FrenchCVVCPhonemizer.cs @@ -593,18 +593,12 @@ private string CheckCoeEnding(string cv, int tone) { return "no Coe Ending"; } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in shortConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 0.75; - } - } - foreach (var c in longConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 1.5; - } - } - return base.GetTransitionBasicLengthMs() * 1.25; + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + return otoLength; } private string CheckAliasFormatting(string alias, string type, int tone, string prevV) { diff --git a/OpenUtau.Plugin.Builtin/FrenchVCCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/FrenchVCCVPhonemizer.cs index 15a36996e..9d03ca2f2 100644 --- a/OpenUtau.Plugin.Builtin/FrenchVCCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FrenchVCCVPhonemizer.cs @@ -253,19 +253,13 @@ protected override List ProcessEnding(Ending ending) { return phonemes; } + // Endings has 50 ticks gap + protected override bool NoGap => true; - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in shortConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 0.75; - } - } - foreach (var c in longConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 1.5; - } - } - return base.GetTransitionBasicLengthMs() * 1.25; + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } protected override string[] GetSymbols(Note note) { diff --git a/OpenUtau.Plugin.Builtin/GermanVCCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/GermanVCCVPhonemizer.cs index 7a05e817f..53ea9ef32 100644 --- a/OpenUtau.Plugin.Builtin/GermanVCCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/GermanVCCVPhonemizer.cs @@ -666,13 +666,12 @@ protected override string ValidateAlias(string alias) { return alias; } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in longConsonants) { - if (alias.Contains(c)) { - return base.GetTransitionBasicLengthMs() * 2.0; - } - } - return base.GetTransitionBasicLengthMs(); + // Endings has 50 ticks gap + protected override bool NoGap => true; + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } } } \ No newline at end of file diff --git a/OpenUtau.Plugin.Builtin/ItalianSyllableBasedPhonemizer.cs b/OpenUtau.Plugin.Builtin/ItalianSyllableBasedPhonemizer.cs index 62e57407d..1617a3f03 100644 --- a/OpenUtau.Plugin.Builtin/ItalianSyllableBasedPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/ItalianSyllableBasedPhonemizer.cs @@ -197,5 +197,14 @@ protected override string ValidateAlias(string alias) { } return alias; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/PolishCVCPhonemizer.cs b/OpenUtau.Plugin.Builtin/PolishCVCPhonemizer.cs index ed6d96598..bb292b8c5 100644 --- a/OpenUtau.Plugin.Builtin/PolishCVCPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/PolishCVCPhonemizer.cs @@ -66,5 +66,14 @@ protected override List ProcessEnding(Ending ending) { return phonemes; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/RussianCVCPhonemizer.cs b/OpenUtau.Plugin.Builtin/RussianCVCPhonemizer.cs index 46f70d4ad..1a62dfaf8 100644 --- a/OpenUtau.Plugin.Builtin/RussianCVCPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/RussianCVCPhonemizer.cs @@ -105,18 +105,13 @@ protected override string ValidateAlias(string alias) { return aliasesFallback.ContainsKey(alias) ? aliasesFallback[alias] : alias; } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in shortConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 0.75; - } - } - foreach (var c in longConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 1.5; - } - } - return base.GetTransitionBasicLengthMs(); + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } } diff --git a/OpenUtau.Plugin.Builtin/RussianVCCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/RussianVCCVPhonemizer.cs index 515bbed72..505246560 100644 --- a/OpenUtau.Plugin.Builtin/RussianVCCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/RussianVCCVPhonemizer.cs @@ -124,18 +124,13 @@ protected override string ValidateAlias(string alias) { return alias; } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in shortConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 0.75; - } - } - foreach (var c in longConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 1.5; - } - } - return base.GetTransitionBasicLengthMs(); + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } } } diff --git a/OpenUtau.Plugin.Builtin/SpanishMakkusanPhonemizer.cs b/OpenUtau.Plugin.Builtin/SpanishMakkusanPhonemizer.cs index ea5244d7e..9ff7e6357 100644 --- a/OpenUtau.Plugin.Builtin/SpanishMakkusanPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/SpanishMakkusanPhonemizer.cs @@ -248,5 +248,14 @@ protected override string ValidateAlias(string alias) { } return alias; } + + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; + } } } diff --git a/OpenUtau.Plugin.Builtin/SpanishSyllableBasedPhonemizer.cs b/OpenUtau.Plugin.Builtin/SpanishSyllableBasedPhonemizer.cs index e350abf21..76ac43fc0 100644 --- a/OpenUtau.Plugin.Builtin/SpanishSyllableBasedPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/SpanishSyllableBasedPhonemizer.cs @@ -460,18 +460,13 @@ protected override string ValidateAlias(string alias) { return alias; } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in longConsonants) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 2.0; - } - } - foreach (var c in new[] { "r" }) { - if (alias.EndsWith(c)) { - return base.GetTransitionBasicLengthMs() * 0.75; - } - } - return base.GetTransitionBasicLengthMs(); + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } } } diff --git a/OpenUtau.Plugin.Builtin/SpanishVCCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/SpanishVCCVPhonemizer.cs index eafd6eeb9..124c9fc46 100644 --- a/OpenUtau.Plugin.Builtin/SpanishVCCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/SpanishVCCVPhonemizer.cs @@ -681,18 +681,13 @@ protected override string ValidateAlias(string alias) { return base.ValidateAlias(alias); } - protected override double GetTransitionBasicLengthMs(string alias = "") { - foreach (var c in shortConsonants) { - if (alias.Contains(c) && !alias.Contains("rr") && !alias.StartsWith(c) && !alias.Contains("ar") && !alias.Contains("er") && !alias.Contains("ir") && !alias.Contains("or") && !alias.Contains("ur")) { - return base.GetTransitionBasicLengthMs() * 0.50; - } - } - foreach (var c in longConsonants) { - if (alias.Contains(c) && !alias.StartsWith(c) && !alias.StartsWith("-" + c)) { - return base.GetTransitionBasicLengthMs() * 2.0; - } - } - return base.GetTransitionBasicLengthMs(); + // Endings has 50 ticks gap + protected override bool NoGap => true; + + protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { + double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); + + return otoLength; } } } From 38cd838f6d2af904c89cdf6764cb8efa2f17a4f6 Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Thu, 19 Mar 2026 12:55:46 +0800 Subject: [PATCH 4/7] Fix DeVCCV test file to reflect correct pitch suffix --- OpenUtau.Test/Plugins/DeVccvTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenUtau.Test/Plugins/DeVccvTest.cs b/OpenUtau.Test/Plugins/DeVccvTest.cs index 44789c6cb..6e7adf00e 100644 --- a/OpenUtau.Test/Plugins/DeVccvTest.cs +++ b/OpenUtau.Test/Plugins/DeVccvTest.cs @@ -23,7 +23,7 @@ protected override Phonemizer CreatePhonemizer() { [InlineData("de_vccv", new string[] { "Mond", "+", "+", "+", "Licht", "+" }, new string[] { "G3", "D3", "G3", "G3", "D3", "G3" }, - new string[] { "- moG3", "onG3", "nt -G3", "t lG3", "lID3", "ICG3", "Ct -G3" })] + new string[] { "- moG3", "onG3", "nt -G3", "t lG3", "lID3", "ICD3", "Ct -G3" })] public void PhonemizeTest(string singerName, string[] lyrics, string[] tones, string[] aliases) { RunPhonemizeTest(singerName, lyrics, RepeatString(lyrics.Length, ""), tones, RepeatString(lyrics.Length, ""), aliases); } From 962e7855771fb61992995b7e60c3e39330adb2ad Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Sat, 21 Mar 2026 11:36:53 +0800 Subject: [PATCH 5/7] Fix phoneme overrides method --- OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs | 12 +----------- OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs | 12 +----------- OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs | 12 +----------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs index 8ed6b91b3..13460ad92 100644 --- a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs @@ -2040,22 +2040,12 @@ protected override string ValidateAlias(string alias) { protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - var tokens = alias.Split(' ') - .Select(t => t.Trim()) - .Where(t => !string.IsNullOrEmpty(t)) - .ToList(); - var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { var symbol = kvp.Key; var value = kvp.Value; - if (symbol.Contains(" ")) { - if (alias.Replace("-", "").Contains(symbol)) { - return GetTransitionBasicLengthMsByConstant() * value; - } - } - else if (tokens.Contains(symbol)) { + if (alias.Contains(symbol)) { return GetTransitionBasicLengthMsByConstant() * value; } } diff --git a/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs b/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs index eebe9ca8e..ee595e428 100644 --- a/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EnXSampaPhonemizer.cs @@ -1205,22 +1205,12 @@ protected override string ValidateAlias(string alias) { protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - var tokens = alias.Split(' ') - .Select(t => t.Trim()) - .Where(t => !string.IsNullOrEmpty(t)) - .ToList(); - var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { var symbol = kvp.Key; var value = kvp.Value; - if (symbol.Contains(" ")) { - if (alias.Replace("-", "").Contains(symbol)) { - return GetTransitionBasicLengthMsByConstant() * value; - } - } - else if (tokens.Contains(symbol)) { + if (alias.Contains(symbol)) { return GetTransitionBasicLengthMsByConstant() * value; } } diff --git a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs index c09ed0fa5..474edbf42 100644 --- a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs @@ -1247,22 +1247,12 @@ protected override string ValidateAlias(string alias) { protected override double GetTransitionBasicLengthMs(string alias, int tone, PhonemeAttributes attr) { double otoLength = GetTransitionBasicLengthMsByOto(alias, tone, attr); - var tokens = alias.Split(' ') - .Select(t => t.Trim()) - .Where(t => !string.IsNullOrEmpty(t)) - .ToList(); - var sortedOverrides = PhonemeOverrides.OrderByDescending(kv => kv.Key.Length); foreach (var kvp in sortedOverrides) { var symbol = kvp.Key; var value = kvp.Value; - if (symbol.Contains(" ")) { - if (alias.Replace("-", "").Contains(symbol)) { - return GetTransitionBasicLengthMsByConstant() * value; - } - } - else if (tokens.Contains(symbol)) { + if (alias.Contains(symbol)) { return GetTransitionBasicLengthMsByConstant() * value; } } From ae5d8a226cb0d97bd4d6669324400073daff25bb Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Sun, 29 Mar 2026 10:41:24 +0800 Subject: [PATCH 6/7] fix EN C+V ending timings --- .../EnglishCpVPhonemizer.cs | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs b/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs index d24cb72a1..8e48f8334 100644 --- a/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs @@ -1115,17 +1115,8 @@ bool PhonemeIsPresent(string alias, string phoneme) { return alias.EndsWith(phoneme); } - private bool PhonemeHasEndingSuffix(string alias, string phoneme) { - var escapedPhoneme = Regex.Escape(phoneme); - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b\s*-") || - Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b-")) { - return true; - } - if (Regex.IsMatch(alias, $@"\b{escapedPhoneme}\b R")) { - return true; - } - return false; - } + + protected override bool NoGap => true; protected override double GetTransitionBasicLengthMs(string alias = "") { //I wish these were automated instead :') @@ -1162,17 +1153,6 @@ protected override double GetTransitionBasicLengthMs(string alias = "") { } } - foreach (var c in allConsonants) { - if (PhonemeHasEndingSuffix(alias, c)) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } - - foreach (var v in vowels) { - if (alias.EndsWith("-")) { - return base.GetTransitionBasicLengthMs() * 0.5; - } - } foreach (var c in fricative) { if (PhonemeIsPresent(alias, c)) { From 27b10cce5edc8666530a0ef83ba3b58faf054f76 Mon Sep 17 00:00:00 2001 From: cadlaxa Date: Sun, 29 Mar 2026 13:24:00 +0800 Subject: [PATCH 7/7] Utilize ValidateAlias on AliasFormat --- .../ArpasingPlusPhonemizer.cs | 34 +++--- .../EnglishCpVPhonemizer.cs | 106 ++++++++++++++++-- OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs | 33 +++--- 3 files changed, 130 insertions(+), 43 deletions(-) diff --git a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs index 13460ad92..fad5389a7 100644 --- a/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/ArpasingPlusPhonemizer.cs @@ -1196,7 +1196,6 @@ protected override List ProcessEnding(Ending ending) { } private string AliasFormat(string alias, string type, int tone, string prevV) { var aliasFormats = new Dictionary { - // Define alias formats for different types { "dynStart", new string[] { "" } }, { "dynMid", new string[] { "" } }, { "dynMid_vv", new string[] { "" } }, @@ -1217,12 +1216,10 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { { "cc1_mix", new string[] { "", " -", "-", " R", "_", "- ", "-" } }, }; - // Check if the given type exists in the aliasFormats dictionary if (!aliasFormats.ContainsKey(type) && !type.Contains("dynamic")) { return alias; } - // Handle dynamic variations when type contains "dynamic" if (type.Contains("dynStart")) { string consonant = ""; string vowel = ""; @@ -1234,10 +1231,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { } else { consonant = alias; } - - // Handle the alias with space and without space var dynamicVariations = new List { - // Variations with space, dash, and underscore $"- {consonant}{vowel}", // "- CV" $"- {consonant} {vowel}", // "- C V" $"-{consonant} {vowel}", // "-C V" @@ -1245,10 +1239,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"-{consonant}_{vowel}", // "-C_V" $"- {consonant}_{vowel}", // "- C_V" }; - // Check each dynamically generated format + foreach (var variation in dynamicVariations) { - if (HasOto(variation, tone) || HasOto(ValidateAlias(variation), tone)) { + if (HasOto(variation, tone)) { return variation; + } else if (HasOto(ValidateAlias(variation), tone)) { + return ValidateAlias(variation); } } } @@ -1256,7 +1252,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { if (type.Contains("dynMid")) { string consonant = ""; string vowel = ""; - // If the alias contains a space, split it into consonant and vowel + if (alias.Contains(" ")) { var parts = alias.Split(' '); consonant = parts[0]; @@ -1269,10 +1265,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"{consonant} {vowel}", // "C V" $"{consonant}_{vowel}", // "C_V" }; - // Check each dynamically generated format + foreach (var variation1 in dynamicVariations1) { - if (HasOto(variation1, tone) || HasOto(ValidateAlias(variation1), tone)) { + if (HasOto(variation1, tone)) { return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); } } } @@ -1280,7 +1278,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { if (type.Contains("dynEnd")) { string consonant = ""; string vowel = ""; - // If the alias contains a space, split it into consonant and vowel + if (alias.Contains(" ")) { var parts = alias.Split(' '); consonant = parts[1]; @@ -1294,10 +1292,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"{vowel}{consonant}-", // "VC-" $"{vowel} {consonant} -", // "V C -" }; - // Check each dynamically generated format + foreach (var variation1 in dynamicVariations1) { - if (HasOto(variation1, tone) || HasOto(ValidateAlias(variation1), tone)) { + if (HasOto(variation1, tone)) { return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); } } } @@ -1315,9 +1315,11 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { } else { aliasFormat = $"{format}{alias}"; } - // Check if the formatted alias exists - if (HasOto(aliasFormat, tone) || HasOto(ValidateAlias(aliasFormat), tone)) { + + if (HasOto(aliasFormat, tone)) { return aliasFormat; + } else if (HasOto(ValidateAlias(aliasFormat), tone)) { + return ValidateAlias(aliasFormat); } } return alias; diff --git a/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs b/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs index 8e48f8334..17f8d27c9 100644 --- a/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/EnglishCpVPhonemizer.cs @@ -1055,28 +1055,110 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { }; - // Check if the given type exists in the aliasFormats dictionary - if (!aliasFormats.ContainsKey(type)) { + if (!aliasFormats.ContainsKey(type) && !type.Contains("dynamic")) { return alias; } - // Get the array of possible alias formats for the specified type + + if (type.Contains("dynStart")) { + string consonant = ""; + string vowel = ""; + // If the alias contains a space, split it into consonant and vowel + if (alias.Contains(" ")) { + var parts = alias.Split(' '); + consonant = parts[0]; + vowel = parts[1]; + } else { + consonant = alias; + } + var dynamicVariations = new List { + $"- {consonant}{vowel}", // "- CV" + $"- {consonant} {vowel}", // "- C V" + $"-{consonant} {vowel}", // "-C V" + $"-{consonant}{vowel}", // "-CV" + $"-{consonant}_{vowel}", // "-C_V" + $"- {consonant}_{vowel}", // "- C_V" + }; + + foreach (var variation in dynamicVariations) { + if (HasOto(variation, tone)) { + return variation; + } else if (HasOto(ValidateAlias(variation), tone)) { + return ValidateAlias(variation); + } + } + } + + if (type.Contains("dynMid")) { + string consonant = ""; + string vowel = ""; + + if (alias.Contains(" ")) { + var parts = alias.Split(' '); + consonant = parts[0]; + vowel = parts[1]; + } else { + consonant = alias; + } + var dynamicVariations1 = new List { + $"{consonant}{vowel}", // "CV" + $"{consonant} {vowel}", // "C V" + $"{consonant}_{vowel}", // "C_V" + }; + + foreach (var variation1 in dynamicVariations1) { + if (HasOto(variation1, tone)) { + return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); + } + } + } + + if (type.Contains("dynEnd")) { + string consonant = ""; + string vowel = ""; + + if (alias.Contains(" ")) { + var parts = alias.Split(' '); + consonant = parts[1]; + vowel = parts[0]; + } else { + consonant = alias; + } + var dynamicVariations1 = new List { + $"{vowel}{consonant} -", // "VC -" + $"{vowel} {consonant}-", // "V C-" + $"{vowel}{consonant}-", // "VC-" + $"{vowel} {consonant} -", // "V C -" + }; + + foreach (var variation1 in dynamicVariations1) { + if (HasOto(variation1, tone)) { + return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); + } + } + } + + // Get the array of possible alias formats for the specified type if not dynamic var formatsToTry = aliasFormats[type]; int counter = 0; foreach (var format in formatsToTry) { string aliasFormat; if (type.Contains("mix") && counter < 4) { - // Alternate between alias + format and format + alias for the first 4 iterations - aliasFormat = (counter % 2 == 0) ? alias + format : format + alias; + aliasFormat = (counter % 2 == 0) ? $"{alias}{format}" : $"{format}{alias}"; counter++; - } else if (type.Contains("end")) { - aliasFormat = alias + format; + } else if (type.Contains("end") || type.Contains("End") && !(type.Contains("dynEnd"))) { + aliasFormat = $"{alias}{format}"; } else { - aliasFormat = format + alias; + aliasFormat = $"{format}{alias}"; } - // Check if the formatted alias exists using HasOto and ValidateAlias - if (HasOto(aliasFormat, tone) || HasOto(ValidateAlias(aliasFormat), tone)) { - alias = aliasFormat; - return alias; + + if (HasOto(aliasFormat, tone)) { + return aliasFormat; + } else if (HasOto(ValidateAlias(aliasFormat), tone)) { + return ValidateAlias(aliasFormat); } } return alias; diff --git a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs index 474edbf42..a9e51543a 100644 --- a/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/FilipinoPhonemizer.cs @@ -1118,12 +1118,10 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { { "cc1_mix", new string[] { "", " -", "-", " R", "_", "- ", "-" } }, }; - // Check if the given type exists in the aliasFormats dictionary if (!aliasFormats.ContainsKey(type) && !type.Contains("dynamic")) { return alias; } - // Handle dynamic variations when type contains "dynamic" if (type.Contains("dynStart")) { string consonant = ""; string vowel = ""; @@ -1135,10 +1133,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { } else { consonant = alias; } - - // Handle the alias with space and without space var dynamicVariations = new List { - // Variations with space, dash, and underscore $"- {consonant}{vowel}", // "- CV" $"- {consonant} {vowel}", // "- C V" $"-{consonant} {vowel}", // "-C V" @@ -1146,10 +1141,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"-{consonant}_{vowel}", // "-C_V" $"- {consonant}_{vowel}", // "- C_V" }; - // Check each dynamically generated format + foreach (var variation in dynamicVariations) { - if (HasOto(variation, tone) || HasOto(ValidateAlias(variation), tone)) { + if (HasOto(variation, tone)) { return variation; + } else if (HasOto(ValidateAlias(variation), tone)) { + return ValidateAlias(variation); } } } @@ -1157,7 +1154,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { if (type.Contains("dynMid")) { string consonant = ""; string vowel = ""; - // If the alias contains a space, split it into consonant and vowel + if (alias.Contains(" ")) { var parts = alias.Split(' '); consonant = parts[0]; @@ -1170,10 +1167,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"{consonant} {vowel}", // "C V" $"{consonant}_{vowel}", // "C_V" }; - // Check each dynamically generated format + foreach (var variation1 in dynamicVariations1) { - if (HasOto(variation1, tone) || HasOto(ValidateAlias(variation1), tone)) { + if (HasOto(variation1, tone)) { return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); } } } @@ -1181,7 +1180,7 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { if (type.Contains("dynEnd")) { string consonant = ""; string vowel = ""; - // If the alias contains a space, split it into consonant and vowel + if (alias.Contains(" ")) { var parts = alias.Split(' '); consonant = parts[1]; @@ -1195,10 +1194,12 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { $"{vowel}{consonant}-", // "VC-" $"{vowel} {consonant} -", // "V C -" }; - // Check each dynamically generated format + foreach (var variation1 in dynamicVariations1) { - if (HasOto(variation1, tone) || HasOto(ValidateAlias(variation1), tone)) { + if (HasOto(variation1, tone)) { return variation1; + } else if (HasOto(ValidateAlias(variation1), tone)) { + return ValidateAlias(variation1); } } } @@ -1216,9 +1217,11 @@ private string AliasFormat(string alias, string type, int tone, string prevV) { } else { aliasFormat = $"{format}{alias}"; } - // Check if the formatted alias exists - if (HasOto(aliasFormat, tone) || HasOto(ValidateAlias(aliasFormat), tone)) { + + if (HasOto(aliasFormat, tone)) { return aliasFormat; + } else if (HasOto(ValidateAlias(aliasFormat), tone)) { + return ValidateAlias(aliasFormat); } } return alias;