Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
note_modifier.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <compare>
6#include <cstdint>
7#include <string_view>
8
9// Per-note playback modifiers: whether a note sounds on a given pass, and how
10// many times it retriggers when it does.
11//
12// Evaluation is a pure function of (draw key, pass index) — never of hidden
13// mutable state and never of evaluation order. Two notes deciding in the same
14// block cannot perturb each other, so an engine replaying the same document
15// against the same transport trace makes the same sounding decisions every
16// time. That is the replay contract; a stateful generator would break it.
17//
18// The draw key folds the authored seed and the note identity together once, at
19// compile time, so the audio thread only mixes the key with the pass index.
20namespace pulp::timeline {
21
25
30constexpr std::uint64_t note_modifier_mix(std::uint64_t value) noexcept {
31 value += 0x9E3779B97F4A7C15ULL;
32 value ^= value >> 30;
33 value *= 0xBF58476D1CE4E5B9ULL;
34 value ^= value >> 27;
35 value *= 0x94D049BB133111EBULL;
36 value ^= value >> 31;
37 return value;
38}
39
41inline constexpr std::uint16_t note_probability_certain = 0xffffu;
42
44inline constexpr std::uint16_t note_ratchet_maximum = 64u;
45
50enum class NoteConditionKind : std::uint8_t {
51 // Sounds on every pass.
53 // Sounds when pass % period == offset.
55 // Sounds only on the first pass.
57 // Sounds on the last pass of each group of `period` — the "fill" bar.
59};
60
62constexpr std::string_view note_condition_name(NoteConditionKind condition) noexcept {
63 switch (condition) {
65 return "always";
67 return "every_nth";
69 return "first";
71 return "fill";
72 }
73 return "always";
74}
75
78constexpr bool parse_note_condition(std::string_view name, NoteConditionKind& out) noexcept {
79 if (name == "always") {
81 return true;
82 }
83 if (name == "every_nth") {
85 return true;
86 }
87 if (name == "first") {
89 return true;
90 }
91 if (name == "fill") {
93 return true;
94 }
95 return false;
96}
97
105 // Chance the note sounds, out of `note_probability_certain`.
107 // Pass grouping for `EveryNth` / `Fill`. Must be at least one.
108 std::uint16_t condition_period = 1;
109 // Which pass within the group `EveryNth` selects. Must be below the period.
110 std::uint16_t condition_offset = 0;
111 // How many times the note retriggers within its own span. One is a plain
112 // note; the count is authored, not drawn, so ratcheting is fully
113 // deterministic independent of the seed.
114 std::uint16_t ratchet_count = 1;
116
117 constexpr auto operator<=>(const NoteModifier&) const = default;
118};
119
121constexpr bool note_modifier_is_neutral(const NoteModifier& modifier) noexcept {
122 return modifier.probability == note_probability_certain &&
123 modifier.condition == NoteConditionKind::Always && modifier.ratchet_count == 1;
124}
125
127constexpr bool note_modifier_well_formed(const NoteModifier& modifier) noexcept {
128 if (modifier.ratchet_count == 0 || modifier.ratchet_count > note_ratchet_maximum)
129 return false;
130 switch (modifier.condition) {
133 // The period and offset are unread here, so only their canonical
134 // encoding is admitted; otherwise one behavior would have many
135 // byte representations.
136 return modifier.condition_period == 1 && modifier.condition_offset == 0;
138 return modifier.condition_period != 0 &&
139 modifier.condition_offset < modifier.condition_period;
141 return modifier.condition_period != 0 && modifier.condition_offset == 0;
142 }
143 return false;
144}
145
147constexpr std::uint64_t note_modifier_draw_key(std::uint64_t seed, ItemId note_id) noexcept {
148 return note_modifier_mix(note_modifier_mix(seed) ^ note_id.value);
149}
150
153 std::uint64_t key = 0;
154 std::uint64_t pass_index = 0;
155
157 constexpr std::uint64_t value() const noexcept {
158 return note_modifier_mix(key ^ (pass_index + 0x9E3779B97F4A7C15ULL));
159 }
160};
161
166constexpr bool note_probability_sounds(std::uint16_t probability, NoteModifierDraw draw) noexcept {
167 if (probability == 0)
168 return false;
169 if (probability >= note_probability_certain)
170 return true;
171 return draw.value() % note_probability_certain < probability;
172}
173
177constexpr bool note_condition_holds(NoteConditionKind condition, std::uint16_t period,
178 std::uint16_t offset, std::uint64_t pass_index) noexcept {
179 switch (condition) {
181 return true;
183 return pass_index == 0;
185 return period != 0 && pass_index % period == offset;
187 return period != 0 && pass_index % period == static_cast<std::uint64_t>(period - 1);
188 }
189 return true;
190}
191
196constexpr bool note_modifier_sounds(const NoteModifier& modifier, std::uint64_t draw_key,
197 std::uint64_t pass_index) noexcept {
198 if (!note_condition_holds(modifier.condition, modifier.condition_period,
199 modifier.condition_offset, pass_index))
200 return false;
201 return note_probability_sounds(modifier.probability, NoteModifierDraw{draw_key, pass_index});
202}
203
205
206} // namespace pulp::timeline
constexpr bool note_probability_sounds(std::uint16_t probability, NoteModifierDraw draw) noexcept
Tests an authored probability against draw.
Definition note_modifier.hpp:166
constexpr std::uint16_t note_ratchet_maximum
Largest admitted authored retrigger count.
Definition note_modifier.hpp:44
constexpr bool note_modifier_sounds(const NoteModifier &modifier, std::uint64_t draw_key, std::uint64_t pass_index) noexcept
Applies both the modifier's condition and deterministic probability gate.
Definition note_modifier.hpp:196
constexpr bool parse_note_condition(std::string_view name, NoteConditionKind &out) noexcept
Parses a canonical condition name into out.
Definition note_modifier.hpp:78
constexpr std::uint16_t note_probability_certain
Denominator and exact "always sounds" value for authored note probability.
Definition note_modifier.hpp:41
constexpr bool note_condition_holds(NoteConditionKind condition, std::uint16_t period, std::uint16_t offset, std::uint64_t pass_index) noexcept
Returns whether a pass-relative condition admits pass_index.
Definition note_modifier.hpp:177
constexpr bool note_modifier_well_formed(const NoteModifier &modifier) noexcept
Checks structural validity independently of whether the note identity exists.
Definition note_modifier.hpp:127
constexpr std::string_view note_condition_name(NoteConditionKind condition) noexcept
Returns the canonical persisted spelling of condition.
Definition note_modifier.hpp:62
NoteConditionKind
Pass-relative condition controlling when a note may sound.
Definition note_modifier.hpp:50
constexpr std::uint64_t note_modifier_mix(std::uint64_t value) noexcept
Mixes one 64-bit value with the stable note-modifier avalanche function.
Definition note_modifier.hpp:30
constexpr bool note_modifier_is_neutral(const NoteModifier &modifier) noexcept
Returns whether modifier encodes unconditional, single-play behavior.
Definition note_modifier.hpp:121
constexpr std::uint64_t note_modifier_draw_key(std::uint64_t seed, ItemId note_id) noexcept
Derives the deterministic draw key for one authored seed and note identity.
Definition note_modifier.hpp:147
@ EveryNth
Definition note_modifier.hpp:54
@ Always
Definition note_modifier.hpp:52
@ First
Definition note_modifier.hpp:56
@ Fill
Definition note_modifier.hpp:58
@ First
Definition clip_launch.hpp:81
Definition program_compiler.hpp:18
Nonzero monotonic identity for a Timeline-owned object.
Definition item_id.hpp:19
One deterministic probability draw for a note key and loop pass.
Definition note_modifier.hpp:152
constexpr std::uint64_t value() const noexcept
Returns the stable mixed value for this key/pass pair.
Definition note_modifier.hpp:157
std::uint64_t pass_index
Definition note_modifier.hpp:154
std::uint64_t key
Definition note_modifier.hpp:153
Sparse playback modifier attached to one note identity.
Definition note_modifier.hpp:103
std::uint16_t condition_period
Definition note_modifier.hpp:108
NoteConditionKind condition
Definition note_modifier.hpp:115
constexpr auto operator<=>(const NoteModifier &) const =default
ItemId note_id
Definition note_modifier.hpp:104
std::uint16_t ratchet_count
Definition note_modifier.hpp:114
std::uint16_t probability
Definition note_modifier.hpp:106
std::uint16_t condition_offset
Definition note_modifier.hpp:110