Implements a full Classical mode with Web Audio synthesizer engine supporting strings, brass, woodwind, bass, timpani, and choir instruments. 26 playable pieces from Beethoven's 5th to Flight of the Bumblebee with 19 minutes of total content and 3,173 hittable melody notes. Includes drums/orchestra hit sound toggle.
1919 lines
90 KiB
HTML
1919 lines
90 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;}
|
|
</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>
|
|
<button class="smbtn" style="margin-top:8px" onclick="goScores()">View All</button>
|
|
</div>
|
|
|
|
<button class="bigbtn" onclick="startGame()">▶ Start</button>
|
|
</div>
|
|
|
|
<!-- GAME -->
|
|
<div class="screen" id="sg" style="padding:0;">
|
|
<div id="hud">
|
|
<button class="smbtn" onclick="goMenu()">← Menu</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">No gamepad</span>
|
|
</div>
|
|
<div id="cwrap"><canvas id="c"></canvas><div id="msg"></div><div id="cdown"></div><div id="pbanner"></div></div>
|
|
</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;
|
|
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) ──
|
|
var b5=(function(){
|
|
var q=0.375,e=q/2,bg=[],mel=[];
|
|
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.6});
|
|
if(freq>200)bg.push({t:t,f:freq/2,d:dur,i:'bass',v:(v||0.35)*0.5});
|
|
}
|
|
function str(t,freq,dur,v){bg.push({t:t,f:freq,d:dur,i:'strings',v:v||0.3});}
|
|
// === EXPOSITION ===
|
|
// Section 1: G-G-G-Eb (THE famous motif, ff)
|
|
orch(0,G4,0.18,0.5);orch(q,G4,0.18,0.5);orch(q*2,G4,0.18,0.5);
|
|
orch(q*3,Eb4,1.8,0.55);
|
|
mel.push({t:0,l:0},{t:q,l:0},{t:q*2,l:0},{t:q*3,l:3});
|
|
// Section 2: F-F-F-D (answer phrase)
|
|
var s=3.2;
|
|
orch(s,F4,0.18,0.5);orch(s+q,F4,0.18,0.5);orch(s+q*2,F4,0.18,0.5);
|
|
orch(s+q*3,D4,1.8,0.55);
|
|
mel.push({t:s,l:1},{t:s+q,l:1},{t:s+q*2,l:1},{t:s+q*3,l:3});
|
|
// Section 3: Motif restated by strings (softer echo)
|
|
s=6.8;
|
|
str(s,G4,0.16,0.3);str(s+q,G4,0.16,0.3);str(s+q*2,G4,0.16,0.3);
|
|
str(s+q*3,Eb4,1.2,0.35);
|
|
mel.push({t:s,l:0},{t:s+q,l:0},{t:s+q*2,l:0},{t:s+q*3,l:3});
|
|
s=9.5;
|
|
str(s,F4,0.16,0.3);str(s+q,F4,0.16,0.3);str(s+q*2,F4,0.16,0.3);
|
|
str(s+q*3,D4,1.2,0.35);
|
|
mel.push({t:s,l:1},{t:s+q,l:1},{t:s+q*2,l:1},{t:s+q*3,l:3});
|
|
// === DEVELOPMENT ===
|
|
// Section 4: Faster motifs (compressed)
|
|
s=12.0;
|
|
orch(s,G4,0.15,0.45);orch(s+e,G4,0.15,0.45);orch(s+e*2,G4,0.15,0.45);
|
|
orch(s+e*3,Eb4,0.5,0.5);
|
|
mel.push({t:s,l:0},{t:s+e,l:0},{t:s+e*2,l:0},{t:s+e*3,l:3});
|
|
s=13.0;
|
|
orch(s,F4,0.15,0.45);orch(s+e,F4,0.15,0.45);orch(s+e*2,F4,0.15,0.45);
|
|
orch(s+e*3,D4,0.5,0.5);
|
|
mel.push({t:s,l:1},{t:s+e,l:1},{t:s+e*2,l:1},{t:s+e*3,l:3});
|
|
// Section 5: Extended scalar passage (descending then ascending)
|
|
s=14.2;
|
|
[G4,F4,Eb4,D4,C4,B3,C4,D4,Eb4,F4,G4,Ab4,Bb4,Ab4,G4,F4,Eb4,D4,C4,B3].forEach(function(f,i){
|
|
var t2=s+i*e;orch(t2,f,0.16,0.4);mel.push({t:t2,l:[2,1,0,1][i%4]});
|
|
});
|
|
// Section 6: Power chords with timpani
|
|
s=18.0;
|
|
[[G4,Eb4],[Ab4,F4],[Bb4,G4],[Ab4,F4],[G4,Eb4],[F4,D4],[Eb4,C4],[D4,B3]].forEach(function(ch,i){
|
|
var t2=s+i*0.7;ch.forEach(function(f){orch(t2,f,0.6,0.55);});
|
|
bg.push({t:t2,f:ch[1]/2,d:0.6,i:'timpani',v:0.5});mel.push({t:t2,l:4});
|
|
});
|
|
// === RECAPITULATION ===
|
|
// Section 7: Motif returns fortissimo with choir
|
|
s=24.0;
|
|
orch(s,G4,0.18,0.6);orch(s+q,G4,0.18,0.6);orch(s+q*2,G4,0.18,0.6);
|
|
orch(s+q*3,Eb4,1.5,0.65);bg.push({t:s+q*3,f:Eb4,d:1.5,i:'choir',v:0.3});
|
|
mel.push({t:s,l:0},{t:s+q,l:0},{t:s+q*2,l:0},{t:s+q*3,l:3});
|
|
s=27.0;
|
|
orch(s,F4,0.18,0.6);orch(s+q,F4,0.18,0.6);orch(s+q*2,F4,0.18,0.6);
|
|
orch(s+q*3,D4,1.5,0.65);bg.push({t:s+q*3,f:D4,d:1.5,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});
|
|
// Section 8: Ascending scalar with full orchestra
|
|
s=30.0;
|
|
[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;orch(t2,f,0.16,0.5);mel.push({t:t2,l:[0,1,2,3][i%4]});
|
|
});
|
|
// Section 9: Rapid motif at rising pitches
|
|
s=33.5;
|
|
[[G4,Eb4],[Ab4,F4],[Bb4,G4],[C5,Ab4],[D5,Bb4]].forEach(function(fr,fi){
|
|
var t2=s+fi*0.9;
|
|
orch(t2,fr[0],0.14,0.55);orch(t2+e,fr[0],0.14,0.55);orch(t2+e*2,fr[0],0.14,0.55);
|
|
orch(t2+e*3,fr[1],0.45,0.6);
|
|
mel.push({t:t2,l:0},{t:t2+e,l:0},{t:t2+e*2,l:0},{t:t2+e*3,l:3});
|
|
});
|
|
// Section 10: Dramatic sustained chords with choir
|
|
s=38.5;
|
|
[[C4,Eb4,G4],[Bb3,D4,F4],[Ab3,C4,Eb4],[G3,B3,D4]].forEach(function(ch,i){
|
|
var t2=s+i*1.5;
|
|
ch.forEach(function(f){orch(t2,f,1.3,0.6);bg.push({t:t2,f:f,d:1.3,i:'choir',v:0.25});});
|
|
bg.push({t:t2,f:NT.C3,d:1.3,i:'timpani',v:0.55});
|
|
mel.push({t:t2,l:4},{t:t2+0.75,l:i%2===0?0:1});
|
|
});
|
|
// Section 11: Final motif statement fff
|
|
s=45.0;
|
|
orch(s,G4,0.2,0.7);orch(s+q,G4,0.2,0.7);orch(s+q*2,G4,0.2,0.7);
|
|
orch(s+q*3,Eb4,2.0,0.7);
|
|
bg.push({t:s+q*3,f:Eb4,d:2.0,i:'choir',v:0.4});
|
|
bg.push({t:s,f:G3,d:0.2,i:'timpani',v:0.6});
|
|
mel.push({t:s,l:0},{t:s+q,l:0},{t:s+q*2,l:0},{t:s+q*3,l:3});
|
|
// Section 12: Final chords
|
|
s=48.5;
|
|
[0,0.5,1.0,1.5,2.0].forEach(function(dt){
|
|
var t2=s+dt;
|
|
[C4,Eb4,G4].forEach(function(f){orch(t2,f,0.4,0.7);});
|
|
bg.push({t:t2,f:NT.C3,d:0.4,i:'timpani',v:0.65});mel.push({t:t2,l:4});
|
|
});
|
|
// Very final long chord
|
|
s=51.0;
|
|
[C4,Eb4,G4,C5].forEach(function(f){orch(s,f,2.5,0.65);bg.push({t:s,f:f,d:2.5,i:'choir',v:0.35});});
|
|
bg.push({t:s,f:NT.C3,d:2.0,i:'timpani',v:0.6});
|
|
return {n:"Beethoven's 5th (Opening)",composer:"Beethoven",bpm:160,duration:54,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];
|
|
})();
|
|
|
|
function buildClassicalNotes(piece){
|
|
return piece.melody.filter(function(m){return activePads[m.l];})
|
|
.map(function(m){
|
|
// Find matching frequency from background notes at same time
|
|
var freq=261.63; // default C4
|
|
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,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,n.d,n.i,n.v,gameSynthNodes);
|
|
}
|
|
setTimeout(function(){if(playing)endTurn();},piece.duration*1000+800);
|
|
}
|
|
|
|
// ── STATE ──────────────────────────────────────────────
|
|
let mode="builtin",curGenre=Object.keys(GENRES)[0],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,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 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(){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 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=curGenre==="Classical"?"block":"none";
|
|
}
|
|
|
|
// ── MENU ───────────────────────────────────────────────
|
|
function buildMenu(){
|
|
const gt=document.getElementById("gtabs");gt.innerHTML="";
|
|
Object.keys(GENRES).forEach(g=>{
|
|
const b=document.createElement("button");
|
|
b.className="gtab"+(g===curGenre?" active":"");
|
|
b.textContent=g;b.onclick=()=>{curGenre=g;curIdx=0;stopPreview();buildMenu();};
|
|
gt.appendChild(b);
|
|
});
|
|
// Classical genre tab
|
|
var cb=document.createElement("button");
|
|
cb.className="gtab"+(curGenre==="Classical"?" active":"");
|
|
cb.textContent="\uD83C\uDFBB Classical";cb.onclick=function(){curGenre="Classical";curIdx=0;stopPreview();buildMenu();};
|
|
gt.appendChild(cb);
|
|
const rg=document.getElementById("rgrid");rg.innerHTML="";
|
|
(curGenre==="Classical"?CLASSICAL_PIECES:GENRES[curGenre]).forEach((r,i)=>{
|
|
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();};
|
|
rg.appendChild(b);
|
|
});
|
|
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();
|
|
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=curGenre==="Classical"?CLASSICAL_PIECES[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=curGenre==="Classical"?CLASSICAL_PIECES[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;}
|
|
|
|
function buildNotes(r){
|
|
const bs=60/r.bpm,bars=r.bars||8,res=[];
|
|
for(let bar=0;bar<bars;bar++)
|
|
for(const p of r.p)
|
|
if(activePads[p.l]) // only include active lanes
|
|
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,bars=r.bars||8;
|
|
gameStartTime=ac.currentTime;
|
|
// Schedule ALL drum hits as audio — this IS the music
|
|
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;
|
|
setTimeout(()=>{if(playing)endTurn();},totalSec*1000+800);
|
|
}
|
|
|
|
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 isClassicalOrch=gameRhythm&&gameRhythm.isClassical&&classicalHitMode==='orchestra';
|
|
if(!isClassicalOrch){
|
|
playDrumAt(getAc(),lane,getAc().currentTime,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.3,'strings',0.5,[]);
|
|
playSynth(ac,best.freq,ct,0.3,'brass',0.3,[]);
|
|
} else if(isClassicalOrch){
|
|
playDrumAt(getAc(),lane,getAc().currentTime,8,[]);
|
|
}
|
|
showMsg(bestD<0.05?"PERFECT!":"GOOD",bestD<0.05?"#68d391":"#d69e2e");
|
|
} else {
|
|
// Miss — play drum sound as feedback even in orchestra mode
|
|
if(isClassicalOrch)playDrumAt(getAc(),lane,getAc().currentTime,4,[]);
|
|
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();}
|
|
}
|
|
|
|
// 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){
|
|
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);
|
|
}
|
|
}
|
|
|
|
// ── INPUT ──────────────────────────────────────────────
|
|
window.addEventListener("keydown",e=>{
|
|
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);
|
|
gp.buttons.forEach((btn,bi)=>{
|
|
if(btn.pressed&&!prevBtns[bi]){
|
|
const m={0:2,1:3,2:0,3:1,4:4,8:4};
|
|
if(m[bi]!==undefined)triggerPad(m[bi]);
|
|
}
|
|
prevBtns[bi]=btn.pressed;
|
|
});
|
|
}
|
|
if(!found)document.getElementById("gpd").textContent="No gamepad";
|
|
requestAnimationFrame(pollGP);
|
|
}
|
|
pollGP();
|
|
</script>
|
|
</body>
|
|
</html>
|