Songs corrected: - Amazing Grace: fixed C5→E5 errors in multiple positions, fixed missing G5 repeat, corrected phrase structure (was 31 notes, now correct 28) - Greensleeves: fixed G3→Ab3 (G#3) at position 17 in the descending phrase (B3,G#3 pattern should match the B3,G#3 at position 14-15) - Deck the Halls: fixed B4→Bb4 at position 1 (key is F major, all B's should be Bb except accidentals) - When the Saints Go Marching In: removed duplicate F4 at index 25 (standard has F4 whole note then E4, not F4-F4-E4) - Auld Lang Syne: removed extra G4 at position 9 that shifted all subsequent notes, fixed garbled second half of verse - Scarborough Fair: rewrote melody to correct register (standard melody reaches D5/E5, game was stuck below C5), now matches lead sheet Verified correct (no changes needed): Jingle Bells, Silent Night, Oh! Susanna Songs not in the PDF (need different reference): Red River Valley, Home on the Range, Yankee Doodle, Turkey in the Straw, Danny Boy, Bella Ciao, Clementine, Molly Malone, Whiskey in the Jar, First Noel, Hark the Herald, 12 Days of Christmas, Wellerman, Drunken Sailor, Skye Boat Song, Away in a Manger, Joy to the World Reference: music-for-music-teachers.com lead sheet images https://claude.ai/code/session_01ELEFwNKTnZvM39VK9wsYd4
2904 lines
140 KiB
HTML
2904 lines
140 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 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];
|
|
|
|
// ── 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,Eb4,E4,Eb4,E4,D4,C4,B3];
|
|
var lanes=[0,1,2,0,1,2,1,3,1,3,1,0,2,1];
|
|
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) ──
|
|
var ofort=(function(){
|
|
var bg=[],mel=[],q=0.6,h=q/2;
|
|
function orch(t,freq,dur,v){
|
|
bg.push({t:t,f:freq,d:dur,i:'strings',v:v||0.35});
|
|
bg.push({t:t,f:freq,d:dur,i:'brass',v:(v||0.35)*0.7});
|
|
}
|
|
function choir(t,freq,dur,v){bg.push({t:t,f:freq,d:dur,i:'choir',v:v||0.4});}
|
|
function timp(t,v){bg.push({t:t,f:110,d:0.5,i:'timpani',v:v||0.5});}
|
|
// Section 1: Opening chant "O Fortuna"
|
|
var t=0;
|
|
var ch1=[D4,D4,D4,D4,A3,A3,Bb3,Bb3,A3,A3];
|
|
var ch1d=[h,h,h,q,h,q,h,q,h,q*1.5];
|
|
var ch1l=[0,0,0,0,1,1,2,2,1,1];
|
|
ch1.forEach(function(f,i){
|
|
choir(t,f,ch1d[i]*0.9,0.45);orch(t,f,ch1d[i]*0.9,0.25);
|
|
if(ch1d[i]>=q)timp(t,0.4);
|
|
mel.push({t:t,l:ch1l[i]});t+=ch1d[i];
|
|
});
|
|
// Section 2: Second chant phrase
|
|
t+=0.4;
|
|
var ch2=[G3,G3,A3,Bb3,A3,G3,A3,A3];
|
|
var ch2d=[h,h,h,h,q,q,h,q*2];
|
|
var ch2l=[3,3,1,2,1,3,1,1];
|
|
ch2.forEach(function(f,i){
|
|
choir(t,f,ch2d[i]*0.9,0.45);orch(t,f,ch2d[i]*0.9,0.25);
|
|
if(ch2d[i]>=q)timp(t,0.4);
|
|
mel.push({t:t,l:ch2l[i]});t+=ch2d[i];
|
|
});
|
|
// Section 3: Ascending passage
|
|
t+=0.4;
|
|
var asc=[D4,E4,F4,E4,D4,D4,E4,F4,G4,A4];
|
|
var ascd=[h,h,h,h,q*1.5,h,h,h,h,q*2];
|
|
var ascl=[0,1,2,1,0,0,1,2,3,0];
|
|
asc.forEach(function(f,i){
|
|
choir(t,f,ascd[i]*0.9,0.5);orch(t,f,ascd[i]*0.9,0.35);
|
|
bg.push({t:t,f:f/2,d:ascd[i]*0.9,i:'bass',v:0.3});
|
|
if(ascd[i]>=q)timp(t,0.5);
|
|
mel.push({t:t,l:ascl[i]});t+=ascd[i];
|
|
});
|
|
// Section 4: Quiet interlude (strings only, mysterious)
|
|
t+=0.5;
|
|
var qi=[A3,Bb3,A3,G3,F3,G3,A3,Bb3,C4,D4];
|
|
var qid=[q,h,q,h,q,h,h,h,h,q*2];
|
|
var qil=[1,2,1,3,4,3,1,2,0,0];
|
|
qi.forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:qid[i]*0.9,i:'strings',v:0.25});
|
|
bg.push({t:t,f:f/2,d:qid[i]*0.9,i:'bass',v:0.2});
|
|
mel.push({t:t,l:qil[i]});t+=qid[i];
|
|
});
|
|
// Section 5: Dramatic descent
|
|
t+=0.4;
|
|
var desc=[A4,G4,F4,E4,D4,E4,F4,G4,A4,A4,G4,F4,E4,D4];
|
|
var descd=[h,h,h,h,h,h,h,h,q,h,h,h,h,q*2];
|
|
var descl=[3,2,1,0,4,1,2,3,3,3,2,1,0,4];
|
|
desc.forEach(function(f,i){
|
|
choir(t,f,descd[i]*0.9,0.55);orch(t,f,descd[i]*0.9,0.4);
|
|
bg.push({t:t,f:f/2,d:descd[i]*0.9,i:'bass',v:0.35});
|
|
if(i%3===0)timp(t,0.55);
|
|
mel.push({t:t,l:descl[i]});t+=descd[i];
|
|
});
|
|
// Section 6: Second ascending at higher intensity
|
|
t+=0.4;
|
|
var a2=[D4,E4,F4,G4,A4,Bb4,A4,G4,A4,Bb4,C5,D5];
|
|
var a2d=[h,h,h,h,h,h,h,h,h,h,h,q*2];
|
|
var a2l=[0,1,2,3,0,2,0,3,0,2,1,4];
|
|
a2.forEach(function(f,i){
|
|
choir(t,f,a2d[i]*0.9,0.6);orch(t,f,a2d[i]*0.9,0.45);
|
|
bg.push({t:t,f:f/2,d:a2d[i]*0.9,i:'bass',v:0.4});
|
|
if(i%2===0)timp(t,0.55);
|
|
mel.push({t:t,l:a2l[i]});t+=a2d[i];
|
|
});
|
|
// Section 7: Extended climactic reprise
|
|
t+=0.5;
|
|
for(var i=0;i<12;i++){
|
|
var f=i<4?D4:i<8?D5:A4;
|
|
choir(t,f,q*0.9,0.6);orch(t,f,q*0.9,0.55);
|
|
bg.push({t:t,f:NT.D3,d:q*0.9,i:'bass',v:0.4});
|
|
timp(t,0.6);mel.push({t:t,l:i<4?0:i<8?4:i%2===0?0:3});t+=q;
|
|
}
|
|
// Section 8: Final massive chord sequence
|
|
t+=0.3;
|
|
[D4,F4,A4,D5].forEach(function(f){
|
|
bg.push({t:t,f:f,d:1.5,i:'choir',v:0.55});
|
|
bg.push({t:t,f:f,d:1.5,i:'strings',v:0.5});
|
|
bg.push({t:t,f:f,d:1.5,i:'brass',v:0.45});
|
|
});
|
|
bg.push({t:t,f:NT.D3,d:1.5,i:'bass',v:0.5});
|
|
timp(t,0.65);mel.push({t:t,l:4});
|
|
t+=2.0;
|
|
// Final D unison
|
|
[D4,D5].forEach(function(f){
|
|
bg.push({t:t,f:f,d:2.0,i:'choir',v:0.5});
|
|
bg.push({t:t,f:f,d:2.0,i:'brass',v:0.5});
|
|
});
|
|
bg.push({t:t,f:NT.D3,d:2.0,i:'bass',v:0.55});
|
|
timp(t,0.7);mel.push({t:t,l:4});
|
|
return {n:"O Fortuna",composer:"Orff",bpm:120,duration:Math.ceil(t+2.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 ──
|
|
var valk=tp("Ride of the Valkyries","Wagner",140,
|
|
[Bb3,Bb3,D4,D4,D4,F4,F4,F4,Bb4,Bb4,F4,D4,Bb3,D4,F4,Bb4,F4,D4,Bb3,F3],
|
|
[0,0,1,1,1,2,2,2,3,3,2,1,0,1,2,3,2,1,0,4],
|
|
[{d:0.30,v:0.25,i:'woodwind'},{d:0.27,v:0.3,i:'strings'},{d:0.24,v:0.38,i:'strings'},{d:0.21,v:0.45,i:'brass'},{d:0.18,v:0.52,i:'brass'},{d:0.15,v:0.6,i:'brass'}],
|
|
[Bb3,D4,F4,Bb4]);
|
|
|
|
// ── William Tell Overture (Gallop) ──
|
|
var tell=tp("William Tell Overture (Gallop)","Rossini",152,
|
|
[G4,G4,B4,G4,G4,B4,D5,D5,B4,G4,E4,G4,B4,D5,B4,G4,E4,D4,E4,G4],
|
|
[0,0,3,0,0,3,1,1,3,0,2,0,3,1,3,0,2,1,2,0],
|
|
[{d:0.28,v:0.22,i:'strings'},{d:0.25,v:0.28,i:'strings'},{d:0.22,v:0.35,i:'strings'},{d:0.20,v:0.42,i:'strings'},{d:0.18,v:0.48,i:'brass'},{d:0.16,v:0.54,i:'brass'},{d:0.14,v:0.58,i:'brass'},{d:0.12,v:0.62,i:'brass'}],
|
|
[G4,B4,D5,G5]);
|
|
|
|
// ── Bolero ──
|
|
var bolero=tp("Bolero","Ravel",72,
|
|
[C4,D4,E4,F4,E4,D4,C4,C4,D4,E4,F4,G4,F4,E4,D4,C4,E4,G4,E4,C4],
|
|
[0,1,2,3,2,1,0,0,1,2,3,0,3,2,1,0,2,0,2,0],
|
|
[{d:0.38,v:0.15,i:'woodwind'},{d:0.36,v:0.2,i:'woodwind'},{d:0.34,v:0.28,i:'strings'},{d:0.32,v:0.35,i:'strings'},{d:0.30,v:0.42,i:'strings'},{d:0.28,v:0.5,i:'brass'},{d:0.26,v:0.58,i:'brass'}],
|
|
[C4,E4,G4,C5]);
|
|
|
|
// ── Barber of Seville Overture ──
|
|
var barber=tp("Barber of Seville Overture","Rossini",144,
|
|
[G4,A4,B4,G4,E4,G4,A4,B4,C5,B4,A4,G4,E4,D4,E4,G4,B4,D5,C5,B4],
|
|
[0,1,3,0,2,0,1,3,0,3,1,0,2,1,2,0,3,1,0,3],
|
|
[{d:0.24,v:0.22,i:'strings'},{d:0.22,v:0.3,i:'strings'},{d:0.20,v:0.38,i:'strings'},{d:0.18,v:0.42,i:'strings'},{d:0.16,v:0.48,i:'brass'},{d:0.14,v:0.54,i:'brass'},{d:0.12,v:0.6,i:'brass'}],
|
|
[G4,B4,D5,G5]);
|
|
|
|
// ── Sabre Dance ──
|
|
var sabre=tp("Sabre Dance","Khachaturian",168,
|
|
[D4,D4,D4,Eb4,D4,D4,D4,Eb4,D4,E4,F4,G4,A4,G4,F4,E4,D4,D4,D4,A3],
|
|
[0,0,0,1,0,0,0,1,0,2,3,0,1,0,3,2,0,0,0,4],
|
|
[{d:0.24,v:0.28,i:'strings'},{d:0.22,v:0.34,i:'strings'},{d:0.20,v:0.4,i:'strings'},{d:0.18,v:0.46,i:'brass'},{d:0.16,v:0.52,i:'brass'},{d:0.14,v:0.56,i:'brass'},{d:0.12,v:0.6,i:'brass'},{d:0.10,v:0.65,i:'brass'}],
|
|
[D4,F4,A4,D5]);
|
|
|
|
// ── Sorcerer's Apprentice ──
|
|
var sorcerer=tp("Sorcerer's Apprentice","Dukas",132,
|
|
[A3,E4,C4,E4,A3,E4,C4,E4,A3,B3,C4,D4,E4,D4,C4,B3,A3,C4,E4,A4],
|
|
[0,2,1,2,0,2,1,2,0,1,2,0,2,0,2,1,0,1,2,3],
|
|
[{d:0.28,v:0.2,i:'woodwind'},{d:0.26,v:0.28,i:'woodwind'},{d:0.24,v:0.35,i:'strings'},{d:0.22,v:0.42,i:'strings'},{d:0.20,v:0.5,i:'brass'},{d:0.18,v:0.58,i:'brass'}],
|
|
[A3,C4,E4,A4]);
|
|
|
|
// ── Morning Mood ──
|
|
var morning=tp("Morning Mood","Grieg",60,
|
|
[F4,A4,C5,A4,F4,A4,C5,E5,F5,E5,C5,A4,F4,G4,A4,Bb4,C5,Bb4,A4,F4],
|
|
[0,1,2,1,0,1,2,3,0,3,2,1,0,1,1,2,2,2,1,0],
|
|
[{d:0.45,v:0.15,i:'woodwind'},{d:0.42,v:0.2,i:'woodwind'},{d:0.39,v:0.28,i:'woodwind'},{d:0.36,v:0.35,i:'strings'},{d:0.33,v:0.42,i:'strings'},{d:0.30,v:0.5,i:'brass'}],
|
|
[F4,A4,C5,F5]);
|
|
|
|
// ── 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 trill: A-Ab-A
|
|
org(0,A4,0.12,0.55);org(0.15,Ab4,0.12,0.55);org(0.3,A4,0.9,0.6);
|
|
mel.push({t:0,l:0},{t:0.15,l:1},{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,Ab3,0.12,0.55);org(3.5,A3,0.9,0.6);
|
|
mel.push({t:3.2,l:0},{t:3.35,l:1},{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 ──
|
|
var danube=(function(){
|
|
var bg=[],mel=[],t=0,q=0.5;
|
|
// Intro: gentle ascending
|
|
[C4,E4,G4,C5,E5,C5,G4,E4].forEach(function(f,i){
|
|
bg.push({t:t,f:f,d:q*2,i:'strings',v:0.25});mel.push({t:t,l:[0,2,3,0,2,0,3,2][i]});t+=q*1.5;
|
|
});
|
|
t+=0.5;
|
|
// Theme 1: the famous waltz melody
|
|
var th1=[[E4,q,2],[E4,q*2,2],[G4,q,3],[G4,q*2,3],[F4,q,1],[F4,q,1],[E4,q,2],[D4,q*2,0],[C4,q,0],[E4,q,2],[G4,q*2,3],[C5,q*3,0]];
|
|
for(var r1=0;r1<2;r1++){
|
|
var v1=0.3+r1*0.1;
|
|
th1.forEach(function(n){bg.push({t:t,f:n[0],d:n[1]*0.9,i:r1<1?'strings':'brass',v:v1});mel.push({t:t,l:n[2]});t+=n[1];});
|
|
t+=0.4;
|
|
}
|
|
// Theme 2: contrasting phrase
|
|
var th2=[[G4,q,3],[A4,q,1],[G4,q,3],[E4,q*2,2],[C4,q,0],[D4,q,0],[E4,q,2],[F4,q,1],[E4,q*2,2],[D4,q,0],[C4,q*3,0]];
|
|
for(var r2=0;r2<2;r2++){
|
|
var v2=0.4+r2*0.1;
|
|
th2.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'strings',v:v2});
|
|
if(r2>0)bg.push({t:t,f:n[0],d:n[1]*0.9,i:'brass',v:v2*0.5});
|
|
mel.push({t:t,l:n[2]});t+=n[1];
|
|
});
|
|
t+=0.4;
|
|
}
|
|
// Recap theme 1 with full orchestra
|
|
th1.forEach(function(n){
|
|
bg.push({t:t,f:n[0],d:n[1]*0.9,i:'brass',v:0.55});bg.push({t:t,f:n[0],d:n[1]*0.9,i:'strings',v:0.45});
|
|
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];
|
|
});
|
|
t+=0.4;
|
|
// Finale waltz chords
|
|
for(var c=0;c<4;c++){
|
|
[C4,E4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:q*2.5,i:'brass',v:0.6});bg.push({t:t,f:f,d:q*2.5,i:'strings',v:0.5});});
|
|
mel.push({t:t,l:4});t+=q*3;
|
|
}
|
|
[C4,E4,G4,C5].forEach(function(f){bg.push({t:t,f:f,d:2.5,i:'brass',v:0.6});bg.push({t:t,f:f,d:2.5,i:'strings',v:0.5});});
|
|
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 ──
|
|
var bumblebee=tp("Flight of the Bumblebee","Rimsky-Korsakov",180,
|
|
[E5,Eb5,D5,C5,B4,Bb4,A4,Ab4,G4,Gb4,F4,E4,F4,Gb4,G4,Ab4,A4,Bb4,B4,C5,D5,Eb5],
|
|
[2,1,0,2,3,2,1,0,3,2,1,0,1,2,3,0,1,2,3,2,0,1],
|
|
[{d:0.20,v:0.2,i:'strings'},{d:0.16,v:0.26,i:'strings'},{d:0.14,v:0.32,i:'strings'},{d:0.12,v:0.38,i:'strings'},{d:0.11,v:0.44,i:'strings'},{d:0.10,v:0.48,i:'strings'},{d:0.09,v:0.52,i:'strings'},{d:0.08,v:0.56,i:'strings'},{d:0.07,v:0.6,i:'strings'}],
|
|
[E4,Ab4,B4,E5]);
|
|
|
|
// ── Dance of the Sugar Plum Fairy ──
|
|
var sugarplum=(function(){
|
|
var bg=[],mel=[],t=0,q=0.4;
|
|
var theme=[E4,Eb4,E4,B3,E4,Eb4,E4,B3,E4,F4,E4,D4,C4,B3,A3,B3];
|
|
var tLn=[2,1,2,3,2,1,2,3,2,1,2,0,1,3,0,3];
|
|
var ans=[E4,G4,B4,E5,B4,G4,E4,B3];
|
|
var aLn=[2,3,0,2,0,3,2,3];
|
|
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;
|
|
}
|
|
[E4,G4,B4,E5].forEach(function(f){bg.push({t:t,f:f,d:2.0,i:'strings',v:0.5});});
|
|
bg.push({t:t,f:E3,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) ──
|
|
var habanera=tp("Habanera (Carmen)","Bizet",108,
|
|
[D5,Db5,C5,B4,Bb4,A4,Ab4,G4,Gb4,F4,E4,F4,Gb4,G4,Ab4,A4,Bb4,A4,Ab4,G4],
|
|
[0,1,2,3,2,1,0,3,2,1,0,1,2,3,0,1,2,1,0,3],
|
|
[{d:0.32,v:0.2,i:'strings'},{d:0.30,v:0.28,i:'strings'},{d:0.28,v:0.35,i:'strings'},{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,F4,A4,D5]);
|
|
|
|
// ── Spring (Four Seasons) ──
|
|
var spring=tp("Spring (Four Seasons)","Vivaldi",120,
|
|
[F4,A4,C5,A4,F4,G4,A4,Bb4,C5,D5,C5,Bb4,A4,G4,F4,E4,F4,G4,A4,C5],
|
|
[0,1,2,1,0,3,1,2,2,0,2,2,1,3,0,2,0,3,1,2],
|
|
[{d:0.24,v:0.22,i:'strings'},{d:0.22,v:0.28,i:'strings'},{d:0.20,v:0.35,i:'strings'},{d:0.18,v:0.42,i:'strings'},{d:0.16,v:0.48,i:'brass'},{d:0.14,v:0.54,i:'brass'},{d:0.12,v:0.6,i:'brass'}],
|
|
[F4,A4,C5,F5]);
|
|
|
|
// ── 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,C5,B4,A4];
|
|
var p2Ln=[1,2,0,3,2,0,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];
|
|
var melody2=[A4,Gb4,G4,A4,Gb4,D4,Gb4,G4,A4,B4,G4,A4,Gb4,D4,E4,Gb4];
|
|
var m2Ln=[1,2,3,1,2,0,2,3,1,0,3,1,2,0,1,2];
|
|
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 ──
|
|
var odeToJoy=tp("Ode to Joy","Beethoven",100,
|
|
[E4,E4,F4,G4,G4,F4,E4,D4,C4,C4,D4,E4,E4,D4,D4,C4,C4,D4,E4,D4,C4,C4],
|
|
[2,2,1,3,3,1,2,0,0,0,1,2,2,0,0,0,0,1,2,0,0,0],
|
|
[{d:0.42,v:0.18,i:'woodwind'},{d:0.40,v:0.25,i:'strings'},{d:0.38,v:0.32,i:'strings'},{d:0.36,v:0.4,i:'brass'},{d:0.34,v:0.48,i:'brass'}],
|
|
[C4,E4,G4,C5]);
|
|
|
|
// ── 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,F4,E4,C4,D4,E4,F4,E4,D4,C4,E4,F4,G4,A4,C5,A4,G4,F4,C4],
|
|
[0,2,1,3,0,3,1,2,0,1,2,1,2,1,0,2,1,3,0,0,0,3,1,0],
|
|
[2,1,1,2,2,1,1,2,2,1,2,1,2,2,2,1,1,2,2,2,1,1,2,3],
|
|
4,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,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,C5,D5,Eb4,C5,A4,Bb4,F4,Bb4,Bb4,Bb4,C5,D5,D5,D5,C5,D5,Eb4,C5,A4,Bb4],
|
|
[1,2,2,2,0,1,1,1,0,1,2,0,0,2,1,2,2,2,0,1,1,1,0,1,2,0,0,2],
|
|
[1,2,1,1,1,2,1,1,1,1,1,1,1,3,1,2,1,1,1,2,1,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);
|
|
return [jingle,silent,deck,wish,ocometree,firstnoel,joy,away,twelve,hark];
|
|
})();
|
|
|
|
// ── 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,E5,D5,C5,A4,G4,G4,C5,E5,E5,D5,E5,E5,G5,G5,E5,C5,C5,A4,G4,G4,C5,E5,E5,D5,C5],
|
|
[3,0,2,2,1,0,0,3,3,0,2,2,1,2,2,3,3,2,0,0,0,3,3,0,2,2,1,0],
|
|
[1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,2,1,2,1,2,1,2,1,2,1,2,1,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,C5,D5,C5,D5,C5,A4,F4,G4,A4,G4,F4,G4],
|
|
[0,1,1,1,0,3,1,3,0,1,1,0,0,1,0,1,0,1,0,0,1,3,0,3,1,3],
|
|
[1,2,1,1,1,2,1,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,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,Bb4,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,2,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,F4,E4,F4,G4,A4,G4,F4,E4,C4,D4,E4,F4,G4,F4,E4,F4,G4,A4,G4,F4,E4,D4,C4,E4,G4,E4,C4],
|
|
[3,1,2,1,3,0,3,1,2,0,1,2,1,3,1,2,1,3,0,3,1,2,1,0,2,3,2,0],
|
|
[1,1,1,1,1,2,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1,2,1,4],
|
|
5,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,
|
|
[G4,C5,C5,E5,D5,C5,A4,G4,E4,G4,A4,G4,E4,C4,D4,E4,G4,C5,C5,E5,D5,C5,A4,G4,E4,G4,A4,C5,G4],
|
|
[3,0,0,2,1,0,0,3,2,3,0,3,2,0,1,2,3,0,0,2,1,0,0,3,2,3,0,0,3],
|
|
[2,2,1,2,1,1,2,2,1,1,1,2,1,2,1,1,2,2,1,2,1,1,2,2,1,1,2,1,4],
|
|
4,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,E4,C4,D4,E4,F4,G4,A4,G4,E4,C4,D4,C4,C4,E4,G4,A4,G4,E4,F4,E4,D4,C4],
|
|
[0,2,1,2,0,1,2,1,3,0,3,2,0,1,0,0,2,3,0,3,2,1,2,1,0],
|
|
[1,1,1,2,1,1,1,1,2,1,1,1,2,1,3,1,1,2,1,1,1,1,1,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,F4,E4,D4,E4,F4,G4,Ab4,G4,F4,E4,F4,G4,Ab4,B4,Ab4,G4,F4,E4,F4,G4,Ab4,G4,F4,E4,D4,E4],
|
|
[2,1,2,1,2,1,3,0,3,1,2,1,3,0,3,0,3,1,2,1,3,0,3,1,2,1,2],
|
|
[1,1,1,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,4],
|
|
5,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,D4,E4,F4,G4,A4,G4,F4,E4,D4,C4,D4,E4,F4,G4,C5,B4,A4,G4,F4,E4,F4,G4,A4,G4,F4,E4,D4,C4],
|
|
[0,1,2,1,3,0,3,1,2,1,0,1,2,1,3,0,3,0,3,1,2,1,3,0,3,1,2,1,0],
|
|
[1,1,1,1,2,1,1,1,1,1,2,1,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,4],
|
|
4,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];
|
|
})();
|
|
|
|
var SYNTH_GENRES={"Singalong":SINGALONG_PIECES,"Christmas":CHRISTMAS_PIECES,"Folk":FOLK_PIECES,"Spirituals":SPIRITUAL_PIECES,"World":WORLD_PIECES,"Classical":CLASSICAL_PIECES,"Video Games":VIDEOGAME_PIECES,"Chiptune Originals":CHIPTUNE_PIECES};
|
|
|
|
function buildClassicalNotes(piece){
|
|
return 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;});
|
|
}
|
|
|
|
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 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});
|
|
return res.sort((a,b)=>a.time-b.time);
|
|
}
|
|
|
|
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>
|