25#if !defined(DSPARK_NO_OFFLINE) && (defined(__cpp_exceptions) || defined(_CPPUNWIND))
26#define DSPARK_HAS_OFFLINE 1
28#define DSPARK_HAS_OFFLINE 0
33#include "AudioBuffer.h"
46#include <memory_resource>
90 [[nodiscard]]
bool isValid() const noexcept
93 sampleRate <= static_cast<double>(std::numeric_limits<int>::max()) &&
channels > 0 &&
95 frames <= std::numeric_limits<std::int64_t>::max() -
timelineOrigin;
138 const std::atomic<bool> *
cancel =
nullptr;
183template <
FloatType T,
int MaxChannels = 16>
189 std::uint64_t sourceId = 0, std::uint64_t revision = 0,
190 std::int64_t timelineOrigin = 0) noexcept
192 revision, timelineOrigin}
203 if (first < 0 || count < 0 || count > spec_.frames || first > spec_.frames - count ||
206 for (
int ch = 0; ch < spec_.channels; ++ch)
210 if (!audio_.getChannel(ch) || !destination.
getChannel(ch))
212 std::memmove(destination.
getChannel(ch), audio_.getChannel(ch) + first,
213 static_cast<std::size_t
>(count) *
sizeof(T));
249 std::uint64_t first = 14695981039346656037ull;
250 std::uint64_t second = 0x9e3779b97f4a7c15ull;
279 catch (
const std::bad_alloc &)
281 return OfflineStatus::AllocationFailure;
285 return OfflineStatus::NumericalFailure;
289[[nodiscard]]
inline std::size_t
offlineBytes(std::uint64_t count, std::size_t size)
291 if (count > std::numeric_limits<std::size_t>::max() / size)
293 return static_cast<std::size_t
>(count) * size;
298 std::pmr::memory_resource *resource =
nullptr;
299 std::size_t count = 0;
304 std::destroy_n(pointer, count);
305 resource->deallocate(pointer, count *
sizeof(T),
alignof(T));
324 if (bytes > options_.memoryBudgetBytes - bytes_)
328 template <
typename U> [[nodiscard]] std::unique_ptr<U[]>
allocate(std::uint64_t count)
331 return std::make_unique<U[]>(
static_cast<std::size_t
>(count));
337 scratch_ = &resource;
342 return {allocate<U>(count).release(), {}};
344 auto *data =
static_cast<U *
>(scratch_->allocate(bytes,
alignof(U)));
347 std::uninitialized_value_construct_n(data,
static_cast<std::size_t
>(count));
351 scratch_->deallocate(data, bytes,
alignof(U));
354 return {data, {scratch_,
static_cast<std::size_t
>(count)}};
356 [[nodiscard]] std::size_t
bytes() const noexcept
362 if (options_.cancel && options_.cancel->load(std::memory_order_relaxed))
364 if (options_.progress)
366 bool proceed =
false;
369 proceed = options_.progress(options_.progressContext, {phase, completed, total});
382 std::size_t bytes_ = 0;
383 std::pmr::memory_resource *scratch_ =
nullptr;
394 : job_(job), roundCapacity_(roundCapacity) {}
401 auto *block = blocks_;
402 blocks_ = block->next;
403 if (block->alignment >
alignof(std::max_align_t))
404 ::operator
delete(block, std::align_val_t{block->alignment});
406 ::operator
delete(block);
410 struct alignas(std::max_align_t) Block
414 std::size_t capacity, alignment;
417 void *do_allocate(std::size_t bytes, std::size_t alignment)
override
419 Block *best =
nullptr;
420 for (
auto *b = blocks_; b; b = b->next)
421 if (b->available && b->capacity >= bytes && b->alignment >= alignment &&
422 (!best || b->capacity < best->capacity))
426 best->available =
false;
429 std::size_t capacity = roundCapacity_ ? 1 : std::max(std::size_t(1), bytes);
430 while (capacity < bytes)
432 if (capacity > std::numeric_limits<std::size_t>::max() / 2)
433 offlineFail(OfflineStatus::MemoryLimit);
436 alignment = std::max(alignment,
alignof(Block));
437 if (alignment > std::numeric_limits<std::size_t>::max() -
sizeof(Block))
439 const auto offset = (
sizeof(Block) + alignment - 1) / alignment * alignment;
440 if (capacity > std::numeric_limits<std::size_t>::max() - offset)
442 const auto total = offset + capacity;
444 void *raw = alignment >
alignof(std::max_align_t)
445 ? ::operator
new(total, std::align_val_t{alignment}) : ::operator new(total);
446 auto *block =
new (raw) Block{blocks_,
static_cast<std::byte *
>(raw) + offset,
447 capacity, alignment,
false};
451 void do_deallocate(
void *p, std::size_t, std::size_t)
override
453 for (
auto *b = blocks_; b; b = b->next)
460 bool do_is_equal(
const std::pmr::memory_resource &other)
const noexcept override
462 return this == &other;
464 OfflineSession &job_;
466 Block *blocks_ =
nullptr;
483 if (spec.
channels > maximumChannels)
493 : frames_(static_cast<int>(std::min<std::int64_t>(spec.frames, blockFrames))),
494 channels_(spec.channels),
495 data_(session.allocate<T>(static_cast<std::uint64_t>(frames_) * channels_))
500 std::array<T *, 16> pointers{};
501 for (
int c = 0; c < channels_; ++c)
502 pointers[
static_cast<std::size_t
>(c)] =
503 data_.get() +
static_cast<std::size_t
>(c) * frames_;
504 return {pointers.data(), channels_, count};
506 [[nodiscard]]
int frames() const noexcept
512 int frames_, channels_;
513 std::unique_ptr<T[]> data_;
516template <FloatType T>
520 if (source.
getSpec() != expected)
525 ok = source.
read(first, block);
533 if (source.
getSpec() != expected)
539 static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
540 "Offline PCM supports float and double");
541 const std::uint64_t word = [&] {
542 if constexpr (
sizeof(T) == 4)
543 return static_cast<std::uint64_t
>(std::bit_cast<std::uint32_t>(sample));
545 return std::bit_cast<std::uint64_t>(sample);
547 state.first = (state.first ^ word) * 1099511628211ull;
549 std::rotl(state.second ^ word, 27) * 0x3c79ac492ba7b653ull + 0x1c69b3f74ac4ae35ull;
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.
Transactional worker sink for arbitrarily long offline output.
virtual bool begin(const OfflineAudioSpec &spec)=0
virtual void abort() noexcept=0
virtual ~OfflineAudioSink()=default
virtual bool write(std::int64_t first, AudioBufferView< const T > block)=0
Rewindable, complete-file source with int64 positions and bounded blocks.
virtual OfflineAudioSpec getSpec() const noexcept=0
Returns format and provenance by value.
virtual bool read(std::int64_t first, AudioBufferView< T > destination)=0
Copies a bounded source-relative range into the supplied block.
virtual ~OfflineAudioSource()=default
Borrowed planar-buffer adapter; does not allocate, copy or own input.
OfflineBufferSource(AudioBufferView< const T, MaxChannels > audio, double sampleRate, std::uint64_t sourceId=0, std::uint64_t revision=0, std::int64_t timelineOrigin=0) noexcept
Binds an existing view and optional host content/revision keys.
OfflineAudioSpec getSpec() const noexcept override
Returns format and provenance by value.
bool read(std::int64_t first, AudioBufferView< T > destination) override
Copies a bounded source-relative range into the supplied block.
int frames() const noexcept
AudioBufferView< T > view(int count) noexcept
OfflineBlock(OfflineSession &session, const OfflineAudioSpec &spec, int blockFrames)
OfflineMemoryPool(const OfflineMemoryPool &)=delete
~OfflineMemoryPool() override
OfflineMemoryPool & operator=(const OfflineMemoryPool &)=delete
OfflineMemoryPool(OfflineSession &job, bool roundCapacity=true)
OfflineSession(const OfflineJobOptions &options)
void useScratchResource(std::pmr::memory_resource &resource) noexcept
void charge(std::size_t bytes)
std::unique_ptr< U[]> allocate(std::uint64_t count)
std::size_t bytes() const noexcept
OfflineScratchArray< U > allocateScratch(std::uint64_t count)
void checkpoint(OfflinePhase phase, std::int64_t completed, std::int64_t total) const
Constrains a type to IEEE floating-point (float or double).
void offlineFail(OfflineStatus status)
void offlineRead(OfflineAudioSource< T > &source, const OfflineAudioSpec &expected, std::int64_t first, AudioBufferView< T > block)
std::unique_ptr< T[], OfflineScratchDeleter< T > > OfflineScratchArray
void offlineValidateSpec(const OfflineAudioSpec &spec, int maximumChannels=16)
void offlineHash(OfflineFingerprint &state, T sample) noexcept
std::size_t offlineBytes(std::uint64_t count, std::size_t size)
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.
OfflineStatus
Explicit completion or no-publication outcome of an offline job.
@ TargetUnreachable
Exact protected PCM prevents the requested processing target.
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.
bool operator==(const OfflineAudioSpec &) const noexcept=default
bool isValid() const noexcept
Checks transport bounds, not the contents or identity of the audio.
std::int64_t timelineOrigin
Metadata; read offsets are relative to frame zero.
Noncryptographic PCM fingerprint, stable across block divisions.
bool operator==(const OfflineFingerprint &) const noexcept=default
Measured-render control adjustments; no clipping or master gain trim.
double rightBoundaryGain
Independent constant continuation after the final frame.
double boundaryGain
Affine reference, before deltaScale; mean of endpoint controls.
double maximumControlGain
Includes any representability cap on the processing delta.
bool targetFeasible
False if no admissible processing delta meets the peak target.
double deltaScale
Applied to the processing delta, leaving protected PCM unchanged.
double leftBoundaryGain
Independent constant continuation before frame zero.
double minimumControlGain
Calibrated control range, not the output/input sample ratio.
bool boundaryCalibrated
Exterior controls adjusted for boundary-event peaks.
bool bandlimited
Product evaluated before optional exact-PCM constraints.
bool representabilityLimited
Extra gain reduced to avoid floating-point overflow.
Resource and cooperative-cancellation controls for one worker operation.
bool(* progress)(void *, OfflineProgress)
Returning false cancels.
std::size_t memoryBudgetBytes
const std::atomic< bool > * cancel
Worker progress in source frames, or control points in the Plan pass.
Source-frame interval [begin,end), relative to the start of the source.
bool operator==(const OfflineRegion &) const noexcept=default
void operator()(T *pointer) const noexcept