Skip to content

Commit 690afd5

Browse files
committed
worldline-r supports directs
1 parent 7582032 commit 690afd5

8 files changed

+95
-60
lines changed

OpenUtau.Core/Classic/ClassicRenderer.cs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Text;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using NAudio.Wave;

OpenUtau.Core/Classic/ExeWavtool.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ void WriteSetUp(StreamWriter writer, List<ResamplerItem> resamplerItems, string
102102

103103
void WriteItem(StreamWriter writer, ResamplerItem item, int index, int total) {
104104
writer.WriteLine($"@set resamp={item.resampler.FilePath}");
105-
writer.WriteLine($"@set params={item.volume} {item.modulation} !{item.tempo.ToString("G999")} {Base64.Base64EncodeInt12(item.pitches)}");
105+
writer.WriteLine($"@set params={item.volume} {item.modulation} !{item.tempo:G999} {Base64.Base64EncodeInt12(item.pitches)}");
106106
writer.WriteLine($"@set flag=\"{item.GetFlagsString()}\"");
107107
writer.WriteLine($"@set env={GetEnvelope(item)}");
108108
writer.WriteLine($"@set stp={item.skipOver}");
109109
writer.WriteLine($"@set vel={item.velocity}");
110110
string relOutputFile = Path.GetRelativePath(PathManager.Inst.CachePath, item.outputFile);
111111
writer.WriteLine($"@set temp=\"%cachedir%\\{relOutputFile}\"");
112112
string toneName = MusicMath.GetToneName(item.tone);
113-
string dur = $"{item.phone.duration.ToString("G999")}@{item.phone.adjustedTempo.ToString("G999")}{(item.durCorrection >= 0 ? "+" : "")}{item.durCorrection}";
113+
string dur = $"{item.phone.duration:G999}@{item.phone.adjustedTempo:G999}{(item.durCorrection >= 0 ? "+" : "")}{item.durCorrection}";
114114
string relInputTemp = Path.GetRelativePath(PathManager.Inst.CachePath, item.inputTemp);
115115
writer.WriteLine($"@echo {MakeProgressBar(index + 1, total)}");
116116
writer.WriteLine($"@call %helper% \"%oto%\\{relInputTemp}\" {toneName} {dur} {item.preutter} {item.offset} {item.durRequired} {item.consonant} {item.cutoff} {index}");

OpenUtau.Core/Classic/ResamplerItem.cs

+41-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Numerics;
56
using System.Text;
67
using K4os.Hash.xxHash;
78
using NAudio.Wave;
89
using OpenUtau.Core;
910
using OpenUtau.Core.Render;
1011
using OpenUtau.Core.Ustx;
12+
using static NetMQ.NetMQSelector;
1113
using static OpenUtau.Api.Phonemizer;
1214

1315
namespace OpenUtau.Classic {
@@ -80,7 +82,7 @@ public ResamplerItem(RenderPhrase phrase, RenderPhone phone) {
8082
var pitchIntervalMs = MusicMath.TempoTickToMs(tempo, 5);
8183
var pitchSampleStartMs = phone.positionMs - pitchLeadingMs;
8284

83-
for (int i=0; i<pitches.Length; i++) {
85+
for (int i = 0; i < pitches.Length; i++) {
8486
var samplePosMs = pitchSampleStartMs + pitchIntervalMs * i;
8587
var samplePosTick = (int)Math.Floor(phrase.timeAxis.MsPosToNonExactTickPos(samplePosMs));
8688

@@ -145,5 +147,43 @@ ulong Hash() {
145147
}
146148
}
147149
}
150+
151+
public List<Vector2> EnvelopeMsToSamples() {
152+
int skipOverSamples = (int)(skipOver * 44100 / 1000);
153+
var envelope = phone.envelope.ToList();
154+
double shift = -envelope[0].X;
155+
for (int i = 0; i < envelope.Count; ++i) {
156+
var point = envelope[i];
157+
point.X = (float)((point.X + shift) * 44100 / 1000) + skipOverSamples;
158+
point.Y /= 100;
159+
envelope[i] = point;
160+
}
161+
return envelope;
162+
}
163+
164+
public void ApplyEnvelope(float[] samples) {
165+
var envelope = EnvelopeMsToSamples();
166+
int nextPoint = 0;
167+
for (int i = 0; i < samples.Length; ++i) {
168+
while (nextPoint < envelope.Count && i > envelope[nextPoint].X) {
169+
nextPoint++;
170+
}
171+
float gain;
172+
if (nextPoint == 0) {
173+
gain = envelope.First().Y;
174+
} else if (nextPoint >= envelope.Count) {
175+
gain = envelope.Last().Y;
176+
} else {
177+
var p0 = envelope[nextPoint - 1];
178+
var p1 = envelope[nextPoint];
179+
if (p0.X >= p1.X) {
180+
gain = p0.Y;
181+
} else {
182+
gain = p0.Y + (p1.Y - p0.Y) * (i - p0.X) / (p1.X - p0.X);
183+
}
184+
}
185+
samples[i] *= gain;
186+
}
187+
}
148188
}
149189
}

OpenUtau.Core/Classic/SharpWavtool.cs

+15-52
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,23 @@ public float[] Concatenate(List<ResamplerItem> resamplerItems, string tempPath,
5656
segment.posMs = item.phone.positionMs - item.phone.leadingMs - (phrase.positionMs - phrase.leadingMs);
5757
segment.posSamples = (int)Math.Round(segment.posMs * 44100 / 1000);
5858
segment.skipSamples = (int)Math.Round(item.skipOver * 44100 / 1000);
59-
segment.envelope = EnvelopeMsToSamples(item.phone.envelope, segment.skipSamples);
60-
61-
if (!phaseComp) {
62-
continue;
59+
segment.envelope = item.EnvelopeMsToSamples();
60+
61+
if (phaseComp) {
62+
var headWindow = GetHeadWindow(segment.samples, segment.envelope, out segment.headWindowStart);
63+
segment.headWindowF0 = GetF0AtSample(phrase,
64+
segment.posSamples - segment.skipSamples + segment.headWindowStart + headWindow.Length / 2);
65+
segment.headPhase = CalcPhase(headWindow,
66+
segment.posSamples - segment.skipSamples + segment.headWindowStart, 44100, segment.headWindowF0);
67+
68+
var tailWindow = GetTailWindow(segment.samples, segment.envelope, out segment.tailWindowStart);
69+
segment.tailWindowF0 = GetF0AtSample(phrase,
70+
segment.posSamples - segment.skipSamples + segment.tailWindowStart + tailWindow.Length / 2);
71+
segment.tailPhase = CalcPhase(tailWindow,
72+
segment.posSamples - segment.skipSamples + segment.tailWindowStart, 44100, segment.tailWindowF0);
6373
}
6474

65-
var headWindow = GetHeadWindow(segment.samples, segment.envelope, out segment.headWindowStart);
66-
segment.headWindowF0 = GetF0AtSample(phrase,
67-
segment.posSamples - segment.skipSamples + segment.headWindowStart + headWindow.Length / 2);
68-
segment.headPhase = CalcPhase(headWindow,
69-
segment.posSamples - segment.skipSamples + segment.headWindowStart, 44100, segment.headWindowF0);
70-
71-
var tailWindow = GetTailWindow(segment.samples, segment.envelope, out segment.tailWindowStart);
72-
segment.tailWindowF0 = GetF0AtSample(phrase,
73-
segment.posSamples - segment.skipSamples + segment.tailWindowStart + tailWindow.Length / 2);
74-
segment.tailPhase = CalcPhase(tailWindow,
75-
segment.posSamples - segment.skipSamples + segment.tailWindowStart, 44100, segment.tailWindowF0);
75+
item.ApplyEnvelope(segment.samples);
7676
}
7777

7878
if (phaseComp) {
@@ -100,50 +100,13 @@ public float[] Concatenate(List<ResamplerItem> resamplerItems, string tempPath,
100100
var phraseSamples = new float[0];
101101
foreach (var segment in segments) {
102102
Array.Resize(ref phraseSamples, segment.posSamples + segment.correction + segment.samples.Length - segment.skipSamples);
103-
ApplyEnvelope(segment.samples, segment.envelope);
104103
for (int i = Math.Max(0, -segment.skipSamples); i < segment.samples.Length - segment.skipSamples; i++) {
105104
phraseSamples[segment.posSamples + segment.correction + i] += segment.samples[segment.skipSamples + i];
106105
}
107106
}
108107
return phraseSamples;
109108
}
110109

111-
private static void ApplyEnvelope(float[] data, IList<Vector2> envelope) {
112-
int nextPoint = 0;
113-
for (int i = 0; i < data.Length; ++i) {
114-
while (nextPoint < envelope.Count && i > envelope[nextPoint].X) {
115-
nextPoint++;
116-
}
117-
float gain;
118-
if (nextPoint == 0) {
119-
gain = envelope.First().Y;
120-
} else if (nextPoint >= envelope.Count) {
121-
gain = envelope.Last().Y;
122-
} else {
123-
var p0 = envelope[nextPoint - 1];
124-
var p1 = envelope[nextPoint];
125-
if (p0.X >= p1.X) {
126-
gain = p0.Y;
127-
} else {
128-
gain = p0.Y + (p1.Y - p0.Y) * (i - p0.X) / (p1.X - p0.X);
129-
}
130-
}
131-
data[i] *= gain;
132-
}
133-
}
134-
135-
private static IList<Vector2> EnvelopeMsToSamples(IList<Vector2> envelope, int skipOverSamples) {
136-
envelope = new List<Vector2>(envelope);
137-
double shift = -envelope[0].X;
138-
for (var i = 0; i < envelope.Count; i++) {
139-
var point = envelope[i];
140-
point.X = (float)((point.X + shift) * 44100 / 1000) + skipOverSamples;
141-
point.Y /= 100;
142-
envelope[i] = point;
143-
}
144-
return envelope;
145-
}
146-
147110
private float[] GetHeadWindow(float[] samples, IList<Vector2> envelope, out int windowStart) {
148111
var windowCenter = (envelope[0] + envelope[1]) * 0.5f;
149112
windowStart = Math.Max((int)windowCenter.X - 440, 0);

OpenUtau.Core/Classic/WorldlineRenderer.cs

+28
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using NAudio.Wave;
8+
using NumSharp;
89
using OpenUtau.Core;
910
using OpenUtau.Core.Format;
1011
using OpenUtau.Core.Render;
1112
using OpenUtau.Core.SignalChain;
1213
using OpenUtau.Core.Ustx;
14+
using static NetMQ.NetMQSelector;
1315

1416
namespace OpenUtau.Classic {
1517
public class WorldlineRenderer : IRenderer {
@@ -28,6 +30,7 @@ public class WorldlineRenderer : IRenderer {
2830
Ustx.BREC,
2931
Ustx.TENC,
3032
Ustx.VOIC,
33+
Ustx.DIR,
3134
};
3235

3336
public USingerType SingerType => USingerType.Classic;
@@ -83,6 +86,7 @@ public Task<RenderResult> Render(RenderPhrase phrase, Progress progress, int tra
8386
var voicing = SampleCurve(phrase, phrase.voicing, 1.0, frames, x => 0.01 * x);
8487
phraseSynth.SetCurves(f0, gender, tension, breathiness, voicing);
8588
result.samples = phraseSynth.Synth();
89+
AddDirects(phrase, resamplerItems, result);
8690
var source = new WaveSource(0, 0, 0, 1);
8791
source.SetSamples(result.samples);
8892
WaveFileWriter.CreateWaveFile16(wavPath, new ExportAdapter(source).ToMono(1, 0));
@@ -114,6 +118,30 @@ double[] SampleCurve(RenderPhrase phrase, float[] curve, double defaultValue, in
114118
return result;
115119
}
116120

121+
private static void AddDirects(RenderPhrase phrase, List<ResamplerItem> resamplerItems, RenderResult result) {
122+
foreach (var item in resamplerItems) {
123+
if (!item.phone.direct) {
124+
continue;
125+
}
126+
double posMs = item.phone.positionMs - item.phone.leadingMs - (phrase.positionMs - phrase.leadingMs);
127+
int startPhraseIndex = (int)(posMs / 1000 * 44100);
128+
using (var waveStream = Wave.OpenFile(item.phone.oto.File)) {
129+
if (waveStream == null) {
130+
continue;
131+
}
132+
float[] samples = Wave.GetSamples(waveStream!.ToSampleProvider().ToMono(1, 0));
133+
int offset = (int)(item.phone.oto.Offset / 1000 * 44100);
134+
int cutoff = (int)(item.phone.oto.Cutoff / 1000 * 44100);
135+
int length = cutoff >= 0 ? (samples.Length - offset - cutoff) : -cutoff;
136+
samples = samples.Skip(offset).Take(length).ToArray();
137+
item.ApplyEnvelope(samples);
138+
for (int i = 0; i < Math.Min(samples.Length, result.samples.Length - startPhraseIndex); ++i) {
139+
result.samples[startPhraseIndex + i] = samples[i];
140+
}
141+
}
142+
}
143+
}
144+
117145
public RenderPitchResult LoadRenderedPitch(RenderPhrase phrase) {
118146
return null;
119147
}

OpenUtau.Core/Format/USTx.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ public class Ustx {
2626
public const string LPF = "lpf";
2727
public const string MOD = "mod";
2828
public const string ALT = "alt";
29+
public const string DIR = "dir";
2930
public const string SHFT = "shft";
3031
public const string SHFC = "shfc";
3132
public const string TENC = "tenc";
3233
public const string VOIC = "voic";
3334

34-
public static readonly string[] required = { DYN, PITD, CLR, ENG, VEL, VOL, ATK, DEC, };
35+
public static readonly string[] required = { DYN, PITD, CLR, ENG, VEL, VOL, ATK, DEC };
3536

3637
public static void AddDefaultExpressions(UProject project) {
3738
project.RegisterExpression(new UExpressionDescriptor("dynamics (curve)", DYN, -240, 120, 0) { type = UExpressionType.Curve });
@@ -49,6 +50,7 @@ public static void AddDefaultExpressions(UProject project) {
4950
project.RegisterExpression(new UExpressionDescriptor("lowpass", LPF, 0, 100, 0, "H"));
5051
project.RegisterExpression(new UExpressionDescriptor("modulation", MOD, 0, 100, 0));
5152
project.RegisterExpression(new UExpressionDescriptor("alternate", ALT, 0, 16, 0));
53+
project.RegisterExpression(new UExpressionDescriptor("direct", DIR, false, new string[] { "off", "on" }));
5254
project.RegisterExpression(new UExpressionDescriptor("tone shift", SHFT, -36, 36, 0));
5355
project.RegisterExpression(new UExpressionDescriptor("tone shift (curve)", SHFC, -1200, 1200, 0) { type = UExpressionType.Curve });
5456
project.RegisterExpression(new UExpressionDescriptor("tension (curve)", TENC, -100, 100, 0) { type = UExpressionType.Curve });

OpenUtau.Core/Render/RenderPhrase.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public class RenderPhone {
6363
public readonly float volume;
6464
public readonly float velocity;
6565
public readonly float modulation;
66+
public readonly bool direct;
6667
public readonly Vector2[] envelope;
6768

6869
public readonly UOto oto;
@@ -109,12 +110,13 @@ internal RenderPhone(UProject project, UTrack track, UVoicePart part, UNote note
109110
flags = phoneme.GetResamplerFlags(project, track);
110111
string voiceColor = phoneme.GetVoiceColor(project, track);
111112
suffix = track.Singer.Subbanks.FirstOrDefault(
112-
subbank => subbank.Color == voiceColor)?.Suffix;
113+
subbank => subbank.Color == voiceColor)?.Suffix ?? string.Empty;
113114
volume = phoneme.GetExpression(project, track, Format.Ustx.VOL).Item1 * 0.01f;
114115
velocity = phoneme.GetExpression(project, track, Format.Ustx.VEL).Item1 * 0.01f;
115116
modulation = phoneme.GetExpression(project, track, Format.Ustx.MOD).Item1 * 0.01f;
116117
leadingMs = phoneme.preutter;
117118
envelope = phoneme.envelope.data.ToArray();
119+
direct = phoneme.GetExpression(project, track, Format.Ustx.DIR).Item1 == 1;
118120

119121
oto = phoneme.oto;
120122
hash = Hash();
@@ -134,10 +136,11 @@ private ulong Hash() {
134136
writer.Write(flag.Item2.Value);
135137
}
136138
}
137-
writer.Write(suffix ?? string.Empty);
139+
writer.Write(suffix);
138140
writer.Write(volume);
139141
writer.Write(velocity);
140142
writer.Write(modulation);
143+
writer.Write(direct);
141144
writer.Write(leadingMs);
142145
foreach (var point in envelope) {
143146
writer.Write(point.X);

OpenUtau.Core/Render/Worldline.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public SynthRequestWrapper(ResamplerItem item) {
206206
required_length = item.durRequired,
207207
consonant = item.consonant,
208208
cut_off = item.cutoff,
209-
volume = item.volume,
209+
volume = item.phone.direct ? 0 : item.volume,
210210
modulation = item.modulation,
211211
tempo = item.tempo,
212212
pitch_bend_length = item.pitches.Length,

0 commit comments

Comments
 (0)