58#ifndef SBL_DSP_PM_TABLE_FRICTION_HPP_
59#define SBL_DSP_PM_TABLE_FRICTION_HPP_
99 void set_bow(
const float* family,
const float* limits, uint8_t slices,
100 uint16_t size, uint16_t stride,
float dv_max = 1.0f) {
106 dv_max_ = dv_max > 0.0f ? dv_max : 1.0f;
107 inv_dv_max_ = 1.0f / dv_max_;
118 force_su_ = su < 0.0f ? 0.0f : (su > 1.0f ? 1.0f : su);
119 if (family_ ==
nullptr || slices_ == 0)
return;
121 const float pos = force_su_ *
static_cast<float>(slices_ - 1);
122 slice_ =
static_cast<uint8_t
>(pos);
123 if (slice_ >= slices_ - 1 && slices_ > 1) slice_ =
static_cast<uint8_t
>(slices_ - 2);
124 blend_ = slices_ > 1 ? pos -
static_cast<float>(slice_) : 0.0f;
127 ? limits_[slice_] + blend_ * (limits_[slice_ + 1] - limits_[slice_])
137 if (family_ ==
nullptr)
return 0.0f;
139 const float a = std::fabs(dv_free);
142 if (a <= limit_)
return dv_free;
146 const float f = lookup(a);
147 if (a - f <= CAPTURE_VELOCITY) stuck_ =
true;
148 return dv_free < 0.0f ? -f : f;
155 float limit()
const {
return limit_; }
169 static constexpr float CAPTURE_VELOCITY = 1e-4f;
172 float lookup(
float a)
const {
173 const float last =
static_cast<float>(size_ - 1);
174 float x = a * inv_dv_max_ * last;
178 x = (x > 0.0f) ? (x < last ? x : last) : 0.0f;
180 const uint16_t i =
static_cast<uint16_t
>(x);
181 const float frac = x -
static_cast<float>(i);
182 const uint16_t j = (i + 1 < size_) ?
static_cast<uint16_t
>(i + 1) : i;
184 const float* row0 = family_ +
static_cast<uint32_t
>(slice_) * stride_;
185 const float v0 = row0[i] + frac * (row0[j] - row0[i]);
186 if (slices_ < 2)
return v0;
188 const float* row1 = row0 + stride_;
189 const float v1 = row1[i] + frac * (row1[j] - row1[i]);
190 return v0 + blend_ * (v1 - v0);
193 const float* family_ =
nullptr;
194 const float* limits_ =
nullptr;
197 uint16_t stride_ = 0;
198 float dv_max_ = 1.0f;
199 float inv_dv_max_ = 1.0f;
201 float force_su_ = 1.0f;
208static_assert(is_friction_law_v<TableFriction>,
"TableFriction is a friction law (physical-modeling.md §4.5)");
uint8_t add_node(Kind kind, const char *name, uint8_t group, float value=0.0f, float value2=0.0f)
float process(float dv_free)
One sample: relative velocity in, injection force out.
float limit() const
Static-friction limit at the current bow force — where the string releases.
void set_bow(const BowTable &bow)
Install a bow described in one value.
void set_bow(const float *family, const float *limits, uint8_t slices, uint16_t size, uint16_t stride, float dv_max=1.0f)
Install a bow: a lute-solved family and its per-slice limits.
bool sticking() const
True while the string travels with the bow.
void set_force_su(float su)
Bow force across the family [0, 1]; crossfades adjacent slices.
diagram::Ports describe(diagram::Graph &g, uint8_t group, const char *name="bow friction") const
One Junction node: the friction nonlinearity. Never called from audio code (AP-037).
What a load, an exciter, a termination and a friction law provide (Physical modeling — cross-cutting)
A model's wiring, as data (AP-037)
@ Junction
where waves scatter or a source meets the string
Physical modeling: laws, bows, junctions, loads, resonators, strings (docs/conventions/physical-model...
The ports a component exposes after describing itself, so an owner can wire them.
uint8_t in
where a wave enters (the junction, the load)
uint8_t out
where a wave leaves (the pickup, the reflection)
One bow: a lute-solved family and the shape of its arrays.
const float * limits
static-friction limit per slice
float dv_max
relative velocity at the last sample
uint16_t size
samples per row
const float * family
force_slices rows, light bow first
uint16_t stride
row pitch (size + guard points)