载入中...
搜索中...
未找到
DialogueVoice.cpp
浏览该文件的文档.
2
3#include "audio/Source.h"
4
5#include <algorithm>
6#include <cstdlib>
7#include <simplesquirrel/simplesquirrel.hpp>
8#include <sstream>
9
10namespace eve::dialogue {
11
13
15
16std::string DialogueVoice::key(const std::string& lineId, const std::string& locale) {
17 return lineId + "\n" + locale;
18}
19
20bool DialogueVoice::registerClip(const std::string& lineId, const std::string& locale,
21 float duration, const std::string& envelope) {
22 if (lineId.empty() || duration < 0.f) return false;
23 Clip& clip = clips_[key(lineId, locale)];
24 clip.lineId = lineId;
25 clip.locale = locale;
26 clip.duration = duration;
27 clip.envelope.clear();
28 std::istringstream input(envelope);
29 std::string item;
30 while (std::getline(input, item, ',')) {
31 char* end = nullptr;
32 const float value = std::strtof(item.c_str(), &end);
33 if (end == item.c_str()) return false;
34 clip.envelope.push_back(std::clamp(value, 0.f, 1.f));
35 }
36 return true;
37}
38
39bool DialogueVoice::bindSource(const std::string& lineId, const std::string& locale,
41 if (!source || lineId.empty()) return false;
42 Clip& clip = clips_[key(lineId, locale)];
43 clip.lineId = lineId;
44 clip.locale = locale;
45 clip.source = source;
46 clip.duration = static_cast<float>(source->getDuration());
47 return true;
48}
49
50DialogueVoice::Clip* DialogueVoice::resolve(const std::string& lineId,
51 const std::string& locale) {
52 const auto exact = clips_.find(key(lineId, locale));
53 if (exact != clips_.end()) return &exact->second;
54 const auto fallback = clips_.find(key(lineId, ""));
55 return fallback == clips_.end() ? nullptr : &fallback->second;
56}
57
58const DialogueVoice::Clip* DialogueVoice::resolve(const std::string& lineId,
59 const std::string& locale) const {
60 return const_cast<DialogueVoice*>(this)->resolve(lineId, locale);
61}
62
63bool DialogueVoice::hasClip(const std::string& lineId, const std::string& locale) const {
64 return resolve(lineId, locale) != nullptr;
65}
66
68 stop();
69 clips_.clear();
70}
71
72bool DialogueVoice::play(const std::string& lineId, const std::string& locale) {
73 stop();
74 current_ = resolve(lineId, locale);
75 if (!current_) return false;
76 currentLineId_ = lineId;
77 elapsed_ = 0.f;
78 simulatedPlaying_ = current_->source == nullptr && current_->duration > 0.f;
79 if (current_->source) current_->source->play();
80 return true;
81}
82
84 if (current_ && current_->source) current_->source->stop();
85 current_ = nullptr;
86 currentLineId_.clear();
87 elapsed_ = 0.f;
88 simulatedPlaying_ = false;
89}
90
91void DialogueVoice::update(float dt) {
92 if (!current_ || current_->source) return;
93 elapsed_ += std::max(dt, 0.f);
94 if (elapsed_ >= current_->duration) simulatedPlaying_ = false;
95}
96
98 return current_ && (current_->source ? current_->source->isPlaying() : simulatedPlaying_);
99}
100
102 return current_ && current_->source ? static_cast<float>(current_->source->tell()) : elapsed_;
103}
104
105float DialogueVoice::getDuration() const { return current_ ? current_->duration : 0.f; }
106
108 if (!current_ || current_->envelope.empty()) return 0.f;
109 const size_t index = std::min(static_cast<size_t>(getTime() * envelopeRate_),
110 current_->envelope.size() - 1);
111 return current_->envelope[index];
112}
113
115 return current_ && !isPlaying() && getTime() >= current_->duration;
116}
117
118void DialogueVoice::setEnvelopeRate(float samplesPerSecond) {
119 envelopeRate_ = samplesPerSecond > 0.f ? samplesPerSecond : 1.f;
120}
121
122void DialogueVoice::expose(ssq::Table& table) {
123 auto cls = table.addClass(name, DialogueVoice::create, false);
124 expose(cls);
125}
126
127void DialogueVoice::expose(ssq::Class& cls) {
128 cls.addFunc("getName", &DialogueVoice::getName);
129 cls.addFunc("registerClip", &DialogueVoice::registerClip);
130 cls.addFunc("bindSource", &DialogueVoice::bindSource);
131 cls.addFunc("hasClip", &DialogueVoice::hasClip);
132 cls.addFunc("clear", &DialogueVoice::clear);
133 cls.addFunc("play", &DialogueVoice::play);
134 cls.addFunc("stop", &DialogueVoice::stop);
135 cls.addFunc("update", &DialogueVoice::update);
136 cls.addFunc("isPlaying", &DialogueVoice::isPlaying);
137 cls.addFunc("getTime", &DialogueVoice::getTime);
138 cls.addFunc("getDuration", &DialogueVoice::getDuration);
139 cls.addFunc("getAmplitude", &DialogueVoice::getAmplitude);
140 cls.addFunc("shouldAutoAdvance", &DialogueVoice::shouldAutoAdvance);
141 cls.addFunc("getCurrentLineId", &DialogueVoice::getCurrentLineId);
142 cls.addFunc("setEnvelopeRate", &DialogueVoice::setEnvelopeRate);
143 cls.addFunc("getEnvelopeRate", &DialogueVoice::getEnvelopeRate);
144}
145
146} // namespace eve::dialogue
double value
float duration
HSQOBJECT cls
Definition ECS.cpp:21
EvpackChunkInput input
Definition Evpack.cpp:170
std::uint32_t key
glm::vec4 clip
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
uint32_t index
const UnitySourceAsset & source
virtual std::string getName() const =0
Returns the name.
A playable audio source (static buffer or streaming decoder). Not thread-safe for playback control ex...
Definition Source.h:30
Localized voice assets, duration-based advance, and amplitude lip sync.
float getTime() const
Returns the time.
~DialogueVoice() override
Dialogue voice.
float getEnvelopeRate() const
Returns the envelope rate.
void setEnvelopeRate(float samplesPerSecond)
Sets the envelope rate.
float getAmplitude() const
Returns the amplitude.
bool bindSource(const std::string &lineId, const std::string &locale, audio::Source *source)
Bind a playable Audio Source; the caller retains source ownership.
bool shouldAutoAdvance() const
Should auto advance.
void update(float dt)
Updates .
std::string getCurrentLineId() const
Returns the current line id.
bool hasClip(const std::string &lineId, const std::string &locale) const
True when clip.
bool isPlaying() const
True when playing.
bool play(const std::string &lineId, const std::string &locale)
Play.
bool registerClip(const std::string &lineId, const std::string &locale, float duration, const std::string &envelope)
Register clip metadata; envelope is comma-separated 0..1 amplitudes.
float getDuration() const
Returns the duration.