载入中...
搜索中...
未找到
ActionAudioBlock.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <set>
5#include <string_view>
6#include <utility>
7
8namespace eve::action {
9namespace {
10
11Result<double> numeric(const Value::Object& payload, std::string_view key, double fallback) {
12 const auto found = payload.find(std::string(key));
13 if (found == payload.end()) return Result<double>::success(fallback);
14 double value = 0.0;
15 if (const auto* integer = found->second.getIf<std::int64_t>())
16 value = static_cast<double>(*integer);
17 else if (const auto* decimal = found->second.getIf<double>())
18 value = *decimal;
19 else
21 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio field must be numeric", std::string(key)));
22 if (!std::isfinite(value))
24 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio field must be finite", std::string(key)));
26}
27
28} // namespace
29
31 ActionAudioBinding candidate;
32 const auto uri = payload.find("uri");
33 if (uri == payload.end() || !uri->second.getIf<std::string>() || uri->second.getIf<std::string>()->empty())
35 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio URI must be non-empty text", "uri"));
36 candidate.uri = *uri->second.getIf<std::string>();
37
38 if (const auto randomUris = payload.find("randomUris"); randomUris != payload.end()) {
39 const auto* values = randomUris->second.getIf<Value::Array>();
40 if (!values)
42 Diagnostic::error(DiagnosticCode::InvalidArgument, "random audio URIs must be an array", "randomUris"));
43 std::set<std::string> unique;
44 for (std::size_t index = 0; index < values->size(); ++index) {
45 const auto* value = (*values)[index].getIf<std::string>();
46 const auto path = "randomUris[" + std::to_string(index) + "]";
47 if (!value || value->empty())
49 DiagnosticCode::InvalidArgument, "random audio URI must be non-empty text", path));
50 if (!unique.insert(*value).second)
52 Diagnostic::error(DiagnosticCode::InvalidArgument, "random audio URI must be unique", path));
53 candidate.randomUris.push_back(*value);
54 }
55 }
56
59 candidate.spatial = std::move(spatial).takeValue();
60
61 auto volume = numeric(payload, "volume", 1.0);
63 if (volume.value() < 0.0)
65 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio volume must be non-negative", "volume"));
66 candidate.volume = volume.value();
67
68 auto pitch = numeric(payload, "pitch", 1.0);
69 if (!pitch) return Result<ActionAudioBinding>::failure(pitch.status());
70 if (pitch.value() <= 0.0)
72 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio pitch must be positive", "pitch"));
73 candidate.pitch = pitch.value();
74
75 auto randomPitchOffset = numeric(payload, "randomPitchOffset", 0.0);
77 if (randomPitchOffset.value() < 0.0 || randomPitchOffset.value() > 0.5)
79 DiagnosticCode::InvalidArgument, "random pitch offset must be between zero and 0.5", "randomPitchOffset"));
80 candidate.randomPitchOffset = randomPitchOffset.value();
81
82 auto spatialBlend = numeric(payload, "spatialBlend", 1.0);
84 if (spatialBlend.value() < 0.0 || spatialBlend.value() > 1.0)
86 DiagnosticCode::InvalidArgument, "audio spatial blend must be between zero and one", "spatialBlend"));
87 candidate.spatialBlend = spatialBlend.value();
88
89 auto minDistance = numeric(payload, "minDistance", 1.0);
91 if (minDistance.value() <= 0.0)
93 DiagnosticCode::InvalidArgument, "audio minimum distance must be positive", "minDistance"));
94 candidate.minDistance = minDistance.value();
95
96 auto maxDistance = numeric(payload, "maxDistance", 35.0);
98 if (maxDistance.value() < candidate.minDistance)
101 "audio maximum distance must not be below minimum distance", "maxDistance"));
102 candidate.maxDistance = maxDistance.value();
103
104 if (const auto looping = payload.find("looping"); looping != payload.end()) {
105 const auto* enabled = looping->second.getIf<bool>();
106 if (!enabled)
108 Diagnostic::error(DiagnosticCode::InvalidArgument, "audio looping must be boolean", "looping"));
109 candidate.looping = *enabled;
110 }
111 if (shape == ActionAudioShape::Instant && candidate.looping)
113 DiagnosticCode::InvalidArgument, "instant audio cannot loop; use audio-state", "looping"));
114 if (const auto fadeOut = payload.find("fadeOutOnExit"); fadeOut != payload.end()) {
115 const auto* enabled = fadeOut->second.getIf<bool>();
116 if (!enabled)
118 DiagnosticCode::InvalidArgument, "audio fade-out flag must be boolean", "fadeOutOnExit"));
119 candidate.fadeOutOnExit = *enabled;
120 }
121 auto fadeOutDuration = numeric(payload, "fadeOutDuration", 0.1);
123 if (fadeOutDuration.value() <= 0.0 || fadeOutDuration.value() > 10.0)
125 DiagnosticCode::InvalidArgument, "audio fade-out duration must be in (0, 10] seconds", "fadeOutDuration"));
126 candidate.fadeOutDuration = fadeOutDuration.value();
127 return Result<ActionAudioBinding>::success(std::move(candidate));
128}
129
130} // namespace eve::action
Typed audio action-block payload contract.
double value
Value::Object payload
std::map< std::string, Var > values
std::uint32_t key
ShaderImageInput shape
std::string path
Definition PlayHost.cpp:110
bool found
uint32_t index
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
ActionAudioShape
Authored audio block shape used to validate looping semantics.
bool enabled
Owning validated settings shared by Audio runtime and editor preview.
static Result< ActionAudioBinding > fromPayload(const Value::Object &payload, ActionAudioShape shape)
Decode an audio payload transactionally.
std::vector< std::string > randomUris
static Result< ActionSpatialBinding > fromPayload(const Value::Object &payload)
Decode and validate the common spatial fields in an owning payload.