34 static_assert(std::is_floating_point_v<SampleType>);
39 : samples_(samples) {}
50 active_ = !samples_.empty();
58 [[nodiscard]] SampleType
next() noexcept {
62 const auto value = samples_[position_++];
63 if (position_ == samples_.size())
68 void render(std::span<SampleType> output)
noexcept {
69 for (
auto& sample : output)
73 [[nodiscard]]
bool active() const noexcept {
76 [[nodiscard]] std::size_t
position() const noexcept {
79 [[nodiscard]] std::size_t
remaining() const noexcept {
80 return active_ ? samples_.size() - position_ : 0;
84 std::span<const SampleType> samples_{};
85 std::size_t position_ = 0;
96template <
typename SampleType =
float, std::
size_t MaximumRetainedSamples = 16384>
99 static_assert(std::is_floating_point_v<SampleType>);
100 static_assert(MaximumRetainedSamples > 0);
105 prepare(std::span<const SampleType> base_excitation,
106 std::span<const SampleType> body_impulse_response)
noexcept {
107 if (base_excitation.empty()) {
110 if (body_impulse_response.empty()) {
113 if (base_excitation.size() > MaximumRetainedSamples ||
114 body_impulse_response.size() > MaximumRetainedSamples ||
115 base_excitation.size() > MaximumRetainedSamples - (body_impulse_response.size() - 1)) {
118 if (overlaps_storage(base_excitation) || overlaps_storage(body_impulse_response)) {
121 for (
const auto sample : base_excitation) {
122 if (!std::isfinite(sample)) {
126 for (
const auto sample : body_impulse_response) {
127 if (!std::isfinite(sample)) {
132 const auto retained = base_excitation.size() + body_impulse_response.size() - 1;
133 constexpr auto sample_lowest =
134 static_cast<Accumulator
>(std::numeric_limits<SampleType>::lowest());
135 constexpr auto sample_maximum =
136 static_cast<Accumulator
>(std::numeric_limits<SampleType>::max());
137 for (std::size_t index = 0; index < retained; ++index) {
138 const auto value = convolve_at(index, base_excitation, body_impulse_response);
139 if (!std::isfinite(value) || value < sample_lowest || value > sample_maximum) {
142 const auto stored =
static_cast<SampleType
>(value);
143 if (!std::isfinite(stored)) {
148 samples_.fill(SampleType{0});
149 for (std::size_t index = 0; index < retained; ++index) {
151 static_cast<SampleType
>(convolve_at(index, base_excitation, body_impulse_response));
153 retained_samples_ = retained;
157 [[nodiscard]] std::span<const SampleType>
samples() const noexcept {
158 return {samples_.data(), retained_samples_};
162 return retained_samples_;
165 [[nodiscard]]
bool empty() const noexcept {
166 return retained_samples_ == 0;
175 std::conditional_t<
sizeof(SampleType) <
sizeof(
double), double,
long double>;
177 [[nodiscard]]
static Accumulator
178 convolve_at(std::size_t output_index, std::span<const SampleType> base_excitation,
179 std::span<const SampleType> body_impulse_response)
noexcept {
180 const auto first_base = output_index >= body_impulse_response.size() - 1
181 ? output_index - (body_impulse_response.size() - 1)
183 const auto last_base = std::min(output_index, base_excitation.size() - 1);
186 for (
auto base_index = first_base; base_index <= last_base; ++base_index) {
187 const auto body_index = output_index - base_index;
188 sum +=
static_cast<Accumulator
>(base_excitation[base_index]) *
189 static_cast<Accumulator
>(body_impulse_response[body_index]);
194 [[nodiscard]]
bool overlaps_storage(std::span<const SampleType> input)
const noexcept {
198 const auto input_begin = input.data();
199 const auto input_end = input_begin + input.size();
200 const auto storage_begin = samples_.data();
201 const auto storage_end = storage_begin + samples_.size();
202 const auto less = std::less<const SampleType*>{};
203 return less(input_begin, storage_end) &&
less(storage_begin, input_end);
206 std::array<SampleType, MaximumRetainedSamples> samples_{};
207 std::size_t retained_samples_ = 0;
Per-voice traversal state for an immutable prepared excitation profile.
Definition commuted_string_excitation.hpp:32
SampleType next() noexcept
Definition commuted_string_excitation.hpp:58
void retrigger() noexcept
Restarts the profile even when the current traversal has not finished.
Definition commuted_string_excitation.hpp:54
void trigger() noexcept
Starts the profile from its first sample.
Definition commuted_string_excitation.hpp:48
std::size_t remaining() const noexcept
Definition commuted_string_excitation.hpp:79
void render(std::span< SampleType > output) noexcept
Definition commuted_string_excitation.hpp:68
bool active() const noexcept
Definition commuted_string_excitation.hpp:73
CommutedStringExcitationCursorT() noexcept=default
std::size_t position() const noexcept
Definition commuted_string_excitation.hpp:76
void reset() noexcept
Leaves the cursor silent at the beginning of the profile.
Definition commuted_string_excitation.hpp:42
A fixed-capacity body-shaped excitation shared by plucked-string voices.
Definition commuted_string_excitation.hpp:97
std::size_t retained_samples() const noexcept
Definition commuted_string_excitation.hpp:161
CommutedStringExcitationPrepareStatus prepare(std::span< const SampleType > base_excitation, std::span< const SampleType > body_impulse_response) noexcept
Definition commuted_string_excitation.hpp:105
static constexpr std::size_t maximum_retained_samples
Definition commuted_string_excitation.hpp:102
std::span< const SampleType > samples() const noexcept
Definition commuted_string_excitation.hpp:157
bool empty() const noexcept
Definition commuted_string_excitation.hpp:165
CommutedStringExcitationCursorT< SampleType > make_cursor() const noexcept
Definition commuted_string_excitation.hpp:169
@ less
Definition gpu_pass.hpp:287
Definition additive_bank.hpp:218
CommutedStringExcitationProfileT< double > CommutedStringExcitationProfile64
Definition commuted_string_excitation.hpp:213
@ non_finite_input
Definition comb_filter.hpp:36
CommutedStringExcitationPrepareStatus
Definition commuted_string_excitation.hpp:17
@ non_finite_output
Definition commuted_string_excitation.hpp:23
@ empty_body_impulse_response
Definition commuted_string_excitation.hpp:20
@ retained_sample_limit_exceeded
Definition commuted_string_excitation.hpp:21
@ ok
Definition commuted_string_excitation.hpp:18
@ non_finite_input
Definition commuted_string_excitation.hpp:22
@ input_aliases_profile
Definition commuted_string_excitation.hpp:24
@ empty_base_excitation
Definition commuted_string_excitation.hpp:19
CommutedStringExcitationCursorT< float > CommutedStringExcitationCursor
Definition commuted_string_excitation.hpp:210
@ ok
Definition breakpoint_envelope.hpp:36
CommutedStringExcitationCursorT< double > CommutedStringExcitationCursor64
Definition commuted_string_excitation.hpp:211
CommutedStringExcitationProfileT< float > CommutedStringExcitationProfile
Definition commuted_string_excitation.hpp:212