93template <std::
size_t MaximumVoices>
95 static_assert(MaximumVoices > 0,
"VoiceModulationSources needs at least one voice");
96 static_assert(MaximumVoices <= 256,
"VoiceModulationSources bounds voice count at 256");
97 static constexpr std::uint32_t kMaxFrames = 1u << 20;
106 if (!config_valid(
config))
109 scratch_.resize(
config.max_frames);
114 config_.envelope.envelope.sample_rate =
config.sample_rate;
115 for (std::size_t i = 0; i < MaximumVoices; ++i) {
116 auto& lfo = lfos_[i];
118 ? pulp::signal::Lfo::Mode::free
119 : pulp::signal::Lfo::Mode::retrig);
120 lfo.set_wave(
config.lfo.wave);
121 lfo.set_rate_hz(
config.lfo.rate_hz);
122 lfo.set_seed(
config.lfo.seed +
static_cast<std::uint32_t
>(i));
123 lfo.set_phase_offset(
config.lfo.unison_phase_spread
124 ?
static_cast<double>(i) /
static_cast<double>(MaximumVoices)
126 lfo.prepare(
config.sample_rate);
127 if (
config.envelope.enabled && !envelopes_[i].prepare(config_.envelope.envelope))
137 scratch_.shrink_to_fit();
143 for (std::size_t i = 0; i < MaximumVoices; ++i) {
145 envelopes_[i].reset();
149 bool prepared() const noexcept {
return prepared_; }
168 if (!
prepared() || !std::isfinite(rate_hz) || !(rate_hz > 0.0))
170 config_.lfo.rate_hz = rate_hz;
171 for (
auto& lfo : lfos_)
172 lfo.set_rate_hz(rate_hz);
186 if (!
prepared() || !std::isfinite(depth))
188 config_.lfo.depth = depth;
205 config_.lfo.wave = wave;
206 for (
auto& lfo : lfos_)
212 void note_on(std::size_t voice_index)
noexcept {
213 if (voice_index >= MaximumVoices)
215 lfos_[voice_index].retrigger();
216 envelopes_[voice_index].note_on();
221 if (voice_index >= MaximumVoices)
223 envelopes_[voice_index].note_off();
233 std::uint32_t frame_count)
noexcept {
234 if (voice_index >= MaximumVoices)
238 if (!destination.prepared())
240 if (!block_bounds_valid(destination, frame_count))
242 if (!config_.lfo.enabled)
244 auto& lfo = lfos_[voice_index];
245 const float depth = config_.lfo.depth;
247 auto reservation = destination.reserve_audio_rate(target);
249 return {
false, reservation.status};
250 for (std::uint32_t frame = 0; frame < frame_count; ++frame)
251 reservation.values[frame] = depth * lfo.next();
254 for (std::uint32_t frame = 0; frame < frame_count; ++frame)
255 scratch_[frame] = depth * lfo.next();
256 return destination.add_constant(target, scratch_[0]);
266 std::uint32_t frame_count)
noexcept {
267 if (voice_index >= MaximumVoices)
271 if (!destination.prepared())
273 if (!block_bounds_valid(destination, frame_count))
275 if (!config_.envelope.enabled)
277 auto& envelope = envelopes_[voice_index];
279 auto reservation = destination.reserve_audio_rate(target);
281 return {
false, reservation.status};
282 envelope.render(std::span<float>(reservation.values, frame_count));
285 envelope.render(std::span<float>(scratch_.data(), frame_count));
286 return destination.add_constant(target, scratch_[0]);
298 std::uint32_t frame_count)
noexcept {
299 if (voice_index >= MaximumVoices)
303 if (!destination.prepared())
305 if (!block_bounds_valid(destination, frame_count))
307 if (!config_.lfo.enabled || !config_.envelope.enabled)
309 if (lfo_target == envelope_target)
311 if (destination.max_lanes() < 2)
313 auto result = destination.begin_block(frame_count);
316 result =
append_lfo_lane(voice_index, destination, lfo_target, rate, frame_count);
335 static bool block_bounds_valid(
const VoiceModulationBuffer& destination,
336 std::uint32_t frame_count)
noexcept {
337 return frame_count != 0 && frame_count <= destination.max_frames();
340 std::array<pulp::signal::Lfo, MaximumVoices> lfos_;
341 std::array<AhdsrEnvelope, MaximumVoices> envelopes_;
342 std::vector<float> scratch_;
343 VoiceModulationSourcesConfig config_{};
344 bool prepared_ =
false;
Definition voice_modulation_buffer.hpp:78
const VoiceModulationSourcesConfig & config() const noexcept
Definition voice_modulation_sources.hpp:150
VoiceModulationResult write_voice(std::size_t voice_index, VoiceModulationBuffer &destination, VoiceModulationTarget lfo_target, VoiceModulationTarget envelope_target, VoiceModulationRate rate, std::uint32_t frame_count) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:293
VoiceModulationSources()=default
void note_on(std::size_t voice_index) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:212
void reset() noexcept
RT-safe after prepare. Resets every voice's sources to a fresh start.
Definition voice_modulation_sources.hpp:142
void release() noexcept
Control/background-thread only. Invalidates prepared storage.
Definition voice_modulation_sources.hpp:135
bool set_lfo_rate_hz(double rate_hz) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:167
bool prepared() const noexcept
Definition voice_modulation_sources.hpp:149
VoiceModulationResult append_lfo_lane(std::size_t voice_index, VoiceModulationBuffer &destination, VoiceModulationTarget target, VoiceModulationRate rate, std::uint32_t frame_count) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:229
VoiceModulationResult append_envelope_lane(std::size_t voice_index, VoiceModulationBuffer &destination, VoiceModulationTarget target, VoiceModulationRate rate, std::uint32_t frame_count) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:262
bool prepare(const VoiceModulationSourcesConfig &config) noexcept
Control/background-thread only.
Definition voice_modulation_sources.hpp:105
void note_off(std::size_t voice_index) noexcept
RT-safe after prepare. Releases that voice's envelope.
Definition voice_modulation_sources.hpp:220
void set_lfo_wave(pulp::signal::Lfo::Wave wave) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:202
bool set_lfo_depth(float depth) noexcept
RT-safe after prepare.
Definition voice_modulation_sources.hpp:185
Wave
Definition lfo.hpp:79
The house low-frequency oscillator: seven waveforms plus a continuous shape morph,...
Definition analyzer_provider.hpp:12
VoiceLfoPhasePolicy
LFO phase policy on note-on.
Definition voice_modulation_sources.hpp:47
@ FreeRunning
The LFO runs from reset() and ignores note-on.
Definition voice_modulation_sources.hpp:50
@ Retrigger
Note-on restarts phase and the lifecycle for that voice.
Definition voice_modulation_sources.hpp:53
VoiceModulationRate
Definition voice_modulation_buffer.hpp:25
@ AudioRate
Definition voice_modulation_buffer.hpp:27
VoiceModulationTarget
Definition voice_modulation_buffer.hpp:9
@ NotPrepared
Definition voice_modulation_buffer.hpp:32
@ InvalidTarget
Definition voice_modulation_buffer.hpp:34
@ LaneOverflow
Definition voice_modulation_buffer.hpp:37
@ InvalidFrameCount
Definition voice_modulation_buffer.hpp:33
@ DuplicateTarget
Definition voice_modulation_buffer.hpp:36
@ Ok
Definition voice_modulation_buffer.hpp:31
Definition instrument_envelope.hpp:17
Definition voice_modulation_sources.hpp:71
bool enabled
Definition voice_modulation_sources.hpp:72
AhdsrEnvelopeConfig envelope
AHDSR shape.
Definition voice_modulation_sources.hpp:75
Definition voice_modulation_sources.hpp:56
double rate_hz
Free-running rate in Hz; clamped into the shipped LFO's legal range.
Definition voice_modulation_sources.hpp:61
std::uint32_t seed
Base seed; voice i uses seed + i.
Definition voice_modulation_sources.hpp:68
float depth
Bipolar output scale. Negative values invert phase. Must be finite.
Definition voice_modulation_sources.hpp:63
bool unison_phase_spread
Spread unison voices evenly around the cycle: voice i starts at phase offset i / MaximumVoices cycles...
Definition voice_modulation_sources.hpp:66
pulp::signal::Lfo::Wave wave
Definition voice_modulation_sources.hpp:58
VoiceLfoPhasePolicy phase_policy
Definition voice_modulation_sources.hpp:59
bool enabled
Definition voice_modulation_sources.hpp:57
Definition voice_modulation_buffer.hpp:40
Definition voice_modulation_sources.hpp:78
VoiceEnvelopeSourceSpec envelope
Definition voice_modulation_sources.hpp:84
double sample_rate
Definition voice_modulation_sources.hpp:79
std::uint32_t max_frames
Per-block frame capacity; bounds the audio-rate lanes this bank can render and its constant-rate scra...
Definition voice_modulation_sources.hpp:82
VoiceLfoSourceSpec lfo
Definition voice_modulation_sources.hpp:83