"""Exploratory mass-string/body/contact model. No recorded audio. SI units; uncalibrated dimensions/masses. Velocity Verlet at 88.2 kHz. String and body exchange equal/opposite bridge and fingerboard forces. This is a reduced model, not a validated acoustic double bass. """ import numpy as np from numba import njit from scipy.signal import resample_poly from scipy.io.wavfile import write import json from pathlib import Path @njit def simulate(pitch=58, amplitude=.0008, contact=False, seconds=3., release_seconds=.0015): sr=88200; dt=1/sr; f=440*2**((pitch-69)/12) tension=55.; speed=2*.65*(195.997718 if pitch>=55 else 146.832384); density=tension/speed**2 length=speed/(2*f); n=min(160,int(sr*.36/(2*f))) h=length/(n+1); mass=density*h; k=tension/h # Modal masses and frequencies are provisional, not measured responses. freq=np.array([102.,195.,340.,520.,700.,1050.,1700.,2600.]) bm=np.array([.4,.15,.1,.07,.06,.05,.04,.03]) weight=np.array([.8,1.,.42,.22,.4,.1,.06,.03]) damping=np.array([32.,42.,55.,75.,95.,135.,190.,270.]) q=np.zeros(8);vbody=np.zeros(8);y=np.zeros(n);v=np.zeros(n) pluck_index=int(.74*(n+1))-1 pluck=(pluck_index+1)/(n+1) for j in range(n): x=(j+1)/(n+1);y[j]=amplitude*(x/pluck if x0: # Elastic distributed penalty spring; no random noise. cf=1800.*penetration force[j]+=cf;total_contact+=cf if half==0:contacts+=1 peak_penetration=max(peak_penetration,penetration) # Finite release of the force holding the string, not an audio envelope. time=(frame+half)*dt if time0];assert max(e)