Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sample_stream_loop_voice_reader.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <algorithm>
11#include <array>
12#include <cmath>
13#include <cstddef>
14#include <cstdint>
15#include <limits>
16#include <type_traits>
17
18namespace pulp::audio {
19
20inline constexpr std::size_t kSampleStreamLoopMaxPageDemands = 16;
21
27 std::uint64_t timeline_serial = 0;
28 std::uint32_t output_frames = 0;
31 std::array<SampleStreamPageDemand, kSampleStreamLoopMaxPageDemands> demands{};
32 std::array<SampleStreamPageView, kSampleStreamLoopMaxPageDemands> ready_pages{};
33 std::uint32_t demand_count = 0;
34};
35
36static_assert(std::is_trivially_copyable_v<SampleStreamLoopBlockPlan>);
37
43public:
44 bool prepare(const SampleAssetView& asset,
46 const LoopRegion& region,
47 double playback_rate) noexcept {
48 return prepare(asset, requester, region, playback_rate,
49 sample_interpolation_policy(region.interpolation));
50 }
51
52 bool prepare(const SampleAssetView& asset,
54 const LoopRegion& region,
55 double playback_rate,
57 return prepare(asset, requester, region, playback_rate,
59 }
60
61 bool prepare(const SampleAssetView& asset,
63 const LoopRegion& region,
64 double playback_rate,
68 reset();
69 if (!asset.valid() || requester.requester_id == 0 ||
70 requester.requester_generation == 0 ||
71 !positive_finite(playback_rate) ||
72 !interpolation.valid() || !starvation_envelope_.prepare(starvation) ||
73 !cursor_.set_region(region, asset.total_frames)) {
74 return false;
75 }
76 cursor_.set_playback_rate(playback_rate);
77 if (!plan_capacity_accepts(asset, region, interpolation,
78 std::abs(cursor_.step()))) {
79 reset();
80 return false;
81 }
82 cursor_.start();
83 asset_ = asset.asset;
84 source_ = asset.source;
85 admission_asset_ = asset;
86 requester_ = requester;
87 interpolation_ = interpolation;
88 prepared_ = true;
89 return true;
90 }
91
92 void reset() noexcept {
93 asset_ = {};
94 source_ = {};
95 admission_asset_ = {};
96 requester_ = {};
97 interpolation_ = {};
98 cursor_ = {};
99 starvation_envelope_.reset();
100 timeline_serial_ = 1;
101 prepared_ = false;
102 }
103
104 bool set_playback_rate(double playback_rate) noexcept {
105 if (!prepared_ || !positive_finite(playback_rate)) return false;
106 auto candidate = cursor_;
107 candidate.set_playback_rate(playback_rate);
108 if (!plan_capacity_accepts(admission_asset_, candidate.region(),
109 interpolation_, std::abs(candidate.step()))) {
110 return false;
111 }
112 cursor_ = candidate;
113 ++timeline_serial_;
114 return true;
115 }
116
118 double playback_rate) noexcept {
119 return prepared_ && same_generation(asset) &&
120 set_playback_rate(playback_rate);
121 }
122
124 const SampleAssetView& asset,
126 if (!prepared_ || !same_generation(asset) || !interpolation.valid() ||
127 !plan_capacity_accepts(asset, cursor_.region(), interpolation,
128 std::abs(cursor_.step()))) {
129 return false;
130 }
131 if (same_sample_interpolation(interpolation_, interpolation)) return true;
132 interpolation_ = interpolation;
133 ++timeline_serial_;
134 return true;
135 }
136
137 const LoopPlaybackCursor& cursor() const noexcept { return cursor_; }
139 return interpolation_;
140 }
141 bool active() const noexcept { return prepared_ && cursor_.active(); }
143 return starvation_envelope_.mode();
144 }
146 return starvation_envelope_.stats();
147 }
148
152 void mark_held_starvation(std::uint64_t frames) noexcept {
153 starvation_envelope_.mark_starved(frames);
154 }
155
157 const LoopPlaybackCursor& cursor) noexcept {
158 if (!prepared_ || !same_generation(asset) ||
159 !validate_loop_region(cursor.region(), asset.total_frames).ok ||
160 !plan_capacity_accepts(asset, cursor.region(), interpolation_,
161 std::abs(cursor.step()))) {
162 return false;
163 }
164 cursor_ = cursor;
165 ++timeline_serial_;
166 return true;
167 }
168
170 const SampleAssetView& asset,
171 std::uint32_t output_frames,
172 double output_sample_rate,
173 SampleStreamDemandClass demand_class = SampleStreamDemandClass::Sustain) const noexcept {
174 return plan_block(
175 asset, output_frames, output_sample_rate,
176 {.source_frames_per_second =
177 std::abs(cursor_.step()) * output_sample_rate},
178 demand_class);
179 }
180
182 const SampleAssetView& asset,
183 std::uint32_t output_frames,
184 double output_sample_rate,
186 SampleStreamDemandClass demand_class =
187 SampleStreamDemandClass::Sustain) const noexcept {
189 plan.asset = asset.asset;
190 plan.source = asset.source;
191 plan.start_cursor = cursor_;
192 plan.end_cursor = cursor_;
193 plan.timeline_serial = timeline_serial_;
194 plan.output_frames = output_frames;
195 plan.interpolation = interpolation_;
196
197 if (!prepared_ || !asset.valid() || output_frames == 0 ||
198 !positive_finite(output_sample_rate)) {
199 return plan;
200 }
201 if (!same_generation(asset)) {
203 return plan;
204 }
205 const auto step = std::abs(cursor_.step());
206 if (!positive_finite(step) ||
207 !contract_accepts(asset, interpolation_, output_frames, step,
208 output_sample_rate,
209 consumption.source_frames_per_second)) {
210 return plan;
211 }
212 if (!cursor_.active()) {
214 return plan;
215 }
216
217 auto scan = cursor_;
218 const auto consumption_frames_per_second =
219 consumption.source_frames_per_second;
220 for (std::uint32_t output = 0; output < output_frames && scan.active(); ++output) {
221 const auto frame_plan = scan.frame_read_plan();
222 if (!append_position(plan, asset, scan.region(), interpolation_,
223 frame_plan.read_position,
224 output, step, consumption_frames_per_second,
225 demand_class) ||
226 (frame_plan.blend &&
227 !append_position(plan, asset, scan.region(), interpolation_,
228 frame_plan.blend_position,
229 output, step, consumption_frames_per_second,
230 demand_class))) {
231 plan.demand_count = 0;
233 return plan;
234 }
235 (void) scan.advance();
236 }
237 plan.end_cursor = scan;
239 return plan;
240 }
241
242 template<std::size_t InboxCapacity>
244 const SampleStreamLoopBlockPlan& plan,
246 std::uint32_t start_index = 0,
247 std::uint64_t resident_source_frame_offset = 0,
248 std::uint32_t end_index =
249 std::numeric_limits<std::uint32_t>::max()) const noexcept {
251 result.demand_count = plan.demand_count;
252 result.next_demand_index = start_index;
253 const auto range_end = std::min(end_index, plan.demand_count);
255 plan.demand_count > plan.demands.size() || start_index > range_end) {
256 return result;
257 }
258 for (std::uint32_t index = start_index; index < range_end; ++index) {
259 auto demand = plan.demands[index];
260 demand.resident_source_frames =
261 resident_source_frame_offset >
262 std::numeric_limits<std::uint64_t>::max() -
263 demand.resident_source_frames
264 ? std::numeric_limits<std::uint64_t>::max()
265 : resident_source_frame_offset + demand.resident_source_frames;
266 if (inbox.demand_page(demand) !=
268 return result;
269 }
270 ++result.enqueued;
271 ++result.next_demand_index;
272 }
273 result.complete = result.next_demand_index == range_end;
274 return result;
275 }
276
278 const SampleStreamLoopBlockPlan& plan) noexcept {
279 if (!prepared_ || !same_plan_generation(asset, plan) ||
281 plan.timeline_serial != timeline_serial_ ||
282 plan.start_cursor.position() != cursor_.position() ||
283 plan.start_cursor.active() != cursor_.active()) {
284 return false;
285 }
286 cursor_ = plan.end_cursor;
287 ++timeline_serial_;
288 return true;
289 }
290
292 const SampleStreamLoopBlockPlan& plan,
293 BufferView<float> destination) noexcept {
295 if (plan.output_frames == 0 || plan.output_frames > destination.num_samples())
296 return result;
297 for (std::size_t channel = 0; channel < destination.num_channels(); ++channel) {
298 if (destination.channel_ptr(channel) == nullptr) return result;
299 std::fill_n(destination.channel_ptr(channel), plan.output_frames, 0.0f);
300 }
301 if (!prepared_ || !asset.valid() || destination.num_channels() != asset.channels) {
302 return result;
303 }
304 if (!same_plan_generation(asset, plan)) {
307 return result;
308 }
309 if (plan.timeline_serial != timeline_serial_ ||
310 plan.start_cursor.position() != cursor_.position() ||
311 plan.start_cursor.active() != cursor_.active()) {
312 return result;
313 }
317 return result;
318 }
321 return result;
322 }
323 if (plan.supply != SampleStreamVoiceSupply::Ready) return result;
324
325 auto replay = plan.start_cursor;
328 for (std::uint32_t output = 0; output < plan.output_frames && replay.active(); ++output) {
329 const auto frame_plan = replay.frame_read_plan();
330 ResolvedRead primary;
331 ResolvedRead blend;
332 const bool primary_ready = resolve_read(plan, asset, replay.region(),
333 frame_plan.read_position, primary);
334 const bool blend_ready = !frame_plan.blend ||
335 resolve_read(plan, asset, replay.region(), frame_plan.blend_position, blend);
336 if (!primary_ready || !blend_ready) {
339 break;
340 }
341 for (std::uint32_t channel = 0; channel < asset.channels; ++channel) {
342 const auto a = interpolate(asset, primary, channel);
343 if (!a.ready) {
346 break;
347 }
348 double sample = a.sample;
349 if (frame_plan.blend) {
350 const auto b = interpolate(asset, blend, channel);
351 if (!b.ready) {
354 break;
355 }
356 sample = sample * frame_plan.primary_gain +
357 static_cast<double>(b.sample) * frame_plan.blend_gain;
358 }
359 destination.channel_ptr(channel)[output] = static_cast<float>(sample);
360 }
361 if (result.supply == SampleStreamVoiceSupply::Starved) break;
362 ++result.ready_output_frames;
363 (void) replay.advance();
364 }
365
367 starvation_envelope_, destination, result, plan.output_frames);
368 cursor_ = plan.end_cursor;
369 ++timeline_serial_;
370 if (result.supply == SampleStreamVoiceSupply::Ready && !cursor_.active()) {
373 }
374 return result;
375 }
376
377private:
378 struct LocatedFrame {
379 std::uint64_t source_frame;
381 bool ready;
382 bool preload;
383 };
384
385 struct ResolvedRead {
386 std::array<LocatedFrame, kMaximumSampleInterpolationTaps> taps;
387 float fraction;
388 PreparedSampleInterpolation interpolation;
389 std::uint32_t tap_count;
390 };
391
392 struct InterpolatedSample { float sample = 0.0f; bool ready = false; };
393
394 static bool positive_finite(double value) noexcept {
395 return value > 0.0 && std::isfinite(value);
396 }
397
398 bool same_generation(const SampleAssetView& asset) const noexcept {
399 return asset.asset.asset_id == asset_.asset_id &&
400 asset.asset.asset_generation == asset_.asset_generation &&
401 asset.source.source_id == source_.source_id &&
402 asset.source.source_generation == source_.source_generation;
403 }
404
405 bool same_plan_generation(const SampleAssetView& asset,
406 const SampleStreamLoopBlockPlan& plan) const noexcept {
407 return same_generation(asset) && plan.asset.asset_id == asset.asset.asset_id &&
408 plan.asset.asset_generation == asset.asset.asset_generation &&
409 plan.source.source_id == asset.source.source_id &&
410 plan.source.source_generation == asset.source.source_generation;
411 }
412
413 static bool contract_accepts(const SampleAssetView& asset,
414 const PreparedSampleInterpolation& interpolation,
415 std::uint32_t output_frames,
416 double step,
417 double output_sample_rate,
418 double consumption_frames_per_second) noexcept {
419 if (!positive_finite(consumption_frames_per_second)) return false;
420 if (asset.fully_resident()) return true;
421 if (!asset.has_preload_contract) return false;
422 const auto& contract = asset.preload_contract;
423 const auto maximum_consumption =
424 static_cast<double>(asset.sample_rate) *
425 contract.maximum_playback_ratio;
426 return output_frames <= contract.maximum_host_block_frames &&
427 output_sample_rate == contract.host_sample_rate &&
428 positive_finite(step) &&
429 consumption_frames_per_second <= maximum_consumption &&
430 interpolation.guard_frames() <=
431 contract.interpolation_guard_frames;
432 }
433
434 static bool plan_capacity_accepts(
435 const SampleAssetView& asset,
436 const LoopRegion& region,
437 const PreparedSampleInterpolation& interpolation,
438 double step) noexcept {
439 if (asset.fully_resident()) return true;
440 if (!asset.has_preload_contract || !asset.has_stream_source ||
441 asset.stream_source.page_frames == 0 || !positive_finite(step)) {
442 return false;
443 }
444 const auto footprint = interpolation.footprint(0.0f);
445 if (footprint.tap_count == 0 ||
446 footprint.tap_count > kMaximumSampleInterpolationTaps) {
447 return false;
448 }
449 const auto advance = std::ceil(
450 step * static_cast<double>(
451 asset.preload_contract.maximum_host_block_frames));
452 if (!std::isfinite(advance) || advance < 0.0 ||
453 advance >= static_cast<double>(
454 std::numeric_limits<std::uint64_t>::max())) {
455 return false;
456 }
457 const auto span = static_cast<std::uint64_t>(advance) +
458 footprint.tap_count;
459 const auto page_frames = asset.stream_source.page_frames;
460 const auto pages_per_region = span / page_frames +
461 (span % page_frames == 0 ? 0u : 1u) + 2u;
462 const auto crossfaded_loop = region.crossfade_frames != 0 &&
463 (region.playback_mode == LoopPlaybackMode::Forward ||
464 region.playback_mode == LoopPlaybackMode::Reverse);
465 const auto read_regions = crossfaded_loop ? 2u : 1u;
466 if (pages_per_region >
467 std::numeric_limits<std::uint64_t>::max() / read_regions) {
468 return false;
469 }
470 const auto first_stream_frame =
471 std::max(region.start_frame, asset.preload_frames);
472 if (first_stream_frame >= region.end_frame) return true;
473 const auto first_stream_page = first_stream_frame / page_frames;
474 const auto last_stream_page = (region.end_frame - 1) / page_frames;
475 const auto total_stream_pages = last_stream_page - first_stream_page + 1;
476 const auto maximum_demands = std::min(
477 total_stream_pages, pages_per_region * read_regions);
478 return maximum_demands <= kSampleStreamLoopMaxPageDemands;
479 }
480
481 bool append_position(SampleStreamLoopBlockPlan& plan,
482 const SampleAssetView& asset,
483 const LoopRegion& region,
484 const PreparedSampleInterpolation& interpolation,
485 double position,
486 std::uint32_t first_output,
487 double step,
488 double consumption_frames_per_second,
489 SampleStreamDemandClass demand_class) const noexcept {
490 const auto normalized = LoopReader::normalize_position(region, position);
491 const auto resolved = resolve_sample_interpolation_position(
492 normalized, interpolation);
493 if (!resolved.valid) return false;
494 for (std::uint32_t tap = 0; tap < resolved.footprint.tap_count; ++tap) {
495 const auto frame = LoopReader::source_frame_for_tap(
496 region, asset.total_frames,
497 resolved.base_frame + resolved.footprint.first_offset + tap);
498 if (!append_page(plan, asset, frame, first_output, step,
499 consumption_frames_per_second, demand_class)) {
500 return false;
501 }
502 }
503 return true;
504 }
505
506 bool append_page(SampleStreamLoopBlockPlan& plan,
507 const SampleAssetView& asset,
508 std::uint64_t source_frame,
509 std::uint32_t first_output,
510 double step,
511 double consumption_frames_per_second,
512 SampleStreamDemandClass demand_class) const noexcept {
513 if (source_frame < asset.preload_frames) return true;
514 if (!asset.has_stream_source || asset.stream_source.page_frames == 0 ||
515 asset.stream_source.window == nullptr) return false;
516 const auto page_index = source_frame / asset.stream_source.page_frames;
517 const auto distance = static_cast<double>(first_output) * step;
518 const auto resident = distance >= static_cast<double>(std::numeric_limits<std::uint64_t>::max())
519 ? std::numeric_limits<std::uint64_t>::max()
520 : static_cast<std::uint64_t>(std::floor(distance));
521 for (std::uint32_t index = 0; index < plan.demand_count; ++index) {
522 if (plan.demands[index].page_index != page_index) continue;
523 plan.demands[index].resident_source_frames =
524 std::min(plan.demands[index].resident_source_frames, resident);
525 if (demand_class == SampleStreamDemandClass::Attack)
526 plan.demands[index].demand_class = demand_class;
527 return true;
528 }
529 if (plan.demand_count >= plan.demands.size()) return false;
530 plan.ready_pages[plan.demand_count] =
531 asset.stream_source.window->ready_page_for_frame(
532 asset.source.source_generation, source_frame);
533 plan.demands[plan.demand_count++] = {
534 .source = asset.source,
535 .requester = requester_,
536 .page_index = page_index,
537 .resident_source_frames = resident,
538 .consumption_frames_per_second = consumption_frames_per_second,
539 .demand_class = demand_class,
540 };
541 return true;
542 }
543
544 static LocatedFrame locate(const SampleStreamLoopBlockPlan& plan,
545 const SampleAssetView& asset,
546 std::uint64_t frame) noexcept {
547 LocatedFrame located{.source_frame = frame};
548 if (frame < asset.preload_frames) {
549 located.ready = true;
550 located.preload = true;
551 return located;
552 }
553 if (!asset.has_stream_source || asset.stream_source.window == nullptr ||
554 asset.stream_source.page_frames == 0) return located;
555 const auto page_index = frame / asset.stream_source.page_frames;
556 for (std::uint32_t index = 0; index < plan.demand_count; ++index) {
557 if (plan.demands[index].page_index != page_index) continue;
558 located.page = plan.ready_pages[index];
559 if (located.page.valid && frame >= located.page.start_frame)
560 located.page.local_offset = frame - located.page.start_frame;
561 located.ready = located.page.valid;
562 break;
563 }
564 return located;
565 }
566
567 static bool resolve_read(const SampleStreamLoopBlockPlan& plan,
568 const SampleAssetView& asset,
569 const LoopRegion& region,
570 double position,
571 ResolvedRead& resolved) noexcept {
572 const auto normalized = LoopReader::normalize_position(region, position);
573 resolved.interpolation = plan.interpolation;
574 const auto position_plan = resolve_sample_interpolation_position(
575 normalized, resolved.interpolation);
576 if (!position_plan.valid ||
577 position_plan.footprint.tap_count > resolved.taps.size()) return false;
578 resolved.fraction = position_plan.fraction;
579 resolved.tap_count = position_plan.footprint.tap_count;
580 for (std::uint32_t tap = 0; tap < resolved.tap_count; ++tap) {
581 const auto frame = LoopReader::source_frame_for_tap(
582 region, asset.total_frames,
583 position_plan.base_frame +
584 position_plan.footprint.first_offset + tap);
585 resolved.taps[tap] = locate(plan, asset, frame);
586 if (!resolved.taps[tap].ready) return false;
587 }
588 return true;
589 }
590
591 static const float* sample_pointer(const SampleAssetView& asset,
592 const LocatedFrame& located,
593 std::uint32_t channel) noexcept {
594 if (!located.ready || channel >= asset.channels) return nullptr;
595 if (located.preload) {
596 const auto* data = asset.preload_channel_data(channel);
597 return data == nullptr ? nullptr : data + located.source_frame;
598 }
599 return asset.stream_source.window->ready_channel_data(located.page, channel);
600 }
601
602 static InterpolatedSample interpolate(const SampleAssetView& asset,
603 const ResolvedRead& read,
604 std::uint32_t channel) noexcept {
605 if (read.tap_count == 0 || read.tap_count > read.taps.size()) return {};
606 std::array<float, kMaximumSampleInterpolationTaps> samples;
607 for (std::uint32_t tap = 0; tap < read.tap_count; ++tap) {
608 const auto* sample = sample_pointer(asset, read.taps[tap], channel);
609 if (sample == nullptr) return {};
610 samples[tap] = *sample;
611 }
612 return {read.interpolation.evaluate(
613 read.fraction,
614 std::span<const float>(samples.data(), read.tap_count)), true};
615 }
616
617 SampleAssetToken asset_{};
618 SampleStreamSourceToken source_{};
619 SampleAssetView admission_asset_{};
620 SampleStreamRequesterToken requester_{};
621 PreparedSampleInterpolation interpolation_{};
622 LoopPlaybackCursor cursor_{};
623 std::uint64_t timeline_serial_ = 1;
624 SampleStarvationEnvelope starvation_envelope_;
625 bool prepared_ = false;
626};
627
628} // namespace pulp::audio
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Allocation-free loop traversal state independent of sample storage.
Definition loop_playback_cursor.hpp:27
bool active() const noexcept
Definition loop_playback_cursor.hpp:43
double position() const noexcept
Definition loop_playback_cursor.hpp:44
static double normalize_position(const LoopRegion &region, double position) noexcept
static std::uint64_t source_frame_for_tap(const LoopRegion &region, std::uint64_t source_frames, std::int64_t signed_frame) noexcept
Resolves an interpolation tap according to the traversal boundary: bounded modes clamp,...
Prepared bounded command port from one RT producer to the service owner.
Definition sample_stream_service.hpp:91
SampleStreamCommandPushStatus demand_page(const SampleStreamPageDemand &demand) noexcept
Definition sample_stream_service.hpp:93
Allocation-free paged adapter for LoopPlaybackCursor.
Definition sample_stream_loop_voice_reader.hpp:42
bool set_interpolation(const SampleAssetView &asset, const PreparedSampleInterpolation &interpolation) noexcept
Definition sample_stream_loop_voice_reader.hpp:123
bool synchronize_cursor(const SampleAssetView &asset, const LoopPlaybackCursor &cursor) noexcept
Definition sample_stream_loop_voice_reader.hpp:156
bool prepare(const SampleAssetView &asset, SampleStreamRequesterToken requester, const LoopRegion &region, double playback_rate, const PreparedSampleInterpolation &interpolation, SampleStarvationEnvelopeConfig starvation=kDefaultSampleStarvationEnvelopeConfig) noexcept
Definition sample_stream_loop_voice_reader.hpp:61
bool prepare(const SampleAssetView &asset, SampleStreamRequesterToken requester, const LoopRegion &region, double playback_rate, SampleInterpolationPolicy interpolation) noexcept
Definition sample_stream_loop_voice_reader.hpp:52
bool set_playback_rate(const SampleAssetView &asset, double playback_rate) noexcept
Definition sample_stream_loop_voice_reader.hpp:117
SampleStarvationMode starvation_mode() const noexcept
Definition sample_stream_loop_voice_reader.hpp:142
bool commit_planned_timeline(const SampleAssetView &asset, const SampleStreamLoopBlockPlan &plan) noexcept
Definition sample_stream_loop_voice_reader.hpp:277
bool set_playback_rate(double playback_rate) noexcept
Definition sample_stream_loop_voice_reader.hpp:104
const PreparedSampleInterpolation & interpolation() const noexcept
Definition sample_stream_loop_voice_reader.hpp:138
void mark_held_starvation(std::uint64_t frames) noexcept
Records an externally held all-silent block without advancing the cursor.
Definition sample_stream_loop_voice_reader.hpp:152
SampleStarvationEnvelopeStats starvation_stats() const noexcept
Definition sample_stream_loop_voice_reader.hpp:145
const LoopPlaybackCursor & cursor() const noexcept
Definition sample_stream_loop_voice_reader.hpp:137
SampleStreamLoopBlockPlan plan_block(const SampleAssetView &asset, std::uint32_t output_frames, double output_sample_rate, SampleStreamConsumptionDeclaration consumption, SampleStreamDemandClass demand_class=SampleStreamDemandClass::Sustain) const noexcept
Definition sample_stream_loop_voice_reader.hpp:181
bool prepare(const SampleAssetView &asset, SampleStreamRequesterToken requester, const LoopRegion &region, double playback_rate) noexcept
Definition sample_stream_loop_voice_reader.hpp:44
void reset() noexcept
Definition sample_stream_loop_voice_reader.hpp:92
SampleStreamLoopBlockPlan plan_block(const SampleAssetView &asset, std::uint32_t output_frames, double output_sample_rate, SampleStreamDemandClass demand_class=SampleStreamDemandClass::Sustain) const noexcept
Definition sample_stream_loop_voice_reader.hpp:169
SampleStreamVoiceDemandEnqueueResult enqueue_demands(const SampleStreamLoopBlockPlan &plan, SampleStreamCommandInbox< InboxCapacity > &inbox, std::uint32_t start_index=0, std::uint64_t resident_source_frame_offset=0, std::uint32_t end_index=std::numeric_limits< std::uint32_t >::max()) const noexcept
Definition sample_stream_loop_voice_reader.hpp:243
SampleStreamVoiceBlockResult render_block(const SampleAssetView &asset, const SampleStreamLoopBlockPlan &plan, BufferView< float > destination) noexcept
Definition sample_stream_loop_voice_reader.hpp:291
bool active() const noexcept
Definition sample_stream_loop_voice_reader.hpp:141
void apply_sample_starvation_envelope(SampleStarvationEnvelope &envelope, BufferView< float > destination, const SampleStreamVoiceBlockResult &result, std::uint32_t output_frames) noexcept
Definition sample_stream_voice_reader.hpp:68
Definition analyzer_provider.hpp:12
bool same_sample_interpolation(const PreparedSampleInterpolation &left, const PreparedSampleInterpolation &right) noexcept
Definition sample_interpolation.hpp:188
SampleStreamDemandClass
Definition sample_stream_scheduler.hpp:15
@ Sustain
Definition sample_stream_scheduler.hpp:17
@ Attack
Definition sample_stream_scheduler.hpp:16
LoopValidationResult validate_loop_region(const LoopRegion &region, std::uint64_t source_frames, std::uint64_t min_loop_frames=1) noexcept
constexpr std::size_t kSampleStreamLoopMaxPageDemands
Definition sample_stream_loop_voice_reader.hpp:20
@ Forward
Definition loop_types.hpp:9
@ Reverse
Definition loop_types.hpp:10
@ InvalidPreloadContract
Definition sample_stream_voice_reader.hpp:31
@ ServiceStarvation
Definition sample_stream_voice_reader.hpp:30
@ StaleGeneration
Definition sample_stream_voice_reader.hpp:32
@ NormalEndOfSource
Definition sample_stream_voice_reader.hpp:33
@ None
Definition sample_stream_voice_reader.hpp:29
@ Enqueued
Definition sample_stream_service.hpp:77
constexpr SampleStarvationEnvelopeConfig kDefaultSampleStarvationEnvelopeConfig
Definition sample_starvation_envelope.hpp:21
SampleStarvationMode
Definition sample_starvation_envelope.hpp:9
SampleInterpolationPolicy
Definition sample_interpolation.hpp:21
ResolvedSampleInterpolationPosition resolve_sample_interpolation_position(double normalized_position, const PreparedSampleInterpolation &interpolation) noexcept
Definition sample_interpolation.hpp:210
constexpr std::uint32_t kMaximumSampleInterpolationTaps
Definition sample_interpolation.hpp:19
SampleStreamVoiceSupply
Definition sample_stream_voice_reader.hpp:20
@ EndOfSource
Definition sample_stream_voice_reader.hpp:23
@ Starved
Definition sample_stream_voice_reader.hpp:22
@ StaleGeneration
Definition sample_stream_voice_reader.hpp:24
@ Ready
Definition sample_stream_voice_reader.hpp:21
@ InvalidContract
Definition sample_stream_voice_reader.hpp:25
constexpr SampleInterpolationPolicy sample_interpolation_policy(LoopInterpolationMode mode) noexcept
Definition sample_interpolation.hpp:244
@ index
Definition gpu_pass.hpp:140
std::uint64_t read(std::span< const std::uint8_t > b, std::size_t p, unsigned n) noexcept
Definition spectral_frame_codec.hpp:111
@ u
Definition formant_filter_bank.hpp:20
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
Definition loop_types.hpp:42
bool ok
Definition loop_types.hpp:63
Definition sample_interpolation.hpp:148
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
Definition sample_starvation_envelope.hpp:16
Definition sample_starvation_envelope.hpp:26
Definition sample_stream_consumption.hpp:62
Definition sample_stream_loop_voice_reader.hpp:22
SampleStreamSourceToken source
Definition sample_stream_loop_voice_reader.hpp:24
std::uint32_t demand_count
Definition sample_stream_loop_voice_reader.hpp:33
std::array< SampleStreamPageView, kSampleStreamLoopMaxPageDemands > ready_pages
Definition sample_stream_loop_voice_reader.hpp:32
SampleStreamVoiceSupply supply
Definition sample_stream_loop_voice_reader.hpp:29
SampleAssetToken asset
Definition sample_stream_loop_voice_reader.hpp:23
PreparedSampleInterpolation interpolation
Definition sample_stream_loop_voice_reader.hpp:30
std::array< SampleStreamPageDemand, kSampleStreamLoopMaxPageDemands > demands
Definition sample_stream_loop_voice_reader.hpp:31
std::uint64_t timeline_serial
Definition sample_stream_loop_voice_reader.hpp:27
LoopPlaybackCursor start_cursor
Definition sample_stream_loop_voice_reader.hpp:25
std::uint32_t output_frames
Definition sample_stream_loop_voice_reader.hpp:28
LoopPlaybackCursor end_cursor
Definition sample_stream_loop_voice_reader.hpp:26
Definition sample_stream_window.hpp:35
Definition sample_stream_service.hpp:23
Definition sample_stream_service.hpp:18
std::uint64_t source_generation
Definition sample_stream_service.hpp:20
std::uint64_t source_id
Definition sample_stream_service.hpp:19
Definition sample_stream_voice_reader.hpp:49
SampleStreamVoiceOutcomeClass outcome
Definition sample_stream_voice_reader.hpp:51
SampleStreamVoiceSupply supply
Definition sample_stream_voice_reader.hpp:50
std::uint32_t ready_output_frames
Definition sample_stream_voice_reader.hpp:53
Definition sample_stream_voice_reader.hpp:56
std::uint32_t enqueued
Definition sample_stream_voice_reader.hpp:58
std::uint32_t next_demand_index
Definition sample_stream_voice_reader.hpp:59
bool complete
Definition sample_stream_voice_reader.hpp:60
std::uint32_t demand_count
Definition sample_stream_voice_reader.hpp:57