25 if (!std::isfinite(rate) || rate < 8000 || rate > 384000)
28 capacity_ = std::max(std::size_t(1), count);
29 events_ = job.
allocate<Event>(capacity_);
30 const auto frames = [rate](
double seconds) {
31 return static_cast<int>(std::llround(seconds * rate));
34 rise_ = frames(.005) - span_;
35 fall_ = frames(.045) - span_;
36 minimumHold_ = frames(.015);
37 maximumHold_ = frames(.050);
38 life_ = maximumHold_ + span_ + fall_;
39 smoother_.
prepare(job, span_, count != 0);
42 riseWindow_ = job.
allocate<
double>(
static_cast<std::uint64_t
>(2 * rise_ + 1));
43 fallWindow_ = job.
allocate<
double>(
static_cast<std::uint64_t
>(2 * fall_ + 1));
48 void set(std::size_t index, std::int64_t onset, std::int64_t duration)
50 if (index >= count_ || onset < 0 || duration <= 0 ||
51 onset > std::numeric_limits<std::int64_t>::max() - life_ - span_ ||
52 (index && onset <= events_[index - 1].onset))
55 onset,
static_cast<int>(std::clamp(duration,
static_cast<std::int64_t
>(minimumHold_),
56 static_cast<std::int64_t
>(maximumHold_)))};
58 [[nodiscard]]
bool isValid() const noexcept
60 return events_ !=
nullptr;
67 ?
static_cast<std::size_t
>(2 * (rise_ + fall_) + 2) *
sizeof(double)
71 [[nodiscard]]
double weightAt(std::int64_t frame)
const noexcept
76 for (
int k = 0; k <= span_; ++k)
77 weight += smoother_.
weights()[k] * rawAt(frame + k, firstAt(frame + k));
78 return std::clamp(weight, 0.0, 1.0);
84 : curve_(curve), smoother_(curve.smoother_, job)
87 [[nodiscard]]
double operator()(std::int64_t frame)
noexcept
89 if (!curve_.
isValid() || !curve_.count_)
92 if (frame != next_ || (frame & 4095) == 0)
95 first_ = curve_.firstAt(frame);
96 for (
int k = 0; k < curve_.span_; ++k)
97 (
void)advance(frame + k);
99 const double result = advance(frame + curve_.span_);
101 return std::clamp(result, 0.0, 1.0);
105 double advance(std::int64_t frame)
noexcept
107 while (first_ < curve_.count_ && curve_.events_[first_].onset < frame - curve_.life_)
109 return smoother_.
advance(curve_.rawAt(frame, first_), frame);
113 std::int64_t next_ = -1;
114 std::size_t first_ = 0;
120 std::int64_t onset = 0;
123 [[nodiscard]] std::size_t firstAt(std::int64_t frame)
const noexcept
125 std::size_t lo = 0, hi = count_;
128 const auto mid = lo + (hi - lo) / 2;
129 if (events_[mid].onset < frame - life_)
136 [[nodiscard]]
double rawAt(std::int64_t frame, std::size_t first)
const noexcept
139 for (
auto i = first; i < count_; ++i)
141 const auto age = frame - events_[i].onset;
145 weight = std::max(weight, riseWindow_[
static_cast<std::size_t
>(age + rise_)]);
146 else if (age <= events_[i].hold + span_)
148 else if (
const auto elapsed = age - events_[i].hold - span_; elapsed < fall_)
149 weight = std::max(weight, fallWindow_[
static_cast<std::size_t
>(fall_ + elapsed)]);
153 std::unique_ptr<Event[]> events_;
154 std::unique_ptr<double[]> riseWindow_, fallWindow_;
155 OfflineBoxSmoother smoother_;
156 std::size_t count_ = 0, capacity_ = 0;
157 int span_ = 1, rise_ = 1, fall_ = 1, minimumHold_ = 1, maximumHold_ = 1, life_ = 3;