function geo = recorder_geometry(P) % RECORDER_GEOMETRY Bore, tone hole lattice, window, and fingering chart. % % The bore runs from the window (x = 0, where the jet drives it) to the open % foot (x = bore_length_m), with a linear taper from r_head to r_foot; real % recorder bores are stepped and locally reworked by the maker, so the taper % here is a first-order stand-in. Eight tone holes: index 1 is the thumb % hole on the back, 2..8 are finger holes 1..7 from the top down. Hole % positions are stored as fractions of the design bore length, so scaling % bore_length_m transposes the whole instrument; radii scale with % P.hole_scale. The positions and radii below were tuned numerically (see % the repository README) so that the first-register fingerings land on an % equal-tempered C major scale at A4 = 440 with the default geometry. % % The fingering chart is baroque (English). Each row is one note, columns % are the eight holes: 0 closed, 1 open, 0.5 pinched (the thumb nail crack % that vents the second register; modeled as a reduced effective radius). % Rows 1..10 are the first register C5..D6; rows 11..14 are pinched % second-register fingerings E6..A6, whose intonation inherits every % simplification of the model and is honestly worse. L = P.bore_length_m; % Tuned hole lattice (fractions of bore length from the window, and radii in % m at the default geometry). Hole 1 is the thumb; 2..8 are finger holes. geo.holeFrac = [0.3699 0.4271 0.4640 0.5292 0.6204 0.6519 0.7424 0.8278]; geo.holeRad = [0.0027 0.0032 0.0030 0.0029 0.0033 0.0026 0.0032 0.0025] * P.hole_scale; geo.holePos = geo.holeFrac * L; geo.holeChim = P.wall_m * ones(1, 8); % Fingering chart: thumb, then finger holes 1..7. % T 1 2 3 4 5 6 7 geo.fingerings = [ ... 0 0 0 0 0 0 0 0; % 1 C5 0 0 0 0 0 0 0 1; % 2 D5 0 0 0 0 0 0 1 1; % 3 E5 0 0 0 0 0 1 0 0; % 4 F5 (fork) 0 0 0 0 1 1 1 1; % 5 G5 0 0 0 1 1 1 1 1; % 6 A5 0 0 1 1 1 1 1 1; % 7 B5 0 1 0 1 1 1 1 1; % 8 C6 1 0 0 1 1 1 1 1; % 9 C#6 1 1 0 1 1 1 1 1; % 10 D6 0.5 0 0 0 0 0 1 1; % 11 E6 (pinched E5) 0.5 0 0 0 0 1 0 1; % 12 F6 (pinched) 0.5 0 0 0 1 1 1 1; % 13 G6 (pinched G5) 0.5 0 0 1 1 1 1 1; % 14 A6 (pinched A5) ]; geo.nNotes = 14; % Equal-tempered targets, A4 = 440: semitones of each row above C5. semis = [0 2 4 5 7 9 11 12 13 14 16 17 19 21]; geo.fTarget = 440 * 2^(3 / 12) * 2.^(reshape(semis, 1, 14) / 12); % Bore radius at position x (column or row vector), linear taper. geo.rHead = P.bore_r_head_m; geo.rFoot = P.bore_r_foot_m; geo.boreLen = L; % Window (mouth) geometry. geo.Sm = P.cutup_m * P.window_width_m; % window area geo.deltaM = 0.7 * sqrt(geo.Sm); % mouth end correction end