29#include "../Analysis/OfflineTransientAnalyzer.h"
31#include "../Core/detail/OfflineGain.h"
32#include "../Core/detail/OfflinePunchEnvelope.h"
79 [[nodiscard]]
bool isValid() const noexcept
96 [[nodiscard]] std::span<const OfflineRegion>
exclusions() const noexcept
98 return isValid() ? exclusions_.
view() : std::span<const OfflineRegion>();
100 [[nodiscard]]
double gainAt(std::int64_t frame)
const noexcept
104 return gain(frame, curve_.
weightAt(frame));
109 [[nodiscard]]
double gain(std::int64_t frame,
double weight)
const noexcept
117 double inputPeak_ = 0, featherFrames_ = 1;
150 validateOptions(options);
151 if (!analysis.
isValid() || (options.boostDb > 0 && !analysis.
getOptions().attacks))
153 detail::OfflineSession job(jobOptions);
155 plan.spec_ = analysis.
energy().getSpec();
156 if (source.
getSpec() != plan.spec_)
158 plan.fingerprint_ = analysis.
energy().fingerprint();
159 plan.inputPeak_ = analysis.
energy().samplePeak();
160 plan.featherFrames_ = std::max(1.0, std::round(.005 * plan.spec_.sampleRate));
161 auto &report = plan.report_;
162 report.requestedBoostDb = options.boostDb;
163 report.inputSamplePeakDb =
164 gainToDecibels(plan.inputPeak_, -std::numeric_limits<double>::infinity());
166 plan.exclusions_.assign(job, options.exclusions, plan.spec_.frames);
167 const bool allExcluded = plan.exclusions_.covers(plan.spec_.frames);
168 const auto attacks = analysis.
attacks();
170 options.boostDb > 0 && plan.inputPeak_ > 0 && !attacks.empty() && !allExcluded;
171 plan.curve_.prepare(job, active ? attacks.size() : 0, plan.spec_.sampleRate);
179 report.transients = attacks.size();
180 report.effectiveBoostDb = options.boostDb;
182 for (std::size_t i = 0; i < attacks.size(); ++i)
186 plan.curve_.set(i, attacks[i].begin, attacks[i].end - attacks[i].begin);
188 detail::OfflineBlock<T> scratch(job, plan.spec_, jobOptions.
blockFrames);
189 detail::OfflinePunchEnvelope::Cursor weights(plan.curve_, job);
190 constexpr double maximum =
static_cast<double>(std::numeric_limits<T>::max());
192 source, plan.spec_, plan.fingerprint_, plan.inputPeak_, scratch, job,
193 [&](std::int64_t frame,
double magnitude) {
194 const double weight = weights(frame);
195 const double gain = plan.gain(frame, weight);
196 if (magnitude > maximum / gain)
199 plan.exclusions_.apply(frame, 2, plan.featherFrames_) - 1;
200 const double cap = 20 / std::log(10.) *
201 std::log1p((maximum / magnitude - 1) / mask) /
204 report.effectiveBoostDb =
205 std::max(0.0, cap - 64 * std::numeric_limits<double>::epsilon() *
207 report.reason = Reason::RepresentabilityLimited;
214 result.memoryBytes = job.bytes();
215 result.plan = std::move(plan);
230 validateOptions(options);
231 typename OfflineTransientAnalyzer<T>::Options features;
232 features.attacks = options.boostDb > 0;
233 features.pulses =
false;
234 auto analyzed = OfflineTransientAnalyzer<T>().analyze(source, features, job);
235 if (!analyzed.succeeded())
237 result.status = analyzed.status;
240 result = makePlan(source, analyzed.analysis, options,
242 result.memoryBytes = std::max(analyzed.memoryBytes,
243 result.memoryBytes + analyzed.analysis.retainedBytes());
255 auto result = detail::offlineRenderGain<Result>(
256 source, plan.spec_, plan.fingerprint_, plan.inputPeak_, plan.
isValid(),
260 [&plan, weights = detail::OfflinePunchEnvelope::Cursor(plan.curve_, session)](
261 std::int64_t frame) mutable { return plan.gain(frame, weights(frame)); };
262 }, {&plan.exclusions_, plan.featherFrames_,
false});
263 if (result.succeeded() && result.report.renderInfo.representabilityLimited)
267 1 + result.report.renderInfo.deltaScale *
273 template <
int MaxChannels>
278 return detail::offlineRun(*
this, input, output, sampleRate, options, job);
282 static void validateOptions(
const Options &options)
284 if (!std::isfinite(options.boostDb) || (options.boostDb < 0) || (options.boostDb > 12))
285 detail::offlineFail(OfflineStatus::InvalidInput);
Owning audio buffer with contiguous, 32-byte aligned storage.
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.
Immutable sparse weight plan bound to a complete source fingerprint.
std::size_t retainedBytes() const noexcept
std::span< const OfflineRegion > exclusions() const noexcept
Borrowed regions, valid while the plan remains alive and unmoved.
Report getReport() const noexcept
bool isValid() const noexcept
double gainAt(std::int64_t frame) const noexcept
Plan(Plan &&) noexcept=default
OfflineAudioSpec getSpec() const noexcept
Offline transient emphasis with a positive boost-dB control.
Result run(const AudioBuffer< T, MaxChannels > &input, AudioBuffer< T, MaxChannels > &output, double sampleRate, const Options &options={}, const OfflineJobOptions &job={}) const
Owning convenience render; aliases are safe and failures preserve output.
@ RepresentabilityLimited
PlanResult makePlan(OfflineAudioSource< T > &source, const typename OfflineTransientAnalyzer< T >::Analysis &analysis, const Options &options={}, const OfflineJobOptions &jobOptions={}) const
Reuses attack events, then verifies PCM and bounds representable boost.
Result render(OfflineAudioSource< T > &source, const Plan &plan, OfflineAudioSink< T > &sink, const OfflineJobOptions &job={}) const
Verifies the source, then measures provisional PCM before committing the sink.
PlanResult analyze(OfflineAudioSource< T > &source, const Options &options={}, const OfflineJobOptions &job={}) const
Complete-source analysis and planning; retains only sparse automation.
Read-only analysis with source provenance; no owned audio samples. Threading: energy() is an owner-th...
const OfflineEnergyAnalyzer< T >::Analysis & energy() const noexcept
Borrows the energy map as an owner-thread reference view. Valid while this analysis remains alive and...
std::span< const Event > attacks() const noexcept
Options getOptions() const noexcept
bool isValid() const noexcept
std::size_t retainedBytes() const noexcept
std::span< const OfflineRegion > view() const noexcept
double apply(std::int64_t frame, double gain, double featherFrames) const noexcept
bool isValid() const noexcept
double weightAt(std::int64_t frame) const noexcept
std::size_t retainedBytes() const noexcept
void offlineFail(OfflineStatus status)
void offlineScanMagnitude(OfflineAudioSource< T > &source, const OfflineAudioSpec &spec, const OfflineFingerprint &expectedFingerprint, double inputPeak, OfflineBlock< T > &scratch, const OfflineSession &job, Consume consume)
OfflineJobOptions offlineRemaining(const OfflineJobOptions &job, std::size_t used)
OfflineStatus offlineExceptionStatus() noexcept
Main namespace for the DSPark framework.
bool offlineSucceeded(OfflineStatus status) noexcept
True for successful processing, including an explicit identity result.
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
OfflineStatus
Explicit completion or no-publication outcome of an offline job.
T gainToDecibels(T gain, T minusInfinityDb=T(-100)) noexcept
Converts a linear gain value to decibels.
Immutable source format and host-provided content/timeline identity.
Noncryptographic PCM fingerprint, stable across block divisions.
Measured-render control adjustments; no clipping or master gain trim.
Resource and cooperative-cancellation controls for one worker operation.
std::span< const OfflineRegion > exclusions
Exact PCM inside these source regions.
double boostDb
Equal transient boost, 0..12 dB; zero is exact identity.
bool succeeded() const noexcept
double outputSamplePeakDb
OfflineGainRenderInfo renderInfo
bool succeeded() const noexcept