From ef223733ddaea5252a7c7e5b9ae54b92222664ae Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Sun, 17 May 2026 16:51:32 +0800 Subject: [PATCH 1/6] Add a rule-based Filipino phonemizer for DiffSinger --- .../DiffSingerRuleBasedFilipinoPhonemizer.cs | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs new file mode 100644 index 000000000..ef1b19aec --- /dev/null +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using OpenUtau.Api; +using OpenUtau.Core.G2p; + +namespace OpenUtau.Core.G2p { + public class RuleBasedFilipinoG2p : IG2p { + readonly static Regex kAllPunct = new Regex(@"^[\p{P}]$"); + + private string[] validPhonemes = + { "m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "w", "r", "vf", "a", "e", "i", "o", "u" }; + + private readonly string[] glides = { "w", "y" }; + + private string[] vowels = { "a", "e", "i", "o", "u" }; + + public bool IsGlide(string symbol) => glides.Contains(symbol); + + public bool IsValidSymbol(string symbol) => validPhonemes.Contains(symbol); + + public bool IsVowel(string symbol) => vowels.Contains(symbol); + + public string[] UnpackHint(string hint, char separator = ' ') { + return hint.Split(separator) + .Where(x => validPhonemes.Contains(x)) + .ToArray(); + } + + public string[] Query(string grapheme) { + if (string.IsNullOrEmpty(grapheme) || kAllPunct.IsMatch(grapheme)) { + return null; + } + return Predict(grapheme); + } + + string[]? Predict(string grapheme) { + grapheme = grapheme.ToLower(new CultureInfo("fil-PH")); + if (grapheme.Equals("mga")) grapheme = "manga"; + if (grapheme.Equals("ng")) grapheme = "nang"; + List phonemes = new List(); + foreach (var c in grapheme) { + var prev = phonemes.LastOrDefault(""); + switch (c) { + case 'a': + case 'o': + case 'u': + if (prev.Equals("c")) phonemes[^1] = "k"; + phonemes.Add(c.ToString()); + break; + case 'e': + if (prev.Equals("c")) phonemes[^1] = "s"; + phonemes.Add("e"); + break; + case 'i': + if (prev.Equals("c")) phonemes[^1] = "sy"; + else phonemes.Add("i"); + break; + case 'f': + phonemes.Add("p"); + break; + case 'g': + if (prev.Equals("n")) phonemes[^1] = "ng"; + else phonemes.Add("g"); + break; + case 'j': + phonemes.Add("dy"); + break; + case 'ñ': + phonemes.Add("n"); + phonemes.Add("y"); + break; + case 'y': + if (prev.Equals("t") || prev.Equals("d") || prev.Equals("s")) + phonemes[^1] = prev + "y"; + else phonemes.Add("y"); + break; + case 'z': + phonemes.Add("s"); + break; + case '-': + phonemes.Add("q"); + break; + case '\'': + phonemes.Add("vf"); + break; + default: + phonemes.Add(c.ToString()); + break; + } + } + + string[] filteredPhonemes = phonemes.Where(x => validPhonemes.Contains(x)).ToArray(); + + return (filteredPhonemes.Length == 0) ? null : filteredPhonemes; + } + } +} + +namespace OpenUtau.Core.DiffSinger { + [Phonemizer("DiffSinger Rule-based Filipino Phonemizer", "DIFFS FIL", "UtaUtaUtau", "FIL")] + public class DiffSingerRuleBasedFilipinoPhonemizer : DiffSingerG2pPhonemizer { + protected override string GetDictionaryName() => "dsdict-fil.yaml"; + + public override string GetLangCode() => "fil"; + + protected override IG2p LoadBaseG2p() => new RuleBasedFilipinoG2p(); + + protected override string[] GetBaseG2pVowels() => new string[] { + "a", "e", "i", "o", "u" + }; + + protected override string[] GetBaseG2pConsonants() => new string[] { + "m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "w", "r", "vf" + }; + } +} From fc518a8ec6a22508203ded9313c641726edf2d18 Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Wed, 20 May 2026 01:18:29 +0800 Subject: [PATCH 2/6] add extra digraphs --- .../DiffSingerRuleBasedFilipinoPhonemizer.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs index ef1b19aec..7b284ae89 100644 --- a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -22,6 +22,8 @@ public class RuleBasedFilipinoG2p : IG2p { public bool IsVowel(string symbol) => vowels.Contains(symbol); + bool IsConsonant(string symbol) => !symbol.Equals("|") && !IsVowel(symbol); + public string[] UnpackHint(string hint, char separator = ' ') { return hint.Split(separator) .Where(x => validPhonemes.Contains(x)) @@ -64,6 +66,10 @@ public string[] Query(string grapheme) { if (prev.Equals("n")) phonemes[^1] = "ng"; else phonemes.Add("g"); break; + case 'h': + if (prev.Equals("c")) phonemes[^1] = "ty"; + else phonemes.Add("h"); + break; case 'j': phonemes.Add("dy"); break; @@ -71,6 +77,10 @@ public string[] Query(string grapheme) { phonemes.Add("n"); phonemes.Add("y"); break; + case 's': + if (prev.Equals("t")) phonemes[^1] = "ty"; + else phonemes.Add("s"); + break; case 'y': if (prev.Equals("t") || prev.Equals("d") || prev.Equals("s")) phonemes[^1] = prev + "y"; @@ -90,6 +100,21 @@ public string[] Query(string grapheme) { break; } } + + // glide pass + for (int i = 1; i < phonemes.Count - 1; i++) { + string prev = phonemes[i - 1]; + string curr = phonemes[i]; + string next = phonemes[i + 1]; + phonemes[i] = curr switch { + "u" when IsConsonant(prev) && IsVowel(next) => "w", + "i" when IsConsonant(prev) && IsVowel(next) => "y", + _ => phonemes[i] + }; + } + + // extra palatalization pass + string[] filteredPhonemes = phonemes.Where(x => validPhonemes.Contains(x)).ToArray(); From dcc0d5e5308f7f0fbc997ce3c81c5260ead49ff1 Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Fri, 22 May 2026 00:08:40 +0800 Subject: [PATCH 3/6] add glottal stop insertion, distinguish coda offglides --- .../DiffSingerRuleBasedFilipinoPhonemizer.cs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs index 7b284ae89..80143f13e 100644 --- a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -7,14 +7,15 @@ namespace OpenUtau.Core.G2p { public class RuleBasedFilipinoG2p : IG2p { - readonly static Regex kAllPunct = new Regex(@"^[\p{P}]$"); + static readonly Regex kAllPunct = new Regex(@"^[\p{P}]$"); - private string[] validPhonemes = - { "m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "w", "r", "vf", "a", "e", "i", "o", "u" }; + private readonly string[] validPhonemes = + ["m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "Y", "w", "W", "r", "vf", "a", "e", "i", "o", "u" + ]; - private readonly string[] glides = { "w", "y" }; + private readonly string[] glides = ["w", "y"]; - private string[] vowels = { "a", "e", "i", "o", "u" }; + private readonly string[] vowels = ["a", "e", "i", "o", "u"]; public bool IsGlide(string symbol) => glides.Contains(symbol); @@ -49,15 +50,20 @@ public string[] Query(string grapheme) { case 'o': case 'u': if (prev.Equals("c")) phonemes[^1] = "k"; + else if (IsVowel(prev)) phonemes.Add("q"); phonemes.Add(c.ToString()); break; case 'e': if (prev.Equals("c")) phonemes[^1] = "s"; + else if (IsVowel(prev)) phonemes.Add("q"); phonemes.Add("e"); break; case 'i': if (prev.Equals("c")) phonemes[^1] = "sy"; - else phonemes.Add("i"); + else if (IsVowel(prev)) { + phonemes.Add("q"); + phonemes.Add("i"); + } else phonemes.Add("i"); break; case 'f': phonemes.Add("p"); @@ -67,8 +73,18 @@ public string[] Query(string grapheme) { else phonemes.Add("g"); break; case 'h': - if (prev.Equals("c")) phonemes[^1] = "ty"; - else phonemes.Add("h"); + switch (prev) + { + case "c": + phonemes[^1] = "ty"; + break; + case "s": + phonemes[^1] = "sh"; + break; + default: + phonemes.Add("h"); + break; + } break; case 'j': phonemes.Add("dy"); @@ -101,7 +117,7 @@ public string[] Query(string grapheme) { } } - // glide pass + // glide+coda pass for (int i = 1; i < phonemes.Count - 1; i++) { string prev = phonemes[i - 1]; string curr = phonemes[i]; @@ -109,13 +125,15 @@ public string[] Query(string grapheme) { phonemes[i] = curr switch { "u" when IsConsonant(prev) && IsVowel(next) => "w", "i" when IsConsonant(prev) && IsVowel(next) => "y", + "w" when IsVowel(prev) && IsConsonant(next) => "W", + "y" when IsVowel(prev) && IsConsonant(next) => "Y", _ => phonemes[i] }; } - // extra palatalization pass + // end coda + if (IsVowel(phonemes[^2]) && IsGlide(phonemes[^1])) phonemes[^1] = phonemes[^1].ToUpperInvariant(); - string[] filteredPhonemes = phonemes.Where(x => validPhonemes.Contains(x)).ToArray(); return (filteredPhonemes.Length == 0) ? null : filteredPhonemes; @@ -137,7 +155,7 @@ public class DiffSingerRuleBasedFilipinoPhonemizer : DiffSingerG2pPhonemizer { }; protected override string[] GetBaseG2pConsonants() => new string[] { - "m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "w", "r", "vf" + "m", "n", "ng", "p", "t", "ty", "k", "q", "b", "d", "dy", "g", "s", "sy", "h", "l", "y", "Y", "w", "W", "r", "vf" }; } } From 773b500b8416cd105b6fbed52d9caa49946dd8d2 Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Thu, 11 Jun 2026 04:58:47 +0800 Subject: [PATCH 4/6] add condition for glide+coda pass --- .../DiffSingerRuleBasedFilipinoPhonemizer.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs index 80143f13e..065ca276c 100644 --- a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -118,17 +118,19 @@ public string[] Query(string grapheme) { } // glide+coda pass - for (int i = 1; i < phonemes.Count - 1; i++) { - string prev = phonemes[i - 1]; - string curr = phonemes[i]; - string next = phonemes[i + 1]; - phonemes[i] = curr switch { - "u" when IsConsonant(prev) && IsVowel(next) => "w", - "i" when IsConsonant(prev) && IsVowel(next) => "y", - "w" when IsVowel(prev) && IsConsonant(next) => "W", - "y" when IsVowel(prev) && IsConsonant(next) => "Y", - _ => phonemes[i] - }; + if (phonemes.Count >= 3) { + for (int i = 1; i < phonemes.Count - 1; i++) { + string prev = phonemes[i - 1]; + string curr = phonemes[i]; + string next = phonemes[i + 1]; + phonemes[i] = curr switch { + "u" when IsConsonant(prev) && IsVowel(next) => "w", + "i" when IsConsonant(prev) && IsVowel(next) => "y", + "w" when IsVowel(prev) && IsConsonant(next) => "W", + "y" when IsVowel(prev) && IsConsonant(next) => "Y", + _ => phonemes[i] + }; + } } // end coda From 86b325dd383d225700c3efe063899b44b9861542 Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Thu, 11 Jun 2026 05:02:24 +0800 Subject: [PATCH 5/6] Revert "add condition for glide+coda pass" This reverts commit 773b500b8416cd105b6fbed52d9caa49946dd8d2. --- .../DiffSingerRuleBasedFilipinoPhonemizer.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs index 065ca276c..80143f13e 100644 --- a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -118,19 +118,17 @@ public string[] Query(string grapheme) { } // glide+coda pass - if (phonemes.Count >= 3) { - for (int i = 1; i < phonemes.Count - 1; i++) { - string prev = phonemes[i - 1]; - string curr = phonemes[i]; - string next = phonemes[i + 1]; - phonemes[i] = curr switch { - "u" when IsConsonant(prev) && IsVowel(next) => "w", - "i" when IsConsonant(prev) && IsVowel(next) => "y", - "w" when IsVowel(prev) && IsConsonant(next) => "W", - "y" when IsVowel(prev) && IsConsonant(next) => "Y", - _ => phonemes[i] - }; - } + for (int i = 1; i < phonemes.Count - 1; i++) { + string prev = phonemes[i - 1]; + string curr = phonemes[i]; + string next = phonemes[i + 1]; + phonemes[i] = curr switch { + "u" when IsConsonant(prev) && IsVowel(next) => "w", + "i" when IsConsonant(prev) && IsVowel(next) => "y", + "w" when IsVowel(prev) && IsConsonant(next) => "W", + "y" when IsVowel(prev) && IsConsonant(next) => "Y", + _ => phonemes[i] + }; } // end coda From 9d9468d49d56a452de250d6291d99c47001753f2 Mon Sep 17 00:00:00 2001 From: UtaUtaUtau <29729824+UtaUtaUtau@users.noreply.github.com> Date: Thu, 11 Jun 2026 05:05:04 +0800 Subject: [PATCH 6/6] condition is actually for the end coda... oops --- .../Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs index 80143f13e..80e6e246c 100644 --- a/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs +++ b/OpenUtau.Core/DiffSinger/Phonemizers/DiffSingerRuleBasedFilipinoPhonemizer.cs @@ -132,7 +132,8 @@ public string[] Query(string grapheme) { } // end coda - if (IsVowel(phonemes[^2]) && IsGlide(phonemes[^1])) phonemes[^1] = phonemes[^1].ToUpperInvariant(); + + if ((phonemes.Count >= 2) && IsVowel(phonemes[^2]) && IsGlide(phonemes[^1])) phonemes[^1] = phonemes[^1].ToUpperInvariant(); string[] filteredPhonemes = phonemes.Where(x => validPhonemes.Contains(x)).ToArray();