108 static constexpr double slope = clipperSmallSignalSlope<C, double>();
111 std::int64_t frames_, pad_, extent_;
112 int factor_, block_, highBlock_, delay_ = 0, left_ = 0, filterSize_ = 0;
120 void operator()(std::int64_t first,
int count,
double *out)
122 owner->readPadded(first, count, out, difference);
124 } raw_{
this,
false}, difference_{
this,
true};
125 std::optional<OfflineHilbertMap> rawMap_, differenceMap_;
126 std::array<std::optional<SincPhase>, 16> phases_;
128 std::array<std::int64_t, 3> nativeLeaf_{-1, -1, -1};
129 std::array<std::int64_t, 3> upLeaf_{-1, -1, -1}, shapedLeaf_{-1, -1, -1};
131 std::uint64_t evaluations_ = 0;
132 struct ProjectedReader
135 void operator()(std::int64_t first,
int count,
double *out)
137 owner->projectedSource(first, count, out);
140 std::optional<SincProjection<ProjectedReader>> projection_;
142 static std::int64_t checkedExtent(std::int64_t frames, std::int64_t pad,
int factor,
int block)
144 if (frames < 1 || pad < 64 || factor < 1 || factor > 16 || (factor & (factor - 1)) ||
145 block < 128 || block > 16384 || block * factor > 65536 || (block & (block - 1)))
147 constexpr auto maximum = std::numeric_limits<std::int64_t>::max();
148 if (pad > (maximum / factor - frames) / 2 || frames > maximum / factor)
150 return frames + 2 * pad;
152 void readPadded(std::int64_t first,
int count,
double *out,
bool difference)
156 const auto leaf = first / block_;
157 const int offset =
static_cast<int>(first % block_),
158 length = std::min(count, block_ - offset);
159 const auto slot =
static_cast<std::size_t
>(leaf % 3);
160 auto *data = native_.get() + slot * 2 * block_;
161 if (nativeLeaf_[slot] != leaf)
163 std::fill_n(data, 2 * block_, 0.);
164 const auto origin = leaf * block_;
165 const auto begin = std::max(origin, pad_),
166 end = std::min(origin + block_, pad_ + frames_);
169 const int at =
static_cast<int>(begin - origin),
170 n =
static_cast<int>(end - begin);
171 for (
int done = 0; done < n;)
173 const int chunk = std::min(n - done, 4096);
174 source_(begin - pad_ + done, chunk, data + at + done);
177 for (
int i = at; i < at + n; ++i)
179 if (!std::isfinite(data[i]))
181 const double shaped = clipperShape<C>(data[i], ceiling_);
186 nativeLeaf_[slot] = leaf;
188 std::copy_n(data + (difference ? block_ : 0) + offset, length, out);
194 double *upsampled(std::int64_t leaf)
196 const auto slot =
static_cast<std::size_t
>(leaf % 3);
197 auto *data = up_.get() + slot * 2 * highBlock_;
198 if (upLeaf_[slot] == leaf)
200 for (
int p = 0; p < factor_; ++p)
202 phases_[p]->evaluate(*rawMap_,
static_cast<std::size_t
>(leaf), raw_, phaseWork_.get());
203 for (
int i = 0; i < block_; ++i)
204 data[i * factor_ + p] = phaseWork_[i];
205 phases_[p]->evaluate(*differenceMap_,
static_cast<std::size_t
>(leaf), difference_,
207 for (
int i = 0; i < block_; ++i)
208 data[highBlock_ + i * factor_ + p] = phaseWork_[i];
210 upLeaf_[slot] = leaf;
213 void highInput(std::int64_t first,
int count,
double *a,
double *b)
215 const auto highExtent = extent_ * factor_;
220 const int n =
static_cast<int>(std::min<std::int64_t>(-first, count));
221 std::fill_n(a, n, 0.);
222 std::fill_n(b, n, 0.);
229 if (first >= highExtent)
231 std::fill_n(a, count, 0.);
232 std::fill_n(b, count, 0.);
235 const auto leaf = first / highBlock_;
236 const int offset =
static_cast<int>(first % highBlock_);
237 const int n =
static_cast<int>(
238 std::min<std::int64_t>(std::min(highBlock_ - offset, count), highExtent - first));
239 const auto *data = upsampled(leaf);
240 std::copy_n(data + offset, n, a);
241 std::copy_n(data + highBlock_ + offset, n, b);
248 const double *stage(std::int64_t leaf)
250 const auto slot =
static_cast<std::size_t
>(leaf % 3);
251 auto *data = shaped_.get() + slot * 2 * highBlock_;
252 if (shapedLeaf_[slot] == leaf)
254 const auto first = leaf * highBlock_;
255 const int length = highBlock_ + filterSize_ - 1;
256 highInput(first - left_, length, scratchA_.get(), scratchB_.get());
257 stage_.
reset(ceiling_);
258 stage_.
process(scratchA_.get(), scratchB_.get(), length, exterior_);
259 std::copy_n(scratchA_.get() + filterSize_ - 1, highBlock_, data);
260 std::copy_n(scratchB_.get() + filterSize_ - 1, highBlock_, data + highBlock_);
261 shapedLeaf_[slot] = leaf;
265 void projectedSource(std::int64_t first,
int count,
double *out)
269 const auto leaf = first / highBlock_;
270 const int offset =
static_cast<int>(first % highBlock_);
271 const int n = std::min(highBlock_ - offset, count);
272 const auto *data = stage(leaf);
273 for (
int i = 0; i < n; ++i)
274 out[i] = data[offset + i] +
284 int factor,
double ceiling,
Exterior exterior,
int block = 512)
285 : job_(job), source_(std::move(reader)), frames_(frames), pad_(pad),
286 extent_(checkedExtent(frames, pad, factor, block)), factor_(factor), block_(block),
287 highBlock_(block * factor), ceiling_(ceiling), exterior_(exterior),
291 if (!(ceiling > 0) || !std::isfinite(ceiling))
293 native_ = job.
allocateScratch<
double>(6 *
static_cast<std::uint64_t
>(block_));
294 rawMap_.emplace(work_, raw_);
295 differenceMap_.emplace(work_, difference_);
296 for (
int p = 0; p < factor; ++p)
297 phases_[p].emplace(work_,
double(p) / factor);
299 delay_ = stage_.
delay();
300 left_ = filterSize_ - 1 - delay_;
301 up_ = job.
allocateScratch<
double>(6 *
static_cast<std::uint64_t
>(highBlock_));
302 shaped_ = job.
allocateScratch<
double>(6 *
static_cast<std::uint64_t
>(highBlock_));
303 scratchA_ = job.
allocateScratch<
double>(
static_cast<std::uint64_t
>(highBlock_) + filterSize_ - 1);
304 scratchB_ = job.
allocateScratch<
double>(
static_cast<std::uint64_t
>(highBlock_) + filterSize_ - 1);
310 [[nodiscard]]
bool hasGeometry(std::int64_t pad,
int block)
const noexcept
312 return pad_ == pad && block_ == block;
316 if (!(ceiling > 0) || !std::isfinite(ceiling))
318 if (ceiling == ceiling_ && exterior == exterior_)
321 exterior_ = exterior;
322 nativeLeaf_.fill(-1);
324 shapedLeaf_.fill(-1);
325 differenceMap_->rebuild(work_, difference_);
327 projection_->rebuild();
336 projection_.emplace(job_, extent_ * factor_, factor_, ProjectedReader{
this},
341 return pad_ / block_;
345 return (pad_ + frames_ - 1) / block_ + 1;
347 [[nodiscard]] std::int64_t
leafOrigin(std::int64_t leaf)
const
349 return std::max(leaf * block_, pad_) - pad_;
351 [[nodiscard]] std::span<const double>
get(std::int64_t leaf)
356 const auto values = projection_->get(
static_cast<std::size_t
>(leaf));
357 const auto first = leaf * block_, begin = std::max(first, pad_),
358 end = std::min(first +
static_cast<int>(values.size()), pad_ + frames_);
359 const auto *filtered = stage(leaf), *raw = upsampled(leaf);
360 const int offset =
static_cast<int>(begin - first), length =
static_cast<int>(end - begin);
361 for (
int i = 0; i < length; ++i)
363 const int at = offset + i;
364 output_[i] = clipperShape<C>(raw[at * factor_], ceiling_) + values[at] +
366 if (!std::isfinite(output_[i]))
369 return {output_.get(),
static_cast<std::size_t
>(length)};
371 template <
class Consumer>
void render(Consumer consume)
376 const auto data =
get(leaf);
377 consume(
leafOrigin(leaf),
static_cast<int>(data.size()), data.data());