feat(knurl): add Knurl cycle texture
Check and Windows preview / check (anvil) (push) Successful in 2m1s
Check and Windows preview / check (crucible) (push) Successful in 1m31s
Check and Windows preview / check (knurl) (push) Successful in 2m4s
Check and Windows preview / check (lathe) (push) Successful in 1m27s
Check and Windows preview / check (patina) (push) Successful in 1m54s
Check and Windows preview / check (rivet) (push) Successful in 1m30s
Check and Windows preview / check (temper) (push) Successful in 1m31s
Check and Windows preview / check (weld) (push) Successful in 2m31s
Check and Windows preview / previews (push) Successful in 1m26s
Independent plugin release / plan (push) Successful in 7s
Check and Windows preview / windows (push) Successful in 2m26s
Independent plugin release / build (push) Successful in 3m0s
Independent plugin release / publish (push) Successful in 8s

Knurl follows the played note and treats every cycle of the wave on its
own: a repeating level pattern over 1-16 cycles makes subharmonics in tune
with every note, random level and timing per cycle add rattle, and a noise
burst per cycle, highpassed at a multiple of f0, adds grit. Amounts are
counted in cycles, so the texture is the same on F1 and on C2.

- Cycle clock from the shared note tracker, restarted at each note; the
  audio is delayed by the lookahead plus 4.2 ms of timing-jitter headroom.
- Per-cycle randomness from a hash of the cycle number: Same every note
  or Free.
- Patterns Off, Double, Triple, Fry (default), Gallop, Stutter or drawn;
  grit switchable per step. Pattern persisted as knurl-pattern.
- Editor: last eight cycles overlaid, pattern bars with the playing step.
- Tests match the prototype: Double gives f0/2 at -8.19 dB, Triple f0/3
  at -10.68 dB, equal on F1 and C2, no added clicks, bit-exact when off,
  no allocation on the audio thread.
This commit is contained in:
nvrl committed 2026-11-06 08:49:11 +01:00
1 parent f4c0a833e6
commit 5cc3770737
27 files changed
+4699 -33

No files matched your search

+28 -5
View File
@@ -11,11 +11,11 @@ fn main() {
.map(|a| a[1].as_str())
.unwrap_or("rivet");
if ![
"rivet", "patina", "anvil", "crucible", "lathe", "temper", "weld",
"rivet", "patina", "anvil", "crucible", "lathe", "temper", "weld", "knurl",
]
.contains(&plugin)
{
eprintln!("Use --plugin rivet, patina, anvil, crucible, lathe, temper or weld");
eprintln!("Use --plugin rivet, patina, anvil, crucible, lathe, temper, weld or knurl");
std::process::exit(2);
}
let patina = plugin == "patina" || args.iter().any(|arg| arg == "--resonance");
@@ -25,7 +25,10 @@ fn main() {
let lathe = plugin == "lathe";
let temper = plugin == "temper";
let weld = plugin == "weld";
let (width, height) = if weld {
let knurl = plugin == "knurl";
let (width, height) = if knurl {
(knurl_ui::WIDTH, knurl_ui::HEIGHT)
} else if weld {
(weld_ui::WIDTH, weld_ui::HEIGHT)
} else if temper {
(temper_ui::WIDTH, temper_ui::HEIGHT)
@@ -146,6 +149,22 @@ fn main() {
}
temper_state.accept_frame(engine.take_analyzer());
}
let mut knurl_settings = knurl_core::Settings::default();
let mut knurl_state = knurl_ui::State::default();
if knurl {
// An F1 saw bass with the default Fry pattern, jitter and grit.
let mut engine = knurl_core::Engine::new(48000., 2).unwrap();
engine.set_settings(knurl_settings).unwrap();
let mut saw = common_dsp::Saw::new(48000., 43.65);
for i in 0..36000 {
let x = (saw.sample() * 2.2).tanh() * 0.5;
engine.process_sample([x, x]);
if i % 1600 == 0 {
knurl_state.accept_frame(engine.take_analyzer());
}
}
knurl_state.accept_frame(engine.take_analyzer());
}
let mut weld_settings = weld_core::Settings::default();
let mut weld_state = weld_ui::State::default();
if weld {
@@ -275,7 +294,9 @@ fn main() {
let session_motif = session.motifs[0].id;
shared.replace(session).unwrap();
let ctx = egui::Context::default();
if weld {
if knurl {
knurl_ui::style(&ctx);
} else if weld {
weld_ui::style(&ctx);
} else if temper {
temper_ui::style(&ctx);
@@ -310,7 +331,9 @@ fn main() {
},
|ui| {
egui::CentralPanel::default().show(ui, |ui| {
if weld {
if knurl {
knurl_ui::show(ui, &mut knurl_settings, &mut knurl_state);
} else if weld {
weld_ui::show(ui, &mut weld_settings, &mut weld_state);
} else if temper {
temper_ui::show(ui, &mut temper_settings, &mut temper_state);