Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KO Classic Phonemizers + BaseKoreanPhonemizer] Improve KO romanization + phonetic hint fix + typo fix #1399

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions OpenUtau.Plugin.Builtin/BaseKoreanPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ public Result GenerateResult(String firstPhoneme, String secondPhoneme, String t
}

public class ProcessResult {
public Note[] KoreanLryicNotes { get; set; }
public Note? KoreanLryicPrevNote { get; set; }
public Note? KoreanLryicNextNote { get; set; }
public Note[] KoreanLyricNotes { get; set; }
public Note? KoreanLyricPrevNote { get; set; }
public Note? KoreanLyricNextNote { get; set; }
}

// <summary>
Expand Down Expand Up @@ -225,9 +225,9 @@ public class ProcessResult {
}
}
return new ProcessResult {
KoreanLryicNotes = notes,
KoreanLryicPrevNote = prevNoteNew,
KoreanLryicNextNote = nextNoteNew,
KoreanLyricNotes = notes,
KoreanLyricPrevNote = prevNoteNew,
KoreanLyricNextNote = nextNoteNew,
};
}
return null;
Expand Down Expand Up @@ -383,7 +383,7 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN

var romaji2Korean = ConvertRomajiNoteToHangeul(notes, prevNeighbour, nextNeighbour);
if (romaji2Korean != null) {
return ConvertPhonemes(romaji2Korean.KoreanLryicNotes, prev, next, romaji2Korean.KoreanLryicPrevNote, romaji2Korean.KoreanLryicNextNote, prevNeighbours);
return ConvertPhonemes(romaji2Korean.KoreanLyricNotes, prev, next, romaji2Korean.KoreanLyricPrevNote, romaji2Korean.KoreanLyricNextNote, prevNeighbours);
}

if (KoreanPhonemizerUtil.IsHangeul(lyric) || !KoreanPhonemizerUtil.IsHangeul(lyric) && additionalTest(lyric)) {
Expand Down
54 changes: 39 additions & 15 deletions OpenUtau.Plugin.Builtin/KOtoJAPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
using System.Text;
using System.Text.RegularExpressions;
using OpenUtau.Api;
using OpenUtau.Core;
using OpenUtau.Core.Ustx;
using WanaKanaNet;
using static OpenUtau.Core.KoreanPhonemizerUtil;

namespace OpenUtau.Plugin.Builtin {
[Phonemizer("KO to JA Phonemizer", "KO to JA", "Lotte V", language: "KO")]
Expand Down Expand Up @@ -314,7 +314,7 @@ static KOtoJAPhonemizer() {
/// </summary>
public override void SetUp(Note[][] groups, UProject project, UTrack track) {
// variate lyrics
KoreanPhonemizerUtil.RomanizeNotes(groups, false);
RomanizeNotes(groups, false);
}

/// <summary>
Expand Down Expand Up @@ -411,6 +411,21 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
string currPhoneme;
string[] prevIMF;

var romaji2Korean = ConvertRomajiNoteToHangeul(notes, prevNeighbour, nextNeighbour);
if (romaji2Korean != null) {
notes = romaji2Korean.KoreanLyricNotes;

note = notes[0];
}

// Check if notes are romaji
var currRomaji = IsKoreanRomaji(note.lyric);
var prevRomaji = IsKoreanRomaji(prevNeighbour?.lyric);

// Improve romaji lyric parsing, to mix with hangeul, vowel endings, and phonetic hint
var currRomLyric = TryParseKoreanRomaji(note.lyric);
var prevRomLyric = TryParseKoreanRomaji(prevNeighbour?.lyric);

// Check if lyric is R, - or an end breath and return appropriate Result; otherwise, move to next steps
if (note.lyric == "R" || note.lyric == "-" || note.lyric == "H" || note.lyric == "B" || note.lyric == "bre" || note.lyric == "息" || note.lyric == "吸") {
currPhoneme = note.lyric;
Expand All @@ -432,13 +447,17 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
if (string.IsNullOrEmpty(prevNeighbour?.phoneticHint)) {
byte[] bytes = Encoding.Unicode.GetBytes($"{prevNeighbour?.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (prevNeighbour?.lyric.Length == 1 && numval >= 44032 && numval <= 55215) prevIMF = GetIMF(prevNeighbour.Value.lyric);
else return new Result {
phonemes = new Phoneme[] {
new Phoneme() {
phoneme = currPhoneme
}
}
if (prevNeighbour?.lyric.Length == 1 && numval >= 44032 && numval <= 55215)
prevIMF = GetIMF(prevNeighbour.Value.lyric);
else if (prevRomaji && prevRomLyric != null) {
prevIMF = GetIMF(prevRomLyric);
} else
return new Result {
phonemes = new Phoneme[] {
new Phoneme() {
phoneme = currPhoneme
}
}
};
} else prevIMF = GetIMFFromHint(prevNeighbour.Value.phoneticHint);

Expand Down Expand Up @@ -475,7 +494,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
byte[] bytes = Encoding.Unicode.GetBytes($"{note.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (note.lyric.Length == 1 && numval >= 44032 && numval <= 55215) currIMF = GetIMF(note.lyric);
else return new Result {
else if (currRomaji && currRomLyric != null) {
currIMF = GetIMF(currRomLyric);
} else return new Result {
phonemes = new Phoneme[] {
new Phoneme() { phoneme = note.lyric }
}
Expand Down Expand Up @@ -522,7 +543,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
byte[] bytes = Encoding.Unicode.GetBytes($"{prevNeighbour?.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (prevNeighbour?.lyric.Length == 1 && numval >= 44032 && numval <= 55215) prevIMF = GetIMF(prevNeighbour.Value.lyric);
else return new Result {
else if (prevRomaji && prevRomLyric != null) {
prevIMF = GetIMF(prevRomLyric);
} else return new Result {
phonemes = new Phoneme[] {
new Phoneme() { phoneme = note.lyric }
}
Expand Down Expand Up @@ -608,19 +631,20 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
&& (singer.TryGetMappedOto($"{ToHiragana(AltCv.ContainsKey(currPhoneme) ? AltCv[currPhoneme] : currPhoneme)}", note.tone + shift1, color1, out _)
|| singer.TryGetMappedOto($"{ToHiragana(ConditionalAlt.ContainsKey(currPhoneme) ? ConditionalAlt[currPhoneme] : currPhoneme)}", note.tone + shift1, color1, out _))) {
int vcLength = 60;
int endTick = notes[^1].position + notes[^1].duration;
// totalDuration calculated on basis of previous note length
int totalDuration = prevNeighbour.Value.duration;
if (singer.TryGetMappedOto($"{ToHiragana(AltCv.ContainsKey(currPhoneme) ? AltCv[currPhoneme] : currPhoneme)}", notes[0].tone + shift1, color1, out var oto)) {
if (oto.Overlap < 0) {
vcLength = MsToTick(oto.Preutter - oto.Overlap);
vcLength = -timeAxis.MsToTickAt(-(oto.Preutter - oto.Overlap), endTick);
} else {
vcLength = MsToTick(oto.Preutter);
vcLength = -timeAxis.MsToTickAt(-oto.Preutter, endTick);
}
} else if (singer.TryGetMappedOto($"{ToHiragana(ConditionalAlt.ContainsKey(currPhoneme) ? ConditionalAlt[currPhoneme] : currPhoneme)}", notes[0].tone + shift1, color1, out var otoCon)) {
if (otoCon.Overlap < 0) {
vcLength = MsToTick(otoCon.Preutter - otoCon.Overlap);
vcLength = -timeAxis.MsToTickAt(-(otoCon.Preutter - otoCon.Overlap), endTick);
} else {
vcLength = MsToTick(otoCon.Preutter);
vcLength = -timeAxis.MsToTickAt(-otoCon.Preutter, endTick);
}
}
// vcLength depends on the Vel of the current base note
Expand Down
6 changes: 3 additions & 3 deletions OpenUtau.Plugin.Builtin/KoreanCVCCVPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN

var romaji2Korean = ConvertRomajiNoteToHangeul(notes, prevNeighbour, nextNeighbour);
if (romaji2Korean != null) {
notes = romaji2Korean.KoreanLryicNotes;
prevNeighbour = romaji2Korean.KoreanLryicPrevNote;
nextNeighbour = romaji2Korean.KoreanLryicNextNote;
notes = romaji2Korean.KoreanLyricNotes;
prevNeighbour = romaji2Korean.KoreanLyricPrevNote;
nextNeighbour = romaji2Korean.KoreanLyricNextNote;

prevLyric = prevNeighbour?.lyric;
nextLyric = nextNeighbour?.lyric;
Expand Down
133 changes: 71 additions & 62 deletions OpenUtau.Plugin.Builtin/KoreanVCVPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using System.Text;
using System.Text.RegularExpressions;
using OpenUtau.Api;
using OpenUtau.Core;
using OpenUtau.Core.Ustx;
using static OpenUtau.Core.KoreanPhonemizerUtil;

namespace OpenUtau.Plugin.Builtin
{
Expand Down Expand Up @@ -45,7 +45,7 @@ public class KoreanVCVPhonemizer : BaseKoreanPhonemizer
/// </summary>
public override void SetUp(Note[][] groups, UProject project, UTrack track) {
// variate lyrics
KoreanPhonemizerUtil.RomanizeNotes(groups, false);
RomanizeNotes(groups, false);
}

/// <summary>
Expand Down Expand Up @@ -133,37 +133,38 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
int? alt;
int totalDuration = notes.Sum(n => n.duration);

string color1 = string.Empty;
int shift1 = 0;
int? alt1;
string color1 = string.Empty;
int shift1 = 0;
int? alt1;

PhonemeAttributes attr = note.phonemeAttributes.FirstOrDefault(a => a.index == 0);
PhonemeAttributes attr = note.phonemeAttributes.FirstOrDefault(a => a.index == 0);
color = attr.voiceColor;
shift = attr.toneShift;
alt = attr.alternate;

PhonemeAttributes attr1 = note.phonemeAttributes.FirstOrDefault(a => a.index == 1);
color1 = attr1.voiceColor;
shift1 = attr1.toneShift;
alt1 = attr1.alternate;
PhonemeAttributes attr1 = note.phonemeAttributes.FirstOrDefault(a => a.index == 1);
color1 = attr1.voiceColor;
shift1 = attr1.toneShift;
alt1 = attr1.alternate;

string[] currIMF;
string currPhoneme;
string[] prevIMF;

var phoneticHint = RenderPhoneticHint(singer, notes[0], totalDuration);
if (phoneticHint != null) {
return (Result) phoneticHint;
}

var romaji2Korean = ConvertRomajiNoteToHangeul(notes, prevNeighbour, nextNeighbour);
if (romaji2Korean != null) {
notes = romaji2Korean.KoreanLryicNotes;
prevNeighbour = romaji2Korean.KoreanLryicPrevNote;
nextNeighbour = romaji2Korean.KoreanLryicNextNote;
var romaji2Korean = ConvertRomajiNoteToHangeul(notes, prevNeighbour, nextNeighbour);
if (romaji2Korean != null) {
notes = romaji2Korean.KoreanLyricNotes;

note = notes[0];
}
}

// Check if notes are romaji
var currRomaji = IsKoreanRomaji(note.lyric);
var prevRomaji = IsKoreanRomaji(prevNeighbour?.lyric);

// Improve romaji lyric parsing, to mix with hangeul, vowel endings, and phonetic hint
var currRomLyric = TryParseKoreanRomaji(note.lyric);
var prevRomLyric = TryParseKoreanRomaji(prevNeighbour?.lyric);

// Check if lyric is R, - or an end breath and return appropriate Result; otherwise, move to next steps
if (note.lyric == "R" || note.lyric == "R2" || note.lyric == "-" || note.lyric == "H" || note.lyric == "B" || note.lyric == "bre")
Expand Down Expand Up @@ -191,25 +192,28 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
byte[] bytes = Encoding.Unicode.GetBytes($"{prevNeighbour?.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (prevNeighbour?.lyric.Length == 1 && numval >= 44032 && numval <= 55215) prevIMF = GetIMF(prevNeighbour?.lyric);
else return new Result {
phonemes = new Phoneme[] {
else if (prevRomaji && prevRomLyric != null) {
prevIMF = GetIMF(prevRomLyric);
} else
return new Result {
phonemes = new Phoneme[] {
new Phoneme { phoneme = currPhoneme }
}
};
};
} else prevIMF = GetIMFFromHint(prevNeighbour?.phoneticHint);

if (string.IsNullOrEmpty(prevIMF[2])) currPhoneme = $"{((prevIMF[1][0] == 'w' || prevIMF[1][0] == 'y' || prevIMF[1] == "oi") ? prevIMF[1].Remove(0, 1) : ((prevIMF[1] == "eui" || prevIMF[1] == "ui") ? "i" : prevIMF[1]))} {currPhoneme}";
else currPhoneme = $"{(!singer.TryGetMappedOto($"{prevIMF[2]} {currPhoneme}", note.tone, color, out _) ? prevIMF[2].ToUpper() : prevIMF[2])} {currPhoneme}";
}

// Map alias (apply shift + color)
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}
// Map alias (apply shift + color)
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}

return new Result {
return new Result {
phonemes = new Phoneme[] {
new Phoneme { phoneme = currPhoneme }
}
Expand All @@ -218,19 +222,19 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
}

// Get IMF of current note if valid, otherwise return the lyric as is
if (string.IsNullOrEmpty(note.phoneticHint))
{
if (string.IsNullOrEmpty(note.phoneticHint)) {
byte[] bytes = Encoding.Unicode.GetBytes($"{note.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (note.lyric.Length == 1 && numval >= 44032 && numval <= 55215) currIMF = GetIMF(note.lyric);
else return new Result
{
phonemes = new Phoneme[] {
else if (currRomaji && currRomLyric != null) {
currIMF = GetIMF(currRomLyric);
} else
return new Result {
phonemes = new Phoneme[] {
new Phoneme { phoneme = note.lyric }
}
};
}
else currIMF = GetIMFFromHint(note.phoneticHint);
};
} else currIMF = GetIMFFromHint(note.phoneticHint);

// Convert current note to phoneme
currPhoneme = $"{currIMF[0]}{currIMF[1]}";
Expand All @@ -256,8 +260,9 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
byte[] bytes = Encoding.Unicode.GetBytes($"{prevNeighbour?.lyric[0]}");
int numval = Convert.ToInt32(bytes[0]) + Convert.ToInt32(bytes[1]) * (16 * 16);
if (prevNeighbour?.lyric.Length == 1 && numval >= 44032 && numval <= 55215) prevIMF = GetIMF(prevNeighbour?.lyric);
else return new Result
{
else if (prevRomaji && prevRomLyric != null) {
prevIMF = GetIMF(prevRomLyric);
} else return new Result {
phonemes = new Phoneme[] {
new Phoneme { phoneme = note.lyric }
}
Expand Down Expand Up @@ -290,14 +295,14 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// Return Result now if note has no batchim
if (string.IsNullOrEmpty(currIMF[2]))
{
// Map alias (apply shift + color)
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}
// Map alias (apply shift + color)
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}

return new Result
return new Result
{
phonemes = new Phoneme[] {
new Phoneme { phoneme = currPhoneme }
Expand All @@ -308,9 +313,13 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// Adjust Result if note has batchim
else
{
string secondPhoneme = (currIMF[1][0] == 'w' || currIMF[1][0] == 'y' || currIMF[1] == "oe" || currIMF[1] == "ui") ? currIMF[1].Remove(0, 1) : currIMF[1];
if (currIMF[1] == "eui") {
currIMF[1] = "ui";
}

string secondPhoneme = (currIMF[1][0] == 'w' || currIMF[1][0] == 'y' || currIMF[1] == "oe" || (currIMF[1] == "ui")) ? currIMF[1].Remove(0, 1) : currIMF[1];

if (nextNeighbour == null)
if (nextNeighbour == null)
{
if (string.IsNullOrEmpty(currIMF[2])) secondPhoneme += " R";
else
Expand All @@ -331,20 +340,20 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
int secondPosition = Math.Max(noteLength - (nextNeighbour == null ? 120 : 180), noteLength / 2);

// Map alias (apply shift + color)
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}
if (singer.TryGetMappedOto(currPhoneme + alt, note.tone + shift, color, out var otoAlt)) {
currPhoneme = otoAlt.Alias;
} else if (singer.TryGetMappedOto(currPhoneme, note.tone + shift, color, out var oto)) {
currPhoneme = oto.Alias;
}

if (singer.TryGetMappedOto(secondPhoneme + alt1, note.tone + shift1, color1, out var otoAlt1)) {
secondPhoneme = otoAlt1.Alias;
} else if (singer.TryGetMappedOto(secondPhoneme, note.tone + shift1, color1, out var oto)) {
secondPhoneme = oto.Alias;
}
if (singer.TryGetMappedOto(secondPhoneme + alt1, note.tone + shift1, color1, out var otoAlt1)) {
secondPhoneme = otoAlt1.Alias;
} else if (singer.TryGetMappedOto(secondPhoneme, note.tone + shift1, color1, out var oto)) {
secondPhoneme = oto.Alias;
}

// Return Result
return new Result
// Return Result
return new Result
{
phonemes = new Phoneme[] {
new Phoneme { phoneme = currPhoneme },
Expand Down
Loading