/ simulated_instruments / triangle
Sign in
simulated_instruments / triangle
Pitch support by geometric scaling in the exported driver
MIDI pitch scales every structural length by 2^-((pitch - ref)/12), so the modal spectrum shifts by the semitone ratio exactly (bending modes go as thickness / length^2; the woodblock's cavity scales consistently). The exciter stays fixed. The default geometry sits at the reference note with a two-octave playable range around it.
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 1056e4024f2d parent 578d248 Browse files
3 changed files+34−7
export-src/triangle/export.jsonmodified+22−4View file
@@ -1,11 +1,29 @@
11 {
22 "id": "triangle",
33 "name": "Triangle",
4- "description": "Struck triangle; unpitched, velocity onto beater speed.",
4+ "description": "Struck triangle; pitch scales the rod frame around MIDI 84 (72..96), velocity onto beater speed.",
55 "source": "simulated_instruments/triangle",
66 "knobs": [
7- { "key": "strike_pos", "label": "strike position", "min": 0.02, "max": 0.98, "default": 0.17 },
8- { "key": "strike_angle_deg", "label": "strike angle (deg)", "min": 0, "max": 90, "default": 20 },
9- { "key": "tail_s", "label": "ring-out (s)", "min": 1, "max": 8, "default": 5 }
7+ {
8+ "key": "strike_pos",
9+ "label": "strike position",
10+ "min": 0.02,
11+ "max": 0.98,
12+ "default": 0.17
13+ },
14+ {
15+ "key": "strike_angle_deg",
16+ "label": "strike angle (deg)",
17+ "min": 0,
18+ "max": 90,
19+ "default": 20
20+ },
21+ {
22+ "key": "tail_s",
23+ "label": "ring-out (s)",
24+ "min": 1,
25+ "max": 8,
26+ "default": 5
27+ }
1028 ]
1129 }
export-src/triangle/render_note.mmodified+10−1View file
@@ -1,5 +1,10 @@
11 function y = render_note(note)
2-% Unpitched: MIDI pitch is ignored, velocity drives the beater speed.
2+% MIDI pitch scales the rod frame: side length, gap, and rod diameter by
3+% s = 2^-((pitch - 84)/12), which shifts the bending-mode spectrum by the
4+% semitone ratio exactly (rod modes go as diameter / length^2). The
5+% beater stays fixed. Pitch 84 is the default 6-inch triangle, playable
6+% 72..96; the spectrum is inharmonic, so the note number is a handle on
7+% register, not a tuned fundamental. Velocity drives the beater speed.
38 % Fixed: a single strike, a fixed seed, normalize off so velocity
49 % survives in pascals.
510 P = triangle_defaults();
@@ -11,6 +16,10 @@ P.normalize = 0;
1116 if note.preview ~= 0
1217 P.oversample = 1;
1318 end
19+s = 2 ^ (-(max(72, min(96, note.pitch)) - 84) / 12);
20+P.side_length_m = P.side_length_m * s;
21+P.gap_m = P.gap_m * s;
22+P.rod_diameter_m = P.rod_diameter_m * s;
1423 P.strike_velocity = 0.1 + (6 - 0.1) * note.velocity;
1524 P.strike_pos = note.params.strike_pos;
1625 P.strike_angle_deg = note.params.strike_angle_deg;
exports/triangle.jsonmodified+2−2View file
@@ -2,7 +2,7 @@
22 "format": "numbl-instrument-v1",
33 "id": "triangle",
44 "name": "Triangle",
5- "description": "Struck triangle; unpitched, velocity onto beater speed.",
5+ "description": "Struck triangle; pitch scales the rod frame around MIDI 84 (72..96), velocity onto beater speed.",
66 "source": "simulated_instruments/triangle",
77 "knobs": [
88 {
@@ -30,7 +30,7 @@
3030 "files": [
3131 {
3232 "name": "render_note.m",
33- "content": "function y = render_note(note)\n% Unpitched: MIDI pitch is ignored, velocity drives the beater speed.\n% Fixed: a single strike, a fixed seed, normalize off so velocity\n% survives in pascals.\nP = triangle_defaults();\nP.sample_rate = note.sample_rate;\nP.strike_count = 1;\nP.seed = 1;\nP.pre_delay_ms = 0;\nP.normalize = 0;\nif note.preview ~= 0\n P.oversample = 1;\nend\nP.strike_velocity = 0.1 + (6 - 0.1) * note.velocity;\nP.strike_pos = note.params.strike_pos;\nP.strike_angle_deg = note.params.strike_angle_deg;\nP.tail_s = note.params.tail_s;\ny = triangle_render(P);\nend\n"
33+ "content": "function y = render_note(note)\n% MIDI pitch scales the rod frame: side length, gap, and rod diameter by\n% s = 2^-((pitch - 84)/12), which shifts the bending-mode spectrum by the\n% semitone ratio exactly (rod modes go as diameter / length^2). The\n% beater stays fixed. Pitch 84 is the default 6-inch triangle, playable\n% 72..96; the spectrum is inharmonic, so the note number is a handle on\n% register, not a tuned fundamental. Velocity drives the beater speed.\n% Fixed: a single strike, a fixed seed, normalize off so velocity\n% survives in pascals.\nP = triangle_defaults();\nP.sample_rate = note.sample_rate;\nP.strike_count = 1;\nP.seed = 1;\nP.pre_delay_ms = 0;\nP.normalize = 0;\nif note.preview ~= 0\n P.oversample = 1;\nend\ns = 2 ^ (-(max(72, min(96, note.pitch)) - 84) / 12);\nP.side_length_m = P.side_length_m * s;\nP.gap_m = P.gap_m * s;\nP.rod_diameter_m = P.rod_diameter_m * s;\nP.strike_velocity = 0.1 + (6 - 0.1) * note.velocity;\nP.strike_pos = note.params.strike_pos;\nP.strike_angle_deg = note.params.strike_angle_deg;\nP.tail_s = note.params.tail_s;\ny = triangle_render(P);\nend\n"
3434 },
3535 {
3636 "name": "triangle_basis.m",
↑↓ move↵openescclose