1function [y, basis, dinfo] = bass_render(P)
2% BASS_RENDER Full render: notes -> basis -> radiate -> output.
3% Convenience composition of the stage functions; the web app calls the
4% stages separately so it can cache intermediates (the per-fingering
5% eigensolves above all).
7[tOn, midi, str, stop, force, ninfo] = bass_notes(P);
8basis = bass_basis(P, str, stop);
9fsHi = P.sample_rate * P.oversample;
10[p, radinfo] = bass_radiate(P, basis, tOn, str, stop, force, fsHi);
11[y, peakPa] = bass_output(P, p, P.oversample);
13dinfo.noteCount = ninfo.count;
14dinfo.durationS = ninfo.durationS;
15dinfo.meanForce = ninfo.meanForce;
16dinfo.maxForce = ninfo.maxForce;
17dinfo.midi = midi;
18dinfo.tOn = tOn;
19dinfo.str = str;
20dinfo.stop = stop;
21dinfo.fingerings = basis.nf;
22dinfo.modesTotal = basis.n;
23dinfo.fundHz = radinfo.fundHz;
24dinfo.fundT60 = radinfo.fundT60;
25dinfo.peakPa = peakPa;
26dinfo.peakSplDb = 20 * log10(peakPa / 20e-6 + 1e-30);
27dinfo.listenerDistM = radinfo.listenerDistM;
28dinfo.listenerInside = radinfo.listenerInside;
29dinfo.ninfo = ninfo;
30end