10#include "../OfflineProcessing.h"
28 T target =
static_cast<T
>(inputPeak * gain);
30 target = std::numeric_limits<T>::denorm_min();
31 else if (
static_cast<double>(target) / inputPeak < gain)
32 target = std::nextafter(target, std::numeric_limits<T>::infinity());
33 return std::min(1.0,
static_cast<double>(target) / inputPeak);
44template <FloatType T,
class Consume>
52 for (std::int64_t first = 0; first < spec.
frames;)
55 static_cast<int>(std::min<std::int64_t>(scratch.
frames(), spec.
frames - first));
56 auto block = scratch.
view(frames);
58 for (
int f = 0; f < frames; ++f)
61 for (
int c = 0; c < spec.
channels; ++c)
63 const T x = block.getChannel(c)[f];
64 if (!std::isfinite(x))
67 magnitude = std::max(magnitude, std::abs(
static_cast<double>(x)));
69 summary.
peak = std::max(summary.
peak, magnitude);
70 consume(first + f, magnitude);
81template <FloatType T,
class Consume>
88 source, spec, scratch, job,
OfflinePhase::Plan, [&](std::int64_t frame,
double magnitude) {
89 if (magnitude > inputPeak)
91 consume(frame, magnitude);
93 if (summary.fingerprint != expectedFingerprint)
102 capacity_ = regions.size();
106 std::copy(regions.begin(), regions.end(), regions_.get());
107 for (
const auto r : regions)
108 if ((r.begin < 0) || (r.end <= r.begin) || (r.end > frames))
110 std::sort(regions_.get(), regions_.get() + capacity_,
112 for (std::size_t i = 0; i < capacity_; ++i)
114 const auto r = regions_[i];
115 if (count_ && r.begin <= regions_[count_ - 1].end)
116 regions_[count_ - 1].end = std::max(r.end, regions_[count_ - 1].end);
118 regions_[count_++] = r;
122 [[nodiscard]] std::span<const OfflineRegion>
view() const noexcept
124 return {regions_.get(), count_};
130 [[nodiscard]]
bool covers(std::int64_t frames)
const noexcept
132 return count_ == 1 && regions_[0].begin == 0 && regions_[0].end == frames;
134 [[nodiscard]]
double apply(std::int64_t frame,
double gain,
double featherFrames)
const noexcept
136 if (count_ == 0 || gain == 1)
138 std::size_t lo = 0, hi = count_;
141 const auto mid = lo + (hi - lo) / 2;
142 if (regions_[mid].end <= frame)
147 double right = 1, left = 1;
150 if (frame >= regions_[lo].begin)
152 right = std::clamp(
static_cast<double>(regions_[lo].begin - frame) / featherFrames, 0.0,
156 left = std::clamp(
static_cast<double>(frame - regions_[lo - 1].end) / featherFrames,
158 if (left == 1 && right == 1)
163 const auto smooth = [](
double t) {
164 const double u = std::min(t, 1 - t);
165 const double value = u * u * u * u * (35 - u * (84 - u * (70 - 20 * u)));
166 return t <= .5 ? value : 1 - value;
168 return 1 + (gain - 1) * smooth(left) * smooth(right);
172 std::size_t count_ = 0, capacity_ = 0;
173 std::unique_ptr<OfflineRegion[]> regions_;
193 catch (
const std::bad_alloc &)
205template <
class Processor, FloatType T,
int MaxChannels,
class Options>
206[[nodiscard]]
typename Processor::Result
offlineRun(
const Processor &processor,
209 double sampleRate,
const Options &options,
212 typename Processor::Result result;
216 auto prepared = processor.analyze(source, options, job);
217 if (!prepared.succeeded())
219 result.status = prepared.status;
222 auto outputSpec = source.
getSpec();
223 if constexpr (
requires { prepared.plan.getOutputSpec(); })
224 outputSpec = prepared.plan.getOutputSpec();
226 if (outputSpec.frames > std::numeric_limits<int>::max())
228 const auto sampleBytes =
230 if (sampleBytes > std::numeric_limits<std::size_t>::max() - 31)
233 (sampleBytes + 31) / 32, 32 *
static_cast<std::size_t
>(outputSpec.channels));
240 : destination_(destination)
251 std::memcpy(candidate_.getChannel(c) + first, block.
getChannel(c),
252 static_cast<std::size_t
>(block.
getNumSamples()) *
sizeof(T));
255 bool commit()
override
257 destination_ = std::move(candidate_);
260 void abort()
noexcept override
269 result = processor.render(source, prepared.plan, sink, renderJob);
270 result.memoryBytes = std::max(
271 prepared.memoryBytes, result.memoryBytes + prepared.plan.retainedBytes() + outputBytes);
Non-owning view over audio channel data.
int getNumSamples() const noexcept
Returns the number of samples per channel.
int getNumChannels() const noexcept
Returns the number of channels in this view.
T * getChannel(int ch) const noexcept
Returns a pointer to the sample data for the given channel.
Owning audio buffer with contiguous, 32-byte aligned storage.
AudioBufferView< T, MaxChannels > toView() noexcept
Returns a non-owning mutable view of this buffer. The view's channel capacity is propagated from MaxC...
Transactional worker sink for arbitrarily long offline output.
Rewindable, complete-file source with int64 positions and bounded blocks.
virtual OfflineAudioSpec getSpec() const noexcept=0
Returns format and provenance by value.
Borrowed planar-buffer adapter; does not allocate, copy or own input.
OfflineAudioSpec getSpec() const noexcept override
Returns format and provenance by value.
int frames() const noexcept
AudioBufferView< T > view(int count) noexcept
void assign(OfflineSession &job, std::span< const OfflineRegion > regions, std::int64_t frames)
std::size_t retainedBytes() const noexcept
bool covers(std::int64_t frames) const noexcept
std::span< const OfflineRegion > view() const noexcept
double apply(std::int64_t frame, double gain, double featherFrames) const noexcept
std::unique_ptr< U[]> allocate(std::uint64_t count)
void checkpoint(OfflinePhase phase, std::int64_t completed, std::int64_t total) const
void offlineFail(OfflineStatus status)
Processor::Result offlineRun(const Processor &processor, const AudioBuffer< T, MaxChannels > &input, AudioBuffer< T, MaxChannels > &output, double sampleRate, const Options &options, const OfflineJobOptions &job)
void offlineCallSink(Function &&function)
void offlineScanMagnitude(OfflineAudioSource< T > &source, const OfflineAudioSpec &spec, const OfflineFingerprint &expectedFingerprint, double inputPeak, OfflineBlock< T > &scratch, const OfflineSession &job, Consume consume)
void offlineRead(OfflineAudioSource< T > &source, const OfflineAudioSpec &expected, std::int64_t first, AudioBufferView< T > block)
double offlineRepresentablePeakGain(double inputPeak, double gain) noexcept
void offlineValidateSpec(const OfflineAudioSpec &spec, int maximumChannels=16)
void offlineHash(OfflineFingerprint &state, T sample) noexcept
OfflineSourceSummary offlineScanSource(OfflineAudioSource< T > &source, const OfflineAudioSpec &spec, OfflineBlock< T > &scratch, const OfflineSession &job, OfflinePhase phase, Consume consume)
std::size_t offlineBytes(std::uint64_t count, std::size_t size)
OfflineJobOptions offlineRemaining(const OfflineJobOptions &job, std::size_t used)
OfflineStatus offlineExceptionStatus() noexcept
OfflinePhase
Current kind of work; completed/total restart for each pass, and a phase may recur.
Immutable source format and host-provided content/timeline identity.
Noncryptographic PCM fingerprint, stable across block divisions.
Resource and cooperative-cancellation controls for one worker operation.
Source-frame interval [begin,end), relative to the start of the source.
~OfflineSinkTransaction()
OfflineAudioSink< T > * sink
OfflineFingerprint fingerprint