载入中...
搜索中...
未找到
EditorDialoguePreview.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <sstream>
6
7namespace eve::editor {
8namespace {
9
10std::vector<std::string> wrap(const std::string& text, int width) {
11 std::vector<std::string> lines;
12 std::istringstream stream(text);
13 std::string word, line;
14 while (stream >> word) {
15 if (line.empty()) line = word;
16 else if (static_cast<int>(line.size() + 1 + word.size()) <= width) line += " " + word;
17 else { lines.push_back(std::move(line)); line = word; }
18 while (static_cast<int>(line.size()) > width) {
19 lines.push_back(line.substr(0, static_cast<std::size_t>(width)));
20 line.erase(0, static_cast<std::size_t>(width));
21 }
22 }
23 if (!line.empty()) lines.push_back(std::move(line));
24 return lines;
25}
26
27} // namespace
28
30 const DialogueSubtitleRequest& request, const AudioWaveformResult* waveform) const {
32 result.lineId = request.lineId; result.sourceRevision = request.sourceRevision;
33 const double values[]{request.viewportWidth, request.viewportHeight, request.safeMarginX,
34 request.safeMarginY, request.minimumDuration,
35 request.charactersPerSecond, request.voiceDuration};
36 if (request.lineId.empty() || request.locale.empty() || request.text.empty() ||
37 request.maximumCharactersPerLine <= 0 || request.maximumLines <= 0 ||
38 std::any_of(std::begin(values), std::end(values), [](double value) { return !std::isfinite(value); }) ||
39 request.viewportWidth <= 0.0 || request.viewportHeight <= 0.0 ||
40 request.safeMarginX < 0.0 || request.safeMarginX >= 0.5 ||
41 request.safeMarginY < 0.0 || request.safeMarginY >= 0.5 ||
42 request.minimumDuration < 0.0 || request.charactersPerSecond <= 0.0 || request.voiceDuration < 0.0) {
43 result.status = EditorStatus::Rejected;
45 DiagnosticCode::InvalidArgument, RuleId("editor.dialogue.invalid-subtitle-request"),
46 DiagnosticSeverity::Error,
47 "Subtitle preview requires valid identity, text, viewport and timing constraints"));
48 return result;
49 }
50 result.safeX = request.viewportWidth * request.safeMarginX;
51 result.safeY = request.viewportHeight * request.safeMarginY;
52 result.safeWidth = request.viewportWidth - result.safeX * 2.0;
53 result.safeHeight = request.viewportHeight - result.safeY * 2.0;
54 result.lines = wrap(request.text, request.maximumCharactersPerLine);
55 result.estimatedReadDuration = std::max(request.minimumDuration,
56 static_cast<double>(request.text.size()) / request.charactersPerSecond);
57 if (static_cast<int>(result.lines.size()) > request.maximumLines)
59 DiagnosticCode::PreconditionViolation, RuleId("editor.dialogue.subtitle-line-overflow"),
60 DiagnosticSeverity::Error, "Subtitle exceeds the configured maximum line count"));
61 if (request.voiceDuration > 0.0 && request.voiceDuration + 0.25 < result.estimatedReadDuration)
63 DiagnosticCode::PreconditionViolation, RuleId("editor.dialogue.voice-too-short"),
64 DiagnosticSeverity::Warning,
65 "Localized voice is shorter than the estimated reading duration"));
66 if (waveform) {
67 if (waveform->status != EditorStatus::Applied || waveform->sourceRevision != request.sourceRevision) {
69 DiagnosticCode::PreconditionViolation, RuleId("editor.dialogue.stale-voice-waveform"),
70 DiagnosticSeverity::Warning,
71 "Voice waveform is unavailable or belongs to another source revision"));
72 } else if (!waveform->envelopes.empty()) {
73 const auto& channel = waveform->envelopes.front();
74 result.lipEnvelope.reserve(channel.size());
75 for (const AudioWaveformBucket& bucket : channel)
76 result.lipEnvelope.push_back({(bucket.startTime + bucket.endTime) * 0.5,
77 std::clamp(static_cast<double>(bucket.rms), 0.0, 1.0)});
78 }
79 }
80 const bool hasError = std::any_of(result.diagnostics.begin(), result.diagnostics.end(),
81 [](const EditorDiagnostic& diagnostic) {
82 return diagnostic.severity() == DiagnosticSeverity::Error;
83 });
84 result.status = hasError ? EditorStatus::Failed : EditorStatus::Applied;
85 return result;
86}
87
88} // namespace eve::editor
double value
std::map< std::string, Var > values
const GltfImportRequest & request
std::uint32_t width
std::string text
std::vector< std::string_view > lines
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
DialogueSubtitlePreview build(const DialogueSubtitleRequest &request, const AudioWaveformResult *waveform=nullptr) const
Build safe-area text layout and optionally derive lip amplitude from a waveform.
std::string word
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
eve::editing::RuleId RuleId
Definition EditorIds.h:32
One min/max/RMS envelope bucket for a single channel.
Revision-tagged waveform and loop-preview diagnostics.
std::vector< std::vector< AudioWaveformBucket > > envelopes
Safe-area subtitle layout and voice/readability diagnostics.
std::vector< DialogueLipEnvelopePoint > lipEnvelope
std::vector< EditorDiagnostic > diagnostics
Immutable subtitle preview request for one localized dialogue line.
uint32_t bucket