@maruaudio/dsp/voices/modal-struck is the struck-resonator pool shared by Bells, Handpan and Kalimba: a 128-entry note map with per-note recipe and mode weights over up to eight recipes, a twelve-slot stereo pool, a raised-cosine excitation through a velocity-coupled one-pole noise filter, and per-mode coupled resonators with stereo detune. A ringing voice keeps the coefficients captured at its strike; modules fill the tables through plain writer functions.
Parameters
setModalStruckV2Param quantizes to a 1e-5 grid (modalStruckQuantize), then clamps:
| Index | Range | Default |
|---|---|---|
| 0 position (marks tables dirty) | 0 to 1 | 0.15 |
| 1 hardness / 2 noise / 6 stereo width | 0 to 1 | 0.6 / 0.1 / 0.5 |
| 3 decay scale / 4 damping / 5 size (marks dirty) | 0.001 to 60 / 0.001 to 60 / 0.001 to 32 | 1 / 1 / 1 |
| 7 release / 17 tail | 0 to 60 s / 0.01 to 60 s | 0.5 / 8 |
| 8 master | 0 to 1; 0.01 s glide while voices ring | 0.8 |
| 9 velocity gain exponent / 16 base gain / 10 velocity–hardness coupling | 0 to 4 / 0 to 4 / −1 to 1 | 2 / 0.03 / 0 |
| 11 / 12 noise cutoff min / max | 1 to 24000 Hz / min to 24000 Hz | 600 / 9600 |
| 14 / 15 excite min / max; 13 seed | 0.1 to 100 ms / min to 100 ms; raw u32, 0 becomes 1 | 3 / 14; 0x9e3779b9 |
setModalStruckVoiceCount clamps the active pool to 10..12 (default 10).
Lifecycle
initModalStruckState(rate) → voice count and parameters → when modalStruckConfigDirty is set, refill the recipe and note tables through modalStruckSetRecipe, …SetRecipeMode, …SetNoteRecipe, …SetNoteMode and clear the flag → modalStruckV2NoteOn(note, velocity) (lowest free slot, else the oldest; returns whether the note is mapped) / modalStruckV2NoteOff → modalStruckV2Tick(state, bus) per frame (or …VoiceTick per voice); the caller applies master live via modalStruckAdvanceMaster. resetModalStruckState retires every voice and restarts the strike counter, keeping tables.
When to use it
Tuned struck instruments whose notes map to a few mode recipes: bells, handpans, kalimbas.
Limits
- Stereo, sample tick; 12 voices of up to 16 modes, 128 notes, 8 recipes.
- The age counter advances for every note-on, mapped or not; a completed note-off ramp retires its voice.
- Modes at or above 0.45 × fs get amplitude 0; a stolen slot fades over 192 frames.