-
Notifications
You must be signed in to change notification settings - Fork 551
Add external renderer plugin API #2340
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
Open
Astel123457
wants to merge
17
commits into
openutau:master
Choose a base branch
from
Astel123457:external-renderer-api
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
27a6178
Basic external renderer API
Astel123457 8f95966
move examples to seperate repo
Astel123457 a002afb
Merge branch 'master' of https://github.com/openutau/OpenUtau
Astel123457 437d785
fix possible issue where external renderers aren't set as default
Astel123457 dc51fc1
Fix external renderer metadata inspection build
Astel123457 1667bef
Avoid locking renderer bridge assemblies on Windows
Astel123457 8f3f9ed
Retry CI after xUnit runner failure
Astel123457 0fc8426
Merge branch 'master' into external-renderer-api
Astel123457 c833f91
Support renderer phrase events
Astel123457 abbb3cf
Merge branch 'master' into external-renderer-api
Astel123457 c0f4105
add track settings dialog
Astel123457 d14678a
Merge branch 'external-renderer-api' of https://github.com/Astel12345…
Astel123457 f6c2e7a
fix issue with a missing curly brace
Astel123457 9e5e98a
Merge remote-tracking branch 'upstream/master' into external-renderer…
Astel123457 ac23fe3
Remove trailing whitespace after upstream merge
Astel123457 9a36835
Rerun CI after flaky phonemizer tests
Astel123457 b1cd3ff
Merge branch 'openutau:master' into external-renderer-api
Astel123457 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,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); | ||
|
Check warning on line 68 in OpenUtau.Core/Render/ExternalRendererPlugin.cs
|
||
|
|
||
| /// <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, | ||
|
Check warning on line 102 in OpenUtau.Core/Render/ExternalRendererPlugin.cs
|
||
| 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; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.