Sound Byte Libs 0.5.1-121-g3358a44
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
bow.hpp
Go to the documentation of this file.
1// sbl/dsp/pm/bow.hpp — A bow: a speed, a weight, and a friction law at a string's junction (Physical modeling — composition)
2//
3// The exciter a bowed string is played with. It owns what a bow owns — how
4// fast it travels and how hard it presses, both ports, smoothed here — and a
5// friction law (TableFriction, ThermalFriction, HybridFriction, or a set of
6// tables) that turns the relative velocity at the junction into a force. The
7// string knows none of that: it hands the bow the junction's state and gets a
8// force back (contracts.hpp, Exciter).
9//
10// Pressure zero is no contact: nothing rests on the string, so no force at
11// all — not the lightest row of the table (physical-modeling.md §3.5). The
12// lightest bow is the smallest pressure above zero.
13//
14// A bow does work on the string and has no passivity proof (RPT-029 Part 4
15// rule 6), so its force passes a SoftLimiter here, at the source. The law
16// speaks in wave units, F / 2Z; the bow returns a force, 2Z times that, and
17// the guide takes the wave back out with its own Z.
18
19#ifndef SBL_DSP_PM_BOW_HPP_
20#define SBL_DSP_PM_BOW_HPP_
21
22#include <cstdint>
23
30
31namespace sbl::dsp::pm {
32
33template<typename Law>
34class Bow {
35 static_assert(is_friction_law_v<Law>, "a Bow runs a friction law (physical-modeling.md §4.5)");
36
37public:
38 /// @note All public methods are ISR-safe — bounded computation, no I/O.
39
40 static constexpr float DEFAULT_VELOCITY_SU = 0.5f;
41 static constexpr float DEFAULT_PRESSURE_SU = 0.5f;
42 static constexpr float MAX_VELOCITY = 0.5f; ///< wave units at full speed
43 static constexpr float SMOOTH_MS = 20.0f;
44 /// Below this the bow is off the string. A smoothed port only approaches
45 /// zero, so "no pressure" has to be a threshold; the lightest bow is this.
46 static constexpr float MIN_PRESSURE_SU = 0.001f;
47
48 Bow() {
49 velocity_.set(DEFAULT_VELOCITY_SU);
50 pressure_port_.set(DEFAULT_PRESSURE_SU);
51 reset();
52 }
53
54 /// The law, for its own parameters (a table, a rosin).
55 Law& law() { return law_; }
56 const Law& law() const { return law_; }
57
58 // ─── Ports ───────────────────────────────────────────────────────
59
60 /// How fast the bow travels across the string. Smoothed.
61 void set_velocity_su(float su) { velocity_.set(math::clamp01(su)); }
62
63 /// How hard it presses: which force slice the law reads. Zero is no contact. Smoothed.
64 void set_pressure_su(float su) { pressure_port_.set(math::clamp01(su)); }
65
66 // ─── Exciter ─────────────────────────────────────────────────────
67
68 /// A block's worth of port movement.
69 void advance(uint16_t n) {
70 v_bow_ = velocity_.advance(n) * MAX_VELOCITY;
71 pressure_ = pressure_port_.advance(n);
72 if (pressure_ < MIN_PRESSURE_SU) pressure_ = 0.0f; // lifted
73 law_.set_force_su(pressure_);
74 }
75
76 /// One sample: the force on the string for the junction's state.
77 float excite(const JunctionState& j) {
78 if (pressure_ <= 0.0f) return last_force_ = 0.0f; // nothing rests on the string
79 const float wave = primitives::SoftLimiter::limit(law_.process(v_bow_ - j.velocity));
80 last_force_ = wave;
81 return TWO * j.impedance * wave;
82 }
83
84 /// A bow stores nothing: it only does work on the string.
85 float energy() const { return 0.0f; }
86
87 void reset() {
88 for (primitives::SmoothedPort* port : {&velocity_, &pressure_port_}) {
89 port->set_time_ms(SMOOTH_MS, types::SAMPLE_RATE_F);
90 port->reset();
91 }
92 v_bow_ = velocity_.value() * MAX_VELOCITY;
93 pressure_ = pressure_port_.value() < MIN_PRESSURE_SU ? 0.0f : pressure_port_.value();
94 law_.set_force_su(pressure_);
95 law_.reset();
96 last_force_ = 0.0f;
97 }
98
99 // ─── For a scope ─────────────────────────────────────────────────
100
101 /// True while the bow holds the string (with weight on it).
102 bool sticking() const { return pressure_ > 0.0f && law_.sticking(); }
103 /// The static limit the bow holds the string with right now, in wave units.
104 float limit() const { return law_.limit(); }
105 /// The force injected on the last sample, in wave units.
106 float force() const { return last_force_; }
107
108 /// The velocity source into the friction junction. Never called from audio code.
109 diagram::Ports describe(diagram::Graph& g, uint8_t group, const char* name = "bow") const {
110 const uint8_t source = g.add_node(diagram::Kind::Source, "bow velocity", group);
111 diagram::Ports p = law_.describe(g, group, name);
112 g.add_edge(source, p.in, diagram::Wave::Velocity);
113 return p;
114 }
115
116private:
117 static constexpr float TWO = 2.0f;
118
119 Law law_;
120 primitives::SmoothedPort velocity_;
121 primitives::SmoothedPort pressure_port_;
122 float v_bow_ = 0.0f;
123 float pressure_ = DEFAULT_PRESSURE_SU;
124 float last_force_ = 0.0f;
125};
126
127} // namespace sbl::dsp::pm
128
129#endif // SBL_DSP_PM_BOW_HPP_
Clamping (Cross-cutting — Math)
void add_edge(uint8_t from, uint8_t to, Wave wave)
Definition graph.hpp:88
uint8_t add_node(Kind kind, const char *name, uint8_t group, float value=0.0f, float value2=0.0f)
Definition graph.hpp:81
void reset()
Definition bow.hpp:87
float energy() const
A bow stores nothing: it only does work on the string.
Definition bow.hpp:85
const Law & law() const
Definition bow.hpp:56
static constexpr float DEFAULT_PRESSURE_SU
Definition bow.hpp:41
static constexpr float DEFAULT_VELOCITY_SU
Definition bow.hpp:40
static constexpr float SMOOTH_MS
Definition bow.hpp:43
diagram::Ports describe(diagram::Graph &g, uint8_t group, const char *name="bow") const
The velocity source into the friction junction. Never called from audio code.
Definition bow.hpp:109
void set_pressure_su(float su)
How hard it presses: which force slice the law reads. Zero is no contact. Smoothed.
Definition bow.hpp:64
static constexpr float MIN_PRESSURE_SU
Definition bow.hpp:46
float excite(const JunctionState &j)
One sample: the force on the string for the junction's state.
Definition bow.hpp:77
void advance(uint16_t n)
A block's worth of port movement.
Definition bow.hpp:69
float force() const
The force injected on the last sample, in wave units.
Definition bow.hpp:106
static constexpr float MAX_VELOCITY
wave units at full speed
Definition bow.hpp:42
bool sticking() const
True while the bow holds the string (with weight on it).
Definition bow.hpp:102
Law & law()
The law, for its own parameters (a table, a rosin).
Definition bow.hpp:55
float limit() const
The static limit the bow holds the string with right now, in wave units.
Definition bow.hpp:104
void set_velocity_su(float su)
How fast the bow travels across the string. Smoothed.
Definition bow.hpp:61
void set(float target)
The port's setter stores here; nothing moves until advance().
float advance(uint16_t n)
Advance by a block: the smoothed value to apply for this block.
static float limit(float x)
The rational sigmoid, available for composition use.
What a load, an exciter, a termination and a friction law provide (Physical modeling — cross-cutting)
Fixed-point constants and audio sample types.
A model's wiring, as data (AP-037)
@ Source
a driving signal (bow velocity, pluck)
constexpr float clamp01(float x)
x held to [0, 1]; NaN → 0.
Definition clamp.hpp:18
Physical modeling: laws, bows, junctions, loads, resonators, strings (docs/conventions/physical-model...
Definition bow.hpp:31
float SAMPLE_RATE_F
Definition fixed.hpp:17
A port's target, its smoother and its last value.
Soft limiter primitive.
The ports a component exposes after describing itself, so an owner can wire them.
Definition graph.hpp:130
uint8_t in
where a wave enters (the junction, the load)
Definition graph.hpp:133
What the guide reports at an excitation point, each sample.
Definition contracts.hpp:43
float impedance
the guide's wave impedance Z
Definition contracts.hpp:46
float velocity
the velocity arriving at the point, before this sample's injection
Definition contracts.hpp:44