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

Add SimpleVOICEVOX ENtoJA Phonemizer #1242

Merged
merged 5 commits into from
Sep 1, 2024
Merged
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
22 changes: 12 additions & 10 deletions OpenUtau.Core/Voicevox/Phonemizers/SimpleVoicevoxPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Voicevox {
[Phonemizer("Simple Voicevox Japanese Phonemizer", "S-VOICEVOX JA", language: "JA")]
public class SimpleVoicevoxPhonemizer : Phonemizer {

protected VoicevoxSinger singer;
protected VoicevoxSinger singer;

public override void SetSinger(USinger singer) {
this.singer = singer as VoicevoxSinger;
Expand All @@ -18,7 +18,7 @@ public override void SetSinger(USinger singer) {
}

protected bool IsSyllableVowelExtensionNote(Note note) {
return note.lyric.StartsWith("+~") || note.lyric.StartsWith("+*");
return note.lyric.StartsWith("+~") || note.lyric.StartsWith("+*") || note.lyric.StartsWith("+") || note.lyric.StartsWith("-");
}

public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevNeighbours) {
Expand All @@ -32,15 +32,17 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
notes[i].lyric = lyricList[1];
}
if (!IsSyllableVowelExtensionNote(notes[i])) {
if (VoicevoxUtils.IsHiraKana(notes[i].lyric)) {
phonemes.Add(new Phoneme { phoneme = notes[i].lyric });
} else if (VoicevoxUtils.IsPau(notes[i].lyric)) {
phonemes.Add(new Phoneme { phoneme = "R" });
} else if (VoicevoxUtils.dic.IsDic(notes[i].lyric)) {
phonemes.Add(new Phoneme { phoneme = VoicevoxUtils.dic.Lyrictodic(notes[i].lyric) });
} else {
phonemes.Add(new Phoneme { phoneme = "error"});
string val = "error";
if (VoicevoxUtils.phoneme_List.kanas.ContainsKey(notes[i].lyric) || VoicevoxUtils.phoneme_List.paus.ContainsKey(notes[i].lyric)) {
if (VoicevoxUtils.phoneme_List.paus.TryGetValue(notes[i].lyric, out string str)) {
val = str;
} else if (VoicevoxUtils.dic.IsDic(notes[i].lyric)) {
val = VoicevoxUtils.dic.Lyrictodic(notes[i].lyric);
} else {
val = notes[i].lyric;
}
}
phonemes.Add(new Phoneme { phoneme = val });
}
}
return new Result { phonemes = phonemes.ToArray() };
Expand Down
55 changes: 0 additions & 55 deletions OpenUtau.Core/Voicevox/VoicevoxConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,61 +167,6 @@ public void SaveLicenses(string location) {
}
}

public class Phoneme_list {
public string[] vowels;
public string[] consonants;
public string[] kana;
}

public class Dictionary_list {
public Dictionary<string, string> dict = new Dictionary<string, string>();

public void Loaddic(string location) {
try {
var parentDirectory = Directory.GetParent(location).ToString();
var yamlPath = Path.Join(parentDirectory, "dictionary.yaml");
if (File.Exists(yamlPath)) {
var yamlTxt = File.ReadAllText(yamlPath);
var yamlObj = Yaml.DefaultDeserializer.Deserialize<Dictionary<string, List<Dictionary<string, string>>>>(yamlTxt);
var list = yamlObj["list"];
dict = new Dictionary<string, string>();

foreach (var item in list) {
foreach (var pair in item) {
dict[pair.Key] = pair.Value;
}
}

}
} catch (Exception e) {
Log.Error($"Failed to read dictionary file. : {e}");
}
}
public string Notetodic(Note[][] notes, int index) {
if (dict.TryGetValue(notes[index][0].lyric, out var lyric_)) {
if (string.IsNullOrEmpty(lyric_)) {
return "";
}
return lyric_;
}
return notes[index][0].lyric;
}

public string Lyrictodic(string lyric) {
if (dict.TryGetValue(lyric, out var lyric_)) {
if (string.IsNullOrEmpty(lyric_)) {
return "";
}
return lyric_;
}
return lyric;
}

public bool IsDic(string lyric) {
return dict.ContainsKey(lyric);
}
}

public class Style_infos {
public int id;
public string icon = string.Empty;
Expand Down
21 changes: 10 additions & 11 deletions OpenUtau.Core/Voicevox/VoicevoxSinger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,18 @@ void Load() {
table.Clear();
otos.Clear();
try {
var parentDirectory = Directory.GetParent(this.Location).ToString();
var yamlPath = Path.Join(parentDirectory, "phonemes.yaml");
var yamlTxt = File.ReadAllText(yamlPath);
var phonemes_list = Yaml.DefaultDeserializer.Deserialize<Phoneme_list>(yamlTxt);
//Prepared for planned changes or additions to phonemizers.
foreach (var str in phonemes_list.vowels) {
phonemes.Add(str);
//foreach (var str in VoicevoxUtils.phoneme_List.vowels) {
// phonemes.Add(str);
//}
//foreach (var str in VoicevoxUtils.phoneme_List.consonants) {
// phonemes.Add(str);
//}
foreach (var str in VoicevoxUtils.phoneme_List.kanas) {
phonemes.Add(str.Key);
}
foreach (var str in phonemes_list.consonants) {
phonemes.Add(str);
}
foreach (var str in phonemes_list.kana) {
phonemes.Add(str);
foreach (var str in VoicevoxUtils.phoneme_List.paus) {
phonemes.Add(str.Key);
}
} catch (Exception e) {
Log.Error(e, $"Failed to load phonemes.yaml for {Name}");
Expand Down
109 changes: 93 additions & 16 deletions OpenUtau.Core/Voicevox/VoicevoxUtils.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -39,15 +40,97 @@ public class VoicevoxQueryNotes {
public class VoicevoxQueryMain {
public List<VoicevoxQueryNotes> notes = new List<VoicevoxQueryNotes>();
}
public class Phoneme_list {
public string[] vowels = "a i u e o A I U E O N pau cl".Split();
public string[] consonants = "b by ch d dy f g gw gy h hy j k kw ky m my n ny p py r ry s sh t ts ty v w y z".Split();
public Dictionary<string, string> kanas = new Dictionary<string, string>();
public Dictionary<string, string> paus = new Dictionary<string, string>();
public Phoneme_list() {
var kanaGroups = new List<string[]> {
"あ ば びゃ ちゃ だ でゃ ふぁ が ぐゎ ぎゃ は ひゃ じゃ か くゎ きゃ ま みゃ な にゃ ぱ ぴゃ ら りゃ さ しゃ た つぁ てゃ ゔぁ わ や ざ".Split(),
"い び ち ぢ でぃ ふぃ ぎ ひ じ き み に ぴ り すぃ し てぃ つぃ ゔぃ うぃ ずぃ".Split(),
"う ぶ びゅ ちゅ どぅ でゅ ふ ぐ ぎゅ ひゅ じゅ く きゅ む みゅ ぬ にゅ ぷ ぴゅ る りゅ す しゅ つ つ てゅ ゔ ゆ ず".Split(),
"え べ びぇ ちぇ で でぇ ふぇ げ ぎぇ へ ひぇ じぇ け きぇ め みぇ ね にぇ ぺ ぴぇ れ りぇ せ しぇ て つぇ ゔぇ うぇ いぇ ぜ".Split(),
"お ぼ びょ ちょ ど でょ ふぉ ご ぎょ ほ ひょ じょ こ きょ も みょ の にょ ぽ ぴょ ろ りょ そ しょ と つぉ てょ ゔぉ を よ ぞ".Split(),
"ん ン".Split(),
"っ ッ".Split()
};

foreach (var group in kanaGroups) {
foreach (var kana in group) {
if (!kanas.ContainsKey(kana)) {
kanas.Add(kana.Normalize(), group[0].Normalize());
}
}
}
string[] pauseGroups = "R pau AP SP".Split();

foreach (string group in pauseGroups) {
if (!paus.ContainsKey(group)) {
paus.Add(group.Normalize(), pauseGroups[0].Normalize());
}
}
}
}

public class Dictionary_list {
public Dictionary<string, string> dict = new Dictionary<string, string>();

public void Loaddic(string location) {
try {
var parentDirectory = Directory.GetParent(location).ToString();
var yamlPath = Path.Join(parentDirectory, "dictionary.yaml");
if (File.Exists(yamlPath)) {
var yamlTxt = File.ReadAllText(yamlPath);
var yamlObj = Yaml.DefaultDeserializer.Deserialize<Dictionary<string, List<Dictionary<string, string>>>>(yamlTxt);
var list = yamlObj["list"];
dict = new Dictionary<string, string>();

foreach (var item in list) {
foreach (var pair in item) {
dict[pair.Key] = pair.Value;
}
}

}
} catch (Exception e) {
Log.Error($"Failed to read dictionary file. : {e}");
}
}
public string Notetodic(Note[][] notes, int index) {
if (dict.TryGetValue(notes[index][0].lyric, out var lyric_)) {
if (string.IsNullOrEmpty(lyric_)) {
return "";
}
return lyric_;
}
return notes[index][0].lyric;
}

public string Lyrictodic(string lyric) {
if (dict.TryGetValue(lyric, out var lyric_)) {
if (string.IsNullOrEmpty(lyric_)) {
return "";
}
return lyric_;
}
return lyric;
}

public bool IsDic(string lyric) {
return dict.ContainsKey(lyric);
}
}


internal static class VoicevoxUtils {
public static class VoicevoxUtils {
public const string VOLC = "volc";
public const int headS = 1;
public const int tailS = 1;
public const double fps = 93.75;
public const string defaultID = "6000";
public static Dictionary_list dic = new Dictionary_list();
public static Phoneme_list phoneme_List = new Phoneme_list();

public static VoicevoxNote VoicevoxVoiceBase(VoicevoxQueryMain qNotes, string id) {
var queryurl = new VoicevoxURL() { method = "POST", path = "/sing_frame_audio_query", query = new Dictionary<string, string> { { "speaker", id } }, body = JsonConvert.SerializeObject(qNotes) };
Expand Down Expand Up @@ -83,11 +166,11 @@ public static VoicevoxQueryMain NoteGroupsToVoicevox(Note[][] notes, TimeAxis ti
string lyric = dic.Notetodic(notes, index);
int length = (int)Math.Round(((timeAxis.TickPosToMsPos(notes[index].Sum(n => n.duration)) / 1000f) * VoicevoxUtils.fps), MidpointRounding.AwayFromZero);
//Avoid synthesis without at least two frames.
if (length < 2 ) {
if (length < 2) {
length = 2;
}
int? tone = null;
if (!string.IsNullOrEmpty(lyric) || VoicevoxUtils.IsPau(lyric)) {
if (!string.IsNullOrEmpty(lyric)) {
if (notes[index][0].phonemeAttributes != null) {
if (notes[index][0].phonemeAttributes.Length > 0) {
tone = notes[index][0].tone + notes[index][0].phonemeAttributes[0].toneShift;
Expand All @@ -97,6 +180,8 @@ public static VoicevoxQueryMain NoteGroupsToVoicevox(Note[][] notes, TimeAxis ti
} else {
tone = notes[index][0].tone;
}
} else {
lyric = "";
}
qnotes.notes.Add(new VoicevoxQueryNotes {
lyric = lyric,
Expand Down Expand Up @@ -146,21 +231,13 @@ public static double[] SampleCurve(RenderPhrase phrase, float[] curve, double de
return result;
}


public static bool IsHiraKana(string s) {
foreach(char c in s.ToCharArray()) {
if (!('\u3041' <= c && c <= '\u309F') || ('\u30A0' <= c && c <= '\u30FF') || c == '\u30FC' || c == '\u30A0') {
return false;
}
}
return true;
public static bool IsPau(string s) {
return phoneme_List.paus.ContainsKey(s);
}

public static bool IsPau(string s) {
if (s.EndsWith("R") || s.ToLower().EndsWith("pau") || s.EndsWith("AP") || s.EndsWith("SP")) {
return true;
}
return false;
public static bool TryGetPau(string s, out string str) {
phoneme_List.paus.TryGetValue(s, out str);
return phoneme_List.paus.ContainsKey(s);
}

public static string getBaseSingerID(VoicevoxSinger singer) {
Expand Down
Loading
Loading