Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sample_asset.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <algorithm>
9#include <cmath>
10#include <cstddef>
11#include <cstdint>
12#include <limits>
13#include <optional>
14#include <type_traits>
15#include <vector>
16
17namespace pulp::audio {
18
20 std::uint64_t asset_id = 0;
21 std::uint64_t asset_generation = 0;
22};
23
27 std::uint32_t channels = 0;
28 std::uint64_t total_frames = 0;
29 double sample_rate = 0.0;
30 std::uint64_t preload_frames = 0;
31 // Required for a streamed tail. The loader supplies the sampler's actual
32 // prepared host/rate/block/interpolation limits, not source-authored metadata.
33 std::optional<SamplePreloadContract> preload_contract;
34 std::optional<SampleStreamCacheSourceView> stream_source;
35};
36
37class SampleAsset;
38
40public:
42
45 const float* const* preload_channels,
46 std::uint32_t channels,
47 std::uint64_t total_frames,
48 double sample_rate,
49 std::uint64_t preload_frames,
50 bool has_preload_contract,
51 const SamplePreloadContract& preload_contract,
52 bool has_stream_source,
53 const SampleStreamCacheSourceView& stream_source) const noexcept {
54 const bool stream_matches =
55 !has_stream_source ||
56 (stream_source.valid() &&
57 stream_registration_.matches(stream_source.token,
58 stream_source.window,
59 stream_source.total_frames,
60 stream_source.page_frames,
61 stream_source.registration_epoch) &&
62 stream_memory_governor_epoch_ == stream_source.memory_governor_epoch);
63 return stream_matches && asset_.asset_id == asset.asset_id &&
64 asset_.asset_generation == asset.asset_generation &&
65 source_.source_id == source.source_id &&
66 source_.source_generation == source.source_generation &&
67 preload_channels_ == preload_channels && channels_ == channels &&
68 total_frames_ == total_frames && sample_rate_ == sample_rate &&
69 preload_frames_ == preload_frames &&
70 has_preload_contract_ == has_preload_contract &&
71 same_contract(preload_contract_, preload_contract) &&
72 has_stream_source_ == has_stream_source;
73 }
74
75private:
76 friend class SampleAsset;
77
79 const float* const* preload_channels) noexcept
80 : asset_(config.asset),
81 source_(config.source),
82 preload_channels_(preload_channels),
83 channels_(config.channels),
84 total_frames_(config.total_frames),
85 sample_rate_(config.sample_rate),
86 preload_frames_(config.preload_frames),
87 has_preload_contract_(config.preload_contract.has_value()),
88 preload_contract_(config.preload_contract.value_or(SamplePreloadContract{})),
89 has_stream_source_(config.stream_source.has_value()),
90 stream_registration_(config.stream_source
91 ? config.stream_source->registration
93 stream_memory_governor_epoch_(config.stream_source
94 ? config.stream_source->memory_governor_epoch
96
97 static bool same_contract(const SamplePreloadContract& left,
98 const SamplePreloadContract& right) noexcept {
99 return left.source_sample_rate == right.source_sample_rate &&
100 left.host_sample_rate == right.host_sample_rate &&
101 left.maximum_playback_ratio == right.maximum_playback_ratio &&
102 left.certified_io_latency_seconds == right.certified_io_latency_seconds &&
103 left.scheduler_margin_seconds == right.scheduler_margin_seconds &&
104 left.decoder_latency_seconds == right.decoder_latency_seconds &&
105 left.maximum_host_block_frames == right.maximum_host_block_frames &&
106 left.interpolation_guard_frames == right.interpolation_guard_frames &&
107 left.loop_prefetch_guard_frames == right.loop_prefetch_guard_frames &&
108 left.configured_preload_frames == right.configured_preload_frames;
109 }
110
111 SampleAssetToken asset_{};
112 SampleStreamSourceToken source_{};
113 const float* const* preload_channels_ = nullptr;
114 std::uint32_t channels_ = 0;
115 std::uint64_t total_frames_ = 0;
116 double sample_rate_ = 0.0;
117 std::uint64_t preload_frames_ = 0;
118 bool has_preload_contract_ = false;
119 SamplePreloadContract preload_contract_{};
120 bool has_stream_source_ = false;
121 SampleStreamSourceRegistrationProof stream_registration_{};
122 SampleMemoryGovernorEpoch stream_memory_governor_epoch_{};
123};
124
125static_assert(std::is_trivially_copyable_v<SampleAssetRegistrationProof>);
126static_assert(std::is_standard_layout_v<SampleAssetRegistrationProof>);
127
133 const float* const* preload_channels = nullptr;
134 std::uint32_t channels = 0;
135 std::uint64_t total_frames = 0;
136 double sample_rate = 0.0;
137 std::uint64_t preload_frames = 0;
140 bool has_stream_source = false;
143
144 bool valid() const noexcept {
145 const bool base_valid = asset.asset_id != 0 && asset.asset_generation != 0 &&
146 source.source_id != 0 && source.source_generation != 0 &&
147 preload_channels != nullptr && channels != 0 &&
148 total_frames != 0 && sample_rate > 0.0 &&
149 std::isfinite(sample_rate) &&
152 if (!base_valid ||
153 !registration.matches(asset,
154 source,
156 channels,
163 stream_source)) {
164 return false;
165 }
166 if (!has_stream_source) return true;
169 preload_contract.source_sample_rate != sample_rate ||
170 preload_contract.configured_preload_frames != preload_frames)) {
171 return false;
172 }
173 return stream_source.valid() &&
174 stream_source.token.source_id == source.source_id &&
175 stream_source.token.source_generation == source.source_generation &&
176 stream_source.total_frames == total_frames;
177 }
178
179 bool fully_resident() const noexcept {
180 return valid() && preload_frames == total_frames;
181 }
182
183 bool preload_contains(std::uint64_t frame) const noexcept {
184 return valid() && frame < preload_frames;
185 }
186
187 const float* preload_channel_data(std::uint32_t channel) const noexcept {
188 if (!valid() || channel >= channels) return nullptr;
189 return preload_channels[channel];
190 }
191};
192
193static_assert(std::is_trivially_copyable_v<SampleAssetView>);
194static_assert(std::is_standard_layout_v<SampleAssetView>);
195
201public:
202 SampleAsset() = default;
203 SampleAsset(const SampleAsset&) = delete;
207
208 template<typename SampleType>
209 bool prepare(const SampleAssetConfig& config, BufferView<SampleType> preload) {
210 static_assert(std::is_same_v<std::remove_const_t<SampleType>, float>);
211 release();
212 if (!config_valid(config, preload)) return false;
213
214 try {
215 preload_.resize(config.channels, static_cast<std::size_t>(config.preload_frames));
216 preload_channel_ptrs_.resize(config.channels);
217 for (std::uint32_t channel = 0; channel < config.channels; ++channel) {
218 std::copy_n(preload.channel_ptr(channel),
219 static_cast<std::size_t>(config.preload_frames),
220 preload_.channel(channel).data());
221 preload_channel_ptrs_[channel] = preload_.channel(channel).data();
222 }
223 } catch (...) {
224 release();
225 return false;
226 }
227
228 publish_view(config);
229 return true;
230 }
231
236 Buffer<float> preload,
237 SampleMemoryLease preload_lease) {
238 release();
239 const auto bytes = checked_sample_storage_bytes(
240 config.channels, config.preload_frames);
241 const auto expected_samples = bytes
242 ? *bytes / static_cast<std::uint64_t>(sizeof(float))
243 : std::uint64_t{0};
244 if (!bytes || !preload_lease ||
245 preload_lease.category() != SampleMemoryCategory::Preload ||
246 preload_lease.bytes() != *bytes ||
247 preload.num_channels() != config.channels ||
248 preload.num_samples() != config.preload_frames ||
249 preload.allocated_sample_capacity() != expected_samples ||
250 (config.stream_source &&
251 preload_lease.epoch() != config.stream_source->memory_governor_epoch) ||
252 !config_valid(config, preload.view())) {
253 preload.release();
254 preload_lease.reset();
255 return false;
256 }
257
258 try {
259 preload_ = std::move(preload);
260 preload_channel_ptrs_.resize(config.channels);
261 for (std::uint32_t channel = 0; channel < config.channels; ++channel) {
262 preload_channel_ptrs_[channel] = preload_.channel(channel).data();
263 }
264 preload_lease_ = std::move(preload_lease);
265 } catch (...) {
266 release();
267 preload.release();
268 preload_lease.reset();
269 return false;
270 }
271
272 publish_view(config);
273 return true;
274 }
275
276 void release() noexcept {
277 view_ = {};
278 std::vector<const float*>().swap(preload_channel_ptrs_);
279 preload_ = {};
280 preload_lease_.reset();
281 }
282
283 bool prepared() const noexcept { return view_.valid(); }
284 SampleAssetView view() const noexcept { return view_; }
285
286private:
287 void publish_view(const SampleAssetConfig& config) noexcept {
288 view_ = {
289 .asset = config.asset,
290 .source = config.source,
291 .preload_channels = preload_channel_ptrs_.data(),
292 .channels = config.channels,
293 .total_frames = config.total_frames,
294 .sample_rate = config.sample_rate,
295 .preload_frames = config.preload_frames,
296 .has_preload_contract = config.preload_contract.has_value(),
297 .preload_contract = config.preload_contract.value_or(SamplePreloadContract{}),
298 .has_stream_source = config.stream_source.has_value(),
299 .stream_source = config.stream_source.value_or(SampleStreamCacheSourceView{}),
300 .registration = SampleAssetRegistrationProof(
301 config, preload_channel_ptrs_.data()),
302 };
303 }
304
305 template<typename SampleType>
306 static bool config_valid(const SampleAssetConfig& config,
307 BufferView<SampleType> preload) noexcept {
308 if (config.asset.asset_id == 0 || config.asset.asset_generation == 0 ||
309 config.source.source_id == 0 || config.source.source_generation == 0 ||
310 config.channels == 0 || config.total_frames == 0 ||
311 !(config.sample_rate > 0.0) || !std::isfinite(config.sample_rate) ||
312 config.preload_frames == 0 || config.preload_frames > config.total_frames ||
313 config.preload_frames > std::numeric_limits<std::size_t>::max() ||
314 config.preload_frames >
315 std::numeric_limits<std::size_t>::max() / config.channels ||
316 preload.num_channels() != config.channels ||
317 config.preload_frames > preload.num_samples()) {
318 return false;
319 }
320 for (std::uint32_t channel = 0; channel < config.channels; ++channel) {
321 if (preload.channel_ptr(channel) == nullptr) return false;
322 }
323
324 if (!config.stream_source) return config.preload_frames == config.total_frames;
325 if (config.preload_frames < config.total_frames) {
326 if (!config.preload_contract) return false;
327 const auto& contract = *config.preload_contract;
328 if (contract.source_sample_rate != config.sample_rate ||
329 contract.configured_preload_frames != config.preload_frames ||
330 contract.maximum_host_block_frames == 0 ||
331 contract.interpolation_guard_frames == 0) {
332 return false;
333 }
334 const auto evaluated = evaluate_sample_preload_contract(contract);
335 if (!evaluated.valid() || !evaluated.sufficient) return false;
336 }
337 const auto& stream = *config.stream_source;
338 return stream.valid() && stream.token.source_id == config.source.source_id &&
339 stream.token.source_generation == config.source.source_generation &&
340 stream.total_frames == config.total_frames && stream.window->prepared() &&
341 stream.window->channels() == config.channels &&
342 stream.window->page_frames() == stream.page_frames;
343 }
344
345 // Declared before the storage so reverse member destruction frees the
346 // buffer before returning its bytes to the governor.
347 SampleMemoryLease preload_lease_;
348 Buffer<float> preload_;
349 std::vector<const float*> preload_channel_ptrs_;
350 SampleAssetView view_{};
351};
352
353} // namespace pulp::audio
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
SampleType * channel_ptr(std::size_t index)
Raw pointer to one channel's sample data.
Definition buffer.hpp:68
Owning multi-channel audio buffer with contiguous storage.
Definition buffer.hpp:119
std::size_t num_channels() const
Definition buffer.hpp:204
void release() noexcept
Release all owned sample and channel-pointer storage.
Definition buffer.hpp:211
std::size_t allocated_sample_capacity() const noexcept
Number of sample elements retained by the backing allocation.
Definition buffer.hpp:208
BufferView< SampleType > view()
Create a non-owning BufferView over this buffer's data.
Definition buffer.hpp:182
std::size_t num_samples() const
Definition buffer.hpp:205
Definition sample_asset.hpp:39
bool matches(SampleAssetToken asset, SampleStreamSourceToken source, const float *const *preload_channels, std::uint32_t channels, std::uint64_t total_frames, double sample_rate, std::uint64_t preload_frames, bool has_preload_contract, const SamplePreloadContract &preload_contract, bool has_stream_source, const SampleStreamCacheSourceView &stream_source) const noexcept
Definition sample_asset.hpp:43
friend class SampleAsset
Definition sample_asset.hpp:76
Off-audio-thread owner for immutable sampler metadata and resident preload.
Definition sample_asset.hpp:200
SampleAssetView view() const noexcept
Definition sample_asset.hpp:284
SampleAsset(SampleAsset &&)=delete
SampleAsset & operator=(const SampleAsset &)=delete
bool prepare_owned(const SampleAssetConfig &config, Buffer< float > preload, SampleMemoryLease preload_lease)
Takes ownership of an already-filled preload without allocating and copying a second planar sample st...
Definition sample_asset.hpp:235
SampleAsset(const SampleAsset &)=delete
bool prepare(const SampleAssetConfig &config, BufferView< SampleType > preload)
Definition sample_asset.hpp:209
void release() noexcept
Definition sample_asset.hpp:276
SampleAsset & operator=(SampleAsset &&)=delete
bool prepared() const noexcept
Definition sample_asset.hpp:283
Definition sample_memory_governor.hpp:16
Move-only reservation against one shared sampler-memory cap.
Definition sample_memory_governor.hpp:79
std::uint64_t bytes() const noexcept
Definition sample_memory_governor.hpp:103
SampleMemoryCategory category() const noexcept
Definition sample_memory_governor.hpp:102
SampleMemoryGovernorEpoch epoch() const noexcept
Definition sample_memory_governor.hpp:105
void reset() noexcept
Definition sample_memory_governor.hpp:111
Definition sample_stream_service.hpp:158
Definition analyzer_provider.hpp:12
@ Preload
Definition sample_memory_governor.hpp:36
std::optional< std::uint64_t > checked_sample_storage_bytes(std::uint32_t channels, std::uint64_t frames_per_buffer, std::uint32_t buffer_count=1, std::uint32_t bytes_per_sample=sizeof(float)) noexcept
Checked planar sample-storage formula shared by preload and page admission.
Definition sample_memory_governor.hpp:254
SamplePreloadContractResult evaluate_sample_preload_contract(const SamplePreloadContract &contract) noexcept
Calculate the resident head required to cover a certified streaming service interval at the fastest s...
Definition sample_preload_contract.hpp:45
@ config
Per-user settings root (an XDG distinction; equals app_data off Linux).
Definition special_dir.hpp:65
Definition sample_asset.hpp:24
std::optional< SampleStreamCacheSourceView > stream_source
Definition sample_asset.hpp:34
std::uint64_t total_frames
Definition sample_asset.hpp:28
double sample_rate
Definition sample_asset.hpp:29
std::uint64_t preload_frames
Definition sample_asset.hpp:30
std::uint32_t channels
Definition sample_asset.hpp:27
SampleAssetToken asset
Definition sample_asset.hpp:25
std::optional< SamplePreloadContract > preload_contract
Definition sample_asset.hpp:33
SampleStreamSourceToken source
Definition sample_asset.hpp:26
Definition sample_asset.hpp:19
std::uint64_t asset_generation
Definition sample_asset.hpp:21
std::uint64_t asset_id
Definition sample_asset.hpp:20
Immutable audio-thread snapshot borrowed from one SampleAsset owner.
Definition sample_asset.hpp:130
std::uint64_t total_frames
Definition sample_asset.hpp:135
bool has_preload_contract
Definition sample_asset.hpp:138
double sample_rate
Definition sample_asset.hpp:136
bool preload_contains(std::uint64_t frame) const noexcept
Definition sample_asset.hpp:183
SampleAssetRegistrationProof registration
Definition sample_asset.hpp:142
const float *const * preload_channels
Definition sample_asset.hpp:133
SampleStreamCacheSourceView stream_source
Definition sample_asset.hpp:141
std::uint32_t channels
Definition sample_asset.hpp:134
std::uint64_t preload_frames
Definition sample_asset.hpp:137
SamplePreloadContract preload_contract
Definition sample_asset.hpp:139
bool valid() const noexcept
Definition sample_asset.hpp:144
bool fully_resident() const noexcept
Definition sample_asset.hpp:179
bool has_stream_source
Definition sample_asset.hpp:140
const float * preload_channel_data(std::uint32_t channel) const noexcept
Definition sample_asset.hpp:187
SampleAssetToken asset
Definition sample_asset.hpp:131
SampleStreamSourceToken source
Definition sample_asset.hpp:132
Definition sample_preload_contract.hpp:18
Definition sample_stream_service.hpp:200
Definition sample_stream_service.hpp:18