7#include <simplesquirrel/simplesquirrel.hpp>
16std::string DialogueVoice::key(
const std::string& lineId,
const std::string& locale) {
17 return lineId +
"\n" + locale;
21 float duration,
const std::string& envelope) {
22 if (lineId.empty() ||
duration < 0.f)
return false;
23 Clip&
clip = clips_[
key(lineId, locale)];
27 clip.envelope.clear();
28 std::istringstream
input(envelope);
30 while (std::getline(
input, item,
',')) {
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));
41 if (!
source || lineId.empty())
return false;
42 Clip&
clip = clips_[
key(lineId, locale)];
46 clip.duration =
static_cast<float>(
source->getDuration());
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;
58const DialogueVoice::Clip* DialogueVoice::resolve(
const std::string& lineId,
59 const std::string& locale)
const {
60 return const_cast<DialogueVoice*
>(
this)->resolve(lineId, locale);
64 return resolve(lineId, locale) !=
nullptr;
74 current_ = resolve(lineId, locale);
75 if (!current_)
return false;
76 currentLineId_ = lineId;
78 simulatedPlaying_ = current_->source ==
nullptr && current_->duration > 0.f;
79 if (current_->source) current_->source->play();
84 if (current_ && current_->source) current_->source->stop();
86 currentLineId_.clear();
88 simulatedPlaying_ =
false;
92 if (!current_ || current_->source)
return;
93 elapsed_ += std::max(dt, 0.f);
94 if (elapsed_ >= current_->duration) simulatedPlaying_ =
false;
98 return current_ && (current_->source ? current_->source->isPlaying() : simulatedPlaying_);
102 return current_ && current_->source ?
static_cast<float>(current_->source->tell()) : elapsed_;
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];
119 envelopeRate_ = samplesPerSecond > 0.f ? samplesPerSecond : 1.f;
122void DialogueVoice::expose(ssq::Table& table) {
123 auto cls = table.addClass(
name, DialogueVoice::create,
false);
127void DialogueVoice::expose(ssq::Class&
cls) {
#define Module_IMPL(ModuleName, newExpr)
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...
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.