function [y, basis, dinfo] = recorder_render(P) % RECORDER_RENDER Full render: notes -> basis + jet -> radiate -> output. % Convenience composition of the stage functions; the web app calls the % stages separately so it can cache intermediates (the bore basis per note, % and the jet integration, which is the expensive stage of this model). % Returns the basis of the first note (the demo prints its resonances). [noteIdx, tOn, spanS, ninfo] = recorder_notes(P); fsHi = P.sample_rate * P.oversample; T = floor(ninfo.durationS * fsHi); p = zeros(1, T); K = ninfo.count; fPlay = zeros(1, K); cents = zeros(1, K); vacRms = zeros(1, K); listenerInside = 0; listenerDistM = 0; basis = []; for i = 1:K bi = recorder_basis(P, noteIdx(i)); if i == 1 basis = bi; end [Zr, Zi, Qin, jinfo] = recorder_jet(P, bi, spanS(i), i, fsHi); [pn, radinfo] = recorder_radiate(P, bi, Zr, Zi, Qin, fsHi); k0 = min(T, round(tOn(i) * fsHi) + 1); len = min(length(pn), T - k0 + 1); p(k0:k0 + len - 1) = p(k0:k0 + len - 1) + pn(1:len); fPlay(i) = jinfo.fPlay; cents(i) = jinfo.cents; vacRms(i) = jinfo.vacRms; listenerInside = listenerInside || radinfo.listenerInside; listenerDistM = radinfo.listenerDistM; end [y, peakPa] = recorder_output(P, p, P.oversample); dinfo.noteCount = K; dinfo.noteIdx = noteIdx; dinfo.fPlay = fPlay; dinfo.cents = cents; dinfo.vacRms = vacRms; dinfo.durationS = ninfo.durationS; dinfo.peakPa = peakPa; dinfo.peakSplDb = 20 * log10(peakPa / 20e-6 + 1e-30); dinfo.listenerInside = listenerInside; dinfo.listenerDistM = listenerDistM; end