Synthesizing a plucked string instrument sound, with variable stiffness.
Can generate sounds ranging from guitar strings to bells and metal bars.
Log in to post a comment.
// A string/bell/bar with variable stiffness. // =========================================== // // This code synthesizes metallic sounds based on the vibrational modes of a prestressed beam. // It can generate sounds ranging from guitar strings to bells and metal bars. // // It uses an efficient recurrence formula to compute the sinusoidal modes // using only two multiplications per time step. // For more info on this technique, see // https://dittytoy.net/ditty/59300f01a0 // // The modal frequencies, amplitudes and decay speeds are assumed to have // a simplified dependence on the mode number; // possible extensions of the code could improve on this. // // Feel free to reuse and customize this, and make music with it! // // =========================================== // Set input parameters for the physical model // Relative stiffness of the string, ranging from 0 to 1, 0 being an ideal string, and 1 an ideal beam. input.stiffness = 0.5; // Position of hammer impact/plucking along string length input.hitPos = 0.21; // Cutoff frequency of the impact input.cutoff = 400; // min=100, max=3000, step=1 ditty.bpm = 60; const stiffString = synth.def(class { constructor(o) { const freq = midi_to_hz(o.note); const omega_1 = 2 * Math.PI * freq; // Fundamental frequency in radians per second const omega_max = 2 * Math.PI * 20000; // Maximal frequency in radians per second const omega_cutoff = 2*Math.PI*o.cutoff; // Cutoff frequency in radians per second const T = ditty.dt; let omega_n = omega_1; this.c1 = []; this.c2 = []; this.s_nm1 = []; this.s_n = []; this.n_modes = 0; for(let n=1; omega_n < omega_max; n++) { // This is where the magic happens! // We set here the frequency, amplitude and decay speed of each mode. // Compute frequency of the nth mode omega_n = omega_1 * Math.sqrt((1 - o.stiffness) * n**2 + o.stiffness * n**4); // Compute initial amplitude of the nth mode based on hit position and cutoff frequency let amp_n = Math.sin(n*Math.PI*o.hitPos) * (Math.min(1, omega_cutoff/omega_n)); // Compute damping coefficient for the nth mode based on duration and frequency let gamma_n = 7/o.duration * (omega_n/omega_1)**0.5; // Compute time stepping coefficients for the nth mode this.c1.push(2 * Math.exp(-gamma_n * T) * Math.cos(omega_n * T)); this.c2.push(Math.exp(-2 * gamma_n * T)); // Set initial conditions for the nth mode: // s[-1] = exp(γT) sin(-ωT), // s[0] = 0, this.s_nm1.push(amp_n * Math.exp(gamma_n*T) * Math.sin(-omega_n*T)); this.s_n.push(0); this.n_modes++; } } // Process one sample of audio for the string model process(p,e,t,o) { let sig = 0; // Update each mode and sum them up for(let n=0; n<this.n_modes; n++) { const s_np1 = this.c1[n] * this.s_n[n] - this.c2[n] * this.s_nm1[n]; this.s_nm1[n] = this.s_n[n]; this.s_n[n] = s_np1; sig += s_np1; } return sig; } }, {env:one, hitPos:0.21, stiffness:0.5, cutoff:800, duration:5, amp:0.1}); loop( () => { stiffString.play(c4, {stiffness: input.stiffness**4, hitPos: input.hitPos, cutoff:()=>input.cutoff}); sleep(1); });