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

[WIP] Create customizable english phonemizer #1267

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
10 changes: 10 additions & 0 deletions OpenUtau.Plugin.Builtin/Data/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions OpenUtau.Plugin.Builtin/Data/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@
<data name="envccv_template" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>envccv.template.yaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="en_custom_template" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>en-custom.template.yaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="en_xsampa_template" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\en-xsampa.template.yaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
Expand Down
128 changes: 128 additions & 0 deletions OpenUtau.Plugin.Builtin/Data/en-custom.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
%YAML 1.2
---
tails:
- "-"
symbols:
- name: aa
type: vowel
rename: aa
- name: ae
type: vowel
rename: ae
- name: ah
type: vowel
rename: ah
- name: ao
type: vowel
rename: ao
- name: aw
type: diphthong
rename: aw
- name: ay
type: diphthong
rename: ay
- name: b
type: consonant
rename: b
- name: ch
type: consonant
rename: ch
- name: d
type: consonant
rename: d
- name: dh
type: consonant
rename: dh
- name: eh
type: vowel
rename: eh
- name: er
type: vowel
rename: er
- name: ey
type: diphthong
rename: ey
- name: f
type: consonant
rename: f
- name: g
type: consonant
rename: g
- name: hh
type: consonant
rename: hh
- name: ih
type: vowel
rename: ih
- name: iy
type: vowel
rename: iy
- name: jh
type: consonant
rename: jh
- name: k
type: consonant
rename: k
- name: l
type: consonant
rename: l
- name: m
type: consonant
rename: m
- name: n
type: consonant
rename: n
- name: ng
type: consonant
rename: ng
- name: ow
type: diphthong
rename: ow
- name: oy
type: diphthong
rename: oy
- name: p
type: consonant
rename: p
- name: r
type: consonant
rename: r
- name: s
type: consonant
rename: s
- name: sh
type: consonant
rename: sh
- name: t
type: consonant
rename: t
- name: th
type: consonant
rename: th
- name: uh
type: vowel
rename: uh
- name: uw
type: vowel
rename: uw
- name: v
type: consonant
rename: v
- name: w
type: consonant
rename: w
- name: y
type: consonant
rename: y
- name: z
type: consonant
rename: z
- name: zh
type: consonant
rename: zh
- name: dx
type: consonant
rename: dx
- name: ax
type: vowel
rename: ax
207 changes: 207 additions & 0 deletions OpenUtau.Plugin.Builtin/EnglishCustomizablePhonemizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text;
using OpenUtau.Api;
using OpenUtau.Classic;
using OpenUtau.Core.G2p;
using OpenUtau.Core.Ustx;

namespace OpenUtau.Plugin.Builtin {
[Phonemizer("English Customizable Phonemizer", "EN CUSTOM", "TUBS", language: "EN")]
public class EnglishCustomizablePhonemizer : SyllableBasedPhonemizer {
public EnglishCustomizablePhonemizer () {
vowels = new string[] { "aa", "ae", "ah", "ao", "eh", "er", "ih", "iy", "uh", "uw", "ay", "ey", "oy", "ow", "aw", "ax" };
replacements = new Dictionary<string, string> { };
}

protected override string GetDictionaryName() => "";
protected override IG2p LoadBaseDictionary() => new ArpabetG2p();

private string[] vowels;
private string[] diphthongs;
protected override string[] GetVowels() => vowels;
protected override string[] GetConsonants() => new string[] { }; // All non-vowel symbols are consonants by default. No need to define
private Dictionary<string, string> replacements;
protected override Dictionary<string, string> GetDictionaryPhonemesReplacement() => replacements;

private string[] tails;
private CombinedPhoneme[] combinations;
private Dictionary<string, string[]> splits;

public override void SetSinger(USinger singer) {
if (this.singer != singer) {
string file = "";
if (singer.Location != null) {
file = Path.Combine(singer.Location, "en-custom.yaml");
}

if (File.Exists(file)) {
var data = Core.Yaml.DefaultDeserializer.Deserialize<EnglishCustomConfigData>(File.ReadAllText(file));

tails = data.tails;

var loadVowels = new List<string>();
var loadDiphthongs = new List<string>();
var loadReplacements = new Dictionary<string, string>();
foreach (var symbol in data.symbols) {
var rename = symbol.rename ?? symbol.name;
loadReplacements.Add(symbol.name, rename);
if ((symbol.type == "vowel" || symbol.type == "diphthong") && !loadVowels.Contains(rename)) {
loadVowels.Add(rename);
}
if (symbol.type == "diphthong" && !loadDiphthongs.Contains(rename)) {
loadDiphthongs.Add(rename);
}
}
vowels = loadVowels.ToArray();
diphthongs = loadDiphthongs.ToArray();
replacements = loadReplacements;

if (data.combinations != null) {
var loadCombinations = new List<CombinedPhoneme>();
foreach (var combo in data.combinations) {
loadCombinations.Add(new CombinedPhoneme {
before = combo.before,
after = combo.after,
prefix = combo.prefix ?? combo.after
});
}
combinations = loadCombinations.ToArray();
}

if (data.splits != null) {
splits = new Dictionary<string, string[]>();
foreach (var split in data.splits) {
splits.Add(split.before, split.after);
}
}
} else if (singer.Location != null) {
File.WriteAllBytes(file, Data.Resources.en_custom_template);
}

ReadDictionaryAndInit();
this.singer = singer;
}
}

protected override string[] GetSymbols(Note note) {
if (tails.Contains(note.lyric)) {
return new string[] { note.lyric };
}

var symbols = base.GetSymbols(note);

if (combinations != null) {
var symbolString = string.Join(" ", symbols);
foreach (var combo in combinations) {
symbolString = symbolString.Replace(combo.before, combo.after);
}
symbols = symbolString.Split();
}

if (splits != null) {
var adjustedSymbols = new List<string>();
foreach(var symbol in symbols) {
if (splits.ContainsKey(symbol)) {
adjustedSymbols.AddRange(splits[symbol]);
} else {
adjustedSymbols.Add(symbol);
}
}
symbols = adjustedSymbols.ToArray();
}

return symbols;
}

protected override List<string> ProcessSyllable(Syllable syllable) {
if (CanMakeAliasExtension(syllable) && !diphthongs.Contains(syllable.prevV)) {
return new List<string>();
}

var phonemes = new List<string>();
var symbols = new List<string>();

syllable.prevV = tails.Contains(syllable.prevV) ? "" : syllable.prevV;
symbols.Add(syllable.prevV == "" ? "-" : syllable.prevV);
symbols.AddRange(syllable.cc);
if (syllable.cc.Length == 0) {
symbols.Add(syllable.v);
}

for (int i = 0; i < symbols.Count - 1; i++) {
var second = symbols[i + 1];
if (combinations != null && combinations.Any(c => c.after == second)) {
second = combinations.Where(c => c.after == second).First().prefix;
}
phonemes.Add($"{symbols[i]} {second}");
}

// TODO: make explicit config option for [CV] or [C V] notation. Never check the OTO
if (syllable.cc.Length > 0) {
var cv = new[] { $"{syllable.cc.Last()}{syllable.v}",
$"{syllable.cc.Last()} {syllable.v}"};
if (!TryAddPhoneme(phonemes, syllable.vowelTone, cv)) {
phonemes.Add(cv[1]);
}
}

return phonemes;
}

protected override List<string> ProcessEnding(Ending ending) {
if (tails.Contains(ending.prevV)) {
return new List<string>();
}

var phonemes = new List<string>();
var symbols = new List<string>();
symbols.Add(ending.prevV);
symbols.AddRange(ending.cc);
symbols.Add("-");

for (int i = 0; i < symbols.Count - 1; i++) {
var second = symbols[i + 1];
if (combinations != null && combinations.Any(c => c.after == second)) {
second = combinations.Where(c => c.after == second).First().prefix;
}
phonemes.Add($"{symbols[i]} {second}");
}
return phonemes;
}
struct CombinedPhoneme {
public string before;
public string after;
public string prefix;
}
}

public class EnglishCustomConfigData {
public struct SymbolData {
public string name;
public string type;
public string? rename;
}

public SymbolData[] symbols;
public string[] tails;

public struct CombinePhonemeData {
public string before;
public string after;
public string? prefix;
}

public CombinePhonemeData[]? combinations;

public struct SplitPhonemeData {
public string before;
public string[] after;
}

public SplitPhonemeData[]? splits;
}
}
2 changes: 1 addition & 1 deletion OpenUtau.Plugin.Builtin/SyllableBasedPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ private Result MakeForcedAliasResult(Note note) {
return MakeSimpleResult(note.lyric.Substring(1));
}

private void ReadDictionaryAndInit() {
protected void ReadDictionaryAndInit() {
var dictionaryName = GetDictionaryName();
if (dictionaryName == null) {
return;
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Test/Files/en_custom_a/character.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name=en_custom_a
Loading
Loading