function [y, basis, dinfo] = cymbal_render(P) % CYMBAL_RENDER Full render: basis + strikes -> cascade -> radiate -> output. % Convenience composition of the stage functions; the web app calls the stages % separately so it can cache intermediates (the shell eigensolves above all). basis = cymbal_basis(P); fsHi = P.sample_rate * P.oversample; [tstr, vstr, jstr, taustr, fstr, sstr, sinfo] = cymbal_strikes(P, basis); [env, tframe, DV, kf, cinfo] = cymbal_cascade(P, basis, tstr, jstr, taustr, sstr, sinfo.durationS); [p, radinfo] = cymbal_radiate(P, basis, env, tframe, DV, kf, tstr, taustr, fstr, sstr, fsHi); [y, peakPa] = cymbal_output(P, p, P.oversample); [r1, z1] = cymbal_point(basis, P.strike_r); [r2, z2] = cymbal_point(basis, P.roll_r2); dinfo.strikeRadiusM = r1; dinfo.strikeRimM = P.radius_m - r1; dinfo.strikeZM = z1; dinfo.roll2RadiusM = r2; dinfo.roll2RimM = P.radius_m - r2; dinfo.strikeCount = sinfo.count; dinfo.meanV = sinfo.meanV; dinfo.maxV = sinfo.maxV; dinfo.meanTauUs = sinfo.meanTauUs; dinfo.peakForceN = sinfo.peakForceN; dinfo.durationS = sinfo.durationS; dinfo.modesKept = basis.n; dinfo.maxN = max(basis.ndia); dinfo.f1 = basis.f(1); dinfo.peakPa = peakPa; dinfo.peakSplDb = 20 * log10(peakPa / 20e-6 + 1e-30); dinfo.directPeakPa = radinfo.directPeakPa; dinfo.listenerInside = radinfo.listenerInside; dinfo.listenerDistM = radinfo.listenerDistM; dinfo.peakStrength = cinfo.peakStrength; dinfo.peakWrmsUm = cinfo.peakWrmsUm; dinfo.injectedJ = cinfo.injectedJ; dinfo.cascadeLostJ = cinfo.cascadeLostJ; dinfo.cascadeFrames = cinfo.frames; dinfo.sinfo = sinfo; end