function [y, basis, dinfo] = triangle_render(P) % TRIANGLE_RENDER Full render: basis + strikes -> radiate -> output. % Convenience composition of the stage functions; the web app calls the % stages separately so it can cache intermediates (the eigensolve above all). basis = triangle_basis(P); fsHi = P.sample_rate * P.oversample; [tstr, vstr, jstr, taustr, fstr, sstr, sinfo] = triangle_strikes(P); [p, radinfo] = triangle_radiate(P, basis, tstr, vstr, jstr, taustr, fstr, sstr, fsHi); [y, peakPa] = triangle_output(P, p, P.oversample); 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.modesIn = sum(basis.family == 0); dinfo.modesOut = sum(basis.family == 1); 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.sinfo = sinfo; end