From 0298736a9602c5603ed376f53ef9a92cdde30636 Mon Sep 17 00:00:00 2001
From: Oris Walft <46623531+oriswalft@users.noreply.github.com>
Date: Thu, 8 Dec 2022 19:10:52 +0100
Subject: [PATCH 01/18] Update Strings.fr-FR.axaml
---
OpenUtau/Strings/Strings.fr-FR.axaml | 38 ++++++++++++++--------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/OpenUtau/Strings/Strings.fr-FR.axaml b/OpenUtau/Strings/Strings.fr-FR.axaml
index 1905b8471..9ca607c2b 100644
--- a/OpenUtau/Strings/Strings.fr-FR.axaml
+++ b/OpenUtau/Strings/Strings.fr-FR.axaml
@@ -47,7 +47,7 @@
Abréviation
Appliquer
- Expréssions
+ Expressions
Valeur par défaut
Est un Resampler Flag
@@ -120,7 +120,7 @@
Préférences...
- Expréssions...
+ Expressions...
Installer une Voix
Installer une Voix (paramètres avancés)
Voix
@@ -153,7 +153,7 @@
Couleur
Consonne
Fin
- Définir Consonant
+ Définir la consonne
Définir la fin
Définir le début
Définir le chevauchement
@@ -167,11 +167,11 @@
Pitch
Suffixe
-
-
+ Assistant phonétique
+ Copier
Paroles
- Remplacer "-" avec "+"
+ Remplacer "-" par "+"
Modifier les paroles
Hanzi -> Pinyin
Hiragana -> Romaji
@@ -230,8 +230,8 @@ Maintenir Ctrl pour sélectionner
Montrer les notes des autres pistes sur le piano roll
Montrer le portrait sur le piano roll
Thème
- Nuit
- Jour
+ Sombre
+ Clair
Préférences
Note: veuillez redémarrer OpenUtau après avoir changé cette option.
Désactivé
@@ -270,10 +270,10 @@ Avertissement : moresampler n'est pas entièrement pris en charge. Il peut être
Note: pour utiliser des resamplers externes, ajoutez le fichier .exe ou .dll du resampler dans le dossier "Resamplers" de OpenUTAU, puis choisissez-le dans Préférences.
-
-
+ <
+ Attention : trop de thread pourrait ralentir OpenUTAU.
+
+ Threads maximum pour le rendu
Projet enregistré. {0}
@@ -282,22 +282,22 @@ Avertissement : moresampler n'est pas entièrement pris en charge. Il peut être
Voicebanks
Editer dans VLabeler
Ouvrir l'emplacement
-
-
+ Régénérer FRQ
+ Régénération FRQ-
Réinitialiser l'Oto
Sauvegarder l'Oto
Téléchargez vLabeler (1.0.0-beta1 ou plus) de https://github.com/sdercolin/vlabeler et définissez le chemin d'accès dans préférences d'abord !
Générer un rapport d'erreur de la Voix
Emplacement
- Bouger a gauche
- Bouger a droite
+ Bouger à gauche
+ Bouger à droite
Séléctionner suivant
Séléctionner précédent
Montrer tout
Zoomer
Dézoomer
Rafraîchir
- Définir le phonemizeur par défaut
+ Définir le phonemizer par défaut
Configurer l'encodage
Définir le portrait
Annuler
@@ -320,7 +320,7 @@ Avertissement : moresampler n'est pas entièrement pris en charge. Il peut être
Clic gauche: Configurer les expressions
Clic droit: Réinitialiser les expressions
- Expression non supporter par le moteur de rendu
+ Expression non supportée par le moteur de rendu
Tab: Note Suivante
Maj+Tab: Note Précédente
@@ -344,7 +344,7 @@ Général
Scroller ici pour zoomer verticalement ▶
Plus
- Déscendre
+ Descendre
Monter
Supprimer
Séléctionner le moteur de rendu
From c291800a7370925759d06bd91d9d249f2ed713c6 Mon Sep 17 00:00:00 2001
From: Oris Walft <46623531+oriswalft@users.noreply.github.com>
Date: Thu, 8 Dec 2022 21:15:21 +0100
Subject: [PATCH 02/18] Update Strings.fr-FR.axaml
---
OpenUtau/Strings/Strings.fr-FR.axaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenUtau/Strings/Strings.fr-FR.axaml b/OpenUtau/Strings/Strings.fr-FR.axaml
index 9ca607c2b..cfda388fd 100644
--- a/OpenUtau/Strings/Strings.fr-FR.axaml
+++ b/OpenUtau/Strings/Strings.fr-FR.axaml
@@ -270,7 +270,7 @@ Avertissement : moresampler n'est pas entièrement pris en charge. Il peut être
Note: pour utiliser des resamplers externes, ajoutez le fichier .exe ou .dll du resampler dans le dossier "Resamplers" de OpenUTAU, puis choisissez-le dans Préférences.
- <
+
Attention : trop de thread pourrait ralentir OpenUTAU.
Threads maximum pour le rendu
From b6a22509c403f983e46586bfd9a91af6abe267fe Mon Sep 17 00:00:00 2001
From: StAkira
Date: Sun, 11 Dec 2022 00:59:20 -0800
Subject: [PATCH 03/18] Update Strings.fr-FR.axaml
---
OpenUtau/Strings/Strings.fr-FR.axaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenUtau/Strings/Strings.fr-FR.axaml b/OpenUtau/Strings/Strings.fr-FR.axaml
index cfda388fd..3481a15c7 100644
--- a/OpenUtau/Strings/Strings.fr-FR.axaml
+++ b/OpenUtau/Strings/Strings.fr-FR.axaml
@@ -283,7 +283,7 @@ Avertissement : moresampler n'est pas entièrement pris en charge. Il peut être
Editer dans VLabeler
Ouvrir l'emplacement
Régénérer FRQ
- Régénération FRQ-
+ Régénération FRQ
Réinitialiser l'Oto
Sauvegarder l'Oto
Téléchargez vLabeler (1.0.0-beta1 ou plus) de https://github.com/sdercolin/vlabeler et définissez le chemin d'accès dans préférences d'abord !
From 3844ff1b1f44633fdff9dcff6c6179756bb36b79 Mon Sep 17 00:00:00 2001
From: oxygen-dioxide <54425948+oxygen-dioxide@users.noreply.github.com>
Date: Wed, 21 Dec 2022 16:37:56 +0800
Subject: [PATCH 04/18] Fix null lyric
---
OpenUtau.Core/OpenUtau.Core.csproj | 2 +-
OpenUtau.Core/Util/Yaml.cs | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/OpenUtau.Core/OpenUtau.Core.csproj b/OpenUtau.Core/OpenUtau.Core.csproj
index fd41727fb..a66834a16 100644
--- a/OpenUtau.Core/OpenUtau.Core.csproj
+++ b/OpenUtau.Core/OpenUtau.Core.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/OpenUtau.Core/Util/Yaml.cs b/OpenUtau.Core/Util/Yaml.cs
index fb38f68e7..143b210b3 100644
--- a/OpenUtau.Core/Util/Yaml.cs
+++ b/OpenUtau.Core/Util/Yaml.cs
@@ -13,6 +13,7 @@ public static class Yaml {
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
.WithEventEmitter(next => new FlowEmitter(next))
.DisableAliases()
+ .WithQuotingNecessaryStrings()
.Build();
public static IDeserializer DefaultDeserializer = new DeserializerBuilder()
From c80df690fd2d929517ee4780b05fd8089def36ee Mon Sep 17 00:00:00 2001
From: oxygen-dioxide <54425948+oxygen-dioxide@users.noreply.github.com>
Date: Wed, 21 Dec 2022 20:59:55 +0800
Subject: [PATCH 05/18] fix test
---
OpenUtau.Core/OpenUtau.Core.csproj | 2 +-
OpenUtau.Test/Classic/VoicebankConfigTest.cs | 15 ++++++++-------
OpenUtau.Test/Core/USTx/UstxYamlTest.cs | 8 ++++++++
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/OpenUtau.Core/OpenUtau.Core.csproj b/OpenUtau.Core/OpenUtau.Core.csproj
index a66834a16..8d66123e4 100644
--- a/OpenUtau.Core/OpenUtau.Core.csproj
+++ b/OpenUtau.Core/OpenUtau.Core.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/OpenUtau.Test/Classic/VoicebankConfigTest.cs b/OpenUtau.Test/Classic/VoicebankConfigTest.cs
index 9be170ae7..2db271c50 100644
--- a/OpenUtau.Test/Classic/VoicebankConfigTest.cs
+++ b/OpenUtau.Test/Classic/VoicebankConfigTest.cs
@@ -51,35 +51,36 @@ public void SerializationTest() {
var yaml = Yaml.DefaultSerializer.Serialize(CreateConfig());
output.WriteLine(yaml);
+ //"" evaluates to " in verbatim string literals
Assert.Equal(@"portrait_opacity: 0.75
symbol_set:
preset: hiragana
head: '-'
tail: R
subbanks:
-- prefix: ''
- suffix: ''
+- prefix: """"
+ suffix: """"
tone_ranges:
- C1-C4
-- prefix: ''
+- prefix: """"
suffix: D4
tone_ranges:
- C#4-F4
-- prefix: ''
+- prefix: """"
suffix: G4
tone_ranges:
- F#4-A#4
-- prefix: ''
+- prefix: """"
suffix: C5
tone_ranges:
- B4-B7
- color: power
- prefix: ''
+ prefix: """"
suffix: C5P
tone_ranges:
- B4-B7
- color: shout
- prefix: ''
+ prefix: """"
suffix: C5S
tone_ranges:
- B4-B7
diff --git a/OpenUtau.Test/Core/USTx/UstxYamlTest.cs b/OpenUtau.Test/Core/USTx/UstxYamlTest.cs
index 2c69680cb..159e21b31 100644
--- a/OpenUtau.Test/Core/USTx/UstxYamlTest.cs
+++ b/OpenUtau.Test/Core/USTx/UstxYamlTest.cs
@@ -72,6 +72,14 @@ public void SpecialLyric() {
yaml = Yaml.DefaultSerializer.Serialize(new UNote() { lyric = "-&" });
actual = Yaml.DefaultDeserializer.Deserialize(yaml);
Assert.Equal("-&", actual.lyric);
+
+ yaml = Yaml.DefaultSerializer.Serialize(new UNote() { lyric = "null" });
+ actual = Yaml.DefaultDeserializer.Deserialize(yaml);
+ Assert.Equal("null", actual.lyric);
+
+ yaml = Yaml.DefaultSerializer.Serialize(new UNote() { lyric = "true" });
+ actual = Yaml.DefaultDeserializer.Deserialize(yaml);
+ Assert.Equal("true", actual.lyric);
}
}
}
From d626bb446e98d455dec6a6f07ac763661e05cbde Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 22 Dec 2022 18:15:21 +0900
Subject: [PATCH 06/18] Create KoreanCBNNPhonemizer.cs
---
.../KoreanCBNNPhonemizer.cs | 802 ++++++++++++++++++
1 file changed, 802 insertions(+)
create mode 100644 OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
new file mode 100644
index 000000000..46419908b
--- /dev/null
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -0,0 +1,802 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using OpenUtau.Api;
+using OpenUtau.Classic;
+using OpenUtau.Core.Ustx;
+using Serilog;
+using static OpenUtau.Api.Phonemizer;
+
+namespace OpenUtau.Plugin.Builtin {
+ /// This phonemizer is based on 'KOR CVC Phonemizer'(by NANA). ///
+ [Phonemizer("Korean CBNN Phonemizer", "KO CBNN", "EX3exp", language:"KO")]
+
+ public class KoreanCBNNPhonemizer : Phonemizer {
+
+ // ↓ Plainvowels of [ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ]. //
+ static readonly string[] naPlainVowels = new string[] { "a", "e", "a", "e", "eo", "e", "eo", "e", "o", "a", "e", "e", "o", "u", "eo", "e", "i", "u", "eu", "i", "i" };
+ static readonly string[] naConsonants = new string[] {
+ "ㄱ:g","ㄲ:gg","ㄴ:n","ㄷ:d","ㄸ:dd","ㄹ:r","ㅁ:m","ㅂ:b","ㅃ:bb","ㅅ:s","ㅆ:ss","ㅇ:","ㅈ:j","ㅉ:jj","ㅊ:ch","ㅋ:k","ㅌ:t","ㅍ:p","ㅎ:h"
+ };
+
+ // ↓ ㅢ is e (* There's no "eui" in Kor CBNN *).//
+ static readonly string[] naVowels = new string[] {
+ "ㅏ:a","ㅐ:e","ㅑ:ya","ㅒ:ye","ㅓ:eo","ㅔ:e","ㅕ:yeo","ㅖ:ye","ㅗ:o","ㅘ:wa","ㅙ:we","ㅚ:we","ㅛ:yo","ㅜ:u","ㅝ:weo","ㅞ:we","ㅟ:wi","ㅠ:yu","ㅡ:eu","ㅢ:e","ㅣ:i"
+ };
+
+ // ↓ ["Grapheme : Phoneme"] of batchims.
+ static readonly string[] naFinals = new string[] {
+ ":","ㄱ:k","ㄲ:k","ㄳ:k","ㄴ:n","ㄵ:n","ㄶ:n","ㄷ:t","ㄹ:l","ㄺ:l","ㄻ:m","ㄼ:l","ㄽ:l","ㄾ:l","ㄿ:p","ㅀ:l","ㅁ:m","ㅂ:p","ㅄ:p","ㅅ:t","ㅆ:t","ㅇ:ng","ㅈ:t","ㅊ:t","ㅋ:k","ㅌ:t","ㅍ:p:1","ㅎ:t:2"
+ };
+ private const int hangeulStartIndex = 0xAC00; // unicode of '가'
+ private const int hangeulEndIndex = 0xD7A3; // unicode of '힣'
+
+ // ======================================================================================
+
+
+ // ↓ Plain vowels of Korean.
+ static readonly string[] plainVowels = new string[] { "eu", "eo", "a", "i", "u", "e", "o" };
+
+ // ↓ Vowels of romanized CVs.
+ static readonly string[] vowels = new string[] {
+ "eu=geu,neu,deu,reu,meu,beu,seu,eu,jeu,cheu,keu,teu,peu,heu,ggeu,ddeu,bbeu,sseu,jjeu",
+ "eo=geo,neo,deo,reo,meo,beo,seo,eo,jeo,cheo,keo,teo,peo,heo,ggeo,ddeo,bbeo,sseo,jjeo,gyeo,nyeo,dyeo,ryeo,myeo,byeo,syeo,yeo,jyeo,chyeo,kyeo,tyeo,pyeo,hyeo,ggyeo,ddyeo,bbyeo,ssyeo,jjyeo,gweo,nweo,dweo,rweo,mweo,bweo,sweo,weo,jweo,chweo,kweo,tweo,pweo,hweo,ggweo,ddweo,bbweo,ssweo,jjweo",
+ "a=ga,na,da,ra,ma,ba,sa,a,ja,cha,ka,ta,pa,ha,gga,dda,bba,ssa,jja,gya,nya,dya,rya,mya,bya,sya,ya,jya,chya,kya,tya,pya,hya,ggya,ddya,bbya,ssya,jjya,gwa,nwa,dwa,rwa,mwa,bwa,swa,wa,jwa,chwa,kwa,twa,pwa,hwa,ggwa,ddwa,bbwa,sswa,jjwa",
+ "e=ge,ne,de,re,me,be,se,e,je,che,ke,te,pe,he,gge,dde,bbe,sse,jje,gye,nye,dye,rye,mye,bye,sye,ye,jye,chye,kye,tye,pye,hye,ggye,ddye,bbye,ssye,jjye,gwe,nwe,dwe,rwe,mwe,bwe,swe,we,jwe,chwe,kwe,twe,pwe,hwe,ggwe,ddwe,bbwe,sswe,jjwe",
+ "i=gi,ni,di,ri,mi,bi,si,i,ji,chi,ki,ti,pi,hi,ggi,ddi,bbi,ssi,jji,gwi,nwi,dwi,rwi,mwi,bwi,swi,wi,jwi,chwi,kwi,twi,pwi,hwi,ggwi,ddwi,bbwi,sswi,jjwi",
+ "o=go,no,do,ro,mo,bo,so,o,jo,cho,ko,to,po,ho,ggo,ddo,bbo,sso,jjo,gyo,nyo,dyo,ryo,myo,byo,syo,yo,jyo,chyo,kyo,tyo,pyo,hyo,ggyo,ddyo,bbyo,ssyo,jjyo",
+ "u=gu,nu,du,ru,mu,bu,su,u,ju,chu,ku,tu,pu,hu,ggu,ddu,bbu,ssu,jju,gyu,nyu,dyu,ryu,myu,byu,syu,yu,jyu,chyu,kyu,tyu,pyu,hyu,ggyu,ddyu,bbyu,ssyu,jjyu",
+ "ng=ang,ing,ung,eng,ong,eung,eong",
+ "n=an,in,un,en,on,eun,eon",
+ "m=am,im,um,em,om,eum,eom",
+ "l=al,il,ul,el,ol,eul,eol",
+ "p=ap,ip,up,ep,op,eup,eop",
+ "t=at,it,ut,et,ot,eut,eot",
+ "k=ak,ik,uk,ek,ok,euk,eok"
+ };
+
+ // ↓ Pure onsonants of romanized CVs.
+ static readonly string[] consonants = new string[] {
+ "ggy=ggya,ggyu,ggye,ggyo,ggyeo",
+ "ggw=ggwa,ggwi,ggwe,ggweo",
+ "gg=gg,gga,ggi,ggu,gge,ggo,ggeu,ggeo",
+ "ddy=ddya,ddyu,ddye,ddyo,ddyeo",
+ "ddw=ddwa,ddwi,ddwe,ddweo",
+ "dd=dd,dda,ddi,ddu,dde,ddo,ddeu,ddeo",
+ "bby=bbya,bbyu,bbye,bbyo,bbyeo",
+ "bbw=bbwa,bbwi,bbwe,bbweo",
+ "bb=bb,bba,bbi,bbu,bbe,bbo,bbeu,bbeo",
+ "ssy=ssya,ssyu,ssye,ssyo,ssyeo",
+ "ssw=sswa,sswi,sswe,ssweo",
+ "ss=ss,ssa,ssi,ssu,sse,sso,sseu,sseo",
+ "gy=gya,gyu,gye,gyo,gyeo",
+ "gw=gwa,gwi,gwe,gweo",
+ "g=g,ga,gi,gu,ge,go,geu,geo",
+ "ny=nya,nyu,nye,nyo,nyeo",
+ "nw=nwa,nwi,nwe,nweo",
+ "n=n,na,ni,nu,ne,no,neu,neo",
+ "dy=dya,dyu,dye,dyo,dyeo",
+ "dw=dwa,dwi,dwe,dweo",
+ "d=d,da,di,du,de,do,deu,deo",
+ "ry=rya,ryu,rye,ryo,ryeo",
+ "rw=rwa,rwi,rwe,rweo",
+ "r=r,ra,ri,ru,re,ro,reu,reo",
+ "my=mya,myu,mye,myo,myeo",
+ "mw=mwa,mwi,mwe,mweo",
+ "m=m,ma,mi,mu,me,mo,meu,meo",
+ "by=bya,byu,bye,byo,byeo",
+ "bw=bwa,bwi,bwe,bweo",
+ "b=b,ba,bi,bu,be,bo,beu,beo",
+ "sy=sya,syu,sye,syo,syeo",
+ "sw=swa,swi,swe,sweo",
+ "s=s,sa,si,su,se,so,seu,seo",
+ "jy=jya,jyu,jye,jyo,jyeo",
+ "jw=jwa,jwi,jwe,jweo",
+ "j=j,ja,ji,ju,je,jo,jeu,jeo",
+ "chy=chya,chyu,chye,chyo,chyeo,chwa",
+ "chw=chwi,chwe,chweo",
+ "ch=ch,cha,chi,chu,che,cho,cheu,cheo",
+ "ky=kya,kyu,kye,kyo,kyeo",
+ "kw=kwa,kwi,kwe,kweo",
+ "k=k,ka,ki,ku,ke,ko,keu,keo",
+ "ty=tya,tyu,tye,tyo,tyeo",
+ "tw=twa,twi,twe,tweo",
+ "t=t,ta,ti,tu,te,to,teu,teo",
+ "py=pya,pyu,pye,pyo,pyeo",
+ "pw=pwa,pwi,pwe,pweo",
+ "p=p,pa,pi,pu,pe,po,peu,peo",
+ "hy=hya,hyu,hye,hyo,hyeo",
+ "hw=hwa,hwi,hwe,hweo",
+ "h=h,ha,hi,hu,he,ho,heu,heo"
+ };
+
+ static readonly Dictionary vowelLookup;
+ static readonly Dictionary consonantLookup;
+
+ string getConsonant(string str) {
+ str = str.Replace('a', ' ');
+ str = str.Replace('i', ' ');
+ str = str.Replace('u', ' ');
+ str = str.Replace('e', ' ');
+ str = str.Replace('o', ' ');
+ str = str.Trim();
+
+ return str;
+ }
+
+ bool isAlphaCon(string consStr) {
+ String str = consStr.Replace('w', ' ');
+ str = consStr.Replace('y', ' ');
+ str = str.Trim();
+
+ if (str == "gg") { return true; }
+ else if (str == "dd") { return true; }
+ else if (str == "bb") { return true; }
+ else if (str == "ss") { return true; }
+ else if (str == "g") { return true; }
+ else if (str == "n") { return true; }
+ else if (str == "d") { return true; }
+ else if (str == "r") { return true; }
+ else if (str == "m") { return true; }
+ else if (str == "b") { return true; }
+ else if (str == "s") { return true; }
+ else if (str == "j") { return true; }
+ else if (str == "ch") { return true; }
+ else if (str == "k") { return true; }
+ else if (str == "t") { return true; }
+ else if (str == "p") { return true; }
+ else if (str == "h") { return true; }else { return false; }
+ }
+
+ static KoreanCBNNPhonemizer() {
+ vowelLookup = vowels.ToList()
+ .SelectMany(line => {
+ var parts = line.Split('=');
+ return parts[1].Split(',').Select(cv => (cv, parts[0]));
+ })
+ .ToDictionary(t => t.Item1, t => t.Item2);
+ consonantLookup = consonants.ToList()
+ .SelectMany(line => {
+ var parts = line.Split('=');
+ return parts[1].Split(',').Select(cv => (cv, parts[0]));
+ })
+ .ToDictionary(t => t.Item1, t => t.Item2);
+ }
+
+
+ // ======================================================================================
+
+
+ private USinger singer;
+ public override void SetSinger(USinger singer) => this.singer = singer;
+
+ // make it quicker to check multiple oto occurrences at once rather than spamming if else if
+ private bool checkOtoUntilHit(string[] input, Note note, out UOto oto){
+ oto = default;
+
+ var attr0 = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
+ var attr1 = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 1) ?? default;
+
+ foreach (string test in input){
+ if (singer.TryGetMappedOto(test, note.tone + attr0.toneShift, attr0.voiceColor, out oto)){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevNeighbours) {
+ var note = notes[0];
+ var currentUnicode = ToUnicodeElements(note.lyric); // ← unicode of current lyric
+ string currentLyric = note.lyric; // ← string of current lyric
+ var attr0 = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
+ var attr1 = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 1) ?? default;
+
+ //-----------------------------------------------------------------------//
+ ////// *** ↓↓↓ Seperates Lyrics in: //
+ ///// - first consonant letter(초성, "consonant" in below), //
+ ///// - middle vowel letter(중성, "vowel" in below), //
+ ///// - last consonant letter(종성, "final" in below) ↓↓↓ *** //.
+
+
+ //// ↓↓ 1 ** Variables for 'Current Notes' ** --
+ // ↓ index of "consonant", "vowel", "final".
+ int CLconsonant = 0;
+ int CLvowel = 0;
+ int CLfinal = 0;
+
+ // ↓ Use for Temp
+ string[] TCLtemp;
+
+ // ↓ use these for applying phonological rules
+ string TCLconsonant = "";
+ string TCLvowel = "";
+ string TCLfinal = "";
+ string TCLplainvowel = ""; //← Simplifies vowels
+
+ int TCLsemivowel = 0; // semi vowel is 'y', 'w'. [0 means "there's no semi vowel], [1 means "there is 'y'"], [2 means "there is 'w'"]]
+
+ // ↓ use these for generating phonemes in phonemizers
+ string TCLconsonantCBNN = "";
+ string TCLvowelCBNN = "";
+
+ //// ↓↓ 2 ** Variables for 'Next Notes' ** --
+ // ↓ index of "consonant", "vowel", "final".
+ int NLconsonant = 0;
+ int NLvowel = 0;
+ int NLfinal = 0;
+
+ // ↓ Use for Temp
+ string[] TNLtemp;
+
+ // ↓ use these for applying phonological rules
+ string TNLconsonant = "";
+ string TNLvowel = "";
+ string TNLfinal = "";
+ string TNLplainvowel = "";
+
+ // ↓ use these for generating phonemes in phonemizers
+ string TNLconsonantCBNN = "";
+ //string TNLvowelCBNN = "";
+
+ int TNLsemivowel = 0; // semi vowel is 'y', 'w'. [0 means "there's no semi vowel], [1 means "there is 'y'"], [2 means "there is 'w'"]]
+
+ //// ↓↓ 3 ** Variables for 'Previous Notes' ** --
+ // ↓ index of "consonant", "vowel", "final".
+ int PLconsonant = 0;
+ int PLvowel = 0;
+ int PLfinal = 0;
+
+ // ↓ Use for Temp
+ string[] TPLtemp;
+
+ // ↓ use these for applying phonological rules
+ string TPLconsonant = "";
+ string TPLvowel = "";
+ string TPLfinal = "";
+ string TPLplainvowel = "";
+
+ // ↓ use these for generating phonemes in phonemizers
+ //string TPLconsonantCBNN = "";
+ //string TPLvowelCBNN = "";
+
+ //int TPLsemivowel = 0; // semi vowel is 'y', 'w'. [0 means "there's no semi vowel], [1 means "there is 'y'"], [2 means "there is 'w'"]]
+
+
+ //// ↓↓ 4 ** Variables for checking notes ** --
+ bool currentHangeul = false;
+ bool prevHangeul = false;
+ bool nextHangeul = false;
+
+ bool prevExist = false;
+ bool nextExist = false;
+
+ char firstCL, firstPL, firstNL;
+ int uCL, uPL, uNL;
+
+
+ // check first lyric
+ firstCL = currentLyric[0];
+
+ uCL = (int)firstCL;
+ if ((uCL >= hangeulStartIndex) && (uCL <= hangeulEndIndex)) {
+ currentHangeul = true;
+ CLconsonant = (uCL - hangeulStartIndex) / (21 * 28);
+ CLvowel = (uCL - hangeulStartIndex) % (21 * 28) / 28;
+ CLfinal = (uCL - hangeulStartIndex) % 28;
+
+
+ TCLtemp = naVowels[CLvowel].Split(":");
+ TCLvowel = TCLtemp[1];
+ TCLplainvowel = naPlainVowels[CLvowel];
+
+ if (TCLvowel.StartsWith('y')) {TCLsemivowel = 1;}
+ else if (TCLvowel.StartsWith('w')) {TCLsemivowel = 2;}
+
+ TCLtemp = naConsonants[CLconsonant].Split(":");
+ TCLconsonant = TCLtemp[1];
+
+ TCLtemp = naFinals[CLfinal].Split(":");
+ TCLfinal = TCLtemp[1];
+
+
+ // TCLconsonant : 현노트 초성 TCLvowel : 현노트 중성 TCLfinal : 현노트 종성
+
+ }
+
+ // 이전 노트 존재 여부 확인 + 이전 노트 첫번째 글자 확인
+ if (prevNeighbour != null) {
+ firstPL = (prevNeighbour?.lyric)[0]; // 가사 받아오기
+ prevExist = true; // 이전 노트 존재한다 반짝
+
+ uPL = (int)firstPL; // 가사를 int로 변환
+
+ if ((uPL >= hangeulStartIndex) && (uPL <= hangeulEndIndex)) {
+ prevHangeul = true;
+
+ PLconsonant = (uPL - hangeulStartIndex) / (21 * 28);
+ PLvowel = (uPL - hangeulStartIndex) % (21 * 28) / 28;
+ PLfinal = (uPL - hangeulStartIndex) % 28;
+
+
+ TPLtemp = naConsonants[PLconsonant].Split(":");
+ TPLconsonant = TPLtemp[1];
+
+ TPLtemp = naVowels[PLvowel].Split(":");
+ TPLvowel = TPLtemp[1];
+ TPLplainvowel = naPlainVowels[PLvowel];
+
+ //if (TPLvowel.StartsWith('y')) {TPLsemivowel = 1;}
+ //else if (TPLvowel.StartsWith('w')) {TPLsemivowel = 2;}
+
+ TPLtemp = naFinals[PLfinal].Split(":");
+ TPLfinal = TPLtemp[1];
+ }
+ }
+
+ // 다음 노트 존재 여부 확인 + 다음 노트 첫번째 글자 확인
+ if (nextNeighbour != null) {
+ firstNL = (nextNeighbour?.lyric)[0];
+ nextExist = true;
+ uNL = (int)firstNL;
+
+ if ((uNL >= hangeulStartIndex) && (uNL <= hangeulEndIndex)) {
+ nextHangeul = true;
+
+ NLconsonant = (uNL - hangeulStartIndex) / (21 * 28);
+ NLvowel = (uNL - hangeulStartIndex) % (21 * 28) / 28;
+ NLfinal = (uNL - hangeulStartIndex) % 28;
+
+
+ TNLtemp = naConsonants[NLconsonant].Split(":");
+ TNLconsonant = TNLtemp[1];
+
+ TNLtemp = naVowels[NLvowel].Split(":");
+ TNLvowel = TNLtemp[1];
+ TNLplainvowel = naPlainVowels[NLvowel];
+
+ if (TNLvowel.StartsWith('y')) {TNLsemivowel = 1;}
+ else if (TNLvowel.StartsWith('w')) {TNLsemivowel = 2;}
+
+
+ TNLtemp = naFinals[NLfinal].Split(":");
+ TNLfinal = TNLtemp[1];
+ }
+ }
+
+ if (currentHangeul) {
+ // 음운규칙 적용
+ if (currentHangeul) {
+
+ // 1. 연음법칙
+ string tempTCLconsonant = "";
+ string tempTCLfinal = "";
+ bool yeoneum = false;
+ bool yeoneum2 = false;
+
+ if (prevExist && prevHangeul && (CLconsonant == 11) && (TPLfinal != "")) {
+ int temp = PLfinal;
+ if (temp == 1) { TCLtemp = naConsonants[0].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 2) { TCLtemp = naConsonants[1].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 3) { TCLtemp = naConsonants[10].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 4) { TCLtemp = naConsonants[2].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 5) { TCLtemp = naConsonants[12].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 6) { TCLtemp = naConsonants[18].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 7) { TCLtemp = naConsonants[3].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 8) { TCLtemp = naConsonants[5].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 9) { TCLtemp = naConsonants[0].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 10) { TCLtemp = naConsonants[6].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 11) { TCLtemp = naConsonants[7].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 12) { TCLtemp = naConsonants[9].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 13) { TCLtemp = naConsonants[16].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 14) { TCLtemp = naConsonants[17].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 15) { TCLtemp = naConsonants[18].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 16) { TCLtemp = naConsonants[6].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 17) { TCLtemp = naConsonants[7].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 18) { TCLtemp = naConsonants[9].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 19) { TCLtemp = naConsonants[9].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 20) { TCLtemp = naConsonants[10].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 21) { tempTCLconsonant = ""; yeoneum = true; }
+ else if (temp == 22) { TCLtemp = naConsonants[12].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 23) { TCLtemp = naConsonants[14].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 24) { TCLtemp = naConsonants[15].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 25) { TCLtemp = naConsonants[16].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 26) { TCLtemp = naConsonants[17].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ else if (temp == 27) { TCLtemp = naConsonants[18].Split(":"); tempTCLconsonant = TCLtemp[1]; yeoneum = true; }
+ }
+
+ if (nextExist && nextHangeul && (TCLfinal != "") && (TNLconsonant == "")) {
+ int temp = CLfinal;
+
+ if (temp == 1) { TCLtemp = naConsonants[0].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 2) { TCLtemp = naConsonants[1].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 3) { TCLfinal = "k"; yeoneum2 = true; }
+ else if (temp == 4) { TCLtemp = naConsonants[2].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 5) { TCLfinal = "n"; yeoneum2 = true; }
+ else if (temp == 6) { TCLfinal = "n"; yeoneum2 = true; }
+ else if (temp == 7) { TCLtemp = naConsonants[3].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 8) { TCLtemp = naConsonants[5].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 9) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 10) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 11) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 12) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 13) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 14) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 15) { TCLfinal = "l"; yeoneum2 = true; }
+ else if (temp == 16) { TCLtemp = naConsonants[6].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 17) { TCLtemp = naConsonants[7].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 18) { TCLfinal = "p"; yeoneum2 = true; }
+ else if (temp == 19) { TCLtemp = naConsonants[9].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 20) { TCLtemp = naConsonants[10].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ //else if (temp == 21) { TCLtemp = naConsonants[11].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+ else if (temp == 22) { TCLtemp = naConsonants[12].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; } else if (temp == 23) { TCLtemp = naConsonants[14].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; } else if (temp == 24) { TCLtemp = naConsonants[15].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; } else if (temp == 25) { TCLtemp = naConsonants[16].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; } else if (temp == 26) { TCLtemp = naConsonants[17].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; } else if (temp == 27) { TCLtemp = naConsonants[18].Split(":"); tempTCLfinal = TCLtemp[1]; TCLfinal = ""; yeoneum2 = true; }
+
+ }
+ if (yeoneum) { TCLconsonant = tempTCLconsonant; }
+ if (yeoneum2) { TNLconsonant = tempTCLfinal; }
+
+
+ // 2. 격음화/유기음화/거센소리되기
+ if (prevExist && prevHangeul && (TPLfinal != "")) {
+ if (((PLfinal == 27) && (CLconsonant == 0)) || ((PLfinal == 6) && (CLconsonant == 0)) || ((PLfinal == 15) && (CLconsonant == 0))) { TCLconsonant = "k"; } else if (((PLfinal == 27) && (CLconsonant == 3)) || ((PLfinal == 6) && (CLconsonant == 3)) || ((PLfinal == 15) && (CLconsonant == 3))) { TCLconsonant = "t"; } else if (((PLfinal == 27) && (CLconsonant == 12)) || ((PLfinal == 6) && (CLconsonant == 12)) || ((PLfinal == 15) && (CLconsonant == 12))) { TCLconsonant = "ch"; } else if (((PLfinal == 27) && (CLconsonant == 9)) || ((PLfinal == 6) && (CLconsonant == 9)) || ((PLfinal == 15) && (CLconsonant == 9))) { TCLconsonant = "ss"; }
+
+ if ((PLfinal == 1) && (CLconsonant == 18)) { TCLconsonant = "k"; } else if ((PLfinal == 7) && (CLconsonant == 18)) { TCLconsonant = "t"; } else if ((PLfinal == 17) && (CLconsonant == 18)) { TCLconsonant = "p"; } else if ((PLfinal == 22) && (CLconsonant == 18)) { TCLconsonant = "ch"; }
+ }
+ if (nextExist && nextHangeul && (TCLfinal != "")) {
+ if ((NLconsonant == 0) && (CLfinal == 27)) { TCLfinal = ""; TNLconsonant = "k"; } else if ((NLconsonant == 0) && (CLfinal == 6)) { TCLfinal = "n"; TNLconsonant = "k"; } else if ((NLconsonant == 0) && (CLfinal == 15)) { TCLfinal = "l"; TNLconsonant = "k"; } else if ((NLconsonant == 3) && (CLfinal == 27)) { TCLfinal = ""; TNLconsonant = "t"; } else if ((NLconsonant == 3) && (CLfinal == 6)) { TCLfinal = "n"; TNLconsonant = "t"; } else if ((NLconsonant == 3) && (CLfinal == 15)) { TCLfinal = "l"; TNLconsonant = "t"; } else if ((NLconsonant == 12) && (CLfinal == 27)) { TCLfinal = ""; TNLconsonant = "ch"; } else if ((NLconsonant == 12) && (CLfinal == 6)) { TCLfinal = "n"; TNLconsonant = "ch"; } else if ((NLconsonant == 12) && (CLfinal == 15)) { TCLfinal = "l"; TNLconsonant = "ch"; } else if ((NLconsonant == 9) && (CLfinal == 27)) { TCLfinal = ""; TNLconsonant = "ss"; } else if ((NLconsonant == 9) && (CLfinal == 6)) { TCLfinal = "n"; TNLconsonant = "ss"; } else if ((NLconsonant == 9) && (CLfinal == 15)) { TCLfinal = "l"; TNLconsonant = "ss"; }
+
+ if ((NLconsonant == 2) && (CLfinal == 27)) { TCLfinal = "n"; }
+
+ if ((NLconsonant == 18) && (CLfinal == 1)) { TCLfinal = ""; TNLconsonant = "k"; } else if ((NLconsonant == 18) && (CLfinal == 7)) { TCLfinal = ""; TNLconsonant = "t"; } else if ((NLconsonant == 18) && (CLfinal == 17)) { TCLfinal = ""; TNLconsonant = "p"; } else if ((NLconsonant == 18) && (CLfinal == 22)) { TCLfinal = ""; TNLconsonant = "ch"; }
+ }
+
+
+ // 3. 음절의 끝소리 규칙 예외
+ if (nextExist && nextHangeul) {
+ /*
+ // ㄼ + 자음이 있을 때 => ㄼ : p
+ if ((CLfinal == 11) && (TCLconsonant != "")) { TCLfinal = "p"; }
+ */
+ // ㄺ + ㄱ => ㄺ : ㄹ
+ if ((CLfinal == 9) && (NLconsonant == 0)) { TCLfinal = "l"; }
+ }
+
+
+ // 4. 경음화/된소리되기
+ if (prevExist && prevHangeul && TPLfinal != "") {
+ // ㄱㄷㅂ + ㄱㄷㅂㅅㅈ = ㄲㄸㅃㅆㅉ
+ if (((TPLfinal == "k") && (CLconsonant == 0)) || ((TPLfinal == "t") && (CLconsonant == 0)) || ((TPLfinal == "p") && (CLconsonant == 0))) { TCLconsonant = "gg"; } else if (((TPLfinal == "k") && (CLconsonant == 3)) || ((TPLfinal == "t") && (CLconsonant == 3)) || ((TPLfinal == "p") && (CLconsonant == 3))) { TCLconsonant = "dd"; } else if (((TPLfinal == "k") && (CLconsonant == 7)) || ((TPLfinal == "t") && (CLconsonant == 7)) || ((TPLfinal == "p") && (CLconsonant == 7))) { TCLconsonant = "bb"; } else if (((TPLfinal == "k") && (CLconsonant == 9)) || ((TPLfinal == "t") && (CLconsonant == 9)) || ((TPLfinal == "p") && (CLconsonant == 9))) { TCLconsonant = "ss"; } else if (((TPLfinal == "k") && (CLconsonant == 12)) || ((TPLfinal == "t") && (CLconsonant == 12)) || ((TPLfinal == "p") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
+
+ /*
+ // 용언 어간 받침 ㄴㅁ + ㄱㄷㅅㅈ = ㄲㄸㅆㅉ
+ if(((TPLfinal=="n")&&(CLconsonant==0))|| ((TPLfinal == "m") && (CLconsonant == 0))) { TCLconsonant = "gg"; }
+ else if (((TPLfinal == "n") && (CLconsonant == 3)) || ((TPLfinal == "m") && (CLconsonant == 3))) { TCLconsonant = "dd"; }
+ else if (((TPLfinal == "n") && (CLconsonant == 9)) || ((TPLfinal == "m") && (CLconsonant == 9))) { TCLconsonant = "ss"; }
+ else if (((TPLfinal == "n") && (CLconsonant == 12)) || ((TPLfinal == "m") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
+ */
+
+ // 관형사형 어미ㄹ / 한자어 ㄹ + ㄷㅅㅈ = ㄸㅆㅉ
+ if ((PLfinal == 8) && (CLconsonant == 3)) { TCLconsonant = "dd"; } else if ((PLfinal == 8) && (CLconsonant == 9)) { TCLconsonant = "ss"; } else if ((PLfinal == 8) && (CLconsonant == 12)) { TCLconsonant = "jj"; }
+
+ // 어간 받침 ㄼㄾ + ㄱㄷㅅㅈ = ㄲㄸㅆㅉ
+ if (((PLfinal == 11) && (CLconsonant == 0)) || ((PLfinal == 13) && (CLconsonant == 0))) { TCLconsonant = "gg"; } else if (((PLfinal == 11) && (CLconsonant == 3)) || ((PLfinal == 13) && (CLconsonant == 3))) { TCLconsonant = "dd"; } else if (((PLfinal == 11) && (CLconsonant == 9)) || ((PLfinal == 13) && (CLconsonant == 9))) { TCLconsonant = "ss"; } else if (((PLfinal == 11) && (CLconsonant == 12)) || ((PLfinal == 13) && (CLconsonant == 12))) { TCLconsonant = "jj"; }
+ }
+
+
+ // 5. 구개음화
+ if (prevExist && prevHangeul && (TPLfinal != "")) {
+ if ((PLfinal == 7) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "j"; } else if ((PLfinal == 25) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "ch"; } else if ((PLfinal == 13) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "ch"; } else if ((PLfinal == 7) && (CLconsonant == 18) && (CLvowel == 20)) { TCLconsonant = "ch"; }
+ }
+ if (nextExist && nextHangeul && (TCLfinal != "")) {
+ if ((CLfinal == 7) && (NLconsonant == 11) && (NLvowel == 20)) { TCLfinal = ""; } else if ((CLfinal == 25) && (NLconsonant == 11) && (NLvowel == 20)) { TCLfinal = ""; } else if ((CLfinal == 13) && (NLconsonant == 11) && (NLvowel == 20)) { TCLfinal = ""; } else if ((CLfinal == 7) && (NLconsonant == 18) && (NLvowel == 20)) { TCLfinal = ""; }
+
+ }
+
+
+ // 6. 비음화
+ if (prevExist && prevHangeul && (TPLfinal != "")) {
+ // 한자어 받침 ㅁㅇ + ㄹ = ㄴ
+ if (((TPLfinal == "m") && (CLconsonant == 5)) || ((TPLfinal == "ng") && (CLconsonant == 5))) { TCLconsonant = "n"; }
+
+ // 한자어 받침 ㄱㄷㅂ + ㄹ = ㅇㄴㅁ + ㄴ(1)
+ if (((TPLfinal == "k") && (CLconsonant == 5)) || ((TPLfinal == "t") && (CLconsonant == 5)) || ((TPLfinal == "p") && (CLconsonant == 5))) { TCLconsonant = "n"; }
+ }
+ if (nextExist && nextHangeul && (TCLfinal != "")) {
+ //받침 ㄱㄷㅂ + ㄴㅁ = ㅇㄴㅁ
+ if (((TCLfinal == "k") && (TNLconsonant == "n")) || ((TCLfinal == "k") && (TNLconsonant == "m"))) { TCLfinal = "ng"; } else if (((TCLfinal == "t") && (TNLconsonant == "n")) || ((TCLfinal == "t") && (TNLconsonant == "m"))) { TCLfinal = "n"; } else if (((TCLfinal == "p") && (TNLconsonant == "n")) || ((TCLfinal == "p") && (TNLconsonant == "m"))) { TCLfinal = "m"; }
+
+ // 한자어 받침 ㄱㄷㅂ + ㄹ = ㅇㄴㅁ + ㄴ(2)
+ if ((TCLfinal == "k") && (NLconsonant == 5)) { TCLfinal = "ng"; } else if ((TCLfinal == "t") && (NLconsonant == 5)) { TCLfinal = "n"; } else if ((TCLfinal == "p") && (NLconsonant == 5)) { TCLfinal = "m"; }
+ }
+
+
+ // 7. 유음화
+ if (prevExist && prevHangeul && (TPLfinal != "")) {
+ if (((PLfinal == 8) && (TCLconsonant == "n")) || ((PLfinal == 13) && (TCLconsonant == "n")) || ((PLfinal == 15) && (TCLconsonant == "n"))) { TCLconsonant = "r"; }
+ }
+ if (nextExist && nextHangeul && (TCLfinal != "")) {
+ if ((TCLfinal == "n") && (TNLconsonant == "r")) { TCLfinal = "l"; }
+ }
+
+
+
+ // 8. 받침 + ㄹ = ㄹㄹ
+
+
+
+ // consonant에 변경 사항이 있을 때
+ if (prevExist && prevHangeul) {
+
+
+ // 비음화
+ // (1) ㄱ(ㄲㅋㄳㄺ)
+ // ㄷ(ㅅ,ㅆ,ㅈ,ㅊ,ㅌ,ㅎ)
+ // ㅂ(ㅍ,ㄼ,ㄿ,ㅄ)
+
+
+ }
+ // final에 변경 사항이 있을 때
+
+
+ }
+ // To use semivowels in VC (ex: [- ga][a gy][gya], ** so not [- ga][a g][gya] **)
+ if (TCLsemivowel == 1 && TPLplainvowel != "i" && TPLplainvowel != "eu") {TCLconsonantCBNN = TCLconsonant + 'y';}
+ else if (TCLsemivowel == 2 && TPLplainvowel != "u" && TPLplainvowel != "o" && TPLplainvowel != "eu") {TCLconsonantCBNN = TCLconsonant + 'w';}
+ else {TCLconsonantCBNN = TCLconsonant;}
+
+ if (TNLsemivowel == 1 && TCLplainvowel != "i" && TCLplainvowel != "eu") {TNLconsonantCBNN = TNLconsonant + 'y';}
+ else if (TNLsemivowel == 2 && TCLplainvowel != "u" && TCLplainvowel != "o" && TCLplainvowel != "eu") {TNLconsonantCBNN = TNLconsonant + 'w';}
+ else {TNLconsonantCBNN = TNLconsonant;}
+
+
+ //To set suffix of CV, according to next-coming batchim.
+ if (TCLfinal == "") {TCLvowelCBNN = TCLvowel;}
+ else if (TCLfinal == "m" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '1';}
+ else if (TCLfinal == "n" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '2';}
+ else if (TCLfinal == "ng" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '3';}
+ else if (TCLfinal == "l" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '4';}
+ else if (TCLfinal == "k" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '3';}
+ else if (TCLfinal == "t" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '2';}
+ else if (TCLfinal == "p" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '1';}
+ else {TCLvowelCBNN = TCLvowel;}
+
+
+ string CV = (TCLconsonant + TCLvowelCBNN);
+ string VC = "";
+ if (nextExist && (TCLfinal == "")) { VC = TCLplainvowel + " " + TNLconsonantCBNN; }
+
+ //for Vowel VCV
+ if (prevExist && TPLfinal == "" && TCLconsonantCBNN == "") {CV = TPLplainvowel + " " + TCLvowel;}
+
+
+ string FC = "";
+ if (TCLfinal != "") { FC = TCLplainvowel + TCLfinal; }
+
+
+ // 만약 앞에 노트가 없다면
+ if (!prevExist) { CV = $"- {CV}"; }
+
+ // 만약 받침이 있다면
+ if (FC != "") {
+ int totalDuration = notes.Sum(n => n.duration);
+ int fcLength = totalDuration / 3;
+ if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) { fcLength = totalDuration / 2; }
+
+ if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(FC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
+ CV = oto1.Alias;
+ FC = oto2.Alias;
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = CV,
+ },
+ new Phoneme() {
+ phoneme = FC,
+ position = totalDuration - fcLength,
+ }
+ },
+ };
+ }
+ }
+
+
+ // 만약 받침이 없다면
+ if (TCLfinal == "") {
+ // 뒤에 노트가 있다면
+ if ((TNLconsonant != "")) {
+ int totalDuration = notes.Sum(n => n.duration);
+ int vcLength = 60;
+ if ((TNLconsonant == "r") || (TNLconsonant == "h")) { vcLength = 30; }
+ else if (TNLconsonant == "s") { vcLength = totalDuration/3; }
+ else if ((TNLconsonant == "k") || (TNLconsonant == "t") || (TNLconsonant == "p") || (TNLconsonant == "ch")) { vcLength = totalDuration / 2; }
+ else if ((TNLconsonant == "gg") || (TNLconsonant == "dd") || (TNLconsonant == "bb") || (TNLconsonant == "ss") || (TNLconsonant == "jj")) { vcLength = totalDuration / 2; }
+ vcLength = Math.Min(totalDuration / 2, vcLength);
+
+ if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(VC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
+ CV = oto1.Alias;
+ VC = oto2.Alias;
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = CV,
+ },
+ new Phoneme() {
+ phoneme = VC,
+ position = totalDuration - vcLength,
+ }
+ },
+ };
+ }
+
+ }
+ }
+
+
+ // 그 외(받침 없는 마지막 노트)
+ if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto)){
+ CV = oto.Alias;
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = CV,
+ }
+ },
+ };
+ }
+ }
+
+ if (prevHangeul) {
+ string endBreath = "-";
+
+ if (prevExist && TPLfinal == "" && endBreath.Contains(currentLyric)) {
+ endBreath = $"{TPLplainvowel} -";
+ }
+
+ if (singer.TryGetMappedOto(endBreath, note.tone + attr0.toneShift, attr0.voiceColor, out var oto)){
+ endBreath = oto.Alias;
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = endBreath,
+ }
+ },
+ };
+ }
+ }
+
+
+
+
+ // ======================================================================================
+
+ if (prevNeighbour == null) {
+ // Use "- V" or "- CV" if present in voicebank
+ var initial = $"- {currentLyric}";
+ string[] tests = new string[] {initial, currentLyric};
+ // try [- XX] before trying plain lyric
+ if (checkOtoUntilHit(tests, note, out var oto)){
+ currentLyric = oto.Alias;
+ }
+ } else if ("R".Contains(currentLyric)) {
+ var prevUnicode = ToUnicodeElements(prevNeighbour?.lyric);
+ // end breath note
+ if (vowelLookup.TryGetValue(prevUnicode.LastOrDefault() ?? string.Empty, out var vow)) {
+ var vowel = "";
+ var prevLyric = string.Join("", prevUnicode);;
+ vowel = vow;
+
+ var endBreath = $"{vow} -";
+ if (prevLyric.EndsWith("eo")) {
+ endBreath = $"eo -";
+ } else if (prevLyric.EndsWith("eu")) {
+ endBreath = $"eu -";
+ }
+
+ // try end breath
+ string[] tests = new string[] {endBreath, currentLyric};
+ if (checkOtoUntilHit(tests, note, out var oto)){
+ currentLyric = oto.Alias;
+ }
+ }
+ } else {
+ string[] tests = new string[] {currentLyric};
+ if (checkOtoUntilHit(tests, note, out var oto)){
+ currentLyric = oto.Alias;
+ }
+ }
+
+ if (nextNeighbour != null) { // 다음에 노트가 있으면
+ var nextUnicode = ToUnicodeElements(nextNeighbour?.lyric);
+ var nextLyric = string.Join("", nextUnicode);
+
+ // Check if next note is a vowel and does not require VC
+ if (plainVowels.Contains(nextUnicode.FirstOrDefault() ?? string.Empty)) {
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = currentLyric,
+ }
+ },
+ };
+ }
+
+ // Insert VC before next neighbor
+ // Get vowel from current note
+ var vowel = "";
+
+ if (vowelLookup.TryGetValue(currentUnicode.LastOrDefault() ?? string.Empty, out var vow)) {
+ vowel = vow;
+
+ if (currentLyric.Contains("e")) {
+ vowel = "e" + vowel;
+ vowel = vowel.Replace("ee", "e");
+ }
+ }
+
+ // Get consonant from next note
+ var consonant = "";
+ if (consonantLookup.TryGetValue(nextUnicode.FirstOrDefault() ?? string.Empty, out var con)) {
+ consonant = getConsonant(nextNeighbour?.lyric); //로마자만 가능
+ if (!(isAlphaCon(consonant))) { consonant = con; }
+ }
+
+ if (consonant == "") {
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = currentLyric,
+ }
+ },
+ };
+ }
+
+ var vcPhoneme = $"{vowel} {consonant}";
+ var vcPhonemes = new string[] {vcPhoneme, ""};
+ if (checkOtoUntilHit(vcPhonemes, note, out var oto1)) {
+ vcPhoneme = oto1.Alias;
+ } else {
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = currentLyric,
+ }
+ },
+ };
+ }
+
+ int totalDuration = notes.Sum(n => n.duration);
+ int vcLength = 60;
+ var nextAttr = nextNeighbour.Value.phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
+ if (singer.TryGetMappedOto(nextLyric, nextNeighbour.Value.tone + nextAttr.toneShift, nextAttr.voiceColor, out var oto)) {
+ vcLength = MsToTick(oto.Preutter);
+ }
+ vcLength = Math.Min(totalDuration / 2, vcLength);
+
+
+
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme() {
+ phoneme = currentLyric,
+ },
+ new Phoneme() {
+ phoneme = vcPhoneme,
+ position = totalDuration - vcLength,
+ }
+ },
+ };
+ }
+
+ // No next neighbor
+ return new Result {
+ phonemes = new Phoneme[] {
+ new Phoneme {
+ phoneme = currentLyric,
+ }
+ },
+ };
+ }
+ }
+}
From ca35bfc5392779f45da2ee2ae01e62d8cdb2376f Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 22 Dec 2022 18:33:15 +0900
Subject: [PATCH 07/18] Update KoreanCBNNPhonemizer.cs
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 46419908b..ac57930f3 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -10,7 +10,7 @@
namespace OpenUtau.Plugin.Builtin {
/// This phonemizer is based on 'KOR CVC Phonemizer'(by NANA). ///
- [Phonemizer("Korean CBNN Phonemizer", "KO CBNN", "EX3exp", language:"KO")]
+ [Phonemizer("Korean CBNN Phonemizer", "KO CBNN", "EX3", language:"KO")]
public class KoreanCBNNPhonemizer : Phonemizer {
From 18bcc94a13a1c34445b25248352be57a5bd5ae41 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 22 Dec 2022 18:34:49 +0900
Subject: [PATCH 08/18] Update KoreanCBNNPhonemizer.cs
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index ac57930f3..c30707a41 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -56,7 +56,7 @@ public class KoreanCBNNPhonemizer : Phonemizer {
"k=ak,ik,uk,ek,ok,euk,eok"
};
- // ↓ Pure onsonants of romanized CVs.
+ // ↓ consonants of romanized CVs.
static readonly string[] consonants = new string[] {
"ggy=ggya,ggyu,ggye,ggyo,ggyeo",
"ggw=ggwa,ggwi,ggwe,ggweo",
@@ -679,7 +679,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (checkOtoUntilHit(tests, note, out var oto)){
currentLyric = oto.Alias;
}
- } else if ("R".Contains(currentLyric)) {
+ } else if ("-".Contains(currentLyric)) {
var prevUnicode = ToUnicodeElements(prevNeighbour?.lyric);
// end breath note
if (vowelLookup.TryGetValue(prevUnicode.LastOrDefault() ?? string.Empty, out var vow)) {
From 8b888fa829ed7e200af8dcb4ef1813dccd6f9ee0 Mon Sep 17 00:00:00 2001
From: Jeroen Janssen <512+japj@users.noreply.github.com>
Date: Thu, 22 Dec 2022 17:23:21 +0100
Subject: [PATCH 09/18] Fix Resamplers wiki link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ca83d1991..024084980 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ OpenUtau is an open source editing environment for UTAU community with modern us
It is **strongly recommend** to go through a few Wiki pages before use:
- [Getting-Started](https://github.com/stakira/OpenUtau/wiki/Getting-Started)
-- [Resamplers](https://github.com/stakira/OpenUtau/wiki/Resamplers)
+- [Resamplers](https://github.com/stakira/OpenUtau/wiki/Resamplers-and-Wavtools)
- [Phonemizers](https://github.com/stakira/OpenUtau/wiki/Phonemizers)
- [FAQ](https://github.com/stakira/OpenUtau/wiki/FAQ)
From 3d681e9fadaeeb06fc8d7758d796b052a30dbf84 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Fri, 23 Dec 2022 02:31:39 +0900
Subject: [PATCH 10/18] Update KoreanCBNNPhonemizer.cs
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index c30707a41..ea593ea08 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -679,7 +679,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (checkOtoUntilHit(tests, note, out var oto)){
currentLyric = oto.Alias;
}
- } else if ("-".Contains(currentLyric)) {
+ } else if ("R".Contains(currentLyric)) {
var prevUnicode = ToUnicodeElements(prevNeighbour?.lyric);
// end breath note
if (vowelLookup.TryGetValue(prevUnicode.LastOrDefault() ?? string.Empty, out var vow)) {
From 8cd17c5f78e353ed45fe3b6f720fe19f57dab92a Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Fri, 23 Dec 2022 18:45:46 +0900
Subject: [PATCH 11/18] Fixed Endbreath & Semivowels issue
---
.../KoreanCBNNPhonemizer.cs | 27 ++++++++++++++-----
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index ea593ea08..490c2b866 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -248,7 +248,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
int PLconsonant = 0;
int PLvowel = 0;
int PLfinal = 0;
-
+
// ↓ Use for Temp
string[] TPLtemp;
@@ -257,6 +257,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
string TPLvowel = "";
string TPLfinal = "";
string TPLplainvowel = "";
+ string TPLplainfinal = "";
// ↓ use these for generating phonemes in phonemizers
//string TPLconsonantCBNN = "";
@@ -275,6 +276,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
char firstCL, firstPL, firstNL;
int uCL, uPL, uNL;
+ bool prevIsBreath = false;
// check first lyric
@@ -333,6 +335,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
TPLtemp = naFinals[PLfinal].Split(":");
TPLfinal = TPLtemp[1];
+ TPLplainfinal = TPLfinal;
}
}
@@ -550,7 +553,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
else if (TNLsemivowel == 2 && TCLplainvowel != "u" && TCLplainvowel != "o" && TCLplainvowel != "eu") {TNLconsonantCBNN = TNLconsonant + 'w';}
else {TNLconsonantCBNN = TNLconsonant;}
-
+
//To set suffix of CV, according to next-coming batchim.
if (TCLfinal == "") {TCLvowelCBNN = TCLvowel;}
else if (TCLfinal == "m" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '1';}
@@ -565,10 +568,16 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
string CV = (TCLconsonant + TCLvowelCBNN);
string VC = "";
+ bool comesSemivowelWithoutVC = false;
+
+
+ if (TCLsemivowel != 0 && TCLconsonant == ""){
+ comesSemivowelWithoutVC = true;
+ }
if (nextExist && (TCLfinal == "")) { VC = TCLplainvowel + " " + TNLconsonantCBNN; }
//for Vowel VCV
- if (prevExist && TPLfinal == "" && TCLconsonantCBNN == "") {CV = TPLplainvowel + " " + TCLvowel;}
+ if (prevExist && TPLfinal == "" && TCLconsonantCBNN == "" && !comesSemivowelWithoutVC) {CV = TPLplainvowel + " " + TCLvowel;}
string FC = "";
@@ -576,7 +585,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 만약 앞에 노트가 없다면
- if (!prevExist) { CV = $"- {CV}"; }
+ if (!prevExist && !prevIsBreath) { CV = $"- {CV}"; }
// 만약 받침이 있다면
if (FC != "") {
@@ -605,7 +614,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 만약 받침이 없다면
if (TCLfinal == "") {
// 뒤에 노트가 있다면
- if ((TNLconsonant != "")) {
+ if ((TNLconsonantCBNN != "")) {
int totalDuration = notes.Sum(n => n.duration);
int vcLength = 60;
if ((TNLconsonant == "r") || (TNLconsonant == "h")) { vcLength = 30; }
@@ -652,6 +661,11 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (prevExist && TPLfinal == "" && endBreath.Contains(currentLyric)) {
endBreath = $"{TPLplainvowel} -";
+ prevIsBreath = true; // to prevent this→→ case→→, for example... "[사, -, 사 (=notes)]" should be "[- sa, a -, - sa(=phonemes)]", but it becomes [sa, a -, 사(=phonemes)] in phonemizer, so '사' note becomes *no sound.
+ }
+ else if (prevExist && TPLfinal != "" && endBreath.Contains(currentLyric)) {
+ endBreath = $"{TPLplainfinal} -";
+ prevIsBreath = true; // to prevent this→→ case→→, for example... "[사, -, 사 (=notes)]" should be "[- sa, a -, - sa(=phonemes)]", but it becomes [sa, a -, 사(=phonemes)] in phonemizer, so '사' note becomes *no sound.
}
if (singer.TryGetMappedOto(endBreath, note.tone + attr0.toneShift, attr0.voiceColor, out var oto)){
@@ -679,8 +693,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (checkOtoUntilHit(tests, note, out var oto)){
currentLyric = oto.Alias;
}
- } else if ("R".Contains(currentLyric)) {
+ } else if ("-".Contains(currentLyric)) {
var prevUnicode = ToUnicodeElements(prevNeighbour?.lyric);
+ prevIsBreath = true;
// end breath note
if (vowelLookup.TryGetValue(prevUnicode.LastOrDefault() ?? string.Empty, out var vow)) {
var vowel = "";
From 974a07579900e896f5be2ecf5a02c5e1da1551a5 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Mon, 26 Dec 2022 15:41:33 +0900
Subject: [PATCH 12/18] Improve batchim pronunciation
- Changed length of VC & FC .
- Changed [- CV] phonemes to come after Final consonant. (instead of [CV] phonemes).
(but if Current consonant is "r" or "n" or "m", [CV] phonemes comes.)
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 490c2b866..14f1660b3 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -584,14 +584,17 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (TCLfinal != "") { FC = TCLplainvowel + TCLfinal; }
- // 만약 앞에 노트가 없다면
- if (!prevExist && !prevIsBreath) { CV = $"- {CV}"; }
+ // for [- XX] phonemes
+ if (!prevExist || prevIsBreath || prevExist && TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "m") { CV = $"- {CV}"; }
// 만약 받침이 있다면
if (FC != "") {
int totalDuration = notes.Sum(n => n.duration);
int fcLength = totalDuration / 3;
- if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) { fcLength = totalDuration / 2; }
+ if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
+ fcLength = totalDuration / 2;}
+ else if ((TCLfinal == "l")) {
+ fcLength = totalDuration / 4;}
if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(FC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
CV = oto1.Alias;
@@ -617,8 +620,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if ((TNLconsonantCBNN != "")) {
int totalDuration = notes.Sum(n => n.duration);
int vcLength = 60;
- if ((TNLconsonant == "r") || (TNLconsonant == "h")) { vcLength = 30; }
- else if (TNLconsonant == "s") { vcLength = totalDuration/3; }
+ if ((TNLconsonant == "r") || (TNLconsonant == "h") || (TNLconsonant == "g") || (TNLconsonant == "d")) { vcLength = 30; }
else if ((TNLconsonant == "k") || (TNLconsonant == "t") || (TNLconsonant == "p") || (TNLconsonant == "ch")) { vcLength = totalDuration / 2; }
else if ((TNLconsonant == "gg") || (TNLconsonant == "dd") || (TNLconsonant == "bb") || (TNLconsonant == "ss") || (TNLconsonant == "jj")) { vcLength = totalDuration / 2; }
vcLength = Math.Min(totalDuration / 2, vcLength);
From 4127f80a24baf828cda6eda5ade544144bfc9f22 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Mon, 26 Dec 2022 16:22:45 +0900
Subject: [PATCH 13/18] Improved some batchims & VCs' connection
- Changed FC's length (mainly batchim 'l' sound)
- Changed VC's length, 1/2(before) to 1/3(now).
(Excepted 'gg' and 'ch')
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 14f1660b3..416933613 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -585,13 +585,13 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// for [- XX] phonemes
- if (!prevExist || prevIsBreath || prevExist && TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "m") { CV = $"- {CV}"; }
+ if (!prevExist || prevIsBreath || prevExist && TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "m" && TCLconsonant != "") { CV = $"- {CV}"; }
// 만약 받침이 있다면
if (FC != "") {
int totalDuration = notes.Sum(n => n.duration);
int fcLength = totalDuration / 3;
- if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
+ if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t") || (TCLfinal == "m") || (TCLfinal == "n")) {
fcLength = totalDuration / 2;}
else if ((TCLfinal == "l")) {
fcLength = totalDuration / 4;}
@@ -620,9 +620,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if ((TNLconsonantCBNN != "")) {
int totalDuration = notes.Sum(n => n.duration);
int vcLength = 60;
- if ((TNLconsonant == "r") || (TNLconsonant == "h") || (TNLconsonant == "g") || (TNLconsonant == "d")) { vcLength = 30; }
- else if ((TNLconsonant == "k") || (TNLconsonant == "t") || (TNLconsonant == "p") || (TNLconsonant == "ch")) { vcLength = totalDuration / 2; }
- else if ((TNLconsonant == "gg") || (TNLconsonant == "dd") || (TNLconsonant == "bb") || (TNLconsonant == "ss") || (TNLconsonant == "jj")) { vcLength = totalDuration / 2; }
+ if ((TNLconsonant == "r") || (TNLconsonant == "h") || (TNLconsonant == "g") || (TNLconsonant == "d") || (TNLconsonant == "n")) { vcLength = 30; }
+ else if ((TNLconsonant == "ch") || (TNLconsonant == "gg")) { vcLength = totalDuration / 2; }
+ else if ((TNLconsonant == "k") || (TNLconsonant == "t") || (TNLconsonant == "p") || (TNLconsonant == "dd") || (TNLconsonant == "bb") || (TNLconsonant == "ss") || (TNLconsonant == "jj")) { vcLength = totalDuration / 3; }
vcLength = Math.Min(totalDuration / 2, vcLength);
if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(VC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
From cf734527869ba88b7d573ccbebfe8ab34452ba35 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Mon, 26 Dec 2022 18:52:33 +0900
Subject: [PATCH 14/18] Improved batchim length ("n", "l", "ng", "m")
Now [batchim-CV] connections sounds more smoother..
---
.../KoreanCBNNPhonemizer.cs | 42 ++++++++++++-------
1 file changed, 26 insertions(+), 16 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 416933613..0e49160bb 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -274,6 +274,8 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
bool prevExist = false;
bool nextExist = false;
+ bool nextComesSuffixedCV = true;
+
char firstCL, firstPL, firstNL;
int uCL, uPL, uNL;
bool prevIsBreath = false;
@@ -472,13 +474,12 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// ㄱㄷㅂ + ㄱㄷㅂㅅㅈ = ㄲㄸㅃㅆㅉ
if (((TPLfinal == "k") && (CLconsonant == 0)) || ((TPLfinal == "t") && (CLconsonant == 0)) || ((TPLfinal == "p") && (CLconsonant == 0))) { TCLconsonant = "gg"; } else if (((TPLfinal == "k") && (CLconsonant == 3)) || ((TPLfinal == "t") && (CLconsonant == 3)) || ((TPLfinal == "p") && (CLconsonant == 3))) { TCLconsonant = "dd"; } else if (((TPLfinal == "k") && (CLconsonant == 7)) || ((TPLfinal == "t") && (CLconsonant == 7)) || ((TPLfinal == "p") && (CLconsonant == 7))) { TCLconsonant = "bb"; } else if (((TPLfinal == "k") && (CLconsonant == 9)) || ((TPLfinal == "t") && (CLconsonant == 9)) || ((TPLfinal == "p") && (CLconsonant == 9))) { TCLconsonant = "ss"; } else if (((TPLfinal == "k") && (CLconsonant == 12)) || ((TPLfinal == "t") && (CLconsonant == 12)) || ((TPLfinal == "p") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
- /*
+
// 용언 어간 받침 ㄴㅁ + ㄱㄷㅅㅈ = ㄲㄸㅆㅉ
- if(((TPLfinal=="n")&&(CLconsonant==0))|| ((TPLfinal == "m") && (CLconsonant == 0))) { TCLconsonant = "gg"; }
- else if (((TPLfinal == "n") && (CLconsonant == 3)) || ((TPLfinal == "m") && (CLconsonant == 3))) { TCLconsonant = "dd"; }
+ //if(((TPLfinal=="n")&&(CLconsonant==0))|| ((TPLfinal == "m") && (CLconsonant == 0))) { TCLconsonant = "gg"; }
+ //else if (((TPLfinal == "n") && (CLconsonant == 3)) || ((TPLfinal == "m") && (CLconsonant == 3))) { TCLconsonant = "dd"; }
else if (((TPLfinal == "n") && (CLconsonant == 9)) || ((TPLfinal == "m") && (CLconsonant == 9))) { TCLconsonant = "ss"; }
- else if (((TPLfinal == "n") && (CLconsonant == 12)) || ((TPLfinal == "m") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
- */
+ //else if (((TPLfinal == "n") && (CLconsonant == 12)) || ((TPLfinal == "m") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
// 관형사형 어미ㄹ / 한자어 ㄹ + ㄷㅅㅈ = ㄸㅆㅉ
if ((PLfinal == 8) && (CLconsonant == 3)) { TCLconsonant = "dd"; } else if ((PLfinal == 8) && (CLconsonant == 9)) { TCLconsonant = "ss"; } else if ((PLfinal == 8) && (CLconsonant == 12)) { TCLconsonant = "jj"; }
@@ -554,15 +555,24 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
else {TNLconsonantCBNN = TNLconsonant;}
+
//To set suffix of CV, according to next-coming batchim.
- if (TCLfinal == "") {TCLvowelCBNN = TCLvowel;}
- else if (TCLfinal == "m" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '1';}
- else if (TCLfinal == "n" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '2';}
- else if (TCLfinal == "ng" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '3';}
- else if (TCLfinal == "l" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '4';}
- else if (TCLfinal == "k" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '3';}
- else if (TCLfinal == "t" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '2';}
- else if (TCLfinal == "p" && TCLconsonantCBNN != "") {TCLvowelCBNN = TCLvowel + '1';}
+ if (TCLfinal == "") {
+ TCLvowelCBNN = TCLvowel;}
+ else if (TCLfinal == "m" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '1';}
+ else if (TCLfinal == "n" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '2';}
+ else if (TCLfinal == "ng" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '3';}
+ else if (TCLfinal == "l" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '4';}
+ else if (TCLfinal == "k" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '3';}
+ else if (TCLfinal == "t" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '2';}
+ else if (TCLfinal == "p" && TCLconsonantCBNN != "") {
+ TCLvowelCBNN = TCLvowel + '1';}
else {TCLvowelCBNN = TCLvowel;}
@@ -585,15 +595,15 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// for [- XX] phonemes
- if (!prevExist || prevIsBreath || prevExist && TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "m" && TCLconsonant != "") { CV = $"- {CV}"; }
+ if (!prevExist || prevIsBreath || TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "" ) { CV = $"- {CV}"; }
// 만약 받침이 있다면
if (FC != "") {
int totalDuration = notes.Sum(n => n.duration);
int fcLength = totalDuration / 3;
- if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t") || (TCLfinal == "m") || (TCLfinal == "n")) {
+ if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
fcLength = totalDuration / 2;}
- else if ((TCLfinal == "l")) {
+ else if ((TCLfinal == "l") || (TCLfinal == "m") || (TCLfinal == "n") || (TCLfinal == "ng")) {
fcLength = totalDuration / 4;}
if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(FC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
From 4760b5c6e62d683b3c7674573ecc0cf65f2109b0 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Mon, 26 Dec 2022 23:34:01 +0900
Subject: [PATCH 15/18] Fixed final batchim sound dragging when low tempo
And [- (plain vowel) + FC] uses [- h(plain vowel)(suffix) + FC] instead.
ex) [- a + an] >> [- ha2 + an]
---
.../KoreanCBNNPhonemizer.cs | 22 ++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 0e49160bb..d4d924ce3 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -274,8 +274,6 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
bool prevExist = false;
bool nextExist = false;
- bool nextComesSuffixedCV = true;
-
char firstCL, firstPL, firstNL;
int uCL, uPL, uNL;
bool prevIsBreath = false;
@@ -545,6 +543,19 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}
+
+ bool isLastBatchim = false;
+
+ // vowels do not have suffixed phonemes in CBNN, so use suffixed '- h'~ phonemes instead.
+ if (!prevExist && TCLconsonant == "" && TCLfinal != "" && TCLvowel != "") {
+ TCLconsonant = "h";
+ }
+
+ // to make FC's length to 1 if FC comes final (=no next note)
+ if (!nextExist && TCLfinal != "" &&TCLvowel != "") {
+ isLastBatchim = true;
+ }
+
// To use semivowels in VC (ex: [- ga][a gy][gya], ** so not [- ga][a g][gya] **)
if (TCLsemivowel == 1 && TPLplainvowel != "i" && TPLplainvowel != "eu") {TCLconsonantCBNN = TCLconsonant + 'y';}
else if (TCLsemivowel == 2 && TPLplainvowel != "u" && TPLplainvowel != "o" && TPLplainvowel != "eu") {TCLconsonantCBNN = TCLconsonant + 'w';}
@@ -597,11 +608,16 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// for [- XX] phonemes
if (!prevExist || prevIsBreath || TPLfinal != "" && TCLconsonant != "r" && TCLconsonant != "n" && TCLconsonant != "" ) { CV = $"- {CV}"; }
+
// 만약 받침이 있다면
if (FC != "") {
int totalDuration = notes.Sum(n => n.duration);
int fcLength = totalDuration / 3;
- if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
+
+ if (isLastBatchim) {
+ fcLength = 1;
+ }
+ else if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
fcLength = totalDuration / 2;}
else if ((TCLfinal == "l") || (TCLfinal == "m") || (TCLfinal == "n") || (TCLfinal == "ng")) {
fcLength = totalDuration / 4;}
From 11f72923efcf68fe7bff90a21476249e25d80ca4 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 29 Dec 2022 14:02:17 +0900
Subject: [PATCH 16/18] Fixed endbreath & VC connection issues
---
.../KoreanCBNNPhonemizer.cs | 28 +++++++++++++------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index d4d924ce3..800765ad4 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -474,10 +474,10 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 용언 어간 받침 ㄴㅁ + ㄱㄷㅅㅈ = ㄲㄸㅆㅉ
- //if(((TPLfinal=="n")&&(CLconsonant==0))|| ((TPLfinal == "m") && (CLconsonant == 0))) { TCLconsonant = "gg"; }
- //else if (((TPLfinal == "n") && (CLconsonant == 3)) || ((TPLfinal == "m") && (CLconsonant == 3))) { TCLconsonant = "dd"; }
+ if(((TPLfinal=="n")&&(CLconsonant==0))|| ((TPLfinal == "m") && (CLconsonant == 0))) { TCLconsonant = "gg"; }
+ else if (((TPLfinal == "n") && (CLconsonant == 3)) || ((TPLfinal == "m") && (CLconsonant == 3))) { TCLconsonant = "dd"; }
else if (((TPLfinal == "n") && (CLconsonant == 9)) || ((TPLfinal == "m") && (CLconsonant == 9))) { TCLconsonant = "ss"; }
- //else if (((TPLfinal == "n") && (CLconsonant == 12)) || ((TPLfinal == "m") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
+ else if (((TPLfinal == "n") && (CLconsonant == 12)) || ((TPLfinal == "m") && (CLconsonant == 12))) { TCLconsonant = "jj"; }
// 관형사형 어미ㄹ / 한자어 ㄹ + ㄷㅅㅈ = ㄸㅆㅉ
if ((PLfinal == 8) && (CLconsonant == 3)) { TCLconsonant = "dd"; } else if ((PLfinal == 8) && (CLconsonant == 9)) { TCLconsonant = "ss"; } else if ((PLfinal == 8) && (CLconsonant == 12)) { TCLconsonant = "jj"; }
@@ -487,7 +487,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}
- // 5. 구개음화
+ // 5. 구개음화
if (prevExist && prevHangeul && (TPLfinal != "")) {
if ((PLfinal == 7) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "j"; } else if ((PLfinal == 25) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "ch"; } else if ((PLfinal == 13) && (CLconsonant == 11) && (CLvowel == 20)) { TCLconsonant = "ch"; } else if ((PLfinal == 7) && (CLconsonant == 18) && (CLvowel == 20)) { TCLconsonant = "ch"; }
}
@@ -498,6 +498,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 6. 비음화
+ /**
if (prevExist && prevHangeul && (TPLfinal != "")) {
// 한자어 받침 ㅁㅇ + ㄹ = ㄴ
if (((TPLfinal == "m") && (CLconsonant == 5)) || ((TPLfinal == "ng") && (CLconsonant == 5))) { TCLconsonant = "n"; }
@@ -505,6 +506,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 한자어 받침 ㄱㄷㅂ + ㄹ = ㅇㄴㅁ + ㄴ(1)
if (((TPLfinal == "k") && (CLconsonant == 5)) || ((TPLfinal == "t") && (CLconsonant == 5)) || ((TPLfinal == "p") && (CLconsonant == 5))) { TCLconsonant = "n"; }
}
+ **/
if (nextExist && nextHangeul && (TCLfinal != "")) {
//받침 ㄱㄷㅂ + ㄴㅁ = ㅇㄴㅁ
if (((TCLfinal == "k") && (TNLconsonant == "n")) || ((TCLfinal == "k") && (TNLconsonant == "m"))) { TCLfinal = "ng"; } else if (((TCLfinal == "t") && (TNLconsonant == "n")) || ((TCLfinal == "t") && (TNLconsonant == "m"))) { TCLfinal = "n"; } else if (((TCLfinal == "p") && (TNLconsonant == "n")) || ((TCLfinal == "p") && (TNLconsonant == "m"))) { TCLfinal = "m"; }
@@ -515,12 +517,14 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// 7. 유음화
+ /**
if (prevExist && prevHangeul && (TPLfinal != "")) {
if (((PLfinal == 8) && (TCLconsonant == "n")) || ((PLfinal == 13) && (TCLconsonant == "n")) || ((PLfinal == 15) && (TCLconsonant == "n"))) { TCLconsonant = "r"; }
}
if (nextExist && nextHangeul && (TCLfinal != "")) {
if ((TCLfinal == "n") && (TNLconsonant == "r")) { TCLfinal = "l"; }
}
+ **/
@@ -529,7 +533,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// consonant에 변경 사항이 있을 때
- if (prevExist && prevHangeul) {
+ //if (prevExist && prevHangeul) {
// 비음화
@@ -538,7 +542,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// ㅂ(ㅍ,ㄼ,ㄿ,ㅄ)
- }
+ //}
// final에 변경 사항이 있을 때
@@ -552,7 +556,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}
// to make FC's length to 1 if FC comes final (=no next note)
- if (!nextExist && TCLfinal != "" &&TCLvowel != "") {
+ if (!nextHangeul && TCLfinal != "" &&TCLvowel != "") {
isLastBatchim = true;
}
@@ -581,7 +585,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
else if (TCLfinal == "k" && TCLconsonantCBNN != "") {
TCLvowelCBNN = TCLvowel + '3';}
else if (TCLfinal == "t" && TCLconsonantCBNN != "") {
- TCLvowelCBNN = TCLvowel + '2';}
+ TCLvowelCBNN = TCLvowel + '3';}
else if (TCLfinal == "p" && TCLconsonantCBNN != "") {
TCLvowelCBNN = TCLvowel + '1';}
else {TCLvowelCBNN = TCLvowel;}
@@ -637,6 +641,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
},
};
}
+
+
+
}
@@ -646,7 +653,10 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if ((TNLconsonantCBNN != "")) {
int totalDuration = notes.Sum(n => n.duration);
int vcLength = 60;
- if ((TNLconsonant == "r") || (TNLconsonant == "h") || (TNLconsonant == "g") || (TNLconsonant == "d") || (TNLconsonant == "n")) { vcLength = 30; }
+ if ((TNLconsonant == "r") || (TNLconsonant == "g") || (TNLconsonant == "d") || (TNLconsonant == "n")) { vcLength = 33; }
+ else if (TNLconsonant == "h") {
+ vcLength = 15;
+ }
else if ((TNLconsonant == "ch") || (TNLconsonant == "gg")) { vcLength = totalDuration / 2; }
else if ((TNLconsonant == "k") || (TNLconsonant == "t") || (TNLconsonant == "p") || (TNLconsonant == "dd") || (TNLconsonant == "bb") || (TNLconsonant == "ss") || (TNLconsonant == "jj")) { vcLength = totalDuration / 3; }
vcLength = Math.Min(totalDuration / 2, vcLength);
From e9cbb4d272daa5fa15c0c99e42be89f5d6da3a92 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 29 Dec 2022 22:17:38 +0900
Subject: [PATCH 17/18] Fixed semivowel issue
---
.../KoreanCBNNPhonemizer.cs | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 800765ad4..29b897502 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -574,19 +574,19 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
//To set suffix of CV, according to next-coming batchim.
if (TCLfinal == "") {
TCLvowelCBNN = TCLvowel;}
- else if (TCLfinal == "m" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "m" && TCLconsonantCBNN != "" || TCLfinal == "m" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '1';}
- else if (TCLfinal == "n" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "n" && TCLconsonantCBNN != "" || TCLfinal == "n" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '2';}
- else if (TCLfinal == "ng" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "ng" && TCLconsonantCBNN != "" || TCLfinal == "ng" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '3';}
- else if (TCLfinal == "l" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "l" && TCLconsonantCBNN != "" || TCLfinal == "l" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '4';}
- else if (TCLfinal == "k" && TCLconsonantCBNN != "") {
- TCLvowelCBNN = TCLvowel + '3';}
- else if (TCLfinal == "t" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "k" && TCLconsonantCBNN != "" || TCLfinal == "k" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
+ TCLvowelCBNN = TCLvowel;}
+ else if (TCLfinal == "t" && TCLconsonantCBNN != "" || TCLfinal == "t" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '3';}
- else if (TCLfinal == "p" && TCLconsonantCBNN != "") {
+ else if (TCLfinal == "p" && TCLconsonantCBNN != "" || TCLfinal == "p" && TCLconsonantCBNN == "" && TCLsemivowel != 0) {
TCLvowelCBNN = TCLvowel + '1';}
else {TCLvowelCBNN = TCLvowel;}
@@ -623,8 +623,11 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}
else if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
fcLength = totalDuration / 2;}
- else if ((TCLfinal == "l") || (TCLfinal == "m") || (TCLfinal == "n") || (TCLfinal == "ng")) {
- fcLength = totalDuration / 4;}
+ else if ((TCLfinal == "l") || (TCLfinal == "ng")) {
+ fcLength = totalDuration / 5;}
+ else if ((TCLfinal == "n") || (TCLfinal == "m")) {
+ fcLength = totalDuration / 3;
+ }
if (singer.TryGetMappedOto(CV, note.tone + attr0.toneShift, attr0.voiceColor, out var oto1) && singer.TryGetMappedOto(FC, note.tone + attr0.toneShift, attr0.voiceColor, out var oto2)) {
CV = oto1.Alias;
From bf264441b557c8c97a210debae2ff002c97f3e48 Mon Sep 17 00:00:00 2001
From: EX3 <100339835+EX3exp@users.noreply.github.com>
Date: Thu, 29 Dec 2022 23:05:42 +0900
Subject: [PATCH 18/18] Improve batchim connection
---
OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
index 29b897502..270a0c6a4 100644
--- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs
@@ -623,9 +623,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}
else if ((TCLfinal == "k") || (TCLfinal == "p") || (TCLfinal == "t")) {
fcLength = totalDuration / 2;}
- else if ((TCLfinal == "l") || (TCLfinal == "ng")) {
+ else if ((TCLfinal == "l") || (TCLfinal == "ng") || (TCLfinal == "m")) {
fcLength = totalDuration / 5;}
- else if ((TCLfinal == "n") || (TCLfinal == "m")) {
+ else if ((TCLfinal == "n")) {
fcLength = totalDuration / 3;
}
@@ -725,7 +725,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// ======================================================================================
-
+/**
if (prevNeighbour == null) {
// Use "- V" or "- CV" if present in voicebank
var initial = $"- {currentLyric}";
@@ -762,7 +762,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
currentLyric = oto.Alias;
}
}
-
+**/
if (nextNeighbour != null) { // 다음에 노트가 있으면
var nextUnicode = ToUnicodeElements(nextNeighbour?.lyric);
var nextLyric = string.Join("", nextUnicode);