Moved 6 Prime songs (Twinkle Twinkle, Mary Had a Little Lamb, Happy Birthday, Chopsticks, Lavender's Blue, Shortnin' Bread) into the Nursery Rhymes genre using the rhyme() helper. Dropped Baa Baa Black Sheep since it's the same melody as Twinkle Twinkle. Updated README to reflect 18 genres, 124 synth pieces. https://claude.ai/code/session_01FNZw4curZbJp38HFUaPXwR
4847 lines
216 KiB
HTML
4847 lines
216 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>🥁 Drum Rhythm Game</title>
|
|
<style>
|
|
*{box-sizing:border-box;margin:0;padding:0;}
|
|
body{background:#0a0a0f;color:#eee;font-family:sans-serif;height:100vh;display:flex;flex-direction:column;overflow:hidden;}
|
|
.screen{display:none;flex-direction:column;align-items:center;width:100%;height:100%;overflow-y:auto;padding:16px;gap:10px;}
|
|
.screen.active{display:flex;}
|
|
.card{background:#13131f;border:1px solid #222;border-radius:10px;padding:14px;width:100%;max-width:700px;}
|
|
.card h2{font-size:11px;opacity:.5;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;}
|
|
.mode-row{display:flex;gap:8px;}
|
|
.mode-card{flex:1;padding:10px;border-radius:8px;border:2px solid #222;background:#0d0d1a;cursor:pointer;text-align:center;}
|
|
.mode-card.sel{border-color:#805ad5;}
|
|
.mode-card h3{font-size:13px;margin-bottom:3px;}
|
|
.mode-card p{font-size:11px;opacity:.5;}
|
|
.genre-tabs{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:8px;}
|
|
.gtab{padding:5px 12px;border-radius:16px;border:1px solid #333;background:transparent;color:#aaa;cursor:pointer;font-size:12px;}
|
|
.gtab.active{background:#805ad5;border-color:#805ad5;color:#fff;}
|
|
.rgrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;max-height:160px;overflow-y:auto;}
|
|
.rbtn{padding:6px 8px;border-radius:5px;border:1px solid #2a2a3a;background:#0d0d1a;color:#ccc;cursor:pointer;font-size:11px;text-align:left;}
|
|
.rbtn.sel{border-color:#38a169;color:#eee;}
|
|
.rbtn small{opacity:.5;display:block;}
|
|
.pad-toggle{display:flex;gap:8px;flex-wrap:wrap;}
|
|
.ptog{padding:7px 14px;border-radius:20px;border:2px solid;cursor:pointer;font-size:12px;font-weight:bold;opacity:.4;transition:opacity .15s;}
|
|
.ptog.on{opacity:1;}
|
|
.prow{display:flex;gap:6px;align-items:center;margin-bottom:5px;}
|
|
.prow input{background:#0d0d1a;border:1px solid #333;border-radius:5px;padding:5px 8px;color:#eee;font-size:12px;flex:1;}
|
|
.smbtn{padding:5px 10px;border-radius:5px;border:1px solid #333;background:transparent;color:#eee;cursor:pointer;font-size:12px;}
|
|
.smbtn.green{color:#68d391;border-color:#68d391;}
|
|
.smbtn.red{color:#fc8181;border-color:#fc8181;}
|
|
.smbtn.purple{color:#b794f4;border-color:#b794f4;}
|
|
.bigbtn{padding:11px 32px;border-radius:9px;border:none;background:#805ad5;color:#fff;font-size:15px;cursor:pointer;font-weight:bold;}
|
|
.bigbtn:hover{background:#9f7aea;}
|
|
#hud{padding:7px 12px;background:#111;border-bottom:1px solid #1e1e2e;display:flex;align-items:center;gap:10px;flex-shrink:0;width:100%;}
|
|
#hud-title{font-size:12px;font-weight:bold;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
.hstat{text-align:center;min-width:50px;}.hstat span{font-size:9px;opacity:.5;display:block;}.hstat b{font-size:16px;}
|
|
#cwrap{flex:1;position:relative;width:100%;}
|
|
canvas{display:block;}
|
|
#msg{position:absolute;top:28%;left:50%;transform:translate(-50%,-50%);font-size:28px;font-weight:bold;pointer-events:none;opacity:0;text-shadow:0 0 20px currentColor;white-space:nowrap;}
|
|
#cdown{position:absolute;top:40%;left:50%;transform:translate(-50%,-50%);font-size:80px;font-weight:bold;pointer-events:none;opacity:0;color:#fff;}
|
|
#pbanner{position:absolute;top:12px;left:50%;transform:translateX(-50%);background:#1a1a2e;border:1px solid #444;border-radius:7px;padding:7px 18px;font-size:13px;opacity:0;pointer-events:none;transition:opacity .3s;}
|
|
#ss{padding:16px;}
|
|
.stbl{width:100%;max-width:600px;border-collapse:collapse;}
|
|
.stbl th{text-align:left;padding:7px 9px;border-bottom:1px solid #222;font-size:11px;opacity:.5;}
|
|
.stbl td{padding:7px 9px;border-bottom:1px solid #111;font-size:12px;}
|
|
.sticky-start{position:sticky;bottom:0;padding:12px 0 4px;background:linear-gradient(transparent,#0a0a0f 35%);z-index:10;display:flex;gap:8px;justify-content:center;align-items:center;width:100%;max-width:700px;}
|
|
#game-controls{display:flex;gap:0;background:#0d0d18;border-bottom:1px solid #1e1e2e;padding:5px 14px;flex-shrink:0;width:100%;align-items:center;justify-content:center;gap:28px;}
|
|
.gctrl-label{display:flex;align-items:center;gap:8px;cursor:default;}
|
|
.gctrl-icon{font-size:16px;}
|
|
.gctrl-name{font-size:11px;color:#aaa;min-width:44px;}
|
|
.gctrl-slider{width:140px;height:6px;accent-color:#805ad5;cursor:pointer;}
|
|
.gctrl-val{font-size:13px;font-weight:bold;color:#b794f4;min-width:40px;text-align:right;}
|
|
#pause-overlay{display:none;position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:20;flex-direction:column;align-items:center;justify-content:center;gap:16px;}
|
|
#pause-overlay.show{display:flex;}
|
|
#pause-overlay h2{font-size:28px;color:#fff;}
|
|
#remap-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:30;flex-direction:column;align-items:center;justify-content:center;gap:16px;}
|
|
#remap-overlay.show{display:flex;}
|
|
#remap-prompt{font-size:20px;font-weight:bold;text-align:center;}
|
|
#remap-status{font-size:13px;opacity:.5;text-align:center;}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- MENU -->
|
|
<div class="screen active" id="sm">
|
|
<div style="text-align:center;padding:6px 0"><h1 style="font-size:24px">🥁 Drum Rhythm Game</h1><p style="opacity:.4;font-size:12px">Hit pads when notes reach the line</p></div>
|
|
|
|
<div class="card">
|
|
<h2>Mode</h2>
|
|
<div class="mode-row">
|
|
<div class="mode-card sel" id="mc-b" onclick="setMode('builtin')"><h3>🥁 Rhythm</h3><p>Play along to drum charts</p></div>
|
|
<div class="mode-card" id="mc-m" onclick="setMode('mp3')"><h3>🎵 MP3 Jam</h3><p>Load music, drum freely</p></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" id="rc">
|
|
<h2>Choose Rhythm</h2>
|
|
<div class="genre-tabs" id="gtabs"></div>
|
|
<div class="rgrid" id="rgrid"></div>
|
|
<div style="display:flex;gap:8px;align-items:center;margin-top:8px;">
|
|
<button class="smbtn purple" id="pvbtn" onclick="togglePreview()">▶ Preview</button>
|
|
<span id="pvlbl" style="font-size:11px;opacity:.5">Select a rhythm to preview</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" id="mc-mp3" style="display:none">
|
|
<h2>Load MP3</h2>
|
|
<div style="display:flex;gap:8px;align-items:center">
|
|
<button class="smbtn" onclick="document.getElementById('fi').click()">📂 Browse</button>
|
|
<span id="mp3n" style="font-size:12px;opacity:.5">No file</span>
|
|
<input type="file" id="fi" accept="audio/*" style="display:none">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Active Pads</h2>
|
|
<div class="pad-toggle" id="pad-toggles"></div>
|
|
<p style="font-size:11px;opacity:.4;margin-top:8px">Toggle which pads show notes. Inactive pads still make sound when hit.</p>
|
|
<div style="margin-top:10px;padding-top:8px;border-top:1px solid #222;">
|
|
<span style="font-size:11px;opacity:.5;margin-right:8px;">Note density:</span>
|
|
<button class="smbtn" id="density-full" onclick="setSimpleMode(false)">Full</button>
|
|
<button class="smbtn" id="density-simple" onclick="setSimpleMode(true)">Simple</button>
|
|
<span style="font-size:10px;opacity:.35;margin-left:6px;" id="density-hint">Fewer notes for beginners</span>
|
|
</div>
|
|
<div id="hitmode-toggle" style="display:none;margin-top:10px;padding-top:8px;border-top:1px solid #222;">
|
|
<span style="font-size:11px;opacity:.5;margin-right:8px;">Classical hit sound:</span>
|
|
<button class="smbtn" id="hitmode-drums" onclick="setHitMode('drums')">🥁 Drums</button>
|
|
<button class="smbtn purple" id="hitmode-orch" onclick="setHitMode('orchestra')">🎻 Orchestra</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Players</h2>
|
|
<div id="plist"></div>
|
|
<button class="smbtn green" onclick="addPlayer()" style="margin-top:5px">+ Add Player</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Leaderboard</h2>
|
|
<div id="mlb" style="font-size:12px;opacity:.5">No scores yet.</div>
|
|
<div style="display:flex;gap:6px;margin-top:8px">
|
|
<button class="smbtn" onclick="goScores()">View All</button>
|
|
<button class="smbtn red" onclick="confirmReset()">Reset All Data</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Settings</h2>
|
|
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:center;">
|
|
<label style="font-size:12px;display:flex;align-items:center;gap:6px;">Speed: <input type="range" min="50" max="150" value="100" step="5" id="speed-slider" oninput="setSpeed(this.value)"><span id="speed-val" style="font-size:11px;min-width:32px">100%</span></label>
|
|
<label style="font-size:12px;display:flex;align-items:center;gap:6px;">Volume: <input type="range" min="0" max="150" value="100" step="5" id="vol-slider" oninput="setVolume(this.value)"><span id="vol-val" style="font-size:11px;min-width:32px">100%</span></label>
|
|
<button class="smbtn" onclick="resetSettings()" style="font-size:11px;">Reset</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sticky-start">
|
|
<button class="smbtn" onclick="startRemap()">🎮 Remap Pads</button>
|
|
<button class="bigbtn" onclick="startGame()">▶ Start</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- GAME -->
|
|
<div class="screen" id="sg" style="padding:0;">
|
|
<div id="hud">
|
|
<button class="smbtn" onclick="goMenu()">← Menu</button>
|
|
<button class="smbtn" onclick="togglePause()" id="pause-btn">⏸</button>
|
|
<div id="hud-title">-</div>
|
|
<div class="hstat"><span>Score</span><b id="hs">0</b></div>
|
|
<div class="hstat"><span>Streak</span><b id="hst">0</b></div>
|
|
<div class="hstat"><span>Acc</span><b id="ha">-</b></div>
|
|
<div class="hstat"><span>Player</span><b id="hp">-</b></div>
|
|
<span id="gpd" style="font-size:10px;opacity:.4;margin-left:auto">No gamepad</span>
|
|
</div>
|
|
<div id="game-controls">
|
|
<label class="gctrl-label">
|
|
<span class="gctrl-icon">🔊</span>
|
|
<span class="gctrl-name">Volume</span>
|
|
<input type="range" min="0" max="150" value="100" step="5" id="hud-vol" class="gctrl-slider" oninput="setVolume(this.value);document.getElementById('vol-slider').value=this.value">
|
|
<span class="gctrl-val" id="hud-vol-val">100%</span>
|
|
</label>
|
|
</div>
|
|
<div id="cwrap">
|
|
<canvas id="c"></canvas><div id="msg"></div><div id="cdown"></div><div id="pbanner"></div>
|
|
<div id="pause-overlay"><h2>PAUSED</h2><button class="bigbtn" onclick="togglePause()">▶ Resume</button><button class="smbtn" onclick="goMenu()">← Back to Menu</button></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- REMAP -->
|
|
<div id="remap-overlay"><div id="remap-prompt"></div><div id="remap-status"></div><button class="smbtn red" onclick="cancelRemap()">Cancel</button></div>
|
|
|
|
<!-- SCORES -->
|
|
<div class="screen" id="ss" style="padding:16px;">
|
|
<div style="width:100%;max-width:600px">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
|
<h2>🏆 Leaderboard</h2>
|
|
<div style="display:flex;gap:6px">
|
|
<button class="smbtn red" onclick="clearScores()">Clear</button>
|
|
<button class="smbtn" onclick="goMenu()">← Back</button>
|
|
</div>
|
|
</div>
|
|
<table class="stbl"><thead><tr><th></th><th>Player</th><th>Rhythm</th><th>Score</th><th>Acc</th><th>Streak</th><th>Date</th></tr></thead>
|
|
<tbody id="stbody"></tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// ── LANE DEFINITIONS ──────────────────────────────────
|
|
const LANES=[
|
|
{label:"Red", color:"#e53e3e",key:"f", type:"tom"},
|
|
{label:"Yellow",color:"#d69e2e",key:"g", type:"snare"},
|
|
{label:"Blue", color:"#3182ce",key:"h", type:"hihat"},
|
|
{label:"Green", color:"#38a169",key:"j", type:"tom2"},
|
|
{label:"Kick", color:"#805ad5",key:" ", type:"kick"},
|
|
];
|
|
|
|
// which pads are active (show notes) — all on by default
|
|
let activePads=[true,true,true,true,true];
|
|
// Simple mode — thins out notes, keeping only strong beats
|
|
let simpleMode=false;
|
|
|
|
// ── RHYTHMS ───────────────────────────────────────────
|
|
// Every pattern now uses ALL 5 lanes (0=Red/tom, 1=Yellow/snare, 2=Blue/hihat, 3=Green/tom2, 4=Kick)
|
|
// t = beat offset within a 4-beat bar
|
|
const GENRES={
|
|
Rock:[
|
|
{n:"Basic Rock",bpm:90,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1,l:2},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:1},{t:3,l:2},{t:3.5,l:2}]},
|
|
{n:"Classic Rock",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
{n:"Punk Rock",bpm:160,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1,l:2},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2.5,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.25,l:0},{t:3.5,l:2}]},
|
|
{n:"Arena Rock",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1,l:3},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
{n:"Hard Rock",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:0},{t:3.25,l:1},{t:3.5,l:3},{t:3.75,l:0}]},
|
|
{n:"Indie Rock",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.75,l:3},{t:1,l:1},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2,l:2},{t:2.75,l:0},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Rock Shuffle",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.67,l:2},{t:1,l:1},{t:1,l:3},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2,l:2},{t:2.67,l:2},{t:3,l:1},{t:3,l:0},{t:3.67,l:2}]},
|
|
{n:"Power Ballad",bpm:72,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Half Time",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:2},{t:1.5,l:3},
|
|
{t:2,l:1},{t:2,l:2},{t:2.5,l:2},{t:3,l:2},{t:3.5,l:0}]},
|
|
{n:"Tom Heavy",bpm:105,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:1,l:1},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Stadium Rock",bpm:118,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1,l:3},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
{n:"Garage Rock",bpm:145,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Post Rock",bpm:80,bars:8,p:[
|
|
{t:0,l:4},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
{n:"Glam Rock",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.75,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Rock Fill",bpm:110,bars:4,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:1,l:2},{t:1.5,l:1},
|
|
{t:2,l:0},{t:2.25,l:1},{t:2.5,l:2},{t:2.75,l:3},{t:3,l:4},{t:3.5,l:0}]},
|
|
],
|
|
Metal:[
|
|
{n:"Basic Metal",bpm:160,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:4},{t:0,l:2},{t:.5,l:2},{t:1,l:1},{t:1,l:3},
|
|
{t:1.25,l:4},{t:1.5,l:2},{t:2,l:4},{t:2.25,l:4},{t:2,l:2},
|
|
{t:2.5,l:0},{t:3,l:1},{t:3,l:4},{t:3.5,l:2}]},
|
|
{n:"Double Bass",bpm:155,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:4},{t:.5,l:4},{t:.75,l:4},{t:1,l:1},{t:1,l:0},
|
|
{t:1.25,l:4},{t:1.5,l:4},{t:1.75,l:4},{t:2,l:4},{t:2.25,l:4},
|
|
{t:2.5,l:4},{t:2.75,l:4},{t:3,l:1},{t:3,l:3},{t:3.5,l:2}]},
|
|
{n:"Thrash",bpm:190,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:4},{t:1,l:1},{t:1,l:0},{t:1.5,l:4},
|
|
{t:2,l:4},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:4}]},
|
|
{n:"Groove Metal",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:2},{t:2,l:4},{t:2,l:2},{t:2.75,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Doom Metal",bpm:55,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Nu Metal",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:4},{t:2.5,l:1},{t:3.25,l:0},{t:3.5,l:2}]},
|
|
{n:"Power Metal",bpm:175,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.5,l:2},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:4},{t:1.5,l:2},{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Metalcore",bpm:150,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1,l:2},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:4},{t:2.5,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
{n:"Prog Metal",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.25,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2.75,l:0},{t:3,l:1},{t:3,l:2},{t:3.5,l:3},{t:3.75,l:0}]},
|
|
{n:"Industrial",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:2},
|
|
{t:1.5,l:4},{t:1.5,l:2},{t:2,l:4},{t:2,l:2},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Sludge",bpm:65,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.75,l:0},{t:1.5,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.75,l:2},{t:3,l:0},{t:3.5,l:1}]},
|
|
{n:"Death Metal",bpm:185,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:1},{t:.75,l:3},{t:1,l:4},
|
|
{t:1.25,l:1},{t:1.5,l:0},{t:1.75,l:4},{t:2,l:1},{t:2.25,l:3},
|
|
{t:2.5,l:4},{t:2.75,l:0},{t:3,l:4},{t:3.5,l:1}]},
|
|
{n:"Blast Beat",bpm:200,bars:4,p:[
|
|
{t:0,l:4},{t:0,l:1},{t:.25,l:4},{t:.25,l:2},{t:.5,l:4},{t:.5,l:1},
|
|
{t:.75,l:4},{t:.75,l:3},{t:1,l:4},{t:1,l:0}]},
|
|
{n:"Breakdown",bpm:90,bars:4,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:.75,l:4},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:4},{t:2.5,l:0},{t:2.75,l:1},{t:3,l:3},{t:3.5,l:4}]},
|
|
{n:"Black Metal",bpm:210,bars:4,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.25,l:0},{t:.25,l:2},{t:.5,l:1},{t:.5,l:4},
|
|
{t:.75,l:3},{t:.75,l:2},{t:1,l:4},{t:1,l:0}]},
|
|
],
|
|
Jazz:[
|
|
{n:"Jazz Ride",bpm:120,bars:8,p:[
|
|
{t:0,l:3},{t:.67,l:3},{t:1,l:3},{t:1,l:1},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2,l:3},{t:2.67,l:0},{t:3,l:3},{t:3,l:1},{t:3.67,l:3}]},
|
|
{n:"Slow Swing",bpm:75,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1.5,l:0},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2.67,l:3},{t:3,l:1},{t:3.67,l:3}]},
|
|
{n:"Bebop",bpm:200,bars:8,p:[
|
|
{t:0,l:3},{t:.67,l:3},{t:1,l:3},{t:1,l:1},{t:1.67,l:3},
|
|
{t:2,l:1},{t:2,l:3},{t:2.67,l:0},{t:3,l:3},{t:3.67,l:3}]},
|
|
{n:"Jazz Waltz",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:3},{t:.67,l:3},{t:1,l:3},{t:1.67,l:3},
|
|
{t:2,l:1},{t:2,l:0},{t:2,l:3},{t:2.67,l:3}]},
|
|
{n:"Bossa Nova",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:3},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},{t:1.75,l:3},
|
|
{t:2,l:4},{t:2,l:3},{t:2.75,l:3},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Cool Jazz",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:1,l:1},{t:1.5,l:0},{t:1.5,l:3},
|
|
{t:2,l:3},{t:2.5,l:4},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Fusion",bpm:135,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:2},{t:.5,l:3},{t:1,l:1},{t:1,l:0},
|
|
{t:1.25,l:4},{t:1.5,l:2},{t:2,l:3},{t:2.75,l:1},{t:3,l:0},{t:3.5,l:3}]},
|
|
{n:"Jazz Shuffle",bpm:105,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1.33,l:0},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2.67,l:3},{t:3,l:1},{t:3.67,l:3}]},
|
|
{n:"Afro-Cuban",bpm:115,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:.75,l:0},{t:1,l:1},{t:1.5,l:4},
|
|
{t:1.75,l:3},{t:2,l:3},{t:2.5,l:0},{t:3,l:1},{t:3.25,l:3},{t:3.75,l:4}]},
|
|
{n:"New Orleans",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:2},{t:1,l:1},{t:1.33,l:0},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2.33,l:2},{t:2.67,l:0},{t:3,l:1},{t:3.33,l:2}]},
|
|
{n:"Hard Bop",bpm:165,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1,l:0},{t:1.5,l:4},
|
|
{t:1.67,l:3},{t:2,l:3},{t:2.67,l:0},{t:3,l:1},{t:3,l:4},{t:3.67,l:3}]},
|
|
{n:"Modal Jazz",bpm:115,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1.33,l:0},{t:2,l:1},{t:2,l:4},
|
|
{t:2.67,l:3},{t:3.33,l:0}]},
|
|
{n:"Jazz Ballad",bpm:60,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2.67,l:3},{t:3,l:1}]},
|
|
{n:"Post-Bop",bpm:155,bars:8,p:[
|
|
{t:0,l:3},{t:.5,l:0},{t:.67,l:3},{t:1,l:1},{t:1.33,l:3},
|
|
{t:1.67,l:4},{t:2,l:3},{t:2.33,l:0},{t:2.67,l:1},{t:3,l:3},{t:3.5,l:4}]},
|
|
{n:"Medium Swing",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1,l:0},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2.67,l:3},{t:3,l:1},{t:3.67,l:3}]},
|
|
],
|
|
"Hip Hop":[
|
|
{n:"Boom Bap",bpm:90,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:2},{t:1.75,l:0},
|
|
{t:2,l:4},{t:2.25,l:3},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Trap",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:4},{t:1,l:1},{t:1.5,l:2},{t:1.75,l:2},
|
|
{t:2,l:4},{t:2.25,l:3},{t:3,l:1},{t:3.5,l:2},{t:3.75,l:4}]},
|
|
{n:"Lo-Fi",bpm:75,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:0},{t:1.75,l:4},
|
|
{t:2,l:2},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Classic Hip Hop",bpm:85,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:2},
|
|
{t:2,l:4},{t:2.5,l:3},{t:2.75,l:2},{t:3,l:1},{t:3.5,l:2},{t:3.75,l:0}]},
|
|
{n:"East Coast",bpm:88,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:0},{t:1.75,l:4},
|
|
{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"West Coast",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.25,l:0},{t:3.5,l:2}]},
|
|
{n:"Drill",bpm:145,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:4},{t:1,l:1},{t:1.25,l:3},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.75,l:4}]},
|
|
{n:"UK Grime",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.25,l:0},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:2},{t:2.75,l:1},{t:3.5,l:0}]},
|
|
{n:"Jazz Rap",bpm:92,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:2},{t:1,l:1},{t:1.5,l:0},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2.33,l:3},{t:2.67,l:4},{t:3,l:1},{t:3.67,l:2}]},
|
|
{n:"R&B Hip Hop",bpm:88,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.25,l:0},{t:3.5,l:2}]},
|
|
{n:"Crunk",bpm:75,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1,l:2},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:4},{t:3,l:1},{t:3,l:0},{t:3.5,l:4}]},
|
|
{n:"Cloud Rap",bpm:78,bars:8,p:[
|
|
{t:0,l:4},{t:.75,l:2},{t:1,l:1},{t:1.5,l:0},
|
|
{t:2,l:3},{t:3,l:1},{t:3.5,l:4}]},
|
|
{n:"Gangsta",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:4},{t:3,l:1},{t:3.75,l:0}]},
|
|
{n:"Freestyle",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.25,l:0},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3},{t:3.75,l:4}]},
|
|
{n:"Chopped",bpm:60,bars:8,p:[
|
|
{t:0,l:4},{t:1,l:1},{t:1.5,l:0},{t:2,l:3},{t:2.5,l:4},{t:3,l:1}]},
|
|
],
|
|
Funk:[
|
|
{n:"Classic Funk",bpm:98,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.25,l:0},{t:.5,l:2},{t:.75,l:1},
|
|
{t:1,l:2},{t:1.5,l:3},{t:1.75,l:2},{t:2,l:4},{t:2,l:2},
|
|
{t:2.5,l:1},{t:2.75,l:0},{t:3,l:4},{t:3.5,l:2}]},
|
|
{n:"Funk Pocket",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3},{t:3.75,l:4}]},
|
|
{n:"Ghost Notes",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.25,l:1},{t:.5,l:2},{t:.75,l:0},
|
|
{t:1,l:1},{t:1.25,l:0},{t:1.5,l:3},{t:2,l:4},{t:2.25,l:0},
|
|
{t:2.5,l:2},{t:3,l:1},{t:3,l:2},{t:3.5,l:0}]},
|
|
{n:"P-Funk",bpm:108,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:2},{t:1,l:1},{t:1.25,l:3},
|
|
{t:1.5,l:2},{t:1.75,l:4},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3},{t:3.75,l:2}]},
|
|
{n:"Disco Funk",bpm:118,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.5,l:2},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:4},{t:1.5,l:2},{t:2,l:0},{t:2,l:2},{t:2.5,l:4},
|
|
{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Soul Funk",bpm:85,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:0},
|
|
{t:1.75,l:3},{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Syncopated",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:2},{t:.75,l:0},{t:1,l:1},{t:1.25,l:3},
|
|
{t:1.5,l:4},{t:2,l:0},{t:2.25,l:2},{t:2.75,l:3},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"Afrobeat",bpm:105,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:.75,l:0},{t:1,l:1},{t:1.25,l:2},
|
|
{t:1.5,l:3},{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.25,l:3},{t:3.75,l:2}]},
|
|
{n:"Hi-Hat Funk",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.25,l:2},{t:.5,l:0},{t:.75,l:2},
|
|
{t:1,l:1},{t:1.25,l:2},{t:1.5,l:3},{t:1.75,l:4},
|
|
{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:2}]},
|
|
{n:"New Orleans",bpm:90,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.67,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.67,l:2},{t:2,l:4},{t:2.5,l:2},{t:2.67,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Electro Funk",bpm:112,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1,l:0},{t:1.5,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3,l:0},{t:3.5,l:4}]},
|
|
{n:"Trap Funk",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.75,l:4}]},
|
|
{n:"Slow Funk",bpm:72,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:0},
|
|
{t:2,l:3},{t:2.5,l:2},{t:2.75,l:4},{t:3,l:1}]},
|
|
{n:"Funk Breakdown",bpm:88,bars:4,p:[
|
|
{t:0,l:4},{t:.25,l:2},{t:.5,l:0},{t:.75,l:2},{t:1,l:1},
|
|
{t:1.25,l:3},{t:1.5,l:2},{t:1.75,l:4},{t:2,l:0},{t:2.5,l:4},{t:2.75,l:1},{t:3,l:2}]},
|
|
{n:"Sly Groove",bpm:102,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:2},{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.25,l:3},{t:3.75,l:2}]},
|
|
],
|
|
Latin:[
|
|
{n:"Samba",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:3},{t:.75,l:0},{t:1,l:1},{t:1,l:2},
|
|
{t:1.5,l:3},{t:1.75,l:4},{t:2,l:0},{t:2,l:2},{t:2.5,l:3},
|
|
{t:2.75,l:3},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Bossa Nova",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:3},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:3},{t:2,l:4},{t:2,l:0},{t:2.75,l:3},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Salsa",bpm:185,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:2},{t:3.5,l:1}]},
|
|
{n:"Cha Cha",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1.5,l:2},{t:1.75,l:3},
|
|
{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:2},{t:3.75,l:3}]},
|
|
{n:"Reggaeton",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1,l:2},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.5,l:4},{t:3,l:1},{t:3,l:0},{t:3.5,l:4}]},
|
|
{n:"Merengue",bpm:150,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.5,l:2},{t:1,l:1},{t:1,l:2},
|
|
{t:1.5,l:3},{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Cumbia",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:2},{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Mambo",bpm:165,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:4},{t:.75,l:2},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:0},{t:2.75,l:4},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Tango",bpm:65,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:1},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:1},{t:2.5,l:0},{t:3,l:1}]},
|
|
{n:"Afro-Cuban",bpm:115,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:.75,l:0},{t:1,l:1},{t:1.5,l:4},
|
|
{t:1.75,l:3},{t:2,l:0},{t:2.5,l:4},{t:3,l:1},{t:3.25,l:3},{t:3.75,l:0}]},
|
|
{n:"Flamenco",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:.33,l:3},{t:.67,l:0},{t:1,l:1},{t:1.33,l:3},
|
|
{t:1.67,l:4},{t:2,l:0},{t:2.33,l:3},{t:2.67,l:1},{t:3,l:4},{t:3.33,l:0}]},
|
|
{n:"Bolero",bpm:72,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1.33,l:0},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2.67,l:3},{t:3,l:1},{t:3.67,l:0}]},
|
|
{n:"Baiao",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:.75,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.25,l:3},{t:3.5,l:0}]},
|
|
{n:"Son Cubano",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:3},{t:1,l:1},{t:1.5,l:0},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Timba",bpm:175,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:4},{t:.75,l:2},{t:1,l:1},{t:1.25,l:3},
|
|
{t:1.5,l:4},{t:1.75,l:2},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:3}]},
|
|
],
|
|
Electronic:[
|
|
{n:"4-on-Floor",bpm:128,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3,l:4},{t:3.5,l:2}]},
|
|
{n:"Techno",bpm:135,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:4},{t:1,l:2},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:4},{t:3,l:2},{t:3.5,l:3}]},
|
|
{n:"Drum & Bass",bpm:174,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:.75,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:1},{t:2.5,l:0},{t:2.75,l:4},{t:3,l:1},{t:3.5,l:4}]},
|
|
{n:"Dubstep",bpm:140,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.75,l:4}]},
|
|
{n:"Breakbeat",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.75,l:4},{t:1,l:1},{t:1.5,l:3},
|
|
{t:1.75,l:2},{t:2,l:4},{t:2.25,l:0},{t:2.5,l:2},{t:3,l:1},{t:3.25,l:4},{t:3.5,l:0}]},
|
|
{n:"Trance",bpm:138,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.25,l:0},{t:.5,l:2},{t:.75,l:3},
|
|
{t:1,l:1},{t:1,l:4},{t:1.5,l:0},{t:2,l:4},{t:2,l:2},{t:2.5,l:3},
|
|
{t:3,l:1},{t:3,l:4},{t:3.5,l:0}]},
|
|
{n:"Jungle",bpm:160,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:1},{t:.5,l:0},{t:.75,l:2},{t:1,l:3},
|
|
{t:1.25,l:1},{t:1.5,l:4},{t:1.75,l:2},{t:2,l:0},{t:2.5,l:1},
|
|
{t:2.75,l:3},{t:3,l:2},{t:3.25,l:4},{t:3.75,l:0}]},
|
|
{n:"Synthwave",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3,l:3},{t:3.5,l:2}]},
|
|
{n:"Future Bass",bpm:150,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1,l:3},{t:1.5,l:2},
|
|
{t:1.75,l:4},{t:2,l:0},{t:2.5,l:3},{t:3,l:1},{t:3.25,l:4},{t:3.75,l:0}]},
|
|
{n:"IDM",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:1},{t:.75,l:3},{t:1,l:2},
|
|
{t:1.25,l:4},{t:1.75,l:0},{t:2,l:2},{t:2.25,l:3},{t:2.5,l:1},
|
|
{t:2.75,l:0},{t:3.25,l:4},{t:3.75,l:2}]},
|
|
{n:"Big Beat",bpm:115,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:.75,l:2},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:4},{t:1.75,l:0},{t:2,l:4},{t:2,l:2},{t:2.5,l:3},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Electro",bpm:118,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:4},{t:1.5,l:2},{t:2,l:0},{t:2,l:2},{t:2.5,l:4},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Minimal",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:1,l:0},{t:1,l:2},{t:2,l:4},{t:2,l:1},{t:3,l:0},{t:3,l:2}]},
|
|
{n:"Footwork",bpm:160,bars:8,p:[
|
|
{t:0,l:4},{t:.25,l:0},{t:.5,l:1},{t:.75,l:4},{t:1,l:3},
|
|
{t:1.25,l:4},{t:1.5,l:0},{t:1.75,l:4},{t:2,l:1},{t:2.5,l:0},
|
|
{t:2.75,l:3},{t:3,l:4},{t:3.25,l:1},{t:3.5,l:0}]},
|
|
{n:"Ambient",bpm:75,bars:8,p:[
|
|
{t:0,l:4},{t:1,l:2},{t:1.5,l:0},{t:2,l:1},{t:2.5,l:3},{t:3,l:2}]},
|
|
],
|
|
Country:[
|
|
{n:"Two-Step",bpm:105,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:1,l:1},{t:1,l:3},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2,l:2},{t:3,l:1},{t:3,l:3}]},
|
|
{n:"Honky Tonk",bpm:120,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Shuffle",bpm:100,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:2},{t:1,l:1},{t:1.5,l:0},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2.67,l:2},{t:3,l:1},{t:3.5,l:0},{t:3.67,l:2}]},
|
|
{n:"Bluegrass",bpm:145,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3,l:3}]},
|
|
{n:"Ballad",bpm:68,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:1,l:1},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2,l:2},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Western Swing",bpm:130,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:3},{t:1,l:1},{t:1.5,l:0},{t:1.67,l:3},
|
|
{t:2,l:4},{t:2.33,l:0},{t:2.67,l:3},{t:3,l:1},{t:3.67,l:3}]},
|
|
{n:"Southern Rock",bpm:125,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1,l:3},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Rockabilly",bpm:155,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2.25,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Outlaw",bpm:110,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"New Country",bpm:118,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1.5,l:3},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:0},{t:3,l:1},{t:3.5,l:3}]},
|
|
{n:"Gospel",bpm:88,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:1,l:1},{t:1,l:3},{t:1.5,l:0},
|
|
{t:2,l:4},{t:2,l:2},{t:2.5,l:2},{t:3,l:1},{t:3,l:0},{t:3.5,l:3}]},
|
|
{n:"Americana",bpm:95,bars:8,p:[
|
|
{t:0,l:4},{t:.5,l:2},{t:1,l:1},{t:1.5,l:0},
|
|
{t:1.5,l:4},{t:2,l:3},{t:2.5,l:2},{t:3,l:1},{t:3.5,l:0}]},
|
|
{n:"Texas Blues",bpm:78,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:2},{t:1,l:1},{t:1.5,l:0},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2.33,l:0},{t:2.67,l:2},{t:3,l:1},{t:3.67,l:2}]},
|
|
{n:"Cajun",bpm:115,bars:8,p:[
|
|
{t:0,l:4},{t:.67,l:2},{t:1,l:1},{t:1.33,l:0},{t:1.67,l:2},
|
|
{t:2,l:4},{t:2.33,l:3},{t:2.67,l:2},{t:3,l:1},{t:3.33,l:0}]},
|
|
{n:"Barn Dance",bpm:138,bars:8,p:[
|
|
{t:0,l:4},{t:0,l:2},{t:.5,l:0},{t:1,l:1},{t:1,l:3},
|
|
{t:1.5,l:2},{t:2,l:4},{t:2.5,l:0},{t:3,l:1},{t:3,l:0},{t:3.5,l:2}]},
|
|
],
|
|
};
|
|
|
|
// ── CLASSICAL MODE ────────────────────────────────────
|
|
const NT={C3:130.81,D3:146.83,Eb3:155.56,E3:164.81,F3:174.61,G3:196.00,Ab3:207.65,A3:220.00,Bb3:233.08,B3:246.94,C4:261.63,D4:293.66,Eb4:311.13,E4:329.63,F4:349.23,G4:392.00,Ab4:415.30,A4:440.00,Bb4:466.16,B4:493.88,C5:523.25,D5:587.33,Eb5:622.25,E5:659.25,F5:698.46,G5:783.99,A5:880.00,Gb3:185.00,Db4:277.18,Gb4:369.99,Db5:554.37,C6:1046.50};
|
|
|
|
function playSynth(ac,freq,t,dur,inst,vol,arr){
|
|
if(t<ac.currentTime-0.1)return;
|
|
var v=(vol||0.25)*3*masterVolume;
|
|
if(inst==='strings'){
|
|
var o=ac.createOscillator(),f=ac.createBiquadFilter(),g=ac.createGain();
|
|
o.type='sawtooth';o.frequency.value=freq;
|
|
f.type='lowpass';f.frequency.value=Math.min(freq*3,4000);
|
|
o.connect(f);f.connect(g);g.connect(ac.destination);
|
|
var att=Math.min(0.08,dur*0.15);
|
|
g.gain.setValueAtTime(0.001,t);g.gain.linearRampToValueAtTime(v,t+att);
|
|
if(dur>att+0.05)g.gain.setValueAtTime(v*0.8,t+dur*0.7);
|
|
g.gain.linearRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
}else if(inst==='brass'){
|
|
var o=ac.createOscillator(),f=ac.createBiquadFilter(),g=ac.createGain();
|
|
o.type='square';o.frequency.value=freq;
|
|
f.type='lowpass';f.frequency.value=Math.min(freq*2,3000);
|
|
o.connect(f);f.connect(g);g.connect(ac.destination);
|
|
g.gain.setValueAtTime(0.001,t);g.gain.linearRampToValueAtTime(v*0.5,t+0.03);
|
|
if(dur>0.08)g.gain.setValueAtTime(v*0.5,t+dur*0.8);
|
|
g.gain.linearRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
}else if(inst==='woodwind'){
|
|
var o=ac.createOscillator(),g=ac.createGain();
|
|
o.type='triangle';o.frequency.value=freq;
|
|
o.connect(g);g.connect(ac.destination);
|
|
g.gain.setValueAtTime(0.001,t);g.gain.linearRampToValueAtTime(v*0.45,t+0.05);
|
|
if(dur>0.1)g.gain.setValueAtTime(v*0.45,t+dur*0.8);
|
|
g.gain.linearRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
}else if(inst==='bass'){
|
|
var o=ac.createOscillator(),g=ac.createGain();
|
|
o.type='sine';o.frequency.value=freq;
|
|
o.connect(g);g.connect(ac.destination);
|
|
g.gain.setValueAtTime(v*0.6,t);
|
|
if(dur>0.08)g.gain.setValueAtTime(v*0.6,t+dur*0.8);
|
|
g.gain.linearRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
}else if(inst==='timpani'){
|
|
var o=ac.createOscillator(),g=ac.createGain();
|
|
o.type='sine';o.connect(g);g.connect(ac.destination);
|
|
o.frequency.setValueAtTime(freq*1.5,t);
|
|
o.frequency.exponentialRampToValueAtTime(Math.max(freq*0.5,20),t+dur);
|
|
g.gain.setValueAtTime(v*0.8,t);
|
|
g.gain.exponentialRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
}else if(inst==='choir'){
|
|
[-6,0,6].forEach(function(d){
|
|
var o=ac.createOscillator(),g=ac.createGain();
|
|
o.type='sine';o.frequency.value=freq+d;
|
|
o.connect(g);g.connect(ac.destination);
|
|
var at2=Math.min(0.1,dur*0.2);
|
|
g.gain.setValueAtTime(0.001,t);g.gain.linearRampToValueAtTime(v*0.3,t+at2);
|
|
if(dur>at2+0.05)g.gain.setValueAtTime(v*0.3,t+dur*0.8);
|
|
g.gain.linearRampToValueAtTime(0.001,t+dur);
|
|
o.start(t);o.stop(t+dur+0.02);if(arr)arr.push(o);
|
|
});
|
|
}
|
|
}
|
|
|
|
const CLASSICAL_PIECES=(function(){
|
|
var G3=NT.G3,Ab3=NT.Ab3,A3=NT.A3,Bb3=NT.Bb3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,G4=NT.G4,Ab4=NT.Ab4,A4=NT.A4,Bb4=NT.Bb4,
|
|
C5=NT.C5,D5=NT.D5,Eb5=NT.Eb5,E5=NT.E5,F5=NT.F5,G5=NT.G5,A5=NT.A5,
|
|
C3=NT.C3,D3=NT.D3,E3=NT.E3,B4=NT.B4,F3=NT.F3,Gb3=NT.Gb3,Db4=NT.Db4,Gb4=NT.Gb4,Db5=NT.Db5;
|
|
|
|
// ── Beethoven's 5th Symphony (Opening) ──
|
|
// ~87 seconds to the natural break at recapitulation
|
|
// Continuous orchestral bed plays throughout; only strong melody accents are hittable
|
|
var b5=(function(){
|
|
var q=0.375,e=q/2,bg=[],mel=[];
|
|
|
|
// === CONTINUOUS HARMONIC BED (plays throughout, not hittable) ===
|
|
// Sustained string/woodwind chords that change with the harmony
|
|
var harmony=[
|
|
[0,5.5,[C4,Eb4,G4],0.15], // Cm - opening
|
|
[5.5,11,[C4,Eb4,G4],0.12], // Cm - echo
|
|
[11,18,[C4,Eb4,G4],0.18], // Cm - development
|
|
[18,26,[Ab3,C4,Eb4],0.2], // Ab - power chords
|
|
[26,32,[C4,Eb4,G4],0.15], // Cm - transition
|
|
[32,40,[G3,Bb3,D4],0.18], // Gm - rising tension
|
|
[40,50,[C4,Eb4,G4],0.22], // Cm - recapitulation
|
|
[50,60,[Ab3,C4,Eb4],0.2], // Ab - chords
|
|
[60,72,[C4,Eb4,G4],0.25], // Cm - building
|
|
[72,82,[G3,B3,D4],0.22], // G - dominant tension
|
|
[82,90,[C4,Eb4,G4],0.2] // Cm - resolution
|
|
];
|
|
harmony.forEach(function(h){
|
|
for(var t=h[0];t<h[1];t+=2.0){
|
|
h[2].forEach(function(f){
|
|
bg.push({t:t,f:f,d:1.9,i:'strings',v:h[3]});
|
|
});
|
|
bg.push({t:t,f:h[2][0]/2,d:1.9,i:'bass',v:h[3]*0.6});
|
|
if(h[3]>0.18)bg.push({t:t,f:h[2][1],d:1.9,i:'woodwind',v:h[3]*0.5});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-5.5s): THE OPENING — G-G-G-Eb, F-F-F-D ===
|
|
function tutti(t,freq,dur,v){
|
|
bg.push({t:t,f:freq,d:dur,i:'strings',v:v});
|
|
bg.push({t:t,f:freq,d:dur,i:'brass',v:v*0.6});
|
|
bg.push({t:t,f:freq*0.75,d:dur,i:'strings',v:v*0.35});
|
|
bg.push({t:t,f:freq*0.6,d:dur,i:'woodwind',v:v*0.3});
|
|
if(freq>200)bg.push({t:t,f:freq/2,d:dur,i:'bass',v:v*0.5});
|
|
}
|
|
// G-G-G-Eb (ff)
|
|
tutti(0,G4,0.18,0.55);tutti(q,G4,0.18,0.55);tutti(q*2,G4,0.18,0.55);
|
|
tutti(q*3,Eb4,1.8,0.6);
|
|
bg.push({t:0,f:G3,d:0.18,i:'timpani',v:0.45});
|
|
mel.push({t:0,l:0},{t:q,l:0},{t:q*2,l:0},{t:q*3,l:3});
|
|
// F-F-F-D
|
|
var s=3.2;
|
|
tutti(s,F4,0.18,0.55);tutti(s+q,F4,0.18,0.55);tutti(s+q*2,F4,0.18,0.55);
|
|
tutti(s+q*3,D4,1.8,0.6);
|
|
mel.push({t:s,l:1},{t:s+q,l:1},{t:s+q*2,l:1},{t:s+q*3,l:3});
|
|
|
|
// === SECTION 2 (5.5-11s): SOFT RESTATEMENT — strings only, woodwind color ===
|
|
s=6.0;
|
|
// Running eighth notes in 2nd violins (background texture, not hittable)
|
|
for(var ri=0;ri<20;ri++){
|
|
var rf=[G4,Eb4,F4,D4,G4,F4,Eb4,D4,C4,Eb4,G4,F4,Eb4,D4,C4,B3,C4,D4,Eb4,F4][ri];
|
|
bg.push({t:s+ri*e,f:rf,d:e*0.8,i:'strings',v:0.18});
|
|
}
|
|
// Melody accents (hittable) — just the motif peaks
|
|
bg.push({t:6.0,f:G4,d:0.16,i:'woodwind',v:0.3});mel.push({t:6.0,l:0});
|
|
bg.push({t:6.0+q,f:G4,d:0.16,i:'woodwind',v:0.3});mel.push({t:6.0+q,l:0});
|
|
bg.push({t:6.0+q*2,f:G4,d:0.16,i:'woodwind',v:0.3});mel.push({t:6.0+q*2,l:0});
|
|
bg.push({t:6.0+q*3,f:Eb4,d:1.0,i:'woodwind',v:0.35});mel.push({t:6.0+q*3,l:3});
|
|
s=8.5;
|
|
bg.push({t:s,f:F4,d:0.16,i:'woodwind',v:0.3});mel.push({t:s,l:1});
|
|
bg.push({t:s+q,f:F4,d:0.16,i:'woodwind',v:0.3});mel.push({t:s+q,l:1});
|
|
bg.push({t:s+q*2,f:F4,d:0.16,i:'woodwind',v:0.3});mel.push({t:s+q*2,l:1});
|
|
bg.push({t:s+q*3,f:D4,d:1.0,i:'woodwind',v:0.35});mel.push({t:s+q*3,l:3});
|
|
|
|
// === SECTION 3 (11-18s): DEVELOPMENT — faster motifs + scalar run ===
|
|
s=11.5;
|
|
tutti(s,G4,0.15,0.5);tutti(s+e,G4,0.15,0.5);tutti(s+e*2,G4,0.15,0.5);
|
|
tutti(s+e*3,Eb4,0.5,0.55);
|
|
mel.push({t:s,l:0},{t:s+e,l:0},{t:s+e*2,l:0},{t:s+e*3,l:3});
|
|
s=12.5;
|
|
tutti(s,F4,0.15,0.5);tutti(s+e,F4,0.15,0.5);tutti(s+e*2,F4,0.15,0.5);
|
|
tutti(s+e*3,D4,0.5,0.55);
|
|
mel.push({t:s,l:1},{t:s+e,l:1},{t:s+e*2,l:1},{t:s+e*3,l:3});
|
|
// Scalar run — violins play fast, you hit the accent notes
|
|
s=14.0;
|
|
var scaleDown=[G4,F4,Eb4,D4,C4,B3,C4,D4,Eb4,F4,G4,Ab4,Bb4,Ab4,G4,F4,Eb4,D4,C4,B3];
|
|
scaleDown.forEach(function(f,i){
|
|
bg.push({t:s+i*e,f:f,d:e*0.85,i:'strings',v:0.4});
|
|
bg.push({t:s+i*e,f:f*0.8,d:e*0.85,i:'strings',v:0.2}); // violas
|
|
});
|
|
// Hittable: only every 4th note (the downbeat accents)
|
|
[0,4,8,12,16].forEach(function(i){
|
|
mel.push({t:s+i*e,l:[2,0,2,0,0][Math.floor(i/4)]});
|
|
});
|
|
|
|
// === SECTION 4 (18-28s): POWER CHORDS — big hits with timpani ===
|
|
s=18.5;
|
|
var chords=[[G4,Eb4,C4],[Ab4,F4,C4],[Bb4,G4,Eb4],[Ab4,F4,C4],[G4,Eb4,C4],[F4,D4,Bb3],[Eb4,C4,Ab3],[D4,B3,G3]];
|
|
chords.forEach(function(ch,i){
|
|
var t2=s+i*1.1;
|
|
ch.forEach(function(f){
|
|
bg.push({t:t2,f:f,d:1.0,i:'brass',v:0.55});
|
|
bg.push({t:t2,f:f,d:1.0,i:'strings',v:0.45});
|
|
});
|
|
bg.push({t:t2,f:ch[2],d:1.0,i:'bass',v:0.4});
|
|
bg.push({t:t2,f:ch[2]/2,d:0.8,i:'timpani',v:0.55});
|
|
mel.push({t:t2,l:4});
|
|
});
|
|
|
|
// === SECTION 5 (28-40s): MOTIF IN NEW KEYS — rising tension ===
|
|
// Motif passed between brass and strings at different pitches
|
|
s=28.0;
|
|
// Background: continuous running strings
|
|
for(var ri=0;ri<48;ri++){
|
|
var rf2=[G4,Eb4,C4,Eb4,F4,D4,Bb3,D4][ri%8];
|
|
bg.push({t:s+ri*e,f:rf2,d:e*0.8,i:'strings',v:0.15});
|
|
}
|
|
// Hittable motif fragments at rising pitches
|
|
[[s,G4,Eb4],[s+2.5,Ab4,F4],[s+5.0,Bb4,G4],[s+7.5,C5,Ab4],[s+10.0,D5,Bb4]].forEach(function(m){
|
|
tutti(m[0],m[1],0.14,0.55);tutti(m[0]+e,m[1],0.14,0.55);tutti(m[0]+e*2,m[1],0.14,0.55);
|
|
tutti(m[0]+e*3,m[2],0.6,0.6);
|
|
mel.push({t:m[0],l:0},{t:m[0]+e,l:0},{t:m[0]+e*2,l:0},{t:m[0]+e*3,l:3});
|
|
});
|
|
|
|
// === SECTION 6 (40-52s): SUSTAINED DRAMA — choir + horns, slow chords ===
|
|
s=41.0;
|
|
[[C4,Eb4,G4],[Bb3,D4,F4],[Ab3,C4,Eb4],[G3,B3,D4],[C4,Eb4,G4],[F3,Ab3,C4]].forEach(function(ch,i){
|
|
var t2=s+i*1.8;
|
|
ch.forEach(function(f){
|
|
bg.push({t:t2,f:f,d:1.6,i:'strings',v:0.5});
|
|
bg.push({t:t2,f:f,d:1.6,i:'brass',v:0.4});
|
|
bg.push({t:t2,f:f,d:1.6,i:'choir',v:0.3});
|
|
});
|
|
bg.push({t:t2,f:ch[0]/2,d:1.6,i:'bass',v:0.4});
|
|
bg.push({t:t2,f:ch[0]/2,d:1.2,i:'timpani',v:0.5});
|
|
mel.push({t:t2,l:4},{t:t2+0.9,l:i%2===0?0:1});
|
|
});
|
|
|
|
// === SECTION 7 (52-64s): RECAPITULATION — motif returns fff ===
|
|
s=53.0;
|
|
tutti(s,G4,0.18,0.7);tutti(s+q,G4,0.18,0.7);tutti(s+q*2,G4,0.18,0.7);
|
|
tutti(s+q*3,Eb4,1.8,0.75);
|
|
bg.push({t:s+q*3,f:Eb4,d:1.8,i:'choir',v:0.35});bg.push({t:s+q*3,f:G4,d:1.8,i:'choir',v:0.3});
|
|
bg.push({t:s,f:G3,d:0.2,i:'timpani',v:0.55});
|
|
mel.push({t:s,l:0},{t:s+q,l:0},{t:s+q*2,l:0},{t:s+q*3,l:3});
|
|
s=56.2;
|
|
tutti(s,F4,0.18,0.7);tutti(s+q,F4,0.18,0.7);tutti(s+q*2,F4,0.18,0.7);
|
|
tutti(s+q*3,D4,1.8,0.75);
|
|
bg.push({t:s+q*3,f:D4,d:1.8,i:'choir',v:0.35});bg.push({t:s+q*3,f:F4,d:1.8,i:'choir',v:0.3});
|
|
mel.push({t:s,l:1},{t:s+q,l:1},{t:s+q*2,l:1},{t:s+q*3,l:3});
|
|
// Ascending run with full orchestra
|
|
s=59.5;
|
|
[C4,D4,Eb4,F4,G4,Ab4,Bb4,C5,D5,Eb5,D5,C5,Bb4,Ab4,G4,F4].forEach(function(f,i){
|
|
var t2=s+i*e;
|
|
bg.push({t:t2,f:f,d:e*0.85,i:'strings',v:0.5});
|
|
bg.push({t:t2,f:f,d:e*0.85,i:'brass',v:0.3});
|
|
bg.push({t:t2,f:f*0.75,d:e*0.85,i:'strings',v:0.25});
|
|
});
|
|
[0,4,8,12].forEach(function(i){mel.push({t:s+i*e,l:[0,1,2,3][Math.floor(i/4)]});});
|
|
|
|
// === SECTION 8 (64-76s): FINAL MOTIF HAMMERING ===
|
|
s=64.0;
|
|
// Motif stated 3 times, each louder — whole orchestra
|
|
[[G4,Eb4,0.65],[G4,Eb4,0.72],[G4,Eb4,0.8]].forEach(function(m,mi){
|
|
var t2=s+mi*3.5;
|
|
tutti(t2,m[0],0.2,m[2]);tutti(t2+q,m[0],0.2,m[2]);tutti(t2+q*2,m[0],0.2,m[2]);
|
|
tutti(t2+q*3,m[1],2.0,m[2]);
|
|
bg.push({t:t2+q*3,f:m[1],d:2.0,i:'choir',v:m[2]*0.5});
|
|
bg.push({t:t2,f:G3,d:0.2,i:'timpani',v:m[2]*0.7});
|
|
mel.push({t:t2,l:0},{t:t2+q,l:0},{t:t2+q*2,l:0},{t:t2+q*3,l:3});
|
|
});
|
|
|
|
// === SECTION 9 (76-85s): FINAL CHORDS — cadence to the break ===
|
|
s=76.0;
|
|
[0,0.6,1.2,1.8,2.4,3.0,3.6,4.2].forEach(function(dt,i){
|
|
var t2=s+dt;
|
|
[C4,Eb4,G4].forEach(function(f){
|
|
bg.push({t:t2,f:f,d:0.5,i:'brass',v:0.7});
|
|
bg.push({t:t2,f:f,d:0.5,i:'strings',v:0.6});
|
|
});
|
|
bg.push({t:t2,f:C3,d:0.5,i:'bass',v:0.55});
|
|
bg.push({t:t2,f:C3,d:0.5,i:'timpani',v:0.65});
|
|
mel.push({t:t2,l:4});
|
|
});
|
|
|
|
// === SECTION 10 (81-87s): FINAL SUSTAINED CHORD — natural break ===
|
|
s=81.0;
|
|
[C4,Eb4,G4,C5].forEach(function(f){
|
|
bg.push({t:s,f:f,d:5.0,i:'brass',v:0.6});
|
|
bg.push({t:s,f:f,d:5.0,i:'strings',v:0.5});
|
|
bg.push({t:s,f:f,d:5.0,i:'choir',v:0.35});
|
|
});
|
|
bg.push({t:s,f:C3,d:4.0,i:'bass',v:0.5});
|
|
bg.push({t:s,f:C3,d:3.0,i:'timpani',v:0.55});
|
|
mel.push({t:s,l:4});
|
|
|
|
return {n:"Beethoven's 5th (Opening)",composer:"Beethoven",bpm:160,duration:88,isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Hall of the Mountain King ──
|
|
var hmk=(function(){
|
|
var bg=[],mel=[];
|
|
function orch(t,freq,dur,v,inst){bg.push({t:t,f:freq,d:dur,i:inst||'strings',v:v||0.3});}
|
|
var theme=[A3,B3,C4,D4,E4,C4,E4,D4,B3,D4,Db4,A3,Db4,A3];
|
|
var lanes=[0,1,2,0,1,2,1,0,3,0,1,3,1,3];
|
|
var t=0;
|
|
// Pass 1: Very slow, barely audible (creeping start)
|
|
var d0=0.55;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d0*0.8,0.2,'woodwind');
|
|
mel.push({t:t,l:lanes[i]});t+=d0;
|
|
});
|
|
t+=0.5;
|
|
// Pass 2: Slow, a bit louder
|
|
var d1=0.45;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d1*0.8,0.25,'woodwind');orch(t,f/2,d1*0.8,0.15,'bass');
|
|
mel.push({t:t,l:lanes[i]});t+=d1;
|
|
});
|
|
t+=0.4;
|
|
// Pass 3: Medium-slow, strings enter
|
|
var d2=0.35;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d2*0.8,0.3,'strings');orch(t,f/2,d2*0.8,0.2,'bass');
|
|
mel.push({t:t,l:lanes[i]});t+=d2;
|
|
});
|
|
t+=0.35;
|
|
// Pass 4: Medium, strings + woodwind
|
|
var d3=0.27;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d3*0.8,0.35,'strings');orch(t,f,d3*0.8,0.2,'woodwind');
|
|
orch(t,f/2,d3*0.8,0.25,'bass');mel.push({t:t,l:lanes[i]});t+=d3;
|
|
});
|
|
t+=0.3;
|
|
// Pass 5: Medium-fast, add brass
|
|
var d4=0.21;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d4*0.8,0.4,'strings');orch(t,f,d4*0.8,0.25,'brass');
|
|
orch(t,f/2,d4*0.8,0.3,'bass');mel.push({t:t,l:lanes[i]});t+=d4;
|
|
});
|
|
t+=0.25;
|
|
// Pass 6: Fast, full orchestra
|
|
var d5=0.16;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d5*0.8,0.5,'strings');orch(t,f,d5*0.8,0.35,'brass');
|
|
orch(t,f/2,d5*0.8,0.35,'bass');
|
|
if(i%4===0)bg.push({t:t,f:A3/2,d:0.3,i:'timpani',v:0.45});
|
|
mel.push({t:t,l:lanes[i]});t+=d5;
|
|
});
|
|
t+=0.2;
|
|
// Pass 7: Very fast, frantic
|
|
var d6=0.12;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d6*0.8,0.6,'strings');orch(t,f,d6*0.8,0.45,'brass');
|
|
orch(t,f/2,d6*0.8,0.4,'bass');
|
|
if(i%2===0)bg.push({t:t,f:A3/2,d:0.2,i:'timpani',v:0.55});
|
|
mel.push({t:t,l:lanes[i]});t+=d6;
|
|
});
|
|
t+=0.15;
|
|
// Pass 8: Extremely fast, maximum intensity
|
|
var d7=0.095;
|
|
theme.forEach(function(f,i){
|
|
orch(t,f,d7*0.8,0.65,'strings');orch(t,f,d7*0.8,0.5,'brass');
|
|
orch(t,f/2,d7*0.8,0.45,'bass');
|
|
bg.push({t:t,f:A3/2,d:0.15,i:'timpani',v:0.6});
|
|
mel.push({t:t,l:lanes[i]});t+=d7;
|
|
});
|
|
// Big finale: repeated chords
|
|
t+=0.15;
|
|
for(var ci=0;ci<4;ci++){
|
|
var t2=t+ci*0.4;
|
|
[A3,C4,E4,A4].forEach(function(f){
|
|
orch(t2,f,0.35,0.65,'brass');orch(t2,f,0.35,0.55,'strings');
|
|
});
|
|
bg.push({t:t2,f:A3/2,d:0.4,i:'timpani',v:0.65});
|
|
mel.push({t:t2,l:4});
|
|
}
|
|
// Final long chord
|
|
t=t+1.8;
|
|
[A3,C4,E4,A4].forEach(function(f){
|
|
orch(t,f,2.0,0.6,'brass');orch(t,f,2.0,0.5,'strings');
|
|
});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'timpani',v:0.7});
|
|
mel.push({t:t,l:4});
|
|
return {n:"Hall of the Mountain King",composer:"Grieg",bpm:120,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── O Fortuna (Carmina Burana) ──
|
|
// ~90 seconds, conductor style with continuous harmonic bed, SATB choir, tutti hits
|
|
// D minor, 3/4 time. Lead-sheet structure with tempo changes throughout.
|
|
var ofort=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
|
|
// === HELPERS ===
|
|
// Full orchestral hit (strings + brass + bass)
|
|
function tutti(time,freq,dur,v){
|
|
bg.push({t:time,f:freq,d:dur,i:'strings',v:v});
|
|
bg.push({t:time,f:freq,d:dur,i:'brass',v:v*0.6});
|
|
bg.push({t:time,f:freq*0.75,d:dur,i:'strings',v:v*0.35});
|
|
if(freq>200)bg.push({t:time,f:freq/2,d:dur,i:'bass',v:v*0.5});
|
|
}
|
|
// SATB choir voicing (soprano + alto + tenor)
|
|
function choir4(time,sop,alt,ten,dur,v){
|
|
bg.push({t:time,f:sop,d:dur,i:'choir',v:v});
|
|
bg.push({t:time,f:alt,d:dur,i:'choir',v:v*0.85});
|
|
bg.push({t:time,f:ten,d:dur,i:'choir',v:v*0.7});
|
|
}
|
|
// Simple choir unison/octave
|
|
function choir1(time,freq,dur,v){
|
|
bg.push({t:time,f:freq,d:dur,i:'choir',v:v});
|
|
bg.push({t:time,f:freq/2,d:dur*0.9,i:'choir',v:v*0.5});
|
|
}
|
|
// Timpani hit
|
|
function timp(time,v){bg.push({t:time,f:D3,d:0.5,i:'timpani',v:v||0.5});}
|
|
|
|
// === CONTINUOUS HARMONIC BED (sustained string chords throughout) ===
|
|
var harmony=[
|
|
[0,6,[D4,F4,A4],0.18], // Dm - opening ff
|
|
[6,18,[D4,F4,A4],0.1], // Dm - pp soft verse
|
|
[18,30,[D4,F4,A4],0.1], // Dm - pp continued
|
|
[30,42,[G3,Bb3,D4],0.14], // Gm - building mp
|
|
[42,54,[D4,F4,A4],0.18], // Dm - dramatic
|
|
[54,64,[A3,D4,F4],0.2], // Dm inv - forte return
|
|
[64,76,[D4,F4,A4],0.25], // Dm - ff marcato
|
|
[76,88,[D4,F4,A4],0.3], // Dm - fff climax
|
|
[88,95,[D4,F4,A4],0.25] // Dm - finale
|
|
];
|
|
harmony.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=1.8){
|
|
h[2].forEach(function(f){
|
|
bg.push({t:ht,f:f,d:1.7,i:'strings',v:h[3]});
|
|
});
|
|
bg.push({t:ht,f:h[2][0]/2,d:1.7,i:'bass',v:h[3]*0.6});
|
|
if(h[3]>0.15)bg.push({t:ht,f:h[2][1],d:1.7,i:'woodwind',v:h[3]*0.4});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-6s): "O Fortuna" — ff, half=60, bold opening ===
|
|
// 3/4 time, half note = 60 → quarter = 0.5s
|
|
var q1=0.5;
|
|
// "O For-tu-na, ve-lut Lu-na, sta-tu va-ri-a-bi-lis"
|
|
// Big unison D, then choral declamation
|
|
var s1notes=[D4,D4,D4,D4, A3,A3,Bb3,Bb3, A3,A3,G3,A3];
|
|
var s1durs= [q1,q1,q1,q1*2, q1,q1*2,q1,q1*2, q1,q1,q1,q1*2];
|
|
var s1lanes=[0,0,0,0, 1,1,2,2, 1,1,3,1];
|
|
t=0;
|
|
s1notes.forEach(function(f,i){
|
|
choir4(t,f,f>A3?D4:f,f>C4?A3:f*0.75,s1durs[i]*0.9,0.55);
|
|
tutti(t,f,s1durs[i]*0.85,0.5);
|
|
if(s1durs[i]>=q1*2)timp(t,0.55);
|
|
mel.push({t:t,l:s1lanes[i]});
|
|
t+=s1durs[i];
|
|
});
|
|
|
|
// === SECTION 2 (6-18s): "semper crescis aut decrescis" — pp, half=132, soft pairs ===
|
|
// half=132 → quarter = ~0.227s
|
|
var q2=0.227;
|
|
t=6.5;
|
|
// Running strings background texture (not hittable)
|
|
for(var ri=0;ri<50;ri++){
|
|
var rf=[D4,F4,A4,F4,D4,E4,F4,G4,A4,G4,F4,E4,D4,C4,Bb3,A3][ri%16];
|
|
bg.push({t:t+ri*q2,f:rf,d:q2*0.8,i:'strings',v:0.12});
|
|
}
|
|
// Soft choral pairs — only accents are hittable
|
|
// "sem-per cres-cis aut de-cres-cis"
|
|
var s2notes=[D4,E4, F4,E4, D4,C4, D4,D4];
|
|
var s2durs= [q2*2,q2*2, q2*2,q2*2, q2*2,q2*2, q2*2,q2*4];
|
|
var s2lanes=[0,1, 2,1, 0,3, 0,0];
|
|
s2notes.forEach(function(f,i){
|
|
choir1(t,f,s2durs[i]*0.9,0.3);
|
|
bg.push({t:t,f:f,d:s2durs[i]*0.85,i:'woodwind',v:0.18});
|
|
mel.push({t:t,l:s2lanes[i]});
|
|
t+=s2durs[i];
|
|
});
|
|
// "vi-ta de-tes-ta-bi-lis"
|
|
t+=q2;
|
|
var s2b=[A3,Bb3,A3,G3,A3,Bb3,C4,D4];
|
|
var s2bd=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s2bl=[1,2,1,3,1,2,0,0];
|
|
s2b.forEach(function(f,i){
|
|
choir1(t,f,s2bd[i]*0.9,0.3);
|
|
bg.push({t:t,f:f,d:s2bd[i]*0.85,i:'woodwind',v:0.18});
|
|
mel.push({t:t,l:s2bl[i]});
|
|
t+=s2bd[i];
|
|
});
|
|
|
|
// === SECTION 3 (18-30s): "nunc obdurat et tunc curat" — pp, same tempo ===
|
|
t=18.5;
|
|
// Running string texture
|
|
for(var ri2=0;ri2<52;ri2++){
|
|
var rf2=[A3,C4,D4,F4,E4,D4,C4,Bb3,A3,G3,A3,Bb3,C4,D4,E4,F4][ri2%16];
|
|
bg.push({t:t+ri2*q2,f:rf2,d:q2*0.8,i:'strings',v:0.13});
|
|
}
|
|
// "nunc ob-du-rat et tunc cu-rat"
|
|
var s3a=[D4,D4,E4,F4,E4,D4,C4,D4];
|
|
var s3ad=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s3al=[0,0,1,2,1,0,3,0];
|
|
s3a.forEach(function(f,i){
|
|
choir1(t,f,s3ad[i]*0.9,0.32);
|
|
bg.push({t:t,f:f,d:s3ad[i]*0.85,i:'woodwind',v:0.2});
|
|
mel.push({t:t,l:s3al[i]});
|
|
t+=s3ad[i];
|
|
});
|
|
// "lu-do men-tis a-ci-em"
|
|
t+=q2;
|
|
var s3b=[A3,Bb3,C4,Bb3,A3,G3,A3];
|
|
var s3bd=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s3bl=[1,2,0,2,1,3,1];
|
|
s3b.forEach(function(f,i){
|
|
choir1(t,f,s3bd[i]*0.9,0.32);
|
|
mel.push({t:t,l:s3bl[i]});
|
|
t+=s3bd[i];
|
|
});
|
|
|
|
// === SECTION 4 (30-42s): "egestatem potestatem" — building mp ===
|
|
t=30.5;
|
|
// Strings now more active
|
|
for(var ri3=0;ri3<52;ri3++){
|
|
var rf3=[D4,F4,A4,D5,A4,F4,D4,F4,G4,Bb4,G4,F4,E4,D4,C4,Bb3][ri3%16];
|
|
bg.push({t:t+ri3*q2,f:rf3,d:q2*0.8,i:'strings',v:0.18});
|
|
}
|
|
// "e-ges-ta-tem po-tes-ta-tem"
|
|
var s4a=[D4,E4,F4,G4,A4,G4,F4,E4];
|
|
var s4ad=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s4al=[0,1,2,3,0,3,2,1];
|
|
s4a.forEach(function(f,i){
|
|
choir4(t,f,f>D4?D4:f,f>C4?A3:f*0.75,s4ad[i]*0.9,0.4);
|
|
bg.push({t:t,f:f,d:s4ad[i]*0.85,i:'brass',v:0.15});
|
|
if(i%4===0)timp(t,0.35);
|
|
mel.push({t:t,l:s4al[i]});
|
|
t+=s4ad[i];
|
|
});
|
|
// "dis-sol-vit ut gla-ci-em"
|
|
t+=q2;
|
|
var s4b=[F4,E4,D4,C4,D4,E4,D4];
|
|
var s4bd=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s4bl=[2,1,0,3,0,1,0];
|
|
s4b.forEach(function(f,i){
|
|
choir4(t,f,D4,A3,s4bd[i]*0.9,0.42);
|
|
bg.push({t:t,f:f/2,d:s4bd[i]*0.85,i:'bass',v:0.25});
|
|
if(s4bd[i]>=q2*4)timp(t,0.4);
|
|
mel.push({t:t,l:s4bl[i]});
|
|
t+=s4bd[i];
|
|
});
|
|
|
|
// === SECTION 5 (42-54s): "Sors immanis et inanis" — dramatic ===
|
|
t=42.5;
|
|
// Running strings more intense
|
|
for(var ri4=0;ri4<52;ri4++){
|
|
var rf4=[D4,A4,F4,D4,G4,Bb4,G4,D4,A4,D5,A4,F4,E4,D4,C4,D4][ri4%16];
|
|
bg.push({t:t+ri4*q2,f:rf4,d:q2*0.8,i:'strings',v:0.22});
|
|
}
|
|
// "Sors im-ma-nis et in-a-nis"
|
|
var s5a=[A4,A4,G4,F4,E4,F4,G4,A4];
|
|
var s5ad=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s5al=[0,0,3,2,1,2,3,0];
|
|
s5a.forEach(function(f,i){
|
|
choir4(t,f,D4,A3,s5ad[i]*0.9,0.5);
|
|
tutti(t,f,s5ad[i]*0.85,0.3);
|
|
if(i%2===0)timp(t,0.45);
|
|
mel.push({t:t,l:s5al[i]});
|
|
t+=s5ad[i];
|
|
});
|
|
// "ro-ta tu vo-lu-bi-lis"
|
|
t+=q2;
|
|
var s5b=[D5,C5,Bb4,A4,G4,F4,E4,D4];
|
|
var s5bd=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s5bl=[4,0,2,1,3,2,1,0];
|
|
s5b.forEach(function(f,i){
|
|
choir4(t,f,F4,D4,s5bd[i]*0.9,0.52);
|
|
tutti(t,f,s5bd[i]*0.85,0.32);
|
|
if(i===0||i===4)timp(t,0.5);
|
|
mel.push({t:t,l:s5bl[i]});
|
|
t+=s5bd[i];
|
|
});
|
|
|
|
// === SECTION 6 (54-64s): More verses — "status malus" continuing pattern ===
|
|
t=54.5;
|
|
for(var ri5=0;ri5<44;ri5++){
|
|
var rf5=[D4,F4,A4,D5,C5,Bb4,A4,G4,F4,E4,D4,C4,Bb3,A3,C4,D4][ri5%16];
|
|
bg.push({t:t+ri5*q2,f:rf5,d:q2*0.8,i:'strings',v:0.2});
|
|
}
|
|
// Rising phrase "ve-rum est quod le-gi-tur"
|
|
var s6a=[D4,F4,A4,Bb4,A4,G4,F4,G4,A4,Bb4,C5,D5];
|
|
var s6ad=[q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*2,q2*4];
|
|
var s6al=[0,2,0,2,1,3,2,3,1,2,0,4];
|
|
s6a.forEach(function(f,i){
|
|
choir4(t,f,f>D4?D4:f,A3,s6ad[i]*0.9,0.48);
|
|
bg.push({t:t,f:f,d:s6ad[i]*0.85,i:'brass',v:0.22});
|
|
bg.push({t:t,f:f/2,d:s6ad[i]*0.85,i:'bass',v:0.3});
|
|
if(i%3===0)timp(t,0.42);
|
|
mel.push({t:t,l:s6al[i]});
|
|
t+=s6ad[i];
|
|
});
|
|
|
|
// === SECTION 7 (64-76s): "Sors salutis et virtutis" — f, half=144, marcato ===
|
|
// half=144 → quarter = ~0.208s
|
|
var q3=0.208;
|
|
t=64.5;
|
|
// Driving string ostinato
|
|
for(var ri6=0;ri6<56;ri6++){
|
|
var rf6=[D4,A4,D4,A4,F4,A4,F4,A4][ri6%8];
|
|
bg.push({t:t+ri6*q3,f:rf6,d:q3*0.75,i:'strings',v:0.25});
|
|
}
|
|
// Marcato choral accents
|
|
// "Sors sa-lu-tis et vir-tu-tis mi-chi nunc con-tra-ri-a"
|
|
var s7a=[D4,D4,E4,F4, E4,D4,C4,D4, A4,G4,F4,E4, D4,E4,F4,D4];
|
|
var s7ad=[q3*2,q3*2,q3*2,q3*2, q3*2,q3*2,q3*2,q3*4, q3*2,q3*2,q3*2,q3*2, q3*2,q3*2,q3*2,q3*4];
|
|
var s7al=[0,0,1,2, 1,0,3,0, 0,3,2,1, 0,1,2,4];
|
|
s7a.forEach(function(f,i){
|
|
choir4(t,f,f>D4?D4:f,f>C4?A3:D3,s7ad[i]*0.85,0.55);
|
|
tutti(t,f,s7ad[i]*0.8,0.45);
|
|
if(i%4===0)timp(t,0.5);
|
|
mel.push({t:t,l:s7al[i]});
|
|
t+=s7ad[i];
|
|
});
|
|
// "est af-fec-tus et de-fec-tus"
|
|
t+=q3;
|
|
var s7b=[A4,Bb4,A4,G4,F4,G4,A4,D4];
|
|
var s7bd=[q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*4];
|
|
var s7bl=[0,2,1,3,2,3,1,4];
|
|
s7b.forEach(function(f,i){
|
|
choir4(t,f,D4,A3,s7bd[i]*0.85,0.58);
|
|
tutti(t,f,s7bd[i]*0.8,0.48);
|
|
if(i%2===0)timp(t,0.52);
|
|
mel.push({t:t,l:s7bl[i]});
|
|
t+=s7bd[i];
|
|
});
|
|
|
|
// === SECTION 8 (76-88s): "Hac in hora sine mora" — ff, climax ===
|
|
// "corde pulsum tangite; quod per sortem sternit fortem mecum omnes plangite!"
|
|
t=76.5;
|
|
// Furious string tremolo
|
|
for(var ri7=0;ri7<60;ri7++){
|
|
var rf7=[D4,F4,A4,D5,A4,F4,D4,F4,A4,D5,F5,D5,A4,F4,D4,A3][ri7%16];
|
|
bg.push({t:t+ri7*q3,f:rf7,d:q3*0.7,i:'strings',v:0.3});
|
|
if(ri7%4===0)bg.push({t:t+ri7*q3,f:rf7*0.5,d:q3*2,i:'bass',v:0.25});
|
|
}
|
|
// "Hac in ho-ra si-ne mo-ra cor-de pul-sum tan-gi-te"
|
|
var s8a=[D5,D5,C5,Bb4,A4,G4,A4,Bb4,A4,G4,F4,E4,D4];
|
|
var s8ad=[q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*4];
|
|
var s8al=[4,4,0,2,1,3,1,2,1,3,2,1,0];
|
|
s8a.forEach(function(f,i){
|
|
choir4(t,f,f>D4?F4:f,f>C4?D4:A3,s8ad[i]*0.85,0.65);
|
|
tutti(t,f,s8ad[i]*0.8,0.55);
|
|
timp(t,0.55);
|
|
mel.push({t:t,l:s8al[i]});
|
|
t+=s8ad[i];
|
|
});
|
|
// "quod per sor-tem ster-nit for-tem"
|
|
t+=q3;
|
|
var s8b=[A4,Bb4,C5,D5,C5,Bb4,A4,G4];
|
|
var s8bd=[q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*2,q3*4];
|
|
var s8bl=[1,2,0,4,0,2,1,3];
|
|
s8b.forEach(function(f,i){
|
|
choir4(t,f,D4,A3,s8bd[i]*0.85,0.68);
|
|
tutti(t,f,s8bd[i]*0.8,0.58);
|
|
timp(t,0.58);
|
|
mel.push({t:t,l:s8bl[i]});
|
|
t+=s8bd[i];
|
|
});
|
|
// "me-cum om-nes plan-gi-te!"
|
|
t+=q3;
|
|
var s8c=[A4,A4,Bb4,A4,D5,D5,D4];
|
|
var s8cd=[q3*2,q3*2,q3*2,q3*2,q3*3,q3*3,q3*6];
|
|
var s8cl=[0,1,2,1,4,4,0];
|
|
s8c.forEach(function(f,i){
|
|
choir4(t,f,f>D4?F4:f,f>C4?D4:A3,s8cd[i]*0.85,0.72);
|
|
tutti(t,f,s8cd[i]*0.8,0.62);
|
|
timp(t,0.62);
|
|
mel.push({t:t,l:s8cl[i]});
|
|
t+=s8cd[i];
|
|
});
|
|
|
|
// === SECTION 9 (88-95s): Final sustained chords — fff, half=160 ===
|
|
t=88.5;
|
|
// Massive repeated D minor chords
|
|
var finQ=0.4;
|
|
for(var fc=0;fc<8;fc++){
|
|
var ft=t+fc*finQ;
|
|
[D4,F4,A4,D5].forEach(function(f){
|
|
bg.push({t:ft,f:f,d:finQ*0.85,i:'choir',v:0.7});
|
|
bg.push({t:ft,f:f,d:finQ*0.85,i:'brass',v:0.6});
|
|
bg.push({t:ft,f:f,d:finQ*0.85,i:'strings',v:0.55});
|
|
});
|
|
bg.push({t:ft,f:D3,d:finQ*0.85,i:'bass',v:0.55});
|
|
timp(ft,0.65);
|
|
mel.push({t:ft,l:4});
|
|
}
|
|
// Final sustained Dm chord
|
|
t=t+8*finQ+0.3;
|
|
[D4,F4,A4,D5].forEach(function(f){
|
|
bg.push({t:t,f:f,d:4.0,i:'choir',v:0.65});
|
|
bg.push({t:t,f:f,d:4.0,i:'brass',v:0.55});
|
|
bg.push({t:t,f:f,d:4.0,i:'strings',v:0.5});
|
|
});
|
|
bg.push({t:t,f:D3,d:3.5,i:'bass',v:0.55});
|
|
bg.push({t:t,f:D3,d:3.0,i:'timpani',v:0.6});
|
|
mel.push({t:t,l:4});
|
|
|
|
return {n:"O Fortuna",composer:"Orff",bpm:120,duration:Math.ceil(t+4.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// Theme-variation helper: repeats a theme across passes with growing intensity
|
|
function tp(name,comp,bpm,th,ln,passes,br){
|
|
var bg=[],mel=[],t=0,root=th[0];
|
|
passes.forEach(function(p,pi){
|
|
th.forEach(function(f,ni){
|
|
bg.push({t:t,f:f,d:p.d*0.85,i:p.i,v:p.v});
|
|
if(pi>=Math.floor(passes.length*0.4))bg.push({t:t,f:f/2,d:p.d*0.85,i:'bass',v:p.v*0.4});
|
|
if(pi>=Math.floor(passes.length*0.6)){
|
|
bg.push({t:t,f:f,d:p.d*0.85,i:pi>=Math.floor(passes.length*0.75)?'brass':'strings',v:p.v*0.5});
|
|
if(ni%4===0)bg.push({t:t,f:root/2,d:0.25,i:'timpani',v:p.v*0.5});
|
|
}
|
|
mel.push({t:t,l:ln[ni%ln.length]});t+=p.d;
|
|
});
|
|
t+=0.35;
|
|
if(br&&pi===Math.floor(passes.length/2)-1){
|
|
br.forEach(function(bf){bg.push({t:t,f:bf,d:1.0,i:'brass',v:0.5});bg.push({t:t,f:bf,d:1.0,i:'strings',v:0.4});bg.push({t:t,f:root/2,d:0.8,i:'timpani',v:0.5});mel.push({t:t,l:4});t+=1.15;});
|
|
}
|
|
});
|
|
for(var c=0;c<3;c++){bg.push({t:t,f:root,d:0.4,i:'brass',v:0.65});bg.push({t:t,f:root*3/2,d:0.4,i:'brass',v:0.6});bg.push({t:t,f:root*2,d:0.4,i:'strings',v:0.55});bg.push({t:t,f:root/2,d:0.4,i:'timpani',v:0.6});mel.push({t:t,l:4});t+=0.5;}
|
|
bg.push({t:t,f:root,d:2.0,i:'brass',v:0.6});bg.push({t:t,f:root*2,d:2.0,i:'strings',v:0.5});bg.push({t:t,f:root/2,d:1.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:name,composer:comp,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
|
|
// ── Ride of the Valkyries ──
|
|
// Conductor-style: ~85s, Bb major, 9/8 galloping compound meter
|
|
// Famous ascending brass arpeggio Bb-D-F as hittable melody
|
|
var valk=(function(){
|
|
var bg=[],mel=[];
|
|
function tutti(time,freq,dur,v){
|
|
bg.push({t:time,f:freq,d:dur,i:'strings',v:v});
|
|
bg.push({t:time,f:freq,d:dur,i:'brass',v:v*0.6});
|
|
bg.push({t:time,f:freq*0.75,d:dur,i:'strings',v:v*0.35});
|
|
if(freq>200)bg.push({t:time,f:freq/2,d:dur,i:'bass',v:v*0.5});
|
|
}
|
|
function timp(time,v){bg.push({t:time,f:Bb3,d:0.5,i:'timpani',v:v||0.5});}
|
|
|
|
// Galloping rhythm constants (9/8 compound: groups of 3 eighths)
|
|
var e8=0.22,q=0.44,dq=0.66; // eighth, quarter, dotted-quarter
|
|
|
|
// === CONTINUOUS HARMONIC BED ===
|
|
var harmony=[
|
|
[0,5,[Bb3,D4,F4],0.06], // Bb - intro tremolo
|
|
[5,17,[Bb3,D4,F4],0.12], // Bb - first horn call
|
|
[17,29,[Bb3,D4,F4],0.16], // Bb - second statement
|
|
[29,41,[Bb3,Eb4,G4],0.14], // Eb - development
|
|
[41,53,[Bb3,D4,F4],0.2], // Bb - recapitulation
|
|
[53,65,[Bb3,D4,F4],0.25], // Bb - climax
|
|
[65,78,[Bb3,D4,F4],0.18], // Bb - final
|
|
[78,88,[Bb3,D4,F4],0.12] // Bb - coda
|
|
];
|
|
harmony.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=1.8){
|
|
h[2].forEach(function(f){
|
|
bg.push({t:ht,f:f,d:1.7,i:'strings',v:h[3]});
|
|
});
|
|
bg.push({t:ht,f:h[2][0]/2,d:1.7,i:'bass',v:h[3]*0.5});
|
|
if(h[3]>0.15)bg.push({t:ht,f:h[2][1],d:1.7,i:'brass',v:h[3]*0.35});
|
|
}
|
|
});
|
|
|
|
// Helper: play the galloping arpeggio (ascending Bb-D-F-Bb)
|
|
// notes/lanes/mode control what plays and how loud
|
|
function gallop(t0,notes,lanes,v,withTutti){
|
|
var gt=t0;
|
|
notes.forEach(function(n,i){
|
|
var d=(i===notes.length-1)?dq:((i%3===0)?q:e8);
|
|
if(withTutti){
|
|
tutti(gt,n,d*0.85,v);
|
|
bg.push({t:gt,f:n,d:d*0.85,i:'brass',v:v*0.7});
|
|
}else{
|
|
bg.push({t:gt,f:n,d:d*0.85,i:'brass',v:v});
|
|
bg.push({t:gt,f:n,d:d*0.85,i:'strings',v:v*0.5});
|
|
}
|
|
mel.push({t:gt,l:lanes[i]});
|
|
gt+=d;
|
|
});
|
|
return gt;
|
|
}
|
|
|
|
// === SECTION 1 (0-5s): INTRODUCTION — string tremolo building ===
|
|
var t=0;
|
|
// Low string tremolo (background, not hittable)
|
|
for(var ri=0;ri<40;ri++){
|
|
var rf=[Bb3,F3,D3,F3,Bb3,F3,D3,F3][ri%8];
|
|
bg.push({t:ri*0.12,f:rf,d:0.11,i:'strings',v:0.06+ri*0.003});
|
|
}
|
|
// Low brass pedal tones
|
|
for(var bi=0;bi<3;bi++){
|
|
bg.push({t:bi*1.8,f:Bb3,d:1.6,i:'brass',v:0.06+bi*0.02});
|
|
}
|
|
// Timpani roll building
|
|
for(bi=0;bi<6;bi++){
|
|
timp(0.5+bi*0.7,0.15+bi*0.04);
|
|
}
|
|
// Sparse hittable timpani hits — building anticipation
|
|
mel.push({t:1.5,l:4});mel.push({t:3.0,l:4});mel.push({t:4.2,l:4});
|
|
t=5.0;
|
|
|
|
// === SECTION 2 (5-17s): FIRST HORN CALL — p to mf ===
|
|
// The famous galloping ascending arpeggio: Bb-Bb-D, D-D-F, F
|
|
// String tremolo continues underneath
|
|
var s2s=t;
|
|
for(ri=0;ri<55;ri++){
|
|
rf=[Bb3,D4,Bb3,F3,Bb3,D4,F4,D4][ri%8];
|
|
bg.push({t:s2s+ri*e8,f:rf,d:e8*0.8,i:'strings',v:0.1});
|
|
}
|
|
|
|
// First statement: ascending Bb-D-F (soft, horns entering)
|
|
// Bb(q) Bb(e8) D(e8) | D(q) D(e8) F(e8) | F(dq)
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.3,false);
|
|
t+=0.3;
|
|
// Descending: Bb(q) F(e8) D(e8) | Bb(q) D(e8) F(e8) | Bb(dq)
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.32,false);
|
|
t+=0.4;
|
|
|
|
// Second statement: louder, with woodwind doubling
|
|
var s2b=t;
|
|
for(ri=0;ri<55;ri++){
|
|
rf=[F4,D4,Bb3,D4,F4,Bb4,F4,D4][ri%8];
|
|
bg.push({t:s2b+ri*e8,f:rf,d:e8*0.8,i:'woodwind',v:0.08});
|
|
}
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.38,false);
|
|
t+=0.3;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.4,false);
|
|
t+=0.5;
|
|
|
|
// === SECTION 3 (17-29s): SECOND STATEMENT — mf to f, full brass ===
|
|
var s3s=t;
|
|
for(ri=0;ri<55;ri++){
|
|
rf=[Bb3,F4,D4,Bb3,F3,Bb3,D4,F4][ri%8];
|
|
bg.push({t:s3s+ri*e8,f:rf,d:e8*0.8,i:'strings',v:0.14});
|
|
}
|
|
// Woodwind sustained chords (background)
|
|
for(bi=0;bi<6;bi++){
|
|
[Bb3,D4,F4].forEach(function(f){
|
|
bg.push({t:s3s+bi*2.0,f:f,d:1.8,i:'woodwind',v:0.12});
|
|
});
|
|
}
|
|
|
|
// Horn call with tutti orchestration now
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.45,true);
|
|
t+=0.3;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.48,true);
|
|
t+=0.4;
|
|
// Repeat with kick accents on downbeats
|
|
timp(t,0.5);mel.push({t:t,l:4});t+=dq;
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.5,true);
|
|
t+=0.3;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.52,true);
|
|
t+=0.5;
|
|
|
|
// === SECTION 4 (29-41s): DEVELOPMENT — new key, Eb major ===
|
|
// Modulation to Eb, secondary melodic material
|
|
var s4s=t;
|
|
for(ri=0;ri<55;ri++){
|
|
rf=[Eb4,G4,Bb4,G4,Eb4,Bb3,Eb4,G4][ri%8];
|
|
bg.push({t:s4s+ri*e8,f:rf,d:e8*0.8,i:'strings',v:0.14});
|
|
}
|
|
for(bi=0;bi<6;bi++){
|
|
[Eb4,G4,Bb4].forEach(function(f){
|
|
bg.push({t:s4s+bi*2.0,f:f,d:1.8,i:'brass',v:0.12});
|
|
});
|
|
bg.push({t:s4s+bi*2.0,f:Eb4/2,d:1.8,i:'bass',v:0.15});
|
|
}
|
|
|
|
// Eb major arpeggio: Eb-Eb-G, G-G-Bb, Bb
|
|
t=gallop(t,[Eb4,Eb4,G4, G4,G4,Bb4, Bb4],[2,2,3, 3,3,0, 0],0.45,true);
|
|
t+=0.3;
|
|
t=gallop(t,[Bb4,G4,Eb4, G4,Eb4,Bb3, Eb4],[0,3,2, 3,2,1, 2],0.48,true);
|
|
t+=0.4;
|
|
// Back to Bb major with added power
|
|
timp(t,0.55);mel.push({t:t,l:4});t+=dq;
|
|
t=gallop(t,[Bb3,D4,F4, Bb4,F4,D4, Bb3],[0,1,2, 3,2,1, 0],0.5,true);
|
|
t+=0.3;
|
|
// Chromatic passage — building tension
|
|
[[C4,0],[D4,1],[Eb4,2],[E4,2],[F4,3]].forEach(function(n){
|
|
tutti(t,n[0],q*0.85,0.48);
|
|
bg.push({t:t,f:n[0],d:q*0.85,i:'brass',v:0.45});
|
|
mel.push({t:t,l:n[1]});t+=q;
|
|
});
|
|
tutti(t,F4,dq*0.85,0.52);mel.push({t:t,l:4});timp(t,0.55);t+=dq+0.5;
|
|
|
|
// === SECTION 5 (41-53s): RECAPITULATION — ff, full orchestra ===
|
|
var s5s=t;
|
|
for(ri=0;ri<55;ri++){
|
|
rf=[Bb3,D4,F4,Bb4,F4,D4,Bb3,F3][ri%8];
|
|
bg.push({t:s5s+ri*e8,f:rf,d:e8*0.8,i:'strings',v:0.18});
|
|
if(ri%4===0)bg.push({t:s5s+ri*e8,f:rf*0.5,d:e8*2,i:'brass',v:0.1});
|
|
}
|
|
for(bi=0;bi<6;bi++){
|
|
bg.push({t:s5s+bi*2.0,f:Bb3/2,d:1.8,i:'bass',v:0.22});
|
|
timp(s5s+bi*2.0,0.4);
|
|
}
|
|
|
|
// Full fff horn call — massive tutti
|
|
timp(t,0.6);mel.push({t:t,l:4});t+=dq;
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.6,true);
|
|
t+=0.25;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.62,true);
|
|
t+=0.35;
|
|
// Repeat — even louder
|
|
timp(t,0.65);mel.push({t:t,l:4});t+=dq;
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.65,true);
|
|
t+=0.25;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.68,true);
|
|
t+=0.5;
|
|
|
|
// === SECTION 6 (53-65s): CLIMAX — fff, maximum intensity ===
|
|
var s6s=t;
|
|
// Massive tremolo
|
|
for(ri=0;ri<60;ri++){
|
|
rf=[Bb3,D4,F4,Bb4,D5,Bb4,F4,D4][ri%8];
|
|
bg.push({t:s6s+ri*e8,f:rf,d:e8*0.8,i:'strings',v:0.2});
|
|
if(ri%3===0)bg.push({t:s6s+ri*e8,f:rf*0.5,d:e8*1.5,i:'brass',v:0.15});
|
|
}
|
|
for(bi=0;bi<7;bi++){
|
|
bg.push({t:s6s+bi*1.6,f:Bb3/2,d:1.4,i:'bass',v:0.28});
|
|
timp(s6s+bi*1.6,0.5);
|
|
}
|
|
|
|
// Rapid repeated gallops — faster, more intense
|
|
var fe8=0.18,fq=0.36,fdq=0.54; // faster tempo
|
|
// Ascending blast
|
|
[Bb3,D4,F4,Bb4].forEach(function(f,i){
|
|
tutti(t,f,fq*0.85,0.7);
|
|
bg.push({t:t,f:f,d:fq*0.85,i:'brass',v:0.65});
|
|
mel.push({t:t,l:i});t+=fq;
|
|
});
|
|
timp(t,0.65);mel.push({t:t,l:4});t+=fdq;
|
|
// Descending
|
|
[Bb4,F4,D4,Bb3].forEach(function(f,i){
|
|
tutti(t,f,fq*0.85,0.7);
|
|
bg.push({t:t,f:f,d:fq*0.85,i:'brass',v:0.65});
|
|
mel.push({t:t,l:[3,2,1,0][i]});t+=fq;
|
|
});
|
|
timp(t,0.65);mel.push({t:t,l:4});t+=fdq;
|
|
// Full speed gallop x2
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.7,true);
|
|
t+=0.2;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.72,true);
|
|
t+=0.3;
|
|
// One more ascending blast
|
|
[Bb3,D4,F4,Bb4,D5].forEach(function(f,i){
|
|
tutti(t,f,fe8*0.85,0.72);
|
|
bg.push({t:t,f:f,d:fe8*0.85,i:'brass',v:0.7});
|
|
mel.push({t:t,l:[0,1,2,3,4][i]});t+=fe8;
|
|
});
|
|
t+=0.5;
|
|
|
|
// === SECTION 7 (65-78s): SUSTAINED POWER — massive held chords ===
|
|
// Big chord hits with space between them
|
|
var chords=[[Bb3,D4,F4,Bb4],[Eb4,G4,Bb4,Eb5],[F4,A4,C5,F5],[Bb3,D4,F4,Bb4],
|
|
[Bb3,D4,F4,Bb4],[Eb4,G4,Bb4,Eb5],[F4,A4,C5,F5],[Bb3,D4,F4,D5]];
|
|
chords.forEach(function(ch,ci){
|
|
var ct=t+ci*1.5;
|
|
ch.forEach(function(f){
|
|
bg.push({t:ct,f:f,d:1.3,i:'brass',v:0.6});
|
|
bg.push({t:ct,f:f,d:1.3,i:'strings',v:0.5});
|
|
});
|
|
bg.push({t:ct,f:ch[0]/2,d:1.3,i:'bass',v:0.45});
|
|
timp(ct,0.6);
|
|
mel.push({t:ct,l:4});
|
|
// Add the gallop rhythm between chords
|
|
if(ci<7){
|
|
mel.push({t:ct+0.5,l:ci%2===0?0:2});
|
|
bg.push({t:ct+0.5,f:ch[1],d:0.3,i:'brass',v:0.4});
|
|
mel.push({t:ct+0.9,l:ci%2===0?1:3});
|
|
bg.push({t:ct+0.9,f:ch[2],d:0.3,i:'brass',v:0.4});
|
|
}
|
|
});
|
|
t+=chords.length*1.5+0.5;
|
|
|
|
// === SECTION 8 (78-88s): FINAL CODA ===
|
|
// Last galloping statement — triumphant
|
|
timp(t,0.7);mel.push({t:t,l:4});t+=dq;
|
|
t=gallop(t,[Bb3,Bb3,D4, D4,D4,F4, F4],[0,0,1, 1,1,2, 2],0.7,true);
|
|
t+=0.2;
|
|
t=gallop(t,[Bb4,F4,D4, Bb3,D4,F4, Bb4],[3,2,1, 0,1,2, 3],0.72,true);
|
|
t+=0.4;
|
|
|
|
// Final massive Bb major chord
|
|
[Bb3,D4,F4,Bb4,D5,F5].forEach(function(f){
|
|
bg.push({t:t,f:f,d:5.0,i:'brass',v:0.6});
|
|
bg.push({t:t,f:f,d:5.0,i:'strings',v:0.5});
|
|
});
|
|
bg.push({t:t,f:Bb3/2,d:4.0,i:'bass',v:0.55});
|
|
bg.push({t:t,f:Bb3,d:3.5,i:'timpani',v:0.65});
|
|
mel.push({t:t,l:4});
|
|
|
|
return {n:"Ride of the Valkyries",composer:"Wagner",bpm:140,duration:Math.ceil(t+5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── William Tell Overture (Gallop) ──
|
|
// The famous finale gallop in G major, ~70s
|
|
// Trumpet fanfare → galloping theme → accelerating coda
|
|
var tell=(function(){
|
|
var bg=[],mel=[],t=0,q=60/152;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});}
|
|
function tutti(t,f,d,v){str(t,f,d,v);brs(t,f,d,(v||0.4)*0.7);bg.push({t:t,f:f/2,d:d,i:'bass',v:(v||0.4)*0.4});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,8,[G3,B3,D4],0.12],[8,20,[G3,B3,D4],0.15],[20,32,[G3,B3,D4],0.18],
|
|
[32,44,[C4,E4,G4],0.16],[44,56,[G3,B3,D4],0.2],[56,72,[G3,B3,D4],0.22]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:1.9,i:'strings',v:h[3]});});
|
|
bg.push({t:ht,f:h[2][0]/2,d:1.9,i:'bass',v:h[3]*0.5});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-8s): Trumpet fanfare intro ===
|
|
// Galloping triplet rhythm on trumpet: da-da-DUM da-da-DUM
|
|
var trip=q/3;
|
|
for(var fi=0;fi<4;fi++){
|
|
var ft=fi*q*2;
|
|
brs(ft,D5,trip*0.8,0.45);brs(ft+trip,D5,trip*0.8,0.45);brs(ft+trip*2,G4,q*0.8,0.5);
|
|
mel.push({t:ft,l:0},{t:ft+trip,l:0},{t:ft+trip*2,l:3});
|
|
}
|
|
t=q*8;
|
|
// Sustained trumpet call
|
|
brs(t,D5,q*2,0.5);brs(t,B4,q*2,0.45);mel.push({t:t,l:1});
|
|
t+=q*2.5;
|
|
brs(t,G4,q*3,0.55);brs(t,D4,q*3,0.5);bg.push({t:t,f:G3,d:q*2,i:'timpani',v:0.4});mel.push({t:t,l:3});
|
|
t=8;
|
|
|
|
// === SECTION 2 (8-20s): Main galloping theme ===
|
|
// The famous melody: G-G-B | G-G-B | D-D-B-G | E-G-B-D-B-G
|
|
var gallop1=[
|
|
[G4,trip,0],[G4,trip,0],[B4,q,3],
|
|
[G4,trip,0],[G4,trip,0],[B4,q,3],
|
|
[D5,trip,1],[D5,trip,1],[B4,q*0.7,3],[G4,q*0.7,0],
|
|
[E4,q*0.7,2],[G4,trip,0],[B4,trip,3],[D5,q,1],[B4,trip,3],[G4,q,0]
|
|
];
|
|
for(var rep=0;rep<3;rep++){
|
|
var v1=0.35+rep*0.08;
|
|
gallop1.forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.85,v1);
|
|
mel.push({t:t,l:n[2]});
|
|
t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// === SECTION 3 (20-32s): Contrasting lyrical phrase ===
|
|
// Strings take over with smoother melody
|
|
var lyric=[[B4,q*2,3],[A4,q,1],[G4,q,0],[A4,q*2,1],[B4,q*2,3],
|
|
[D5,q*2,1],[C5,q,2],[B4,q,3],[A4,q*2,1],[G4,q*2,0]];
|
|
for(var lr=0;lr<2;lr++){
|
|
var vl=0.4+lr*0.08;
|
|
lyric.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,vl);
|
|
if(lr>0)brs(t,n[0],n[1]*0.9,vl*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (32-44s): Theme returns with full orchestra ===
|
|
for(var rep2=0;rep2<3;rep2++){
|
|
var v2=0.45+rep2*0.08;
|
|
gallop1.forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.85,v2);
|
|
if(rep2>0)bg.push({t:t,f:n[0],d:n[1]*0.85,i:'brass',v:v2*0.5});
|
|
if(rep2>1&&n[2]===0)bg.push({t:t,f:G3,d:0.15,i:'timpani',v:v2*0.5});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// === SECTION 5 (44-56s): Ascending fanfare + gallop intensifies ===
|
|
// Brass fanfare ascending
|
|
var fanfare=[[G4,0],[B4,3],[D5,1],[G5,3],[D5,1],[B4,3],[G4,0]];
|
|
for(var ff=0;ff<2;ff++){
|
|
fanfare.forEach(function(n){
|
|
brs(t,n[0],q*0.85,0.55);str(t,n[0],q*0.85,0.45);
|
|
mel.push({t:t,l:n[1]});t+=q;
|
|
});
|
|
bg.push({t:t,f:G3,d:0.4,i:'timpani',v:0.5});mel.push({t:t,l:4});t+=q*1.5;
|
|
}
|
|
// Double-time gallop
|
|
var dTrip=trip*0.8;
|
|
for(var dg=0;dg<4;dg++){
|
|
var vd=0.5+dg*0.04;
|
|
gallop1.slice(0,10).forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.7,vd);mel.push({t:t,l:n[2]});t+=n[1]*0.8;
|
|
});
|
|
t+=q*0.3;
|
|
}
|
|
|
|
// === SECTION 6 (56-68s): Final gallop — full speed ===
|
|
for(var fg=0;fg<4;fg++){
|
|
var vf=0.55+fg*0.05;
|
|
gallop1.forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.7,vf);
|
|
brs(t,n[0],n[1]*0.7,vf*0.6);
|
|
if(fg>1)bg.push({t:t,f:n[0],d:n[1]*0.7,i:'choir',v:vf*0.25});
|
|
mel.push({t:t,l:n[2]});t+=n[1]*0.75;
|
|
});
|
|
bg.push({t:t,f:G3,d:0.3,i:'timpani',v:vf*0.7});mel.push({t:t,l:4});t+=q*0.5;
|
|
}
|
|
|
|
// === FINALE: Big G major chords ===
|
|
for(var c=0;c<6;c++){
|
|
[G3,B3,D4,G4,B4,D5].forEach(function(f){brs(t,f,0.4,0.65);str(t,f,0.4,0.55);});
|
|
bg.push({t:t,f:G3/2,d:0.4,i:'timpani',v:0.65});mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[G3,B3,D4,G4,B4,D5].forEach(function(f){brs(t,f,3.0,0.7);str(t,f,3.0,0.6);});
|
|
bg.push({t:t,f:G3/2,d:2.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
|
|
return {n:"William Tell Overture (Gallop)",composer:"Rossini",bpm:152,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Bolero ──
|
|
// Ravel's Bolero in C major, ~90s
|
|
// Built around an endlessly repeating snare drum rhythm that crescendos
|
|
// The melody is stated by different instruments, each time louder
|
|
var bolero=(function(){
|
|
var bg=[],mel=[],t=0,q=60/72;
|
|
|
|
// The iconic Bolero snare rhythm: 3+3+3+3+2+2 pattern per bar
|
|
// ta-ta-ta ta-ta-ta ta-ta-ta ta-ta-ta ta-ta ta-ta
|
|
function snareBar(time,v){
|
|
var s=q/3;
|
|
for(var g=0;g<4;g++){
|
|
for(var n=0;n<3;n++){bg.push({t:time+g*q+n*s,f:300,d:0.05,i:'woodwind',v:v*0.3});}
|
|
}
|
|
// The two duplets at end
|
|
bg.push({t:time+q*4,f:300,d:0.05,i:'woodwind',v:v*0.35});
|
|
bg.push({t:time+q*4+q/2,f:300,d:0.05,i:'woodwind',v:v*0.35});
|
|
bg.push({t:time+q*5,f:300,d:0.05,i:'woodwind',v:v*0.35});
|
|
bg.push({t:time+q*5+q/2,f:300,d:0.05,i:'woodwind',v:v*0.35});
|
|
}
|
|
|
|
// Bolero Theme A (C major): C-D-E-F-E-D-E-C | E-C-D-C-B3-C-D-E-F-E-D-C
|
|
var themeA=[[C4,q,0],[D4,q,1],[E4,q*0.5,2],[F4,q*0.5,3],[E4,q*0.5,2],[D4,q*0.5,1],
|
|
[E4,q,2],[C4,q*2,0],
|
|
[E4,q*0.5,2],[C4,q*0.5,0],[D4,q*0.5,1],[C4,q*0.5,0],[B3,q*0.5,3],
|
|
[C4,q*0.5,0],[D4,q,1],[E4,q*0.5,2],[F4,q*0.5,3],[E4,q*0.5,2],[D4,q*0.5,1],[C4,q*2,0]];
|
|
|
|
// Bolero Theme B (chromatic inflection): C-D-Eb-D-C-D-Eb-F-Eb-D-C-Bb3-C
|
|
var themeB=[[C4,q,0],[D4,q*0.5,1],[Eb4,q*0.5,2],[D4,q*0.5,1],[C4,q*0.5,0],
|
|
[D4,q*0.5,1],[Eb4,q*0.5,2],[F4,q,3],[Eb4,q*0.5,2],[D4,q*0.5,1],
|
|
[C4,q,0],[Bb3,q,3],[C4,q*2,0]];
|
|
|
|
// Continuous harmonic bed — very sparse at first, grows
|
|
var harm=[
|
|
[0,20,[C3,G3,C4],0.06],[20,40,[C3,G3,C4],0.1],[40,60,[C3,G3,C4],0.15],
|
|
[60,80,[C3,G3,C4],0.2],[80,95,[C3,G3,E4],0.25]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=q*4){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:q*3.8,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// 7 statements of the theme, each louder with different instruments
|
|
var statements=[
|
|
{inst:'woodwind',v:0.18,theme:'A'}, // flute solo
|
|
{inst:'woodwind',v:0.22,theme:'A'}, // clarinet
|
|
{inst:'woodwind',v:0.28,theme:'B'}, // bassoon
|
|
{inst:'strings',v:0.35,theme:'A'}, // strings pizz
|
|
{inst:'strings',v:0.42,theme:'B'}, // strings arco
|
|
{inst:'brass',v:0.5,theme:'A'}, // brass
|
|
{inst:'brass',v:0.6,theme:'A'} // full orchestra
|
|
];
|
|
|
|
statements.forEach(function(st,si){
|
|
var barLen=q*6;
|
|
// Snare drum rhythm underneath
|
|
snareBar(t,0.1+si*0.06);
|
|
snareBar(t+barLen,0.1+si*0.06);
|
|
|
|
var theme=st.theme==='A'?themeA:themeB;
|
|
theme.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:st.inst,v:st.v});
|
|
// Add harmony voices in later statements
|
|
if(si>=3)bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:st.v*0.4});
|
|
if(si>=4)bg.push({t:t,f:n[0]*1.5,d:n[1]*0.9,i:'strings',v:st.v*0.3});
|
|
if(si>=5){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'strings',v:st.v*0.5});
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:st.v*0.25});
|
|
}
|
|
if(si>=6&&n[2]===0)bg.push({t:t,f:C3,d:0.2,i:'timpani',v:st.v*0.5});
|
|
mel.push({t:t,l:n[2]});
|
|
t+=n[1];
|
|
});
|
|
// Kick hit at end of each statement
|
|
bg.push({t:t,f:C3,d:0.3,i:'timpani',v:0.15+si*0.06});
|
|
mel.push({t:t,l:4});
|
|
t+=q*2;
|
|
});
|
|
|
|
// === FINALE: Modulation up + massive crescendo ===
|
|
// Theme in Db (up a half step) — the famous key change
|
|
var finaleTheme=[[Db4,q*0.8,0],[Eb4,q*0.4,1],[F4,q*0.4,3],[Eb4,q*0.4,1],[Db4,q*0.4,0],
|
|
[Eb4,q*0.8,1],[Db4,q*1.5,0]];
|
|
for(var fc=0;fc<2;fc++){
|
|
var fv=0.6+fc*0.08;
|
|
snareBar(t,0.4+fc*0.05);
|
|
finaleTheme.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'brass',v:fv});
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'strings',v:fv*0.8});
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:fv*0.5});
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:fv*0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
bg.push({t:t,f:Db4/2,d:0.3,i:'timpani',v:fv*0.7});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// Final massive C chord
|
|
t+=q;
|
|
[C3,G3,C4,E4,G4,C5].forEach(function(f){
|
|
bg.push({t:t,f:f,d:3.5,i:'brass',v:0.7});bg.push({t:t,f:f,d:3.5,i:'strings',v:0.6});
|
|
bg.push({t:t,f:f,d:3.5,i:'choir',v:0.35});
|
|
});
|
|
bg.push({t:t,f:C3/2,d:3.0,i:'timpani',v:0.75});mel.push({t:t,l:4});
|
|
|
|
return {n:"Bolero",composer:"Ravel",bpm:72,duration:Math.ceil(t+4),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Barber of Seville Overture ──
|
|
// Rossini's famous overture in E major, ~75s
|
|
// Quiet opening → crescendo string runs → the "Rossini crescendo"
|
|
var barber=(function(){
|
|
var bg=[],mel=[],t=0,q=60/144;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,12,[E3,G3,B3],0.08],[12,28,[E3,G3,B3],0.14],[28,44,[E3,G3,B3],0.18],
|
|
[44,56,[A3,C4,E4],0.16],[56,72,[E3,G3,B3],0.22],[72,80,[E3,G3,B3],0.25]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:1.9,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-12s): Quiet pizzicato opening ===
|
|
// Tip-toeing melody, very soft
|
|
var pizz=[[E4,q,2],[E4,q*0.5,2],[G4,q*0.5,3],[B4,q,0],[G4,q*0.5,3],[E4,q*0.5,2],
|
|
[D4,q,1],[E4,q,2],[G4,q*0.5,3],[E4,q*0.5,2],[D4,q,1],[E4,q*2,2]];
|
|
for(var pr=0;pr<2;pr++){
|
|
var pv=0.2+pr*0.05;
|
|
pizz.forEach(function(n){
|
|
str(t,n[0],n[1]*0.4,pv); // short staccato for pizzicato feel
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (12-28s): The famous "Rossini crescendo" ===
|
|
// Repeated motif that gets louder and louder
|
|
var crescMotif=[[E4,q*0.5,2],[E4,q*0.5,2],[G4,q*0.5,3],[G4,q*0.5,3],
|
|
[B4,q*0.5,0],[B4,q*0.5,0],[E5,q*0.5,2],[D5,q*0.5,1],
|
|
[B4,q*0.5,0],[G4,q*0.5,3],[E4,q,2]];
|
|
for(var cr=0;cr<4;cr++){
|
|
var cv=0.22+cr*0.08;
|
|
var ci=cr<2?'strings':cr<3?'brass':'brass';
|
|
crescMotif.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.85,i:ci,v:cv});
|
|
if(cr>=1)str(t,n[0],n[1]*0.85,cv*0.6);
|
|
if(cr>=2)bg.push({t:t,f:n[0]/2,d:n[1]*0.85,i:'bass',v:cv*0.4});
|
|
if(cr>=3){brs(t,n[0],n[1]*0.85,cv*0.5);bg.push({t:t,f:n[0],d:n[1]*0.85,i:'choir',v:cv*0.2});}
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
// Punctuating chord
|
|
if(cr>=2){bg.push({t:t,f:E3,d:0.3,i:'timpani',v:cv*0.5});}
|
|
mel.push({t:t,l:4});t+=q*1.5;
|
|
}
|
|
|
|
// === SECTION 3 (28-44s): Running string passages ===
|
|
// Fast scalar runs — the hallmark of Rossini
|
|
var run1=[E4,G4,B4,E5,D5,B4,G4,E4,D4,E4,G4,B4,D5,E5,D5,B4];
|
|
var r1Ln=[2,3,0,2,1,0,3,2,1,2,3,0,1,2,1,0];
|
|
for(var rr=0;rr<3;rr++){
|
|
var rv=0.4+rr*0.06;
|
|
var rq=q*(0.5-rr*0.05); // speeds up each time
|
|
run1.forEach(function(f,i){
|
|
str(t,f,rq*0.85,rv);
|
|
if(rr>0)brs(t,f,rq*0.85,rv*0.4);
|
|
if(rr>1)bg.push({t:t,f:f/2,d:rq*0.85,i:'bass',v:rv*0.3});
|
|
mel.push({t:t,l:r1Ln[i]});t+=rq;
|
|
});
|
|
// Big chord punctuation
|
|
[E3,B3,E4,G4].forEach(function(f){brs(t,f,0.4,rv);str(t,f,0.4,rv*0.8);});
|
|
bg.push({t:t,f:E3/2,d:0.3,i:'timpani',v:rv*0.6});mel.push({t:t,l:4});t+=q*2;
|
|
}
|
|
|
|
// === SECTION 4 (44-56s): Lyrical contrasting theme ===
|
|
var lyric=[[B4,q*1.5,0],[A4,q*0.5,1],[G4,q,3],[E4,q,2],[G4,q*1.5,3],[A4,q*0.5,1],
|
|
[B4,q*2,0],[E5,q*1.5,2],[D5,q*0.5,1],[B4,q,0],[G4,q,3],[E4,q*2,2]];
|
|
for(var ll=0;ll<2;ll++){
|
|
var lv=0.4+ll*0.1;
|
|
lyric.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,lv);brs(t,n[0],n[1]*0.9,lv*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:lv*0.35});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (56-72s): Final Rossini crescendo + runs ===
|
|
for(var fc=0;fc<3;fc++){
|
|
var fv=0.5+fc*0.06;
|
|
var fq=q*(0.45-fc*0.04);
|
|
run1.forEach(function(f,i){
|
|
str(t,f,fq*0.85,fv);brs(t,f,fq*0.85,fv*0.6);
|
|
bg.push({t:t,f:f/2,d:fq*0.85,i:'bass',v:fv*0.4});
|
|
if(fc>0&&i%4===0)bg.push({t:t,f:E3,d:0.15,i:'timpani',v:fv*0.5});
|
|
mel.push({t:t,l:r1Ln[i]});t+=fq;
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// === FINALE: Chords ===
|
|
for(var c=0;c<6;c++){
|
|
[E3,B3,E4,G4,B4].forEach(function(f){brs(t,f,0.35,0.65);str(t,f,0.35,0.55);});
|
|
bg.push({t:t,f:E3/2,d:0.35,i:'timpani',v:0.6});mel.push({t:t,l:4});t+=0.45;
|
|
}
|
|
[E3,B3,E4,G4,B4,E5].forEach(function(f){brs(t,f,3.0,0.7);str(t,f,3.0,0.6);});
|
|
bg.push({t:t,f:E3/2,d:2.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
|
|
return {n:"Barber of Seville Overture",composer:"Rossini",bpm:144,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Sabre Dance ──
|
|
// Khachaturian's frenetic Sabre Dance in D minor, ~75s
|
|
// Famous for its relentless, driving ostinato
|
|
var sabre=(function(){
|
|
var bg=[],mel=[],t=0,q=60/168;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});}
|
|
function tutti(t,f,d,v){str(t,f,d,v);brs(t,f,d,(v||0.4)*0.6);bg.push({t:t,f:f/2,d:d,i:'bass',v:(v||0.4)*0.4});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,15,[D3,A3,D4],0.1],[15,30,[D3,A3,D4],0.15],[30,50,[D3,F3,A3],0.18],
|
|
[50,65,[D3,A3,D4],0.2],[65,80,[D3,A3,D4],0.25]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=1.5){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:1.4,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-15s): The famous ostinato — D-D-D-Eb-D repeated ===
|
|
// Frantic repeated notes that define the piece
|
|
var ostinato=[[D4,q*0.5,0],[D4,q*0.5,0],[D4,q*0.5,0],[Eb4,q*0.5,1],[D4,q*0.5,0],
|
|
[D4,q*0.5,0],[D4,q*0.5,0],[Eb4,q*0.5,1]];
|
|
for(var os=0;os<6;os++){
|
|
var ov=0.3+os*0.04;
|
|
ostinato.forEach(function(n){
|
|
str(t,n[0],n[1]*0.7,ov);
|
|
if(os>=3)brs(t,n[0],n[1]*0.7,ov*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
// Ascending burst: D-E-F-G-A
|
|
[D4,E4,F4,G4,A4].forEach(function(f,i){
|
|
str(t,f,q*0.4,ov);mel.push({t:t,l:[0,2,3,0,1][i]});t+=q*0.5;
|
|
});
|
|
bg.push({t:t,f:D3,d:0.2,i:'timpani',v:ov*0.6});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (15-30s): Contrasting lyrical middle section ===
|
|
// Slower, sweeping melody in strings over sustained chords
|
|
var lyric=[[A4,q*2,1],[G4,q,0],[F4,q,3],[E4,q*2,2],[D4,q,0],
|
|
[F4,q*2,3],[E4,q,2],[D4,q,0],[A3,q*2,1],[D4,q*3,0]];
|
|
for(var lr=0;lr<3;lr++){
|
|
var lv=0.35+lr*0.08;
|
|
lyric.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,lv);
|
|
if(lr>0)brs(t,n[0],n[1]*0.9,lv*0.5);
|
|
if(lr>1)bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:lv*0.4});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
if(lr>0){bg.push({t:t,f:D3,d:0.3,i:'timpani',v:lv*0.5});mel.push({t:t,l:4});t+=q;}
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 3 (30-50s): Ostinato returns — faster, louder ===
|
|
var fastOst=[[D4,q*0.4,0],[D4,q*0.4,0],[D4,q*0.4,0],[Eb4,q*0.4,1],[D4,q*0.4,0],
|
|
[D4,q*0.4,0],[Eb4,q*0.4,1],[F4,q*0.4,3]];
|
|
for(var fo=0;fo<6;fo++){
|
|
var fv=0.45+fo*0.04;
|
|
fastOst.forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.7,fv);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
// Ascending chromatic burst
|
|
[D4,Eb4,E4,F4,G4,Ab4,A4,Bb4].forEach(function(f,i){
|
|
str(t,f,q*0.35,fv);brs(t,f,q*0.35,fv*0.5);
|
|
mel.push({t:t,l:[0,1,2,3,0,1,1,2][i]});t+=q*0.4;
|
|
});
|
|
// Slam
|
|
[D4,A4,D5].forEach(function(f){brs(t,f,0.3,fv);});
|
|
bg.push({t:t,f:D3,d:0.3,i:'timpani',v:fv*0.7});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (50-65s): Wild descending runs + ostinato ===
|
|
var desc=[A4,G4,F4,E4,D4,E4,F4,G4,A4,Bb4,A4,G4,F4,E4,D4,A3];
|
|
var dLn=[1,0,3,2,0,2,3,0,1,2,1,0,3,2,0,4];
|
|
for(var dr=0;dr<3;dr++){
|
|
var dv=0.55+dr*0.05;
|
|
var dq=q*(0.45-dr*0.04);
|
|
desc.forEach(function(f,i){
|
|
tutti(t,f,dq*0.8,dv);
|
|
if(dr>0)bg.push({t:t,f:f,d:dq*0.8,i:'brass',v:dv*0.5});
|
|
if(dr>1&&i%4===0)bg.push({t:t,f:D3,d:0.15,i:'timpani',v:dv*0.6});
|
|
mel.push({t:t,l:dLn[i]});t+=dq;
|
|
});
|
|
t+=q*0.5;
|
|
// Ostinato fragment
|
|
fastOst.forEach(function(n){
|
|
tutti(t,n[0],n[1]*0.65,dv);mel.push({t:t,l:n[2]});t+=n[1]*0.85;
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// === FINALE: Hammered D minor chords ===
|
|
for(var c=0;c<8;c++){
|
|
[D3,A3,D4,F4,A4].forEach(function(f){brs(t,f,0.3,0.65);str(t,f,0.3,0.55);});
|
|
bg.push({t:t,f:D3/2,d:0.3,i:'timpani',v:0.65});mel.push({t:t,l:4});t+=0.38;
|
|
}
|
|
[D3,A3,D4,F4,A4,D5].forEach(function(f){brs(t,f,2.5,0.7);str(t,f,2.5,0.6);});
|
|
bg.push({t:t,f:D3/2,d:2.0,i:'timpani',v:0.75});mel.push({t:t,l:4});
|
|
|
|
return {n:"Sabre Dance",composer:"Khachaturian",bpm:168,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Sorcerer's Apprentice ──
|
|
// Dukas's magical march in F minor, ~80s
|
|
// Famous bassoon theme → march builds → magical chaos
|
|
var sorcerer=(function(){
|
|
var bg=[],mel=[],t=0,q=60/132;
|
|
function ww(t,f,d,v){bg.push({t:t,f:f,d:d,i:'woodwind',v:v||0.3});}
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});}
|
|
|
|
// Harmonic bed — mysterious
|
|
var harm=[
|
|
[0,16,[F3,Ab3,C4],0.06],[16,32,[F3,Ab3,C4],0.1],[32,48,[F3,Ab3,C4],0.15],
|
|
[48,64,[Bb3,Db4,F4],0.18],[64,82,[F3,Ab3,C4],0.22]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.5){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:2.3,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-16s): The famous bassoon theme ===
|
|
// Creeping march: F-Ab-C-Ab-F then Ab-C-Eb-C-Ab
|
|
var bsnTheme=[[F3,q,0],[Ab3,q*0.5,1],[C4,q*0.5,2],[Ab3,q*0.5,1],[F3,q*0.5,0],
|
|
[Ab3,q,1],[C4,q*0.5,2],[Eb4,q*0.5,3],[C4,q*0.5,2],[Ab3,q*0.5,1],
|
|
[F3,q*1.5,0],[C4,q*0.5,2],[Ab3,q,1],[F3,q*2,0]];
|
|
for(var br=0;br<3;br++){
|
|
var bv=0.2+br*0.06;
|
|
bsnTheme.forEach(function(n){
|
|
ww(t,n[0],n[1]*0.85,bv);
|
|
if(br>0)str(t,n[0]*2,n[1]*0.85,bv*0.3); // octave higher strings echo
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (16-32s): March begins — walking brooms ===
|
|
// Steady march rhythm with the theme on top
|
|
var march=[[C4,q,2],[C4,q*0.5,2],[Ab3,q*0.5,1],[F3,q,0],[Ab3,q*0.5,1],[C4,q*0.5,2],
|
|
[Eb4,q,3],[Db4,q*0.5,0],[C4,q*0.5,2],[Ab3,q,1],[F3,q*1.5,0]];
|
|
for(var mr=0;mr<4;mr++){
|
|
var mv=0.3+mr*0.06;
|
|
var mInst=mr<2?'woodwind':'strings';
|
|
// March bass: oom-pah
|
|
for(var mb=0;mb<6;mb++){
|
|
bg.push({t:t+mb*q*2,f:F3,d:q*0.8,i:'bass',v:mv*0.5});
|
|
bg.push({t:t+mb*q*2+q,f:C3,d:q*0.8,i:'bass',v:mv*0.4});
|
|
if(mr>=2)bg.push({t:t+mb*q*2,f:F3,d:0.15,i:'timpani',v:mv*0.4});
|
|
}
|
|
march.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.85,i:mInst,v:mv});
|
|
if(mr>=2)brs(t,n[0],n[1]*0.85,mv*0.4);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 3 (32-48s): Chaos builds — theme fragments overlap ===
|
|
// Faster fragments of the theme, like multiple brooms marching
|
|
var frag1=[[F3,q*0.5,0],[Ab3,q*0.5,1],[C4,q*0.5,2],[Eb4,q*0.5,3],[C4,q*0.5,2],[Ab3,q*0.5,1]];
|
|
var frag2=[[C4,q*0.5,2],[Db4,q*0.5,0],[C4,q*0.5,2],[Ab3,q*0.5,1],[F3,q*0.5,0],[Ab3,q*0.5,1]];
|
|
for(var ch=0;ch<4;ch++){
|
|
var cv=0.4+ch*0.05;
|
|
(ch%2===0?frag1:frag2).forEach(function(n){
|
|
str(t,n[0],n[1]*0.8,cv);brs(t,n[0],n[1]*0.8,cv*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.8,i:'bass',v:cv*0.4});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
// Ascending rush
|
|
[F3,Ab3,C4,Eb4,F4,Ab4].forEach(function(f,i){
|
|
str(t,f,q*0.35,cv);mel.push({t:t,l:[0,1,2,3,0,1][i]});t+=q*0.4;
|
|
});
|
|
bg.push({t:t,f:F3,d:0.3,i:'timpani',v:cv*0.6});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (48-64s): Full orchestra — magical pandemonium ===
|
|
// Full theme at double speed with brass
|
|
var fullTheme=[[F4,q*0.4,0],[Ab4,q*0.4,1],[C5,q*0.4,2],[Ab4,q*0.4,1],[F4,q*0.4,0],
|
|
[Ab4,q*0.4,1],[C5,q*0.4,2],[Eb5,q*0.4,3],[C5,q*0.4,2],[Ab4,q*0.4,1],
|
|
[F4,q*0.8,0],[C5,q*0.4,2],[Ab4,q*0.4,1],[F4,q*0.8,0]];
|
|
for(var ft=0;ft<4;ft++){
|
|
var fv=0.5+ft*0.05;
|
|
fullTheme.forEach(function(n){
|
|
str(t,n[0],n[1]*0.8,fv);brs(t,n[0],n[1]*0.8,fv*0.6);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.8,i:'bass',v:fv*0.4});
|
|
if(ft>1)bg.push({t:t,f:n[0],d:n[1]*0.8,i:'choir',v:fv*0.2});
|
|
if(ft>2&&n[2]===0)bg.push({t:t,f:F3,d:0.15,i:'timpani',v:fv*0.5});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
bg.push({t:t,f:F3,d:0.3,i:'timpani',v:fv*0.6});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (64-78s): Wild ending ===
|
|
// Descending cascades
|
|
[C5,Bb4,Ab4,G4,F4,Eb4,Db4,C4,Bb3,Ab3,G3,F3].forEach(function(f,i){
|
|
str(t,f,q*0.3,0.55);brs(t,f,q*0.3,0.45);
|
|
mel.push({t:t,l:[2,3,1,0,3,2,0,1,3,1,0,4][i]});t+=q*0.35;
|
|
});
|
|
t+=q;
|
|
|
|
// Final chords
|
|
for(var c=0;c<6;c++){
|
|
[F3,C4,F4,Ab4].forEach(function(f){brs(t,f,0.35,0.65);str(t,f,0.35,0.55);});
|
|
bg.push({t:t,f:F3/2,d:0.35,i:'timpani',v:0.6});mel.push({t:t,l:4});t+=0.45;
|
|
}
|
|
[F3,C4,F4,Ab4,C5].forEach(function(f){brs(t,f,3.0,0.65);str(t,f,3.0,0.55);});
|
|
bg.push({t:t,f:F3/2,d:2.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
|
|
return {n:"Sorcerer's Apprentice",composer:"Dukas",bpm:132,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Morning Mood ──
|
|
// Grieg's Peer Gynt — gentle sunrise in E major, ~80s
|
|
// Famous flute melody: G#-A-B-C#-B-A, then G#-A-B-C#-E-D#-C#-B-A-G#
|
|
var morning=(function(){
|
|
var bg=[],mel=[],t=0,q=60/60; // slow, pastoral tempo
|
|
function ww(t,f,d,v){bg.push({t:t,f:f,d:d,i:'woodwind',v:v||0.25});}
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.3});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.35});}
|
|
|
|
// Gentle harmonic bed — E major sustained strings
|
|
var harm=[
|
|
[0,18,[E3,Ab3,B3],0.06],[18,36,[E3,Ab3,B3],0.1],[36,54,[E3,Ab3,B3],0.15],
|
|
[54,70,[A3,Db4,E4],0.14],[70,85,[E3,Ab3,B3],0.18]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=3.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:2.8,i:'strings',v:h[3]});});
|
|
bg.push({t:ht,f:h[2][0]/2,d:2.8,i:'bass',v:h[3]*0.4});
|
|
}
|
|
});
|
|
|
|
// === The actual Grieg melody in E major ===
|
|
// G#(Ab4)-A-B-C#(Db5)-B-A | first phrase (ascending then descending)
|
|
var fluteMel1=[[Ab4,q*0.75,1],[A4,q*0.75,0],[B4,q*0.75,3],[Db5,q*0.75,2],
|
|
[B4,q*0.75,3],[A4,q*0.75,0]];
|
|
// G#-A-B-C#-E-D#(Eb5)-C#-B-A-G# | second phrase (up higher then back)
|
|
var fluteMel2=[[Ab4,q*0.75,1],[A4,q*0.75,0],[B4,q*0.75,3],[Db5,q*0.75,2],
|
|
[E5,q*0.75,0],[Eb5,q*0.75,1],[Db5,q*0.75,2],[B4,q*0.75,3],
|
|
[A4,q*0.75,0],[Ab4,q*1.5,1]];
|
|
|
|
// First statement — solo flute, very quiet
|
|
fluteMel1.forEach(function(n){ww(t,n[0],n[1]*0.9,0.2);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q*0.5;
|
|
fluteMel2.forEach(function(n){ww(t,n[0],n[1]*0.9,0.22);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q;
|
|
|
|
// Second statement — flute + light strings
|
|
fluteMel1.forEach(function(n){ww(t,n[0],n[1]*0.9,0.25);str(t,n[0],n[1]*0.9,0.1);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q*0.5;
|
|
fluteMel2.forEach(function(n){ww(t,n[0],n[1]*0.9,0.28);str(t,n[0],n[1]*0.9,0.12);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q;
|
|
|
|
// === SECTION 2 (18-36s): Melody grows — oboe joins, then strings ===
|
|
// Third statement — woodwind + strings
|
|
fluteMel1.forEach(function(n){
|
|
ww(t,n[0],n[1]*0.9,0.3);str(t,n[0],n[1]*0.9,0.2);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
fluteMel2.forEach(function(n){
|
|
ww(t,n[0],n[1]*0.9,0.32);str(t,n[0],n[1]*0.9,0.22);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.12});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// Fourth statement — strings lead, richer
|
|
fluteMel1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.35);ww(t,n[0],n[1]*0.9,0.2);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
fluteMel2.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.38);ww(t,n[0],n[1]*0.9,0.22);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.15});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// === SECTION 3 (36-54s): Full sunrise — brass enters, melody soars ===
|
|
// Expanded melody reaching up to high E
|
|
var soarMel=[[Ab4,q*0.65,1],[A4,q*0.65,0],[B4,q*0.65,3],[Db5,q*0.65,2],
|
|
[E5,q*0.65,0],[Db5,q*0.65,2],[B4,q*0.65,3],[A4,q*0.65,0],
|
|
[Ab4,q*0.65,1],[A4,q*0.65,0],[B4,q*0.65,3],[Db5,q*0.65,2],
|
|
[E5,q*0.65,0],[Eb5,q*0.65,1],[Db5,q*1.3,2]];
|
|
for(var sr=0;sr<2;sr++){
|
|
var sv=0.4+sr*0.08;
|
|
soarMel.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,sv);brs(t,n[0],n[1]*0.9,sv*0.4);
|
|
ww(t,n[0],n[1]*0.9,sv*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:sv*0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (54-70s): Warm development — horns + full strings ===
|
|
// Broader phrasing with E major harmonies
|
|
var warmMel=[[Db5,q*1.3,2],[B4,q*0.65,3],[A4,q*0.65,0],[Ab4,q*1.3,1],
|
|
[A4,q*0.65,0],[B4,q*0.65,3],[Db5,q*1.3,2],[E5,q*1.3,0],
|
|
[Db5,q*0.65,2],[B4,q*0.65,3],[A4,q*0.65,0],[Ab4,q*2,1]];
|
|
for(var wr=0;wr<2;wr++){
|
|
var wv=0.48+wr*0.06;
|
|
warmMel.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,wv);brs(t,n[0],n[1]*0.9,wv*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:wv*0.35});
|
|
if(wr>0)bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:wv*0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (70-82s): Final sunrise — melody one last time, full and warm ===
|
|
fluteMel1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.5);brs(t,n[0],n[1]*0.9,0.35);ww(t,n[0],n[1]*0.9,0.3);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.25});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
fluteMel2.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.5);brs(t,n[0],n[1]*0.9,0.35);ww(t,n[0],n[1]*0.9,0.3);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// Gentle ending — sustained E major chord fading
|
|
[E3,Ab3,B3,E4,Ab4,B4,E5].forEach(function(f){
|
|
str(t,f,4.0,0.4);ww(t,f,4.0,0.25);
|
|
});
|
|
bg.push({t:t,f:E3/2,d:3.0,i:'bass',v:0.2});mel.push({t:t,l:4});
|
|
|
|
return {n:"Morning Mood",composer:"Grieg",bpm:60,duration:Math.ceil(t+4),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Mars, Bringer of War ──
|
|
var mars=(function(){
|
|
var bg=[],mel=[],t=0,q=0.38;
|
|
// 5/4 ostinato: 20 bars of relentless 5-beat march
|
|
for(var bar=0;bar<20;bar++){
|
|
var v=0.2+bar*0.022;
|
|
var inst=bar<7?'strings':bar<14?'brass':'brass';
|
|
for(var beat=0;beat<5;beat++){
|
|
var bt=t+beat*q;
|
|
bg.push({t:bt,f:G3,d:q*0.7,i:'timpani',v:v*0.8});
|
|
bg.push({t:bt,f:G3,d:q*0.7,i:'bass',v:v*0.6});
|
|
if(bar>3)bg.push({t:bt,f:beat<3?G4:D4,d:q*0.7,i:inst,v:v*0.4});
|
|
mel.push({t:bt,l:beat<3?[0,1,2][beat]:beat===3?3:4});
|
|
}
|
|
if(bar>=4&&bar%2===0){
|
|
var mf=bar%4===0?[G4,Bb4,D5,C5,Bb4]:[D5,C5,Bb4,A4,G4];
|
|
mf.forEach(function(f,i){bg.push({t:t+i*q,f:f,d:q*1.5,i:inst,v:v*0.6});});
|
|
}
|
|
t+=5*q;
|
|
}
|
|
[G3,Bb3,D4,G4].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'brass',v:0.65});});
|
|
bg.push({t:t,f:G3/2,d:2.0,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Mars, Bringer of War",composer:"Holst",bpm:126,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Toccata & Fugue in D Minor ──
|
|
var toccata=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
function org(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});bg.push({t:t,f:f,d:d,i:'woodwind',v:(v||0.4)*0.5});}
|
|
// Opening mordent: A-G-A (whole step, per standard editions)
|
|
org(0,A4,0.12,0.55);org(0.15,G4,0.12,0.55);org(0.3,A4,0.9,0.6);
|
|
mel.push({t:0,l:0},{t:0.15,l:3},{t:0.3,l:0});
|
|
// Descending: G-F-E-D
|
|
[G4,F4,E4,D4].forEach(function(f,i){org(1.3+i*0.4,f,0.35,0.55);mel.push({t:1.3+i*0.4,l:[3,2,1,0][i]});});
|
|
// Lower octave statement
|
|
org(3.2,A3,0.12,0.55);org(3.35,G3,0.12,0.55);org(3.5,A3,0.9,0.6);
|
|
mel.push({t:3.2,l:0},{t:3.35,l:3},{t:3.5,l:0});
|
|
[G3,F3,E3,D3].forEach(function(f,i){org(4.5+i*0.4,f,0.35,0.55);bg.push({t:4.5+i*0.4,f:f,d:0.35,i:'bass',v:0.45});mel.push({t:4.5+i*0.4,l:[3,2,1,4][i]});});
|
|
// D minor chord
|
|
t=6.2;
|
|
[D3,A3,D4,F4,A4,D5].forEach(function(f){org(t,f,2.0,0.65);});
|
|
bg.push({t:t,f:D3/2,d:1.5,i:'timpani',v:0.6});mel.push({t:t,l:4});
|
|
// Ascending scale
|
|
t=8.5;
|
|
[D4,E4,F4,G4,A4,Bb4,C5,D5].forEach(function(f,i){org(t,f,0.2,0.5);mel.push({t:t,l:[0,1,2,3][i%4]});t+=0.22;});
|
|
// Descending chromatic
|
|
[D5,C5,Bb4,A4,Ab4,G4,F4,E4,Eb4,D4].forEach(function(f,i){org(t,f,0.18,0.5);mel.push({t:t,l:[1,2,3,0][i%4]});t+=0.2;});
|
|
// Big chord
|
|
[D3,A3,D4,F4,A4].forEach(function(f){org(t,f,1.5,0.6);});
|
|
bg.push({t:t,f:D3/2,d:1.2,i:'timpani',v:0.55});mel.push({t:t,l:4});t+=1.8;
|
|
// Fugue: 6 entries accelerating
|
|
var subj=[D4,E4,F4,G4,A4,Bb4,A4,G4,F4,E4,D4,A3,D4];
|
|
var sLn=[0,1,2,3,0,2,0,3,2,1,0,4,0];
|
|
var speeds=[0.30,0.26,0.23,0.20,0.17,0.14];
|
|
var vols=[0.3,0.35,0.4,0.48,0.55,0.62];
|
|
speeds.forEach(function(spd,si){
|
|
subj.forEach(function(f,i){
|
|
org(t,f,spd*0.85,vols[si]);
|
|
if(si>1)bg.push({t:t,f:f/2,d:spd*0.85,i:'bass',v:vols[si]*0.5});
|
|
if(si>3)bg.push({t:t,f:f,d:spd*0.85,i:'strings',v:vols[si]*0.4});
|
|
if(si>3&&i%3===0)bg.push({t:t,f:D3,d:0.3,i:'timpani',v:vols[si]*0.6});
|
|
mel.push({t:t,l:sLn[i]});t+=spd;
|
|
});
|
|
t+=0.4;
|
|
});
|
|
// Final cascade
|
|
[D5,C5,Bb4,A4,G4,F4,E4,D4,C4,Bb3,A3,G3].forEach(function(f,i){
|
|
org(t,f,0.13,0.6);bg.push({t:t,f:f,d:0.13,i:'strings',v:0.4});mel.push({t:t,l:[0,2,3,1][i%4]});t+=0.14;
|
|
});
|
|
t+=0.2;
|
|
[D3,A3,D4,F4,A4,D5].forEach(function(f){org(t,f,3.0,0.7);});
|
|
bg.push({t:t,f:D3/2,d:2.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Toccata & Fugue in D Minor",composer:"Bach",bpm:100,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Also Sprach Zarathustra ──
|
|
var zarath=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
// Timpani rumble + pulse
|
|
bg.push({t:0,f:C3,d:4.0,i:'timpani',v:0.1});
|
|
bg.push({t:0,f:C3,d:8.0,i:'bass',v:0.08});
|
|
[0.5,1.5,3.0,4.5].forEach(function(pt){bg.push({t:pt,f:C3,d:0.4,i:'timpani',v:0.15});mel.push({t:pt,l:4});});
|
|
// C note emerges
|
|
t=2.0;bg.push({t:t,f:C4,d:3.0,i:'brass',v:0.15});mel.push({t:t,l:0});
|
|
// G note
|
|
t=5.0;bg.push({t:t,f:G4,d:3.0,i:'brass',v:0.25});bg.push({t:t,f:C4,d:3.0,i:'strings',v:0.15});mel.push({t:t,l:1});
|
|
// High C — the sunrise!
|
|
t=8.0;
|
|
bg.push({t:t,f:C5,d:4.0,i:'brass',v:0.5});bg.push({t:t,f:G4,d:4.0,i:'brass',v:0.4});
|
|
bg.push({t:t,f:C4,d:4.0,i:'strings',v:0.3});bg.push({t:t,f:C3,d:4.0,i:'timpani',v:0.5});
|
|
mel.push({t:t,l:3});
|
|
// Big C major chord
|
|
t=12.0;
|
|
[C3,G3,C4,E4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:3.0,i:'brass',v:0.6});bg.push({t:t,f:f,d:3.0,i:'strings',v:0.45});});
|
|
bg.push({t:t,f:C3/2,d:2.5,i:'timpani',v:0.65});mel.push({t:t,l:4});
|
|
// Second statement: faster and louder
|
|
t=16.0;bg.push({t:t,f:C4,d:1.5,i:'brass',v:0.35});mel.push({t:t,l:0});
|
|
t=17.5;bg.push({t:t,f:G4,d:1.5,i:'brass',v:0.45});mel.push({t:t,l:1});
|
|
t=19.0;bg.push({t:t,f:C5,d:2.0,i:'brass',v:0.55});bg.push({t:t,f:G4,d:2.0,i:'brass',v:0.45});
|
|
bg.push({t:t,f:C3,d:2.0,i:'timpani',v:0.55});mel.push({t:t,l:3});
|
|
t=21.0;
|
|
[C3,G3,C4,E4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:3.0,i:'brass',v:0.65});});
|
|
bg.push({t:t,f:C3/2,d:2.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
// Development: ascending scale
|
|
t=25.0;
|
|
[C4,D4,E4,F4,G4,A4,B4,C5,D5,E5].forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:0.5,i:'strings',v:0.4+i*0.02});bg.push({t:t,f:f,d:0.5,i:'brass',v:0.3+i*0.02});
|
|
mel.push({t:t,l:[0,1,2,3,0][i%5]});t+=0.55;
|
|
});
|
|
// Descending
|
|
[E5,D5,C5,B4,A4,G4,F4,E4,D4,C4].forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:0.5,i:'strings',v:0.5});bg.push({t:t,f:f,d:0.5,i:'brass',v:0.4});
|
|
mel.push({t:t,l:[3,2,1,0,4][i%5]});t+=0.5;
|
|
});
|
|
// Third sunrise: biggest
|
|
t+=0.5;bg.push({t:t,f:C3,d:2.0,i:'timpani',v:0.4});
|
|
bg.push({t:t,f:C4,d:1.0,i:'brass',v:0.5});mel.push({t:t,l:0});t+=1.0;
|
|
bg.push({t:t,f:G4,d:1.0,i:'brass',v:0.55});mel.push({t:t,l:1});t+=1.0;
|
|
bg.push({t:t,f:C5,d:1.5,i:'brass',v:0.6});mel.push({t:t,l:3});t+=1.5;
|
|
// Final massive chord
|
|
[C3,G3,C4,E4,G4,C5,E5,G5].forEach(function(f){bg.push({t:t,f:f,d:4.0,i:'brass',v:0.7});bg.push({t:t,f:f,d:4.0,i:'choir',v:0.4});});
|
|
bg.push({t:t,f:C3/2,d:3.0,i:'timpani',v:0.75});mel.push({t:t,l:4});
|
|
return {n:"Also Sprach Zarathustra",composer:"R. Strauss",bpm:60,duration:Math.ceil(t+4.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── The Blue Danube ──
|
|
// J. Strauss II waltz in D major (arr. C major), ~75s
|
|
// The most famous waltz: ascending thirds C-E-G, G-B-D with oom-pah-pah bass
|
|
var danube=(function(){
|
|
var bg=[],mel=[],t=0,q=60/180; // waltz tempo, q = one beat of 3/4
|
|
|
|
// Waltz oom-pah-pah accompaniment helper
|
|
function waltz(time,root,third,v){
|
|
bg.push({t:time,f:root,d:q*0.8,i:'bass',v:v*0.5}); // oom
|
|
bg.push({t:time+q,f:third,d:q*0.6,i:'strings',v:v*0.25}); // pah
|
|
bg.push({t:time+q*2,f:third,d:q*0.6,i:'strings',v:v*0.2}); // pah
|
|
}
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.35});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,12,[C3,E3,G3],0.06],[12,30,[C3,E3,G3],0.1],[30,50,[C3,E3,G3],0.14],
|
|
[50,65,[G3,B3,D4],0.12],[65,80,[C3,E3,G3],0.18]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=3.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:2.8,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-12s): Tremolo intro + rising anticipation ===
|
|
// Soft tremolo strings, then the famous ascending pickup
|
|
for(var tr=0;tr<8;tr++){
|
|
bg.push({t:tr*q*3,f:C4,d:q*2.5,i:'strings',v:0.12+tr*0.01});
|
|
bg.push({t:tr*q*3,f:E4,d:q*2.5,i:'strings',v:0.1+tr*0.01});
|
|
}
|
|
t=6;
|
|
// Pickup notes before the waltz: ascending B-C
|
|
str(t,B3,q*0.8,0.25);mel.push({t:t,l:3});t+=q;
|
|
|
|
// === SECTION 2 (7-30s): First waltz — the famous melody ===
|
|
// Real melody (transposed to C): C-C-(rest) | C-E-G | G-G-(rest) | G-B-D5
|
|
// Pattern: note on beat 1, sustained or leap on beat 1 of next bar
|
|
var waltz1=[
|
|
[C4,q*3,0],[C4,q*3,0],[C4,q,0],[E4,q,2],[G4,q,3],
|
|
[G4,q*3,3],[G4,q*3,3],[G4,q,3],[B4,q,0],[D5,q,1],
|
|
[D5,q*3,1],[C5,q*3,2],[C5,q,2],[E4,q,2],[G4,q,3],
|
|
[G4,q*2,3],[E4,q,2],[C4,q*3,0]
|
|
];
|
|
for(var w1=0;w1<2;w1++){
|
|
var wv=0.3+w1*0.08;
|
|
var bi=0;
|
|
waltz1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,wv);
|
|
if(w1>0)brs(t,n[0],n[1]*0.9,wv*0.4);
|
|
// Add waltz bass on beat 1 of each 3-beat group
|
|
if(n[1]>=q*2)waltz(t,C3,E3,wv);
|
|
mel.push({t:t,l:n[2]});t+=n[1];bi++;
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 3 (30-50s): Second waltz — contrasting theme ===
|
|
// More lyrical flowing melody
|
|
var waltz2=[
|
|
[E4,q*2,2],[D4,q,1],[C4,q*3,0],[E4,q*2,2],[G4,q,3],
|
|
[G4,q*2,3],[F4,q,1],[E4,q*3,2],[D4,q*2,1],[E4,q,2],
|
|
[F4,q*2,1],[E4,q,2],[D4,q*3,1],[C4,q*3,0]
|
|
];
|
|
for(var w2=0;w2<2;w2++){
|
|
var wv2=0.38+w2*0.08;
|
|
waltz2.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,wv2);
|
|
if(w2>0)brs(t,n[0],n[1]*0.9,wv2*0.4);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:wv2*0.3});
|
|
if(n[1]>=q*2)waltz(t,C3,E3,wv2);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (50-65s): Waltz 1 returns — full orchestra ===
|
|
waltz1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.52);brs(t,n[0],n[1]*0.9,0.4);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.3});
|
|
if(n[1]>=q*2)waltz(t,C3,E3,0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
// Faster, more energetic repeat
|
|
waltz1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,0.55);brs(t,n[0],n[1]*0.85,0.45);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.85,i:'choir',v:0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1]*0.9;
|
|
});
|
|
|
|
// === FINALE: Big waltz chords ===
|
|
t+=q;
|
|
for(var c=0;c<6;c++){
|
|
[C4,E4,G4,C5].forEach(function(f){brs(t,f,q*2.5,0.6);str(t,f,q*2.5,0.5);});
|
|
bg.push({t:t,f:C3,d:q*2,i:'timpani',v:0.5});
|
|
mel.push({t:t,l:4});t+=q*3;
|
|
}
|
|
[C4,E4,G4,C5].forEach(function(f){brs(t,f,2.5,0.65);str(t,f,2.5,0.55);});
|
|
bg.push({t:t,f:C3,d:2.0,i:'timpani',v:0.65});mel.push({t:t,l:4});
|
|
|
|
return {n:"The Blue Danube",composer:"J. Strauss II",bpm:180,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Night on Bald Mountain ──
|
|
var bald=(function(){
|
|
var bg=[],mel=[],t=0,q=0.35;
|
|
function dark(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});bg.push({t:t,f:f,d:d,i:'strings',v:(v||0.4)*0.8});bg.push({t:t,f:f/2,d:d,i:'bass',v:(v||0.4)*0.5});}
|
|
// Ominous opening
|
|
bg.push({t:0,f:D3,d:3.0,i:'timpani',v:0.2});bg.push({t:0,f:D3,d:3.0,i:'bass',v:0.15});
|
|
t=1.0;
|
|
// Dark ascending chromatic
|
|
[D4,Eb4,E4,F4,G4,Ab4,A4,Bb4,B4,C5,D5].forEach(function(f,i){
|
|
dark(t,f,q*0.8,0.25+i*0.02);mel.push({t:t,l:[0,1,2,3,0,1,2,3,0,1,4][i]});t+=q;
|
|
});
|
|
t+=0.5;
|
|
// Dark sustained chords
|
|
[[D4,F4],[A3,E4],[Bb3,D4],[A3,C4],[D4,F4],[A4,D5]].forEach(function(ch){
|
|
ch.forEach(function(f){dark(t,f,1.0,0.4);});mel.push({t:t,l:4});t+=1.1;
|
|
});
|
|
// Witches' dance: 7 passes accelerating
|
|
var witch=[D4,F4,A4,D5,A4,F4,D4,E4,F4,G4,A4,Bb4,A4,G4,F4,E4,D4,A3,D4,F4];
|
|
var wLn=[0,2,0,1,0,2,0,1,2,3,0,2,0,3,2,1,0,4,0,2];
|
|
[0.26,0.24,0.22,0.20,0.18,0.16,0.14].forEach(function(spd,si){
|
|
var wv=0.3+si*0.05;
|
|
witch.forEach(function(f,i){
|
|
dark(t,f,spd*0.85,wv);
|
|
if(si>3&&i%4===0)bg.push({t:t,f:D3,d:0.3,i:'timpani',v:wv*0.7});
|
|
mel.push({t:t,l:wLn[i]});t+=spd;
|
|
});
|
|
t+=0.3;
|
|
});
|
|
// Massive ending
|
|
for(var c=0;c<4;c++){
|
|
[D3,A3,D4,F4,A4].forEach(function(f){dark(t,f,0.4,0.65);});
|
|
bg.push({t:t,f:D3/2,d:0.4,i:'timpani',v:0.65});mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[D3,A3,D4,F4,A4,D5].forEach(function(f){dark(t,f,2.5,0.65);});
|
|
bg.push({t:t,f:D3/2,d:2.0,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Night on Bald Mountain",composer:"Mussorgsky",bpm:152,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Swan Lake (Theme) ──
|
|
var swan=(function(){
|
|
var bg=[],mel=[],t=0,q=0.55;
|
|
var theme=[A4,B4,C5,B4,A4,E4,A4,G4,F4,E4,D4,E4,F4,E4,D4,C4,B3,A3,B3,C4,D4,E4,A4];
|
|
var tLn=[0,1,2,1,0,2,0,3,2,1,0,1,2,1,0,2,1,0,1,2,0,2,0];
|
|
for(var rep=0;rep<3;rep++){
|
|
var v=0.25+rep*0.12;
|
|
var inst=rep===0?'woodwind':rep===1?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.85,i:inst,v:v});
|
|
if(rep>0)bg.push({t:t,f:f/2,d:q*0.85,i:'bass',v:v*0.4});
|
|
if(rep>1)bg.push({t:t,f:f,d:q*0.85,i:'strings',v:v*0.5});
|
|
if(i%3===0){bg.push({t:t,f:A3,d:q*2,i:'bass',v:v*0.3});}
|
|
mel.push({t:t,l:tLn[i]});t+=q;
|
|
});
|
|
t+=0.5;
|
|
}
|
|
for(var c=0;c<3;c++){
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:q*2.5,i:'strings',v:0.55});bg.push({t:t,f:f,d:q*2.5,i:'brass',v:0.45});});
|
|
mel.push({t:t,l:4});t+=q*3;
|
|
}
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'strings',v:0.55});});
|
|
bg.push({t:t,f:A3/2,d:2.0,i:'timpani',v:0.6});mel.push({t:t,l:4});
|
|
return {n:"Swan Lake (Theme)",composer:"Tchaikovsky",bpm:110,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Flight of the Bumblebee ──
|
|
// Rimsky-Korsakov's famous chromatic whirlwind in A minor, ~65s
|
|
// Rapid chromatic scales up and down — the buzzing bee
|
|
var bumblebee=(function(){
|
|
var bg=[],mel=[],t=0,q=60/180;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
|
|
// Sparse harmonic bed — stays out of the way of the rapid runs
|
|
var harm=[
|
|
[0,12,[A3,C4,E4],0.06],[12,24,[A3,C4,E4],0.1],[24,40,[A3,C4,E4],0.14],
|
|
[40,52,[D4,F4,A4],0.12],[52,68,[A3,C4,E4],0.16]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.5){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:2.3,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// The chromatic descending run: E5-Eb5-D5-Db5-C5-B4-Bb4-A4-Ab4-G4-Gb4-F4-E4
|
|
var descChrom=[E5,Eb5,D5,Db5,C5,B4,Bb4,A4,Ab4,G4,Gb4,F4,E4];
|
|
var dLn=[2,1,0,1,2,3,2,1,0,3,2,1,0];
|
|
// The chromatic ascending run: E4-F4-Gb4-G4-Ab4-A4-Bb4-B4-C5-Db5-D5-Eb5-E5
|
|
var ascChrom=[E4,F4,Gb4,G4,Ab4,A4,Bb4,B4,C5,Db5,D5,Eb5,E5];
|
|
var aLn=[0,1,2,3,0,1,2,3,2,1,0,1,2];
|
|
|
|
// === SECTION 1 (0-12s): Solo violin — quiet buzzing ===
|
|
for(var s1=0;s1<2;s1++){
|
|
var v1=0.2+s1*0.04;
|
|
var nq=q*0.5; // very fast sixteenth notes
|
|
// Descending run
|
|
descChrom.forEach(function(f,i){
|
|
str(t,f,nq*0.8,v1);mel.push({t:t,l:dLn[i]});t+=nq;
|
|
});
|
|
// Ascending run
|
|
ascChrom.forEach(function(f,i){
|
|
str(t,f,nq*0.8,v1);mel.push({t:t,l:aLn[i]});t+=nq;
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (12-24s): More instruments join, slightly louder ===
|
|
for(var s2=0;s2<3;s2++){
|
|
var v2=0.28+s2*0.04;
|
|
var nq2=q*0.45;
|
|
descChrom.forEach(function(f,i){
|
|
str(t,f,nq2*0.8,v2);
|
|
if(s2>0)bg.push({t:t,f:f,d:nq2*0.8,i:'woodwind',v:v2*0.4});
|
|
mel.push({t:t,l:dLn[i]});t+=nq2;
|
|
});
|
|
ascChrom.forEach(function(f,i){
|
|
str(t,f,nq2*0.8,v2);
|
|
if(s2>0)bg.push({t:t,f:f,d:nq2*0.8,i:'woodwind',v:v2*0.4});
|
|
mel.push({t:t,l:aLn[i]});t+=nq2;
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// === SECTION 3 (24-40s): Full string section — the bee gets frantic ===
|
|
for(var s3=0;s3<4;s3++){
|
|
var v3=0.38+s3*0.04;
|
|
var nq3=q*0.4;
|
|
// Alternate between descending and ascending with different patterns
|
|
var pattern=s3%2===0?descChrom:ascChrom;
|
|
var pLn=s3%2===0?dLn:aLn;
|
|
pattern.forEach(function(f,i){
|
|
str(t,f,nq3*0.8,v3);bg.push({t:t,f:f,d:nq3*0.8,i:'woodwind',v:v3*0.3});
|
|
bg.push({t:t,f:f/2,d:nq3*0.8,i:'bass',v:v3*0.2});
|
|
mel.push({t:t,l:pLn[i]});t+=nq3;
|
|
});
|
|
// Quick turnaround notes
|
|
[E4,G4,B4,E5].forEach(function(f,i){
|
|
str(t,f,nq3*0.6,v3);mel.push({t:t,l:[0,3,0,2][i]});t+=nq3*0.7;
|
|
});
|
|
t+=q*0.3;
|
|
}
|
|
|
|
// === SECTION 4 (40-52s): Contrasting middle — broader melody ===
|
|
var middle=[[A4,q*1.5,1],[G4,q*0.75,3],[F4,q*0.75,0],[E4,q*1.5,2],
|
|
[G4,q*0.75,3],[A4,q*0.75,1],[B4,q*1.5,0],[A4,q*0.75,1],
|
|
[G4,q*0.75,3],[E4,q*2,2]];
|
|
for(var m=0;m<2;m++){
|
|
var mv=0.4+m*0.08;
|
|
middle.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,mv);bg.push({t:t,f:n[0],d:n[1]*0.9,i:'woodwind',v:mv*0.5});
|
|
if(m>0)bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:mv*0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (52-65s): Final frenzy — fastest runs ===
|
|
for(var s5=0;s5<4;s5++){
|
|
var v5=0.48+s5*0.04;
|
|
var nq5=q*(0.38-s5*0.02); // accelerating
|
|
descChrom.forEach(function(f,i){
|
|
str(t,f,nq5*0.75,v5);bg.push({t:t,f:f,d:nq5*0.75,i:'woodwind',v:v5*0.4});
|
|
if(s5>1)bg.push({t:t,f:f/2,d:nq5*0.75,i:'bass',v:v5*0.3});
|
|
mel.push({t:t,l:dLn[i]});t+=nq5;
|
|
});
|
|
if(s5<3){
|
|
ascChrom.slice(0,7).forEach(function(f,i){
|
|
str(t,f,nq5*0.75,v5);mel.push({t:t,l:aLn[i]});t+=nq5;
|
|
});
|
|
}
|
|
t+=q*0.3;
|
|
}
|
|
|
|
// Final sting — the bee lands!
|
|
[A3,C4,E4,A4].forEach(function(f){
|
|
str(t,f,0.3,0.55);bg.push({t:t,f:f,d:0.3,i:'brass',v:0.4});
|
|
});
|
|
mel.push({t:t,l:4});t+=0.5;
|
|
[A3,E4,A4].forEach(function(f){str(t,f,2.0,0.4);});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'bass',v:0.3});mel.push({t:t,l:4});
|
|
|
|
return {n:"Flight of the Bumblebee",composer:"Rimsky-Korsakov",bpm:180,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Dance of the Sugar Plum Fairy ──
|
|
var sugarplum=(function(){
|
|
var bg=[],mel=[],t=0,q=0.4;
|
|
var theme=[E4,Eb4,E4,B3,D4,C4,A3,C4,E4,A4,B3,E4,Ab4,B4,A4];
|
|
var tLn=[2,1,2,3,0,2,0,2,2,1,3,2,1,0,1];
|
|
var ans=[E4,Eb4,E4,B3,D4,C4,A3];
|
|
var aLn=[2,1,2,3,0,2,0];
|
|
for(var rep=0;rep<5;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<2?'woodwind':rep<4?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.5,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:q*0.5,i:'bass',v:v*0.3});
|
|
if(rep>3)bg.push({t:t,f:f,d:q*0.5,i:'strings',v:v*0.4});
|
|
mel.push({t:t,l:tLn[i]});t+=q;
|
|
});
|
|
t+=0.5;
|
|
ans.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.4,i:inst,v:v*0.8});
|
|
mel.push({t:t,l:aLn[i]});t+=q*0.8;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
[E3,B3,E4,Ab4,B4].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});});
|
|
bg.push({t:t,f:E3/2,d:1.5,i:'bass',v:0.4});mel.push({t:t,l:4});
|
|
return {n:"Dance of the Sugar Plum Fairy",composer:"Tchaikovsky",bpm:120,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── 1812 Overture (Finale) ──
|
|
var overture1812=(function(){
|
|
var bg=[],mel=[],t=0,q=0.4;
|
|
function brass(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.5});bg.push({t:t,f:f,d:d,i:'strings',v:(v||0.5)*0.7});}
|
|
function cannon(t,v){bg.push({t:t,f:60,d:0.8,i:'timpani',v:v||0.7});}
|
|
var theme=[G4,G4,A4,B4,B4,A4,G4,Gb4,E4,E4,Gb4,G4,G4,Gb4,Gb4];
|
|
var tLn=[0,0,1,3,3,1,0,2,1,1,2,0,0,2,2];
|
|
for(var rep=0;rep<5;rep++){
|
|
var v=0.3+rep*0.08;
|
|
theme.forEach(function(f,i){
|
|
brass(t,f,q*0.9,v);
|
|
if(rep>0)bg.push({t:t,f:f/2,d:q*0.9,i:'bass',v:v*0.5});
|
|
if(rep>2)bg.push({t:t,f:f,d:q*0.9,i:'choir',v:v*0.3});
|
|
mel.push({t:t,l:tLn[i]});t+=q;
|
|
});
|
|
cannon(t,0.4+rep*0.08);mel.push({t:t,l:4});t+=0.8;
|
|
cannon(t,0.45+rep*0.08);mel.push({t:t,l:4});t+=0.8;
|
|
t+=0.3;
|
|
}
|
|
for(var c=0;c<8;c++){
|
|
[G3,B3,D4,G4].forEach(function(f){brass(t,f,0.4,0.65);});
|
|
cannon(t,0.7);mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[G3,B3,D4,G4,B4,D5].forEach(function(f){brass(t,f,3.0,0.7);bg.push({t:t,f:f,d:3.0,i:'choir',v:0.4});});
|
|
cannon(t,0.8);mel.push({t:t,l:4});
|
|
return {n:"1812 Overture (Finale)",composer:"Tchaikovsky",bpm:120,duration:Math.ceil(t+3.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Habanera (Carmen) ──
|
|
// Bizet's famous aria in D minor, ~80s
|
|
// Descending chromatic melody with the habanera rhythm (dotted + sixteenth)
|
|
var habanera=(function(){
|
|
var bg=[],mel=[],t=0,q=60/108;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.35});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.35});}
|
|
function ww(t,f,d,v){bg.push({t:t,f:f,d:d,i:'woodwind',v:v||0.3});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,18,[D3,F3,A3],0.08],[18,36,[D3,F3,A3],0.12],[36,54,[D3,F3,A3],0.16],
|
|
[54,68,[Bb3,D4,F4],0.14],[68,84,[D3,F3,A3],0.18]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:1.9,i:'strings',v:h[3]});});
|
|
}
|
|
});
|
|
|
|
// The habanera bass rhythm: dotted-eighth + sixteenth + quarter + quarter
|
|
// DUM da-DUM DUM (characteristic Cuban rhythm)
|
|
function habBass(time,root,v){
|
|
bg.push({t:time,f:root,d:q*0.7,i:'bass',v:v*0.5});
|
|
bg.push({t:time+q*0.75,f:root*1.5,d:q*0.2,i:'bass',v:v*0.35});
|
|
bg.push({t:time+q,f:root,d:q*0.7,i:'bass',v:v*0.45});
|
|
bg.push({t:time+q*2,f:root,d:q*0.7,i:'bass',v:v*0.4});
|
|
}
|
|
|
|
// === SECTION 1 (0-18s): The famous descending chromatic melody ===
|
|
// D-Db-C-B-Bb-A-Ab-G — each note slightly longer, seductive descent
|
|
var desc1=[[D5,q*0.7,0],[Db5,q*0.7,1],[C5,q*0.7,2],[B4,q*0.7,3],
|
|
[Bb4,q*0.7,2],[A4,q*0.7,1],[Ab4,q*0.7,0],[G4,q*1.4,3]];
|
|
// Answer phrase: bouncing back up
|
|
var ans1=[[A4,q*0.5,1],[Bb4,q*0.5,2],[A4,q,1],[G4,q*0.5,3],[A4,q*0.5,1],[D4,q*1.5,0]];
|
|
|
|
for(var s1=0;s1<3;s1++){
|
|
var v1=0.2+s1*0.05;
|
|
habBass(t,D3,v1);habBass(t+q*3,D3,v1);
|
|
desc1.forEach(function(n){
|
|
ww(t,n[0],n[1]*0.9,v1);
|
|
if(s1>0)str(t,n[0],n[1]*0.9,v1*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
ans1.forEach(function(n){
|
|
ww(t,n[0],n[1]*0.9,v1);
|
|
if(s1>0)str(t,n[0],n[1]*0.9,v1*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (18-36s): Second verse — strings take melody ===
|
|
// Same chromatic descent but with richer orchestration
|
|
var desc2=[[D5,q*0.65,0],[Db5,q*0.65,1],[C5,q*0.65,2],[B4,q*0.65,3],
|
|
[Bb4,q*0.65,2],[A4,q*0.65,1],[Ab4,q*0.65,0],[G4,q*1.3,3]];
|
|
var ans2=[[A4,q*0.5,1],[Bb4,q*0.5,2],[C5,q*0.5,2],[A4,q,1],[F4,q*0.5,3],[D4,q*1.5,0]];
|
|
|
|
for(var s2=0;s2<3;s2++){
|
|
var v2=0.32+s2*0.06;
|
|
habBass(t,D3,v2);habBass(t+q*3,A3,v2);
|
|
desc2.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,v2);ww(t,n[0],n[1]*0.9,v2*0.5);
|
|
if(s2>0)bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:v2*0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
ans2.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,v2);ww(t,n[0],n[1]*0.9,v2*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
bg.push({t:t,f:D3,d:0.2,i:'timpani',v:v2*0.4});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 3 (36-54s): Major key contrast — "L'amour" refrain ===
|
|
// Shifts to D major — bright, passionate
|
|
var refrain=[[D4,q,0],[F4,q*0.5,3],[A4,q*0.5,1],[D5,q*1.5,0],
|
|
[C5,q*0.5,2],[Bb4,q*0.5,2],[A4,q,1],[G4,q*0.5,3],[A4,q*0.5,1],
|
|
[D4,q*2,0]];
|
|
for(var s3=0;s3<3;s3++){
|
|
var v3=0.4+s3*0.06;
|
|
habBass(t,D3,v3);habBass(t+q*3,A3,v3);
|
|
refrain.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,v3);brs(t,n[0],n[1]*0.9,v3*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:v3*0.35});
|
|
if(s3>1)bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:v3*0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
if(s3>0){bg.push({t:t,f:D3,d:0.3,i:'timpani',v:v3*0.5});mel.push({t:t,l:4});}
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 4 (54-68s): Return of chromatic descent — full orchestra ===
|
|
for(var s4=0;s4<3;s4++){
|
|
var v4=0.5+s4*0.05;
|
|
habBass(t,D3,v4);habBass(t+q*3,D3,v4);
|
|
desc1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,v4);brs(t,n[0],n[1]*0.9,v4*0.6);
|
|
ww(t,n[0],n[1]*0.9,v4*0.4);
|
|
if(s4>0)bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:v4*0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
ans1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,v4);brs(t,n[0],n[1]*0.9,v4*0.6);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
bg.push({t:t,f:D3,d:0.25,i:'timpani',v:v4*0.6});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === FINALE: Passionate ending ===
|
|
// Final descent + big D minor chord
|
|
desc1.forEach(function(n){
|
|
str(t,n[0],n[1]*0.8,0.6);brs(t,n[0],n[1]*0.8,0.5);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.8,i:'choir',v:0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1]*0.85;
|
|
});
|
|
t+=q;
|
|
for(var c=0;c<4;c++){
|
|
[D3,A3,D4,F4].forEach(function(f){brs(t,f,0.4,0.6);str(t,f,0.4,0.5);});
|
|
bg.push({t:t,f:D3/2,d:0.4,i:'timpani',v:0.6});mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[D3,A3,D4,F4,A4].forEach(function(f){brs(t,f,3.0,0.6);str(t,f,3.0,0.5);});
|
|
bg.push({t:t,f:D3/2,d:2.5,i:'timpani',v:0.65});mel.push({t:t,l:4});
|
|
|
|
return {n:"Habanera (Carmen)",composer:"Bizet",bpm:108,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Spring (Four Seasons) ──
|
|
// Vivaldi's Spring from The Four Seasons in E major, ~75s
|
|
// Joyful opening ritornello → birdsong trills → pastoral dance
|
|
var spring=(function(){
|
|
var bg=[],mel=[],t=0,q=60/120;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.35});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,14,[E3,G3,B3],0.1],[14,28,[E3,G3,B3],0.14],[28,44,[A3,C4,E4],0.13],
|
|
[44,58,[E3,G3,B3],0.16],[58,78,[E3,G3,B3],0.2]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.0){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:1.9,i:'strings',v:h[3]});});
|
|
bg.push({t:ht,f:h[2][0]/2,d:1.9,i:'bass',v:h[3]*0.5});
|
|
}
|
|
});
|
|
|
|
// === SECTION 1 (0-14s): The famous opening ritornello ===
|
|
// Bright, joyful E major: E-E-E-G#-B-B-B-G#-E
|
|
var rit=[[E4,q*0.5,2],[E4,q*0.5,2],[E4,q*0.5,2],[Ab4,q*0.5,1],[B4,q,0],
|
|
[B4,q*0.5,0],[B4,q*0.5,0],[Ab4,q*0.5,1],[E4,q*1,2]];
|
|
// Answer: ascending scale burst
|
|
var ritAns=[[E4,q*0.5,2],[Ab4,q*0.5,1],[B4,q*0.5,0],[E5,q,2],
|
|
[D5,q*0.5,1],[B4,q*0.5,0],[Ab4,q*0.5,1],[E4,q*1,2]];
|
|
|
|
for(var r1=0;r1<2;r1++){
|
|
var rv=0.35+r1*0.08;
|
|
rit.forEach(function(n){str(t,n[0],n[1]*0.85,rv);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q*0.3;
|
|
ritAns.forEach(function(n){str(t,n[0],n[1]*0.85,rv);mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 2 (14-28s): Birdsong — trills and rapid ornaments ===
|
|
// High violin trills imitating birds
|
|
var bird1=[[E5,q*0.25,2],[D5,q*0.25,1],[E5,q*0.25,2],[D5,q*0.25,1],
|
|
[E5,q*0.5,2],[B4,q*0.5,0],[Ab4,q*0.5,1],[E4,q,2]];
|
|
var bird2=[[B4,q*0.25,0],[A4,q*0.25,1],[B4,q*0.25,0],[A4,q*0.25,1],
|
|
[B4,q*0.5,0],[Ab4,q*0.5,1],[E4,q*0.5,2],[B3,q,3]];
|
|
for(var b=0;b<3;b++){
|
|
var bv=0.3+b*0.06;
|
|
var birds=b%2===0?bird1:bird2;
|
|
birds.forEach(function(n){
|
|
str(t,n[0],n[1]*0.8,bv);
|
|
if(b>0)bg.push({t:t,f:n[0],d:n[1]*0.8,i:'woodwind',v:bv*0.4});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
// Gentle bass response
|
|
[E3,B3,E4].forEach(function(f){bg.push({t:t,f:f,d:q*1.5,i:'bass',v:bv*0.3});});
|
|
mel.push({t:t,l:4});t+=q*2;
|
|
}
|
|
|
|
// === SECTION 3 (28-44s): Ritornello returns — fuller ===
|
|
for(var r2=0;r2<2;r2++){
|
|
var rv2=0.42+r2*0.08;
|
|
rit.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,rv2);brs(t,n[0],n[1]*0.85,rv2*0.4);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.85,i:'bass',v:rv2*0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.3;
|
|
ritAns.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,rv2);brs(t,n[0],n[1]*0.85,rv2*0.4);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
// Running scales
|
|
[E4,Ab4,B4,E5,D5,B4,Ab4,E4,D4,E4,Ab4,B4,D5,E5,Ab5,E5].forEach(function(f,i){
|
|
str(t,f,q*0.35,0.5);mel.push({t:t,l:[2,1,0,2,1,0,1,2,0,2,1,0,1,2,0,2][i]});t+=q*0.4;
|
|
});
|
|
t+=q;
|
|
|
|
// === SECTION 4 (44-58s): Pastoral dance — lilting 12/8 feel ===
|
|
var dance=[[E4,q,2],[Ab4,q*0.5,1],[B4,q*0.5,0],[E4,q*0.5,2],[Ab4,q*0.5,1],
|
|
[B4,q,0],[E5,q*0.5,2],[D5,q*0.5,1],[B4,q,0],[Ab4,q*0.5,1],[E4,q*1,2]];
|
|
for(var d=0;d<3;d++){
|
|
var dv=0.45+d*0.06;
|
|
dance.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,dv);
|
|
if(d>0)brs(t,n[0],n[1]*0.85,dv*0.4);
|
|
if(d>1)bg.push({t:t,f:n[0]/2,d:n[1]*0.85,i:'bass',v:dv*0.35});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (58-72s): Final ritornello — full orchestra ===
|
|
for(var r3=0;r3<3;r3++){
|
|
var rv3=0.52+r3*0.06;
|
|
rit.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,rv3);brs(t,n[0],n[1]*0.85,rv3*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.85,i:'bass',v:rv3*0.35});
|
|
if(r3>1)bg.push({t:t,f:n[0],d:n[1]*0.85,i:'choir',v:rv3*0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.3;
|
|
ritAns.forEach(function(n){
|
|
str(t,n[0],n[1]*0.85,rv3);brs(t,n[0],n[1]*0.85,rv3*0.5);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q*0.5;
|
|
}
|
|
|
|
// Finale chords
|
|
for(var c=0;c<4;c++){
|
|
[E3,B3,E4,Ab4,B4].forEach(function(f){str(t,f,0.4,0.6);brs(t,f,0.4,0.5);});
|
|
mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[E3,B3,E4,Ab4,B4,E5].forEach(function(f){str(t,f,3.0,0.6);brs(t,f,3.0,0.5);});
|
|
bg.push({t:t,f:E3/2,d:2.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
|
|
return {n:"Spring (Four Seasons)",composer:"Vivaldi",bpm:120,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Für Elise ──
|
|
var furelise=(function(){
|
|
var bg=[],mel=[],t=0,q=0.35;
|
|
var motif=[E4,Eb4,E4,Eb4,E4,B3,D4,C4,A3];
|
|
var mLn=[2,1,2,1,2,3,0,2,0];
|
|
var ph2=[C4,E4,A4,B4,E4,Ab4,B4,A4];
|
|
var p2Ln=[1,2,0,3,2,1,3,0];
|
|
for(var rep=0;rep<5;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<2?'woodwind':rep<3?'strings':'brass';
|
|
motif.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.7,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:q*0.7,i:'bass',v:v*0.3});
|
|
mel.push({t:t,l:mLn[i]});t+=q;
|
|
});
|
|
t+=0.2;
|
|
ph2.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.7,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:q*0.7,i:'bass',v:v*0.3});
|
|
mel.push({t:t,l:p2Ln[i]});t+=q;
|
|
});
|
|
t+=0.2;
|
|
motif.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.7,i:inst,v:v});
|
|
if(rep>2)bg.push({t:t,f:f,d:q*0.7,i:'strings',v:v*0.4});
|
|
mel.push({t:t,l:mLn[i]});t+=q;
|
|
});
|
|
t+=0.5;
|
|
}
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'strings',v:0.45});});
|
|
bg.push({t:t,f:A3/2,d:2.0,i:'bass',v:0.35});mel.push({t:t,l:4});
|
|
return {n:"F\u00fcr Elise",composer:"Beethoven",bpm:130,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Canon in D ──
|
|
var canon=(function(){
|
|
var bg=[],mel=[],t=0,q=0.55;
|
|
var bassLine=[D3,A3,B3,Gb3,G3,D3,G3,A3];
|
|
// Real Pachelbel violin melody: F#-E-D-C#-B-A-B-C# (var 1) then D-C#-B-A-G-F#-G-A (var 2)
|
|
var melody2=[Gb4,E4,D4,Db4,B3,A3,B3,Db4,D4,Db4,B3,A3,G3,Gb3,G3,A3];
|
|
var m2Ln=[2,1,0,1,3,0,3,1,0,1,3,0,3,2,3,0];
|
|
for(var rep=0;rep<4;rep++){
|
|
var v=0.2+rep*0.08;
|
|
bassLine.forEach(function(f,i){
|
|
var bt=t+i*q*2;
|
|
bg.push({t:bt,f:f,d:q*1.8,i:'bass',v:v*0.5});
|
|
bg.push({t:bt,f:f*2,d:q*1.8,i:'strings',v:v*0.3});
|
|
});
|
|
if(rep>=1){
|
|
melody2.forEach(function(f,i){
|
|
bg.push({t:t+i*q,f:f,d:q*0.85,i:rep<3?'strings':'brass',v:v});
|
|
if(rep>2)bg.push({t:t+i*q,f:f,d:q*0.85,i:'choir',v:v*0.3});
|
|
mel.push({t:t+i*q,l:m2Ln[i]});
|
|
});
|
|
} else {
|
|
bassLine.forEach(function(f,i){mel.push({t:t+i*q*2,l:4});});
|
|
}
|
|
t+=bassLine.length*q*2;t+=0.3;
|
|
}
|
|
[D3,A3,D4,Gb4,A4,D5].forEach(function(f){bg.push({t:t,f:f,d:3.0,i:'strings',v:0.55});bg.push({t:t,f:f,d:3.0,i:'brass',v:0.4});});
|
|
bg.push({t:t,f:D3/2,d:2.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
return {n:"Canon in D",composer:"Pachelbel",bpm:60,duration:Math.ceil(t+3.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Hungarian Dance No. 5 ──
|
|
var hungarian=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
function orch(t,f,d,v,i){bg.push({t:t,f:f,d:d,i:i||'strings',v:v||0.4});}
|
|
var fast=[A4,G4,A4,E4,A4,G4,A4,E4,F4,E4,D4,C4,B3,A3,B3,C4,D4,E4,F4,E4];
|
|
var fLn=[0,3,0,2,0,3,0,2,1,2,0,1,3,0,3,1,0,2,1,2];
|
|
var slow=[A4,A4,G4,F4,E4,D4,E4,F4,G4,A4];
|
|
var sLn=[0,0,3,1,2,0,2,1,3,0];
|
|
// Fast section 1
|
|
[0.20,0.18].forEach(function(qf,qi){
|
|
var v=0.35+qi*0.1;
|
|
fast.forEach(function(f,i){
|
|
orch(t,f,qf*0.85,v);if(qi>0)orch(t,f,qf*0.85,v*0.6,'brass');
|
|
orch(t,f/2,qf*0.85,v*0.5,'bass');mel.push({t:t,l:fLn[i]});t+=qf;
|
|
});
|
|
t+=0.35;
|
|
});
|
|
t+=0.3;
|
|
// Slow section (dramatic)
|
|
[0.55,0.55,0.50].forEach(function(qs,qi){
|
|
var v=0.45+qi*0.05;
|
|
slow.forEach(function(f,i){
|
|
orch(t,f,qs*0.85,v,'brass');orch(t,f,qs*0.85,v*0.8);
|
|
bg.push({t:t,f:f/2,d:qs*0.85,i:'bass',v:v*0.5});
|
|
if(qi>0&&i%2===0)bg.push({t:t,f:A3/2,d:0.3,i:'timpani',v:v*0.6});
|
|
mel.push({t:t,l:sLn[i]});t+=qs;
|
|
});
|
|
t+=0.5;
|
|
});
|
|
t+=0.3;
|
|
// Fast section 2 (even faster!)
|
|
[0.15,0.13,0.11].forEach(function(qf,qi){
|
|
var v=0.5+qi*0.06;
|
|
fast.forEach(function(f,i){
|
|
orch(t,f,qf*0.85,v);orch(t,f,qf*0.85,v*0.7,'brass');
|
|
orch(t,f/2,qf*0.85,v*0.5,'bass');
|
|
if(i%3===0)bg.push({t:t,f:A3/2,d:0.2,i:'timpani',v:v*0.7});
|
|
mel.push({t:t,l:fLn[i]});t+=qf;
|
|
});
|
|
t+=0.3;
|
|
});
|
|
for(var c=0;c<4;c++){
|
|
[A3,C4,E4,A4].forEach(function(f){orch(t,f,0.35,0.65,'brass');});
|
|
bg.push({t:t,f:A3/2,d:0.35,i:'timpani',v:0.65});mel.push({t:t,l:4});t+=0.45;
|
|
}
|
|
[A3,C4,E4,A4].forEach(function(f){orch(t,f,2.0,0.6,'brass');orch(t,f,2.0,0.5);});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Hungarian Dance No. 5",composer:"Brahms",bpm:140,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Ode to Joy ──
|
|
// Beethoven's 9th Symphony finale in D major, ~85s
|
|
// The most famous melody in classical music, builds from solo to full chorus
|
|
var odeToJoy=(function(){
|
|
var bg=[],mel=[],t=0,q=60/100;
|
|
function str(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});}
|
|
function brs(t,f,d,v){bg.push({t:t,f:f,d:d,i:'brass',v:v||0.4});}
|
|
function ww(t,f,d,v){bg.push({t:t,f:f,d:d,i:'woodwind',v:v||0.3});}
|
|
|
|
// Harmonic bed
|
|
var harm=[
|
|
[0,16,[D3,A3,D4],0.08],[16,32,[D3,A3,D4],0.12],[32,48,[D3,A3,D4],0.16],
|
|
[48,64,[G3,B3,D4],0.15],[64,88,[D3,A3,D4],0.2]
|
|
];
|
|
harm.forEach(function(h){
|
|
for(var ht=h[0];ht<h[1];ht+=2.5){
|
|
h[2].forEach(function(f){bg.push({t:ht,f:f,d:2.3,i:'strings',v:h[3]});});
|
|
bg.push({t:ht,f:h[2][0]/2,d:2.3,i:'bass',v:h[3]*0.5});
|
|
}
|
|
});
|
|
|
|
// The Ode to Joy melody (A section):
|
|
// E-E-F-G | G-F-E-D | C-C-D-E | E-D-D
|
|
var melA=[[Gb4,q,2],[Gb4,q,2],[G4,q,3],[A4,q,1],
|
|
[A4,q,1],[G4,q,3],[Gb4,q,2],[E4,q,0],
|
|
[D4,q,0],[D4,q,0],[E4,q,2],[Gb4,q,2],
|
|
[Gb4,q*1.5,2],[E4,q*0.5,0],[E4,q*2,0]];
|
|
// B section (contrasting middle):
|
|
// E-E-F-G | G-F-E-D | C-C-D-E | D-C-C
|
|
var melB=[[Gb4,q,2],[Gb4,q,2],[G4,q,3],[A4,q,1],
|
|
[A4,q,1],[G4,q,3],[Gb4,q,2],[E4,q,0],
|
|
[D4,q,0],[D4,q,0],[E4,q,2],[Gb4,q,2],
|
|
[E4,q*1.5,0],[D4,q*0.5,1],[D4,q*2,1]];
|
|
|
|
// === SECTION 1 (0-16s): Quiet cellos — theme stated simply ===
|
|
melA.forEach(function(n){
|
|
str(t,n[0]/2,n[1]*0.9,0.2); // cello register
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
melB.forEach(function(n){
|
|
str(t,n[0]/2,n[1]*0.9,0.22);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// === SECTION 2 (16-32s): Violas + woodwinds join ===
|
|
melA.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.3);ww(t,n[0],n[1]*0.9,0.18);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
melB.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.32);ww(t,n[0],n[1]*0.9,0.2);
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// === SECTION 3 (32-48s): Full strings + brass hint ===
|
|
melA.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.42);brs(t,n[0],n[1]*0.9,0.2);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.2});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
melB.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.44);brs(t,n[0],n[1]*0.9,0.22);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.22});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// === SECTION 4 (48-64s): Development — contrasting bridge ===
|
|
// Middle section with different harmonies
|
|
var bridge=[[E4,q,0],[E4,q*0.5,0],[Gb4,q*0.5,2],[A4,q,1],[A4,q*0.5,1],[G4,q*0.5,3],
|
|
[Gb4,q,2],[E4,q*0.5,0],[D4,q*0.5,1],[E4,q,2],[D4,q,1],[A3,q*2,0]];
|
|
for(var br=0;br<2;br++){
|
|
var bv=0.4+br*0.08;
|
|
bridge.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,bv);brs(t,n[0],n[1]*0.9,bv*0.5);
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:bv*0.4});
|
|
if(br>0)bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:bv*0.25});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
bg.push({t:t,f:D3,d:0.3,i:'timpani',v:bv*0.5});mel.push({t:t,l:4});t+=q;
|
|
}
|
|
|
|
// === SECTION 5 (64-80s): Grand finale — full orchestra + choir ===
|
|
// "Freude, schöner Götterfunken" at full power
|
|
melA.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.55);brs(t,n[0],n[1]*0.9,0.45);
|
|
ww(t,n[0],n[1]*0.9,0.3);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:0.35});
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.3});
|
|
if(n[2]===1||n[2]===0)bg.push({t:t,f:D3,d:0.15,i:'timpani',v:0.35});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
melB.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.58);brs(t,n[0],n[1]*0.9,0.48);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:0.38});
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.3});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=q;
|
|
|
|
// Final triumphant repeat of A
|
|
melA.forEach(function(n){
|
|
str(t,n[0],n[1]*0.9,0.6);brs(t,n[0],n[1]*0.9,0.5);
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'choir',v:0.4});
|
|
bg.push({t:t,f:n[0]/2,d:n[1]*0.9,i:'bass',v:0.35});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
|
|
// === CODA: Big D major chords ===
|
|
t+=q;
|
|
for(var c=0;c<6;c++){
|
|
[D3,A3,D4,Gb4,A4].forEach(function(f){brs(t,f,0.45,0.65);str(t,f,0.45,0.55);});
|
|
bg.push({t:t,f:D3/2,d:0.45,i:'timpani',v:0.6});
|
|
bg.push({t:t,f:D4,d:0.45,i:'choir',v:0.4});
|
|
mel.push({t:t,l:4});t+=0.55;
|
|
}
|
|
[D3,A3,D4,Gb4,A4,D5].forEach(function(f){
|
|
brs(t,f,4.0,0.7);str(t,f,4.0,0.6);bg.push({t:t,f:f,d:4.0,i:'choir',v:0.4});
|
|
});
|
|
bg.push({t:t,f:D3/2,d:3.0,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
|
|
return {n:"Ode to Joy",composer:"Beethoven",bpm:100,duration:Math.ceil(t+4),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── William Tell Overture (Storm) ──
|
|
var tellStorm=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
function dark(t,f,d,v){bg.push({t:t,f:f,d:d,i:'strings',v:v||0.4});bg.push({t:t,f:f/2,d:d,i:'bass',v:(v||0.4)*0.5});}
|
|
var theme=[E4,E4,F4,E4,D4,C4,D4,E4,F4,G4,Ab4,G4,F4,E4,D4,C4];
|
|
var tLn=[2,2,1,2,0,1,0,2,1,3,0,3,1,2,0,1];
|
|
[0.30,0.27,0.24,0.21,0.18,0.16,0.14,0.12].forEach(function(spd,si){
|
|
var v=0.25+si*0.05;
|
|
theme.forEach(function(f,i){
|
|
dark(t,f,spd*0.85,v);
|
|
if(si>2)bg.push({t:t,f:f,d:spd*0.85,i:'brass',v:v*0.5});
|
|
if(si>3&&i%4===0)bg.push({t:t,f:C3,d:0.4,i:'timpani',v:v*0.8});
|
|
mel.push({t:t,l:tLn[i]});t+=spd;
|
|
});
|
|
t+=0.3;
|
|
bg.push({t:t,f:C3,d:0.6,i:'timpani',v:0.35+si*0.04});mel.push({t:t,l:4});t+=0.7;
|
|
});
|
|
for(var c=0;c<6;c++){
|
|
bg.push({t:t,f:C3,d:0.5,i:'timpani',v:0.65});
|
|
[C4,Eb4,G4].forEach(function(f){dark(t,f,0.4,0.6);});
|
|
mel.push({t:t,l:4});t+=0.5;
|
|
}
|
|
[C4,Eb4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:3.0,i:'strings',v:0.5});});
|
|
bg.push({t:t,f:C3,d:2.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
return {n:"William Tell Overture (Storm)",composer:"Rossini",bpm:132,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
return [b5,hmk,ofort,valk,mars,toccata,tell,bolero,zarath,barber,danube,sabre,bald,sorcerer,swan,morning,bumblebee,sugarplum,overture1812,habanera,spring,furelise,canon,hungarian,odeToJoy,tellStorm];
|
|
})();
|
|
|
|
// ── VIDEO GAMES (public domain pieces associated with games) ──
|
|
const VIDEOGAME_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,G3=NT.G3,Ab3=NT.Ab3,A3=NT.A3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,Gb4=NT.Gb4,G4=NT.G4,Ab4=NT.Ab4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5,F5=NT.F5,G5=NT.G5;
|
|
|
|
// ── Korobeiniki (Tetris Theme) ──
|
|
var tetris=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
// The iconic Tetris melody in A minor
|
|
var theme=[E4,B3,C4,D4,C4,B3,A3,A3,C4,E4,D4,C4,B3,B3,C4,D4,E4,C4,A3,A3];
|
|
var tLn= [2,3,0,1,0,3,0,0,1,2,1,0,3,3,0,1,2,0,0,0];
|
|
var durations=[2,1,1,1,1,1,2,1,1,2,1,1,2,1,1,2,2,2,2,2];
|
|
var q=0.22;
|
|
for(var rep=0;rep<5;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<2?'woodwind':rep<4?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
var d=durations[i]*q;
|
|
bg.push({t:t,f:f,d:d*0.85,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.4});
|
|
if(rep>3)bg.push({t:t,f:f,d:d*0.85,i:'brass',v:v*0.4});
|
|
mel.push({t:t,l:tLn[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.55});bg.push({t:t,f:f,d:2.0,i:'strings',v:0.45});});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'timpani',v:0.6});mel.push({t:t,l:4});
|
|
return {n:"Korobeiniki (Tetris Theme)",composer:"Russian Folk",bpm:140,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Turkish March (Mozart) ──
|
|
var turkish=(function(){
|
|
var bg=[],mel=[],t=0,q=0.18;
|
|
var theme=[B3,A3,Ab3,A3,C4,D4,C4,B3,A3,B3,C4,D4,E4,D4,C4,B3,A3,B3,A3,A3];
|
|
var tLn= [3,0,1,0,2,1,2,3,0,3,0,1,2,1,0,3,0,3,0,0];
|
|
for(var rep=0;rep<6;rep++){
|
|
var v=0.22+rep*0.06;
|
|
var inst=rep<2?'woodwind':rep<4?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.85,i:inst,v:v});
|
|
if(rep>2)bg.push({t:t,f:f/2,d:q*0.85,i:'bass',v:v*0.4});
|
|
if(rep>4)bg.push({t:t,f:f,d:q*0.85,i:'brass',v:v*0.4});
|
|
mel.push({t:t,l:tLn[i]});t+=q;
|
|
});
|
|
t+=0.35;
|
|
}
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.6});});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'timpani',v:0.6});mel.push({t:t,l:4});
|
|
return {n:"Turkish March",composer:"Mozart",bpm:132,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Hungarian Rhapsody No. 2 (Liszt) ──
|
|
var rhapsody=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
function orch(t,f,d,v,i){bg.push({t:t,f:f,d:d,i:i||'strings',v:v||0.4});}
|
|
// Slow dramatic opening then fast friska
|
|
var slow=[C4,D4,Eb4,D4,C4,D4,Eb4,F4,G4,F4,Eb4,D4];
|
|
var sLn= [0,1,2,1,0,1,2,3,0,3,2,1];
|
|
// Slow section
|
|
[0.45,0.42].forEach(function(qs,qi){
|
|
var v=0.35+qi*0.1;
|
|
slow.forEach(function(f,i){
|
|
orch(t,f,qs*0.85,v,'strings');
|
|
if(qi>0)orch(t,f,qs*0.85,v*0.5,'brass');
|
|
bg.push({t:t,f:f/2,d:qs*0.85,i:'bass',v:v*0.4});
|
|
mel.push({t:t,l:sLn[i]});t+=qs;
|
|
});
|
|
t+=0.5;
|
|
});
|
|
// Fast friska
|
|
var fast=[C4,E4,G4,C5,G4,E4,C4,D4,F4,A4,F4,D4,C4,E4,G4,C5,B4,A4,G4,E4];
|
|
var fLn= [0,2,3,0,3,2,0,1,2,1,2,1,0,2,3,0,3,1,3,2];
|
|
[0.16,0.14,0.12,0.10].forEach(function(qf,qi){
|
|
var v=0.4+qi*0.06;
|
|
fast.forEach(function(f,i){
|
|
orch(t,f,qf*0.85,v);
|
|
if(qi>0)orch(t,f,qf*0.85,v*0.5,'brass');
|
|
orch(t,f/2,qf*0.85,v*0.4,'bass');
|
|
if(qi>1&&i%4===0)bg.push({t:t,f:C3,d:0.2,i:'timpani',v:v*0.6});
|
|
mel.push({t:t,l:fLn[i]});t+=qf;
|
|
});
|
|
t+=0.3;
|
|
});
|
|
for(var c=0;c<4;c++){
|
|
[C4,E4,G4,C5].forEach(function(f){orch(t,f,0.35,0.65,'brass');});
|
|
bg.push({t:t,f:C3,d:0.35,i:'timpani',v:0.65});mel.push({t:t,l:4});t+=0.45;
|
|
}
|
|
[C4,E4,G4,C5].forEach(function(f){orch(t,f,2.0,0.6,'brass');orch(t,f,2.0,0.5);});
|
|
bg.push({t:t,f:C3,d:1.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Hungarian Rhapsody No. 2",composer:"Liszt",bpm:140,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Can-Can (Offenbach) ──
|
|
var cancan=(function(){
|
|
var bg=[],mel=[],t=0,q=0.16;
|
|
var theme=[C4,D4,E4,C4,E4,C4,E4,D4,E4,F4,E4,D4,C4,D4,E4,F4,G4,A4,G4,E4,C4,D4,E4,C4];
|
|
var tLn= [0,1,2,0,2,0,2,1,2,3,2,1,0,1,2,3,0,1,0,2,0,1,2,0];
|
|
for(var rep=0;rep<6;rep++){
|
|
var v=0.25+rep*0.06;
|
|
var inst=rep<2?'strings':rep<4?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*0.85,i:inst,v:v});
|
|
if(rep>2)bg.push({t:t,f:f/2,d:q*0.85,i:'bass',v:v*0.4});
|
|
if(rep>3)bg.push({t:t,f:f,d:q*0.85,i:'brass',v:v*0.4});
|
|
if(rep>4&&i%4===0)bg.push({t:t,f:C3,d:0.2,i:'timpani',v:v*0.6});
|
|
mel.push({t:t,l:tLn[i]});t+=q;
|
|
});
|
|
t+=0.35;
|
|
}
|
|
[C4,E4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.6});});
|
|
bg.push({t:t,f:C3,d:1.5,i:'timpani',v:0.65});mel.push({t:t,l:4});
|
|
return {n:"Can-Can",composer:"Offenbach",bpm:152,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
// ── Kalinka (Russian Folk) ──
|
|
var kalinka=(function(){
|
|
var bg=[],mel=[],t=0;
|
|
// Kalinka starts slow then gets very fast
|
|
var theme=[E4,E4,D4,C4,B3,A3,B3,C4,D4,E4,F4,E4,D4,C4,B3,A3];
|
|
var tLn= [2,2,0,1,3,0,3,1,0,2,1,2,0,1,3,0];
|
|
var speeds=[0.35,0.30,0.25,0.20,0.16,0.13,0.10];
|
|
var vols= [0.2, 0.26,0.32,0.38,0.45,0.52,0.6];
|
|
var insts= ['woodwind','woodwind','strings','strings','brass','brass','brass'];
|
|
speeds.forEach(function(spd,si){
|
|
theme.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:spd*0.85,i:insts[si],v:vols[si]});
|
|
if(si>2)bg.push({t:t,f:f/2,d:spd*0.85,i:'bass',v:vols[si]*0.4});
|
|
if(si>4&&i%3===0)bg.push({t:t,f:A3/2,d:0.2,i:'timpani',v:vols[si]*0.6});
|
|
mel.push({t:t,l:tLn[i]});t+=spd;
|
|
});
|
|
t+=0.35;
|
|
});
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.6});});
|
|
bg.push({t:t,f:A3/2,d:1.5,i:'timpani',v:0.7});mel.push({t:t,l:4});
|
|
return {n:"Kalinka",composer:"Russian Folk",bpm:140,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
|
|
return [tetris,turkish,rhapsody,cancan,kalinka];
|
|
})();
|
|
|
|
// ── CHIPTUNE ORIGINALS (Claude.ai compositions using square waves) ──
|
|
const CHIPTUNE_PIECES=(function(){
|
|
var C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,G4=NT.G4,Ab4=NT.Ab4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5,G3=NT.G3,A3=NT.A3,B3=NT.B3,C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,
|
|
Gb4=NT.Gb4,F5=NT.F5,G5=NT.G5;
|
|
|
|
// Chiptune helper: square wave + simple pulse bass
|
|
function chip(name,bpm,theme,lanes,passes,root){
|
|
var bg=[],mel=[],t=0;
|
|
passes.forEach(function(p,pi){
|
|
theme.forEach(function(f,ni){
|
|
// Square wave lead (chiptune character)
|
|
bg.push({t:t,f:f,d:p.d*0.8,i:'brass',v:p.v}); // square-ish
|
|
// Pulse bass on beat
|
|
if(ni%4===0)bg.push({t:t,f:root||theme[0],d:p.d*3,i:'bass',v:p.v*0.35});
|
|
// Add arpeggiated accompaniment in later passes
|
|
if(pi>=Math.floor(passes.length*0.4)){
|
|
bg.push({t:t,f:f*1.5,d:p.d*0.4,i:'woodwind',v:p.v*0.25});
|
|
}
|
|
if(pi>=Math.floor(passes.length*0.7)&&ni%2===0){
|
|
bg.push({t:t,f:root||theme[0],d:p.d*0.5,i:'timpani',v:p.v*0.4});
|
|
}
|
|
mel.push({t:t,l:lanes[ni%lanes.length]});t+=p.d;
|
|
});
|
|
t+=0.3;
|
|
});
|
|
var r=root||theme[0];
|
|
for(var c=0;c<3;c++){bg.push({t:t,f:r,d:0.3,i:'brass',v:0.6});bg.push({t:t,f:r*2,d:0.3,i:'brass',v:0.5});bg.push({t:t,f:r/2,d:0.3,i:'timpani',v:0.55});mel.push({t:t,l:4});t+=0.4;}
|
|
bg.push({t:t,f:r,d:1.5,i:'brass',v:0.55});bg.push({t:t,f:r*2,d:1.5,i:'brass',v:0.45});mel.push({t:t,l:4});
|
|
return {n:name,composer:"Claude.ai",bpm:bpm,duration:Math.ceil(t+2),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
|
|
// ── Pixel Quest (Platformer Theme) ──
|
|
var platformer=chip("Pixel Quest",150,
|
|
[C4,E4,G4,C5,G4,E4,C4,D4,F4,A4,F4,D4,E4,G4,B4,G4,C4,E4,G4,E4,C4,G4,E4,C4],
|
|
[0,2,3,0,3,2,0,1,2,1,2,1,2,3,0,3,0,2,3,2,0,3,2,0],
|
|
[{d:0.20,v:0.22,i:'brass'},{d:0.18,v:0.28,i:'brass'},{d:0.16,v:0.35,i:'brass'},{d:0.15,v:0.42,i:'brass'},{d:0.14,v:0.48,i:'brass'},{d:0.13,v:0.55,i:'brass'}],
|
|
C4);
|
|
|
|
// ── Starfield (Space Shooter Theme) ──
|
|
var spaceshooter=chip("Starfield",160,
|
|
[E4,E4,G4,A4,G4,E4,D4,E4,G4,A4,B4,A4,G4,E4,D4,C4,E4,G4,B4,D5,B4,G4,E4,D4],
|
|
[2,2,3,0,3,2,1,2,3,0,1,0,3,2,1,0,2,3,1,0,1,3,2,1],
|
|
[{d:0.18,v:0.22,i:'brass'},{d:0.16,v:0.3,i:'brass'},{d:0.15,v:0.38,i:'brass'},{d:0.14,v:0.44,i:'brass'},{d:0.13,v:0.5,i:'brass'},{d:0.12,v:0.56,i:'brass'}],
|
|
E4);
|
|
|
|
// ── Dragon's Keep (RPG Overworld) ──
|
|
var rpg=chip("Dragon's Keep",110,
|
|
[A3,C4,E4,A4,G4,F4,E4,D4,C4,E4,G4,C5,B4,A4,G4,E4,F4,A4,C5,A4,F4,D4,E4,A3],
|
|
[0,1,2,0,3,1,2,0,1,2,3,0,3,0,3,2,1,0,2,0,1,0,2,0],
|
|
[{d:0.30,v:0.18,i:'brass'},{d:0.28,v:0.24,i:'brass'},{d:0.26,v:0.32,i:'brass'},{d:0.24,v:0.4,i:'brass'},{d:0.22,v:0.48,i:'brass'},{d:0.20,v:0.55,i:'brass'}],
|
|
A3);
|
|
|
|
// ── Turbo Dash (Racing Theme) ──
|
|
var racing=chip("Turbo Dash",170,
|
|
[G4,B4,D5,G5,D5,B4,G4,A4,C5,E5,C5,A4,G4,B4,D5,B4,G4,D4,G4,B4,D5,G4,B4,D5],
|
|
[0,3,1,0,1,3,0,1,2,0,2,1,0,3,1,3,0,1,0,3,1,0,3,1],
|
|
[{d:0.16,v:0.25,i:'brass'},{d:0.14,v:0.32,i:'brass'},{d:0.13,v:0.4,i:'brass'},{d:0.12,v:0.46,i:'brass'},{d:0.11,v:0.52,i:'brass'},{d:0.10,v:0.58,i:'brass'}],
|
|
G4);
|
|
|
|
// ── Dungeon Crawl (Dark Adventure) ──
|
|
var dungeon=chip("Dungeon Crawl",100,
|
|
[D4,F4,A4,D4,Eb4,G4,Bb4,Eb4,D4,F4,Ab4,D4,C4,Eb4,G4,C4,D4,F4,A4,F4,D4,A3,D4,F4],
|
|
[0,2,0,0,1,3,2,1,0,2,0,0,1,2,3,1,0,2,0,2,0,4,0,2],
|
|
[{d:0.32,v:0.2,i:'brass'},{d:0.30,v:0.28,i:'brass'},{d:0.28,v:0.35,i:'brass'},{d:0.26,v:0.42,i:'brass'},{d:0.24,v:0.48,i:'brass'},{d:0.22,v:0.55,i:'brass'}],
|
|
D4);
|
|
|
|
// ── Victory Fanfare (Boss Defeated) ──
|
|
var victory=chip("Victory Fanfare",130,
|
|
[C4,C4,C4,E4,G4,E4,G4,A4,G4,E4,C4,E4,G4,C5,G4,E4,C5,D5,E5,C5,G4,E4,C4,C5],
|
|
[0,0,0,2,3,2,3,0,3,2,0,2,3,0,3,2,0,1,2,0,3,2,0,0],
|
|
[{d:0.22,v:0.25,i:'brass'},{d:0.20,v:0.32,i:'brass'},{d:0.18,v:0.4,i:'brass'},{d:0.16,v:0.48,i:'brass'},{d:0.14,v:0.55,i:'brass'},{d:0.12,v:0.62,i:'brass'}],
|
|
C4);
|
|
|
|
return [platformer,spaceshooter,rpg,racing,dungeon,victory];
|
|
})();
|
|
|
|
|
|
// ── FOLK (public domain traditional songs) ──
|
|
const FOLK_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,Ab3=NT.Ab3,A3=NT.A3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,Gb4=NT.Gb4,G4=NT.G4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5,F5=NT.F5,G5=NT.G5;
|
|
|
|
// Folk piece helper
|
|
function folk(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.25;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<Math.floor(passes*0.3)?'woodwind':rep<Math.floor(passes*0.7)?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
bg.push({t:t,f:f,d:d*0.85,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.35});
|
|
if(rep>Math.floor(passes*0.6))bg.push({t:t,f:f,d:d*0.85,i:'strings',v:v*0.4});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*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:r/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
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],
|
|
[1,1,2,2,1,1,2,2,1,1,2,1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,1,1,1,3],
|
|
5,C4);
|
|
|
|
// ── Greensleeves ──
|
|
var greensleeves=folk("Greensleeves","English Traditional (1580)",100,
|
|
[A3,C4,D4,E4,F4,E4,D4,B3,G3,A3,B3,C4,A3,A3,Ab3,A3,B3,Ab3,A3,C4,D4,E4,F4,E4,D4,B3,G3,A3,B3,C4,B3,A3],
|
|
[0,1,0,2,1,2,0,3,3,0,3,1,0,0,1,0,3,3,0,1,0,2,1,2,0,3,3,0,3,1,3,0],
|
|
[2,1,2,2,1,2,2,1,2,1,1,2,2,1,1,1,2,2,2,1,2,2,1,2,2,1,2,1,1,1,1,3],
|
|
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],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3],
|
|
3,C4);
|
|
|
|
// ── Red River Valley ──
|
|
var redriver=folk("Red River Valley","American Traditional (1870s)",85,
|
|
[C4,D4,E4,G4,G4,E4,D4,C4,D4,E4,F4,E4,D4,C4,E4,G4,A4,G4,E4,D4,C4,D4,E4,C4],
|
|
[0,1,2,3,3,2,1,0,1,2,1,2,1,0,2,3,0,3,2,1,0,1,2,0],
|
|
[2,1,1,3,1,1,1,2,1,1,1,2,1,2,1,2,2,1,1,1,2,1,2,3],
|
|
4,C4);
|
|
|
|
// ── Home on the Range ──
|
|
var homerange=folk("Home on the Range","American Traditional (1870s)",95,
|
|
[C4,E4,G4,A4,G4,E4,C4,D4,F4,A4,G4,F4,E4,D4,C4,E4,G4,C5,A4,G4,E4,D4,C4,C4],
|
|
[0,2,3,0,3,2,0,1,1,0,3,1,2,1,0,2,3,0,0,3,2,1,0,0],
|
|
[2,1,2,2,1,1,2,1,1,2,1,1,2,1,2,1,2,2,1,1,1,1,2,3],
|
|
4,C4);
|
|
|
|
// ── Yankee Doodle ──
|
|
var yankee=folk("Yankee Doodle","American Traditional (1770s)",130,
|
|
[C4,C4,D4,E4,C4,E4,D4,G3,C4,C4,D4,E4,C4,B3,C4,C4,D4,E4,F4,E4,D4,C4,B3,G3,A3,B3,C4,C4],
|
|
[0,0,1,2,0,2,1,3,0,0,1,2,0,3,0,0,1,2,1,2,1,0,3,3,0,3,0,0],
|
|
[1,1,1,1,1,1,2,2,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,2,3],
|
|
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],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3],
|
|
5,G4);
|
|
|
|
// ── Danny Boy ──
|
|
var dannyboy=folk("Danny Boy","Irish Traditional (1910s)",72,
|
|
[C4,E4,F4,G4,A4,G4,A4,C5,B4,A4,G4,F4,G4,C4,E4,F4,G4,A4,G4,A4,C5,D5,C5,A4,G4,A4,A4,C5,D5,E5,D5,C5,A4,C5,B4,A4,G4,A4,G4,E4,C4,E4,F4,G4,A4,G4,F4,E4,C4],
|
|
[0,2,1,3,0,3,0,0,3,0,3,1,3,0,2,1,3,0,3,0,0,1,0,0,3,0,0,0,1,2,1,0,0,0,3,0,3,0,3,2,0,2,1,3,0,3,1,2,0],
|
|
[2,1,1,2,1,1,1,2,1,1,1,2,3,2,1,1,2,1,1,1,2,1,1,1,2,3,1,1,2,2,1,1,1,1,1,1,2,1,1,1,2,1,1,2,1,1,1,2,4],
|
|
3,C4);
|
|
|
|
return [ohsusanna,greensleeves,camptown,redriver,homerange,yankee,turkey,dannyboy];
|
|
})();
|
|
|
|
// Synth genre lookup — all genres that use bg+melody synth mode
|
|
// ── CHRISTMAS (public domain carols) ──
|
|
const CHRISTMAS_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,A3=NT.A3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,G4=NT.G4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,Eb5=NT.Eb5,E5=NT.E5,F5=NT.F5,G5=NT.G5;
|
|
function carol(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.25;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<Math.floor(passes*0.3)?'woodwind':rep<Math.floor(passes*0.7)?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
bg.push({t:t,f:f,d:d*0.85,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.35});
|
|
if(rep>Math.floor(passes*0.5))bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.3});
|
|
if(rep>Math.floor(passes*0.7))bg.push({t:t,f:f,d:d*0.85,i:'brass',v:v*0.35});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*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:r/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4});
|
|
return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
var jingle=carol("Jingle Bells","J. Pierpont (1857)",140,
|
|
[E4,E4,E4,E4,E4,E4,E4,G4,C4,D4,E4,F4,F4,F4,F4,F4,E4,E4,E4,E4,D4,D4,E4,D4,G4,E4,E4,E4,E4,E4,E4,E4,G4,C4,D4,E4,F4,F4,F4,F4,F4,E4,E4,G4,G4,F4,D4,C4],
|
|
[2,2,2,2,2,2,2,3,0,1,2,1,1,1,1,1,2,2,2,2,1,1,2,1,3,2,2,2,2,2,2,2,3,0,1,2,1,1,1,1,1,2,2,3,3,1,1,0],
|
|
[1,1,2,1,1,2,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,1,4,1,1,2,1,1,2,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
var silent=carol("Silent Night","F. Gruber (1818)",72,
|
|
[G4,A4,G4,E4,G4,A4,G4,E4,D5,D5,B4,C5,C5,G4,A4,A4,C5,B4,A4,G4,A4,G4,E4,A4,A4,C5,B4,A4,G4,A4,G4,E4,D5,D5,F5,D5,B4,C5,E5,C5,G4,E4,G4,F4,D4,C4],
|
|
[3,0,3,2,3,0,3,2,0,0,3,0,0,3,0,0,0,3,0,3,0,3,2,0,0,0,3,0,3,0,3,2,0,0,1,0,3,0,2,0,3,2,3,1,1,0],
|
|
[2,1,2,3,2,1,2,3,2,1,3,2,1,3,2,1,2,1,2,2,1,2,3,2,1,2,1,2,2,1,2,3,2,1,2,1,3,2,2,2,2,1,1,1,1,4],
|
|
3,C4);
|
|
var deck=carol("Deck the Halls","Welsh (1862)",130,
|
|
[C5,Bb4,A4,G4,F4,G4,A4,F4,G4,A4,Bb4,G4,A4,G4,F4,E4,D4,E4,F4,G4,A4,G4,F4,E4,F4,G4,A4,Bb4,C5,A4,G4,F4],
|
|
[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],
|
|
[2,1,1,2,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,2,1,1,4],
|
|
4,F4);
|
|
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],
|
|
[1,1,1,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,1,4],
|
|
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],
|
|
[1,2,2,2,0,1,1,1,1,2,0,0,2,1,2,2,2,0,1,1,1,1,2,0,0,2],
|
|
[1,2,1,1,1,2,1,1,1,1,1,1,3,1,2,1,1,1,2,1,1,1,1,1,1,4],
|
|
4,Bb4);
|
|
var firstnoel=carol("The First Noel","English (1823)",95,
|
|
[E4,D4,C4,D4,E4,F4,G4,A4,B4,C5,B4,A4,G4,A4,B4,C5,B4,A4,G4,A4,B4,C5,G4,F4,E4,D4,C4,D4,C4],
|
|
[2,1,0,1,2,1,3,0,3,0,3,0,3,0,3,0,3,0,3,0,3,0,3,1,2,1,0,1,0],
|
|
[1,1,2,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1,4],
|
|
4,C4);
|
|
var joy=carol("Joy to the World","G.F. Handel (1719)",120,
|
|
[C5,B4,A4,G4,F4,E4,D4,C4,G4,A4,A4,B4,B4,C5,C5,C5,B4,A4,G4,G4,F4,E4,C5,C5,B4,A4,G4,G4,F4,E4,D4,C4],
|
|
[0,3,0,3,1,2,1,0,3,0,0,3,3,0,0,0,3,0,3,3,1,2,0,0,3,0,3,3,1,2,1,0],
|
|
[2,1,1,2,1,1,1,3,2,1,2,1,2,1,2,1,1,1,2,1,1,3,2,1,1,1,2,1,1,1,1,4],
|
|
4,C4);
|
|
var away=carol("Away in a Manger","J.Murray (1885)",80,
|
|
[C4,F4,F4,F4,G4,A4,F4,F4,A4,G4,F4,G4,F4,D4,C4,C4,F4,F4,F4,G4,A4,F4,F4,A4,G4,F4,G4,F4,D4,C4,Bb4,A4,F4],
|
|
[0,1,1,1,3,0,1,1,0,3,1,3,1,1,0,0,1,1,1,3,0,1,1,0,3,1,3,1,1,0,2,0,1],
|
|
[1,2,1,1,1,2,1,1,2,1,1,1,2,1,3,1,2,1,1,1,2,1,1,2,1,1,1,2,1,2,1,1,4],
|
|
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],
|
|
[1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4],
|
|
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],
|
|
[1,2,1,1,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,4],
|
|
4,F4);
|
|
// ── 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],
|
|
[2,1,2,1,2,3,1,1,1,1,1,1,2,1,1,1,2,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,4],
|
|
3,C4);
|
|
|
|
// ── God Rest Ye Merry Gentlemen (E minor / A minor) ──
|
|
var godrest=carol("God Rest Ye Merry Gentlemen","English (1500s)",120,
|
|
[E4,E4,E4,D4,C4,D4,E4,F4,G4,A4,Bb4,A4,G4,F4,E4,D4,C4,D4,E4,F4,E4,D4,C4,B3,C4,G4,G4,F4,E4,F4,G4,A4,Bb4,A4,G4,F4,E4],
|
|
[2,2,2,1,0,1,2,1,3,0,2,0,3,1,2,1,0,1,2,1,2,1,0,3,0,3,3,1,2,1,3,0,2,0,3,1,2],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,E4);
|
|
|
|
// ── What Child Is This (Greensleeves melody as a carol) ──
|
|
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,2,2,1,2,2,2,1,2,1,2,1,2,3,2,2,2,1,2,1,1,1,2,1,4],
|
|
4,A4);
|
|
|
|
// ── Angels We Have Heard on High ──
|
|
var angels=carol("Angels We Have Heard on High","French (1862)",110,
|
|
[E4,E4,E4,E4,F4,D4,E4,F4,E4,E4,F4,D4,E4,F4,G4,F4,E4,D4,E4,C4,D4,E4,F4,D4,E4,F4,G4,F4,E4,D4,E4,C4,C4],
|
|
[2,2,2,2,1,1,2,1,2,2,1,1,2,1,3,1,2,1,2,0,1,2,1,1,2,1,3,1,2,1,2,0,0],
|
|
[1,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,2,1,1,1,2,1,1,2,1,1,1,1,2,4],
|
|
4,C4);
|
|
|
|
// ── O Holy Night (in C major) ──
|
|
var oholynight=carol("O Holy Night","A. Adam (1847)",72,
|
|
[C4,F4,F4,F4,E4,F4,A4,A4,G4,F4,F4,Bb4,A4,G4,F4,G4,A4,G4,F4,E4,F4,C5,C5,Bb4,A4,G4,A4,Bb4,A4,G4,F4],
|
|
[0,1,1,1,2,1,0,0,3,1,1,2,0,3,1,3,0,3,1,2,1,0,0,2,0,3,0,2,0,3,1],
|
|
[2,2,1,1,1,1,2,1,1,2,1,2,1,1,1,1,2,1,1,1,3,2,1,1,1,1,1,1,1,1,4],
|
|
3,F4);
|
|
|
|
// ── Rudolph the Red-Nosed Reindeer (in C major) ──
|
|
var rudolph=carol("Rudolph the Red-Nosed Reindeer","J. Marks (1949)",130,
|
|
[G4,A4,G4,E4,C5,A4,G4,G4,A4,G4,A4,G4,C5,B4,F4,A4,G4,F4,D4,E4,F4,D4,G4,A4,G4,E4,C5,A4,G4,G4,A4,G4,A4,G4,A4,D5,C5],
|
|
[3,0,3,2,0,0,3,3,0,3,0,3,0,3,1,0,3,1,1,2,1,1,3,0,3,2,0,0,3,3,0,3,0,3,0,1,0],
|
|
[1,1,1,1,2,1,2,1,1,1,1,1,2,3,1,1,1,1,1,1,1,3,1,1,1,1,2,1,2,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
|
|
// ── Frosty the Snowman (in C major) ──
|
|
var frosty=carol("Frosty the Snowman","S. Nelson (1950)",125,
|
|
[G4,E4,F4,G4,C5,B4,A4,G4,A4,G4,F4,E4,F4,A4,G4,F4,E4,D4,C4,C4,G4,E4,F4,G4,C5,B4,A4,G4,A4,G4,F4,E4,D4,C4],
|
|
[3,2,1,3,0,3,0,3,0,3,1,2,1,0,3,1,2,1,0,0,3,2,1,3,0,3,0,3,0,3,1,2,1,0],
|
|
[1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
|
|
// ── Winter Wonderland (in C major) ──
|
|
var winter=carol("Winter Wonderland","F. Bernard (1934)",120,
|
|
[E4,F4,G4,G4,A4,G4,F4,E4,D4,E4,F4,F4,G4,F4,E4,D4,C4,E4,F4,G4,G4,A4,G4,F4,E4,D4,E4,F4,F4,G4,F4,E4,D4,C4],
|
|
[2,1,3,3,0,3,1,2,1,2,1,1,3,1,2,1,0,2,1,3,3,0,3,1,2,1,2,1,1,3,1,2,1,0],
|
|
[1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,3,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,4],
|
|
3,C4);
|
|
|
|
// ── Good King Wenceslas (in F major) ──
|
|
var wenceslas=carol("Good King Wenceslas","Finnish (1582)",110,
|
|
[F4,F4,F4,G4,F4,F4,C4,D4,E4,F4,F4,G4,F4,F4,C4,C5,Bb4,A4,G4,A4,Bb4,A4,G4,F4,G4,A4,Bb4,C5,Bb4,A4,G4,F4],
|
|
[1,1,1,3,1,1,0,1,2,1,1,3,1,1,0,0,2,0,3,0,2,0,3,1,3,0,2,0,2,0,3,1],
|
|
[1,1,1,1,1,1,2,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,2,1,1,1,2,1,1,1,4],
|
|
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],
|
|
[2,1,2,1,1,1,2,1,2,1,1,1,1,1,2,3,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,4],
|
|
4,E4);
|
|
|
|
return [jingle,silent,deck,wish,ocometree,firstnoel,joy,away,twelve,hark,ocome,godrest,whatchild,angels,oholynight,rudolph,frosty,winter,wenceslas,threekings];
|
|
})();
|
|
|
|
// ── SINGALONG (campfire, sea shanties, pub songs) ──
|
|
const SINGALONG_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,Ab3=NT.Ab3,A3=NT.A3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,E4=NT.E4,F4=NT.F4,G4=NT.G4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5,F5=NT.F5,G5=NT.G5;
|
|
function pub(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.25;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.22+rep*0.07;
|
|
var inst=rep<Math.floor(passes*0.4)?'woodwind':rep<Math.floor(passes*0.7)?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
bg.push({t:t,f:f,d:d*0.85,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.35});
|
|
if(rep>Math.floor(passes*0.6))bg.push({t:t,f:f,d:d*0.85,i:'brass',v:v*0.4});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.5});bg.push({t:t,f:f,d:2.0,i:'strings',v:0.4});});
|
|
bg.push({t:t,f:r/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
var drunken=pub("Drunken Sailor","Sea Shanty (Trad.)",140,
|
|
[D4,D4,D4,D4,E4,F4,D4,D4,D4,D4,E4,D4,C4,D4,D4,D4,E4,F4,D4,D4,F4,E4,C4,A3,D4,D4,D4,D4,F4,A4,A4,G4,F4,E4,D4,C4,D4],
|
|
[0,0,0,0,2,1,0,0,0,0,2,0,1,0,0,0,2,1,0,0,1,2,1,0,0,0,0,0,1,0,0,3,1,2,0,1,0],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,3],
|
|
4,D4);
|
|
var molly=pub("Molly Malone","Irish (1880s)",100,
|
|
[C4,E4,E4,E4,F4,G4,G4,G4,A4,G4,F4,E4,D4,E4,F4,G4,A4,C5,C5,B4,A4,G4,F4,E4,D4,C4,E4,G4,C5,C5,B4,A4,G4,F4,E4,D4,C4],
|
|
[0,2,2,2,1,3,3,3,0,3,1,2,1,2,1,3,0,0,0,3,0,3,1,2,1,0,2,3,0,0,3,0,3,1,2,1,0],
|
|
[1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
var whiskey=pub("Whiskey in the Jar","Irish (Trad.)",125,
|
|
[G4,G4,A4,B4,B4,A4,G4,E4,D4,E4,G4,G4,A4,B4,D5,B4,A4,G4,E4,D4,E4,G4,A4,B4,A4,G4,E4,D4,E4,G4],
|
|
[3,3,0,3,3,0,3,2,1,2,3,3,0,3,1,3,0,3,2,1,2,3,0,3,0,3,2,1,2,3],
|
|
[1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,3],
|
|
5,G4);
|
|
var parting=pub("The Parting Glass","Irish/Scots (Trad.)",80,
|
|
[E4,G4,A4,B4,A4,G4,E4,D4,E4,G4,A4,B4,D5,B4,A4,G4,E4,G4,A4,B4,A4,G4,E4,D4,E4,G4,A4,B4,D5,B4,A4,G4],
|
|
[2,3,0,3,0,3,2,1,2,3,0,3,1,3,0,3,2,3,0,3,0,3,2,1,2,3,0,3,1,3,0,3],
|
|
[1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,3],
|
|
4,E4);
|
|
var amazing=pub("Amazing Grace","J. Newton (1779)",80,
|
|
[G4,C5,E5,C5,E5,D5,C5,A4,G4,G4,C5,E5,C5,E5,D5,G5,G5,E5,C5,C5,A4,G4,G4,C5,E5,C5,E5,D5,C5],
|
|
[3,0,2,0,2,1,0,0,3,3,0,2,0,2,1,3,3,2,0,0,0,3,3,0,2,0,2,1,0],
|
|
[1,2,1,1,2,2,1,2,2,1,2,1,1,2,4,2,1,2,1,2,1,2,1,2,1,1,2,2,4],
|
|
4,C4);
|
|
var auld=pub("Auld Lang Syne","R. Burns (1788)",95,
|
|
[C4,F4,F4,F4,A4,G4,F4,G4,A4,F4,F4,A4,C5,D5,D5,D5,C5,A4,F4,F4,G4,A4,G4,F4,D4,C4],
|
|
[0,1,1,1,0,3,1,3,0,1,1,0,0,1,1,1,0,0,1,1,3,0,3,1,1,0],
|
|
[1,2,1,1,1,2,1,1,1,2,1,1,1,4,1,1,1,2,1,1,1,2,1,2,1,4],
|
|
4,F4);
|
|
var saints=pub("When the Saints Go Marching In","Spiritual (Trad.)",120,
|
|
[C4,E4,F4,G4,C4,E4,F4,G4,C4,E4,F4,G4,E4,C4,E4,D4,E4,E4,D4,C4,C4,E4,G4,G4,F4,E4,F4,G4,E4,C4,D4,C4],
|
|
[0,2,1,3,0,2,1,3,0,2,1,3,2,0,2,1,2,2,1,0,0,2,3,3,1,2,1,3,2,0,1,0],
|
|
[1,1,1,3,1,1,1,3,1,1,1,2,1,1,1,2,1,1,1,2,1,1,2,1,2,1,1,2,1,1,1,4],
|
|
4,C4);
|
|
var clementine=pub("Oh My Darling, Clementine","P. Montrose (1884)",110,
|
|
[C4,C4,C4,G3,E4,E4,E4,C4,C4,E4,G4,G4,F4,E4,F4,G4,G4,F4,E4,F4,E4,D4,C4,D4,E4,F4,F4,E4,D4,E4,C4],
|
|
[0,0,0,3,2,2,2,0,0,2,3,3,1,2,1,3,3,1,2,1,2,1,0,1,2,1,1,2,1,2,0],
|
|
[1,1,1,2,1,1,1,2,1,1,2,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,4],
|
|
4,C4);
|
|
var scarborough=pub("Scarborough Fair","English (Medieval)",85,
|
|
[D4,D4,A4,A4,D5,D5,C5,D5,E5,D5,C5,A4,B4,A4,G4,B4,A4,G4,F4,F4,G4,A4,F4,E4,D4,E4,F4,D4,D4,F4,G4,A4,G4,F4,E4,D4],
|
|
[0,0,0,0,1,1,0,1,2,1,0,0,3,0,3,3,0,3,1,1,3,0,1,2,0,2,1,0,0,1,3,0,3,1,2,0],
|
|
[3,3,2,1,3,1,1,1,3,2,1,1,1,1,2,1,3,3,1,2,1,1,1,1,2,1,3,3,1,1,1,3,1,1,1,4],
|
|
4,D4);
|
|
var bonnie=pub("My Bonnie Lies Over the Ocean","Scottish (1881)",120,
|
|
[G4,C5,C5,D5,E5,E5,D5,C5,A4,G4,C5,C5,D5,E5,G5,E5,D5,G4,C5,C5,D5,E5,E5,D5,C5,A4,A4,G4,F4,E4,F4,G4,E4,C4,E5,G5,G5,G5,E5,C5,C5,D5,E5,G5,G5,G5,F5,E5,D5,C5],
|
|
[3,0,0,1,2,2,1,0,0,3,0,0,1,2,3,2,1,3,0,0,1,2,2,1,0,0,0,3,1,2,1,3,2,0,2,3,3,3,2,0,0,1,2,3,3,3,1,2,1,0],
|
|
[1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,4,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
// ── Wellerman (accurate melody with driving stomp beat) ──
|
|
var wellerman=(function(){
|
|
var bg=[],mel=[],t=0,q=0.32;
|
|
function vox(t,f,d,v,inst){bg.push({t:t,f:f,d:d,i:inst||'strings',v:v||0.35});}
|
|
function stomp(t,v){bg.push({t:t,f:110,d:0.2,i:'timpani',v:v||0.4});}
|
|
// Verse melody (Am): "There once was a ship that put to sea..."
|
|
var verse1=[E4,E4,E4,E4,A4,A4,G4,E4];
|
|
var verse2=[E4,E4,E4,E4,A4,A4,G4,E4];
|
|
var verse3=[A4,A4,A4,A4,C5,C5,B4,A4];
|
|
var verse4=[E4,E4,G4,G4,E4,D4];
|
|
var vL1=[2,2,2,2,0,0,3,2],vL2=[2,2,2,2,0,0,3,2],vL3=[0,0,0,0,1,1,3,0],vL4=[2,2,3,3,2,1];
|
|
var vD1=[1,1,1,1,1,1,1,2],vD2=[1,1,1,1,1,1,1,2],vD3=[1,1,1,1,1,1,1,2],vD4=[1,1,1,1,1,3];
|
|
// Chorus melody: "Soon may the Wellerman come..."
|
|
var ch1=[A4,C5,C5,C5,D5,E5];
|
|
var ch2=[E5,D5,C5,D5,E5,C5];
|
|
var ch3=[A4,C5,C5,C5,D5,E5];
|
|
var ch4=[E5,D5,C5,B4,A4];
|
|
var cL1=[0,1,1,1,1,2],cL2=[2,1,0,1,2,0],cL3=[0,1,1,1,1,2],cL4=[2,1,0,3,0];
|
|
var cD1=[1,1,1,1,1,2],cD2=[1,1,1,1,1,2],cD3=[1,1,1,1,1,2],cD4=[1,1,1,1,4];
|
|
function playLine(notes,lanes,durs,v,inst){
|
|
notes.forEach(function(f,i){
|
|
var d=durs[i]*q;
|
|
vox(t,f,d*0.85,v,inst);
|
|
if(v>0.3)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.4});
|
|
mel.push({t:t,l:lanes[i]});
|
|
// Driving stomp on every beat
|
|
if(durs[i]>=1)stomp(t,v*0.8);
|
|
t+=d;
|
|
});
|
|
}
|
|
// 3 full rounds: verse+chorus, each building
|
|
var rounds=[
|
|
{v:0.3,i:'woodwind'},{v:0.4,i:'strings'},{v:0.55,i:'brass'}
|
|
];
|
|
rounds.forEach(function(r,ri){
|
|
// Add chord backing from round 2
|
|
if(ri>0){
|
|
var chordT=t;
|
|
[A3,A3,A3,A3,A3,A3,E3,E3].forEach(function(f,ci){
|
|
bg.push({t:chordT+ci*q*4,f:f,d:q*3.5,i:'strings',v:r.v*0.3});
|
|
});
|
|
}
|
|
// Add choir from round 3
|
|
if(ri>1){
|
|
var choirNotes=verse1.concat(verse2,verse3,verse4,ch1,ch2,ch3,ch4);
|
|
var choirT=t;
|
|
var allDurs=vD1.concat(vD2,vD3,vD4,cD1,cD2,cD3,cD4);
|
|
choirNotes.forEach(function(f,i){bg.push({t:choirT,f:f,d:allDurs[i]*q*0.8,i:'choir',v:r.v*0.3});choirT+=allDurs[i]*q;});
|
|
}
|
|
// Verse
|
|
playLine(verse1,vL1,vD1,r.v,r.i);
|
|
playLine(verse2,vL2,vD2,r.v,r.i);
|
|
playLine(verse3,vL3,vD3,r.v,r.i);
|
|
playLine(verse4,vL4,vD4,r.v,r.i);
|
|
t+=q*0.5;
|
|
// Chorus (bigger)
|
|
playLine(ch1,cL1,cD1,r.v*1.2,r.i);
|
|
playLine(ch2,cL2,cD2,r.v*1.2,r.i);
|
|
playLine(ch3,cL3,cD3,r.v*1.2,r.i);
|
|
playLine(ch4,cL4,cD4,r.v*1.2,r.i);
|
|
t+=q;
|
|
});
|
|
// Finale: big Am chord
|
|
[A3,C4,E4,A4].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'brass',v:0.55});bg.push({t:t,f:f,d:2.5,i:'choir',v:0.4});});
|
|
bg.push({t:t,f:A3/2,d:2.0,i:'timpani',v:0.6});mel.push({t:t,l:4});
|
|
return {n:"Wellerman",composer:"NZ Sea Shanty (1860s)",bpm:125,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
})();
|
|
// ── Leave Her Johnny ──
|
|
var leavejohnny=pub("Leave Her Johnny","Sea Shanty (Trad.)",105,
|
|
[A3,A3,C4,D4,E4,E4,D4,C4,A3,G3,A3,C4,D4,E4,G4,E4,D4,C4,A3,G3,A3,C4,E4,D4,C4,A3],
|
|
[0,0,1,0,2,2,0,1,0,3,0,1,0,2,3,2,0,1,0,3,0,1,2,0,1,0],
|
|
[1,1,1,1,2,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,3],
|
|
5,A3);
|
|
// ── Blow the Man Down ──
|
|
var blowman=pub("Blow the Man Down","Sea Shanty (Trad.)",120,
|
|
[C4,E4,G4,G4,F4,E4,D4,C4,D4,E4,F4,E4,D4,C4,C4,E4,G4,C5,B4,A4,G4,F4,E4,D4,C4],
|
|
[0,2,3,3,1,2,1,0,1,2,1,2,1,0,0,2,3,0,3,0,3,1,2,1,0],
|
|
[1,1,2,1,1,1,1,2,1,1,1,1,1,3,1,1,2,1,1,1,1,1,1,1,4],
|
|
5,C4);
|
|
// ── Randy Dandy Oh ──
|
|
var randy=pub("Randy Dandy Oh","Sea Shanty (Trad.)",130,
|
|
[G4,G4,A4,B4,B4,A4,G4,E4,D4,G4,G4,A4,B4,D5,B4,A4,G4,E4,D4,G4,A4,B4,D5,B4,G4],
|
|
[3,3,0,3,3,0,3,2,1,3,3,0,3,1,3,0,3,2,1,3,0,3,1,3,3],
|
|
[1,1,1,2,1,1,1,1,2,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,4],
|
|
5,G4);
|
|
// ── South Australia ──
|
|
var southaus=pub("South Australia","Sea Shanty (Trad.)",115,
|
|
[C4,C4,D4,E4,G4,G4,E4,D4,C4,E4,G4,A4,G4,E4,C4,D4,E4,G4,E4,D4,C4,C4,E4,G4,E4,C4],
|
|
[0,0,1,2,3,3,2,1,0,2,3,0,3,2,0,1,2,3,2,1,0,0,2,3,2,0],
|
|
[1,1,1,1,2,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,4],
|
|
5,C4);
|
|
// ── Roll the Old Chariot ──
|
|
var chariot=pub("Roll the Old Chariot Along","Sea Shanty (Trad.)",120,
|
|
[C4,E4,G4,G4,G4,A4,G4,E4,C4,D4,E4,F4,E4,D4,C4,C4,E4,G4,C5,A4,G4,E4,D4,C4],
|
|
[0,2,3,3,3,0,3,2,0,1,2,1,2,1,0,0,2,3,0,0,3,2,1,0],
|
|
[1,1,1,1,1,1,1,1,2,1,1,1,1,1,3,1,1,1,2,1,1,1,1,4],
|
|
5,C4);
|
|
// ── Haul Away Joe ──
|
|
var hauljoe=pub("Haul Away Joe","Sea Shanty (Trad.)",115,
|
|
[D4,D4,F4,A4,A4,G4,F4,D4,D4,F4,A4,C5,A4,G4,F4,D4,F4,A4,G4,F4,D4,C4,D4],
|
|
[0,0,1,0,0,3,1,0,0,1,0,0,0,3,1,0,1,0,3,1,0,1,0],
|
|
[1,1,1,2,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,2,1,4],
|
|
5,D4);
|
|
// ── Spanish Ladies ──
|
|
var spanish=pub("Spanish Ladies","Royal Navy Shanty (1790s)",110,
|
|
[E4,E4,D4,C4,D4,E4,F4,G4,A4,G4,F4,E4,D4,C4,D4,E4,F4,E4,D4,C4,B3,A3,G3,A3,B3,C4,D4,E4],
|
|
[2,2,1,0,1,2,1,3,0,3,1,2,1,0,1,2,1,2,1,0,3,0,3,0,3,0,1,2],
|
|
[1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
5,C4);
|
|
// ── The Skye Boat Song ──
|
|
var skye=pub("The Skye Boat Song","Scottish (1884)",85,
|
|
[C4,F4,A4,G4,F4,E4,F4,G4,A4,C5,A4,G4,F4,E4,D4,C4,F4,A4,G4,F4,E4,F4,G4,A4,F4,D4,C4],
|
|
[0,1,0,3,1,2,1,3,0,0,0,3,1,2,1,0,1,0,3,1,2,1,3,0,1,1,0],
|
|
[1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,2,2,1,1,1,1,1,2,1,1,1,4],
|
|
4,F4);
|
|
// ── Bella Ciao ──
|
|
var bella=pub("Bella Ciao","Italian Partisan Folk (1900s)",120,
|
|
[A3,B3,C4,D4,D4,C4,D4,E4,A3,B3,C4,D4,D4,C4,B3,A3,E4,E4,D4,E4,F4,F4,E4,D4,C4,B3,A3,C4,B3,A3],
|
|
[0,3,0,1,1,0,1,2,0,3,0,1,1,0,3,0,2,2,1,2,1,1,2,1,0,3,0,1,3,0],
|
|
[1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,4],
|
|
5,A3);
|
|
return [drunken,wellerman,molly,whiskey,parting,leavejohnny,blowman,randy,southaus,chariot,hauljoe,spanish,skye,bella,amazing,auld,saints,clementine,scarborough,bonnie];
|
|
})();
|
|
|
|
// ── SPIRITUALS (traditional African American songs) ──
|
|
const SPIRITUAL_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,A3=NT.A3,Bb3=NT.Bb3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,G4=NT.G4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5;
|
|
function spirit(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.25;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<Math.floor(passes*0.3)?'woodwind':rep<Math.floor(passes*0.6)?'strings':'choir';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
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});
|
|
if(rep>Math.floor(passes*0.5))bg.push({t:t,f:f,d:d*0.85,i:'choir',v:v*0.35});
|
|
if(rep>Math.floor(passes*0.7))bg.push({t:t,f:f,d:d*0.85,i:'brass',v:v*0.3});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*2].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'choir',v:0.5});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.35});});
|
|
bg.push({t:t,f:r/2,d:2.0,i:'timpani',v:0.5});mel.push({t:t,l:4});
|
|
return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+3),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
var swing=spirit("Swing Low, Sweet Chariot","Spiritual (1862)",80,
|
|
[E4,C4,D4,E4,G4,G4,F4,E4,D4,C4,E4,C4,D4,E4,G4,G4,F4,E4,D4,C4,C4,C4,E4,G4,A4,G4,E4,C4,G4,F4,E4,D4,C4,C4,E4,G4,A4,G4,E4,C4,G4,F4,E4,D4,C4],
|
|
[2,0,1,2,3,3,1,2,1,0,2,0,1,2,3,3,1,2,1,0,0,0,2,3,0,3,2,0,3,1,2,1,0,0,2,3,0,3,2,0,3,1,2,1,0],
|
|
[2,2,1,1,2,1,1,1,1,3,2,2,1,1,2,1,1,1,1,3,1,1,1,2,1,1,1,2,1,1,1,1,3,1,1,1,2,1,1,2,1,1,1,1,4],
|
|
3,C4);
|
|
var godown=spirit("Go Down Moses","Spiritual (Trad.)",90,
|
|
[A3,C4,C4,D4,E4,E4,D4,C4,A3,C4,D4,E4,G4,E4,D4,C4,A3,C4,E4,G4,A4,G4,E4,D4,C4,A3],
|
|
[0,0,0,1,2,2,1,0,0,0,1,2,3,2,1,0,0,0,2,3,0,3,2,1,0,0],
|
|
[1,2,1,1,2,1,1,2,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,1,2,4],
|
|
5,A3);
|
|
var wade=spirit("Wade in the Water","Spiritual (Trad.)",100,
|
|
[E4,E4,G4,A4,B4,A4,G4,E4,D4,E4,G4,A4,B4,D5,B4,A4,G4,E4,D4,E4,G4,B4,A4,G4,E4],
|
|
[2,2,3,0,3,0,3,2,1,2,3,0,3,1,3,0,3,2,1,2,3,3,0,3,2],
|
|
[1,1,1,1,2,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,4],
|
|
5,E4);
|
|
var kumbaya=spirit("Kumbaya","Spiritual (Trad.)",75,
|
|
[C4,E4,F4,G4,G4,F4,E4,C4,C4,E4,F4,G4,A4,G4,E4,C4,E4,F4,G4,G4,F4,E4,D4,C4],
|
|
[0,2,1,3,3,1,2,0,0,2,1,3,0,3,2,0,2,1,3,3,1,2,1,0],
|
|
[1,1,1,2,1,1,2,2,1,1,1,2,1,2,1,1,1,1,2,1,1,1,1,4],
|
|
5,C4);
|
|
var michael=spirit("Michael Row the Boat Ashore","Spiritual (1860s)",90,
|
|
[C4,E4,F4,G4,A4,A4,G4,E4,C4,C4,E4,F4,G4,A4,A4,G4,E4,C4,D4,E4,F4,E4,D4,C4,D4,C4],
|
|
[0,2,1,3,0,0,3,2,0,0,2,1,3,0,0,3,2,0,1,2,1,2,1,0,1,0],
|
|
[1,1,1,2,2,1,1,1,2,3,1,1,1,2,2,1,1,2,1,1,1,1,1,2,1,4],
|
|
5,C4);
|
|
var wholeworld=spirit("He's Got the Whole World","Spiritual (Trad.)",110,
|
|
[C4,C4,E4,G4,G4,E4,G4,A4,G4,E4,C4,E4,G4,C5,A4,G4,E4,C4,E4,G4,E4,C4],
|
|
[0,0,2,3,3,2,3,0,3,2,0,2,3,0,0,3,2,0,2,3,2,0],
|
|
[1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,2,1,1,1,4],
|
|
5,C4);
|
|
var nobody=spirit("Nobody Knows the Trouble I've Seen","Spiritual (Trad.)",75,
|
|
[C4,E4,G4,A4,G4,E4,C4,D4,E4,G4,C5,A4,G4,E4,D4,C4,E4,G4,A4,C5,A4,G4,E4,D4,C4],
|
|
[0,2,3,0,3,2,0,1,2,3,0,0,3,2,1,0,2,3,0,0,0,3,2,1,0],
|
|
[1,1,2,1,1,2,2,1,1,2,1,1,1,2,1,2,1,2,1,2,1,1,1,1,4],
|
|
4,C4);
|
|
var battle=spirit("Battle Hymn of the Republic","J.W. Howe (1861)",115,
|
|
[C4,C4,C4,E4,E4,D4,C4,D4,E4,F4,E4,D4,C4,C4,E4,G4,G4,F4,E4,F4,G4,A4,G4,E4,C4,E4,D4,C4],
|
|
[0,0,0,2,2,1,0,1,2,1,2,1,0,0,2,3,3,1,2,1,3,0,3,2,0,2,1,0],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,2,1,1,4],
|
|
4,C4);
|
|
return [swing,godown,wade,kumbaya,michael,wholeworld,nobody,battle];
|
|
})();
|
|
|
|
// ── WORLD (folk songs from various cultures) ──
|
|
const WORLD_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,Ab3=NT.Ab3,A3=NT.A3,Bb3=NT.Bb3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,Gb4=NT.Gb4,G4=NT.G4,Ab4=NT.Ab4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5;
|
|
function world(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.25;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.2+rep*0.08;
|
|
var inst=rep<Math.floor(passes*0.3)?'woodwind':rep<Math.floor(passes*0.7)?'strings':'brass';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
bg.push({t:t,f:f,d:d*0.85,i:inst,v:v});
|
|
if(rep>1)bg.push({t:t,f:f/2,d:d*0.85,i:'bass',v:v*0.35});
|
|
if(rep>Math.floor(passes*0.6))bg.push({t:t,f:f,d:d*0.85,i:'brass',v:v*0.35});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.4;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'brass',v:0.5});bg.push({t:t,f:f,d:2.0,i:'strings',v:0.4});});
|
|
bg.push({t:t,f:r/2,d:1.5,i:'timpani',v:0.55});mel.push({t:t,l:4});
|
|
return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
var hava=world("Hava Nagila","Jewish/Israeli (1918)",130,
|
|
[E4,E4,F4,E4,D4,E4,E4,F4,Ab4,A4,B4,B4,A4,Ab4,F4,E4,E4,F4,Ab4,A4,B4,A4,Ab4,A4,B4,C5,D5,C5,B4,A4,Ab4,A4,B4,C5,D5,C5,B4,A4,Ab4,E4],
|
|
[2,2,1,2,1,2,2,1,0,0,3,3,0,0,1,2,2,1,0,0,3,0,0,0,3,0,1,0,3,0,0,0,3,0,1,0,3,0,0,2],
|
|
[1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,E4);
|
|
var cucaracha=world("La Cucaracha","Mexican (Trad.)",135,
|
|
[C4,C4,C4,F4,A4,C4,C4,C4,F4,A4,F4,F4,E4,E4,D4,D4,C4,C4,C4,G4,Bb4,C4,C4,C4,G4,Bb4,A4,G4,F4,E4,D4,C4],
|
|
[0,0,0,1,0,0,0,0,1,0,1,1,2,2,1,1,0,0,0,3,2,0,0,0,3,2,0,3,1,2,1,0],
|
|
[1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,1,2,1,1,1,1,2,1,1,1,1,1,4],
|
|
4,F4);
|
|
var frere=world("Fr\u00e8re Jacques","French (1780s)",110,
|
|
[C4,D4,E4,C4,C4,D4,E4,C4,E4,F4,G4,E4,F4,G4,G4,A4,G4,F4,E4,C4,G4,A4,G4,F4,E4,C4,C4,G3,C4,C4,G3,C4],
|
|
[0,1,2,0,0,1,2,0,2,1,3,2,1,3,3,0,3,1,2,0,3,0,3,1,2,0,0,3,0,0,3,0],
|
|
[1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,4],
|
|
4,C4);
|
|
var cielito=world("Cielito Lindo","Mexican (1882)",120,
|
|
[E4,E4,F4,G4,A4,G4,F4,E4,D4,E4,F4,E4,D4,C4,E4,G4,C5,C5,B4,A4,G4,F4,E4,D4,C4,E4,G4,C5],
|
|
[2,2,1,3,0,3,1,2,1,2,1,2,1,0,2,3,0,0,3,0,3,1,2,1,0,2,3,0],
|
|
[1,1,1,1,2,1,1,1,1,1,1,1,1,3,1,1,2,1,1,1,1,1,1,1,2,1,1,4],
|
|
4,C4);
|
|
var waltzing=world("Waltzing Matilda","Australian (1895)",110,
|
|
[G4,G4,A4,B4,D5,B4,A4,G4,E4,G4,A4,B4,A4,G4,E4,D4,G4,G4,A4,B4,D5,B4,A4,G4,E4,D4,G4],
|
|
[3,3,0,3,1,3,0,3,2,3,0,3,0,3,2,1,3,3,0,3,1,3,0,3,2,1,3],
|
|
[1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,4],
|
|
5,G4);
|
|
var osole=world("'O Sole Mio","Italian (1898)",90,
|
|
[C4,E4,E4,F4,E4,D4,C4,D4,E4,E4,F4,G4,A4,G4,F4,E4,E4,F4,G4,A4,Bb4,A4,G4,C5,C5,Bb4,A4,G4,F4,E4,F4,G4,A4,G4,F4,E4,D4,C4],
|
|
[0,2,2,1,2,1,0,1,2,2,1,3,0,3,1,2,2,1,3,0,2,0,3,0,0,2,0,3,1,2,1,3,0,3,1,2,1,0],
|
|
[2,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,1,1,1,2,1,1,2,2,1,1,1,1,2,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
var ballgame=world("Take Me Out to the Ball Game","American (1908)",125,
|
|
[C4,C4,A4,G4,E4,G4,D4,C4,C4,A4,G4,E4,G4,C4,C4,A4,G4,F4,E4,D4,E4,F4,D4,C4,A4,G4,E4,C4],
|
|
[0,0,0,3,2,3,1,0,0,0,3,2,3,0,0,0,3,1,2,1,2,1,1,0,0,3,2,0],
|
|
[1,1,2,1,1,2,1,2,1,2,1,1,3,1,1,2,1,1,1,1,1,1,2,1,1,1,1,4],
|
|
4,C4);
|
|
var mountain=world("She'll Be Coming Round the Mountain","American (1890s)",130,
|
|
[G4,G4,B4,B4,D5,D5,B4,G4,A4,B4,C5,C5,A4,A4,G4,B4,D5,D5,B4,G4,A4,C5,B4,A4,G4],
|
|
[3,3,3,3,1,1,3,3,0,3,0,0,0,0,3,3,1,1,3,3,0,0,3,0,3],
|
|
[1,1,2,1,2,1,1,2,1,1,2,1,2,1,2,1,2,1,1,2,1,1,1,1,4],
|
|
5,G4);
|
|
return [hava,cucaracha,frere,cielito,waltzing,osole,ballgame,mountain];
|
|
})();
|
|
|
|
// ── NURSERY RHYMES (very simple, beginner-friendly) ──
|
|
const NURSERY_PIECES=(function(){
|
|
var C4=NT.C4,D4=NT.D4,E4=NT.E4,F4=NT.F4,G4=NT.G4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,E5=NT.E5,G3=NT.G3,A3=NT.A3,B3=NT.B3;
|
|
function rhyme(name,origin,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.3;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.25+rep*0.08;
|
|
var inst=rep<1?'woodwind':'strings';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
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.25});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.5;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.4});});
|
|
mel.push({t:t,l:4});
|
|
return {n:name,composer:origin,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
// ── Twinkle Twinkle Little Star (same melody as Baa Baa Black Sheep) ──
|
|
var twinkle=rhyme("Twinkle Twinkle Little Star","Traditional (1760s)",75,
|
|
[C4,C4,G4,G4,A4,A4,G4,F4,F4,E4,E4,D4,D4,C4,G4,G4,F4,F4,E4,E4,D4,C4,C4,G4,G4,A4,A4,G4,F4,F4,E4,E4,D4,D4,C4],
|
|
[0,0,3,3,0,0,3,1,1,2,2,1,1,0,3,3,1,1,2,2,1,0,0,3,3,0,0,3,1,1,2,2,1,1,0],
|
|
[1,1,1,1,1,1,3,1,1,1,1,1,1,3,1,1,1,1,1,1,3,1,1,1,1,1,1,3,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Mary Had a Little Lamb ──
|
|
var mary=rhyme("Mary Had a Little Lamb","S.J. Hale (1830)",80,
|
|
[E4,D4,C4,D4,E4,E4,E4,D4,D4,D4,E4,G4,G4,E4,D4,C4,D4,E4,E4,E4,D4,D4,E4,D4,C4],
|
|
[2,1,0,1,2,2,2,1,1,1,2,3,3,2,1,0,1,2,2,2,1,1,2,1,0],
|
|
[1,1,1,1,1,1,3,1,1,3,1,1,3,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Happy Birthday ──
|
|
var birthday=rhyme("Happy Birthday","P.S. Hill (1893)",75,
|
|
[C4,C4,D4,C4,F4,E4,C4,C4,D4,C4,G4,F4,C4,C4,C5,A4,F4,E4,D4,Bb4,Bb4,A4,F4,G4,F4],
|
|
[0,0,1,0,1,2,0,0,1,0,3,1,0,0,0,0,1,2,1,2,2,0,1,3,1],
|
|
[1,1,2,2,2,3,1,1,2,2,2,3,1,1,2,1,1,1,2,1,1,1,1,2,4],
|
|
3,C4);
|
|
// ── Chopsticks ──
|
|
var chopsticks=rhyme("Chopsticks","Euphemia Allen (1877)",95,
|
|
[F4,G4,F4,G4,F4,G4,E4,G4,E4,G4,E4,G4,D4,F4,D4,F4,C4,E4,C4,E4,F4,G4,F4,G4,E4,G4,E4,G4,D4,F4,D4,F4,C4,E4,C4,E4],
|
|
[1,3,1,3,1,3,2,3,2,3,2,3,1,1,1,1,0,2,0,2,1,3,1,3,2,3,2,3,1,1,1,1,0,2,0,2],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Lavender's Blue ──
|
|
var lavender=rhyme("Lavender's Blue","English Traditional (1670s)",80,
|
|
[E4,E4,D4,E4,F4,G4,G4,A4,G4,F4,F4,E4,D4,E4,E4,E4,D4,E4,F4,G4,G4,A4,G4,F4,E4],
|
|
[2,2,1,2,1,3,3,0,3,1,1,2,1,2,2,2,1,2,1,3,3,0,3,1,2],
|
|
[1,1,1,1,1,1,1,1,1,2,1,1,1,3,1,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Shortnin' Bread ──
|
|
var shortnin=rhyme("Shortnin' Bread","Traditional (1900s)",95,
|
|
[E5,E5,D5,C5,D5,C5,A4,G4,A4,G4,A4,G4,E5,E5,D5,C5,D5,C5,A4,G4,A4,G4,C5],
|
|
[2,2,1,0,1,0,0,3,0,3,0,3,2,2,1,0,1,0,0,3,0,3,0],
|
|
[1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,1,4],
|
|
3,C4);
|
|
// ── London Bridge ──
|
|
var london=rhyme("London Bridge Is Falling Down","English (1744)",75,
|
|
[G4,A4,G4,F4,E4,F4,G4,D4,E4,F4,E4,F4,G4,G4,A4,G4,F4,E4,F4,G4,D4,G4,E4,C4],
|
|
[3,0,3,1,2,1,3,1,2,1,2,1,3,3,0,3,1,2,1,3,1,3,2,0],
|
|
[1,1,1,1,1,1,2,1,1,2,1,1,2,1,1,1,1,1,1,2,2,1,1,4],
|
|
3,C4);
|
|
// ── Old MacDonald ──
|
|
var macdonald=rhyme("Old MacDonald Had a Farm","English (1706)",80,
|
|
[C4,C4,C4,G4,A4,A4,G4,E4,E4,D4,D4,C4,G4,C4,C4,C4,G4,A4,A4,G4,E4,E4,D4,D4,C4],
|
|
[0,0,0,3,0,0,3,2,2,1,1,0,3,0,0,0,3,0,0,3,2,2,1,1,0],
|
|
[1,1,1,1,1,1,2,1,1,1,1,3,2,1,1,1,1,1,1,2,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Row Row Row Your Boat ──
|
|
var row=rhyme("Row Row Row Your Boat","English (1852)",70,
|
|
[C4,C4,C4,D4,E4,E4,D4,E4,F4,G4,C5,C5,C5,G4,G4,G4,E4,E4,E4,C4,C4,C4,G4,F4,E4,D4,C4],
|
|
[0,0,0,1,2,2,1,2,1,3,0,0,0,3,3,3,2,2,2,0,0,0,3,1,2,1,0],
|
|
[1,1,1,1,2,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Three Blind Mice ──
|
|
var mice=rhyme("Three Blind Mice","English (1609)",75,
|
|
[E4,D4,C4,E4,D4,C4,G4,F4,E4,G4,F4,E4,G4,A4,G4,F4,E4,D4,G4,A4,G4,F4,E4,D4,C4],
|
|
[2,1,0,2,1,0,3,1,2,3,1,2,3,0,3,1,2,1,3,0,3,1,2,1,0],
|
|
[1,1,2,1,1,2,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Itsy Bitsy Spider ──
|
|
var spider=rhyme("Itsy Bitsy Spider","English (1910s)",75,
|
|
[G4,C5,C5,C5,D5,E5,E5,D5,C5,D5,E5,C5,E5,E5,D5,D5,C5,G4,C5,C5,C5,D5,E5,E5,D5,C5,D5,E5,C5],
|
|
[3,0,0,0,1,2,2,1,0,1,2,0,2,2,1,1,0,3,0,0,0,1,2,2,1,0,1,2,0],
|
|
[1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Pop Goes the Weasel ──
|
|
var weasel=rhyme("Pop Goes the Weasel","English (1850s)",85,
|
|
[C4,E4,E4,F4,G4,G4,C4,E4,E4,D4,F4,A4,A4,G4,C4,E4,E4,F4,G4,G4,E4,C4,D4,F4,E4,C4],
|
|
[0,2,2,1,3,3,0,2,2,1,1,0,0,3,0,2,2,1,3,3,2,0,1,1,2,0],
|
|
[1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Hot Cross Buns ──
|
|
var hotcross=rhyme("Hot Cross Buns","English (1798)",65,
|
|
[E4,D4,C4,E4,D4,C4,C4,C4,C4,C4,D4,D4,D4,D4,E4,D4,C4],
|
|
[2,1,0,2,1,0,0,0,0,0,1,1,1,1,2,1,0],
|
|
[1,1,3,1,1,3,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
// ── Jack and Jill ──
|
|
var jack=rhyme("Jack and Jill","English (1765)",80,
|
|
[C4,C4,D4,E4,E4,F4,G4,G4,F4,E4,F4,G4,A4,A4,G4,F4,E4,D4,C4],
|
|
[0,0,1,2,2,1,3,3,1,2,1,3,0,0,3,1,2,1,0],
|
|
[1,1,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,4],
|
|
3,C4);
|
|
// ── Humpty Dumpty ──
|
|
var humpty=rhyme("Humpty Dumpty","English (1797)",75,
|
|
[E4,G4,A4,G4,F4,E4,D4,E4,F4,E4,D4,C4,E4,G4,A4,G4,F4,E4,D4,C4],
|
|
[2,3,0,3,1,2,1,2,1,2,1,0,2,3,0,3,1,2,1,0],
|
|
[1,1,2,1,1,1,2,1,1,1,1,2,1,1,2,1,1,1,1,4],
|
|
3,C4);
|
|
return [twinkle,mary,birthday,hotcross,london,macdonald,row,mice,spider,weasel,chopsticks,lavender,shortnin,jack,humpty];
|
|
})();
|
|
|
|
// ── RAGTIME (Scott Joplin & contemporaries, all public domain) ──
|
|
const RAGTIME_PIECES=(function(){
|
|
var C3=NT.C3,D3=NT.D3,E3=NT.E3,F3=NT.F3,G3=NT.G3,A3=NT.A3,Bb3=NT.Bb3,B3=NT.B3,
|
|
C4=NT.C4,D4=NT.D4,Eb4=NT.Eb4,E4=NT.E4,F4=NT.F4,G4=NT.G4,Ab4=NT.Ab4,A4=NT.A4,Bb4=NT.Bb4,B4=NT.B4,
|
|
C5=NT.C5,D5=NT.D5,Eb5=NT.Eb5,E5=NT.E5,F5=NT.F5,G5=NT.G5;
|
|
function rag(name,composer,bpm,theme,lanes,durations,passes,root){
|
|
var bg=[],mel=[],t=0,q=0.2;
|
|
for(var rep=0;rep<passes;rep++){
|
|
var v=0.2+rep*0.08;
|
|
// Ragtime uses piano-like timbres: strings for right hand, bass for left
|
|
var inst=rep%2===0?'strings':'woodwind';
|
|
theme.forEach(function(f,i){
|
|
var d=(durations?durations[i]:1)*q;
|
|
bg.push({t:t,f:f,d:d*0.8,i:inst,v:v});
|
|
// Stride bass pattern: root on beats, fifth on off-beats
|
|
if(i%4===0)bg.push({t:t,f:(root||theme[0])/2,d:d*0.6,i:'bass',v:v*0.5});
|
|
if(i%4===2)bg.push({t:t,f:(root||theme[0])*3/4,d:d*0.6,i:'bass',v:v*0.4});
|
|
mel.push({t:t,l:lanes[i]});t+=d;
|
|
});
|
|
t+=0.3;
|
|
}
|
|
var r=root||theme[0];
|
|
[r,r*5/4,r*3/2,r*2].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});});
|
|
bg.push({t:t,f:r/2,d:1.5,i:'bass',v:0.5});mel.push({t:t,l:4});
|
|
return {n:name,composer:composer,bpm:bpm,duration:Math.ceil(t+2.5),isClassical:true,bg:bg,melody:mel};
|
|
}
|
|
|
|
// ── The Entertainer — A section (most famous part) ──
|
|
var entertainer=rag("The Entertainer","Scott Joplin (1902)",130,
|
|
[D4,Eb4,E4,C5,E4,C5,E4,C5,C5,D5,Eb5,E5,C5,D5,E5,C5,D5,Eb5,E5,C5,E4,C5,E4,C5,A4,G4,A4,B4,C5],
|
|
[1,2,2,0,2,0,2,0,0,1,2,2,0,1,2,0,1,2,2,0,2,0,2,0,0,3,0,3,0],
|
|
[1,1,1,2,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
|
|
// ── Maple Leaf Rag — A section ──
|
|
var maple=rag("Maple Leaf Rag","Scott Joplin (1899)",140,
|
|
[Ab4,A4,C5,E5,C5,A4,C5,A4,Ab4,A4,C5,E5,D5,C5,A4,G4,Ab4,A4,C5,E5,C5,A4,C5,A4,G4,A4,G4,E4,C4],
|
|
[0,0,0,2,0,0,0,0,0,0,0,2,1,0,0,3,0,0,0,2,0,0,0,0,3,0,3,2,0],
|
|
[1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,4],
|
|
4,A4);
|
|
|
|
// ── The Easy Winners — A section ──
|
|
var easyw=rag("The Easy Winners","Scott Joplin (1901)",125,
|
|
[G4,B4,D5,B4,A4,G4,E4,G4,A4,B4,C5,D5,E5,D5,C5,B4,A4,G4,B4,D5,B4,A4,G4,E4,D4,E4,G4],
|
|
[3,3,1,3,0,3,2,3,0,3,0,1,2,1,0,3,0,3,3,1,3,0,3,2,1,2,3],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4],
|
|
4,G4);
|
|
|
|
// ── Elite Syncopations — A section ──
|
|
var elite=rag("Elite Syncopations","Scott Joplin (1902)",130,
|
|
[E4,G4,A4,B4,C5,D5,E5,D5,C5,B4,A4,G4,E4,G4,A4,B4,C5,E5,D5,C5,B4,A4,G4,E4,D4,C4],
|
|
[2,3,0,3,0,1,2,1,0,3,0,3,2,3,0,3,0,2,1,0,3,0,3,2,1,0],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
|
|
// ── Solace — A section (a Mexican serenade, slower ragtime) ──
|
|
var solace=rag("Solace","Scott Joplin (1909)",95,
|
|
[A4,G4,A4,C5,B4,A4,G4,E4,D4,E4,G4,A4,B4,A4,G4,E4,A4,G4,A4,C5,D5,C5,B4,A4,G4,E4,D4,C4],
|
|
[0,3,0,0,3,0,3,2,1,2,3,0,3,0,3,2,0,3,0,0,1,0,3,0,3,2,1,0],
|
|
[1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,4],
|
|
4,A4);
|
|
|
|
// ── Peacherine Rag — A section ──
|
|
var peacherine=rag("Peacherine Rag","Scott Joplin (1901)",135,
|
|
[C5,C5,B4,C5,E5,D5,C5,A4,C5,C5,B4,C5,G4,A4,B4,C5,C5,B4,C5,E5,D5,C5,B4,A4,G4],
|
|
[0,0,3,0,2,1,0,0,0,0,3,0,3,0,3,0,0,3,0,2,1,0,3,0,3],
|
|
[1,1,1,2,1,1,1,2,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,4],
|
|
4,C4);
|
|
|
|
// ── The Cascades — A section ──
|
|
var cascades=rag("The Cascades","Scott Joplin (1904)",125,
|
|
[C5,B4,C5,E5,G5,E5,C5,D5,C5,B4,A4,G4,C5,B4,C5,E5,G5,E5,C5,D5,E5,D5,C5,B4,C5],
|
|
[0,3,0,2,3,2,0,1,0,3,0,3,0,3,0,2,3,2,0,1,2,1,0,3,0],
|
|
[1,1,2,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
|
|
// ── Pineapple Rag — A section ──
|
|
var pineapple=rag("Pineapple Rag","Scott Joplin (1908)",130,
|
|
[C4,E4,G4,C5,E5,C5,G4,E4,C4,E4,G4,B4,D5,B4,G4,E4,C4,E4,G4,C5,E5,D5,C5,B4,A4,G4,C5],
|
|
[0,2,3,0,2,0,3,2,0,2,3,3,1,3,3,2,0,2,3,0,2,1,0,3,0,3,0],
|
|
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4],
|
|
4,C4);
|
|
|
|
return [entertainer,maple,easyw,elite,solace,peacherine,cascades,pineapple];
|
|
})();
|
|
|
|
var SYNTH_GENRES={"Nursery Rhymes":NURSERY_PIECES,"Singalong":SINGALONG_PIECES,"Christmas":CHRISTMAS_PIECES,"Folk":FOLK_PIECES,"Spirituals":SPIRITUAL_PIECES,"World":WORLD_PIECES,"Ragtime":RAGTIME_PIECES,"Classical":CLASSICAL_PIECES,"Video Games":VIDEOGAME_PIECES,"Chiptune Originals":CHIPTUNE_PIECES};
|
|
|
|
function simplifyNotes(notes){
|
|
if(!simpleMode||notes.length<2)return notes;
|
|
// Keep roughly half the notes — enforce a minimum gap between consecutive notes
|
|
var sorted=notes.slice().sort(function(a,b){return a.time-b.time;});
|
|
var minGap=0.35/gameSpeed; // ~350ms minimum between notes in simple mode
|
|
var result=[sorted[0]];
|
|
for(var i=1;i<sorted.length;i++){
|
|
if(sorted[i].time-result[result.length-1].time>=minGap){
|
|
result.push(sorted[i]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function buildClassicalNotes(piece){
|
|
var raw=piece.melody.filter(function(m){return activePads[m.l];})
|
|
.map(function(m){
|
|
var freq=261.63;
|
|
for(var i=0;i<piece.bg.length;i++){
|
|
var b=piece.bg[i];
|
|
if(Math.abs(b.t-m.t)<0.01&&b.i!=='bass'&&b.i!=='timpani'){freq=b.f;break;}
|
|
}
|
|
return{time:m.t/gameSpeed,lane:m.l,freq:freq,hit:false,missed:false};
|
|
})
|
|
.sort(function(a,b){return a.time-b.time;});
|
|
return simplifyNotes(raw);
|
|
}
|
|
|
|
function beginClassical(){
|
|
playing=true;gameSynthNodes=[];
|
|
var ac=getAc();ac.resume();
|
|
var piece=gameRhythm;
|
|
gameStartTime=ac.currentTime+0.15;
|
|
for(var i=0;i<piece.bg.length;i++){
|
|
var n=piece.bg[i];
|
|
playSynth(ac,n.f,gameStartTime+n.t/gameSpeed,n.d/gameSpeed,n.i,n.v*1.5,gameSynthNodes);
|
|
}
|
|
gameEndTime=piece.duration/gameSpeed;
|
|
setTimeout(function(){if(playing)endTurn();},(piece.duration/gameSpeed)*1000+5000);
|
|
}
|
|
|
|
// ── STATE ──────────────────────────────────────────────
|
|
let mode="builtin",curGenre="Singalong",curIdx=0;
|
|
let players=[{name:"Player 1"}];
|
|
let savedScores=lsLoad();
|
|
let previewAc=null,previewNodes=[],previewing=false;
|
|
let gameAc=null,mp3Buffer=null,mp3Src=null;
|
|
let notes=[],gameStartTime=null,gameEndTime=null,playing=false;
|
|
let gamePlayers=[],activePIdx=0;
|
|
let cScore=0,cStreak=0,cTotal=0,cGood=0,cMaxStreak=0;
|
|
let laneFlash=[0,0,0,0,0],msgTmr=null,prevBtns={},gameSynthNodes=[],classicalHitMode='orchestra';
|
|
let paused=false,pauseTime=0;
|
|
var gpMap=JSON.parse(localStorage.getItem("drgameGpMap")||"null")||{0:2,1:3,2:0,3:1,4:4,8:4};
|
|
var remapping=false,remapStep=0,remapResult={};
|
|
var _savedSettings=JSON.parse(localStorage.getItem("drgameSettings")||"{}");
|
|
var gameSpeed=_savedSettings.speed||1.0,masterVolume=_savedSettings.volume||1.0;
|
|
var hiddenGenres=JSON.parse(localStorage.getItem("drgameHidden")||"[]");
|
|
var hiddenSongs=JSON.parse(localStorage.getItem("drgameHiddenSongs")||"{}");
|
|
function isSongHidden(genre,name){return hiddenSongs[genre]&&hiddenSongs[genre].indexOf(name)>=0;}
|
|
function hideSong(genre,name){
|
|
if(!hiddenSongs[genre])hiddenSongs[genre]=[];
|
|
if(hiddenSongs[genre].indexOf(name)<0)hiddenSongs[genre].push(name);
|
|
localStorage.setItem("drgameHiddenSongs",JSON.stringify(hiddenSongs));
|
|
buildMenu();
|
|
}
|
|
function showAllSongs(genre){
|
|
delete hiddenSongs[genre];
|
|
localStorage.setItem("drgameHiddenSongs",JSON.stringify(hiddenSongs));
|
|
buildMenu();
|
|
}
|
|
let gameRhythm=null;
|
|
const HIT_WIN=0.17,SPEED=260;
|
|
|
|
// ── SCREEN ─────────────────────────────────────────────
|
|
function show(id){document.querySelectorAll(".screen").forEach(s=>s.classList.remove("active"));document.getElementById(id).classList.add("active");}
|
|
function goMenu(){if(paused){paused=false;getAc().resume();document.getElementById("pause-overlay").classList.remove("show");}stopGame();stopPreview();show("sm");refreshMiniLB();}
|
|
function goScores(){renderScores();show("ss");}
|
|
|
|
// ── PAD TOGGLES ────────────────────────────────────────
|
|
function buildPadToggles(){
|
|
const el=document.getElementById("pad-toggles");el.innerHTML="";
|
|
LANES.forEach((lane,i)=>{
|
|
const b=document.createElement("button");
|
|
b.className="ptog"+(activePads[i]?" on":"");
|
|
b.style.borderColor=lane.color;b.style.color=lane.color;
|
|
b.textContent=lane.label+(i===4?" (Kick)":"");
|
|
b.onclick=()=>{activePads[i]=!activePads[i];buildPadToggles();};
|
|
el.appendChild(b);
|
|
});
|
|
}
|
|
buildPadToggles();
|
|
function setSimpleMode(on){
|
|
simpleMode=on;
|
|
var full=document.getElementById("density-full"),simple=document.getElementById("density-simple");
|
|
if(full){full.className="smbtn"+(on?"":" purple");}
|
|
if(simple){simple.className="smbtn"+(on?" purple":"");}
|
|
}
|
|
setSimpleMode(false);
|
|
function saveSettings(){localStorage.setItem("drgameSettings",JSON.stringify({speed:gameSpeed,volume:masterVolume}));}
|
|
function syncSliders(){
|
|
var sv=Math.round(gameSpeed*100),vv=Math.round(masterVolume*100);
|
|
['speed-slider'].forEach(function(id){var e=document.getElementById(id);if(e)e.value=sv;});
|
|
['vol-slider','hud-vol'].forEach(function(id){var e=document.getElementById(id);if(e)e.value=vv;});
|
|
var se=document.getElementById("speed-val");if(se)se.textContent=sv+"%";
|
|
var ve=document.getElementById("vol-val");if(ve)ve.textContent=vv+"%";
|
|
var hve=document.getElementById("hud-vol-val");if(hve)hve.textContent=vv+"%";
|
|
}
|
|
function setSpeed(v){gameSpeed=v/100;syncSliders();saveSettings();}
|
|
function setVolume(v){masterVolume=v/100;syncSliders();saveSettings();}
|
|
function resetSettings(){gameSpeed=1.0;masterVolume=1.0;syncSliders();saveSettings();}
|
|
function loadSettings(){syncSliders();}
|
|
function confirmReset(){
|
|
var a=Math.floor(Math.random()*20)+5,b=Math.floor(Math.random()*20)+5;
|
|
var ans=prompt("To reset ALL saved data (scores, settings, gamepad mapping, hidden genres), solve this:\n\nWhat is "+a+" + "+b+"?");
|
|
if(ans===null)return;
|
|
if(parseInt(ans)===a+b){
|
|
localStorage.removeItem("drgame5");localStorage.removeItem("drgameSettings");
|
|
localStorage.removeItem("drgameGpMap");localStorage.removeItem("drgameHidden");localStorage.removeItem("drgameHiddenSongs");
|
|
savedScores=[];hiddenGenres=[];hiddenSongs={};gpMap={0:2,1:3,2:0,3:1,4:4,8:4};
|
|
refreshMiniLB();resetSettings();buildMenu();
|
|
alert("All data cleared!");
|
|
} else {alert("Incorrect — data NOT cleared.");}
|
|
}
|
|
function hideGenre(g){
|
|
if(hiddenGenres.indexOf(g)<0)hiddenGenres.push(g);
|
|
localStorage.setItem("drgameHidden",JSON.stringify(hiddenGenres));
|
|
if(curGenre===g){var allG=Object.keys(SYNTH_GENRES).concat(Object.keys(GENRES));curGenre=allG.find(function(x){return hiddenGenres.indexOf(x)<0;})||allG[0];curIdx=0;}
|
|
buildMenu();
|
|
}
|
|
function setHitMode(m){
|
|
classicalHitMode=m;
|
|
document.getElementById("hitmode-drums").className="smbtn"+(m==='drums'?' purple':'');
|
|
document.getElementById("hitmode-orch").className="smbtn"+(m==='orchestra'?' purple':'');
|
|
}
|
|
function updateHitModeVisibility(){
|
|
document.getElementById("hitmode-toggle").style.display=SYNTH_GENRES[curGenre]?"block":"none";
|
|
}
|
|
|
|
// ── MENU ───────────────────────────────────────────────
|
|
function buildMenu(){
|
|
const gt=document.getElementById("gtabs");gt.innerHTML="";
|
|
var synthIcons={"Singalong":"\uD83C\uDFB5","Christmas":"\uD83C\uDF84","Folk":"\uD83C\uDFB6","Spirituals":"\uD83D\uDE4F","World":"\uD83C\uDF0D","Classical":"\uD83C\uDFBB","Video Games":"\uD83C\uDFAE","Chiptune Originals":"\uD83D\uDC7E"};
|
|
var allGenres=Object.keys(SYNTH_GENRES).concat(Object.keys(GENRES));
|
|
var anyHidden=hiddenGenres.length>0;
|
|
allGenres.forEach(function(g){
|
|
if(hiddenGenres.indexOf(g)>=0)return;
|
|
var b=document.createElement("button");
|
|
b.className="gtab"+(g===curGenre?" active":"");
|
|
b.textContent=(synthIcons[g]?synthIcons[g]+" ":"")+g;
|
|
b.onclick=function(){curGenre=g;curIdx=0;stopPreview();buildMenu();};
|
|
b.oncontextmenu=function(e){e.preventDefault();hideGenre(g);};
|
|
gt.appendChild(b);
|
|
});
|
|
if(anyHidden){
|
|
var sb=document.createElement("button");
|
|
sb.className="gtab";sb.style.opacity="0.4";sb.style.fontSize="10px";
|
|
sb.textContent="\u2795 Show All ("+hiddenGenres.length+" hidden)";
|
|
sb.onclick=function(){hiddenGenres=[];localStorage.setItem("drgameHidden","[]");buildMenu();};
|
|
gt.appendChild(sb);
|
|
}
|
|
const rg=document.getElementById("rgrid");rg.innerHTML="";rg.ondblclick=()=>startGame();
|
|
var songList=SYNTH_GENRES[curGenre]||GENRES[curGenre];
|
|
var hidCount=0;
|
|
songList.forEach((r,i)=>{
|
|
if(isSongHidden(curGenre,r.n)){hidCount++;return;}
|
|
const b=document.createElement("button");
|
|
b.className="rbtn"+(i===curIdx?" sel":"");
|
|
b.innerHTML=`${r.n}<small>${r.composer?r.composer+' \u00b7 ':''}${r.bpm} bpm</small>`;
|
|
b.onclick=()=>{curIdx=i;stopPreview();document.getElementById("pvlbl").textContent=r.n+" selected";buildMenu();};
|
|
b.oncontextmenu=(e)=>{e.preventDefault();hideSong(curGenre,r.n);};
|
|
rg.appendChild(b);
|
|
});
|
|
if(hidCount>0){
|
|
const sb=document.createElement("button");sb.className="rbtn";sb.style.opacity="0.4";sb.style.fontSize="10px";
|
|
sb.textContent="\u2795 Show "+hidCount+" hidden";sb.onclick=()=>showAllSongs(curGenre);
|
|
rg.appendChild(sb);
|
|
}
|
|
const pl=document.getElementById("plist");pl.innerHTML="";
|
|
players.forEach((p,i)=>{
|
|
const row=document.createElement("div");row.className="prow";
|
|
const inp=document.createElement("input");inp.type="text";inp.value=p.name;
|
|
inp.oninput=e=>players[i].name=e.target.value;
|
|
row.appendChild(inp);
|
|
if(players.length>1){const d=document.createElement("button");d.className="smbtn red";d.textContent="✕";d.onclick=()=>{players.splice(i,1);buildMenu();};row.appendChild(d);}
|
|
pl.appendChild(row);
|
|
});
|
|
updateHitModeVisibility();
|
|
}
|
|
buildMenu();loadSettings();
|
|
function addPlayer(){players.push({name:"Player "+(players.length+1)});buildMenu();}
|
|
function setMode(m){
|
|
mode=m;
|
|
document.getElementById("mc-b").classList.toggle("sel",m==="builtin");
|
|
document.getElementById("mc-m").classList.toggle("sel",m==="mp3");
|
|
document.getElementById("rc").style.display=m==="builtin"?"block":"none";
|
|
document.getElementById("mc-mp3").style.display=m==="mp3"?"block":"none";
|
|
}
|
|
document.getElementById("fi").addEventListener("change",async e=>{
|
|
const f=e.target.files[0];if(!f)return;
|
|
document.getElementById("mp3n").textContent="Loading…";
|
|
const ac=getAc(true);
|
|
mp3Buffer=await ac.decodeAudioData(await f.arrayBuffer());
|
|
document.getElementById("mp3n").textContent=f.name;
|
|
});
|
|
|
|
// ── AUDIO CTX ──────────────────────────────────────────
|
|
// Linux/Chrome requires a user gesture before AudioContext will run.
|
|
// We create one shared context and resume it on every user interaction.
|
|
let _ac=null;
|
|
function getAc(prev=false){
|
|
if(prev){
|
|
if(!previewAc||previewAc.state==="closed")previewAc=new(window.AudioContext||window.webkitAudioContext)();
|
|
previewAc.resume();
|
|
return previewAc;
|
|
}
|
|
if(!gameAc||gameAc.state==="closed")gameAc=new(window.AudioContext||window.webkitAudioContext)();
|
|
gameAc.resume();
|
|
return gameAc;
|
|
}
|
|
// Unlock audio on ANY user interaction — click, touch, keydown
|
|
function unlockAudio(){
|
|
[previewAc,gameAc].forEach(ac=>{if(ac&&ac.state==="suspended")ac.resume();});
|
|
// create them early so they're ready
|
|
if(!previewAc){previewAc=new(window.AudioContext||window.webkitAudioContext)();previewAc.resume();}
|
|
if(!gameAc){gameAc=new(window.AudioContext||window.webkitAudioContext)();gameAc.resume();}
|
|
}
|
|
["click","touchstart","keydown","mousedown"].forEach(ev=>document.addEventListener(ev,unlockAudio,{once:false}));
|
|
|
|
// ── PREVIEW ────────────────────────────────────────────
|
|
function togglePreview(){previewing?stopPreview():startPreview();}
|
|
function stopPreview(){
|
|
previewing=false;previewNodes.forEach(n=>{try{n.stop();}catch(e){}});previewNodes=[];
|
|
const b=document.getElementById("pvbtn");b.textContent="▶ Preview";b.classList.remove("playing");
|
|
}
|
|
function startPreview(){
|
|
stopPreview();previewing=true;
|
|
const b=document.getElementById("pvbtn");b.textContent="■ Stop";b.classList.add("playing");
|
|
const r=SYNTH_GENRES[curGenre]?SYNTH_GENRES[curGenre][curIdx]:GENRES[curGenre][curIdx];
|
|
document.getElementById("pvlbl").textContent="▶ "+r.n;
|
|
loopPreview(r,0);
|
|
}
|
|
function loopPreview(r,gen){
|
|
if(!previewing||gen>200)return;
|
|
if(r.isClassical){var ac2=getAc(true);ac2.resume();var t0c=ac2.currentTime+0.15;for(var ci=0;ci<r.bg.length;ci++){var cn=r.bg[ci];playSynth(ac2,cn.f,t0c+cn.t,cn.d,cn.i,(cn.v||0.3)*0.7,previewNodes);}return;}
|
|
const ac=getAc(true),bs=60/r.bpm,bars=r.bars||8,len=bars*4*bs,t0=ac.currentTime+0.05;
|
|
for(let bar=0;bar<bars;bar++)for(const p of r.p)schedPvHit(ac,p.l,t0+bar*4*bs+p.t*bs);
|
|
setTimeout(()=>loopPreview(r,gen+1),len*1000-100);
|
|
}
|
|
function schedPvHit(ac,lane,t){playDrumAt(ac,lane,t,1.5,previewNodes);}
|
|
|
|
// ── SCORES ─────────────────────────────────────────────
|
|
function lsLoad(){try{const s=localStorage.getItem("drgame5");return s?JSON.parse(s):[]}catch(e){return[];}}
|
|
function lsSave(){try{localStorage.setItem("drgame5",JSON.stringify(savedScores));}catch(e){}}
|
|
function clearScores(){if(confirm("Clear all scores?")){savedScores=[];lsSave();renderScores();refreshMiniLB();}}
|
|
function refreshMiniLB(){
|
|
const top=[...savedScores].sort((a,b)=>b.score-a.score).slice(0,3);
|
|
document.getElementById("mlb").innerHTML=top.length?top.map((s,i)=>`${["🥇","🥈","🥉"][i]} <b>${s.player}</b> — ${s.rhythm} — ${s.score.toLocaleString()} (${s.acc}%)`).join("<br>"):"No scores yet.";
|
|
}
|
|
function renderScores(){
|
|
const all=[...savedScores].sort((a,b)=>b.score-a.score);
|
|
document.getElementById("stbody").innerHTML=all.map((s,i)=>`<tr><td>${["🥇","🥈","🥉"][i]||""}</td><td>${s.player}</td><td>${s.rhythm}</td><td><b>${s.score.toLocaleString()}</b></td><td>${s.acc}%</td><td>${s.streak}</td><td>${s.date}</td></tr>`).join("");
|
|
}
|
|
refreshMiniLB();
|
|
|
|
// ── GAME START ─────────────────────────────────────────
|
|
function startGame(){
|
|
stopPreview();
|
|
// Pre-warm AudioContext on Linux — resume early so it's ready after countdown
|
|
var warmAc=getAc();warmAc.resume();
|
|
gamePlayers=players.map(p=>({...p,score:0,streak:0,maxStreak:0,total:0,good:0}));
|
|
activePIdx=0;resetStats();show("sg");resizeCv();
|
|
if(mode==="builtin"){
|
|
gameRhythm=SYNTH_GENRES[curGenre]?SYNTH_GENRES[curGenre][curIdx]:GENRES[curGenre][curIdx];
|
|
document.getElementById("hud-title").textContent=curGenre+" — "+gameRhythm.n+" ("+gameRhythm.bpm+" bpm)";
|
|
notes=gameRhythm.isClassical?buildClassicalNotes(gameRhythm):buildNotes(gameRhythm);
|
|
countdown(()=>gameRhythm.isClassical?beginClassical():beginBuiltin());
|
|
} else {
|
|
gameRhythm=null;notes=[];
|
|
document.getElementById("hud-title").textContent=mp3Buffer?"MP3 Jam":"Jam";
|
|
countdown(()=>beginMp3());
|
|
}
|
|
updateHud();
|
|
}
|
|
function stopGame(){playing=false;if(mp3Src){try{mp3Src.stop();}catch(e){}mp3Src=null;}gameSynthNodes.forEach(function(n){try{n.stop();}catch(e){}});gameSynthNodes=[];}
|
|
function resetStats(){cScore=0;cStreak=0;cTotal=0;cGood=0;cMaxStreak=0;gameEndTime=null;}
|
|
|
|
function buildNotes(r){
|
|
const bs=60/r.bpm/gameSpeed,bars=r.bars||8,res=[];
|
|
for(let bar=0;bar<bars;bar++)
|
|
for(const p of r.p)
|
|
if(activePads[p.l])
|
|
res.push({time:(bar*4+p.t)*bs,lane:p.l,hit:false,missed:false});
|
|
var sorted=res.sort((a,b)=>a.time-b.time);
|
|
return simplifyNotes(sorted);
|
|
}
|
|
|
|
function countdown(cb){
|
|
const el=document.getElementById("cdown");let n=3;
|
|
el.style.opacity=1;el.textContent=n;
|
|
const iv=setInterval(()=>{n--;if(n<=0){clearInterval(iv);el.style.opacity=0;cb();}else el.textContent=n;},1000);
|
|
}
|
|
|
|
function beginBuiltin(){
|
|
playing=true;
|
|
const ac=getAc(),r=gameRhythm,bs=60/r.bpm/gameSpeed,bars=r.bars||8;
|
|
gameStartTime=ac.currentTime;
|
|
for(let bar=0;bar<bars;bar++)
|
|
for(const p of r.p)
|
|
playDrumAt(ac,p.l,gameStartTime+bar*4*bs+p.t*bs,4,[]);
|
|
const totalSec=bars*4*bs;
|
|
gameEndTime=totalSec;
|
|
setTimeout(()=>{if(playing)endTurn();},totalSec*1000+5000);
|
|
}
|
|
|
|
function beginMp3(){
|
|
playing=true;gameStartTime=getAc().currentTime;
|
|
if(mp3Buffer){
|
|
mp3Src=getAc().createBufferSource();mp3Src.buffer=mp3Buffer;
|
|
mp3Src.connect(getAc().destination);mp3Src.start();
|
|
mp3Src.onended=()=>{if(playing)endTurn();};
|
|
} else setTimeout(()=>{if(playing)endTurn();},30000);
|
|
}
|
|
|
|
function endTurn(){
|
|
playing=false;
|
|
const p=gamePlayers[activePIdx];
|
|
p.score=cScore;p.streak=cMaxStreak;p.total=cTotal;p.good=cGood;
|
|
const acc=cTotal?Math.round(cGood/cTotal*100):0;
|
|
savedScores.push({player:p.name,rhythm:gameRhythm?curGenre+" — "+gameRhythm.n:"MP3 Jam",score:cScore,acc,streak:cMaxStreak,date:new Date().toLocaleDateString()});
|
|
lsSave();
|
|
activePIdx++;
|
|
if(activePIdx<gamePlayers.length){
|
|
resetStats();notes=gameRhythm?(gameRhythm.isClassical?buildClassicalNotes(gameRhythm):buildNotes(gameRhythm)):[];updateHud();
|
|
const banner=document.getElementById("pbanner");
|
|
banner.textContent="Next: "+gamePlayers[activePIdx].name+"!";banner.style.opacity=1;
|
|
setTimeout(()=>{banner.style.opacity=0;setTimeout(()=>countdown(()=>gameRhythm.isClassical?beginClassical():beginBuiltin()),400);},2200);
|
|
} else {
|
|
const sorted=[...gamePlayers].sort((a,b)=>b.score-a.score);
|
|
const lines=sorted.map((p,i)=>`${["🥇","🥈","🥉"][i]||" "} ${p.name}: ${p.score.toLocaleString()} (${p.total?Math.round(p.good/p.total*100):0}%)`);
|
|
setTimeout(()=>{alert("Results:\n\n"+lines.join("\n"));goMenu();},500);
|
|
}
|
|
}
|
|
|
|
// ── HIT LOGIC ──────────────────────────────────────────
|
|
function triggerPad(lane){
|
|
var isClassical=gameRhythm&&gameRhythm.isClassical;
|
|
var isClassicalOrch=isClassical&&classicalHitMode==='orchestra';
|
|
// Drum hit sound — quieter in classical so orchestra background is audible
|
|
if(!isClassicalOrch){
|
|
playDrumAt(getAc(),lane,getAc().currentTime,isClassical?3:8,[]);
|
|
}
|
|
laneFlash[lane]=1;setTimeout(()=>laneFlash[lane]=0,120);
|
|
if(!playing)return;
|
|
if(mode==="mp3"){showMsg("♪",LANES[lane].color);return;}
|
|
if(!activePads[lane])return; // inactive pad — sound only
|
|
const now=getAc().currentTime-gameStartTime;
|
|
let best=null,bestD=HIT_WIN;
|
|
for(const n of notes){
|
|
if(n.lane!==lane||n.hit||n.missed)continue;
|
|
const d=Math.abs(n.time-now);
|
|
if(d<bestD){bestD=d;best=n;}
|
|
}
|
|
cTotal++;
|
|
if(best){
|
|
best.hit=true;cStreak++;cGood++;cScore+=100*Math.min(cStreak,10);
|
|
if(cStreak>cMaxStreak)cMaxStreak=cStreak;
|
|
// Play orchestral hit sound for classical mode
|
|
if(isClassicalOrch&&best.freq){
|
|
var ac=getAc(),ct=ac.currentTime;
|
|
playSynth(ac,best.freq,ct,0.35,'strings',0.7,[]);
|
|
playSynth(ac,best.freq,ct,0.35,'brass',0.4,[]);
|
|
} else if(isClassicalOrch){
|
|
playDrumAt(getAc(),lane,getAc().currentTime,3,[]);
|
|
}
|
|
showMsg(bestD<0.05?"PERFECT!":"GOOD",bestD<0.05?"#68d391":"#d69e2e");
|
|
} else {
|
|
// Miss — quiet drum thud as feedback
|
|
if(isClassicalOrch)playDrumAt(getAc(),lane,getAc().currentTime,2,[]);
|
|
cStreak=0;showMsg("MISS","#fc8181");
|
|
}
|
|
updateHud();
|
|
}
|
|
function updateHud(){
|
|
document.getElementById("hs").textContent=cScore.toLocaleString();
|
|
document.getElementById("hst").textContent=cStreak;
|
|
document.getElementById("ha").textContent=cTotal?Math.round(cGood/cTotal*100)+"%":"-";
|
|
document.getElementById("hp").textContent=gamePlayers[activePIdx]?.name||"-";
|
|
}
|
|
function showMsg(txt,color){
|
|
const el=document.getElementById("msg");el.textContent=txt;el.style.color=color;el.style.opacity=1;
|
|
clearTimeout(msgTmr);msgTmr=setTimeout(()=>el.style.opacity=0,500);
|
|
}
|
|
|
|
// ── CANVAS ─────────────────────────────────────────────
|
|
const canvas=document.getElementById("c");
|
|
const wrap=document.getElementById("cwrap");
|
|
const cx=canvas.getContext("2d");
|
|
function resizeCv(){canvas.width=wrap.clientWidth;canvas.height=wrap.clientHeight;}
|
|
resizeCv();window.addEventListener("resize",resizeCv);
|
|
|
|
function draw(){
|
|
requestAnimationFrame(draw);
|
|
const W=canvas.width,H=canvas.height;
|
|
if(!W||!H)return;
|
|
cx.clearRect(0,0,W,H);
|
|
|
|
// Active pad lanes (only those toggled on, excluding kick)
|
|
const activeLanes=LANES.slice(0,4).map((l,i)=>i).filter(i=>activePads[i]);
|
|
const hasKick=activePads[4];
|
|
const PAD_H=hasKick?Math.floor(H*0.78):H;
|
|
const KICK_H=H-PAD_H;
|
|
const nLanes=activeLanes.length;
|
|
const PAD_W=nLanes>0?W/nLanes:W;
|
|
const HIT_Y=Math.floor(PAD_H*0.76);
|
|
const KICK_HIT_Y=PAD_H+Math.floor(KICK_H*0.55);
|
|
const now=(playing&&gameStartTime!=null)?getAc().currentTime-gameStartTime:-9999;
|
|
|
|
// Lane positions map: lane index -> x center
|
|
const laneX={};
|
|
activeLanes.forEach((li,col)=>{laneX[li]=col*PAD_W+PAD_W/2;});
|
|
|
|
// Draw pad lanes
|
|
activeLanes.forEach((li,col)=>{
|
|
const x=col*PAD_W;
|
|
cx.fillStyle=laneFlash[li]?LANES[li].color+"33":"#0d0d16";
|
|
cx.fillRect(x,0,PAD_W,PAD_H);
|
|
if(col>0){cx.strokeStyle="#1e1e2e";cx.lineWidth=1;cx.beginPath();cx.moveTo(x,0);cx.lineTo(x,PAD_H);cx.stroke();}
|
|
cx.fillStyle=LANES[li].color;cx.font="bold 12px sans-serif";cx.textAlign="center";
|
|
cx.fillText(LANES[li].label,x+PAD_W/2,PAD_H-20);
|
|
cx.fillStyle="#999";cx.font="10px sans-serif";
|
|
cx.fillText(LANES[li].key.toUpperCase(),x+PAD_W/2,PAD_H-7);
|
|
});
|
|
|
|
// Kick zone
|
|
if(hasKick){
|
|
cx.fillStyle=laneFlash[4]?LANES[4].color+"33":"#0a0a12";
|
|
cx.fillRect(0,PAD_H,W,KICK_H);
|
|
cx.strokeStyle="#1e1e2e";cx.lineWidth=1;cx.beginPath();cx.moveTo(0,PAD_H);cx.lineTo(W,PAD_H);cx.stroke();
|
|
cx.fillStyle=LANES[4].color;cx.font="bold 13px sans-serif";cx.textAlign="center";
|
|
cx.fillText("Kick",W/2,PAD_H+KICK_H*0.42);
|
|
cx.fillStyle="#999";cx.font="10px sans-serif";
|
|
cx.fillText("SPACE",W/2,PAD_H+KICK_H*0.72);
|
|
}
|
|
|
|
if(nLanes===0&&!hasKick)return;
|
|
|
|
// Hit line
|
|
if(nLanes>0){
|
|
cx.strokeStyle="rgba(255,255,255,0.5)";cx.lineWidth=2;
|
|
cx.beginPath();cx.moveTo(0,HIT_Y);cx.lineTo(W,HIT_Y);cx.stroke();
|
|
cx.fillStyle="rgba(255,255,255,0.03)";
|
|
cx.fillRect(0,HIT_Y-SPEED*HIT_WIN,W,SPEED*HIT_WIN*2);
|
|
}
|
|
|
|
// Kick hit line
|
|
if(hasKick){
|
|
cx.strokeStyle=LANES[4].color+"88";cx.lineWidth=2;
|
|
cx.beginPath();cx.moveTo(W*0.15,KICK_HIT_Y);cx.lineTo(W*0.85,KICK_HIT_Y);cx.stroke();
|
|
}
|
|
|
|
// Target rings — pads
|
|
activeLanes.forEach(li=>{
|
|
const x=laneX[li],r=Math.min(PAD_W*0.33,26);
|
|
cx.beginPath();cx.arc(x,HIT_Y,r,0,Math.PI*2);
|
|
cx.strokeStyle=laneFlash[li]?LANES[li].color:LANES[li].color+"55";
|
|
cx.lineWidth=laneFlash[li]?3:2;cx.stroke();
|
|
if(laneFlash[li]){cx.beginPath();cx.arc(x,HIT_Y,r,0,Math.PI*2);cx.fillStyle=LANES[li].color+"44";cx.fill();}
|
|
});
|
|
// Kick ring
|
|
if(hasKick){
|
|
const kr=Math.min(W*0.08,34);
|
|
cx.beginPath();cx.arc(W/2,KICK_HIT_Y,kr,0,Math.PI*2);
|
|
cx.strokeStyle=laneFlash[4]?LANES[4].color:LANES[4].color+"55";
|
|
cx.lineWidth=laneFlash[4]?3:2;cx.stroke();
|
|
if(laneFlash[4]){cx.beginPath();cx.arc(W/2,KICK_HIT_Y,kr,0,Math.PI*2);cx.fillStyle=LANES[4].color+"44";cx.fill();}
|
|
}
|
|
|
|
// Auto-end when all notes resolved and past expected end time
|
|
if(playing&&mode==="builtin"&&gameEndTime!=null&&now>=gameEndTime){
|
|
if(notes.length===0||notes.every(n=>n.hit||n.missed))endTurn();
|
|
}
|
|
|
|
// Notes
|
|
if(mode==="builtin"&&now>-9000){
|
|
for(const n of notes){
|
|
if(n.hit)continue;
|
|
const dt=n.time-now;
|
|
if(n.lane<4){
|
|
if(!laneX.hasOwnProperty(n.lane))continue;
|
|
const ny=HIT_Y-dt*SPEED;
|
|
if(ny<-40||ny>PAD_H+40){
|
|
if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}}
|
|
continue;
|
|
}
|
|
const x=laneX[n.lane],r=Math.min(PAD_W*0.33,26);
|
|
cx.globalAlpha=n.missed?0.2:1;
|
|
const g=cx.createRadialGradient(x,ny,0,x,ny,r*2);
|
|
g.addColorStop(0,LANES[n.lane].color+"aa");g.addColorStop(1,LANES[n.lane].color+"00");
|
|
cx.fillStyle=g;cx.beginPath();cx.arc(x,ny,r*2,0,Math.PI*2);cx.fill();
|
|
cx.fillStyle=LANES[n.lane].color;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.fill();
|
|
cx.strokeStyle="#fff";cx.lineWidth=2;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.stroke();
|
|
cx.globalAlpha=1;
|
|
if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}}
|
|
} else if(hasKick){
|
|
const ky=KICK_HIT_Y-dt*SPEED*0.5;
|
|
if(ky<PAD_H-30||ky>H+30){
|
|
if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}}
|
|
continue;
|
|
}
|
|
const kr=Math.min(W*0.08,34);
|
|
cx.globalAlpha=n.missed?0.2:1;
|
|
cx.fillStyle=LANES[4].color;cx.beginPath();cx.arc(W/2,ky,kr,0,Math.PI*2);cx.fill();
|
|
cx.strokeStyle="#fff";cx.lineWidth=2;cx.beginPath();cx.arc(W/2,ky,kr,0,Math.PI*2);cx.stroke();
|
|
cx.globalAlpha=1;
|
|
if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
draw();
|
|
|
|
// ── DRUM SOUNDS ────────────────────────────────────────
|
|
function playDrumAt(ac,lane,t,vol,arr){
|
|
vol=vol*masterVolume;
|
|
const type=LANES[lane].type;
|
|
function mk(n){if(arr)arr.push(n);}
|
|
if(type==="kick"||type==="bass"){
|
|
const o=ac.createOscillator(),g=ac.createGain();
|
|
o.connect(g);g.connect(ac.destination);
|
|
o.frequency.setValueAtTime(180,t);o.frequency.exponentialRampToValueAtTime(40,t+0.15);
|
|
g.gain.setValueAtTime(vol,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.3);
|
|
o.start(t);o.stop(t+0.35);mk(o);
|
|
} else if(type==="snare"){
|
|
const sz=ac.sampleRate*0.12,buf=ac.createBuffer(1,sz,ac.sampleRate),d=buf.getChannelData(0);
|
|
for(let i=0;i<sz;i++)d[i]=Math.random()*2-1;
|
|
const s=ac.createBufferSource(),f=ac.createBiquadFilter(),g=ac.createGain();
|
|
f.type="highpass";f.frequency.value=1500;s.buffer=buf;s.connect(f);f.connect(g);g.connect(ac.destination);
|
|
g.gain.setValueAtTime(vol*.75,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.12);
|
|
s.start(t);s.stop(t+0.15);mk(s);
|
|
const o=ac.createOscillator(),g2=ac.createGain();
|
|
o.connect(g2);g2.connect(ac.destination);o.frequency.value=180;
|
|
g2.gain.setValueAtTime(vol*.35,t);g2.gain.exponentialRampToValueAtTime(0.001,t+0.07);
|
|
o.start(t);o.stop(t+0.08);mk(o);
|
|
} else if(type==="hihat"){
|
|
const sz=ac.sampleRate*0.05,buf=ac.createBuffer(1,sz,ac.sampleRate),d=buf.getChannelData(0);
|
|
for(let i=0;i<sz;i++)d[i]=Math.random()*2-1;
|
|
const s=ac.createBufferSource(),f=ac.createBiquadFilter(),g=ac.createGain();
|
|
f.type="highpass";f.frequency.value=7000;s.buffer=buf;s.connect(f);f.connect(g);g.connect(ac.destination);
|
|
g.gain.setValueAtTime(vol*.5,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.05);
|
|
s.start(t);s.stop(t+0.06);mk(s);
|
|
} else { // tom / tom2
|
|
const freq=type==="tom"?160:120;
|
|
const o=ac.createOscillator(),g=ac.createGain();
|
|
o.connect(g);g.connect(ac.destination);
|
|
o.frequency.setValueAtTime(freq,t);o.frequency.exponentialRampToValueAtTime(freq*0.4,t+0.2);
|
|
g.gain.setValueAtTime(vol*.85,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.25);
|
|
o.start(t);o.stop(t+0.28);mk(o);
|
|
}
|
|
}
|
|
|
|
// ── PAUSE ─────────────────────────────────────────────
|
|
function togglePause(){
|
|
if(!playing&&!paused)return;
|
|
var ac=getAc();
|
|
if(!paused){
|
|
paused=true;playing=false;pauseTime=ac.currentTime;
|
|
ac.suspend();
|
|
document.getElementById("pause-overlay").classList.add("show");
|
|
} else {
|
|
paused=false;playing=true;
|
|
ac.resume();
|
|
document.getElementById("pause-overlay").classList.remove("show");
|
|
}
|
|
}
|
|
|
|
// ── REMAP ─────────────────────────────────────────────
|
|
var remapLanes=[{l:0,name:"RED",color:"#e53e3e"},{l:1,name:"YELLOW",color:"#d69e2e"},{l:2,name:"BLUE",color:"#3182ce"},{l:3,name:"GREEN",color:"#38a169"},{l:4,name:"KICK",color:"#805ad5"}];
|
|
function startRemap(){
|
|
remapping=true;remapStep=0;remapResult={};
|
|
document.getElementById("remap-overlay").classList.add("show");
|
|
showRemapStep();
|
|
}
|
|
function cancelRemap(){
|
|
remapping=false;document.getElementById("remap-overlay").classList.remove("show");
|
|
}
|
|
function showRemapStep(){
|
|
if(remapStep>=remapLanes.length){
|
|
// Done — save mapping
|
|
gpMap={};
|
|
for(var k in remapResult)gpMap[k]=remapResult[k];
|
|
localStorage.setItem("drgameGpMap",JSON.stringify(gpMap));
|
|
remapping=false;
|
|
document.getElementById("remap-overlay").classList.remove("show");
|
|
document.getElementById("remap-prompt").textContent="";
|
|
return;
|
|
}
|
|
var rl=remapLanes[remapStep];
|
|
document.getElementById("remap-prompt").innerHTML="Hit your <span style='color:"+rl.color+";font-size:24px'>"+rl.name+"</span> pad";
|
|
document.getElementById("remap-status").textContent="Step "+(remapStep+1)+" of "+remapLanes.length+(remapStep===4?" (or press foot pedal)":"");
|
|
}
|
|
var remapCooldown=false;
|
|
function handleRemapButton(bi){
|
|
if(!remapping||remapCooldown)return false;
|
|
remapResult[bi]=remapLanes[remapStep].l;
|
|
remapStep++;
|
|
remapCooldown=true;
|
|
setTimeout(function(){remapCooldown=false;},300);
|
|
showRemapStep();
|
|
return true;
|
|
}
|
|
|
|
// ── INPUT ──────────────────────────────────────────────
|
|
window.addEventListener("keydown",e=>{
|
|
if(e.key==="Escape"){e.preventDefault();togglePause();return;}
|
|
if(paused)return;
|
|
const i=LANES.findIndex(l=>l.key===e.key.toLowerCase()||(l.key===" "&&e.key===" "));
|
|
if(i>=0){e.preventDefault();triggerPad(i);}
|
|
});
|
|
function pollGP(){
|
|
const gps=navigator.getGamepads?navigator.getGamepads():[];
|
|
let found=false;
|
|
for(const gp of gps){
|
|
if(!gp)continue;found=true;
|
|
document.getElementById("gpd").textContent="🎮 "+gp.id.slice(0,20);
|
|
var remapHandled=false;
|
|
gp.buttons.forEach((btn,bi)=>{
|
|
if(btn.pressed&&!prevBtns[bi]){
|
|
if(remapping){
|
|
if(!remapHandled){remapHandled=true;handleRemapButton(bi);}
|
|
}
|
|
else if(!paused&&gpMap[bi]!==undefined)triggerPad(gpMap[bi]);
|
|
}
|
|
prevBtns[bi]=btn.pressed;
|
|
});
|
|
}
|
|
if(!found)document.getElementById("gpd").textContent="No gamepad";
|
|
requestAnimationFrame(pollGP);
|
|
}
|
|
pollGP();
|
|
</script>
|
|
</body>
|
|
</html>
|