From dfde6a0b98ee97482693b0223a1f84ae73de3c12 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 03:16:13 +0000 Subject: [PATCH 1/2] Fix Folk/Christmas songs missing chorus/refrain melodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The folk()/carol() helper loops one fixed melody per verse, which breaks for songs whose chorus/refrain is musically distinct from the verse — the helper just repeated the verse tune where the chorus should be, silently dropping the actual chorus content from both the audio and the highway. Rewrote 8 pieces as explicit note-by-note builders (matching the existing Drunken Sailor/Jingle Bells pattern) with real verse+chorus structure: - Oh! Susanna, Camptown Races: added the missing chorus melody - Turkey in the Straw: added the missing second fiddle strain (AABB reel) - We Three Kings, Angels We Have Heard on High, Hark the Herald Angels Sing, O Come All Ye Faithful: added the missing refrain - 12 Days of Christmas: rebuilt as a genuinely cumulative structure (day N sings N gift lines counting down to "a partridge in a pear tree"), which a single fixed-length loop could never represent --- index.html | 326 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 285 insertions(+), 41 deletions(-) diff --git a/index.html b/index.html index 9b31a7a..c609280 100644 --- a/index.html +++ b/index.html @@ -3377,12 +3377,40 @@ const FOLK_PIECES=(function(){ return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel}; } - // ── Oh Susanna ── - var ohsusanna=folk("Oh! Susanna","Stephen Foster (1848)",120, - [C4,D4,E4,G4,G4,A4,G4,E4,C4,D4,E4,E4,D4,C4,D4,C4,D4,E4,G4,G4,A4,G4,E4,C4,D4,E4,E4,D4,D4,C4], - [0,1,2,3,3,0,3,2,0,1,2,2,1,0,1,0,1,2,3,3,0,3,2,0,1,2,2,1,1,0], - [2,2,4,4,2,2,4,4,2,2,4,2,2,4,4,2,2,4,4,2,2,4,4,2,2,4,2,2,2,6], - 5,C4); + // ── Oh! Susanna ── + // Verse + a melodically DISTINCT chorus ("Oh! Susanna, oh don't you cry for me"). + // The chorus's second half reprises the verse tune ("for I come from Alabama..."). + var ohsusanna=(function(){ + var bg=[],mel=[],t=0,q=60/120,e=q/2; + function note(f,dur,lane,inst,v,rep){ + bg.push({t:t,f:f,d:dur*0.85,i:inst,v:v}); + if(rep>0)bg.push({t:t,f:f/2,d:dur*0.85,i:'bass',v:v*0.35}); + if(rep>1)bg.push({t:t,f:f,d:dur*0.85,i:'strings',v:v*0.4}); + mel.push({t:t,l:lane,d:dur,f:f}); + t+=dur; + } + function verseLine(inst,v,rep){ + // "I come from Al-a-bam-a with my ban-jo on my knee" + note(C4,e,0,inst,v,rep);note(D4,e,1,inst,v,rep);note(E4,q,2,inst,v,rep);note(G4,e,3,inst,v,rep);note(G4,e,3,inst,v,rep); + note(A4,q,0,inst,v,rep);note(G4,q,3,inst,v,rep);note(E4,q,2,inst,v,rep); + note(C4,e,0,inst,v,rep);note(D4,e,1,inst,v,rep);note(E4,q,2,inst,v,rep);note(E4,q,2,inst,v,rep);note(D4,q*2,1,inst,v,rep); + } + for(var rep=0;rep<3;rep++){ + var v=0.22+rep*0.09; + var inst=rep===0?'woodwind':rep===1?'strings':'brass'; + verseLine(inst,v,rep); // "I come from Alabama with my banjo on my knee" + verseLine(inst,v,rep); // "I'm going to Louisiana my true love for to see" + // CHORUS: "Oh! Su-san-na, oh don't you cry for me," + note(G4,q,3,inst,v*1.15,rep);note(C5,e,2,inst,v*1.15,rep);note(C5,e,2,inst,v*1.15,rep);note(C5,q,2,inst,v*1.15,rep); + note(A4,e,0,inst,v*1.15,rep);note(G4,e,3,inst,v*1.15,rep);note(E4,q,2,inst,v*1.15,rep);note(D4,q,1,inst,v*1.15,rep); + note(E4,q,2,inst,v*1.15,rep);note(E4,q*2,2,inst,v*1.15,rep); + verseLine(inst,v*1.15,rep); // "for I come from Alabama, with my banjo on my knee" + t+=0.4; + } + [C4,C4*5/4,C4*3/2,C4*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});bg.push({t:t,f:f,d:2.0,i:'brass',v:0.4});}); + bg.push({t:t,f:C4/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4}); + return {n:"Oh! Susanna",composer:"Stephen Foster (1848)",bpm:120,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel}; + })(); // ── Greensleeves ── var greensleeves=folk("Greensleeves","English Traditional (1580)",100, @@ -3392,11 +3420,38 @@ const FOLK_PIECES=(function(){ 4,A3); // ── Camptown Races ── - var camptown=folk("Camptown Races","Stephen Foster (1850)",140, - [G4,G4,E4,G4,A4,G4,E4,C4,D4,D4,E4,E4,F4,E4,D4,C4,C4,E4,G4,G4,G4,E4,G4,A4,G4,E4,C4,G4,A4,B4,A4,G4,E4,D4,D4,C4], - [3,3,2,3,0,3,2,0,1,1,2,2,1,2,1,0,0,2,3,3,3,2,3,0,3,2,0,3,0,3,0,3,2,1,1,0], - [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8], - 3,C4); + // Verse (existing tune) + the DISTINCT syncopated refrain ("Gwine to run all night... + // I'll bet my money on de bob-tail nag") which was previously missing entirely. + var camptown=(function(){ + var bg=[],mel=[],t=0,u=30/140; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>0)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.35}); + if(rep>1)bg.push({t:t,f:f,d:d*0.85,i:'strings',v:v*0.4}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var verseTheme=[G4,G4,E4,G4,A4,G4,E4,C4,D4,D4,E4,E4,F4,E4,D4,C4,C4,E4,G4,G4,G4,E4,G4,A4,G4,E4,C4,G4,A4,B4,A4,G4,E4,D4,D4,C4]; + var verseLanes=[3,3,2,3,0,3,2,0,1,1,2,2,1,2,1,0,0,2,3,3,3,2,3,0,3,2,0,3,0,3,0,3,2,1,1,0]; + var verseDurs=[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8]; + // "Gwine to run all night, gwine to run all day," / "I'll bet my money on de bob-tail nag," / "somebody bet on de bay." + var refrainTheme=[G4,G4,G4,A4,G4,E4,E4,E4,F4,G4, C5,C5,B4,A4,G4,G4,G4,A4,G4,E4, D4,E4,F4,G4,E4,D4,C4]; + var refrainLanes=[3,3,3,0,3,2,2,2,1,3, 0,0,3,0,3,3,3,0,3,2, 1,2,1,3,2,1,0]; + var refrainDurs=[2,1,1,2,2,2,1,1,2,4, 1,1,1,1,2,1,1,2,1,2, 1,1,2,2,1,1,4]; + for(var rep=0;rep<3;rep++){ + var v=0.22+rep*0.09; + var inst=rep===0?'woodwind':rep===1?'strings':'brass'; + seq(verseTheme,verseLanes,verseDurs,inst,v,rep); + seq(refrainTheme,refrainLanes,refrainDurs,inst,v*1.1,rep); + t+=0.4; + } + [C4,C4*5/4,C4*3/2,C4*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});bg.push({t:t,f:f,d:2.0,i:'brass',v:0.4});}); + bg.push({t:t,f:C4/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4}); + return {n:"Camptown Races",composer:"Stephen Foster (1850)",bpm:140,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel}; + })(); // ── Red River Valley ── // Slow American ballad in 4/4. "From this val-ley they say you are go-ing" @@ -3422,11 +3477,41 @@ const FOLK_PIECES=(function(){ 5,C4); // ── Turkey in the Straw ── - var turkey=folk("Turkey in the Straw","American Traditional (1820s)",145, - [G4,A4,B4,G4,E4,G4,A4,B4,C5,B4,A4,G4,E4,D4,E4,G4,A4,B4,C5,D5,C5,B4,A4,G4], - [0,1,3,0,2,0,1,3,0,3,1,0,2,1,2,0,1,3,0,1,0,3,1,0], - [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8], - 5,G4); + // Old-time fiddle reel in AABB form — two DISTINCT strains (the traditional tune + // archive describes it as "low-high": the B strain sits a register above the A strain). + // Previously only the A strain was encoded, looped identically forever. + var turkey=(function(){ + var bg=[],mel=[],t=0,u=30/145; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>0)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.3}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + // A strain + var aTheme=[G4,A4,B4,G4,E4,G4,A4,B4,C5,B4,A4,G4,E4,D4,E4,G4,A4,B4,C5,D5,C5,B4,A4,G4]; + var aLanes=[0,1,3,0,2,0,1,3,0,3,1,0,2,1,2,0,1,3,0,1,0,3,1,0]; + var aDurs=[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8]; + // B strain — climbs a register higher, more running eighth notes + var bTheme=[B4,D5,B4,D5,C5,E5,C5,E5,G5,G5,F5,E5,D5,B4,A4,G4]; + var bLanes=[3,1,3,1,0,2,0,2,3,3,1,2,1,3,0,3]; + var bDurs=[1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,4]; + for(var rep=0;rep<3;rep++){ + var v=0.22+rep*0.09; + var inst=rep===0?'woodwind':rep===1?'strings':'brass'; + seq(aTheme,aLanes,aDurs,inst,v,rep); + seq(aTheme,aLanes,aDurs,inst,v,rep); + seq(bTheme,bLanes,bDurs,inst,v*1.1,rep); + seq(bTheme,bLanes,bDurs,inst,v*1.1,rep); + t+=0.4; + } + [G4,G4*5/4,G4*3/2,G4*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});bg.push({t:t,f:f,d:2.0,i:'brass',v:0.4});}); + bg.push({t:t,f:G4/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4}); + return {n:"Turkey in the Straw",composer:"American Traditional (1820s)",bpm:145,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel}; + })(); // ── Danny Boy ── // Londonderry Air: flowing Irish ballad, famous "long-short" dotted feel. @@ -3514,22 +3599,123 @@ const CHRISTMAS_PIECES=(function(){ // beat 1 of each bar gets a longer note, phrase ends get dotted-half [2,2,2,2,2,4, 2,2,2,2,6, 2,2,2,4, 2,2,2,2,2,4, 2,2,2,2,6, 2,2,2,4, 2,4,8], 4,F4); - var twelve=carol("12 Days of Christmas","English (1780)",110, - [C4,F4,F4,F4,F4,E4,F4,G4,A4,Bb4,A4,G4,F4,G4,A4,Bb4,A4,G4,F4,G4,A4,Bb4,C5,A4,Bb4,G4,A4,F4], - [0,1,1,1,1,2,1,3,0,2,0,3,1,3,0,2,0,3,1,3,0,2,0,0,2,3,0,1], - [2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,8], - 5,F4); - var hark=carol("Hark the Herald Angels Sing","Mendelssohn (1840)",115, - [C4,F4,F4,E4,F4,A4,A4,G4,C5,A4,A4,G4,C5,A4,A4,G4,A4,Bb4,G4,A4,Bb4,C5,A4,F4,G4,Bb4,A4,G4,F4], - [0,1,1,2,1,0,0,3,0,0,0,3,0,0,0,3,0,2,3,0,2,0,0,1,3,2,0,3,1], - [2,4,2,2,4,2,2,4,2,2,2,4,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,8], - 4,F4); + // ── 12 Days of Christmas ── + // Genuinely CUMULATIVE: each day adds one more gift line, counted back down to + // "a partridge in a pear tree." A single fixed-length loop can't represent this — + // day 12 has twelve gift lines, day 1 has just one. Built day-by-day below. + var twelve=(function(){ + var bg=[],mel=[],t=0,u=30/110; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var introTheme=[F4,F4,F4,F4,E4,F4,G4],introLanes=[1,1,1,1,2,1,3],introDurs=[1,1,1,1,1,1,2]; // "On the [n]th day of Christmas" + var gaveTheme=[A4,Bb4,A4,G4,F4],gaveLanes=[0,2,0,3,1],gaveDurs=[1,1,1,1,4]; // "my true love gave to me" + // gifts[0]="a partridge in a pear tree" (always last) ... gifts[11]="twelve drummers drumming" (day 12 only) + var gifts=[ + {th:[G4,A4,Bb4,C5,A4,G4,F4],ln:[3,0,2,0,0,3,1],du:[1,1,1,1,1,1,4]}, + {th:[D5,C5,D5,C5],ln:[3,0,3,0],du:[2,2,2,4]}, + {th:[C5,Bb4,A4,G4],ln:[0,2,0,3],du:[2,2,2,4]}, + {th:[Bb4,A4,G4,F4],ln:[2,0,3,1],du:[2,2,2,4]}, + {th:[C5,Bb4,A4,G4],ln:[0,2,0,3],du:[4,4,4,6]}, // "five golden rings" — held, emphasized + {th:[A4,G4,F4,G4,A4],ln:[0,3,1,3,0],du:[2,1,1,2,4]}, + {th:[G4,A4,Bb4,A4,G4,F4],ln:[3,0,2,0,3,1],du:[1,1,2,1,1,4]}, + {th:[F4,G4,A4,G4,F4,G4],ln:[1,3,0,3,1,3],du:[1,1,2,1,1,4]}, + {th:[A4,Bb4,C5,Bb4,A4,G4],ln:[0,2,0,2,0,3],du:[1,1,2,1,1,4]}, + {th:[D5,C5,Bb4,C5,D5,C5],ln:[3,0,2,0,3,0],du:[1,1,2,1,1,4]}, + {th:[C5,C5,Bb4,A4,G4,A4,Bb4],ln:[0,0,2,0,3,0,2],du:[1,1,1,1,1,1,4]}, + {th:[F4,F4,G4,A4,G4,F4],ln:[1,1,3,0,3,1],du:[1,1,1,1,1,4]}, + ]; + for(var day=1;day<=12;day++){ + var v=0.2+day*0.02; + var inst=day<=4?'woodwind':day<=8?'strings':'brass'; + seq(introTheme,introLanes,introDurs,inst,v); + seq(gaveTheme,gaveLanes,gaveDurs,inst,v); + for(var g=day-1;g>=0;g--){ + var gift=gifts[g]; + seq(gift.th,gift.ln,gift.du,inst,g===4?v*1.3:v); + } + t+=0.5; + } + [F4,F4*5/4,F4*3/2,F4*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});bg.push({t:t,f:f,d:2.0,i:'brass',v:0.4});}); + bg.push({t:t,f:F4/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4}); + return {n:"12 Days of Christmas",composer:"English (1780)",bpm:110,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel}; + })(); + // Sung as Refrain, Verse, Refrain, Verse, Refrain — the existing melody below is the + // famous "Hark! The herald angels sing" REFRAIN; the "Joyful, all ye nations rise" + // VERSE (a different tune) was previously missing, so the refrain just looped forever. + var hark=(function(){ + var bg=[],mel=[],t=0,u=30/115; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>1)bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.3}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var refTheme=[C4,F4,F4,E4,F4,A4,A4,G4,C5,A4,A4,G4,C5,A4,A4,G4,A4,Bb4,G4,A4,Bb4,C5,A4,F4,G4,Bb4,A4,G4,F4]; + var refLanes=[0,1,1,2,1,0,0,3,0,0,0,3,0,0,0,3,0,2,3,0,2,0,0,1,3,2,0,3,1]; + var refDurs=[2,4,2,2,4,2,2,4,2,2,2,4,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,8]; + // "Joyful, all ye nations rise, / join the triumph of the skies, / with angelic host proclaim, / Christ is born in Bethlehem." + var verseTheme=[F4,G4,A4,Bb4,C5,Bb4,A4, F4,G4,A4,Bb4,C5,D5,C5, F4,G4,A4,Bb4,C5,Bb4,A4, F4,G4,A4,F4,G4,A4,F4]; + var verseLanes=[1,3,0,2,0,2,0, 1,3,0,2,0,3,0, 1,3,0,2,0,2,0, 1,3,0,1,3,0,1]; + var verseDurs=[2,1,1,2,2,1,4, 2,1,1,2,2,1,4, 2,1,1,2,2,1,4, 2,1,1,2,1,1,8]; + seq(refTheme,refLanes,refDurs,'woodwind',0.22,0); + for(var rep=0;rep<2;rep++){ + var v=0.28+rep*0.12; + var inst=rep===0?'strings':'brass'; + seq(verseTheme,verseLanes,verseDurs,inst,v,rep+1); + seq(refTheme,refLanes,refDurs,inst,v,rep+1); + t+=0.4; + } + [F4,F4*5/4,F4*3/2,F4*2].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'choir',v:0.45});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.4});}); + bg.push({t:t,f:F4/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4}); + return {n:"Hark the Herald Angels Sing",composer:"Mendelssohn (1840)",bpm:115,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel}; + })(); // ── O Come All Ye Faithful (G major → transposed to C) ── - var ocome=carol("O Come All Ye Faithful","J.F. Wade (1743)",100, - [C4,C4,G4,C4,D4,G4,E4,D4,E4,F4,E4,D4,C4,B4,A4,B4,C5,B4,A4,G4,A4,E4,F4,E4,D4,E4,F4,D4,C4,D4,C4,Bb4,A4,G4,F4,E4,F4,E4,D4,C4], - [0,0,3,0,1,3,2,1,2,1,2,1,0,3,0,3,0,3,0,3,0,2,1,2,1,2,1,1,0,1,0,2,0,3,1,2,1,2,1,0], - [4,2,4,2,4,6,2,2,2,2,2,2,4,2,2,2,4,2,2,4,2,2,2,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,2,6], - 3,C4); + // Verse + the DISTINCT "O come, let us adore Him" refrain (repeated three times, + // ending "Christ the Lord"), which was previously missing entirely. + var ocome=(function(){ + var bg=[],mel=[],t=0,u=30/100; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>1)bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.3}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var verseTheme=[C4,C4,G4,C4,D4,G4,E4,D4,E4,F4,E4,D4,C4,B4,A4,B4,C5,B4,A4,G4,A4,E4,F4,E4,D4,E4,F4,D4,C4,D4,C4,Bb4,A4,G4,F4,E4,F4,E4,D4,C4]; + var verseLanes=[0,0,3,0,1,3,2,1,2,1,2,1,0,3,0,3,0,3,0,3,0,2,1,2,1,2,1,1,0,1,0,2,0,3,1,2,1,2,1,0]; + var verseDurs=[4,2,4,2,4,6,2,2,2,2,2,2,4,2,2,2,4,2,2,4,2,2,2,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,2,6]; + // "O come, let us adore Him," (x3) "Christ the Lord." + var adoreTheme=[G4,A4,G4,F4,E4,D4,C4]; + var adoreLanes=[3,0,3,1,2,1,0]; + var adoreDurs=[2,2,2,2,2,2,4]; + var lordTheme=[F4,E4,D4,C4]; + var lordLanes=[1,2,1,0]; + var lordDurs=[2,2,2,8]; + for(var rep=0;rep<3;rep++){ + var v=0.2+rep*0.09; + var inst=rep===0?'woodwind':rep===1?'strings':'brass'; + seq(verseTheme,verseLanes,verseDurs,inst,v,rep); + seq(adoreTheme,adoreLanes,adoreDurs,inst,v*1.1,rep); + seq(adoreTheme,adoreLanes,adoreDurs,inst,v*1.1,rep); + seq(adoreTheme,adoreLanes,adoreDurs,inst,v*1.2,rep); + seq(lordTheme,lordLanes,lordDurs,inst,v*1.2,rep); + t+=0.4; + } + [C4,C4*5/4,C4*3/2,C4*2].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'choir',v:0.45});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.4});}); + bg.push({t:t,f:C4/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4}); + return {n:"O Come All Ye Faithful",composer:"J.F. Wade (1743)",bpm:100,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel}; + })(); // ── God Rest Ye Merry Gentlemen (E minor / A minor) ── var godrest=carol("God Rest Ye Merry Gentlemen","English (1500s)",120, @@ -3546,11 +3732,43 @@ const CHRISTMAS_PIECES=(function(){ 4,A4); // ── Angels We Have Heard on High ── - var angels=carol("Angels We Have Heard on High","French (1862)",110, - [F4,F4,F4,F4,A4,G4,F4,F4,G4,A4,F4,D4,C4,D4,F4,G4,A4,G4,F4,E4,F4,G4,A4,G4,F4,E4,F4,G4,A4,G4,F4,G4,F4], - [1,1,1,1,0,3,1,1,3,0,1,1,0,1,1,3,0,3,1,2,1,3,0,3,1,2,1,3,0,3,1,3,1], - [2,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,2,2,2,2,4,8], - 4,F4); + // Verse + the famous melismatic "Glo-o-o-ria in excelsis Deo" refrain (sung twice + // per verse in the traditional carol), which was previously missing entirely. + var angels=(function(){ + var bg=[],mel=[],t=0,u=30/110; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>1)bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.3}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var verseTheme=[F4,F4,F4,F4,A4,G4,F4,F4,G4,A4,F4,D4,C4,D4,F4,G4,A4,G4,F4,E4,F4,G4,A4,G4,F4,E4,F4,G4,A4,G4,F4,G4,F4]; + var verseLanes=[1,1,1,1,0,3,1,1,3,0,1,1,0,1,1,3,0,3,1,2,1,3,0,3,1,2,1,3,0,3,1,3,1]; + var verseDurs=[2,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,4,2,2,2,2,2,4,2,2,2,2,2,2,2,2,4,8]; + // "Glo-o-o-o-o-ria" melisma, then "in excelsis Deo" — the refrain repeats twice. + var gloriaTheme=[C5,D5,C5,Bb4,A4,G4,A4,Bb4,C5,D5,C5,Bb4,A4,G4,F4]; + var gloriaLanes=[0,1,0,2,0,3,0,2,0,1,0,2,0,3,1]; + var gloriaDurs=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]; + var deoTheme=[F4,G4,A4,Bb4,A4,G4,F4]; + var deoLanes=[1,3,0,2,0,3,1]; + var deoDurs=[2,2,2,2,2,2,4]; + for(var rep=0;rep<3;rep++){ + var v=0.2+rep*0.09; + var inst=rep===0?'woodwind':rep===1?'strings':'brass'; + seq(verseTheme,verseLanes,verseDurs,inst,v,rep); + seq(gloriaTheme,gloriaLanes,gloriaDurs,inst,v*1.15,rep); + seq(deoTheme,deoLanes,deoDurs,inst,v*1.15,rep); + seq(gloriaTheme,gloriaLanes,gloriaDurs,inst,v*1.15,rep); + seq(deoTheme,deoLanes,deoDurs,inst,v*1.15,rep); + t+=0.4; + } + [F4,F4*5/4,F4*3/2,F4*2].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'choir',v:0.45});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.4});}); + bg.push({t:t,f:F4/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4}); + return {n:"Angels We Have Heard on High",composer:"French (1862)",bpm:110,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel}; + })(); // ── O Holy Night (in C major) ── var oholynight=carol("O Holy Night","A. Adam (1847)",72, @@ -3569,11 +3787,37 @@ const CHRISTMAS_PIECES=(function(){ 4,F4); // ── We Three Kings (E minor) ── - var threekings=carol("We Three Kings","J.H. Hopkins Jr. (1857)",105, - [B4,A4,G4,A4,B4,B4,B4,A4,G4,A4,B4,A4,G4,F4,E4,E4,B4,B4,C5,D5,D5,C5,B4,A4,B4,C5,B4,A4,G4,A4,G4,E4], - [3,0,3,0,3,3,3,0,3,0,3,0,3,1,2,2,3,3,0,1,1,0,3,0,3,0,3,0,3,0,3,2], - [4,2,4,2,2,2,4,2,4,2,2,2,2,2,4,6,4,2,2,4,2,2,4,2,2,2,2,2,2,2,2,8], - 4,E4); + // Verse ("We three kings of Orient are...") + the DISTINCT refrain + // ("O star of wonder, star of night...") which was previously missing entirely. + var threekings=(function(){ + var bg=[],mel=[],t=0,u=30/105; // u = one eighth-note unit + function seq(theme,lanes,durs,inst,v,rep){ + theme.forEach(function(f,i){ + var d=durs[i]*u; + bg.push({t:t,f:f,d:d*0.85,i:inst,v:v}); + if(rep>1)bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.3}); + mel.push({t:t,l:lanes[i],d:d,f:f}); + t+=d; + }); + } + var verseTheme=[B4,A4,G4,A4,B4,B4,B4,A4,G4,A4,B4,A4,G4,F4,E4,E4,B4,B4,C5,D5,D5,C5,B4,A4,B4,C5,B4,A4,G4,A4,G4,E4]; + var verseLanes=[3,0,3,0,3,3,3,0,3,0,3,0,3,1,2,2,3,3,0,1,1,0,3,0,3,0,3,0,3,0,3,2]; + var verseDurs=[4,2,4,2,2,2,4,2,4,2,2,2,2,2,4,6,4,2,2,4,2,2,4,2,2,2,2,2,2,2,2,8]; + // "O star of wonder, star of night, / star with royal beauty bright, / westward leading, still proceeding, / guide us to thy perfect light." + var refTheme=[B4,B4,A4,G4,A4,B4,C5,B4, B4,B4,A4,G4,A4,B4,B4, E5,D5,C5,B4,A4,G4,A4,B4, A4,B4,C5,B4,A4,G4,E4]; + var refLanes=[3,3,0,3,0,3,1,3, 3,3,0,3,0,3,3, 1,1,1,3,0,3,0,3, 0,3,1,3,0,3,2]; + var refDurs=[2,2,2,1,1,2,2,4, 2,2,2,1,1,2,4, 2,2,2,2,1,1,1,4, 2,2,2,2,1,1,4]; + for(var rep=0;rep<4;rep++){ + var v=0.2+rep*0.08; + var inst=rep<1?'woodwind':rep<3?'strings':'brass'; + seq(verseTheme,verseLanes,verseDurs,inst,v,rep); + seq(refTheme,refLanes,refDurs,inst,v*1.15,rep); + t+=0.4; + } + [E4,E4*5/4,E4*3/2,E4*2].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'choir',v:0.45});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.4});}); + bg.push({t:t,f:E4/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4}); + return {n:"We Three Kings",composer:"J.H. Hopkins Jr. (1857)",bpm:105,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel}; + })(); return [jingle,silent,deck,wish,ocometree,firstnoel,joy,away,twelve,hark,ocome,godrest,whatchild,angels,oholynight,wenceslas,threekings]; })(); From 581e26644eb306e419574a6e4002b98488ed45f2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 11:05:23 +0000 Subject: [PATCH 2/2] Fix remaining pitch/rhythm errors in Christmas carols - We Wish You a Merry Christmas: the closing "and a happy New Year!" line was truncated to 4 notes with an odd upward leap to D5; replaced with a proper 6-note stepwise descent resolving to the tonic. - What Child Is This: this carol is written to the Greensleeves melody, but its note data had diverged into a different contour. Rewrote it to mirror the Greensleeves shape one octave up so the two match as they should. --- index.html | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index c609280..06424c5 100644 --- a/index.html +++ b/index.html @@ -3572,10 +3572,12 @@ const CHRISTMAS_PIECES=(function(){ [0,3,0,3,1,3,0,1,3,0,2,3,0,3,1,2,1,2,1,3,0,3,1,2,1,3,0,2,0,0,3,1], [3,1,2,2,3,1,2,2,2,2,2,2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1,2,4], 4,F4); + // Final line "and a happy New Year!" (6 syllables) was previously truncated to 4 notes + // with an odd upward leap; fixed to a proper stepwise descent resolving to the tonic. var wish=carol("We Wish You a Merry Christmas","English (1500s)",140, - [G4,C5,C5,D5,C5,B4,A4,A4,D5,D5,E5,D5,C5,B4,G4,E5,E5,F5,E5,D5,C5,A4,G4,G4,A4,D5,B4,C5], - [3,0,0,1,0,3,0,0,1,1,2,1,0,3,3,2,2,1,2,1,0,0,3,3,0,1,3,0], - [2,2,2,2,2,2,4,2,2,2,2,2,2,4,2,2,2,2,2,2,4,2,2,2,2,2,2,10], + [G4,C5,C5,D5,C5,B4,A4,A4,D5,D5,E5,D5,C5,B4,G4,E5,E5,F5,E5,D5,C5,A4,G4,G4,B4,C5,A4,G4,E4,C4], + [3,0,0,1,0,3,0,0,1,1,2,1,0,3,3,2,2,1,2,1,0,0,3,3,3,0,0,3,2,0], + [2,2,2,2,2,2,4,2,2,2,2,2,2,4,2,2,2,2,2,2,4,2,2,2,2,1,1,2,2,8], 5,C4); var ocometree=carol("O Christmas Tree","German (1824)",100, [F4,Bb4,Bb4,Bb4,C5,D5,D5,D5,D5,Eb5,C5,A4,Bb4,F4,Bb4,Bb4,Bb4,C5,D5,D5,D5,D5,Eb5,C5,A4,Bb4], @@ -3725,10 +3727,12 @@ const CHRISTMAS_PIECES=(function(){ 4,E4); // ── What Child Is This (Greensleeves melody as a carol) ── + // Same tune as Greensleeves (it was written to fit that melody exactly), so this + // now mirrors the Greensleeves note-shape one octave up instead of diverging from it. var whatchild=carol("What Child Is This","English (1865)",95, - [E4,A4,B4,C5,B4,A4,G4,E4,D4,E4,F4,E4,D4,C4,E4,A4,B4,C5,B4,A4,Ab4,B4,C5,A4,A4], - [2,0,3,0,3,0,3,2,1,2,1,2,1,0,2,0,3,0,3,0,1,3,0,0,0], - [2,3,1,2,3,1,2,3,1,3,1,2,6,2,3,1,2,3,1,2,3,1,6,2,6], + [A4,C5,D5,E5,F5,E5,D5,B4, G4,A4,B4,C5,A4,A4,Ab4,A4, B4,Ab4,A4,C5,D5,E5,F5,E5, D5,B4,G4,A4,B4,C5,B4,A4], + [0,1,2,2,1,2,2,3, 3,0,3,1,0,0,0,0, 3,0,0,1,2,2,1,2, 2,3,3,0,3,1,3,0], + [2,3,1,2,3,1,2,3, 1,2,1,2,6,2,1,2, 3,6,2,3,1,2,3,1, 2,3,1,2,1,2,2,6], 4,A4); // ── Angels We Have Heard on High ──