一条乐句回来,你认出了它;对比段落让你等更久才听到回归;后来开头又带着更多乐器出现。这些是跨时间的关系,尺度大于单个音程或拍子。
曲式关注段落如何通过重复、变奏与对比组织起来。 最后一课重排少量材料,并分清旋律变化与织体变化。建议留十至十五分钟,再把整个系列的方法用于熟悉录音。
亲手排四小节,再解释差别
A 是 E–G–A–G,B 是 C–B–G–D,A′ 只把 A 的尾音 G 改成较高的 C。各小节均为 4/4、四个四分音符,速度 100 BPM。初始排列为 A–A–B–A。
选择小节,再改变材料。若只比较顺序,保持三个 A、一个 B,排成 A–B–A–A;再自由加入 A′。先预测什么时候会听到回归,再整段试听核对。也可以读谱并单独听某小节。
正在呈现知识画面
亲手排列音乐材料A 是 E–G–A–G,A′ 把尾音 G 改成 C,B 是 C–B–G–D。各小节四个四分音符,可换材料、单小节或整段试听。
{"id":"music-form-workshop","title":"亲手排列音乐材料","summary":"A 是 E–G–A–G,A′ 把尾音 G 改成 C,B 是 C–B–G–D。各小节四个四分音符,可换材料、单小节或整段试听。","height":800,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"mf-intro\" data-i18n=\"intro\"></p>\n<div id=\"controls\" class=\"mf-controls\"></div><div id=\"sheet\"></div><p id=\"meta\" class=\"mf-meta\"></p>\n<div id=\"answers\" class=\"mf-answers\" role=\"group\" data-i18n-label=\"question\"></div><div id=\"quiz-actions\" class=\"mf-actions\"></div>\n<p id=\"feedback\" class=\"mf-feedback\" role=\"status\" aria-live=\"polite\"></p>\n<div class=\"mf-actions\"><button id=\"play\" data-i18n=\"play\"></button><button id=\"stop\" data-i18n=\"stop\" disabled></button></div><p id=\"status\" class=\"mf-meta\" role=\"status\" data-i18n=\"idle\"></p>\n<label class=\"mf-volume\"><span data-i18n=\"volume\"></span><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\"></label>\n","css":"body{padding:24px}h2{font-size:22px;line-height:1.4}p{line-height:1.75}.mf-intro{color:var(--muted);margin:10px 0 18px}.mf-staff{display:block;width:100%;height:auto;max-height:270px;color:var(--ink);background:var(--surface);border:1px solid var(--rule);border-radius:8px;margin:16px 0}.mf-controls,.mf-actions,.mf-answers{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0}button,select{min-height:46px;font-size:16px}button{white-space:normal;line-height:1.5}.mf-controls button,.mf-answers button{flex:1 1 64px}.mf-actions button{flex:1 1 110px}label{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:16px;margin:12px 0}select{max-width:100%;background:var(--paper);color:var(--ink);border:1px solid var(--rule);padding:8px;border-radius:6px}.mf-feedback{border-left:3px solid var(--accent);padding:12px;background:var(--surface);min-height:3.5em;margin:14px 0;font-size:16px}.mf-meta{font-size:14px;color:var(--muted);margin:10px 0}.mf-staff [data-current=true]{color:var(--accent);filter:drop-shadow(0 0 1px currentColor)}.mf-staff [data-current=true] path{fill:currentColor}.mf-queue{display:flex;flex-wrap:wrap;gap:8px}.mf-queue button{min-width:50px}.mf-queue button[aria-pressed=true]{border-color:var(--accent);background:var(--accent-soft)}.mf-score{font-variant-numeric:tabular-nums}.mf-volume{display:flex;gap:12px;align-items:center;font-size:14px;border-top:1px solid var(--rule);padding-top:12px}.mf-volume input{min-width:80px;max-width:180px}.mf-answer-note{color:var(--muted);font-size:14px}button[aria-pressed=true]{background:var(--accent-soft);border-color:var(--accent)}[hidden]{display:none!important}@media(max-width:420px){body{padding:20px 14px}h2{font-size:20px}.mf-controls button{flex-basis:75px}}\n\n.mf-answers button{border:1px solid var(--rule);background:var(--surface);border-radius:8px;min-height:52px;padding:10px 14px;cursor:pointer}.mf-answers button:not(:disabled):hover{border-color:var(--accent);background:var(--accent-soft)}.mf-answers button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.mf-answers button[aria-pressed=true]{border:2px solid var(--accent);background:var(--accent-soft);font-weight:600}.mf-answers button:disabled{cursor:default}\n","js":"const GLYPHS = {\"treble\":\"M376 415C374 427 376 428 382 434C398 449 419 470 438 491C522 583 572 702 572 815C572 902 548 988 507 1048C492 1070 466 1098 455 1098C441 1098 410 1072 390 1050C316 968 292 843 292 739C292 681 299 616 306 575C308 563 309 561 297 551C233 498 164 437 112 373C43 287 0 194 0 87C0 -87 119 -252 364 -252C387 -252 413 -250 433 -246C444 -244 446 -243 448 -255C460 -322 475 -409 475 -456C475 -604 375 -622 316 -622C262 -622 236 -606 236 -593C236 -586 245 -583 268 -576C299 -567 335 -540 335 -482C335 -427 300 -380 239 -380C172 -380 132 -433 132 -495C132 -560 171 -658 322 -658C389 -658 519 -628 519 -458C519 -401 501 -306 490 -244C488 -232 489 -233 503 -227C604 -187 671 -102 671 11C671 139 577 252 430 252C404 252 404 252 401 270ZM470 943C503 943 530 916 530 861C530 792 497 728 419 650C403 634 379 611 356 591C349 585 345 586 343 599C339 625 337 659 337 691C337 847 409 943 470 943ZM361 262C364 243 364 244 346 238C258 208 201 129 201 44C201 -46 248 -110 316 -133C324 -136 336 -139 343 -139C351 -139 355 -134 355 -128C355 -121 347 -118 340 -115C298 -97 268 -54 268 -8C268 49 307 92 368 109C384 113 386 112 388 101L438 -197C440 -208 439 -208 424 -211C408 -214 388 -216 368 -216C193 -216 80 -119 80 20C80 79 90 158 173 252C233 319 279 356 326 394C336 402 338 401 340 390ZM430 103C428 115 429 118 441 117C522 110 589 42 589 -46C589 -109 551 -160 495 -188C483 -194 481 -194 479 -182Z\",\"bass\":\"M252 262C78 262 0 135 0 39C0 -41 42 -110 123 -110C186 -110 229 -66 229 -4C229 60 182 100 133 100C106 100 96 93 83 93C70 93 67 101 67 111C67 151 127 224 229 224C335 224 381 120 381 -37C381 -140 359 -260 297 -356C237 -449 134 -534 10 -605C1 -610 -5 -615 -5 -623C-5 -629 -1 -635 8 -635C13 -635 19 -633 25 -630C158 -565 286 -489 392 -375C479 -281 531 -159 531 -28C531 146 425 262 252 262ZM629 180C598 180 574 156 574 125C574 94 598 70 629 70C660 70 684 94 684 125C684 156 660 180 629 180ZM630 -71C599 -71 576 -94 576 -125C576 -156 599 -179 630 -179C661 -179 684 -156 684 -125C684 -94 661 -71 630 -71Z\",\"black\":\"M97 -125C186 -125 295 -43 295 42C295 93 255 125 198 125C88 125 0 44 0 -42C0 -94 43 -125 97 -125Z\",\"half\":\"M97 -125C262 -125 295 9 295 42C295 93 254 125 196 125C47 125 0 10 0 -42C0 -95 42 -125 97 -125ZM173 -46C128 -76 97 -87 75 -87C54 -87 42 -76 35 -64C32 -58 29 -51 29 -44C29 -22 51 5 120 45C170 74 201 84 221 84C240 84 251 75 258 63C261 57 264 51 264 44C264 24 243 0 173 -46Z\",\"whole\":\"M216 125C83 125 0 70 0 2C0 -65 57 -125 206 -125C370 -125 422 -68 422 2C422 73 309 125 216 125ZM111 63C122 98 159 103 190 103C259 103 314 29 314 -31C314 -38 313 -44 312 -50C307 -75 293 -92 268 -98C258 -101 247 -102 237 -102C228 -102 220 -101 211 -98C194 -93 178 -84 164 -72C156 -65 149 -58 143 -50C123 -27 108 7 108 39C108 47 109 55 111 63Z\",\"flag\":\"M238 -790C238 -790 264 -695 264 -617C264 -492 212 -374 149 -274C98 -195 56 -109 40 -13C37 3 29 9 19 9C8 9 0 6 0 -6V-245C66 -257 161 -393 197 -478C212 -512 221 -569 221 -628C221 -673 214 -720 197 -765C195 -771 194 -776 194 -780C194 -796 204 -805 210 -809C211 -810 213 -810 215 -810C222 -810 234 -804 238 -790Z\",\"quarterRest\":\"M78 -38C94 -58 108 -77 121 -98C123 -102 127 -110 127 -112C127 -113 127 -115 126 -116C124 -120 120 -121 115 -121C111 -121 103 -119 99 -118C94 -118 88 -115 83 -115C40 -115 1 -158 1 -211C1 -261 44 -310 117 -366C125 -372 135 -375 143 -375C150 -375 157 -373 158 -369C159 -366 160 -364 160 -362C160 -353 152 -345 144 -338C131 -338 120 -311 118 -302C115 -294 114 -285 114 -276C114 -245 129 -210 161 -204C166 -203 171 -203 177 -203C206 -203 239 -214 255 -220C256 -220 257 -221 258 -221C261 -222 263 -222 265 -222C268 -222 270 -221 270 -218C270 -206 244 -173 233 -161C195 -115 164 -78 164 -22C164 -18 165 -13 165 -9C169 49 205 97 231 138C234 143 235 148 235 153C235 163 231 172 231 172C231 172 83 348 66 365C61 370 54 373 48 373C38 373 28 366 28 352C28 347 29 342 32 336C36 325 93 274 93 202C93 165 78 122 33 75C23 65 19 54 19 46C19 32 29 22 29 22Z\",\"eighthRest\":\"M134 107C134 144 104 174 67 174C30 174 0 144 0 107C0 86 12 68 27 56C36 50 45 45 55 43C63 41 72 39 81 39C95 39 109 42 120 46C134 50 143 54 156 61C158 62 160 62 161 62C165 62 166 58 166 53C166 50 166 46 165 42C162 27 90 -172 72 -238C72 -250 95 -251 101 -251C112 -251 126 -249 136 -241C139 -239 237 112 237 112C241 130 246 146 247 151C247 161 237 166 235 167C233 167 230 167 224 163C217 157 167 97 134 97Z\",\"halfRest\":\"M282 24V116C282 131 270 142 256 142H26C11 142 0 131 0 116V24C0 10 11 -2 26 -2H256C270 -2 282 10 282 24Z\",\"wholeRest\":\"M282 -109V-17C282 -2 270 9 256 9H26C11 9 0 -2 0 -17V-109C0 -123 11 -135 26 -135H256C270 -135 282 -123 282 -109Z\",\"sharp\":\"M237 118C244 121 249 129 249 135V206C249 211 246 214 242 214C240 214 239 214 237 213C237 213 217 205 212 204C205 204 198 209 198 217V339C198 345 192 350 184 350C174 350 168 345 168 339V209C167 199 164 186 155 180C143 173 109 159 92 155C83 155 80 167 80 175V295C80 301 73 306 66 306C56 306 50 301 50 295V160C50 146 44 136 38 133C32 130 12 122 12 122C5 120 0 112 0 106V35C0 29 3 26 8 26C9 26 11 27 12 27C12 27 27 33 34 37C35 37 36 38 37 38C44 38 50 28 50 20V-79C50 -90 45 -99 39 -102C33 -104 12 -113 12 -113C5 -115 0 -123 0 -129V-200C0 -206 3 -209 8 -209C9 -209 11 -208 12 -208C12 -208 26 -202 35 -199C36 -198 37 -198 38 -198C45 -198 50 -209 50 -214V-337C50 -343 56 -348 63 -348C73 -348 80 -343 80 -337V-198C80 -185 85 -178 90 -176L151 -151C152 -151 154 -150 155 -150C163 -150 168 -162 168 -168V-293C168 -299 174 -304 181 -304C192 -304 198 -299 198 -293V-151C198 -143 202 -131 209 -128C216 -125 237 -117 237 -117C244 -114 249 -106 249 -100V-29C249 -24 246 -21 242 -21C240 -21 239 -21 237 -22L211 -32C205 -32 198 -26 198 -14V79C198 86 203 105 211 108ZM168 -45C162 -65 115 -85 92 -85C86 -85 81 -83 80 -80C78 -76 77 -54 77 -30C77 1 78 36 80 44C82 61 128 82 153 82C160 82 166 80 168 76C170 71 172 46 172 19C172 -8 170 -36 168 -45Z\",\"flat\":\"M12 -170C15 -174 18 -175 21 -175C24 -175 27 -173 27 -173C57 -156 81 -129 106 -112C195 -50 226 11 226 57C226 114 182 150 136 153C129 153 122 152 115 150C104 147 92 143 81 136C75 131 64 122 59 122C57 122 56 122 54 123C47 126 43 133 43 140C44 162 50 402 50 422C50 433 41 439 31 439C17 439 1 429 0 411C0 411 4 -160 12 -170ZM47 -81C47 -81 44 -21 44 19C44 35 45 47 46 51C50 63 76 85 90 93C99 98 108 100 116 100C126 100 135 96 141 89C151 78 157 61 157 42C157 24 152 3 140 -18C127 -42 98 -74 68 -93C64 -95 61 -96 58 -96C49 -96 47 -86 47 -81Z\",\"natural\":\"M141 181C139 181 138 180 137 180C137 180 73 157 47 157C41 157 37 158 37 162V329C37 336 31 341 25 341H12C5 341 0 336 0 329V-186C0 -192 3 -195 8 -195C9 -195 11 -194 12 -194C12 -194 14 -194 15 -193C29 -187 85 -163 114 -163C124 -163 131 -166 131 -174V-323C131 -330 136 -335 143 -335H156C162 -335 168 -330 168 -323V179C168 184 164 187 160 187C159 187 157 187 156 186ZM37 39C37 53 98 79 122 79C128 79 131 78 131 74V-29C131 -47 74 -70 49 -70C42 -70 37 -68 37 -64Z\",\"flagDown\":\"M240 760C254 718 261 668 261 623C261 564 236 480 221 446C184 362 134 281 0 236V1C0 -9 8 -14 16 -14C25 -14 38 -8 40 8C57 103 131 190 182 269C245 368 306 487 306 612C306 690 286 761 278 793C275 804 269 808 262 808C250 808 237 796 237 777C237 772 238 766 240 760Z\"};\n// Small SVG engraver for the deliberately limited notation taught in this course.\n// Bravura outlines: Steinberg, SIL OFL 1.1; see engraving/OFL.txt.\nconst MusicStaff=(()=>{\n const ns='http://www.w3.org/2000/svg',letters=['C','D','E','F','G','A','B'],pcs=[0,2,4,5,7,9,11];\n const el=(tag,attrs={})=>{const n=document.createElementNS(ns,tag);for(const [k,v] of Object.entries(attrs))n.setAttribute(k,v);return n;};\n const parse=p=>{const m=p.match(/^([A-G])([♯♭♮]?)([0-8])$/);if(!m)throw Error('invalid written pitch');const letter=letters.indexOf(m[1]),oct=+m[3],alter=m[2]==='♯'?1:m[2]==='♭'?-1:0;return {step:oct*7+letter,midi:(oct+1)*12+pcs[letter]+alter,alter,sign:m[2],name:p};};\n const fromStep=(step,alter=0)=>letters[((step%7)+7)%7]+(alter>0?'♯':alter<0?'♭':'')+Math.floor(step/7);\n const fromMidi=(m,flat=false)=>{const names=flat?['C','D♭','D','E♭','E','F','G♭','G','A♭','A','B♭','B']:['C','C♯','D','D♯','E','F','F♯','G','G♯','A','A♯','B'];return names[m%12]+(Math.floor(m/12)-1);};\n function glyph(parent,name,x,y,gap){parent.append(el('path',{d:GLYPHS[name],transform:`translate(${x} ${y}) scale(${gap/250} ${-gap/250})`,fill:'currentColor'}));}\n function render({clef='treble',notes=[],meter=null,key=null,labels=false,small=false,answerLabel='',barEnds=[]}={}){\n const gap=small?12:16,bottom=small?92:114,width=Math.max(300,Math.min(660,110+notes.length*66)),height=small?158:196;\n const svg=el('svg',{viewBox:`0 0 ${width} ${height}`,class:'mf-staff',role:'img','aria-label':answerLabel||viz.t('staff_description')});\n const line=(x1,y1,x2,y2,w=1)=>svg.append(el('line',{x1,y1,x2,y2,stroke:'currentColor','stroke-width':w,opacity:.7}));\n for(let i=0;i<5;i++)line(12,bottom-i*gap,width-12,bottom-i*gap);\n glyph(svg,clef,24,clef==='treble'?bottom-gap:bottom-3*gap,gap);\n let left=80;\n if(key==='G'){glyph(svg,'sharp',left,bottom-4*gap,gap);left+=32;}\n if(meter){const [n,d]=meter.split('/');for(const [v,y] of [[n,bottom-2.65*gap],[d,bottom-.7*gap]]){const t=el('text',{x:left,y,'font-size':gap*1.9,'font-weight':700,'text-anchor':'middle',fill:'currentColor'});t.textContent=v;svg.append(t);}left+=30;}\n const base=clef==='treble'?30:18,positions=[];\n notes.forEach((n,i)=>{\n const x=left+(i+.5)*(width-24-left)/Math.max(1,notes.length),group=el('g',{'data-note-index':i});svg.append(group);positions.push(x);\n if(n.blank){group.append(el('rect',{x:x-11,y:bottom-2*gap-13,width:22,height:26,fill:'var(--paper)',stroke:'currentColor','stroke-dasharray':'3 2'}));}\n else if(n.rest){glyph(group,({4:'wholeRest',2:'halfRest',1:'quarterRest','0.5':'eighthRest'})[n.value]||'quarterRest',x-6,bottom-(n.value===4?3:2)*gap,gap);}\n else{\n const pitches=n.chord||[n.pitch];\n for(const pitch of pitches){const p=parse(pitch),pos=p.step-base,y=bottom-pos*gap/2;\n for(let k=-2;k>=pos;k-=2)line(x-14,bottom-k*gap/2,x+14,bottom-k*gap/2,1.4);\n for(let k=10;k<=pos;k+=2)line(x-14,bottom-k*gap/2,x+14,bottom-k*gap/2,1.4);\n glyph(group,n.value===4?'whole':n.value===2?'half':'black',x-gap*.59,y,gap);\n if(p.sign)glyph(group,p.sign==='♯'?'sharp':p.sign==='♭'?'flat':'natural',x-gap*1.7,y,gap);\n if(n.dot)group.append(el('circle',{cx:x+gap*.95,cy:y-(pos%2===0?gap/2:0),r:gap*.13,fill:'currentColor'}));\n }\n const ps=pitches.map(parse).map(p=>p.step-base),highest=Math.max(...ps),lowest=Math.min(...ps),down=(highest+lowest)/2>=4;\n if(n.value!==4){const stemx=x+(down?-gap*.55:gap*.55),from=bottom-(down?highest:lowest)*gap/2,to=bottom-(down?lowest:highest)*gap/2+(down?gap*3.5:-gap*3.5);group.append(el('line',{x1:stemx,x2:stemx,y1:from,y2:to,stroke:'currentColor','stroke-width':1.5}));\n if(n.value===.5){glyph(group,down?'flagDown':'flag',stemx,to,gap);}\n }\n }\n if(labels){const text=el('text',{x,y:height-15,'font-size':14,'text-anchor':'middle',fill:'currentColor'});text.textContent=n.rest?viz.t('rest'):(n.chord||[n.pitch]).join(' ');svg.append(text);}\n if(n.barAfter||barEnds.includes(i))line(x+(width-24-left)/notes.length*.48,bottom,x+(width-24-left)/notes.length*.48,bottom-4*gap,1.5);\n });\n for(let i=0;i<notes.length-1;i++)if(notes[i].tie){const x=positions[i],x2=positions[i+1],pos=parse(notes[i].pitch).step-base,direction=pos>=4?-1:1,y=bottom-pos*gap/2+direction*gap;svg.append(el('path',{d:`M ${x} ${y} Q ${(x+x2)/2} ${y+direction*gap} ${x2} ${y}`,fill:'none',stroke:'currentColor','stroke-width':1.5}));}\n line(width-12,bottom,width-12,bottom-4*gap,1.5);return svg;\n }\n function score(notes,bpm=90){let beat=0;const events=[];notes.forEach(n=>{const length=n.value*(n.dot?1.5:1);if(!n.rest){if(n.continueTie){const last=events[events.length-1];last.duration+=length*60/bpm;}else for(const p of n.chord||[n.pitch])events.push({beat,hz:440*2**((parse(p).midi-69)/12),duration:length*60/bpm*.94,gain:.8,wave:'triangle',hold:true});}beat+=length;});return {bpm,beats:beat,events};}\n return {render,parse,fromStep,fromMidi,score};\n})();\n\nconst PRACTICE = {\"kind\": \"form\", \"bars\": 4};\nconst $=s=>document.querySelector(s),t=viz.t;\nfunction make(tag,attrs={},text=''){const e=document.createElement(tag);for(const [k,v]of Object.entries(attrs))e.setAttribute(k,v);e.textContent=text;return e;}\nfunction button(parent,key,fn,attrs={}){const b=make('button',{type:'button',...attrs},t(key));b.onclick=fn;parent.append(b);return b;}\nfunction select(parent,key,items,fn,id){const label=make('label',{},t(key)),s=make('select',{id});for(const [value,k]of items)s.append(make('option',{value},t(k)));s.onchange=()=>{player.stop();fn(s.value);};label.append(s);parent.append(label);return s;}\nlet tickAction=()=>{},currentScore=null;\nconst player=viz.audio({onState(s){document.body.dataset.audioState=s;$('#status').textContent=t(s==='idle'?'idle':s==='starting'?'starting':'playing');$('#stop').disabled=s==='idle';},onTick:b=>tickAction(b),onError:()=>$('#status').textContent=t('error')});\n$('#stop').onclick=()=>player.stop();$('#volume').oninput=e=>player.setVolume(+e.target.value/100);\n$('#play').onclick=()=>{if(currentScore)player.play(currentScore);};document.body.dataset.audioState='idle';\nconst controls=$('#controls'),answers=$('#answers'),feedback=$('#feedback'),sheet=$('#sheet'),meta=$('#meta');\nconst staff=(notes,extra={})=>{sheet.replaceChildren(MusicStaff.render({notes,...extra}));};\nconst note=pitch=>({pitch,value:1});\nfunction highlight(beat,notes){let at=0;for(const [i,n]of notes.entries()){const end=at+n.value*(n.dot?1.5:1);const g=sheet.querySelector(`[data-note-index=\"${i}\"]`);g?.setAttribute('data-current',String(beat>=at&&beat<end));at=end;}}\nconst shuffle=a=>{a=[...a];for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;};\nif(PRACTICE.kind==='map'){\n let clef='treble',step=32,alter=0;\n select(controls,'clef',[['treble','treble'],['bass','bass']],v=>{clef=v;step=v==='treble'?32:24;draw();},'clef');\n const row=make('div',{class:'mf-controls'});controls.append(row);\n button(row,'down',()=>{player.stop();step=Math.max(clef==='treble'?28:16,step-1);draw();},{id:'pitch-down'});\n button(row,'up',()=>{player.stop();step=Math.min(clef==='treble'?39:28,step+1);draw();},{id:'pitch-up'});\n select(controls,'accidental',[['0','natural'],['1','sharp'],['-1','flat']],v=>{alter=+v;draw();},'accidental');\n function draw(){const pitch=MusicStaff.fromStep(step,alter),notes=[note(pitch)];staff(notes,{clef,labels:true});feedback.textContent=pitch+' · '+t('position')+' '+(step-(clef==='treble'?30:18));meta.textContent=t('anchor_'+clef)+(step<(clef==='treble'?30:18)||step>(clef==='treble'?38:26)?' '+t('ledger'):'');currentScore=MusicStaff.score(notes);tickAction=b=>highlight(b,notes);}\n draw();\n}else if(['pitchQuiz','rhythmQuiz','keyQuiz'].includes(PRACTICE.kind)){\n let clefMode='treble',altered=false,deck=[],index=0,choice=null,answered=false,usedHint=false,assisted=0,correct=0;\n const stats=make('p',{class:'mf-meta mf-score',id:'round-score'});controls.append(stats);\n if(PRACTICE.kind==='pitchQuiz'){\n select(controls,'clef',[['treble','treble'],['bass','bass'],['mixed','mixed']],v=>{clefMode=v;start();},'clef');\n select(controls,'difficulty',[['plain','plain'],['altered','altered']],v=>{altered=v==='altered';start();},'difficulty');\n }\n const action=make('div',{class:'mf-actions'});$('#quiz-actions').append(action);\n const check=button(action,'check',()=>finish(false),{id:'check',disabled:''});\n const hint=button(action,'hint',()=>{if(!answered)usedHint=true;meta.textContent=hintText();},{id:'hint'});\n const reveal=button(action,'reveal',()=>finish(true),{id:'reveal'});\n const next=button(action,'next',()=>{if(index<deck.length-1){index++;draw();}else{feedback.textContent=t('finished');next.disabled=true;}},{id:'next',disabled:''});\n button(action,'restart',start,{id:'restart'});\n function bank(){\n if(PRACTICE.kind==='pitchQuiz'){\n const clefs=clefMode==='mixed'?['treble','bass']:[clefMode],qs=[];\n for(const clef of clefs)for(let step=clef==='treble'?28:16;step<=(clef==='treble'?39:28);step++){const alter=altered?[0,1,-1][Math.floor(Math.random()*3)]:0,pitch=MusicStaff.fromStep(step,alter);qs.push({clef,step,pitch,answer:pitch,notes:[note(pitch)]});}\n return shuffle(qs).slice(0,12);\n }\n if(PRACTICE.kind==='rhythmQuiz')return shuffle([\n ['4/4',[note('C5'),{rest:true,value:1},{pitch:'C5',value:.5},{pitch:'C5',value:.5}],1],\n ['3/4',[note('C5'),note('C5')],1],['4/4',[{pitch:'C5',value:2},note('C5')],1],\n ['3/4',[{pitch:'C5',value:1,dot:true},{pitch:'C5',value:.5}],1],\n ['4/4',[{rest:true,value:2}],2],['6/8',[{pitch:'C5',value:1,dot:true},note('C5')],.5],\n ['6/8',[{pitch:'C5',value:.5},{rest:true,value:.5},note('C5')],1],\n ['4/4',[{pitch:'C5',value:1,dot:true},note('C5')],1.5],['3/4',[note('C5')],2],\n ['4/4',[{rest:true,value:1}],3],['3/4',[{rest:true,value:.5},note('C5')],1.5],['6/8',[{pitch:'C5',value:.5},{pitch:'C5',value:.5}],2]\n ].map(([meter,notes,missing])=>({meter,notes,missing,answer:String(missing),clef:'treble'})));\n return shuffle([\n {notes:[{pitch:'F4',value:4}],target:0,answer:'F♯4',sounds:['F♯4']},\n {notes:[note('F♮4'),note('F4'),note('G4'),note('A4')],target:1,answer:'F4',sounds:['F4','F4','G4','A4']},\n {notes:[{pitch:'F♮4',value:4,barAfter:true},{pitch:'F4',value:4}],target:1,answer:'F♯4',sounds:['F4','F♯4']},\n {notes:[{pitch:'F5',value:4}],target:0,answer:'F♯5',sounds:['F♯5']}\n ].map(q=>({...q,clef:'treble',key:'G',meter:'4/4'})));\n }\n function start(){player.stop();deck=bank();index=0;correct=0;assisted=0;draw();}\n function hintText(){const q=deck[index];if(PRACTICE.kind==='pitchQuiz')return t('anchor_'+q.clef)+' '+t('position')+' '+(q.step-(q.clef==='treble'?30:18));return t(PRACTICE.kind==='keyQuiz'?'key_hint':'bar_hint');}\n function draw(){player.stop();choice=null;answered=false;usedHint=false;check.disabled=true;next.disabled=true;reveal.disabled=false;hint.disabled=false;$('#play').disabled=PRACTICE.kind!=='pitchQuiz';feedback.textContent=t(PRACTICE.kind==='pitchQuiz'?'before':'before_check');meta.textContent=t(PRACTICE.kind==='pitchQuiz'?'question':PRACTICE.kind==='keyQuiz'?'key_prompt':'bar_prompt');answers.replaceChildren();\n const q=deck[index];let choices;\n if(PRACTICE.kind==='pitchQuiz'){const offsets=shuffle([-2,-1,1,2]).slice(0,2),accOther=MusicStaff.parse(q.pitch).alter===0?1:0;choices=shuffle([q.answer,...offsets.map(d=>MusicStaff.fromStep(q.step+d,MusicStaff.parse(q.pitch).alter)),MusicStaff.fromStep(q.step,accOther)]);staff(q.notes,{clef:q.clef});currentScore=MusicStaff.score(q.notes);}\n else if(PRACTICE.kind==='rhythmQuiz'){staff([...q.notes,{blank:true,value:q.missing}],{meter:q.meter});choices=['0.5','1','1.5','2','3','4'];currentScore=null;}\n else{staff(q.notes,{meter:q.meter,key:'G'});sheet.querySelector(`[data-note-index=\"${q.target}\"]`)?.setAttribute('data-current','true');meta.textContent+=' '+t('target_note')+' '+(q.target+1);choices=['F4','F♯4','F5','F♯5'];currentScore=MusicStaff.score(q.notes.map((n,i)=>({...n,pitch:q.sounds[i]})));}\n for(const c of choices){const b=make('button',{type:'button','data-answer':c,'aria-pressed':'false'},c==='0.5'?'½':c==='1.5'?'1½':c);b.onclick=()=>{if(answered)return;choice=c;check.disabled=false;[...answers.children].forEach(x=>x.setAttribute('aria-pressed',String(x===b)));};answers.append(b);}\n updateStats();tickAction=b=>highlight(b,q.notes);\n }\n function updateStats(){stats.textContent=t('round')+' '+(index+1)+' / '+deck.length+' · '+t('first_try')+' '+correct+' · '+t('assisted')+' '+assisted;}\n function finish(explicit){if(answered||(!explicit&&choice===null))return;player.stop();answered=true;if(explicit)usedHint=true;if(usedHint)assisted++;if(!usedHint&&choice===deck[index].answer)correct++;const q=deck[index];feedback.textContent=(explicit?'':t(choice===q.answer?'correct':'wrong')+' ')+t('answer')+' '+q.answer+'. '+hintText();\n if(PRACTICE.kind==='rhythmQuiz'){const missing=q.missing===3?{pitch:'C5',value:2,dot:true}:q.missing===1.5?{pitch:'C5',value:1,dot:true}:{pitch:'C5',value:q.missing};const ns=[...q.notes,missing];staff(ns,{meter:q.meter});currentScore=MusicStaff.score(ns);const sum=q.notes.reduce((a,n)=>a+n.value*(n.dot?1.5:1),0);feedback.textContent+=' '+t('bar_total')+' '+(sum+q.missing)+' − '+t('written')+' '+sum+' = '+q.missing;tickAction=b=>highlight(b,ns);}\n else if(PRACTICE.kind==='pitchQuiz')staff(q.notes,{clef:q.clef,labels:true});\n $('#play').disabled=false;check.disabled=true;reveal.disabled=true;hint.disabled=true;next.disabled=false;[...answers.children].forEach(b=>b.disabled=true);updateStats();}\n start();\n}else if(PRACTICE.kind==='symbols'){\n const kinds=[['whole',4,false,false],['half',2,false,false],['quarter',1,false,false],['eighth',.5,false,false],['dotted',1,false,true],['whole_rest',4,true,false],['half_rest',2,true,false],['quarter_rest',1,true,false],['eighth_rest',.5,true,false]];\n for(const [label,value,rest,dot] of kinds)button(controls,label,()=>draw(label,{pitch:'C5',value,rest,dot}));\n for(const tied of [true,false])button(controls,tied?'tied':'untied',()=>draw(tied?'tied':'untied',[{pitch:'C5',value:1,tie:tied},{pitch:'C5',value:1,continueTie:tied}]));\n function draw(label,n){player.stop();const notes=Array.isArray(n)?n:[n];staff(notes);feedback.textContent=t(label);meta.textContent=t('unit');const score=MusicStaff.score(notes);score.beats=4;for(let i=0;i<4;i++)score.events.push({beat:i,hz:1500,duration:.035,gain:.25});currentScore=score;tickAction=b=>highlight(b,notes);}\n draw('quarter',{pitch:'C5',value:1});\n}else if(PRACTICE.kind==='chord'){\n let root='C4',quality='major',inv=0;\n select(controls,'root',[['C4','C'],['D4','D'],['F4','F']],v=>{root=v;draw();},'root');\n select(controls,'quality',[['major','major'],['minor','minor']],v=>{quality=v;draw();},'quality');\n select(controls,'inversion',[['0','root_position'],['1','first_inversion'],['2','second_inversion']],v=>{inv=+v;draw();},'inversion');\n function draw(){const p=MusicStaff.parse(root),intervals=[0,quality==='major'?4:3,7],pitches=intervals.map((v,i)=>{const step=p.step+i*2,plain=MusicStaff.parse(MusicStaff.fromStep(step));return MusicStaff.fromStep(step,p.midi+v-plain.midi);});for(let i=0;i<inv;i++){const p=MusicStaff.parse(pitches.shift());pitches.push(MusicStaff.fromStep(p.step+7,p.alter));}const ns=[{chord:pitches,value:2}];staff(ns,{labels:false});feedback.textContent=t('root')+': '+root+' · '+t('quality')+': '+t(quality)+' · '+pitches.join(' – ');meta.textContent=t('chord_tip');currentScore=MusicStaff.score(ns);tickAction=b=>highlight(b,ns);}\n draw();\n}else if(PRACTICE.kind==='form'){\n const initial=PRACTICE.bars===8?['A','A','B','A','A','A′','B','A']:['A','A','B','A'];let order=[...initial],selected=0,melodyOnly=false;\n const material={A:['E4','G4','A4','G4'],'A′':['E4','G4','A4','C5'],B:['C5','B4','G4','D4']};\n const queue=make('div',{class:'mf-queue',id:'bar-queue'});controls.append(queue);\n const sel=select(controls,'material',[['A','A'],['A′','A′'],['B','B']],v=>{order[selected]=v;draw();},'material');\n const label=make('label'),box=make('input',{type:'checkbox',id:'melody-only'});label.append(box,make('span',{},t('melody_only')));controls.append(label);box.onchange=()=>{player.stop();melodyOnly=box.checked;draw();};\n button(controls,'reset',()=>{player.stop();order=[...initial];selected=0;draw();},{id:'reset'});\n button($('#quiz-actions'),'one_bar',()=>play(false),{id:'play-bar'});$('#play').textContent=t('full');$('#play').onclick=()=>play(true);\n function getScore(sequence){const s={bpm:100,beats:sequence.length*4,events:[]};sequence.forEach((name,bar)=>{const ns=material[name].map(note),m=MusicStaff.score(ns,100);s.events.push(...m.events.map(e=>({...e,beat:e.beat+bar*4})));if(!melodyOnly)for(const beat of [0,2])for(const midi of [36,48,52,55])s.events.push({beat:bar*4+beat,hz:440*2**((midi-69)/12),duration:.95,gain:midi===36?.65:.35,wave:'triangle'});});return s;}\n function draw(){queue.replaceChildren();order.forEach((m,i)=>{const b=make('button',{type:'button','data-bar':i,'aria-pressed':String(i===selected)},(i+1)+' · '+m);b.onclick=()=>{player.stop();selected=i;draw();};queue.append(b);});sel.value=order[selected];staff(material[order[selected]].map(note),{meter:'4/4'});feedback.textContent=order.join(' · ');meta.textContent=t('arrange_tip');}\n function play(all){const start=selected,sequence=all?order:[order[selected]];tickAction=beat=>{if(beat<0){highlight(-1,material[order[selected]].map(note));return;}const b=all?Math.min(order.length-1,Math.floor(beat/4)):start;if(b!==selected){selected=b;draw();}highlight(beat%4,material[order[selected]].map(note));};player.play(getScore(sequence));}\n draw();\n}\n","audio":true,"strings":{"accidental":"变音记号","altered":"加入变音记号","anchor_bass":"参照:从下往上第四线、两点之间是 F3。","anchor_treble":"参照:从下往上第二线是 G4。每换到相邻线或间,音名字母走一步。","answer":"答案:","arrange_tip":"选择小节,再替换材料并试听。谱面显示所选小节,整段播放时跟随当前小节。","assisted":"使用过提示的题目","bar":"小节","bar_hint":"先把拍号换成四分音符单位,再把已有时值相加,从小节总值中减去。","bar_prompt":"补齐这一小节。方框是缺少的时值,请选择需要几个四分音符拍。","bar_total":"小节总值","bass":"低音谱号","before":"选好答案,再核对解释。","before_check":"先选择答案,再检查或主动显示解释。","check":"检查答案","choose_p":"P 是移调","choose_q":"Q 是移调","chord_tip":"从下往上读叠音。改变转位时,根音标签保持不变。","clef":"谱号","compare_title":"先作答,再显示图示","comparison_correct":"P 保留全部音程,Q 改了内部一个音。请核对揭示后的距离。","correct":"正确。","difficulty":"题目范围","dotted":"附点四分音符 · 1½","down":"下移一个线/间","eighth":"八分音符 · ½","eighth_rest":"八分休止符 · ½","error":"音频未能启动,请重试或阅读文字说明。","finished":"本轮完成。可复习最后一题解释,或开始新一轮。","first_inversion":"第一转位","first_try":"首次作答正确","flat":"降号 ♭","full":"播放整段编排","half":"二分音符 · 2","half_rest":"二分休止符 · 2","heading":"亲手排列音乐材料","hint":"显示位置提示","idle":"点击播放才发声,修改会停止播放。","intro":"选择四小节中的任一小节,替换为 A、A′ 或 B,与初始 A–A–B–A 比较。","inversion":"低音位置","key_hint":"调号一个升号表示各八度 F 升高,除非临时记号改变它。小节线重置临时记号,不清除调号。","key_prompt":"结合调号与临时变音记号,读出突出显示的音。","ledger":"短加线延伸五线谱,计数方法与谱内线相同。","major":"大三","material":"所选小节的材料","melody_only":"只听旋律","minor":"小三","missing":"缺少","mixed":"混合谱号","natural":"还原 ♮","next":"下一题","one_bar":"只播放所选小节","plain":"自然音","play":"播放","playing":"正在播放…","position":"相对最下方线的位置步数(线到相邻间为 1):","pulse":"参照脉冲","quality":"性质","quarter":"四分音符 · 1","quarter_rest":"四分休止符 · 1","question":"读出音名、八度与变音记号。先选答案,再核对。","reset":"恢复初始编排","rest":"休止","restart":"新一轮","reveal":"显示解释","root":"根音","root_position":"原位","round":"轮次进度","second_inversion":"第二转位","sharp":"升号 ♯","staff_description":"五线谱。无法看图时可使用位置说明。","starting":"正在准备声音…","stop":"停止","symbols":"音符与休止符时值","target_note":"读第几音:","tied":"两个四分音符延音 · 2","treble":"高音谱号","unit":"这里展示符号片段,不是完整小节;时值用四分音符单位。每次试听四拍,轻脉冲在休止处继续计时。","untied":"两个独立四分音符 · 1 + 1","up":"上移一个线/间","volume":"音量","whole":"全音符 · 4","whole_rest":"全休止符 · 此处为 4","written":"已有时值","wrong":"还不对,请对照解释检查所选答案。"}}
A–A–B–A 先重复建立 A,再对比、回归;A–B–A–A 更早出现对比。把 A 换成 A′ 还改变了材料,应与改顺序分开描述。字母在这组比较中保留身份,即使 B 放到开头也仍称为 B。
这里用一小节材料学习,不等于真实歌曲的完整段落。这种微型 AABA 不能代表 32 小节 AABA 歌曲形式的乐句长度与和声惯例。乐句与段落
重复可以保留某些东西,同时改变其他东西
熟悉曲调可能换乐器回来,也可能只改一个音。决定是否换字母前,先说明你跟踪哪一层相似性。
| 关系 | 什么仍可辨认 | 本课改变了什么 |
| 重复 | 完整 A 旋律 | 旋律不变 |
| 变奏 A′ | A 的开头与节奏 | 最后一个音 |
| 对比 B | 共同脉冲与伴奏 | 更多音高模式 |
| 织体变化 | 完整 A 旋律 | 伴奏声部进入 |
A′ 的撇号表示「与 A 相关的版本」。这不是按改了几个音就能机械计算的规则:有时一个小改动在结构上很关键,有时表面大幅变化仍保留主题。
比较三种不同的边界
每版包含两个四拍单元。第一版重复 A,同时加入伴奏;第二版把 A 最后的 G 改成更高的 C;第三版用 B 替换 A,但固定伴奏。
正在呈现知识画面
这个边界改变了什么?第一对重复 A,同时加入伴奏。第二对伴奏固定,仅把 A 末尾 G 改成 C,成为 A′。第三对伴奏固定,把 A 换成 B。
{"id":"music-form-change","title":"这个边界改变了什么?","summary":"第一对重复 A,同时加入伴奏。第二对伴奏固定,仅把 A 末尾 G 改成 C,成为 A′。第三对伴奏固定,把 A 换成 B。","height":760,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"mf-intro\" data-i18n=\"intro\"></p>\n<div id=\"variants\" class=\"mf-options\" role=\"group\" data-i18n-label=\"choose\"></div>\n<p id=\"facts\" class=\"mf-facts\" aria-live=\"polite\"></p>\n<svg id=\"plot\" class=\"mf-plot\" viewBox=\"0 0 540 200\" aria-hidden=\"true\"></svg>\n<div class=\"mf-axis\"><span data-i18n=\"axis\"></span><span id=\"duration\"></span></div>\n<div id=\"groups\" class=\"mf-groups\"></div><p id=\"notation\" class=\"mf-notation\"></p><p class=\"mf-key\" data-i18n=\"key\"></p>\n<div id=\"envelope-panel\" hidden><svg id=\"envelope-plot\" class=\"mf-plot\" viewBox=\"0 0 540 120\" aria-hidden=\"true\"></svg><p class=\"mf-key\" data-i18n=\"envelope\"></p></div>\n<div class=\"mf-actions\"><button id=\"play\" data-i18n=\"play\"></button><button id=\"stop\" data-i18n=\"stop\" disabled></button></div>\n<div class=\"mf-progress\" aria-hidden=\"true\"><span id=\"progress\"></span></div><p id=\"status\" class=\"mf-status\" role=\"status\" data-i18n=\"idle\"></p>\n<div class=\"mf-volume\"><label for=\"volume\" data-i18n=\"volume\"></label><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\"></div>\n","css":"body{padding:24px}h2{font-size:22px;line-height:1.4}.mf-intro{color:var(--muted);margin:10px 0 18px;line-height:1.7}.mf-options{display:flex;gap:8px;flex-wrap:wrap}.mf-options button{flex:1 1 125px;min-height:46px;font-size:16px;white-space:normal;line-height:1.45;text-align:left;padding:10px 12px}.mf-options button[aria-pressed=true]{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.mf-facts{margin:16px 0;padding:14px;border-left:3px solid var(--accent);background:var(--surface);font-size:16px;line-height:1.75;min-height:4em}.mf-plot{width:100%;height:auto;display:block;margin:12px 0 0;background:var(--surface);border:1px solid var(--rule);border-radius:8px}.mf-axis{display:flex;justify-content:space-between;font-size:13px;color:var(--muted);gap:16px;margin:6px 0}.mf-notation{font-size:15px;line-height:1.8;overflow-wrap:anywhere;margin:10px 0;color:var(--ink)}.mf-groups{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0}.mf-groups span{padding:6px 10px;border:1px solid var(--rule);border-radius:5px;font-size:15px;white-space:pre}.mf-actions{display:flex;gap:8px;margin:18px 0 12px}.mf-actions button{min-height:46px;font-size:16px;flex:1}.mf-progress{height:3px;background:var(--rule);overflow:hidden}.mf-progress span{display:block;height:100%;background:var(--accent);width:0}.mf-status{font-size:14px;line-height:1.7;margin:10px 0;min-height:3.4em;color:var(--muted)}.mf-volume{display:flex;gap:12px;align-items:center;margin-top:12px;border-top:1px solid var(--rule);padding-top:12px;font-size:14px}.mf-volume input{min-width:60px;max-width:180px}.mf-volume label{white-space:nowrap}.mf-key{color:var(--muted);font-size:14px;line-height:1.6;margin:12px 0}@media(max-width:420px){body{padding:20px 14px}h2{font-size:20px}.mf-options button{flex-basis:105px}.mf-facts{padding:12px}}\n\n.mf-groups span.is-current{border-color:var(--accent);background:var(--accent-soft);box-shadow:inset 0 -2px 0 var(--accent)}\n\n[hidden]{display:none!important}\n#blind-answers{flex-wrap:wrap}#blind-answers button{flex:1 1 100px;white-space:normal;line-height:1.5;min-width:0;padding:10px}\n","js":"const MODEL = {\"mode\":\"notes\",\"min\":36,\"max\":86,\"blind\":false,\"variants\":[{\"score\":{\"bpm\":100,\"beats\":8,\"events\":[{\"midi\":64,\"hz\":329.627557,\"beat\":0,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":1,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":69,\"hz\":440.0,\"beat\":2,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":3,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":64,\"hz\":329.627557,\"beat\":4,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":5,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":69,\"hz\":440.0,\"beat\":6,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":7,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":36,\"hz\":65.406391,\"beat\":4,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":36,\"hz\":65.406391,\"beat\":6,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":48,\"hz\":130.812783,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":48,\"hz\":130.812783,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":4,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":5,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":6,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":7,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"}]},\"notation\":\"E G A G | E G A G\",\"groups\":[\"A\",\"A\"]},{\"score\":{\"bpm\":100,\"beats\":8,\"events\":[{\"midi\":64,\"hz\":329.627557,\"beat\":0,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":1,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":69,\"hz\":440.0,\"beat\":2,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":3,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":36,\"hz\":65.406391,\"beat\":0,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":36,\"hz\":65.406391,\"beat\":2,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":48,\"hz\":130.812783,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":48,\"hz\":130.812783,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":0,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":1,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":2,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":3,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":64,\"hz\":329.627557,\"beat\":4,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":5,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":69,\"hz\":440.0,\"beat\":6,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":72,\"hz\":523.251131,\"beat\":7,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":36,\"hz\":65.406391,\"beat\":4,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":36,\"hz\":65.406391,\"beat\":6,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":48,\"hz\":130.812783,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":48,\"hz\":130.812783,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":4,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":5,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":6,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":7,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"}]},\"notation\":\"E G A G | E G A C\",\"groups\":[\"A\",\"A′\"]},{\"score\":{\"bpm\":100,\"beats\":8,\"events\":[{\"midi\":64,\"hz\":329.627557,\"beat\":0,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":1,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":69,\"hz\":440.0,\"beat\":2,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":3,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":36,\"hz\":65.406391,\"beat\":0,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":36,\"hz\":65.406391,\"beat\":2,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":48,\"hz\":130.812783,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":0,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":48,\"hz\":130.812783,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":2,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":0,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":1,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":2,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":3,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":72,\"hz\":523.251131,\"beat\":4,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":71,\"hz\":493.883301,\"beat\":5,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":67,\"hz\":391.995436,\"beat\":6,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":62,\"hz\":293.664768,\"beat\":7,\"duration\":0.45,\"gain\":0.85,\"wave\":\"triangle\",\"layer\":\"melody\"},{\"midi\":36,\"hz\":65.406391,\"beat\":4,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":36,\"hz\":65.406391,\"beat\":6,\"duration\":0.7,\"gain\":0.75,\"wave\":\"triangle\",\"layer\":\"bass\"},{\"midi\":48,\"hz\":130.812783,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":4,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":48,\"hz\":130.812783,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":52,\"hz\":164.813778,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":55,\"hz\":195.997718,\"beat\":6,\"duration\":1.1,\"gain\":0.35,\"wave\":\"triangle\",\"layer\":\"chord\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":4,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":5,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":6,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"},{\"midi\":86,\"hz\":1174.659072,\"beat\":7,\"duration\":0.035,\"gain\":0.3,\"wave\":\"sine\",\"layer\":\"pulse\"}]},\"notation\":\"E G A G | C B G D\",\"groups\":[\"A\",\"B\"]}]};\nconst $ = s => document.querySelector(s);\nconst variants = MODEL.variants;\nlet selected = 0;\nlet quizRevealed = !MODEL.blind;\nfunction visibility(){for(const q of ['#facts','#plot','.mf-axis','#groups','#notation','.mf-key'])$(q).toggleAttribute('hidden',!quizRevealed);document.body.dataset.quizRevealed=String(quizRevealed);}\nconst svgNS = 'http://www.w3.org/2000/svg';\nfunction node(tag, attrs = {}) { const el = document.createElementNS(svgNS, tag); for (const [k,v] of Object.entries(attrs)) el.setAttribute(k,v); return el; }\nfunction draw() {\n const v = variants[selected], plot = $('#plot'); plot.replaceChildren();\n $('#facts').textContent = viz.t('fact'+selected);\n $('#duration').textContent = viz.t('clip') + ' ' + (v.score.beats * 60 / v.score.bpm).toFixed(1) + ' ' + viz.t('seconds');\n $('#notation').textContent = v.notation;\n if (MODEL.mode === 'partials') {\n const e=v.score.events[0], p=e.partials;\n $('#envelope-panel').hidden=false;\n const envelope=$('#envelope-plot');envelope.replaceChildren();\n const attack=Math.min(e.attack,e.duration/2),points=[[12,108],[12+516*attack/e.duration,12]];\n for(let i=1;i<=60;i++){const t=attack+(e.duration-attack)*i/60,amplitude=Math.pow(.0001/(.05*e.gain),(t-attack)/(e.duration-attack));points.push([12+516*t/e.duration,108-amplitude*96]);}\n points.push([528,108]);envelope.append(node('polyline',{points:points.map(p=>p.join(',')).join(' '),fill:'none',stroke:'var(--accent)','stroke-width':3}));\n for(let i=0;i<4;i++) {\n plot.append(node('rect',{x:40+i*125,y:160-(p[i]||0)*125,width:58,height:(p[i]||0)*125,fill:'var(--accent)'}));\n const t=node('text',{x:69+i*125,y:185,'text-anchor':'middle',fill:'var(--ink)','font-size':18}); t.textContent=(i+1)+'×';plot.append(t);\n }\n } else {\n const events=v.score.events, min=MODEL.min??Math.min(...events.map(e=>e.midi)), max=MODEL.max??Math.max(...events.map(e=>e.midi));\n for(let i=0;i<=v.score.beats;i++) plot.append(node('line',{x1:12+i/v.score.beats*516,x2:12+i/v.score.beats*516,y1:16,y2:170,stroke:'var(--rule)'}));\n for(const e of events){\n const x=12+e.beat/v.score.beats*516;\n if(MODEL.mode==='pulse') plot.append(node('rect',{x,y:166-e.gain*140,width:Math.max(4,516/v.score.beats*.38),height:e.gain*140,rx:3,fill:'var(--accent)'}));\n else { const y=160-(e.midi-min)/Math.max(1,max-min)*130; plot.append(node('rect',{x,y:y-4,width:Math.max(4,Math.min(528-x,e.duration*v.score.bpm/60/v.score.beats*516)),height:8,rx:3,fill:e.layer==='bass'?'var(--ink)':'var(--accent)',opacity:e.layer==='chord'?'.45':'1'})); }\n }\n }\n if(MODEL.mode!=='partials') plot.append(node('line',{id:'cursor',x1:12,x2:12,y1:10,y2:177,stroke:'var(--ink)','stroke-width':2,visibility:'hidden'}));\n const groups=$('#groups'); groups.replaceChildren();\n for (const label of v.groups || []) {const chip=document.createElement('span');chip.textContent=label;groups.append(chip);}\n if(MODEL.blind)visibility();\n [...$('#variants').children].forEach((b,i)=>b.setAttribute('aria-pressed',String(i===selected)));\n}\nconst player=viz.audio({onState(state){document.body.dataset.audioState=state;$('#play').disabled=state!=='idle';$('#stop').disabled=state==='idle';$('#status').textContent=viz.t(state==='idle'?'idle':state==='starting'?'starting':'playing');},onTick(beat){const fraction=beat<0?0:Math.min(1,beat/variants[selected].score.beats);$('#progress').style.width=(fraction*100)+'%';const cursor=$('#cursor');if(cursor){cursor.setAttribute('x1',12+fraction*516);cursor.setAttribute('x2',12+fraction*516);cursor.setAttribute('visibility',beat<0?'hidden':'visible');}const chips=[...$('#groups').children];chips.forEach((el,i)=>el.classList.toggle('is-current',beat>=0&&i===Math.min(chips.length-1,Math.floor(fraction*chips.length))));},onError(){$('#status').textContent=viz.t('error');}});\nvariants.forEach((v,i)=>{const b=document.createElement('button');b.type='button';b.dataset.variant=i;b.textContent=viz.t('option'+i);b.onclick=()=>{player.stop();selected=i;draw();};$('#variants').append(b);});\n$('#play').onclick=()=>player.play(variants[selected].score);\n$('#stop').onclick=()=>player.stop();\n$('#volume').oninput=e=>player.setVolume(Number(e.target.value)/100);\ndocument.body.dataset.audioState='idle';draw();\n\nif(MODEL.blind){\n const panel=document.createElement('div');panel.className='mf-actions';panel.id='blind-answers';\n const result=document.createElement('p');result.className='mf-facts';result.id='blind-feedback';result.setAttribute('role','status');result.textContent=viz.t('before');\n for(const [key,correct]of [['choose_p',true],['choose_q',false],['reveal',null]]){const b=document.createElement('button');b.type='button';b.textContent=viz.t(key);b.dataset.blindAnswer=key;b.onclick=()=>{player.stop();quizRevealed=true;visibility();result.textContent=(correct===null?'':viz.t(correct?'correct':'wrong')+' ')+viz.t('comparison_correct');};panel.append(b);}\n $('#variants').after(panel,result);\n}\n","audio":true,"strings":{"axis":"时间 → · 越高表示音越高","choose":"选择版本","clip":"段长:","envelope":"振幅随时间 →(1.4 秒)。曲线升到峰值后衰减,比较起音阶段占用的时间。","error":"声音未能启动,请重试,或阅读图示与文字说明。","fact0":"A 仍是 E G A G。后半加入低音、和弦、脉冲;变厚本身不等于新旋律。","fact1":"E G A G 变成 E G A C。共同开头与节奏支持把它听作变奏。","fact2":"E G A G 变成 C B G D。更多音高关系改变,节奏与伴奏不变。","heading":"这个边界改变了什么?","idle":"选择版本后播放。修改会停止播放,每段播完自动停止。","intro":"比较两个四拍单元,分清加声部、局部变奏和对比乐句。","key":"从左向右读。横条表示起音与预设时长,上下叠放表示同时发声。也可直接读音名与说明。","option0":"同旋律 · 加声部","option1":"A → A′ · 改一音","option2":"A → B · 对比","play":"播放此版","playing":"正在播放,可随时停止。","seconds":"秒","starting":"正在准备声音…","stop":"停止","volume":"音量"}}
先跟上方旋律,再听整体。第一对后半变厚,旋律却精确重复。A′ 版共享 E–G–A 开头,最后一音的修改容易定位。A–B 版的新 C–B–G–D 则在这个小例子中形成更强的旋律对比。
不能把每个更响、更密的瞬间都叫副歌。副歌是在歌曲整体组织中的角色,常涉及重复音乐与歌词,无法只凭音量识别;也有音乐根本不用主歌—副歌结构。
给新序列写结构图
一段音乐依次出现四音想法 X、只改尾音的 X、对比想法 Y、原来的 X。怎样用字母保留前两段的关系?若最后的 X 加入打击乐,它的旋律必须改用新字母吗?
查看一种有依据的分析
可以写 A–A′–B–A:A′ 记录相关变奏,B 记录对比,末尾 A 记录回归。最后 A 可注明「加入打击乐」,旋律标签仍保留 A;加一个声部并不直接改变旋律身份。
这个答案说明了跟踪层级。若另画织体图,最后的入口可以标为变化。分析可以同时使用多层描述,不必把所有差异挤进一串字母。
综合练习:读、听、修改八小节
下面的乐句有八小节。先不播放,读第一小节:高音谱号、4/4,四个四分音符 E4–G4–A4–G4。数四拍再听;选择第六小节,在谱上找出只改一个音的变奏。
正在呈现知识画面
综合练习:八小节作品初始结构 A A B A A A′ B A,四分音符每分钟 100 拍。C 大三伴奏固定,可单独听旋律;所选小节有可读的高音谱表。
{"id":"music-final-piece","title":"综合练习:八小节作品","summary":"初始结构 A A B A A A′ B A,四分音符每分钟 100 拍。C 大三伴奏固定,可单独听旋律;所选小节有可读的高音谱表。","height":800,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"mf-intro\" data-i18n=\"intro\"></p>\n<div id=\"controls\" class=\"mf-controls\"></div><div id=\"sheet\"></div><p id=\"meta\" class=\"mf-meta\"></p>\n<div id=\"answers\" class=\"mf-answers\" role=\"group\" data-i18n-label=\"question\"></div><div id=\"quiz-actions\" class=\"mf-actions\"></div>\n<p id=\"feedback\" class=\"mf-feedback\" role=\"status\" aria-live=\"polite\"></p>\n<div class=\"mf-actions\"><button id=\"play\" data-i18n=\"play\"></button><button id=\"stop\" data-i18n=\"stop\" disabled></button></div><p id=\"status\" class=\"mf-meta\" role=\"status\" data-i18n=\"idle\"></p>\n<label class=\"mf-volume\"><span data-i18n=\"volume\"></span><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\"></label>\n","css":"body{padding:24px}h2{font-size:22px;line-height:1.4}p{line-height:1.75}.mf-intro{color:var(--muted);margin:10px 0 18px}.mf-staff{display:block;width:100%;height:auto;max-height:270px;color:var(--ink);background:var(--surface);border:1px solid var(--rule);border-radius:8px;margin:16px 0}.mf-controls,.mf-actions,.mf-answers{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0}button,select{min-height:46px;font-size:16px}button{white-space:normal;line-height:1.5}.mf-controls button,.mf-answers button{flex:1 1 64px}.mf-actions button{flex:1 1 110px}label{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:16px;margin:12px 0}select{max-width:100%;background:var(--paper);color:var(--ink);border:1px solid var(--rule);padding:8px;border-radius:6px}.mf-feedback{border-left:3px solid var(--accent);padding:12px;background:var(--surface);min-height:3.5em;margin:14px 0;font-size:16px}.mf-meta{font-size:14px;color:var(--muted);margin:10px 0}.mf-staff [data-current=true]{color:var(--accent);filter:drop-shadow(0 0 1px currentColor)}.mf-staff [data-current=true] path{fill:currentColor}.mf-queue{display:flex;flex-wrap:wrap;gap:8px}.mf-queue button{min-width:50px}.mf-queue button[aria-pressed=true]{border-color:var(--accent);background:var(--accent-soft)}.mf-score{font-variant-numeric:tabular-nums}.mf-volume{display:flex;gap:12px;align-items:center;font-size:14px;border-top:1px solid var(--rule);padding-top:12px}.mf-volume input{min-width:80px;max-width:180px}.mf-answer-note{color:var(--muted);font-size:14px}button[aria-pressed=true]{background:var(--accent-soft);border-color:var(--accent)}[hidden]{display:none!important}@media(max-width:420px){body{padding:20px 14px}h2{font-size:20px}.mf-controls button{flex-basis:75px}}\n\n.mf-answers button{border:1px solid var(--rule);background:var(--surface);border-radius:8px;min-height:52px;padding:10px 14px;cursor:pointer}.mf-answers button:not(:disabled):hover{border-color:var(--accent);background:var(--accent-soft)}.mf-answers button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.mf-answers button[aria-pressed=true]{border:2px solid var(--accent);background:var(--accent-soft);font-weight:600}.mf-answers button:disabled{cursor:default}\n","js":"const GLYPHS = {\"treble\":\"M376 415C374 427 376 428 382 434C398 449 419 470 438 491C522 583 572 702 572 815C572 902 548 988 507 1048C492 1070 466 1098 455 1098C441 1098 410 1072 390 1050C316 968 292 843 292 739C292 681 299 616 306 575C308 563 309 561 297 551C233 498 164 437 112 373C43 287 0 194 0 87C0 -87 119 -252 364 -252C387 -252 413 -250 433 -246C444 -244 446 -243 448 -255C460 -322 475 -409 475 -456C475 -604 375 -622 316 -622C262 -622 236 -606 236 -593C236 -586 245 -583 268 -576C299 -567 335 -540 335 -482C335 -427 300 -380 239 -380C172 -380 132 -433 132 -495C132 -560 171 -658 322 -658C389 -658 519 -628 519 -458C519 -401 501 -306 490 -244C488 -232 489 -233 503 -227C604 -187 671 -102 671 11C671 139 577 252 430 252C404 252 404 252 401 270ZM470 943C503 943 530 916 530 861C530 792 497 728 419 650C403 634 379 611 356 591C349 585 345 586 343 599C339 625 337 659 337 691C337 847 409 943 470 943ZM361 262C364 243 364 244 346 238C258 208 201 129 201 44C201 -46 248 -110 316 -133C324 -136 336 -139 343 -139C351 -139 355 -134 355 -128C355 -121 347 -118 340 -115C298 -97 268 -54 268 -8C268 49 307 92 368 109C384 113 386 112 388 101L438 -197C440 -208 439 -208 424 -211C408 -214 388 -216 368 -216C193 -216 80 -119 80 20C80 79 90 158 173 252C233 319 279 356 326 394C336 402 338 401 340 390ZM430 103C428 115 429 118 441 117C522 110 589 42 589 -46C589 -109 551 -160 495 -188C483 -194 481 -194 479 -182Z\",\"bass\":\"M252 262C78 262 0 135 0 39C0 -41 42 -110 123 -110C186 -110 229 -66 229 -4C229 60 182 100 133 100C106 100 96 93 83 93C70 93 67 101 67 111C67 151 127 224 229 224C335 224 381 120 381 -37C381 -140 359 -260 297 -356C237 -449 134 -534 10 -605C1 -610 -5 -615 -5 -623C-5 -629 -1 -635 8 -635C13 -635 19 -633 25 -630C158 -565 286 -489 392 -375C479 -281 531 -159 531 -28C531 146 425 262 252 262ZM629 180C598 180 574 156 574 125C574 94 598 70 629 70C660 70 684 94 684 125C684 156 660 180 629 180ZM630 -71C599 -71 576 -94 576 -125C576 -156 599 -179 630 -179C661 -179 684 -156 684 -125C684 -94 661 -71 630 -71Z\",\"black\":\"M97 -125C186 -125 295 -43 295 42C295 93 255 125 198 125C88 125 0 44 0 -42C0 -94 43 -125 97 -125Z\",\"half\":\"M97 -125C262 -125 295 9 295 42C295 93 254 125 196 125C47 125 0 10 0 -42C0 -95 42 -125 97 -125ZM173 -46C128 -76 97 -87 75 -87C54 -87 42 -76 35 -64C32 -58 29 -51 29 -44C29 -22 51 5 120 45C170 74 201 84 221 84C240 84 251 75 258 63C261 57 264 51 264 44C264 24 243 0 173 -46Z\",\"whole\":\"M216 125C83 125 0 70 0 2C0 -65 57 -125 206 -125C370 -125 422 -68 422 2C422 73 309 125 216 125ZM111 63C122 98 159 103 190 103C259 103 314 29 314 -31C314 -38 313 -44 312 -50C307 -75 293 -92 268 -98C258 -101 247 -102 237 -102C228 -102 220 -101 211 -98C194 -93 178 -84 164 -72C156 -65 149 -58 143 -50C123 -27 108 7 108 39C108 47 109 55 111 63Z\",\"flag\":\"M238 -790C238 -790 264 -695 264 -617C264 -492 212 -374 149 -274C98 -195 56 -109 40 -13C37 3 29 9 19 9C8 9 0 6 0 -6V-245C66 -257 161 -393 197 -478C212 -512 221 -569 221 -628C221 -673 214 -720 197 -765C195 -771 194 -776 194 -780C194 -796 204 -805 210 -809C211 -810 213 -810 215 -810C222 -810 234 -804 238 -790Z\",\"quarterRest\":\"M78 -38C94 -58 108 -77 121 -98C123 -102 127 -110 127 -112C127 -113 127 -115 126 -116C124 -120 120 -121 115 -121C111 -121 103 -119 99 -118C94 -118 88 -115 83 -115C40 -115 1 -158 1 -211C1 -261 44 -310 117 -366C125 -372 135 -375 143 -375C150 -375 157 -373 158 -369C159 -366 160 -364 160 -362C160 -353 152 -345 144 -338C131 -338 120 -311 118 -302C115 -294 114 -285 114 -276C114 -245 129 -210 161 -204C166 -203 171 -203 177 -203C206 -203 239 -214 255 -220C256 -220 257 -221 258 -221C261 -222 263 -222 265 -222C268 -222 270 -221 270 -218C270 -206 244 -173 233 -161C195 -115 164 -78 164 -22C164 -18 165 -13 165 -9C169 49 205 97 231 138C234 143 235 148 235 153C235 163 231 172 231 172C231 172 83 348 66 365C61 370 54 373 48 373C38 373 28 366 28 352C28 347 29 342 32 336C36 325 93 274 93 202C93 165 78 122 33 75C23 65 19 54 19 46C19 32 29 22 29 22Z\",\"eighthRest\":\"M134 107C134 144 104 174 67 174C30 174 0 144 0 107C0 86 12 68 27 56C36 50 45 45 55 43C63 41 72 39 81 39C95 39 109 42 120 46C134 50 143 54 156 61C158 62 160 62 161 62C165 62 166 58 166 53C166 50 166 46 165 42C162 27 90 -172 72 -238C72 -250 95 -251 101 -251C112 -251 126 -249 136 -241C139 -239 237 112 237 112C241 130 246 146 247 151C247 161 237 166 235 167C233 167 230 167 224 163C217 157 167 97 134 97Z\",\"halfRest\":\"M282 24V116C282 131 270 142 256 142H26C11 142 0 131 0 116V24C0 10 11 -2 26 -2H256C270 -2 282 10 282 24Z\",\"wholeRest\":\"M282 -109V-17C282 -2 270 9 256 9H26C11 9 0 -2 0 -17V-109C0 -123 11 -135 26 -135H256C270 -135 282 -123 282 -109Z\",\"sharp\":\"M237 118C244 121 249 129 249 135V206C249 211 246 214 242 214C240 214 239 214 237 213C237 213 217 205 212 204C205 204 198 209 198 217V339C198 345 192 350 184 350C174 350 168 345 168 339V209C167 199 164 186 155 180C143 173 109 159 92 155C83 155 80 167 80 175V295C80 301 73 306 66 306C56 306 50 301 50 295V160C50 146 44 136 38 133C32 130 12 122 12 122C5 120 0 112 0 106V35C0 29 3 26 8 26C9 26 11 27 12 27C12 27 27 33 34 37C35 37 36 38 37 38C44 38 50 28 50 20V-79C50 -90 45 -99 39 -102C33 -104 12 -113 12 -113C5 -115 0 -123 0 -129V-200C0 -206 3 -209 8 -209C9 -209 11 -208 12 -208C12 -208 26 -202 35 -199C36 -198 37 -198 38 -198C45 -198 50 -209 50 -214V-337C50 -343 56 -348 63 -348C73 -348 80 -343 80 -337V-198C80 -185 85 -178 90 -176L151 -151C152 -151 154 -150 155 -150C163 -150 168 -162 168 -168V-293C168 -299 174 -304 181 -304C192 -304 198 -299 198 -293V-151C198 -143 202 -131 209 -128C216 -125 237 -117 237 -117C244 -114 249 -106 249 -100V-29C249 -24 246 -21 242 -21C240 -21 239 -21 237 -22L211 -32C205 -32 198 -26 198 -14V79C198 86 203 105 211 108ZM168 -45C162 -65 115 -85 92 -85C86 -85 81 -83 80 -80C78 -76 77 -54 77 -30C77 1 78 36 80 44C82 61 128 82 153 82C160 82 166 80 168 76C170 71 172 46 172 19C172 -8 170 -36 168 -45Z\",\"flat\":\"M12 -170C15 -174 18 -175 21 -175C24 -175 27 -173 27 -173C57 -156 81 -129 106 -112C195 -50 226 11 226 57C226 114 182 150 136 153C129 153 122 152 115 150C104 147 92 143 81 136C75 131 64 122 59 122C57 122 56 122 54 123C47 126 43 133 43 140C44 162 50 402 50 422C50 433 41 439 31 439C17 439 1 429 0 411C0 411 4 -160 12 -170ZM47 -81C47 -81 44 -21 44 19C44 35 45 47 46 51C50 63 76 85 90 93C99 98 108 100 116 100C126 100 135 96 141 89C151 78 157 61 157 42C157 24 152 3 140 -18C127 -42 98 -74 68 -93C64 -95 61 -96 58 -96C49 -96 47 -86 47 -81Z\",\"natural\":\"M141 181C139 181 138 180 137 180C137 180 73 157 47 157C41 157 37 158 37 162V329C37 336 31 341 25 341H12C5 341 0 336 0 329V-186C0 -192 3 -195 8 -195C9 -195 11 -194 12 -194C12 -194 14 -194 15 -193C29 -187 85 -163 114 -163C124 -163 131 -166 131 -174V-323C131 -330 136 -335 143 -335H156C162 -335 168 -330 168 -323V179C168 184 164 187 160 187C159 187 157 187 156 186ZM37 39C37 53 98 79 122 79C128 79 131 78 131 74V-29C131 -47 74 -70 49 -70C42 -70 37 -68 37 -64Z\",\"flagDown\":\"M240 760C254 718 261 668 261 623C261 564 236 480 221 446C184 362 134 281 0 236V1C0 -9 8 -14 16 -14C25 -14 38 -8 40 8C57 103 131 190 182 269C245 368 306 487 306 612C306 690 286 761 278 793C275 804 269 808 262 808C250 808 237 796 237 777C237 772 238 766 240 760Z\"};\n// Small SVG engraver for the deliberately limited notation taught in this course.\n// Bravura outlines: Steinberg, SIL OFL 1.1; see engraving/OFL.txt.\nconst MusicStaff=(()=>{\n const ns='http://www.w3.org/2000/svg',letters=['C','D','E','F','G','A','B'],pcs=[0,2,4,5,7,9,11];\n const el=(tag,attrs={})=>{const n=document.createElementNS(ns,tag);for(const [k,v] of Object.entries(attrs))n.setAttribute(k,v);return n;};\n const parse=p=>{const m=p.match(/^([A-G])([♯♭♮]?)([0-8])$/);if(!m)throw Error('invalid written pitch');const letter=letters.indexOf(m[1]),oct=+m[3],alter=m[2]==='♯'?1:m[2]==='♭'?-1:0;return {step:oct*7+letter,midi:(oct+1)*12+pcs[letter]+alter,alter,sign:m[2],name:p};};\n const fromStep=(step,alter=0)=>letters[((step%7)+7)%7]+(alter>0?'♯':alter<0?'♭':'')+Math.floor(step/7);\n const fromMidi=(m,flat=false)=>{const names=flat?['C','D♭','D','E♭','E','F','G♭','G','A♭','A','B♭','B']:['C','C♯','D','D♯','E','F','F♯','G','G♯','A','A♯','B'];return names[m%12]+(Math.floor(m/12)-1);};\n function glyph(parent,name,x,y,gap){parent.append(el('path',{d:GLYPHS[name],transform:`translate(${x} ${y}) scale(${gap/250} ${-gap/250})`,fill:'currentColor'}));}\n function render({clef='treble',notes=[],meter=null,key=null,labels=false,small=false,answerLabel='',barEnds=[]}={}){\n const gap=small?12:16,bottom=small?92:114,width=Math.max(300,Math.min(660,110+notes.length*66)),height=small?158:196;\n const svg=el('svg',{viewBox:`0 0 ${width} ${height}`,class:'mf-staff',role:'img','aria-label':answerLabel||viz.t('staff_description')});\n const line=(x1,y1,x2,y2,w=1)=>svg.append(el('line',{x1,y1,x2,y2,stroke:'currentColor','stroke-width':w,opacity:.7}));\n for(let i=0;i<5;i++)line(12,bottom-i*gap,width-12,bottom-i*gap);\n glyph(svg,clef,24,clef==='treble'?bottom-gap:bottom-3*gap,gap);\n let left=80;\n if(key==='G'){glyph(svg,'sharp',left,bottom-4*gap,gap);left+=32;}\n if(meter){const [n,d]=meter.split('/');for(const [v,y] of [[n,bottom-2.65*gap],[d,bottom-.7*gap]]){const t=el('text',{x:left,y,'font-size':gap*1.9,'font-weight':700,'text-anchor':'middle',fill:'currentColor'});t.textContent=v;svg.append(t);}left+=30;}\n const base=clef==='treble'?30:18,positions=[];\n notes.forEach((n,i)=>{\n const x=left+(i+.5)*(width-24-left)/Math.max(1,notes.length),group=el('g',{'data-note-index':i});svg.append(group);positions.push(x);\n if(n.blank){group.append(el('rect',{x:x-11,y:bottom-2*gap-13,width:22,height:26,fill:'var(--paper)',stroke:'currentColor','stroke-dasharray':'3 2'}));}\n else if(n.rest){glyph(group,({4:'wholeRest',2:'halfRest',1:'quarterRest','0.5':'eighthRest'})[n.value]||'quarterRest',x-6,bottom-(n.value===4?3:2)*gap,gap);}\n else{\n const pitches=n.chord||[n.pitch];\n for(const pitch of pitches){const p=parse(pitch),pos=p.step-base,y=bottom-pos*gap/2;\n for(let k=-2;k>=pos;k-=2)line(x-14,bottom-k*gap/2,x+14,bottom-k*gap/2,1.4);\n for(let k=10;k<=pos;k+=2)line(x-14,bottom-k*gap/2,x+14,bottom-k*gap/2,1.4);\n glyph(group,n.value===4?'whole':n.value===2?'half':'black',x-gap*.59,y,gap);\n if(p.sign)glyph(group,p.sign==='♯'?'sharp':p.sign==='♭'?'flat':'natural',x-gap*1.7,y,gap);\n if(n.dot)group.append(el('circle',{cx:x+gap*.95,cy:y-(pos%2===0?gap/2:0),r:gap*.13,fill:'currentColor'}));\n }\n const ps=pitches.map(parse).map(p=>p.step-base),highest=Math.max(...ps),lowest=Math.min(...ps),down=(highest+lowest)/2>=4;\n if(n.value!==4){const stemx=x+(down?-gap*.55:gap*.55),from=bottom-(down?highest:lowest)*gap/2,to=bottom-(down?lowest:highest)*gap/2+(down?gap*3.5:-gap*3.5);group.append(el('line',{x1:stemx,x2:stemx,y1:from,y2:to,stroke:'currentColor','stroke-width':1.5}));\n if(n.value===.5){glyph(group,down?'flagDown':'flag',stemx,to,gap);}\n }\n }\n if(labels){const text=el('text',{x,y:height-15,'font-size':14,'text-anchor':'middle',fill:'currentColor'});text.textContent=n.rest?viz.t('rest'):(n.chord||[n.pitch]).join(' ');svg.append(text);}\n if(n.barAfter||barEnds.includes(i))line(x+(width-24-left)/notes.length*.48,bottom,x+(width-24-left)/notes.length*.48,bottom-4*gap,1.5);\n });\n for(let i=0;i<notes.length-1;i++)if(notes[i].tie){const x=positions[i],x2=positions[i+1],pos=parse(notes[i].pitch).step-base,direction=pos>=4?-1:1,y=bottom-pos*gap/2+direction*gap;svg.append(el('path',{d:`M ${x} ${y} Q ${(x+x2)/2} ${y+direction*gap} ${x2} ${y}`,fill:'none',stroke:'currentColor','stroke-width':1.5}));}\n line(width-12,bottom,width-12,bottom-4*gap,1.5);return svg;\n }\n function score(notes,bpm=90){let beat=0;const events=[];notes.forEach(n=>{const length=n.value*(n.dot?1.5:1);if(!n.rest){if(n.continueTie){const last=events[events.length-1];last.duration+=length*60/bpm;}else for(const p of n.chord||[n.pitch])events.push({beat,hz:440*2**((parse(p).midi-69)/12),duration:length*60/bpm*.94,gain:.8,wave:'triangle',hold:true});}beat+=length;});return {bpm,beats:beat,events};}\n return {render,parse,fromStep,fromMidi,score};\n})();\n\nconst PRACTICE = {\"kind\": \"form\", \"bars\": 8};\nconst $=s=>document.querySelector(s),t=viz.t;\nfunction make(tag,attrs={},text=''){const e=document.createElement(tag);for(const [k,v]of Object.entries(attrs))e.setAttribute(k,v);e.textContent=text;return e;}\nfunction button(parent,key,fn,attrs={}){const b=make('button',{type:'button',...attrs},t(key));b.onclick=fn;parent.append(b);return b;}\nfunction select(parent,key,items,fn,id){const label=make('label',{},t(key)),s=make('select',{id});for(const [value,k]of items)s.append(make('option',{value},t(k)));s.onchange=()=>{player.stop();fn(s.value);};label.append(s);parent.append(label);return s;}\nlet tickAction=()=>{},currentScore=null;\nconst player=viz.audio({onState(s){document.body.dataset.audioState=s;$('#status').textContent=t(s==='idle'?'idle':s==='starting'?'starting':'playing');$('#stop').disabled=s==='idle';},onTick:b=>tickAction(b),onError:()=>$('#status').textContent=t('error')});\n$('#stop').onclick=()=>player.stop();$('#volume').oninput=e=>player.setVolume(+e.target.value/100);\n$('#play').onclick=()=>{if(currentScore)player.play(currentScore);};document.body.dataset.audioState='idle';\nconst controls=$('#controls'),answers=$('#answers'),feedback=$('#feedback'),sheet=$('#sheet'),meta=$('#meta');\nconst staff=(notes,extra={})=>{sheet.replaceChildren(MusicStaff.render({notes,...extra}));};\nconst note=pitch=>({pitch,value:1});\nfunction highlight(beat,notes){let at=0;for(const [i,n]of notes.entries()){const end=at+n.value*(n.dot?1.5:1);const g=sheet.querySelector(`[data-note-index=\"${i}\"]`);g?.setAttribute('data-current',String(beat>=at&&beat<end));at=end;}}\nconst shuffle=a=>{a=[...a];for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;};\nif(PRACTICE.kind==='map'){\n let clef='treble',step=32,alter=0;\n select(controls,'clef',[['treble','treble'],['bass','bass']],v=>{clef=v;step=v==='treble'?32:24;draw();},'clef');\n const row=make('div',{class:'mf-controls'});controls.append(row);\n button(row,'down',()=>{player.stop();step=Math.max(clef==='treble'?28:16,step-1);draw();},{id:'pitch-down'});\n button(row,'up',()=>{player.stop();step=Math.min(clef==='treble'?39:28,step+1);draw();},{id:'pitch-up'});\n select(controls,'accidental',[['0','natural'],['1','sharp'],['-1','flat']],v=>{alter=+v;draw();},'accidental');\n function draw(){const pitch=MusicStaff.fromStep(step,alter),notes=[note(pitch)];staff(notes,{clef,labels:true});feedback.textContent=pitch+' · '+t('position')+' '+(step-(clef==='treble'?30:18));meta.textContent=t('anchor_'+clef)+(step<(clef==='treble'?30:18)||step>(clef==='treble'?38:26)?' '+t('ledger'):'');currentScore=MusicStaff.score(notes);tickAction=b=>highlight(b,notes);}\n draw();\n}else if(['pitchQuiz','rhythmQuiz','keyQuiz'].includes(PRACTICE.kind)){\n let clefMode='treble',altered=false,deck=[],index=0,choice=null,answered=false,usedHint=false,assisted=0,correct=0;\n const stats=make('p',{class:'mf-meta mf-score',id:'round-score'});controls.append(stats);\n if(PRACTICE.kind==='pitchQuiz'){\n select(controls,'clef',[['treble','treble'],['bass','bass'],['mixed','mixed']],v=>{clefMode=v;start();},'clef');\n select(controls,'difficulty',[['plain','plain'],['altered','altered']],v=>{altered=v==='altered';start();},'difficulty');\n }\n const action=make('div',{class:'mf-actions'});$('#quiz-actions').append(action);\n const check=button(action,'check',()=>finish(false),{id:'check',disabled:''});\n const hint=button(action,'hint',()=>{if(!answered)usedHint=true;meta.textContent=hintText();},{id:'hint'});\n const reveal=button(action,'reveal',()=>finish(true),{id:'reveal'});\n const next=button(action,'next',()=>{if(index<deck.length-1){index++;draw();}else{feedback.textContent=t('finished');next.disabled=true;}},{id:'next',disabled:''});\n button(action,'restart',start,{id:'restart'});\n function bank(){\n if(PRACTICE.kind==='pitchQuiz'){\n const clefs=clefMode==='mixed'?['treble','bass']:[clefMode],qs=[];\n for(const clef of clefs)for(let step=clef==='treble'?28:16;step<=(clef==='treble'?39:28);step++){const alter=altered?[0,1,-1][Math.floor(Math.random()*3)]:0,pitch=MusicStaff.fromStep(step,alter);qs.push({clef,step,pitch,answer:pitch,notes:[note(pitch)]});}\n return shuffle(qs).slice(0,12);\n }\n if(PRACTICE.kind==='rhythmQuiz')return shuffle([\n ['4/4',[note('C5'),{rest:true,value:1},{pitch:'C5',value:.5},{pitch:'C5',value:.5}],1],\n ['3/4',[note('C5'),note('C5')],1],['4/4',[{pitch:'C5',value:2},note('C5')],1],\n ['3/4',[{pitch:'C5',value:1,dot:true},{pitch:'C5',value:.5}],1],\n ['4/4',[{rest:true,value:2}],2],['6/8',[{pitch:'C5',value:1,dot:true},note('C5')],.5],\n ['6/8',[{pitch:'C5',value:.5},{rest:true,value:.5},note('C5')],1],\n ['4/4',[{pitch:'C5',value:1,dot:true},note('C5')],1.5],['3/4',[note('C5')],2],\n ['4/4',[{rest:true,value:1}],3],['3/4',[{rest:true,value:.5},note('C5')],1.5],['6/8',[{pitch:'C5',value:.5},{pitch:'C5',value:.5}],2]\n ].map(([meter,notes,missing])=>({meter,notes,missing,answer:String(missing),clef:'treble'})));\n return shuffle([\n {notes:[{pitch:'F4',value:4}],target:0,answer:'F♯4',sounds:['F♯4']},\n {notes:[note('F♮4'),note('F4'),note('G4'),note('A4')],target:1,answer:'F4',sounds:['F4','F4','G4','A4']},\n {notes:[{pitch:'F♮4',value:4,barAfter:true},{pitch:'F4',value:4}],target:1,answer:'F♯4',sounds:['F4','F♯4']},\n {notes:[{pitch:'F5',value:4}],target:0,answer:'F♯5',sounds:['F♯5']}\n ].map(q=>({...q,clef:'treble',key:'G',meter:'4/4'})));\n }\n function start(){player.stop();deck=bank();index=0;correct=0;assisted=0;draw();}\n function hintText(){const q=deck[index];if(PRACTICE.kind==='pitchQuiz')return t('anchor_'+q.clef)+' '+t('position')+' '+(q.step-(q.clef==='treble'?30:18));return t(PRACTICE.kind==='keyQuiz'?'key_hint':'bar_hint');}\n function draw(){player.stop();choice=null;answered=false;usedHint=false;check.disabled=true;next.disabled=true;reveal.disabled=false;hint.disabled=false;$('#play').disabled=PRACTICE.kind!=='pitchQuiz';feedback.textContent=t(PRACTICE.kind==='pitchQuiz'?'before':'before_check');meta.textContent=t(PRACTICE.kind==='pitchQuiz'?'question':PRACTICE.kind==='keyQuiz'?'key_prompt':'bar_prompt');answers.replaceChildren();\n const q=deck[index];let choices;\n if(PRACTICE.kind==='pitchQuiz'){const offsets=shuffle([-2,-1,1,2]).slice(0,2),accOther=MusicStaff.parse(q.pitch).alter===0?1:0;choices=shuffle([q.answer,...offsets.map(d=>MusicStaff.fromStep(q.step+d,MusicStaff.parse(q.pitch).alter)),MusicStaff.fromStep(q.step,accOther)]);staff(q.notes,{clef:q.clef});currentScore=MusicStaff.score(q.notes);}\n else if(PRACTICE.kind==='rhythmQuiz'){staff([...q.notes,{blank:true,value:q.missing}],{meter:q.meter});choices=['0.5','1','1.5','2','3','4'];currentScore=null;}\n else{staff(q.notes,{meter:q.meter,key:'G'});sheet.querySelector(`[data-note-index=\"${q.target}\"]`)?.setAttribute('data-current','true');meta.textContent+=' '+t('target_note')+' '+(q.target+1);choices=['F4','F♯4','F5','F♯5'];currentScore=MusicStaff.score(q.notes.map((n,i)=>({...n,pitch:q.sounds[i]})));}\n for(const c of choices){const b=make('button',{type:'button','data-answer':c,'aria-pressed':'false'},c==='0.5'?'½':c==='1.5'?'1½':c);b.onclick=()=>{if(answered)return;choice=c;check.disabled=false;[...answers.children].forEach(x=>x.setAttribute('aria-pressed',String(x===b)));};answers.append(b);}\n updateStats();tickAction=b=>highlight(b,q.notes);\n }\n function updateStats(){stats.textContent=t('round')+' '+(index+1)+' / '+deck.length+' · '+t('first_try')+' '+correct+' · '+t('assisted')+' '+assisted;}\n function finish(explicit){if(answered||(!explicit&&choice===null))return;player.stop();answered=true;if(explicit)usedHint=true;if(usedHint)assisted++;if(!usedHint&&choice===deck[index].answer)correct++;const q=deck[index];feedback.textContent=(explicit?'':t(choice===q.answer?'correct':'wrong')+' ')+t('answer')+' '+q.answer+'. '+hintText();\n if(PRACTICE.kind==='rhythmQuiz'){const missing=q.missing===3?{pitch:'C5',value:2,dot:true}:q.missing===1.5?{pitch:'C5',value:1,dot:true}:{pitch:'C5',value:q.missing};const ns=[...q.notes,missing];staff(ns,{meter:q.meter});currentScore=MusicStaff.score(ns);const sum=q.notes.reduce((a,n)=>a+n.value*(n.dot?1.5:1),0);feedback.textContent+=' '+t('bar_total')+' '+(sum+q.missing)+' − '+t('written')+' '+sum+' = '+q.missing;tickAction=b=>highlight(b,ns);}\n else if(PRACTICE.kind==='pitchQuiz')staff(q.notes,{clef:q.clef,labels:true});\n $('#play').disabled=false;check.disabled=true;reveal.disabled=true;hint.disabled=true;next.disabled=false;[...answers.children].forEach(b=>b.disabled=true);updateStats();}\n start();\n}else if(PRACTICE.kind==='symbols'){\n const kinds=[['whole',4,false,false],['half',2,false,false],['quarter',1,false,false],['eighth',.5,false,false],['dotted',1,false,true],['whole_rest',4,true,false],['half_rest',2,true,false],['quarter_rest',1,true,false],['eighth_rest',.5,true,false]];\n for(const [label,value,rest,dot] of kinds)button(controls,label,()=>draw(label,{pitch:'C5',value,rest,dot}));\n for(const tied of [true,false])button(controls,tied?'tied':'untied',()=>draw(tied?'tied':'untied',[{pitch:'C5',value:1,tie:tied},{pitch:'C5',value:1,continueTie:tied}]));\n function draw(label,n){player.stop();const notes=Array.isArray(n)?n:[n];staff(notes);feedback.textContent=t(label);meta.textContent=t('unit');const score=MusicStaff.score(notes);score.beats=4;for(let i=0;i<4;i++)score.events.push({beat:i,hz:1500,duration:.035,gain:.25});currentScore=score;tickAction=b=>highlight(b,notes);}\n draw('quarter',{pitch:'C5',value:1});\n}else if(PRACTICE.kind==='chord'){\n let root='C4',quality='major',inv=0;\n select(controls,'root',[['C4','C'],['D4','D'],['F4','F']],v=>{root=v;draw();},'root');\n select(controls,'quality',[['major','major'],['minor','minor']],v=>{quality=v;draw();},'quality');\n select(controls,'inversion',[['0','root_position'],['1','first_inversion'],['2','second_inversion']],v=>{inv=+v;draw();},'inversion');\n function draw(){const p=MusicStaff.parse(root),intervals=[0,quality==='major'?4:3,7],pitches=intervals.map((v,i)=>{const step=p.step+i*2,plain=MusicStaff.parse(MusicStaff.fromStep(step));return MusicStaff.fromStep(step,p.midi+v-plain.midi);});for(let i=0;i<inv;i++){const p=MusicStaff.parse(pitches.shift());pitches.push(MusicStaff.fromStep(p.step+7,p.alter));}const ns=[{chord:pitches,value:2}];staff(ns,{labels:false});feedback.textContent=t('root')+': '+root+' · '+t('quality')+': '+t(quality)+' · '+pitches.join(' – ');meta.textContent=t('chord_tip');currentScore=MusicStaff.score(ns);tickAction=b=>highlight(b,ns);}\n draw();\n}else if(PRACTICE.kind==='form'){\n const initial=PRACTICE.bars===8?['A','A','B','A','A','A′','B','A']:['A','A','B','A'];let order=[...initial],selected=0,melodyOnly=false;\n const material={A:['E4','G4','A4','G4'],'A′':['E4','G4','A4','C5'],B:['C5','B4','G4','D4']};\n const queue=make('div',{class:'mf-queue',id:'bar-queue'});controls.append(queue);\n const sel=select(controls,'material',[['A','A'],['A′','A′'],['B','B']],v=>{order[selected]=v;draw();},'material');\n const label=make('label'),box=make('input',{type:'checkbox',id:'melody-only'});label.append(box,make('span',{},t('melody_only')));controls.append(label);box.onchange=()=>{player.stop();melodyOnly=box.checked;draw();};\n button(controls,'reset',()=>{player.stop();order=[...initial];selected=0;draw();},{id:'reset'});\n button($('#quiz-actions'),'one_bar',()=>play(false),{id:'play-bar'});$('#play').textContent=t('full');$('#play').onclick=()=>play(true);\n function getScore(sequence){const s={bpm:100,beats:sequence.length*4,events:[]};sequence.forEach((name,bar)=>{const ns=material[name].map(note),m=MusicStaff.score(ns,100);s.events.push(...m.events.map(e=>({...e,beat:e.beat+bar*4})));if(!melodyOnly)for(const beat of [0,2])for(const midi of [36,48,52,55])s.events.push({beat:bar*4+beat,hz:440*2**((midi-69)/12),duration:.95,gain:midi===36?.65:.35,wave:'triangle'});});return s;}\n function draw(){queue.replaceChildren();order.forEach((m,i)=>{const b=make('button',{type:'button','data-bar':i,'aria-pressed':String(i===selected)},(i+1)+' · '+m);b.onclick=()=>{player.stop();selected=i;draw();};queue.append(b);});sel.value=order[selected];staff(material[order[selected]].map(note),{meter:'4/4'});feedback.textContent=order.join(' · ');meta.textContent=t('arrange_tip');}\n function play(all){const start=selected,sequence=all?order:[order[selected]];tickAction=beat=>{if(beat<0){highlight(-1,material[order[selected]].map(note));return;}const b=all?Math.min(order.length-1,Math.floor(beat/4)):start;if(b!==selected){selected=b;draw();}highlight(beat%4,material[order[selected]].map(note));};player.play(getScore(sequence));}\n draw();\n}\n","audio":true,"strings":{"accidental":"变音记号","altered":"加入变音记号","anchor_bass":"参照:从下往上第四线、两点之间是 F3。","anchor_treble":"参照:从下往上第二线是 G4。每换到相邻线或间,音名字母走一步。","answer":"答案:","arrange_tip":"选择小节,再替换材料并试听。谱面显示所选小节,整段播放时跟随当前小节。","assisted":"使用过提示的题目","bar":"小节","bar_hint":"先把拍号换成四分音符单位,再把已有时值相加,从小节总值中减去。","bar_prompt":"补齐这一小节。方框是缺少的时值,请选择需要几个四分音符拍。","bar_total":"小节总值","bass":"低音谱号","before":"选好答案,再核对解释。","before_check":"先选择答案,再检查或主动显示解释。","check":"检查答案","choose_p":"P 是移调","choose_q":"Q 是移调","chord_tip":"从下往上读叠音。改变转位时,根音标签保持不变。","clef":"谱号","compare_title":"先作答,再显示图示","comparison_correct":"P 保留全部音程,Q 改了内部一个音。请核对揭示后的距离。","correct":"正确。","difficulty":"题目范围","dotted":"附点四分音符 · 1½","down":"下移一个线/间","eighth":"八分音符 · ½","eighth_rest":"八分休止符 · ½","error":"音频未能启动,请重试或阅读文字说明。","finished":"本轮完成。可复习最后一题解释,或开始新一轮。","first_inversion":"第一转位","first_try":"首次作答正确","flat":"降号 ♭","full":"播放整段编排","half":"二分音符 · 2","half_rest":"二分休止符 · 2","heading":"综合练习:八小节作品","hint":"显示位置提示","idle":"点击播放才发声,修改会停止播放。","intro":"读谱并聆听原创八小节,跟踪旋律、低音、和弦与回归;再做一次有意图的修改并解释。","inversion":"低音位置","key_hint":"调号一个升号表示各八度 F 升高,除非临时记号改变它。小节线重置临时记号,不清除调号。","key_prompt":"结合调号与临时变音记号,读出突出显示的音。","ledger":"短加线延伸五线谱,计数方法与谱内线相同。","major":"大三","material":"所选小节的材料","melody_only":"只听旋律","minor":"小三","missing":"缺少","mixed":"混合谱号","natural":"还原 ♮","next":"下一题","one_bar":"只播放所选小节","plain":"自然音","play":"播放","playing":"正在播放…","position":"相对最下方线的位置步数(线到相邻间为 1):","pulse":"参照脉冲","quality":"性质","quarter":"四分音符 · 1","quarter_rest":"四分休止符 · 1","question":"读出音名、八度与变音记号。先选答案,再核对。","reset":"恢复初始编排","rest":"休止","restart":"新一轮","reveal":"显示解释","root":"根音","root_position":"原位","round":"轮次进度","second_inversion":"第二转位","sharp":"升号 ♯","staff_description":"五线谱。无法看图时可使用位置说明。","starting":"正在准备声音…","stop":"停止","symbols":"音符与休止符时值","target_note":"读第几音:","tied":"两个四分音符延音 · 2","treble":"高音谱号","unit":"这里展示符号片段,不是完整小节;时值用四分音符单位。每次试听四拍,轻脉冲在休止处继续计时。","untied":"两个独立四分音符 · 1 + 1","up":"上移一个线/间","volume":"音量","whole":"全音符 · 4","whole_rest":"全休止符 · 此处为 4","written":"已有时值","wrong":"还不对,请对照解释检查所选答案。"}}
先听完整合奏,再只听旋律,辨认低音与反复的 C 大三伴奏。跟踪 B 之后 A 在哪里回归,初始结构为 A A B A A A′ B A。再替换一小节,记住原版并预测变化,通过恢复、重改比较两个版本。
核对读谱,并解释一种修改
第六小节为 A′:E4–G4–A4–C5,尾音不同于 A 的 G4,四个时值仍为四分音符。A 中的 A4 不在底层 C–E–G 三和弦里,但不因此成为错音。旋律改变时,低音仍为 C。
把第八小节从 A 换成 B,就去掉了原来的最后一次 A 回归,结尾材料变了,节拍与速度没变。若只是静音伴奏,旋律结构不变,织体改变。说明自己做了哪种修改,并用具体音符或声部作为依据。
用整个系列观察一段录音
选一段喜欢的音乐,第一遍只听。第二遍用时间戳或纸上线段标大致边界,再记录几条具体观察:
- 时间:脉冲是否稳定?速度近似不变时,重音或敲击位置有没有改变?
- 音高:乐句是重复、移调,还是内部某个音程改变?
- 和声:重复旋律是否配了新低音或和弦?哪里像到达点?
- 声音与结构:哪些声部进入、退出?回归是精确重复、变奏,还是主要改变织体?
不必每项都给出肯定答案。标出听到的、可以核对的和仍不确定的。「约 0:40 曲调回来,并加入低音」比没有依据的曲式标签更有信息。
本系列想建立的习惯是:察觉差别,指出音乐关系,回到声音检查,再解释依据。现在可以带着更丰富的问题重读第一课,也可以继续提升视奏、听音训练与更长乐段的编配能力。