Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sample_heritage_engine.hpp
Go to the documentation of this file.
1#pragma once
2
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12#include <cstddef>
13#include <cstdint>
14#include <limits>
15#include <string>
16#include <type_traits>
17#include <utility>
18#include <variant>
19#include <vector>
20
21namespace pulp::audio {
22
23namespace detail {
24
25inline std::uint64_t next_random(std::uint64_t& state) noexcept {
26 state ^= state >> 12;
27 state ^= state << 25;
28 state ^= state >> 27;
29 return state * UINT64_C(2685821657736338717);
30}
31
32inline float bipolar_random(std::uint64_t& state) noexcept {
33 const auto bits = static_cast<std::uint32_t>(next_random(state) >> 40);
34 return static_cast<float>(bits) * (2.0f / 16777215.0f) - 1.0f;
35}
36
37} // namespace detail
38
48
51 std::size_t channel_count = 0;
52 std::size_t maximum_output_frames = 0;
53 // The view is copied during prepare. Its issuing bank must retain the
54 // coefficient storage until the prepared engine is released.
58};
59
66
69 std::uint64_t prepare_epoch = 0;
70 std::uint64_t sequence = 0;
71 std::size_t output_frames = 0;
72 std::size_t machine_frames = 0;
73 std::size_t pre_live_machine_frames = 0;
74 std::size_t input_frames = 0;
77
78 bool valid() const noexcept { return status == SampleHeritagePlanStatus::Ok; }
79};
80
90
92public:
95 bool prepare(const SampleHeritagePreparedProfile& profile) noexcept {
96 return prepare_impl(profile, 0, 0, false, nullptr) ==
98 }
99
101 const SampleHeritagePrepareConfig& config) noexcept {
102 return prepare_impl(config.profile, config.channel_count,
103 config.maximum_output_frames, true,
104 config.external_sinc_bank,
105 config.maximum_runtime_clock_factor,
106 config.maximum_note_pitch_factor);
107 }
108
109 void release() noexcept {
110 profile_ = {};
111 stages_ = {};
112 input_src_.release();
113 return_src_.release();
114 live_cyclic_.release();
115 voice_dsp_.release();
116 sinc_bank_.release();
117 std::vector<float>().swap(machine_scratch_);
118 std::vector<float>().swap(pre_live_scratch_);
119 std::vector<float>().swap(dynamic_identity_delay_);
120 machine_ptrs_.fill(nullptr);
121 machine_const_ptrs_.fill(nullptr);
122 pre_live_ptrs_.fill(nullptr);
123 pre_live_const_ptrs_.fill(nullptr);
124 prepared_ = false;
125 exact_prepared_ = false;
126 all_bypassed_ = false;
127 typed_voice_ = false;
128 live_cyclic_active_ = false;
129 runtime_dynamic_clock_ = false;
130 dynamic_identity_exact_ = false;
131 dynamic_identity_epoch_ = false;
132 dynamic_identity_delay_frames_ = 0;
133 dynamic_identity_delay_head_ = 0;
134 dynamic_return_latency_output_frames_ = 0.0;
135 dynamic_input_latency_machine_frames_ = 0.0;
136 dynamic_return_src_latency_output_frames_ = 0.0;
137 input_identity_ = true;
138 return_identity_ = true;
139 channel_count_ = 0;
140 maximum_output_frames_ = 0;
141 maximum_input_frames_ = 0;
142 maximum_machine_frames_ = 0;
143 maximum_pre_live_machine_frames_ = 0;
144 maximum_runtime_clock_factor_ = 1.0;
145 maximum_artifact_clock_factor_ = 1.0;
146 maximum_note_pitch_factor_ = 1.0;
147 machine_sample_rate_ = 0.0;
148 clock_ratio_ = 1.0;
149 clocked_sample_rate_ = 0.0;
150 input_ratio_ = 1.0;
151 return_ratio_ = 1.0;
152 process_sequence_ = 0;
153 last_valid_output_frames_ = 0;
154 ++prepare_epoch_;
155 if (prepare_epoch_ == 0) ++prepare_epoch_;
156 }
157
158 void reset() noexcept {
159 if (!prepared_) return;
160 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
161 auto& runtime = stages_[index];
162 runtime.filter_state.fill(0.0f);
163 runtime.hold_value.fill(0.0f);
164 runtime.hold_phase.fill(0);
165 std::visit([&](const auto& stage) noexcept {
166 using Stage = std::decay_t<decltype(stage)>;
167 if constexpr (std::is_same_v<Stage, SampleHeritageNoiseStage> ||
168 std::is_same_v<Stage, SampleHeritageQuantizationStage>) {
169 if (stage.seed_policy ==
171 runtime.random_state = stage.seed;
172 }
173 }, runtime.spec.parameters);
174 }
175 input_src_.reset();
176 return_src_.reset();
177 live_cyclic_.reset();
178 voice_dsp_.reset();
179 std::fill(dynamic_identity_delay_.begin(),
180 dynamic_identity_delay_.end(), 0.0f);
181 dynamic_identity_delay_head_ = 0;
182 dynamic_identity_epoch_ = dynamic_identity_exact_;
183 process_sequence_ = 0;
184 last_valid_output_frames_ = 0;
185 ++prepare_epoch_;
186 if (prepare_epoch_ == 0) ++prepare_epoch_;
187 }
188
189 bool prepared() const noexcept { return prepared_; }
190 bool exact_processing_prepared() const noexcept { return exact_prepared_; }
191 std::size_t maximum_input_frames() const noexcept {
192 return maximum_input_frames_;
193 }
194 std::size_t maximum_machine_frames() const noexcept {
195 return maximum_machine_frames_;
196 }
197 std::size_t maximum_pre_live_machine_frames() const noexcept {
198 return maximum_pre_live_machine_frames_;
199 }
201 return live_cyclic_.resources();
202 }
203 double machine_sample_rate() const noexcept { return machine_sample_rate_; }
204 double clock_ratio() const noexcept { return clock_ratio_; }
205 double clocked_sample_rate() const noexcept { return clocked_sample_rate_; }
206
207 double latency_output_frames() const noexcept {
208 return latency_output_frames(1.0);
209 }
210
211 double latency_output_frames(double runtime_clock_multiplier) const noexcept {
212 if (!exact_prepared_) return 0.0;
213 if (!valid_runtime_clock_multiplier(runtime_clock_multiplier)) return 0.0;
214 if (all_bypassed_ && !runtime_dynamic_clock_) return 0.0;
215 if (runtime_dynamic_clock_)
216 return dynamic_return_latency_output_frames_;
217 constexpr double half =
218 static_cast<double>(kHighQualitySampleSincHalfWidth);
219 const auto input_latency = input_identity_ ? 0.0 : half;
220 const auto effective_return_ratio = return_ratio_ *
221 runtime_clock_multiplier;
222 const auto return_latency =
223 return_identity_ ? 0.0 : half / effective_return_ratio;
224 return input_latency + return_latency;
225 }
226
229 std::uint64_t tail_output_frames() const noexcept {
230 if (!exact_prepared_ || (all_bypassed_ && !runtime_dynamic_clock_))
231 return 0;
232
233 long double machine_frames = typed_voice_
234 ? static_cast<long double>(voice_dsp_.tail_machine_domain_frames())
235 : 0.0L;
236 const auto host_frames = typed_voice_
237 ? static_cast<long double>(voice_dsp_.tail_host_frames())
238 : 0.0L;
239 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
240 const auto& runtime = stages_[index];
241 if (runtime.spec.bypass)
242 continue;
243 std::visit(
244 [&](const auto& stage) noexcept {
245 using Stage = std::decay_t<decltype(stage)>;
246 if constexpr (std::is_same_v<Stage, SampleHeritageDacHoldStage>) {
247 machine_frames += stage.hold_samples;
248 } else if constexpr (std::is_same_v<Stage,
250 const auto pole = static_cast<long double>(runtime.filter_pole);
251 if (pole > 0.0L && pole < 1.0L) {
252 constexpr long double reference_peak = 16.0L;
253 machine_frames += std::ceil(
254 std::log(static_cast<long double>(
256 reference_peak) /
257 std::log(pole));
258 }
259 }
260 },
261 runtime.spec.parameters);
262 }
263 machine_frames =
264 std::min(machine_frames, std::ceil(static_cast<long double>(machine_sample_rate_) *
266 if (live_cyclic_active_)
267 machine_frames += static_cast<long double>(live_cyclic_.remaining_output_frames());
268 if (live_cyclic_active_) {
269 machine_frames +=
270 static_cast<long double>(input_src_.remaining_valid_output_frames()) /
271 static_cast<long double>(live_cyclic_.source_frames_per_output_frame());
272 }
273
274 const auto required_frames =
275 static_cast<long double>(latency_output_frames()) +
276 machine_frames /
277 static_cast<long double>(return_ratio_ /
278 maximum_runtime_clock_factor_) +
279 host_frames;
280 const auto output_frames = std::ceil(required_frames);
281 if (!std::isfinite(output_frames) || output_frames <= 0.0L)
282 return output_frames <= 0.0L ? 0 : std::numeric_limits<std::uint64_t>::max();
283 if (output_frames >= static_cast<long double>(std::numeric_limits<std::uint64_t>::max()))
284 return std::numeric_limits<std::uint64_t>::max();
285 return static_cast<std::uint64_t>(output_frames);
286 }
287
290 bool process(BufferView<float> buffer) noexcept {
291 if (!prepared_ || exact_prepared_ || !input_identity_ || !return_identity_ ||
292 buffer.num_channels() == 0 ||
293 buffer.num_channels() > kSampleHeritageMaximumChannels) {
294 return false;
295 }
296 process_machine_stages(buffer);
297 if (typed_voice_)
298 voice_dsp_.process_host_frame(buffer);
299 return true;
300 }
301
302 SampleHeritageProcessPlan plan_exact(std::size_t output_frames) const noexcept {
303 return plan_exact(output_frames, 1.0);
304 }
305
307 std::size_t output_frames,
308 double runtime_clock_multiplier) const noexcept {
309 return plan_exact(output_frames, runtime_clock_multiplier, 1.0);
310 }
311
313 std::size_t output_frames,
314 double runtime_clock_multiplier,
315 double note_pitch_multiplier) const noexcept {
317 result.prepare_epoch = prepare_epoch_;
318 result.sequence = process_sequence_;
319 result.output_frames = output_frames;
320 result.runtime_clock_multiplier = runtime_clock_multiplier;
321 result.note_pitch_multiplier = note_pitch_multiplier;
322 if (!exact_prepared_) return result;
323 if (!valid_runtime_rate_multipliers(runtime_clock_multiplier,
324 note_pitch_multiplier)) {
326 return result;
327 }
328 if (output_frames > maximum_output_frames_) {
330 return result;
331 }
332 if (all_bypassed_ && !runtime_dynamic_clock_) {
334 result.input_frames = output_frames;
335 return result;
336 }
337 if (dynamic_identity_epoch_ && runtime_clock_multiplier == 1.0 &&
338 note_pitch_multiplier == 1.0) {
340 result.input_frames = output_frames;
341 return result;
342 }
343 const auto return_plan = return_src_.plan(
344 output_frames, return_ratio_ * runtime_clock_multiplier *
345 note_pitch_multiplier);
346 if (!return_plan.valid()) {
348 return result;
349 }
350 result.machine_frames = return_plan.input_frames;
352 if (live_cyclic_active_) {
353 const auto live_plan = live_cyclic_.plan(result.machine_frames);
354 if (!live_plan.valid()) {
356 return result;
357 }
358 result.pre_live_machine_frames = live_plan.input_frames;
359 }
360 const auto input_plan = input_src_.plan(
362 input_ratio_ / runtime_clock_multiplier);
363 if (!input_plan.valid() || result.machine_frames > maximum_machine_frames_ ||
364 result.pre_live_machine_frames > maximum_pre_live_machine_frames_ ||
365 input_plan.input_frames > maximum_input_frames_) {
367 return result;
368 }
369 result.input_frames = input_plan.input_frames;
371 return result;
372 }
373
375 const SampleHeritageProcessPlan& plan,
376 const BufferView<const float>& input,
377 BufferView<float> output) noexcept {
378 return process_exact_impl(plan, input, output, false,
379 input.num_samples(), false);
380 }
381
383 const SampleHeritageProcessPlan& plan,
384 const BufferView<const float>& input,
385 BufferView<float> output,
386 std::size_t valid_input_frames,
387 bool end_of_source) noexcept {
388 return process_exact_impl(plan, input, output, false,
389 valid_input_frames, end_of_source);
390 }
391
392 std::size_t last_valid_output_frames() const noexcept {
393 return last_valid_output_frames_;
394 }
395
399 const SampleHeritageProcessPlan& plan,
400 const BufferView<const float>& input,
401 BufferView<float> output) noexcept {
402 for (std::size_t channel = 0; channel < input.num_channels(); ++channel)
403 for (const auto sample : input.channel(channel))
404 if (sample != 0.0f)
406 return process_exact_impl(plan, input, output, true, 0, true);
407 }
408
409private:
410 SampleHeritageProcessStatus process_exact_impl(
411 const SampleHeritageProcessPlan& plan,
412 const BufferView<const float>& input,
413 BufferView<float> output,
414 bool draining_tail,
415 std::size_t valid_input_frames,
416 bool end_of_source) noexcept {
417 if (!exact_prepared_) return SampleHeritageProcessStatus::NotPrepared;
418 const auto expected = plan_exact(output.num_samples(),
419 plan.runtime_clock_multiplier,
420 plan.note_pitch_multiplier);
421 if (!plan.valid() || !expected.valid() ||
422 plan.prepare_epoch != expected.prepare_epoch ||
423 plan.sequence != expected.sequence ||
424 plan.output_frames != expected.output_frames ||
425 plan.machine_frames != expected.machine_frames ||
426 plan.pre_live_machine_frames != expected.pre_live_machine_frames ||
427 plan.input_frames != expected.input_frames ||
428 plan.runtime_clock_multiplier != expected.runtime_clock_multiplier ||
429 plan.note_pitch_multiplier != expected.note_pitch_multiplier) {
431 }
432 if (input.num_channels() != channel_count_ ||
433 output.num_channels() != channel_count_) {
435 }
436 if (input.num_samples() != plan.input_frames)
438 if (valid_input_frames > input.num_samples())
440 last_valid_output_frames_ = output.num_samples();
441
442 if (all_bypassed_ && !runtime_dynamic_clock_) {
443 for (std::size_t channel = 0; channel < channel_count_; ++channel)
444 std::copy(input.channel(channel).begin(), input.channel(channel).end(),
445 output.channel(channel).begin());
446 ++process_sequence_;
447 if (end_of_source)
448 last_valid_output_frames_ =
449 std::min(output.num_samples(), valid_input_frames);
451 }
452 if (dynamic_identity_epoch_ &&
453 plan.runtime_clock_multiplier == 1.0 &&
454 plan.note_pitch_multiplier == 1.0) {
455 for (std::size_t channel = 0; channel < channel_count_; ++channel)
456 std::copy(input.channel(channel).begin(),
457 input.channel(channel).end(),
458 output.channel(channel).begin());
459 process_dynamic_latency_delay(output,
460 dynamic_identity_delay_frames_);
461 if (end_of_source) {
462 const auto valid =
463 std::min(output.num_samples(), valid_input_frames);
464 last_valid_output_frames_ = valid + std::min(
465 output.num_samples() - valid,
466 dynamic_identity_delay_frames_);
467 }
468 ++process_sequence_;
470 }
471
472 auto* prepared_ptrs = live_cyclic_active_ ? pre_live_ptrs_.data() : machine_ptrs_.data();
473 BufferView<float> prepared_machine(prepared_ptrs, channel_count_,
474 plan.pre_live_machine_frames);
475 const auto effective_input_ratio =
476 input_ratio_ / plan.runtime_clock_multiplier;
477 const auto input_status = live_cyclic_active_
478 ? input_src_.process_source(input, prepared_machine,
479 valid_input_frames, end_of_source,
480 effective_input_ratio)
481 : input_src_.process(input, prepared_machine,
482 effective_input_ratio);
483 if (input_status != SampleHeritageSrcStatus::Ok)
485 BufferView<float> machine(machine_ptrs_.data(), channel_count_, plan.machine_frames);
486 if (live_cyclic_active_) {
487 const auto valid_pre_live_frames = input_src_.last_valid_output_frames();
488 BufferView<float> valid_pre_live(pre_live_ptrs_.data(), channel_count_,
489 valid_pre_live_frames);
490 voice_dsp_.process_before_live(valid_pre_live, draining_tail);
491 if (valid_pre_live_frames < plan.pre_live_machine_frames) {
492 for (std::size_t channel = 0; channel < channel_count_; ++channel)
493 std::fill(pre_live_ptrs_[channel] + valid_pre_live_frames,
494 pre_live_ptrs_[channel] + plan.pre_live_machine_frames, 0.0f);
495 }
496 BufferView<const float> live_input(pre_live_const_ptrs_.data(), channel_count_,
497 plan.pre_live_machine_frames);
498 const auto live_end_of_source = end_of_source && input_src_.source_drained();
499 if (live_cyclic_.process(live_input, machine, valid_pre_live_frames,
500 live_end_of_source) != SampleHeritageLiveCyclicStatus::Ok)
502 if (live_end_of_source) {
503 const auto valid_machine = live_cyclic_.last_valid_output_frames();
504 const auto converted = std::ceil(static_cast<double>(valid_machine) /
505 (return_ratio_ * plan.runtime_clock_multiplier *
506 plan.note_pitch_multiplier));
507 last_valid_output_frames_ =
508 static_cast<std::size_t>(std::min<double>(output.num_samples(), converted));
509 }
510 voice_dsp_.process_after_live(machine, draining_tail);
511 } else {
512 process_machine_stages(machine, draining_tail);
513 }
514 BufferView<const float> machine_input(machine_const_ptrs_.data(), channel_count_,
515 plan.machine_frames);
516 if (return_src_.process(machine_input, output,
517 return_ratio_ * plan.runtime_clock_multiplier *
518 plan.note_pitch_multiplier) !=
521 if (typed_voice_)
522 voice_dsp_.process_host_frame(output, draining_tail);
523 if (runtime_dynamic_clock_) {
524 const auto effective_return_ratio = return_ratio_ *
525 plan.runtime_clock_multiplier * plan.note_pitch_multiplier;
526 const auto active_input_latency =
527 input_identity_ && maximum_artifact_clock_factor_ == 1.0
528 ? 0.0
529 : dynamic_input_latency_machine_frames_;
530 const auto raw_latency = static_cast<std::size_t>(std::ceil(
531 active_input_latency /
532 effective_return_ratio)) +
533 static_cast<std::size_t>(
534 dynamic_return_src_latency_output_frames_);
535 const auto correction = raw_latency < dynamic_identity_delay_frames_
536 ? dynamic_identity_delay_frames_ - raw_latency
537 : 0;
538 process_dynamic_latency_delay(output, correction);
539 if (end_of_source &&
540 last_valid_output_frames_ < output.num_samples())
541 last_valid_output_frames_ += std::min(
542 output.num_samples() - last_valid_output_frames_,
543 correction);
544 if (plan.runtime_clock_multiplier != 1.0 ||
545 plan.note_pitch_multiplier != 1.0)
546 dynamic_identity_epoch_ = false;
547 }
548 ++process_sequence_;
550 }
551
552public:
553
559 if (!prepared_) return result;
560 result.state.profile_schema_version = profile_.schema_version;
561 std::copy(profile_.profile_id.begin(), profile_.profile_id.end(),
562 result.state.profile_id.begin());
563 result.state.profile_digest = profile_.profile_digest;
564 if (typed_voice_) {
565 for (std::size_t index = 0; index < profile_.voice_count; ++index) {
566 if (voice_dsp_.converter_continues_state(index)) {
567 auto& saved = result.state.rng_states[
568 result.state.rng_state_count++];
569 saved.stage_index = static_cast<std::uint8_t>(index);
570 saved.stage_type =
572 saved.random_state = voice_dsp_.converter_random_state(index);
573 continue;
574 }
575 const auto* live =
576 std::get_if<SampleHeritageVoiceLiveCyclicStretchBlock>(
577 &profile_.voice[index].parameters);
578 if (live == nullptr || profile_.voice[index].bypass ||
579 live->seed_policy !=
581 continue;
582 const auto continuation =
583 live_cyclic_.capture_next_cycle_rng_continuation();
584 if (continuation.next_cycle_index ==
585 std::numeric_limits<std::uint64_t>::max())
586 return {};
587 auto& saved = result.state.rng_states[
588 result.state.rng_state_count++];
589 saved.stage_index = static_cast<std::uint8_t>(index);
591 saved.random_state = continuation.next_cycle_index + 1;
592 }
594 return result;
595 }
596 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
597 const auto& runtime = stages_[index];
598 std::visit(
599 [&](const auto& stage) noexcept {
600 using Stage = std::decay_t<decltype(stage)>;
601 if constexpr (std::is_same_v<Stage,
603 std::is_same_v<Stage,
605 if (stage.seed_policy !=
607 return;
608 auto& saved = result.state.rng_states[
609 result.state.rng_state_count++];
610 saved.stage_index = static_cast<std::uint8_t>(index);
611 saved.stage_type =
612 std::is_same_v<Stage, SampleHeritageQuantizationStage>
615 saved.random_state = runtime.random_state;
616 }
617 },
618 runtime.spec.parameters);
619 }
621 return result;
622 }
623
627 const SampleHeritageRuntimeState& state) noexcept {
631 if (state.profile_schema_version != profile_.schema_version ||
632 state.bound_profile_id() != profile_.id() ||
633 state.profile_digest_version != kSampleHeritageProfileDigestVersion ||
634 state.profile_digest != profile_.profile_digest)
636
637 if (typed_voice_) {
638 std::size_t expected_count = 0;
639 for (std::size_t index = 0; index < profile_.voice_count; ++index) {
640 const auto* live =
641 std::get_if<SampleHeritageVoiceLiveCyclicStretchBlock>(
642 &profile_.voice[index].parameters);
643 const auto converter =
644 voice_dsp_.converter_continues_state(index);
645 const auto cyclic = live != nullptr &&
646 !profile_.voice[index].bypass &&
647 live->seed_policy ==
649 if (!converter && !cyclic) continue;
650 if (expected_count >= state.rng_state_count)
652 const auto& saved = state.rng_states[expected_count];
653 if (saved.stage_index != index ||
654 saved.stage_type != (converter
657 saved.random_state == 0)
659 ++expected_count;
660 }
661 if (expected_count != state.rng_state_count ||
662 state.rng_state_count > state.rng_states.size())
664 reset();
665 for (std::size_t index = 0; index < state.rng_state_count; ++index) {
666 const auto& saved = state.rng_states[index];
667 if (saved.stage_type ==
669 if (!voice_dsp_.restore_converter_random_state(
670 saved.stage_index, saved.random_state))
672 } else {
673 const auto* live =
674 std::get_if<SampleHeritageVoiceLiveCyclicStretchBlock>(
675 &profile_.voice[saved.stage_index].parameters);
676 if (live == nullptr || live->seed == 0)
678 live_cyclic_.reset_with_rng_continuation(
679 {live->seed, saved.random_state - 1});
680 }
681 }
683 }
684
685 std::size_t expected_count = 0;
686 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
687 const auto expected = std::visit(
688 [](const auto& stage) noexcept
689 -> std::pair<bool, SampleHeritageRuntimeRngStageType> {
690 using Stage = std::decay_t<decltype(stage)>;
691 if constexpr (std::is_same_v<Stage,
693 return {stage.seed_policy ==
696 if constexpr (std::is_same_v<Stage, SampleHeritageNoiseStage>)
697 return {stage.seed_policy ==
701 },
702 stages_[index].spec.parameters);
703 if (!expected.first) continue;
704 if (expected_count >= state.rng_state_count)
706 const auto& saved = state.rng_states[expected_count];
707 if (saved.stage_index != index || saved.stage_type != expected.second)
709 if (saved.random_state == 0)
711 ++expected_count;
712 }
713 if (expected_count != state.rng_state_count ||
714 state.rng_state_count > state.rng_states.size())
716
717 reset();
718 for (std::size_t index = 0; index < state.rng_state_count; ++index) {
719 const auto& saved = state.rng_states[index];
720 stages_[saved.stage_index].random_state = saved.random_state;
721 }
723 }
724
725private:
726 struct RuntimeStage {
728 std::uint64_t random_state = 0;
729 float filter_pole = 0.0f;
730 std::array<float, kSampleHeritageMaximumChannels> filter_state{};
731 std::array<float, kSampleHeritageMaximumChannels> hold_value{};
732 std::array<std::uint32_t, kSampleHeritageMaximumChannels> hold_phase{};
733 };
734
735 SampleHeritagePrepareStatus prepare_impl(
736 const SampleHeritagePreparedProfile& profile,
737 std::size_t channel_count,
738 std::size_t maximum_output_frames,
739 bool exact,
740 const SampleSincKernelBankView* external_sinc_bank,
741 double maximum_runtime_clock_factor = 1.0) noexcept {
742 return prepare_impl(profile, channel_count, maximum_output_frames,
743 exact, external_sinc_bank,
744 maximum_runtime_clock_factor, 1.0);
745 }
746
747 SampleHeritagePrepareStatus prepare_impl(
748 const SampleHeritagePreparedProfile& profile,
749 std::size_t channel_count,
750 std::size_t maximum_output_frames,
751 bool exact,
752 const SampleSincKernelBankView* external_sinc_bank,
753 double maximum_runtime_clock_factor,
754 double maximum_note_pitch_factor) noexcept {
755 release();
756 if (profile.schema_version != kSampleHeritageProfileSchemaVersion ||
757 profile.stage_count > stages_.size() ||
758 !(profile.host_sample_rate > 0.0) ||
759 !std::isfinite(profile.host_sample_rate)) {
761 }
762 if (exact && (channel_count == 0 ||
763 channel_count > kSampleHeritageMaximumChannels ||
764 maximum_output_frames == 0 ||
765 !(maximum_runtime_clock_factor >= 1.0) ||
766 maximum_runtime_clock_factor > 64.0 ||
767 !std::isfinite(maximum_runtime_clock_factor) ||
768 !(maximum_note_pitch_factor >= 1.0) ||
769 maximum_note_pitch_factor > 64.0 ||
770 !std::isfinite(maximum_note_pitch_factor) ||
771 maximum_runtime_clock_factor *
772 maximum_note_pitch_factor >
773 64.0)) {
775 }
776 try {
777 SampleHeritageProfile authoring;
778 authoring.schema_version = profile.schema_version;
779 authoring.profile_id = std::string(profile.id());
780 authoring.host_sample_rate = profile.host_sample_rate;
781 if (profile.voice_count != 0 || profile.bus_count != 0 ||
782 profile.record_commit_count != 0) {
783 authoring.voice.assign(
784 profile.voice.begin(),
785 profile.voice.begin() +
786 static_cast<std::ptrdiff_t>(profile.voice_count));
787 authoring.bus.assign(
788 profile.bus.begin(),
789 profile.bus.begin() +
790 static_cast<std::ptrdiff_t>(profile.bus_count));
791 authoring.record_commit.assign(
792 profile.record_commit.begin(),
793 profile.record_commit.begin() +
794 static_cast<std::ptrdiff_t>(profile.record_commit_count));
795 } else {
796 authoring.stages.assign(
797 profile.stages.begin(),
798 profile.stages.begin() +
799 static_cast<std::ptrdiff_t>(profile.stage_count));
800 }
801 const auto validated = validate_sample_heritage_profile(authoring);
802 if (!validated.valid() ||
803 validated.profile.profile_digest != profile.profile_digest)
805 profile_ = validated.profile;
806 } catch (...) {
808 }
809
810 machine_sample_rate_ = profile_.host_sample_rate;
811 clock_ratio_ = 1.0;
812 all_bypassed_ = true;
813 typed_voice_ = profile_.voice_count != 0;
814 if (typed_voice_) {
815 for (std::size_t index = 0; index < profile_.voice_count; ++index) {
816 const auto& spec = profile_.voice[index];
817 all_bypassed_ = all_bypassed_ && spec.bypass;
818 if (spec.bypass) continue;
819 if (const auto* machine =
820 std::get_if<SampleHeritageVoiceMachineDomainBlock>(
821 &spec.parameters))
822 machine_sample_rate_ = machine->sample_rate;
823 else if (const auto* clock =
824 std::get_if<SampleHeritageVoiceClockBlock>(
825 &spec.parameters))
826 clock_ratio_ = clock->ratio;
827 }
828 } else {
829 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
830 const auto& spec = profile_.stages[index];
831 all_bypassed_ = all_bypassed_ && spec.bypass;
832 if (spec.bypass) continue;
833 if (const auto* machine =
834 std::get_if<SampleHeritageMachineDomainStage>(
835 &spec.parameters))
836 machine_sample_rate_ = machine->sample_rate;
837 else if (const auto* clock =
838 std::get_if<SampleHeritageClockPitchStage>(
839 &spec.parameters))
840 clock_ratio_ = clock->ratio;
841 }
842 }
843 clocked_sample_rate_ = machine_sample_rate_ * clock_ratio_;
844 input_ratio_ = profile_.host_sample_rate / clocked_sample_rate_;
845 return_ratio_ = clocked_sample_rate_ / profile_.host_sample_rate;
846 input_identity_ = clocked_sample_rate_ == profile_.host_sample_rate;
847 return_identity_ = clocked_sample_rate_ == profile_.host_sample_rate;
848 if ((!input_identity_ && input_ratio_ >
850 (!return_identity_ && return_ratio_ >
852 !std::isfinite(input_ratio_) || !std::isfinite(return_ratio_)) {
853 release();
855 }
856 if (!exact && (!input_identity_ || !return_identity_)) {
857 release();
859 }
860
861 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
862 stages_[index].spec = profile_.stages[index];
863 initialize_seed(stages_[index]);
864 if (const auto* filter =
865 std::get_if<SampleHeritageReconstructionFilterStage>(
866 &stages_[index].spec.parameters)) {
867 stages_[index].filter_pole = static_cast<float>(
868 std::exp(-2.0 * 3.14159265358979323846 * filter->cutoff_hz /
869 clocked_sample_rate_));
870 }
871 }
872 if (typed_voice_ &&
873 !voice_dsp_.prepare(profile_, machine_sample_rate_,
874 profile_.host_sample_rate)) {
875 release();
877 }
878 live_cyclic_active_ = typed_voice_ && voice_dsp_.live_stage_active();
879 if (live_cyclic_active_ && !exact) {
880 release();
882 }
883
884 if (exact) {
885 const auto maximum_combined_factor =
886 maximum_runtime_clock_factor * maximum_note_pitch_factor;
887 runtime_dynamic_clock_ = maximum_combined_factor > 1.0;
888 const auto maximum_return_ratio =
889 return_ratio_ * maximum_combined_factor;
890 const auto maximum_input_ratio =
891 input_ratio_ * maximum_runtime_clock_factor;
892 const auto maximum_ratio =
893 std::max(maximum_input_ratio, maximum_return_ratio);
894 SampleSincKernelBankView bank;
895 if (external_sinc_bank != nullptr) {
896 bank = *external_sinc_bank;
897 if (!bank.valid()) {
898 release();
900 }
901 } else if (!input_identity_ || !return_identity_ ||
902 runtime_dynamic_clock_) {
903 if (!sinc_bank_.build_dense_for_maximum_consumption(
904 maximum_ratio)) {
905 release();
907 }
908 bank = sinc_bank_.view();
909 }
910 const auto input_selection = input_identity_ &&
911 maximum_runtime_clock_factor == 1.0
912 ? SampleSincKernelSelection{}
913 : bank.select(input_ratio_);
914 const auto return_selection = return_identity_
915 ? SampleSincKernelSelection{}
916 : bank.select(return_ratio_);
917 if ((!(input_identity_ && maximum_runtime_clock_factor == 1.0) &&
918 !input_selection.valid()) ||
919 (!return_identity_ && !return_selection.valid())) {
920 release();
922 }
923 const auto input_status = maximum_runtime_clock_factor == 1.0
924 ? input_src_.prepare(input_ratio_, channel_count,
925 input_selection, input_identity_)
926 : input_src_.prepare_variable(
927 input_ratio_,
928 input_ratio_ / maximum_runtime_clock_factor,
929 maximum_input_ratio, channel_count, bank);
930 const auto return_status = maximum_combined_factor == 1.0
931 ? return_src_.prepare(return_ratio_, channel_count,
932 return_selection, return_identity_)
933 : return_src_.prepare_variable(return_ratio_,
934 return_ratio_ /
935 maximum_combined_factor,
936 maximum_return_ratio,
937 channel_count, bank);
938 if (input_status != SampleHeritageSrcStatus::Ok ||
939 return_status != SampleHeritageSrcStatus::Ok) {
940 const auto allocation_failed =
943 const auto overflow =
946 release();
947 return allocation_failed
951 }
952
953 if (!checked_frame_bound(maximum_output_frames,
954 maximum_return_ratio,
955 return_identity_ &&
956 maximum_combined_factor == 1.0,
957 maximum_machine_frames_) ||
958 (channel_count != 0 && maximum_machine_frames_ >
959 std::numeric_limits<std::size_t>::max() / channel_count)) {
960 release();
962 }
963 maximum_pre_live_machine_frames_ = maximum_machine_frames_;
964 if (live_cyclic_active_) {
965 const auto live_index = voice_dsp_.live_stage_index();
966 const auto* block =
967 std::get_if<SampleHeritageVoiceLiveCyclicStretchBlock>(
968 &profile_.voice[live_index].parameters);
969 std::size_t cycle_samples = 0;
970 std::size_t splice_samples = 0;
971 if (block == nullptr ||
972 !milliseconds_to_machine_frames(block->cycle_ms,
973 machine_sample_rate_,
974 cycle_samples) ||
975 !milliseconds_to_machine_frames(block->splice_ms,
976 machine_sample_rate_,
977 splice_samples, true) ||
978 splice_samples > cycle_samples / 2) {
979 release();
981 }
982 const auto divisions = block->shuffle_divisions == 0
983 ? std::size_t{1}
984 : static_cast<std::size_t>(block->shuffle_divisions);
985 const SampleHeritageLiveCyclicConfig live_config{
986 .factor = block->factor,
987 .cycle_samples = cycle_samples,
988 .crossfade_samples = splice_samples,
989 .shuffle_divisions = divisions,
990 .linked_channels = block->stereo_link,
991 .seed = block->seed,
992 .shuffle = block->shuffle_divisions == 0
995 .max_block_samples = maximum_machine_frames_,
996 .channel_count = channel_count,
997 .pitch_mode = block->pitch_mode,
998 .tempo_lock = block->tempo_lock};
999 const auto live_status = live_cyclic_.prepare(live_config);
1000 if (live_status != SampleHeritageLiveCyclicStatus::Ok) {
1001 release();
1002 return live_status ==
1006 }
1007 maximum_pre_live_machine_frames_ =
1008 live_cyclic_.resources().maximum_input_frames;
1009 if (maximum_pre_live_machine_frames_ == 0) {
1010 release();
1012 }
1013 }
1014 if (!checked_frame_bound(maximum_pre_live_machine_frames_,
1015 input_ratio_ /
1016 maximum_runtime_clock_factor,
1017 input_identity_ &&
1018 maximum_combined_factor == 1.0,
1019 maximum_input_frames_) ||
1020 (channel_count != 0 && maximum_pre_live_machine_frames_ >
1021 std::numeric_limits<std::size_t>::max() / channel_count)) {
1022 release();
1024 }
1025 try {
1026 machine_scratch_.assign(channel_count * maximum_machine_frames_, 0.0f);
1027 if (live_cyclic_active_)
1028 pre_live_scratch_.assign(
1029 channel_count * maximum_pre_live_machine_frames_, 0.0f);
1030 dynamic_identity_exact_ = runtime_dynamic_clock_ &&
1031 runtime_clock_identity_only() &&
1032 input_identity_ && return_ratio_ == 1.0;
1033 dynamic_input_latency_machine_frames_ = runtime_dynamic_clock_
1034 ? std::ceil(
1035 static_cast<double>(kHighQualitySampleSincHalfWidth) /
1036 (input_ratio_ / maximum_runtime_clock_factor))
1037 : 0.0;
1038 dynamic_return_src_latency_output_frames_ = runtime_dynamic_clock_
1039 ? std::ceil(
1040 static_cast<double>(kHighQualitySampleSincHalfWidth) /
1041 (return_ratio_ / maximum_combined_factor))
1042 : 0.0;
1043 dynamic_return_latency_output_frames_ = runtime_dynamic_clock_
1044 ? std::ceil(dynamic_input_latency_machine_frames_ /
1045 (return_ratio_ /
1046 maximum_combined_factor)) +
1047 dynamic_return_src_latency_output_frames_
1048 : 0.0;
1049 dynamic_identity_delay_frames_ = runtime_dynamic_clock_
1050 ? static_cast<std::size_t>(
1051 dynamic_return_latency_output_frames_)
1052 : 0;
1053 dynamic_identity_delay_.assign(
1054 channel_count * dynamic_identity_delay_frames_, 0.0f);
1055 } catch (...) {
1056 release();
1058 }
1059 for (std::size_t channel = 0; channel < channel_count; ++channel) {
1060 auto* pointer = machine_scratch_.data() +
1061 channel * maximum_machine_frames_;
1062 machine_ptrs_[channel] = pointer;
1063 machine_const_ptrs_[channel] = pointer;
1064 if (live_cyclic_active_) {
1065 auto* pre_live = pre_live_scratch_.data() +
1066 channel * maximum_pre_live_machine_frames_;
1067 pre_live_ptrs_[channel] = pre_live;
1068 pre_live_const_ptrs_[channel] = pre_live;
1069 }
1070 }
1071 channel_count_ = channel_count;
1072 maximum_output_frames_ = maximum_output_frames;
1073 maximum_runtime_clock_factor_ = maximum_combined_factor;
1074 maximum_artifact_clock_factor_ =
1075 maximum_runtime_clock_factor;
1076 maximum_note_pitch_factor_ = maximum_note_pitch_factor;
1077 exact_prepared_ = true;
1078 dynamic_identity_epoch_ = dynamic_identity_exact_;
1079 }
1080
1081 prepared_ = true;
1082 process_sequence_ = 0;
1083 ++prepare_epoch_;
1084 if (prepare_epoch_ == 0) ++prepare_epoch_;
1086 }
1087
1088 static bool checked_frame_bound(std::size_t output_frames,
1089 double ratio,
1090 bool identity,
1091 std::size_t& result) noexcept {
1092 if (identity) {
1093 result = output_frames;
1094 return true;
1095 }
1096 const auto frames = std::ceil(static_cast<double>(output_frames) * ratio);
1097 if (!std::isfinite(frames) || frames < 0.0 ||
1098 frames >= static_cast<double>(std::numeric_limits<std::size_t>::max()))
1099 return false;
1100 result = static_cast<std::size_t>(frames) + 1;
1101 return result != 0;
1102 }
1103
1106 static bool milliseconds_to_machine_frames(double milliseconds,
1107 double sample_rate,
1108 std::size_t& result,
1109 bool allow_zero = false) noexcept {
1110 const auto frames = std::floor(milliseconds * sample_rate * 0.001 + 0.5);
1111 if (!std::isfinite(frames) || frames < 0.0 ||
1112 frames >= static_cast<double>(std::numeric_limits<std::size_t>::max()) ||
1113 (!allow_zero && frames < 1.0))
1114 return false;
1115 result = static_cast<std::size_t>(frames);
1116 return true;
1117 }
1118
1119 bool valid_runtime_clock_multiplier(double multiplier) const noexcept {
1120 return std::isfinite(multiplier) &&
1121 multiplier >= 1.0 / maximum_runtime_clock_factor_ &&
1122 multiplier <= maximum_runtime_clock_factor_;
1123 }
1124
1125 bool valid_runtime_rate_multipliers(double clock_multiplier,
1126 double pitch_multiplier) const noexcept {
1127 if (!(clock_multiplier > 0.0) || !std::isfinite(clock_multiplier) ||
1128 !(pitch_multiplier > 0.0) || !std::isfinite(pitch_multiplier))
1129 return false;
1130 return clock_multiplier >= 1.0 / maximum_artifact_clock_factor_ &&
1131 clock_multiplier <= maximum_artifact_clock_factor_ &&
1132 pitch_multiplier >= 1.0 / maximum_note_pitch_factor_ &&
1133 pitch_multiplier <= maximum_note_pitch_factor_;
1134 }
1135
1136 bool runtime_clock_identity_only() const noexcept {
1137 if (!typed_voice_) {
1138 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
1139 const auto& spec = profile_.stages[index];
1140 if (spec.bypass) continue;
1141 const auto supported = std::visit(
1142 [&](const auto& stage) noexcept {
1143 using Stage = std::decay_t<decltype(stage)>;
1144 if constexpr (std::is_same_v<
1145 Stage,
1146 SampleHeritageMachineDomainStage>)
1147 return stage.sample_rate == profile_.host_sample_rate;
1148 if constexpr (std::is_same_v<
1149 Stage,
1150 SampleHeritageClockPitchStage>)
1151 return stage.ratio == 1.0;
1152 return false;
1153 },
1154 spec.parameters);
1155 if (!supported) return false;
1156 }
1157 return true;
1158 }
1159 bool has_pitch_marker = false;
1160 for (std::size_t index = 0; index < profile_.voice_count; ++index) {
1161 const auto& spec = profile_.voice[index];
1162 if (spec.bypass) continue;
1163 const auto supported = std::visit(
1164 [&](const auto& block) noexcept {
1165 using Block = std::decay_t<decltype(block)>;
1166 if constexpr (std::is_same_v<
1167 Block,
1168 SampleHeritageVoiceMachineDomainBlock>)
1169 return block.sample_rate == profile_.host_sample_rate;
1170 if constexpr (std::is_same_v<Block,
1171 SampleHeritageVoiceClockBlock>)
1172 return block.ratio == 1.0;
1173 if constexpr (std::is_same_v<Block,
1174 SampleHeritageVoicePitchBlock>) {
1175 has_pitch_marker = true;
1176 return true;
1177 }
1178 return false;
1179 },
1180 spec.parameters);
1181 if (!supported) return false;
1182 }
1183 return has_pitch_marker;
1184 }
1185
1186 void process_dynamic_latency_delay(BufferView<float> output,
1187 std::size_t delay_frames) noexcept {
1188 if (dynamic_identity_delay_frames_ == 0) return;
1189 delay_frames = std::min(delay_frames, dynamic_identity_delay_frames_);
1190 for (std::size_t frame = 0; frame < output.num_samples(); ++frame) {
1191 for (std::size_t channel = 0; channel < channel_count_; ++channel) {
1192 const auto read_index =
1193 (dynamic_identity_delay_head_ +
1194 dynamic_identity_delay_frames_ - delay_frames) %
1195 dynamic_identity_delay_frames_;
1196 auto& destination = dynamic_identity_delay_[
1197 channel * dynamic_identity_delay_frames_ +
1198 dynamic_identity_delay_head_];
1199 const auto delayed = dynamic_identity_delay_[
1200 channel * dynamic_identity_delay_frames_ + read_index];
1201 destination = output.channel(channel)[frame];
1202 if (delay_frames != 0)
1203 output.channel(channel)[frame] = delayed;
1204 }
1205 dynamic_identity_delay_head_ =
1206 (dynamic_identity_delay_head_ + 1) %
1207 dynamic_identity_delay_frames_;
1208 }
1209 }
1210
1211 static void initialize_seed(RuntimeStage& runtime) noexcept {
1212 std::visit([&](const auto& stage) noexcept {
1213 using Stage = std::decay_t<decltype(stage)>;
1214 if constexpr (std::is_same_v<Stage, SampleHeritageNoiseStage> ||
1215 std::is_same_v<Stage, SampleHeritageQuantizationStage>)
1216 runtime.random_state = stage.seed;
1217 }, runtime.spec.parameters);
1218 }
1219
1220 void process_machine_stages(BufferView<float> buffer,
1221 bool draining_tail = false) noexcept {
1222 if (typed_voice_) {
1223 if (draining_tail)
1224 voice_dsp_.process_machine_tail(buffer);
1225 else
1226 voice_dsp_.process_before_live(buffer);
1227 return;
1228 }
1229 for (std::size_t index = 0; index < profile_.stage_count; ++index) {
1230 auto& runtime = stages_[index];
1231 if (runtime.spec.bypass) continue;
1232 if (draining_tail &&
1233 (std::holds_alternative<SampleHeritageQuantizationStage>(
1234 runtime.spec.parameters) ||
1235 std::holds_alternative<SampleHeritageNoiseStage>(
1236 runtime.spec.parameters)))
1237 continue;
1238 std::visit([&](const auto& stage) noexcept {
1239 process_stage(runtime, stage, buffer);
1240 }, runtime.spec.parameters);
1241 }
1242 }
1243
1244 static void process_stage(RuntimeStage&,
1245 const SampleHeritageMachineDomainStage&,
1246 BufferView<float>) noexcept {}
1247 static void process_stage(RuntimeStage&,
1248 const SampleHeritageClockPitchStage&,
1249 BufferView<float>) noexcept {}
1250
1251 static void process_stage(RuntimeStage& runtime,
1252 const SampleHeritageQuantizationStage& stage,
1253 BufferView<float> buffer) noexcept {
1254 const auto levels = static_cast<float>(std::uint32_t{1} << (stage.bit_depth - 1));
1255 const auto upper = 1.0f - 1.0f / levels;
1256 for (std::size_t frame = 0; frame < buffer.num_samples(); ++frame) {
1257 for (std::size_t channel = 0; channel < buffer.num_channels(); ++channel) {
1258 auto& sample = buffer.channel(channel)[frame];
1259 const auto dither = stage.dither_lsb == 0.0f
1260 ? 0.0f
1261 : detail::bipolar_random(runtime.random_state) * stage.dither_lsb;
1262 sample = std::round(std::clamp(sample, -1.0f, upper) * levels + dither) /
1263 levels;
1264 }
1265 }
1266 }
1267
1268 static void process_stage(RuntimeStage& runtime,
1269 const SampleHeritageDacHoldStage& stage,
1270 BufferView<float> buffer) noexcept {
1271 for (std::size_t channel = 0; channel < buffer.num_channels(); ++channel) {
1272 auto& held = runtime.hold_value[channel];
1273 auto& phase = runtime.hold_phase[channel];
1274 for (auto& sample : buffer.channel(channel)) {
1275 if (phase == 0) held = sample;
1276 sample = held;
1277 phase = (phase + 1) % stage.hold_samples;
1278 }
1279 }
1280 }
1281
1282 static void process_stage(RuntimeStage& runtime,
1283 const SampleHeritageReconstructionFilterStage&,
1284 BufferView<float> buffer) noexcept {
1285 const auto pole = runtime.filter_pole;
1286 for (std::size_t channel = 0; channel < buffer.num_channels(); ++channel) {
1287 auto previous = runtime.filter_state[channel];
1288 for (auto& sample : buffer.channel(channel)) {
1289 previous = (1.0f - pole) * sample + pole * previous;
1290 sample = previous;
1291 }
1292 runtime.filter_state[channel] = previous;
1293 }
1294 }
1295
1296 static void process_stage(RuntimeStage& runtime,
1297 const SampleHeritageNoiseStage& stage,
1298 BufferView<float> buffer) noexcept {
1299 for (std::size_t frame = 0; frame < buffer.num_samples(); ++frame) {
1300 for (std::size_t channel = 0; channel < buffer.num_channels(); ++channel) {
1301 buffer.channel(channel)[frame] +=
1302 detail::bipolar_random(runtime.random_state) * stage.amplitude;
1303 }
1304 }
1305 }
1306
1307 static void process_stage(RuntimeStage&,
1308 const SampleHeritageOutputStage& stage,
1309 BufferView<float> buffer) noexcept {
1310 for (std::size_t channel = 0; channel < buffer.num_channels(); ++channel)
1311 for (auto& sample : buffer.channel(channel)) sample *= stage.gain;
1312 }
1313
1314 SampleHeritagePreparedProfile profile_{};
1315 std::array<RuntimeStage, kSampleHeritageMaximumStages> stages_{};
1316 SampleSincKernelBank sinc_bank_;
1317 SampleHeritageCausalSrc input_src_;
1318 SampleHeritageCausalSrc return_src_;
1319 SampleHeritageLiveCyclicStretch live_cyclic_;
1320 SampleHeritageVoiceDsp voice_dsp_;
1321 std::vector<float> machine_scratch_;
1322 std::vector<float> pre_live_scratch_;
1323 std::vector<float> dynamic_identity_delay_;
1324 std::array<float*, kSampleHeritageMaximumChannels> machine_ptrs_{};
1325 std::array<const float*, kSampleHeritageMaximumChannels> machine_const_ptrs_{};
1326 std::array<float*, kSampleHeritageMaximumChannels> pre_live_ptrs_{};
1327 std::array<const float*, kSampleHeritageMaximumChannels>
1328 pre_live_const_ptrs_{};
1329 bool prepared_ = false;
1330 bool exact_prepared_ = false;
1331 bool all_bypassed_ = false;
1332 bool typed_voice_ = false;
1333 bool live_cyclic_active_ = false;
1334 bool input_identity_ = true;
1335 bool return_identity_ = true;
1336 bool runtime_dynamic_clock_ = false;
1337 bool dynamic_identity_exact_ = false;
1338 bool dynamic_identity_epoch_ = false;
1339 std::size_t channel_count_ = 0;
1340 std::size_t maximum_output_frames_ = 0;
1341 std::size_t maximum_input_frames_ = 0;
1342 std::size_t maximum_machine_frames_ = 0;
1343 std::size_t maximum_pre_live_machine_frames_ = 0;
1344 double machine_sample_rate_ = 0.0;
1345 double clock_ratio_ = 1.0;
1346 double clocked_sample_rate_ = 0.0;
1347 double input_ratio_ = 1.0;
1348 double return_ratio_ = 1.0;
1349 double maximum_runtime_clock_factor_ = 1.0;
1350 double maximum_artifact_clock_factor_ = 1.0;
1351 double maximum_note_pitch_factor_ = 1.0;
1352 double dynamic_return_latency_output_frames_ = 0.0;
1353 double dynamic_input_latency_machine_frames_ = 0.0;
1354 double dynamic_return_src_latency_output_frames_ = 0.0;
1355 std::size_t dynamic_identity_delay_frames_ = 0;
1356 std::size_t dynamic_identity_delay_head_ = 0;
1357 std::uint64_t prepare_epoch_ = 1;
1358 std::uint64_t process_sequence_ = 0;
1359 std::size_t last_valid_output_frames_ = 0;
1360};
1361
1362} // namespace pulp::audio
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Definition sample_heritage_engine.hpp:91
double latency_output_frames() const noexcept
Definition sample_heritage_engine.hpp:207
bool prepare(const SampleHeritagePreparedProfile &profile) noexcept
Compatibility preparation for fixed-rate profiles.
Definition sample_heritage_engine.hpp:95
bool process(BufferView< float > buffer) noexcept
Flat fixed-rate in-place processing for the internal engine representation when active machine and cl...
Definition sample_heritage_engine.hpp:290
double clock_ratio() const noexcept
Definition sample_heritage_engine.hpp:204
double machine_sample_rate() const noexcept
Definition sample_heritage_engine.hpp:203
SampleHeritageRuntimeStateCapture capture_runtime_state() const noexcept
Captures ContinueSerializedState RNG streams and the live shuffler's next-cycle sequence.
Definition sample_heritage_engine.hpp:557
std::uint64_t tail_output_frames() const noexcept
Host-rate process_tail_exact() frames sufficient to flush every finite SRC, hold, and filter state fo...
Definition sample_heritage_engine.hpp:229
std::size_t maximum_machine_frames() const noexcept
Definition sample_heritage_engine.hpp:194
void release() noexcept
Definition sample_heritage_engine.hpp:109
void reset() noexcept
Definition sample_heritage_engine.hpp:158
const SampleHeritageLiveCyclicResources & live_cyclic_resources() const noexcept
Definition sample_heritage_engine.hpp:200
SampleHeritageProcessStatus process_source_exact(const SampleHeritageProcessPlan &plan, const BufferView< const float > &input, BufferView< float > output, std::size_t valid_input_frames, bool end_of_source) noexcept
Definition sample_heritage_engine.hpp:382
std::size_t maximum_pre_live_machine_frames() const noexcept
Definition sample_heritage_engine.hpp:197
double latency_output_frames(double runtime_clock_multiplier) const noexcept
Definition sample_heritage_engine.hpp:211
bool exact_processing_prepared() const noexcept
Definition sample_heritage_engine.hpp:190
SampleHeritageProcessStatus process_exact(const SampleHeritageProcessPlan &plan, const BufferView< const float > &input, BufferView< float > output) noexcept
Definition sample_heritage_engine.hpp:374
SampleHeritageProcessStatus process_tail_exact(const SampleHeritageProcessPlan &plan, const BufferView< const float > &input, BufferView< float > output) noexcept
Advances finite SRC, hold, and filter memory without exciting noise or dither stages.
Definition sample_heritage_engine.hpp:398
SampleHeritageProcessPlan plan_exact(std::size_t output_frames, double runtime_clock_multiplier) const noexcept
Definition sample_heritage_engine.hpp:306
SampleHeritageProcessPlan plan_exact(std::size_t output_frames) const noexcept
Definition sample_heritage_engine.hpp:302
std::size_t maximum_input_frames() const noexcept
Definition sample_heritage_engine.hpp:191
SampleHeritageRuntimeStateStatus restore_runtime_state(const SampleHeritageRuntimeState &state) noexcept
Restores RNG-stream continuation after resetting every DSP transient; this is not sample-exact whole-...
Definition sample_heritage_engine.hpp:626
std::size_t last_valid_output_frames() const noexcept
Definition sample_heritage_engine.hpp:392
double clocked_sample_rate() const noexcept
Definition sample_heritage_engine.hpp:205
SampleHeritageProcessPlan plan_exact(std::size_t output_frames, double runtime_clock_multiplier, double note_pitch_multiplier) const noexcept
Definition sample_heritage_engine.hpp:312
SampleHeritagePrepareStatus prepare(const SampleHeritagePrepareConfig &config) noexcept
Definition sample_heritage_engine.hpp:100
bool prepared() const noexcept
Definition sample_heritage_engine.hpp:189
static constexpr float kTailSilenceThreshold
Definition sample_heritage_voice_dsp.hpp:40
static constexpr double kMaximumTailSeconds
Definition sample_heritage_voice_dsp.hpp:41
Definition impulse_response.hpp:69
std::uint64_t next_random(std::uint64_t &state) noexcept
Definition sample_heritage_engine.hpp:25
float bipolar_random(std::uint64_t &state) noexcept
Definition sample_heritage_engine.hpp:32
Definition analyzer_provider.hpp:12
@ FisherYates
Definition sample_heritage_live_cyclic.hpp:18
@ Identity
Definition sample_heritage_live_cyclic.hpp:17
SampleHeritagePlanStatus
Definition sample_heritage_engine.hpp:60
@ SizeOverflow
Definition sample_heritage_engine.hpp:64
@ NotPrepared
Definition sample_heritage_engine.hpp:62
@ OutputTooLarge
Definition sample_heritage_engine.hpp:63
@ Ok
Definition sample_heritage_engine.hpp:61
@ LiveCyclic
Definition sample_heritage_runtime_state.hpp:21
@ Quantization
Definition sample_heritage_runtime_state.hpp:19
@ Noise
Definition sample_heritage_runtime_state.hpp:20
SampleHeritageRuntimeStateStatus
Definition sample_heritage_runtime_state.hpp:95
@ ProfileMismatch
Definition sample_heritage_runtime_state.hpp:99
@ NotPrepared
Definition sample_heritage_runtime_state.hpp:97
@ InvalidRandomState
Definition sample_heritage_runtime_state.hpp:101
@ Ok
Definition sample_heritage_runtime_state.hpp:96
@ InvalidStageLayout
Definition sample_heritage_runtime_state.hpp:100
@ UnsupportedSchemaVersion
Definition sample_heritage_runtime_state.hpp:98
@ AllocationFailed
Definition finite_time_stretch.hpp:27
SampleHeritageProfileValidation validate_sample_heritage_profile(const SampleHeritageProfile &source) noexcept
Definition sample_heritage_schema.hpp:387
@ SizeOverflow
Definition sample_heritage_src.hpp:22
@ Ok
Definition sample_heritage_src.hpp:19
@ AllocationFailed
Definition sample_heritage_src.hpp:23
constexpr std::uint32_t kHighQualitySampleSincHalfWidth
Definition sample_sinc_kernel.hpp:19
constexpr double kMaximumDenseSampleSincConsumption
Definition sample_sinc_kernel.hpp:17
SampleHeritagePrepareStatus
Definition sample_heritage_engine.hpp:39
@ SizeOverflow
Definition sample_heritage_engine.hpp:44
@ KernelBuildFailed
Definition sample_heritage_engine.hpp:46
@ InvalidDimensions
Definition sample_heritage_engine.hpp:42
@ Ok
Definition sample_heritage_engine.hpp:40
@ UnsupportedRateConversion
Definition sample_heritage_engine.hpp:43
@ AllocationFailed
Definition sample_heritage_engine.hpp:45
@ InvalidProfile
Definition sample_heritage_engine.hpp:41
@ InvalidDimensions
Definition sample_heritage_bus_dsp.hpp:23
@ NotPrepared
Definition sample_heritage_bus_dsp.hpp:21
@ InvalidProfile
Definition sample_heritage_bus_dsp.hpp:22
@ Ok
Definition loop_types.hpp:34
@ RestartFromProfileSeed
Definition sample_heritage_schema.hpp:31
@ ContinueSerializedState
Definition sample_heritage_schema.hpp:32
SampleHeritageProcessStatus
Definition sample_heritage_engine.hpp:81
@ InvalidPlan
Definition sample_heritage_engine.hpp:84
@ InvalidDimensions
Definition sample_heritage_engine.hpp:85
@ NotPrepared
Definition sample_heritage_engine.hpp:83
@ InputFrameMismatch
Definition sample_heritage_engine.hpp:86
@ InternalContractFailure
Definition sample_heritage_engine.hpp:88
@ TailInputNotSilent
Definition sample_heritage_engine.hpp:87
@ Ok
Definition sample_heritage_engine.hpp:82
constexpr std::uint32_t kSampleHeritageProfileSchemaVersion
Definition sample_heritage_schema.hpp:18
constexpr std::uint32_t kSampleHeritageProfileDigestVersion
Definition sample_heritage_schema.hpp:19
@ allocation_failed
Definition analyzer_provider.hpp:70
@ Ok
Definition sample_heritage_live_cyclic.hpp:22
@ AllocationFailed
Definition sample_heritage_live_cyclic.hpp:26
constexpr std::uint32_t kSampleHeritageRuntimeStateSchemaVersion
Definition sample_heritage_runtime_state.hpp:14
constexpr std::size_t kSampleHeritageMaximumChannels
Definition sample_heritage_schema.hpp:27
@ supported
Profile registered, versions satisfied, capabilities available.
Definition preview.hpp:23
@ index
Definition gpu_pass.hpp:140
Definition generated_event_source.hpp:33
@ valid
Definition external_input.hpp:12
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
@ levels
Definition multi_channel_meter.hpp:48
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
Stage
Ordered semantic stage described by one EvidenceEvent.
Definition health_result.hpp:133
@ block
Filled glyph cell; the covered glyph is drawn in the bg color.
Definition caret.hpp:66
Definition sample_heritage_live_cyclic.hpp:46
Definition sample_heritage_schema.hpp:256
Definition sample_heritage_engine.hpp:49
double maximum_note_pitch_factor
Definition sample_heritage_engine.hpp:57
const SampleSincKernelBankView * external_sinc_bank
Definition sample_heritage_engine.hpp:55
std::size_t maximum_output_frames
Definition sample_heritage_engine.hpp:52
double maximum_runtime_clock_factor
Definition sample_heritage_engine.hpp:56
std::size_t channel_count
Definition sample_heritage_engine.hpp:51
SampleHeritagePreparedProfile profile
Definition sample_heritage_engine.hpp:50
Definition sample_heritage_schema.hpp:296
Definition sample_heritage_engine.hpp:67
double runtime_clock_multiplier
Definition sample_heritage_engine.hpp:75
std::uint64_t sequence
Definition sample_heritage_engine.hpp:70
double note_pitch_multiplier
Definition sample_heritage_engine.hpp:76
SampleHeritagePlanStatus status
Definition sample_heritage_engine.hpp:68
bool valid() const noexcept
Definition sample_heritage_engine.hpp:78
std::size_t output_frames
Definition sample_heritage_engine.hpp:71
std::size_t input_frames
Definition sample_heritage_engine.hpp:74
std::size_t machine_frames
Definition sample_heritage_engine.hpp:72
std::uint64_t prepare_epoch
Definition sample_heritage_engine.hpp:69
std::size_t pre_live_machine_frames
Definition sample_heritage_engine.hpp:73
Definition sample_heritage_schema.hpp:236
Definition sample_heritage_schema.hpp:252
Definition sample_heritage_runtime_state.hpp:106
SampleHeritageRuntimeStateStatus status
Definition sample_heritage_runtime_state.hpp:107
SampleHeritageRuntimeState state
Definition sample_heritage_runtime_state.hpp:109
Fixed-capacity continuation payload for seeded stages.
Definition sample_heritage_runtime_state.hpp:37
std::array< SampleHeritageRuntimeRngState, kSampleHeritageMaximumStages > rng_states
Definition sample_heritage_runtime_state.hpp:44
std::uint32_t profile_schema_version
Definition sample_heritage_runtime_state.hpp:39
std::array< char, kSampleHeritageMaximumProfileIdBytes+1 > profile_id
Definition sample_heritage_runtime_state.hpp:40
std::size_t rng_state_count
Definition sample_heritage_runtime_state.hpp:45
std::array< std::uint8_t, 32 > profile_digest
Definition sample_heritage_runtime_state.hpp:42
Definition sample_heritage_schema.hpp:276
Definition sample_sinc_kernel.hpp:229