Coupled Meshes + String — User Guide
Vectorised mass‑spring physical model. Two 8×8 meshes (140 nodes) connected by a 12‑node string, with 235 linear springs. Uses matrix‑vector products for force scattering and Verlet integration – the hot loop stays in Praat’s compiled kernels, yielding 5‑20× speedup over scalar implementations. Five presets: Bright Metallic Plate, Dark Gong, Prepared Piano, Percussive Short, Sustained Drone.
What this does
Coupled Meshes + String is a physical modelling synthesiser that simulates two 8×8 mass‑spring meshes connected by a 12‑node string. The system has 140 nodes and 235 linear springs, with independent stiffness and damping per spring group. The simulation uses Verlet integration and is fully vectorised – force scattering and state updates are expressed as matrix‑vector products (mul#) and element‑wise vector arithmetic, keeping the hot loop in Praat’s compiled kernels rather than the script interpreter.
- Mesh 1 (nodes 1–64) – 8×8 grid, 112 springs (horizontal + vertical).
- String (nodes 65–76) – 12 nodes, 11 springs.
- Mesh 2 (nodes 77–140) – 8×8 grid, 112 springs.
- Coupling springs – attach each mesh to a string endpoint (nodes 65 and 76) via bilinear interpolation.
- Excitation – half‑sine pulse applied to the string at a user‑defined position.
Output is taken from node 70 (centre of the string). The audio is downsampled from the model rate to the sample rate, with a cosine fade‑out applied at the end.
Quick start
- In Praat, run the script
CoupledMeshString.praat(no sound selection needed – the script generates its own audio). - Choose a Preset:
- Bright Metallic Plate, Dark Gong, Prepared Piano, Percussive Short, Sustained Drone
- For custom mode (preset = Custom), adjust parameters as desired:
- StrStiffness, M1Stiffness, M2Stiffness – spring constants.
- StrDamping, M1Damping, M2Damping – per‑spring damping.
- CouplingK, CouplingZ – coupling spring stiffness/damping.
- GlobalZ0 – global damping (Verlet friction).
- ExcitationPosition – where on the string the impulse is applied (0–1).
- ExcitAmplitude – strength of the initial impulse.
- Set Duration, SampleRate, and ModelRate (the physics update rate).
- Click OK. The script runs the simulation (vectorised, fast) and creates a Sound object named
Modeled_Instrument.
str_k < 0.5, mesh_k < 0.25 when downsampling.
The 5 presets (+ Custom)
Physics model
new_u = u + (u - u_prev)·(1 - z0) + frcwhere
frc includes spring forces, coupling forces, and excitation.Spring force (per spring):
dist = u[i1] - u[i2]vel = (u[i1] - u_prev[i1]) - (u[i2] - u_prev[i2])fspr = k·dist + z·velCoupling (bilinear interpolation):
Each mesh is attached to a string endpoint via bilinear interpolation of the 2×2 neighbourhood around the attach point.
pos_mesh = Σ w_ij·u[node_ij]fc = k_c·(pos_mesh - pos_string) + z_c·(vel_mesh - vel_string)The force is then distributed back to the four mesh nodes and the string endpoint.
Boundary conditions: All mesh boundary nodes (edges) are fixed at zero displacement (masked out in the Verlet step). The string endpoints (nodes 65 and 76) are free to move – they are coupled to the meshes.
Excitation: A half‑sine pulse of length 5 ms (at model rate) is applied to two adjacent string nodes, weighted by the fractional excitation position. The amplitude is user‑controlled.
Output: The displacement of node 70 (centre of the string) is recorded and linearly interpolated to the sample rate. A cosine fade‑out (30 % of duration, max 400 ms) is applied to avoid clicks.
Vectorised implementation (speed optimisation)
⚡ How vectorisation works
Instead of iterating over each spring in a Praat script loop (slow), the simulation precomputes two matrices:
diff##[s, n]– +1 if spring s starts at node n, -1 if it ends at node n. Multiplyingdiff##by the displacement vectoru#gives the per‑spring extensiondist#in one matrix‑vector product.scatter##[n, s]– -1 if spring s starts at node n, +1 if it ends at node n. Multiplyingscatter##by the per‑spring force vectorfspr#scatters the forces back to the nodes.
The hot loop then consists of three mul# calls, element‑wise vector arithmetic, and a masked Verlet update – all operations that Praat executes in compiled C code, not in the script interpreter. Typical speedup vs. a scalar loop: 5–20×.
The coupling and excitation blocks are small scalar operations (bilinear interpolation, force distribution) and are not vectorised – they represent a negligible fraction of the runtime.
Parameters & defaults
Simulation
| Parameter | Range | Default | Description | ||
|---|---|---|---|---|---|
| Duration | 0.5–30 | 2.0 | 。|||
| SampleRate | 8000–96000 | 11025 | 。
Resonator (spring constants)
Excitation / geometry
Output
Visualization (Praat picture)
When Draw_visualization = 1, the script draws a 4‑panel figure:
- Waveform – the generated audio (blue).
- Magnitude spectrum – 0–5000 Hz, showing the harmonic structure.
- Spectrogram – time‑frequency representation (0–5000 Hz).
- Summary panel – preset name, stiffness/damping values, coupling parameters, excitation position, fade‑out length, and mesh attachment points.
FAQ / troubleshooting
The number of physics steps = duration × model_rate. For a 10‑second sound at 2205 Hz model rate, that’s 22,050 steps. Each step includes 235 spring force calculations (via matrix‑vector products) plus coupling and excitation. The vectorised implementation is already optimised – if it’s still too slow, reduce ModelRate (e.g., to 1102 Hz) or Duration.
Check that the stiffness values are not too high. For stability at the given model rate, keep str_k < 0.5 and mesh_k < 0.25. If you lower the model rate, you may need to reduce stiffness proportionally. Also ensure GlobalZ0 is not too low – a value of 0.00005–0.0002 is safe.
The output is taken from node 70 (centre of the string). If the string is heavily damped or the coupling is weak, the amplitude may be very low. Increase ExcitAmplitude (e.g., to 2.0) or reduce damping. Also check that CouplingK is not zero – without coupling, the meshes do not affect the string.
The two meshes act as resonators – they absorb energy from the string and re‑radiate it, creating a complex, multi‑modal decay. The attachment points determine which modes of each mesh are excited. This simulates a physical instrument where the string is coupled to a soundboard (or two soundboards).
Audio output is produced by linearly interpolating the model‑rate displacement values to the target sample rate. This is fast and avoids artefacts from resampling. The model rate should be at least 2× the highest frequency of interest – for most presets, 2205 Hz is sufficient.
The fade‑out is a half‑cosine (equal‑power‑ish) over 30 % of the duration (capped at 400 ms). This provides a smooth, click‑free ending without an abrupt cutoff. The fade length is printed in the summary panel.