Skip to content
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
50 changes: 47 additions & 3 deletions OpenUtau.Core/Api/Phonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/// <param name="tag">Use IETF language code + phonetic type as tag, e.g., "EN ARPA", "JA VCV", etc. Required.</param>
/// <param name="author">Author of this phonemizer.</param>
/// <param name="language">IETF language code of this phonemizer's singing language, e.g., "EN", "JA"</param>
public PhonemizerAttribute(string name, string tag, string author = null, string language = null) {

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (ubuntu-latest, linux-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (ubuntu-latest, linux-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (macos-15-intel, osx-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (macos-15-intel, osx-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (windows-latest, win-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (windows-latest, win-x64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (macos-latest, osx-arm64)

Cannot convert null literal to non-nullable reference type.

Check warning on line 22 in OpenUtau.Core/Api/Phonemizer.cs

View workflow job for this annotation

GitHub Actions / pr-test (macos-latest, osx-arm64)

Cannot convert null literal to non-nullable reference type.
Name = name;
Tag = tag;
Author = author;
Expand Down Expand Up @@ -86,15 +86,15 @@
/// <summary>
/// Tone shift. Shifts the note tone used for oto lookup.
/// </summary>
public int toneShift;
public int? toneShift;
/// <summary>
/// Alternate index. The number suffix of duplicate aliases.
/// </summary>
public int? alternate;
/// <summary>
/// Voice color.
/// </summary>
public string voiceColor;
public string? voiceColor;
}

public struct PhonemeExpression {
Expand Down Expand Up @@ -176,7 +176,13 @@
/// </summary>
public virtual bool LegacyMapping => false;

public virtual void SetUp(Note[][] notes, UProject project, UTrack track) { }
public UProject? project;
public UTrack? track;

public virtual void SetUp(Note[][] notes, UProject project, UTrack track) {
this.project = project;
this.track = track;
}

/// <summary>
/// Phonemize a consecutive sequence of notes. This is the main logic of a phonemizer.
Expand Down Expand Up @@ -259,6 +265,44 @@
};
}

public double GetParentConsonantStretchRatio() {
if (project != null && track != null) {
if (track.TryGetExpDescriptor(project, Core.Format.Ustx.VEL, out var trackVEL)) {
return Math.Pow(2, 1.0 - trackVEL.CustomDefaultValue / 100.0);
}
}
return 1;
}

public int GetParentToneShift() {
if (project != null && track != null) {
if (track.TryGetExpDescriptor(project, Core.Format.Ustx.SHFT, out var trackTS)) {
return (int)trackTS.CustomDefaultValue;
}
}
return 0;
}

public int? GetParentAlternate() {
if (project != null && track != null) {
if (track.TryGetExpDescriptor(project, Core.Format.Ustx.ALT, out var trackAlt)) {
if (trackAlt.CustomDefaultValue != 0) {
return (int)trackAlt.CustomDefaultValue;
}
}
}
return null;
}

public string GetParentVoiceColor() {
if (project != null && track != null) {
if (track.TryGetExpDescriptor(project, Core.Format.Ustx.CLR, out var trackCLR)) {
return track.VoiceColorExp.options[(int)trackCLR.CustomDefaultValue];
}
}
return string.Empty;
}

/// <summary>
/// Utility method to map a phoneme alias to proper pitch using prefixmap.
/// For example, MapPhoneme("あ", 72, singer) may return "あC5".
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Core/BaseChinesePhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static void RomanizeNotes(Note[][] groups) {
}

public override void SetUp(Note[][] groups, UProject project, UTrack track) {
base.SetUp(groups, project, track);
RomanizeNotes(groups);
}
}
Expand Down
6 changes: 5 additions & 1 deletion OpenUtau.Core/DefaultPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN
// For this simple phonemizer, all these notes maps to a single phoneme.
string alias = notes[0].lyric;
var attr0 = notes[0].phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
if (singer.TryGetMappedOto(notes[0].lyric, notes[0].tone + attr0.toneShift, attr0.voiceColor, out var oto)) {
string color = attr0.voiceColor ?? GetParentVoiceColor();
int shift = attr0.toneShift ?? GetParentToneShift();
int? alt = attr0.alternate ?? GetParentAlternate();

if (singer.TryGetMappedOto(notes[0].lyric + alt, notes[0].tone + shift, color, out var oto)) {
alias = oto.Alias;
}
return new Result {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using OpenUtau.Api;
using OpenUtau.Api;
using OpenUtau.Core.Ustx;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -12,6 +12,7 @@ public class DiffSingerKoreanPhonemizer : DiffSingerBasePhonemizer
public override string GetLangCode()=>"ko";

public override void SetUp(Note[][] groups, UProject project, UTrack track) {
base.SetUp(groups, project, track);
if (groups.Length == 0) {
return;
}
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Core/Enunu/EnunuKoreanPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ public enum BatchimType{
Dictionary<Note[], Phoneme[]> partResult = new Dictionary<Note[], Phoneme[]>();

public override void SetUp(Note[][] notes, UProject project, UTrack track) {
base.SetUp(notes, project, track);
partResult.Clear();
if (notes.Length == 0 || singer == null || !singer.Found) {
return;
Expand Down
3 changes: 2 additions & 1 deletion OpenUtau.Core/Enunu/EnunuPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public override void SetSinger(USinger singer) {
}

public override void SetUp(Note[][] notes, UProject project, UTrack track) {
base.SetUp(notes, project, track);
partResult.Clear();
if (notes.Length == 0 || singer == null || !singer.Found) {
return;
Expand Down Expand Up @@ -174,4 +175,4 @@ public override void CleanUp() {
partResult.Clear();
}
}
}
}
1 change: 1 addition & 0 deletions OpenUtau.Core/MachineLearningPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public abstract class MachineLearningPhonemizer : Phonemizer
//groups is a two-dimensional array of Note, each Note[] represents a lyrical note and its following slur notes
//Run phoneme timing model in sections to prevent butterfly effect
public override void SetUp(Note[][] groups, UProject project, UTrack track) {
base.SetUp(groups, project, track);
SetUpException = null;
lastProcessPartException = null;
partResult.Clear();
Expand Down
2 changes: 1 addition & 1 deletion OpenUtau.Core/Ustx/UNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ internal Phonemizer.Note ToPhonemizerNote(UTrack track, UPart part) {
attr.index = exp.index.Value;
if (exp.abbr == Format.Ustx.VEL) {
attr.consonantStretchRatio = Math.Pow(2, 1.0 - exp.value / 100.0);
} else if (exp.abbr == Format.Ustx.ALT) {
} else if (exp.abbr == Format.Ustx.ALT && exp.value != 0) { // 0 means no alt (nothing added)
attr.alternate = (int)exp.value;
} else if (exp.abbr == Format.Ustx.CLR && track.VoiceColorExp != null) {
int optionIdx = (int)exp.value;
Expand Down
1 change: 1 addition & 0 deletions OpenUtau.Core/Vogen/VogenBasePhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ private void AddGroup(List<Note> phrase, Note[] group){
});
}
public override void SetUp(Note[][] groups, UProject project, UTrack track) {
base.SetUp(groups, project, track);
if (groups.Length == 0) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion OpenUtau.Core/Voicevox/Phonemizers/VoicevoxPhonemizer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenUtau.Api;
using OpenUtau.Core.Ustx;
Expand All @@ -17,6 +16,7 @@ public override void SetSinger(USinger singer) {
}

public override void SetUp(Note[][] notes, UProject project, UTrack track) {
base.SetUp(notes, project, track);
partResult.Clear();
VoicevoxNote[] vNotes = new VoicevoxNote[notes.Length];
for (int i = 0; i < notes.Length; i++) {
Expand Down
13 changes: 5 additions & 8 deletions OpenUtau.Plugin.Builtin/BaseKoreanPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,18 @@ protected virtual bool additionalTest(string lyric) {
return false;
}
public override void SetSinger(USinger singer) => this.singer = singer;
public static string? FindInOto(USinger singer, string phoneme, Note note, bool nullIfNotFound = false) {
public string? FindInOto(USinger singer, string phoneme, Note note, bool nullIfNotFound = false) {
// 음소와 노트를 입력받고, 다음계 및 보이스컬러 에일리어스를 적용한다.
// nullIfNotFound가 true이면 음소가 찾아지지 않을 때 음소가 아닌 null을 리턴한다.
// nullIfNotFound가 false면 음소가 찾아지지 않을 때 그대로 음소를 반환
string phonemeToReturn;
var attr = note.phonemeAttributes?.FirstOrDefault(attr => attr.index == 0) ?? default;
string color = attr.voiceColor ?? string.Empty;
int toneShift = 0;
int? alt = null;
string color = attr.voiceColor ?? GetParentVoiceColor();
int toneShift = attr.toneShift ?? GetParentToneShift();
int? alt = attr.alternate ?? GetParentAlternate();
if (phoneme.Equals("")) {return phoneme;}

if (singer.TryGetMappedOto(phoneme + alt, note.tone + toneShift, color, out var otoAlt)) {
phonemeToReturn = otoAlt.Alias;
}
else if (singer.TryGetMappedOto(phoneme, note.tone + toneShift, color, out var oto)) {
if (singer.TryGetMappedOto(phoneme + alt, note.tone + toneShift, color, out var oto)) {
phonemeToReturn = oto.Alias;
}
else if (singer.TryGetMappedOto(phoneme, note.tone, color, out oto)) {
Expand Down
28 changes: 9 additions & 19 deletions OpenUtau.Plugin.Builtin/CantoneseSyoPhonemizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static CantoneseSyoPhonemizer() {
/// </summary>
/// <param name="groups"></param>
public override void SetUp(Note[][] groups, UProject project, UTrack track) {
base.SetUp(groups, project, track);
JyutpingConversion.RomanizeNotes(groups);
}
public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevNeighbour, Note? nextNeighbour, Note[] prevNeighbours) {
Expand Down Expand Up @@ -98,17 +99,6 @@ public override Result Process(Note[] notes, Note? prev, Note? next, Note? prevN

string phoneme0 = lyric;

// Get color
string color = string.Empty;
int toneShift = 0;
int? alt = 0;
if (note.phonemeAttributes != null) {
var attr = note.phonemeAttributes.FirstOrDefault(attr0 => attr0.index == 0);
color = attr.voiceColor;
toneShift = attr.toneShift;
alt = attr.alternate;
}

string fin = $"{vowel} -";
// We will need to split the total duration for phonemes, so we compute it here.
int totalDuration = notes.Sum(n => n.duration);
Expand Down Expand Up @@ -316,17 +306,17 @@ public void SetUp(Note[][] groups) {
private bool checkOtoUntilHit(List<string> input, Note note, out UOto oto) {
oto = default;
var attr = note.phonemeAttributes?.FirstOrDefault(attrCheck => attrCheck.index == 0) ?? default;
string color = attr.voiceColor ?? GetParentVoiceColor();
int shift = attr.toneShift ?? GetParentToneShift();
int? alt = attr.alternate ?? GetParentAlternate();

var otos = new List<UOto>();
foreach (string test in input) {
if (singer.TryGetMappedOto(test + attr.alternate, note.tone + attr.toneShift, attr.voiceColor, out var otoAlt)) {
otos.Add(otoAlt);
} else if (singer.TryGetMappedOto(test, note.tone + attr.toneShift, attr.voiceColor, out var otoCandidacy)) {
if (singer.TryGetMappedOto(test + alt, note.tone + shift, color, out var otoCandidacy)) {
otos.Add(otoCandidacy);
}
}

string color = attr.voiceColor ?? "";
if (otos.Count > 0) {
oto = otos.FirstOrDefault(oto => oto.IsColorMatch(color));
if (oto == null) {
Expand All @@ -341,17 +331,17 @@ private bool checkOtoUntilHit(List<string> input, Note note, out UOto oto) {
private bool checkOtoUntilHitFinal(List<string> input, Note note, out UOto oto) {
oto = default;
var attr = note.phonemeAttributes?.FirstOrDefault(attrCheck => attrCheck.index == 1) ?? default;
string color = attr.voiceColor ?? GetParentVoiceColor();
int shift = attr.toneShift ?? GetParentToneShift();
int? alt = attr.alternate ?? GetParentAlternate();

var otos = new List<UOto>();
foreach (string test in input) {
if (singer.TryGetMappedOto(test + attr.alternate, note.tone + attr.toneShift, attr.voiceColor, out var otoAlt)) {
otos.Add(otoAlt);
} else if (singer.TryGetMappedOto(test, note.tone + attr.toneShift, attr.voiceColor, out var otoCandidacy)) {
if (singer.TryGetMappedOto(test + alt, note.tone + shift, color, out var otoCandidacy)) {
otos.Add(otoCandidacy);
}
}

string color = attr.voiceColor ?? "";
if (otos.Count > 0) {
oto = otos.FirstOrDefault(oto => oto.IsColorMatch(color));
if (oto != null) {
Expand Down
Loading
Loading