38 double releaseFrames,
bool localRelease =
false)
40 if (attackFrames < 1 || attackFrames > 1000000 || !std::isfinite(releaseFrames) ||
43 capacity_ = std::max(std::size_t(1), count);
45 releases_ = localRelease ? job.
allocate<
double>(capacity_) :
nullptr;
47 attack_ = attackFrames;
48 release_ = releaseFrames;
49 coefficient_ = std::exp(-1 / release_);
50 smoother_.
prepare(job, attack_, count_ != 0);
53 void set(std::size_t index, std::int64_t begin, std::int64_t end,
double gain,
54 double localReleaseFrames = 0)
56 if (index >= count_ || begin < 0 || end <= begin ||
57 end > std::numeric_limits<std::int64_t>::max() - attack_ || !std::isfinite(gain) ||
58 gain <= 0 || gain > 1 ||
59 (index && (begin < holds_[index - 1].begin || end < holds_[index - 1].end)) ||
60 (releases_ && (!std::isfinite(localReleaseFrames) || localReleaseFrames <= 0)))
63 releases_[index] = localReleaseFrames;
64 auto &hold = holds_[index];
65 hold = {begin, end, 1 - gain, 1 - gain};
66 maximumDeficit_ = std::max(maximumDeficit_, hold.deficit);
69 const auto &previous = holds_[index - 1];
71 std::max(hold.deficit, previous.releasePrefix * decay(end - previous.end, index - 1));
74 [[nodiscard]]
bool isValid() const noexcept
76 return holds_ !=
nullptr;
80 return isValid() ? capacity_ * (
sizeof(
Hold) + (releases_ ?
sizeof(
double) : 0)) +
87 std::int64_t attackFrames)
const
92 result.
prepare(job, count_, attackFrames, release_, releases_ !=
nullptr);
93 for (std::size_t i = 0; i < count_; ++i)
94 result.
set(i, holds_[i].begin, holds_[i].end, 1 - holds_[i].deficit,
95 releases_ ? releases_[i] : 0);
100 [[nodiscard]]
double gainAt(std::int64_t frame)
const noexcept
105 for (std::int64_t k = 0; k <= attack_; ++k)
106 sum += smoother_.
weights()[
static_cast<std::size_t
>(k)] * rawAt(frame + k);
113 : curve_(curve), smoother_(curve.smoother_, job)
118 if (!curve_.
isValid() || curve_.count_ == 0)
122 if (frame != next_ || (frame & 4095) == 0)
127 tailEnd_ = std::numeric_limits<std::size_t>::max();
128 for (std::int64_t k = 0; k < curve_.attack_; ++k)
131 const double deficit = advance(frame + curve_.attack_);
133 return curve_.gain(deficit);
137 double advance(std::int64_t frame)
noexcept
139 while (ended_ < curve_.count_ && curve_.holds_[ended_].end + curve_.attack_ <= frame)
141 if (ended_ != tailEnd_ || (frame & 4095) == 0)
145 ? curve_.holds_[ended_ - 1].releasePrefix *
146 curve_.decay(frame - curve_.holds_[ended_ - 1].end - curve_.attack_,
149 coefficient_ = curve_.releases_ && ended_
150 ? std::exp(-1 / curve_.releases_[ended_ - 1])
151 : curve_.coefficient_;
154 tail_ *= coefficient_;
156 const double raw = curve_.active(frame, ended_, tail_);
157 return smoother_.
advance(raw, frame);
159 const OfflineAttenuation &curve_;
160 OfflineBoxSmoother::Cursor smoother_;
161 std::int64_t next_ = -1;
162 std::size_t ended_ = 0, tailEnd_ = std::numeric_limits<std::size_t>::max();
163 double tail_ = 0, coefficient_ = 0;
167 [[nodiscard]]
double decay(std::int64_t frames, std::size_t hold)
const noexcept
169 const double time =
static_cast<double>(frames) / (releases_ ? releases_[hold] : release_);
170 return time >= 36 ? 0 : std::exp(-time);
172 [[nodiscard]]
double gain(
double deficit)
const noexcept
174 return deficit <= 2.32e-16 ? 1 : 1 - std::min(maximumDeficit_, deficit);
176 [[nodiscard]]
double active(std::int64_t frame, std::size_t ended,
double tail)
const noexcept
178 double deficit = tail;
179 for (
auto i = ended; i < count_ && holds_[i].begin <= frame; ++i)
180 deficit = std::max(deficit, holds_[i].deficit);
181 return deficit <= 2.32e-16 ? 0 : deficit;
183 [[nodiscard]]
double rawAt(std::int64_t frame)
const noexcept
185 std::size_t lo = 0, hi = count_;
188 const auto mid = lo + (hi - lo) / 2;
189 if (holds_[mid].end + attack_ <= frame)
196 lo ? holds_[lo - 1].releasePrefix * decay(frame - holds_[lo - 1].end - attack_, lo - 1)
199 std::unique_ptr<Hold[]> holds_;
200 std::unique_ptr<double[]> releases_;
201 std::size_t count_ = 0, capacity_ = 0;
202 std::int64_t attack_ = 1;
203 OfflineBoxSmoother smoother_;
204 double release_ = 1, coefficient_ = 0, maximumDeficit_ = 0;