Repository navigation
Expand file tree
/
Copy pathExternalRendererPlugin.cs
More file actions
169 lines (149 loc) · 6.91 KB
/
Copy pathExternalRendererPlugin.cs
File metadata and controls
169 lines (149 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using OpenUtau.Classic;
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
using Serilog;
namespace OpenUtau.Core.Render {
public interface IExternalRendererIdentity {
string Id { get; }
string Name { get; }
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ExternalRendererAttribute : Attribute {
public string Id { get; }
public string Name { get; }
public USingerType SingerType { get; }
public ExternalRendererAttribute(string id, string name, USingerType singerType = USingerType.Classic) {
Id = id;
Name = name;
SingerType = singerType;
}
}
public sealed class RendererPluginMetadata {
public RendererCapabilitiesManifest Capabilities { get; init; } = new RendererCapabilitiesManifest();
public IReadOnlyDictionary<string, AnalysisFormatManifest> AnalysisFormats { get; init; }
= new Dictionary<string, AnalysisFormatManifest>();
public IReadOnlyDictionary<string, UExpressionDescriptor> Expressions { get; init; }
= new Dictionary<string, UExpressionDescriptor>();
}
/// <summary>
/// Versioned entry point implemented by external renderer bridge assemblies.
/// </summary>
public interface IOpenUtauRendererPlugin {
int ApiVersion { get; }
RendererPluginMetadata Metadata => new RendererPluginMetadata();
IRenderer CreateRenderer(RendererPluginContext context);
IRendererAnalysisProvider? CreateAnalysisProvider(RendererPluginContext context) => null;
}
public enum RendererAnalysisState {
Valid,
Missing,
Stale,
Invalid,
}
public sealed record RendererAnalysisRequest(
string Format, string SourceFile, string OutputFile, bool Overwrite);
public enum RendererAnalysisOutcome {
Generated,
AlreadyValid,
Failed,
}
public sealed record RendererAnalysisResult(
RendererAnalysisRequest Request,
RendererAnalysisOutcome Outcome,
string Message = null);
/// <summary>Owns engine-specific validation and generation of reusable source
/// analysis. The host handles paths, fallback timestamp checks and orchestration.</summary>
public interface IRendererAnalysisProvider {
Task<IReadOnlyList<RendererAnalysisResult>> GenerateAsync(
IReadOnlyList<RendererAnalysisRequest> requests,
IProgress<int> progress,
CancellationToken cancellation);
ValueTask<RendererAnalysisState> ValidateAsync(
RendererAnalysisRequest request,
CancellationToken cancellation);
}
public sealed class RendererPluginContext {
public int ApiVersion => ExternalRendererRegistry.ApiVersion;
public Version HostVersion => typeof(IRenderer).Assembly.GetName().Version ?? new Version();
public string RendererId { get; }
public string RendererName { get; }
public string PluginDirectory { get; }
public string ManifestPath { get; }
public string CacheDirectory => PathManager.Inst.CachePath;
public ILogger Logger { get; }
public ResamplerManifest Manifest { get; }
public RendererPluginMetadata Metadata { get; }
public RendererAnalysisService Analysis { get; }
public RendererCacheService Cache { get; }
public RendererPluginContext(
string rendererId,
string rendererName,
string pluginDirectory,
string manifestPath,
ResamplerManifest manifest,
RendererPluginMetadata metadata = null,
ILogger logger = null) {
RendererId = rendererId;
RendererName = rendererName;
PluginDirectory = pluginDirectory;
ManifestPath = manifestPath;
Manifest = manifest;
Metadata = metadata ?? new RendererPluginMetadata();
Logger = logger ?? Log.Logger;
Analysis = new RendererAnalysisService(Metadata.AnalysisFormats);
Cache = new RendererCacheService(rendererId);
}
}
/// <summary>Resolves renderer-declared source analysis files without coupling
/// plugins to OpenUtau's render-output cache.</summary>
public sealed class RendererAnalysisService {
readonly IReadOnlyDictionary<string, AnalysisFormatManifest> formats;
internal RendererAnalysisService(IReadOnlyDictionary<string, AnalysisFormatManifest> formats) {
this.formats = formats;
}
public IReadOnlyDictionary<string, AnalysisFormatManifest> Formats => formats;
public string GetPath(string format, string sourceFile) {
if (!formats.TryGetValue(format, out var descriptor)) {
throw new KeyNotFoundException($"Unknown renderer analysis format '{format}'.");
}
var fullSource = Path.GetFullPath(sourceFile);
var directory = Path.GetDirectoryName(fullSource) ?? string.Empty;
var stem = Path.GetFileNameWithoutExtension(fullSource);
return descriptor.path
.Replace("{wav_dir}", directory, StringComparison.Ordinal)
.Replace("{wav_stem}", stem, StringComparison.Ordinal)
.Replace("{wav_name}", Path.GetFileName(fullSource), StringComparison.Ordinal);
}
public RendererAnalysisState GetBasicState(string format, string sourceFile) {
var outputFile = GetPath(format, sourceFile);
if (!File.Exists(outputFile)) return RendererAnalysisState.Missing;
if (!File.Exists(sourceFile)) return RendererAnalysisState.Invalid;
return File.GetLastWriteTimeUtc(outputFile) < File.GetLastWriteTimeUtc(sourceFile)
? RendererAnalysisState.Stale
: RendererAnalysisState.Valid;
}
}
/// <summary>Provides namespaced final-output cache paths. Intermediate engine
/// state belongs in memory; reusable source analysis belongs beside the source.</summary>
public sealed class RendererCacheService {
readonly string rendererKey;
internal RendererCacheService(string rendererId) {
rendererKey = string.Concat(rendererId.Select(character =>
char.IsLetterOrDigit(character) || character is '-' or '_' ? character : '-'));
}
public string GetPhrasePath(RenderPhrase phrase, string extension = ".wav") {
if (string.IsNullOrEmpty(extension)) extension = ".wav";
if (!extension.StartsWith('.')) extension = "." + extension;
var path = Path.Combine(PathManager.Inst.CachePath,
$"renderer-{rendererKey}-{phrase.hash:x16}{extension}");
phrase.AddCacheFile(path);
return path;
}
}
}