diff --git a/OpenUtau.Core/Enunu/EnunuKoreanPhonemizer.cs b/OpenUtau.Core/Enunu/EnunuKoreanPhonemizer.cs index 1064ba437..e7acd5481 100644 --- a/OpenUtau.Core/Enunu/EnunuKoreanPhonemizer.cs +++ b/OpenUtau.Core/Enunu/EnunuKoreanPhonemizer.cs @@ -393,7 +393,7 @@ public enum BatchimType{ } Dictionary partResult = new Dictionary(); - + public override void SetUp(Note[][] notes, UProject project, UTrack track) { partResult.Clear(); if (notes.Length == 0 || singer == null || !singer.Found) { @@ -484,7 +484,7 @@ static Phoneme[] ParseLabel(string path) { } protected override EnunuNote[] NoteGroupsToEnunu(Note[][] notes) { - KoreanPhonemizerUtil.RomanizeNotes(notes, FirstConsonants, MiddleVowels, LastConsonants, semivowelSep); + KoreanPhonemizerUtil.RomanizeNotes(notes, true, FirstConsonants, MiddleVowels, LastConsonants, semivowelSep); var result = new List(); int position = 0; int index = 0; @@ -513,69 +513,6 @@ protected override EnunuNote[] NoteGroupsToEnunu(Note[][] notes) { return result.ToArray(); } - // public void AdjustPos(Phoneme[] phonemes, Note[] prevNote){ - // //TODO - // Phoneme? prevPhone = null; - // Phoneme? nextPhone = null; - // Phoneme currPhone; - - // int length = phonemes.Last().position; - // int prevLength; - // if (prevNote == null){ - // prevLength = length; - // } - // else{ - // prevLength = MsToTick(prevNote.Sum(n => n.duration)); - // } - - // for (int i=0; i < phonemes.Length; i++) { - // currPhone = phonemes[i]; - // if (i < phonemes.Length - 1){ - // nextPhone = phonemes[i+1]; - // } - // else{ - // nextPhone = null; - // } - - // if (i == 0){ - // // TODO 받침 + 자음 오면 받침길이 + 자음길이 / 2의 위치에 자음이 오도록 하기 - // if (isPlainVowel(phonemes[i].phoneme)) { - // phonemes[i].position = 0; - // } - // else if (nextPhone != null && ! isPlainVowel(((Phoneme)nextPhone).phoneme) && ! isSemivowel(((Phoneme)nextPhone).phoneme) && isPlainVowel(((Phoneme)nextPhone).phoneme) && isSemivowel(currPhone.phoneme)) { - // phonemes[i + 1].position = length / 10; - // } - // else if (nextPhone != null && isSemivowel(((Phoneme)nextPhone).phoneme)){ - // if (i + 2 < phonemes.Length){ - // phonemes[i + 2].position = length / 10; - // } - - // } - // } - // prevPhone = currPhone; - // } - // } - - // private bool isPlainVowel(string phoneme){ - // if (phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅏ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅣ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅜ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅔ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅗ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅡ") || phoneme == koreanENUNUSetting.GetPlainVowelPhoneme("ㅓ")){ - // return true; - // } - // return false; - // } - - // private bool isBatchim(string phoneme){ - // if (phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㄱ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㄴ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㄷ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㄹ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㅁ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㅂ") || phoneme == koreanENUNUSetting.GetFinalConsonantPhoneme("ㅇ")){ - // return true; - // } - // return false; - // } - - // private bool isSemivowel(string phoneme) { - // if (phoneme == koreanENUNUSetting.GetSemiVowelPhoneme("w") || phoneme == koreanENUNUSetting.GetSemiVowelPhoneme("y")){ - // return true; - // } - // return false; - // } public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevs) { if (partResult.TryGetValue(notes, out var phonemes)) { var phonemes_ = phonemes.Select(p => { @@ -584,7 +521,6 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN return p; }).ToArray(); - //AdjustPos(phonemes_, prevs); return new Result { phonemes = phonemes_, }; diff --git a/OpenUtau.Core/KoreanPhonemizerUtil.cs b/OpenUtau.Core/KoreanPhonemizerUtil.cs index 632d195cd..a08236b83 100644 --- a/OpenUtau.Core/KoreanPhonemizerUtil.cs +++ b/OpenUtau.Core/KoreanPhonemizerUtil.cs @@ -177,20 +177,20 @@ public static Hashtable Separate(string character) { } /// - /// merges separated hangeul into complete hangeul. (Example: {[0]: "ㄱ", [1]: "ㅏ", [2]: " "} => "가"}) + /// merges separated hangeul into complete hangeul. (Example: {[offset + 0]: "ㄱ", [offset + 1]: "ㅏ", [offset + 2]: " "} => "가"}) /// 자모로 쪼개진 한글을 합쳐진 한글로 반환합니다. /// /// separated Hangeul. /// Returns complete Hangeul Character. - public static string Merge(Hashtable separatedHangeul){ + public static string Merge(Hashtable separatedHangeul, int offset = 0){ int firstConsonantIndex; // (ex) 2 int middleVowelIndex; // (ex) 2 int lastConsonantIndex; // (ex) 21 - char firstConsonant = ((string)separatedHangeul[0])[0]; // (ex) "ㄴ" - char middleVowel = ((string)separatedHangeul[1])[0]; // (ex) "ㅑ" - char lastConsonant = ((string)separatedHangeul[2])[0]; // (ex) "ㅇ" + char firstConsonant = ((string)separatedHangeul[offset + 0])[0]; // (ex) "ㄴ" + char middleVowel = ((string)separatedHangeul[offset + 1])[0]; // (ex) "ㅑ" + char lastConsonant = ((string)separatedHangeul[offset + 2])[0]; // (ex) "ㅇ" if (firstConsonant == ' ') {firstConsonant = 'ㅇ';} @@ -371,7 +371,7 @@ private static Hashtable Variate(Hashtable firstCharSeparated, Hashtable nextCha nextFirstConsonant = "ㅇ"; } - if ((!firstLastConsonant.Equals("")) && nextFirstConsonant.Equals("ㅇ") && (!firstLastConsonant.Equals("ㅇ"))) { + if ((!firstLastConsonant.Equals(" ")) && nextFirstConsonant.Equals("ㅇ") && (!firstLastConsonant.Equals("ㅇ"))) { // 연음 2 nextFirstConsonant = firstLastConsonant; firstLastConsonant = " "; @@ -919,10 +919,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, thisNoteSeparated[4]); result.Add(8, thisNoteSeparated[5]); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } } else if ((lyrics[0] != null) && (lyrics[2] == null)) { @@ -941,10 +938,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, "null"); result.Add(8, "null"); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } else if (whereYeonEum == 0) { // 앞 노트에서 단어가 끝났다고 가정 @@ -959,10 +953,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, "null"); result.Add(8, "null"); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } else { Hashtable result = Variate(lyrics[0], lyrics[1], 0); // 첫 글자 @@ -976,10 +967,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, "null"); result.Add(8, "null"); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } } else if ((lyrics[0] != null) && (lyrics[2] != null)) { @@ -998,10 +986,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, thisNoteSeparated[4]); result.Add(8, thisNoteSeparated[5]); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } else if (whereYeonEum == 0) { // 앞 노트에서 단어가 끝났다고 가정 / 릎. [위] 놓 @@ -1016,10 +1001,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, thisNoteSeparated[4]); result.Add(8, thisNoteSeparated[5]); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } else { Hashtable result = Variate(lyrics[0], lyrics[1], 0); @@ -1033,10 +1015,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, thisNoteSeparated[4]); result.Add(8, thisNoteSeparated[5]); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5]}); + return Merge(result, 3); } } else { @@ -1060,11 +1039,7 @@ public static String Variate(String? prevNeighbour, String note, String? nextNei result.Add(7, "null"); result.Add(8, "null"); - return Merge(new Hashtable{ - [0] = (string)result[3], - [1] = (string)result[4], - [2] = (string)result[5] - }); + return Merge(result, 3); } } @@ -1081,10 +1056,27 @@ public static Note[] ChangeLyric(Note[] group, string lyric) { }; return group; } - public static void RomanizeNotes(Note[][] groups, Dictionary firstConsonants, Dictionary vowels, Dictionary lastConsonants, string semivowelSeparator=" ") { - // for ENUNU Phonemizer - + + public static void ModifyLyrics(Hashtable lyricSeparated,string lyric, Dictionary firstConsonants, Dictionary vowels, Dictionary lastConsonants, string semivowelSeparator){ + lyric += firstConsonants[(string)lyricSeparated[3]][0]; + if (vowels[(string)lyricSeparated[4]][1] != "") { + // this vowel contains semivowel + lyric += semivowelSeparator + vowels[(string)lyricSeparated[4]][1] + vowels[(string)lyricSeparated[4]][2]; + } + else{ + lyric += " " + vowels[(string)lyricSeparated[4]][2]; + } + + lyric += lastConsonants[(string)lyricSeparated[5]][0]; + } + + public static void RomanizeNotes(Note[][] groups, bool _modifyLyrics = false, Dictionary firstConsonants = null, Dictionary vowels = null, Dictionary lastConsonants = null, string semivowelSeparator = " ") { + // for ENUNU & DIFFS Phonemizer + int noteIdx = 0; + string lyric; + bool modifyLyrics = (!_modifyLyrics || firstConsonants == null || vowels == null || lastConsonants == null) ? false : true; + Note[] currentNote; Note[]? prevNote = null; Note[]? nextNote; @@ -1092,10 +1084,13 @@ public static void RomanizeNotes(Note[][] groups, Dictionary f Note? prevNote_; Note? nextNote_; - List ResultLyrics = new List(); + foreach (Note[] group in groups){ currentNote = groups[noteIdx]; + string originalLyric; // uses this when no variation needed + originalLyric = currentNote[0].lyric; + if (groups.Length > noteIdx + 1 && IsHangeul(groups[noteIdx + 1][0].lyric)) { nextNote = groups[noteIdx + 1]; } @@ -1120,7 +1115,7 @@ public static void RomanizeNotes(Note[][] groups, Dictionary f } else{nextNote_ = null;} - string lyric = ""; + lyric = originalLyric; if (! IsHangeul(currentNote[0].lyric)){ ResultLyrics.Add(currentNote[0].lyric); @@ -1129,27 +1124,27 @@ public static void RomanizeNotes(Note[][] groups, Dictionary f continue; } - Hashtable lyricSeparated = Variate(prevNote_, currentNote[0], nextNote_); - lyric += firstConsonants[(string)lyricSeparated[3]][0]; - if (vowels[(string)lyricSeparated[4]][1] != "") { - // this vowel contains semivowel - lyric += semivowelSeparator + vowels[(string)lyricSeparated[4]][1] + vowels[(string)lyricSeparated[4]][2]; - } - else{ - lyric += " " + vowels[(string)lyricSeparated[4]][2]; - } - - lyric += lastConsonants[(string)lyricSeparated[5]][0]; + + Hashtable lyricSeparated = Variate(prevNote_, currentNote[0], nextNote_); - ResultLyrics.Add(lyric.Trim()); + if (modifyLyrics) { + ModifyLyrics(lyricSeparated, lyric, firstConsonants, vowels, lastConsonants, semivowelSeparator); + } + else { + lyric = Merge(lyricSeparated, 3); + } + + ResultLyrics.Add(lyric.Trim()); - prevNote = currentNote; + prevNote = currentNote; - noteIdx++; + noteIdx++; + } Enumerable.Zip(groups, ResultLyrics.ToArray(), ChangeLyric).Last(); } + /// /// abstract class for Ini Management /// To use, child phonemizer should implement this class(BaseIniManager) with its own setting values! diff --git a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs index 0ee400ff3..008f86847 100644 --- a/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/KoreanCBNNPhonemizer.cs @@ -141,11 +141,11 @@ private Result ConvertForCBNN(Note[] notes, string[] prevLyric, string[] thisLyr } string frontCV; string batchim; - string VC = $"{thisMidVowelTail} {FIRST_CONSONANTS[nextLyric[0]]}"; + string VC = $"{thisMidVowelTail} {FIRST_CONSONANTS[nextLyric[0]]}{MIDDLE_VOWELS[nextLyric[1]][1]}"; string VV = $"{MIDDLE_VOWELS[prevLyric[1]][2]} {thisMidVowelTail}"; string VSv = $"{thisMidVowelTail} {MIDDLE_VOWELS[nextLyric[1]][1]}"; isItNeedsVSv = thisLyric[2] == " " && nextLyric[0] == "ㅇ" && !PLAIN_VOWELS.Contains(nextLyric[1]) && FindInOto(VSv, note, true) != null; - isItNeedsVC = thisLyric[2] == " " && nextLyric[0] != "ㅇ" && nextLyric[0] != "null" && FindInOto(VC, note, true) != null; + isItNeedsVC = thisLyric[2] == " " && nextLyric[0] != "ㅇ" && nextLyric[0] != "null"; frontCV = $"- {CV}"; if (FindInOto(frontCV, note, true) == null) { @@ -155,6 +155,15 @@ private Result ConvertForCBNN(Note[] notes, string[] prevLyric, string[] thisLyr } } + if (FindInOto(VC, note, true) == null) { + if (VC.EndsWith("w") || VC.EndsWith("y")) { + VC = VC.Substring(0, VC.Length - 1); + } + if (FindInOto(VC, note, true) == null) { + isItNeedsVC = false; + } + } + if (isItNeedsVV) {CV = VV;} @@ -214,26 +223,22 @@ private Result ConvertForCBNN(Note[] notes, string[] prevLyric, string[] thisLyr } - private string HandleEmptyFirstConsonant(string lyric) { - return lyric == " " ? "ㅇ" : lyric; - } - public override Result ConvertPhonemes(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevNeighbours) { Note note = notes[0]; Hashtable lyrics = KoreanPhonemizerUtil.Variate(prevNeighbour, note, nextNeighbour); string[] prevLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[0]), + (string)lyrics[0], (string)lyrics[1], (string)lyrics[2] }; string[] thisLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[3]), + (string)lyrics[3], (string)lyrics[4], (string)lyrics[5] }; string[] nextLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[6]), + (string)lyrics[6], (string)lyrics[7], (string)lyrics[8] }; @@ -257,7 +262,7 @@ public override Result GenerateEndSound(Note[] notes, Note? prev, Note? next, No Hashtable lyrics = KoreanPhonemizerUtil.Separate(prevNeighbour_.lyric); string[] prevLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[0]), + (string)lyrics[0], (string)lyrics[1], (string)lyrics[2] }; diff --git a/OpenUtau.Plugin.Builtin/KoreanCVPhonemizer.cs b/OpenUtau.Plugin.Builtin/KoreanCVPhonemizer.cs index 2e77ff344..9713aed06 100644 --- a/OpenUtau.Plugin.Builtin/KoreanCVPhonemizer.cs +++ b/OpenUtau.Plugin.Builtin/KoreanCVPhonemizer.cs @@ -232,26 +232,22 @@ private Result ConvertForCV(Note[] notes, string[] prevLyric, string[] thisLyric } - private string HandleEmptyFirstConsonant(string lyric) { - return lyric == " " ? "ㅇ" : lyric; - } - public override Result ConvertPhonemes(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevNeighbours) { Note note = notes[0]; Hashtable lyrics = KoreanPhonemizerUtil.Variate(prevNeighbour, note, nextNeighbour); string[] prevLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[0]), + (string)lyrics[0], (string)lyrics[1], (string)lyrics[2] }; string[] thisLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[3]), + (string)lyrics[3], (string)lyrics[4], (string)lyrics[5] }; string[] nextLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[6]), + (string)lyrics[6], (string)lyrics[7], (string)lyrics[8] }; @@ -275,7 +271,7 @@ public override Result GenerateEndSound(Note[] notes, Note? prev, Note? next, No Hashtable lyrics = KoreanPhonemizerUtil.Separate(prevNeighbour_.lyric); string[] prevLyric = new string[]{ // "ㄴ", "ㅑ", "ㅇ" - HandleEmptyFirstConsonant((string)lyrics[0]), + (string)lyrics[0], (string)lyrics[1], (string)lyrics[2] };