diff --git a/OpenUtau.Plugin.Builtin/ChineseCVVPlusPhonemizer.cs b/OpenUtau.Plugin.Builtin/ChineseCVVPlusPhonemizer.cs
index 3fcb3444c..e6e3ee21f 100644
--- a/OpenUtau.Plugin.Builtin/ChineseCVVPlusPhonemizer.cs
+++ b/OpenUtau.Plugin.Builtin/ChineseCVVPlusPhonemizer.cs
@@ -12,66 +12,66 @@
namespace OpenUtau.Plugin.Builtin {
///
- /// zhcvvplus.yaml 클래스
+ /// zhcvvplus.yaml class
///
[Serializable]
public class ChineseCVVPlusConfigYaml {
///
- /// 접모음의 접두사 지정. 기본값 "_"
+ /// Prefix of affix vowel. Default value is "_"
///
public string VowelTailPrefix = "_";
///
- /// 일정 길이 이상의 비운모를 접모음 없이 사용할 건지에 대한 여부
+ /// Whether to use a long Nasal Vowel without a tail vowel
///
public bool UseSingleNasalVowel = false;
///
- /// 일정 길이 이상의 복합운모를 접모음 없이 사용할 건지에 대한 여부
+ /// Whether to use a long Multiple Vowel without a tail vowel
///
public bool UseSingleMultipleVowel = false;
///
- /// 연단음 여부. True로 설정시 첫번째 노트의 가사에 "- " 추가
+ /// Whether to use retan. If set to True, "- " is added to the first note's lyrics.
///
public bool UseRetan = false;
///
- /// 어미숨 종류. 여러개 사용 가능
+ /// Types of End Breath. Multiple types can be used.
///
public string[] SupportedTailBreath = { "-" };
///
- /// 성모 지정. 추가 자음이 생길 상황을 대비해 커스텀 가능하게 yaml에 분리
+ /// Specify the Consonant. Separated into yaml for customization in case additional consonants are needed.
///
public string[] ConsonantDict = { "zh", "ch", "sh", "b", "p", "m", "f", "d", "t", "n", "l", "z", "c", "s", "r", "j", "q", "x", "g", "k", "h" };
///
- /// 운모 지정. 위와 동일한 이유로 yaml에 분리
+ /// Specify the Vowel. Separated into yaml for the same reason as above.
///
public string[] SingleVowelDict = { "a", "o", "e", "i", "u", "v", "er" };
///
- /// 비운모 지정. 위와 동일한 이유로 yaml에 분리
+ /// Specify the Nasal Vowel. Separated into yaml for the same reason as above.
///
public string[] NasalVowelDict = { "an", "en", "ang", "eng", "ong", "ian", "iang", "ing", "iong", "uan", "uen", "un", "uang", "ueng", "van", "vn" };
///
- /// 복합운모 지정. 위와 동일한 이유로 yaml에 분리
+ /// Specify the Multiple Vowel. Separated into yaml for the same reason as above.
///
public string[] MultipleVowelDict = { "ai", "ei", "ao", "ou", "ia", "iao", "ie", "iou", "ua", "uo", "uai", "uei", "ui", "ve" };
///
- /// 빠른 접모음의 위치 (tick 단위).
+ /// Position of fast tail vowel (in ticks).
///
public int FastTailVowelTimingTick = 100;
///
- /// UseSingleNasalVowel 혹은 UseSingleMultipleVowel 가 True 일때, 단독 사용의 판단 기준 (tick 단위)
+ /// The criterion for determining single usage when UseSingleNasalVowel or UseSingleMultipleVowel is set to True (in ticks).
///
public int SingleVowelsReferenceTimimgTick = 480;
///
- /// 빠른 복합운모. 만일 빠른 복합운모의 비운모가 필요가 없다면 이부분은 비워두고 전부 SlowTailVowelDict 로 옮겨두면 됨
+ /// Fast Multiple Vowel. If a fast Nasal Vowel is not needed, leave this empty and move everything to SlowTailVowelDict.
///
public Dictionary FastTailVowelDict = new Dictionary() {
{"ia", "ia"},
@@ -81,9 +81,9 @@ public class ChineseCVVPlusConfigYaml {
{"ve", "ve"},
};
///
- /// 느린 복합운모. 느린 복합운모의 포지션은 노트의 1/3로 계산 됨
+ /// Slow Multiple Vowel. The position of the slow Multiple Vowel is calculated as 1/3 of the note.
///
- /// {"모음의 기본형": "접모음의 접두사를 제외한 표기"}
+ /// {"Basic form of the vowel": "Representation excluding the prefix of the tail vowel"}
///
public Dictionary SlowTailVowelDict = new Dictionary()
{
@@ -133,7 +133,7 @@ public string[] Consonants {
}
///
- /// yaml을 직렬화 할 때, 배열을 인라인 스타일로 만들기 위한 커스텀 이벤트
+ /// Custom event to make arrays in inline style when serializing yaml
///
class FlowStyleIntegerSequences : ChainedEventEmitter {
public FlowStyleIntegerSequences(IEventEmitter nextEmitter)
@@ -150,13 +150,13 @@ public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) {
///
- /// 포네마이저
+ /// Phonemizer
///
[Phonemizer("Chinese CVV Plus Phonemizer", "ZH CVV+", "2xxbin", language: "ZH")]
public class ChineseCVVPlusPhonemizer : BaseChinesePhonemizer {
private USinger? singer;
///
- /// zhcvvplus.yaml를 담아두는 변수
+ /// Variable containing zhcvvplus.yaml
///
ChineseCVVPlusConfigYaml Config;
public override void SetSinger(USinger singer) {
@@ -165,15 +165,15 @@ public override void SetSinger(USinger singer) {
return;
}
- // zhcvvplus.yaml 경로 지정
+ // Specify the path of zhcvvplus.yaml
var configPath = Path.Join(singer.Location, "zhcvvplus.yaml");
- // 만약 없다면, 새로 제작해 추가
+ // If it doesn't exist, create and add it
if (!File.Exists(configPath)) {
CreateConfigChineseCVVPlus(configPath);
}
- // zhcvvplus.yaml 읽기
+ // Read zhcvvplus.yaml
try {
var configContent = File.ReadAllText(configPath);
var deserializer = new DeserializerBuilder().Build();
@@ -187,7 +187,7 @@ public override void SetSinger(USinger singer) {
}
}
- // 음원 지정
+ // Specify the singer
this.singer = singer;
if (Config == null) {
@@ -196,7 +196,7 @@ public override void SetSinger(USinger singer) {
}
}
- // 노트의 가사를 받아 모음을 반환하는 메소드.
+ // Method that takes the lyrics of a note and returns the vowel.
private string GetLyricVowel(string lyric) {
string initialPrefix = string.Empty;
@@ -209,22 +209,22 @@ private string GetLyricVowel(string lyric) {
}
}
- // 자음 뿐만이 아닌 모음도 제거가 되는 문제(ian -> ia 등) 을 방지하기 위해 가사의 앞 2글자 분리
+ // Split the first two characters of the lyrics to prevent the issue of removing vowels, not just consonants (e.g., ian -> ia)
string prefix = lyric.Substring(0, Math.Min(2, lyric.Length));
string suffix = lyric.Length > 2 ? lyric.Substring(2) : string.Empty;
- // 자음 리스트를 순서대로 선회하며 replace 됨.
+ // Iterate through the consonant list in order and replace them.
foreach (var consonant in Config.Consonants) {
if (prefix.StartsWith(consonant)) {
prefix = prefix.Replace(consonant, string.Empty);
}
}
- // 모음 표기를 일반 표기로 변경
+ // Convert vowel notation to the standard form
return $"{initialPrefix}{(prefix + suffix).Replace("yu", "v").Replace("y", "i").Replace("w", "u").Trim()}";
}
- // oto.ini 안에 해당 에일리어스가 있는지 확인하는 메소드.
+ // Method to check if the alias exists in oto.ini.
public static bool isExistPhonemeInOto(USinger singer, string phoneme, Note note) {
var attr = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
@@ -279,16 +279,16 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
string phoneme = notes[0].lyric;
string? lryicVowel = GetLyricVowel(notes[0].lyric);
- // 만약 발음 힌트가 존재 한다면.
+ // If a phonetic hint exists.
if (notes[0].phoneticHint != null) {
- // 발음 힌트는 쉼표 기준으로 분리 됨.
+ // Phonetic hints are separated by commas.
var phoneticHints = notes[0].phoneticHint.Split(",");
var phonemes = new Phoneme[phoneticHints.Length];
foreach (var phoneticHint in phoneticHints.Select((hint, index) => (hint, index))) {
phonemes[phoneticHint.index] = new Phoneme {
phoneme = GetOtoAlias(singer, phoneticHint.hint.Trim(), notes[0]) ,
- // 포지션은 균등하게 n등분
+ // The position is evenly divided into n parts.
position = totalDuration - ((totalDuration / phoneticHints.Length) * (phoneticHints.Length - phoneticHint.index)),
};
}
@@ -298,68 +298,68 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
};
}
- // 만약 노트가 끝 어미숨 노트라면
+ // If the note is an End Breath note
if (Config.SupportedTailBreath.Contains(phoneme) && prev != null) {
phoneme = GetOtoAlias(singer, $"{GetLyricVowel(prev?.lyric)} {phoneme}", notes[0]);
return new Result {
- // "모음의 기본 형태 + 가사로 작성한 어미숨" 형태로 출력
+ // Output in the form "Basic vowel shape + End Breath written with lyrics"
phonemes = new Phoneme[] { new Phoneme { phoneme = phoneme } }
};
}
- // 만약 zhcvvplus.yaml에서 연단음 여부가 True고, 앞 노트가 없으면서, oto.ini에 "- 가사" 에일리어스가 존재한다면
+ // If retan is set to True in zhcvvplus.yaml, there is no previous note, and the "- lyrics" alias exists in oto.ini
if (Config.UseRetan && prev == null && isExistPhonemeInOto(singer, $"- {phoneme}", notes[0])) {
// 가사를 "- 가사"로 변경
phoneme = $"- {phoneme}";
phoneme = GetOtoAlias(singer, phoneme, notes[0]);
}
- // 만약 접모음이 필요한 가사라면
+ // If the lyrics require a tail vowel
if (Config.TailVowels.ContainsKey(lryicVowel)) {
- // 접노트 가사 선언
+ // Declare the lyrics for the connecting note
var tailPhoneme = $"{Config.VowelTailPrefix}{Config.TailVowels[lryicVowel]}";
- // 1. 노트의 길이가 zhcvvplus.yaml의 판단 틱보다 작거나 같은 동시에
- // 1-1. 가사가 비운모면서 zhcvvplus.yaml의 비운모 단독 사용 여부가 True 거나
- // 1-2. 가사가 복합 운모면서 zhcvvplus.yaml의 복합운모 단독 사용 여부가 True 일때
- // 2. 혹은 zhcvvplus.yaml의 비운모 단독 사용 여부가 False 이면서 가사가 비운모일때
- // 3. 혹은 zhcvvplus.yaml의 복합운모 단독 사용 여부가 False 이면서 가사가 복합운모일때
+ // 1. When the length of the note is less than or equal to the judgment tick in zhcvvplus.yaml
+ // 1-1. The lyrics are a Nasal Vowel, and the single use of Nasal Vowel in zhcvvplus.yaml is set to True, or
+ // 1-2. The lyrics are a Multiple Vowel, and the single use of Multiple Vowel in zhcvvplus.yaml is set to True
+ // 2. Or when the single use of Nasal Vowel in zhcvvplus.yaml is set to False, and the lyrics are a Nasal Vowel
+ // 3. Or when the single use of Multiple Vowel in zhcvvplus.yaml is set to False, and the lyrics are a Multiple Vowel
if ((totalDuration <= Config.SingleVowelsReferenceTimimgTick &&
(Config.UseSingleNasalVowel && Config.NasalVowelDict.Contains(lryicVowel)
|| Config.UseSingleMultipleVowel && Config.MultipleVowelDict.Contains(lryicVowel)) ||
(!Config.UseSingleNasalVowel && Config.NasalVowelDict.Contains(lryicVowel)) ||
(!Config.UseSingleMultipleVowel && Config.MultipleVowelDict.Contains(lryicVowel)))) {
- // 자연스러움을 위해 접운모의 위치는 노트의 1/3로 지정
+ // To ensure naturalness, the position of the tail vowel is set to 1/3 of the note.
var tailVowelPosition = totalDuration - totalDuration / 3;
- // 만약 빠른 접모음 이라면
+ // If it is a fast tail vowel,
if (Config.FastTailVowelDict.ContainsKey(lryicVowel)) {
- // zhcvvplus.yaml에서 지정한 포지션으로 변경
+ // Change to the position specified in zhcvvplus.yaml.
tailVowelPosition = Config.FastTailVowelTimingTick;
}
phoneme = GetOtoAlias(singer, phoneme, notes[0]);
tailPhoneme = GetOtoAlias(singer, tailPhoneme, notes[0]);
return new Result() {
phonemes = new Phoneme[] {
- new Phoneme { phoneme = phoneme }, // 원 노트 가사
- new Phoneme { phoneme = tailPhoneme, position = tailVowelPosition}, // 접모음
+ new Phoneme { phoneme = phoneme }, // Original note lyrics
+ new Phoneme { phoneme = tailPhoneme, position = tailVowelPosition}, // Tail vowel
}
};
}
};
- // 위 if문중 어디에도 해당하지 않는다면
+ // If it does not match any of the above if statements,
return new Result {
phonemes = new Phoneme[] {
new Phoneme() {
- phoneme = phoneme, // 입력한 가사로 출력
+ phoneme = phoneme, // Output the entered lyrics.
}
}
};
} catch (Exception e) {
- Log.Error(e, "An error occurred during the phoneme processing in zh cvv+ module."); // 로깅
+ Log.Error(e, "An error occurred during the phoneme processing in zh cvv+ module."); // Logging
return new Result {
phonemes = new Phoneme[] {