ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/Jennys-Drumste / drum-song-mode.html

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/Jennys-Drumste / drum-song-mode.html
DateNot specified in source
RepositoryJanady13/Jennys-Drumste
Revisionc601bf1648fe71058835589331c7f7e669940b8b
Branchmain
Path or archive memberdrum-song-mode.html
Original SHA-2568a368aa187c3f5e893a0a52d14aa0de1b649c80e5405ed007d43dfc089076231
Git blob SHA98e13c4edd668547b33a77d03992a4927008a680
Reading-copy SHA-2568a368aa187c3f5e893a0a52d14aa0de1b649c80e5405ed007d43dfc089076231

Original source ↗

Every recorded occurrence

These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.

Original recordBranch or archiveRevision / identifierOriginal location
Janady13/Jennys-Drumste / drum-song-mode.htmlmainc601bf1648fe71058835589331c7f7e669940b8bOpen original
Janady13/Jennys-Drumste / drum-song-mode.htmlclaude/complete-integration-feature-011CUkTzyyk8bKmXaRXJAR6101c36ac1a180916e11864b4573b8c1999e2c5db3Open original

Recovered source transcript

Download complete source text ↓1,506 lines · 48.7 KB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DrumMaster Pro - Learn Real Songs | YouTube Integration</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;600;700&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } :root { --primary: #00ffcc; --secondary: #7c3aed; --accent: #ff00ff; --dark: #0f0f23; --darker: #050510; --light: #ffffff; --success: #10b981; --error: #ef4444; --warning: #f59e0b; } body { font-family: 'Inter', sans-serif; background: var(--darker); color: var(--light); overflow-x: hidden; min-height: 100vh; } .app-container { display: grid; grid-template-columns: 250px 1fr 300px; height: 100vh; gap: 20px; padding: 20px; background: linear-gradient(135deg, rgba(124, 58, 237, 0.05) 0%, rgba(0, 255, 204, 0.05) 100%); } /* Header */ .header { grid-column: 1 / -1; background: rgba(15, 15, 35, 0.95); backdrop-filter: blur(10px); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 20px; padding: 20px; display: flex; justify-content: space-between; align-items: center; } .logo { font-family: 'Space Grotesk', sans-serif; font-size: 1.8em; font-weight: 700; background: linear-gradient(135deg, var(--primary), var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .connection-status { display: flex; align-items: center; gap: 10px; padding: 10px 20px; background: rgba(0, 255, 204, 0.1); border: 1px solid rgba(0, 255, 204, 0.3); border-radius: 50px; } .connection-status.connected { background: rgba(16, 185, 129, 0.1); border-color: var(--success); } /* Left Sidebar - Song Library */ .sidebar { background: rgba(15, 15, 35, 0.95); backdrop-filter: blur(10px); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 20px; padding: 20px; overflow-y: auto; max-height: calc(100vh - 120px); } .sidebar h2 { font-family: 'Space Grotesk', sans-serif; font-size: 1.3em; margin-bottom: 20px; color: var(--primary); } .search-box { width: 100%; padding: 12px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 10px; color: var(--light); font-size: 1em; margin-bottom: 20px; } .search-box:focus { outline: none; border-color: var(--primary); background: rgba(255, 255, 255, 0.08); } .song-categories { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; } .category-tab { padding: 10px 15px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 10px; cursor: pointer; transition: all 0.3s; } .category-tab:hover { background: rgba(0, 255, 204, 0.1); border-color: var(--primary); } .category-tab.active { background: linear-gradient(135deg, rgba(0, 255, 204, 0.2), rgba(255, 0, 255, 0.1)); border-color: var(--primary); } .song-list { display: flex; flex-direction: column; gap: 10px; } .song-card { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 12px; padding: 12px; cursor: pointer; transition: all 0.3s; } .song-card:hover { background: rgba(0, 255, 204, 0.05); border-color: var(--primary); transform: translateX(5px); } .song-card.active { background: linear-gradient(135deg, rgba(0, 255, 204, 0.1), rgba(255, 0, 255, 0.05)); border-color: var(--primary); } .song-title { font-weight: 600; margin-bottom: 4px; } .song-artist { color: rgba(255, 255, 255, 0.6); font-size: 0.9em; margin-bottom: 8px; } .song-stats { display: flex; gap: 15px; font-size: 0.85em; color: rgba(255, 255, 255, 0.5); } .difficulty-badge { padding: 2px 8px; background: rgba(255, 255, 255, 0.1); border-radius: 20px; font-size: 0.8em; } .difficulty-easy { background: rgba(16, 185, 129, 0.2); color: #10b981; } .difficulty-medium { background: rgba(245, 158, 11, 0.2); color: #f59e0b; } .difficulty-hard { background: rgba(239, 68, 68, 0.2); color: #ef4444; } /* Main Content Area */ .main-content { display: flex; flex-direction: column; gap: 20px; max-height: calc(100vh - 120px); } /* YouTube Video Player */ .video-section { background: rgba(15, 15, 35, 0.95); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 20px; padding: 20px; flex: 1; } .youtube-input { display: flex; gap: 10px; margin-bottom: 20px; } .youtube-input input { flex: 1; padding: 12px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 10px; color: var(--light); font-size: 1em; } .btn { padding: 12px 24px; background: linear-gradient(135deg, var(--primary), var(--accent)); color: var(--dark); border: none; border-radius: 10px; font-weight: 600; cursor: pointer; transition: all 0.3s; } .btn:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(0, 255, 204, 0.3); } .btn-outline { background: transparent; border: 2px solid var(--primary); color: var(--primary); } .btn-outline:hover { background: var(--primary); color: var(--dark); } #youtube-player { width: 100%; aspect-ratio: 16/9; background: #000; border-radius: 15px; overflow: hidden; } .video-controls { display: flex; gap: 10px; margin-top: 20px; align-items: center; flex-wrap: wrap; } .tempo-slider { flex: 1; min-width: 150px; } .tempo-slider input { width: 100%; -webkit-appearance: none; height: 6px; background: rgba(0, 255, 204, 0.1); border-radius: 3px; outline: none; } .tempo-slider input::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; background: var(--primary); border-radius: 50%; cursor: pointer; } .tempo-display { padding: 8px 16px; background: rgba(0, 255, 204, 0.1); border-radius: 8px; font-family: 'Space Grotesk', monospace; } /* Drum Pattern Display */ .pattern-section { background: rgba(15, 15, 35, 0.95); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 20px; padding: 20px; height: 300px; overflow-y: auto; } .pattern-timeline { position: relative; background: rgba(0, 0, 0, 0.3); border-radius: 10px; padding: 10px; min-height: 200px; } .pattern-tracks { display: flex; flex-direction: column; gap: 5px; } .track { display: flex; align-items: center; height: 30px; background: rgba(255, 255, 255, 0.02); border-radius: 5px; padding: 0 10px; } .track-label { width: 80px; font-size: 0.9em; color: var(--primary); } .track-beats { flex: 1; display: flex; gap: 2px; height: 20px; } .beat { flex: 1; background: rgba(255, 255, 255, 0.05); border-radius: 3px; transition: all 0.1s; } .beat.active { background: var(--primary); box-shadow: 0 0 10px var(--primary); } .beat.hit { background: var(--success); animation: hitPulse 0.3s; } .beat.upcoming { background: var(--warning); animation: pulse 0.5s infinite; } @keyframes hitPulse { 0% { transform: scale(1); } 50% { transform: scale(1.3); } 100% { transform: scale(1); } } @keyframes pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } } .playhead { position: absolute; width: 2px; height: 100%; background: var(--accent); top: 0; left: 0; box-shadow: 0 0 20px var(--accent); transition: left 0.1s linear; } /* Right Panel - Drum Kit */ .drum-panel { background: rgba(15, 15, 35, 0.95); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 20px; padding: 20px; display: flex; flex-direction: column; gap: 20px; max-height: calc(100vh - 120px); } .stats-display { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } .stat-card { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 10px; padding: 10px; text-align: center; } .stat-value { font-size: 1.5em; font-weight: 700; color: var(--primary); } .stat-label { font-size: 0.8em; color: rgba(255, 255, 255, 0.6); } .drum-kit-mini { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; flex: 1; } .drum-pad { background: rgba(255, 255, 255, 0.02); border: 2px solid rgba(0, 255, 204, 0.2); border-radius: 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; transition: all 0.1s; padding: 10px; } .drum-pad.active { background: var(--primary); border-color: var(--primary); color: var(--dark); transform: scale(0.95); } .drum-pad.target { animation: targetPulse 0.5s infinite; border-color: var(--warning); background: rgba(245, 158, 11, 0.1); } @keyframes targetPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } } .drum-icon { font-size: 1.5em; margin-bottom: 5px; } .drum-name { font-size: 0.8em; font-weight: 600; } /* Song Learning Mode */ .learning-controls { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 10px; padding: 15px; display: flex; flex-direction: column; gap: 10px; } .loop-section { display: flex; gap: 10px; align-items: center; } .loop-input { width: 80px; padding: 5px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 5px; color: var(--light); text-align: center; } /* Modal for Song Import */ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(10px); z-index: 1000; align-items: center; justify-content: center; } .modal.active { display: flex; } .modal-content { background: linear-gradient(135deg, rgba(15, 15, 35, 0.98), rgba(124, 58, 237, 0.1)); border: 1px solid var(--primary); border-radius: 20px; padding: 30px; max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; } .modal-header { font-size: 1.5em; font-weight: 700; margin-bottom: 20px; color: var(--primary); } .tab-tabs { display: flex; gap: 10px; margin-bottom: 20px; } .tab-tab { padding: 10px 20px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 10px; cursor: pointer; transition: all 0.3s; } .tab-tab.active { background: var(--primary); color: var(--dark); border-color: var(--primary); } .tab-content { display: none; } .tab-content.active { display: block; } .tab-editor { width: 100%; min-height: 300px; padding: 15px; background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 10px; color: var(--light); font-family: 'Courier New', monospace; font-size: 0.9em; resize: vertical; } .import-instructions { background: rgba(0, 255, 204, 0.05); border-left: 3px solid var(--primary); padding: 15px; margin-bottom: 20px; border-radius: 5px; } .song-info-form { display: flex; flex-direction: column; gap: 15px; } .form-group { display: flex; flex-direction: column; gap: 5px; } .form-group label { color: rgba(255, 255, 255, 0.8); font-size: 0.9em; } .form-group input, .form-group select { padding: 10px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(0, 255, 204, 0.2); border-radius: 8px; color: var(--light); } /* Progress Tracker */ .progress-section { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(0, 255, 204, 0.1); border-radius: 10px; padding: 15px; } .progress-bar { height: 30px; background: rgba(0, 0, 0, 0.3); border-radius: 15px; overflow: hidden; margin-bottom: 10px; } .progress-fill { height: 100%; background: linear-gradient(90deg, var(--primary), var(--accent)); width: 0%; transition: width 0.3s; display: flex; align-items: center; justify-content: flex-end; padding-right: 10px; font-weight: 600; color: var(--dark); } .section-markers { display: flex; justify-content: space-between; margin-top: 5px; font-size: 0.8em; color: rgba(255, 255, 255, 0.6); } /* Responsive */ @media (max-width: 1200px) { .app-container { grid-template-columns: 1fr; } .sidebar, .drum-panel { display: none; } } </style> </head> <body> <div class="app-container"> <!-- Header --> <div class="header"> <div class="logo">🥁 DrumMaster Pro - Song Mode</div> <div class="connection-status" id="connectionStatus"> <span class="status-dot">🔴</span> <span>Connect MIDI Device</span> </div> <button class="btn" onclick="connectMIDI()">Connect MIDI</button> </div> <!-- Left Sidebar - Song Library --> <div class="sidebar"> <h2>🎵 Song Library</h2> <input type="text" class="search-box" placeholder="Search songs..." id="songSearch"> <div class="song-categories"> <div class="category-tab active" data-category="popular">🔥 Popular</div> <div class="category-tab" data-category="rock">🎸 Rock</div> <div class="category-tab" data-category="pop">🎤 Pop</div> <div class="category-tab" data-category="metal">🤘 Metal</div> <div class="category-tab" data-category="custom">💾 My Songs</div> </div> <div class="song-list" id="songList"> <!-- Songs will be populated here --> </div> <button class="btn btn-outline" style="width: 100%; margin-top: 20px;" onclick="showImportModal()"> + Import Song </button> </div> <!-- Main Content --> <div class="main-content"> <!-- YouTube Video Section --> <div class="video-section"> <div class="youtube-input"> <input type="text" id="youtubeUrl" placeholder="Paste YouTube URL or search for a song..."> <button class="btn" onclick="loadYouTubeVideo()">Load Video</button> </div> <div id="youtube-player"></div> <div class="video-controls"> <button class="btn btn-outline" onclick="playPause()">⏯ Play/Pause</button> <button class="btn btn-outline" onclick="restart()">⏮ Restart</button> <button class="btn btn-outline" onclick="toggleLoop()">🔁 Loop</button> <div class="tempo-slider"> <input type="range" id="tempoSlider" min="50" max="150" value="100"> </div> <div class="tempo-display">Speed: <span id="tempoValue">100</span>%</div> </div> <div class="learning-controls"> <div class="loop-section"> <label>Loop Section:</label> <input type="text" class="loop-input" id="loopStart" placeholder="0:00"> <span>to</span> <input type="text" class="loop-input" id="loopEnd" placeholder="0:30"> <button class="btn btn-outline" onclick="setLoop()">Set Loop</button> </div> </div> </div> <!-- Drum Pattern Display --> <div class="pattern-section"> <div class="pattern-timeline"> <div class="playhead" id="playhead"></div> <div class="pattern-tracks" id="patternTracks"> <!-- Pattern will be displayed here --> </div> </div> <div class="progress-section" style="margin-top: 20px;"> <div class="progress-bar"> <div class="progress-fill" id="progressFill">0%</div> </div> <div class="section-markers"> <span>Intro</span> <span>Verse 1</span> <span>Chorus</span> <span>Verse 2</span> <span>Chorus</span> <span>Bridge</span> <span>Outro</span> </div> </div> </div> </div> <!-- Right Panel - Drum Kit & Stats --> <div class="drum-panel"> <div class="stats-display"> <div class="stat-card"> <div class="stat-value" id="accuracy">0%</div> <div class="stat-label">Accuracy</div> </div> <div class="stat-card"> <div class="stat-value" id="combo">0</div> <div class="stat-label">Combo</div> </div> <div class="stat-card"> <div class="stat-value" id="score">0</div> <div class="stat-label">Score</div> </div> <div class="stat-card"> <div class="stat-value" id="bpm">120</div> <div class="stat-label">BPM</div> </div> </div> <div class="drum-kit-mini"> <div class="drum-pad" data-note="49"> <span class="drum-icon">💥</span> <span class="drum-name">Crash</span> </div> <div class="drum-pad" data-note="46"> <span class="drum-icon">🎩</span> <span class="drum-name">Hi-Hat</span> </div> <div class="drum-pad" data-note="51"> <span class="drum-icon">🔔</span> <span class="drum-name">Ride</span> </div> <div class="drum-pad" data-note="48"> <span class="drum-icon">🥁</span> <span class="drum-name">Tom 1</span> </div> <div class="drum-pad" data-note="38"> <span class="drum-icon">🎵</span> <span class="drum-name">Snare</span> </div> <div class="drum-pad" data-note="45"> <span class="drum-icon">🥁</span> <span class="drum-name">Tom 2</span> </div> <div class="drum-pad" data-note="43"> <span class="drum-icon">🥁</span> <span class="drum-name">Tom 3</span> </div> <div class="drum-pad" data-note="36"> <span class="drum-icon">🦶</span> <span class="drum-name">Kick</span> </div> <div class="drum-pad" data-note="41"> <span class="drum-icon">🥁</span> <span class="drum-name">Floor</span> </div> </div> <button class="btn" style="width: 100%;" onclick="startPractice()"> 🎯 Start Practice </button> </div> </div> <!-- Import Song Modal --> <div class="modal" id="importModal"> <div class="modal-content"> <div class="modal-header">Import Song Pattern</div> <div class="tab-tabs"> <div class="tab-tab active" data-tab="youtube">YouTube</div> <div class="tab-tab" data-tab="tab">Tab Editor</div> <div class="tab-tab" data-tab="midi">MIDI File</div> </div> <div class="tab-content active" data-content="youtube"> <div class="import-instructions"> <strong>Import from YouTube:</strong><br> Enter a YouTube URL and we'll help you create the drum pattern. </div> <div class="song-info-form"> <div class="form-group"> <label>YouTube URL</label> <input type="text" id="importYoutubeUrl" placeholder="https://youtube.com/watch?v=..."> </div> <div class="form-group"> <label>Song Title</label> <input type="text" id="importTitle" placeholder="Enter song title"> </div> <div class="form-group"> <label>Artist</label> <input type="text" id="importArtist" placeholder="Enter artist name"> </div> <div class="form-group"> <label>BPM (Tempo)</label> <input type="number" id="importBPM" placeholder="120" min="60" max="200"> </div> <div class="form-group"> <label>Difficulty</label> <select id="importDifficulty"> <option value="easy">Easy</option> <option value="medium">Medium</option> <option value="hard">Hard</option> </select> </div> </div> </div> <div class="tab-content" data-content="tab"> <div class="import-instructions"> <strong>Tab Format:</strong><br> C = Crash, H = Hi-Hat, S = Snare, K = Kick, T = Tom, R = Ride<br> Use | for bar lines, - for rests </div> <textarea class="tab-editor" id="tabEditor" placeholder="Example: |K---S---K---S---|K---S---K---S---| |H-H-H-H-H-H-H-H-|H-H-H-H-H-H-H-H-| |----------------|C---------------|"></textarea> </div> <div class="tab-content" data-content="midi"> <div class="import-instructions"> <strong>Upload MIDI File:</strong><br> Import drum patterns from MIDI files </div> <input type="file" accept=".mid,.midi" id="midiFileInput"> </div> <div style="display: flex; gap: 10px; margin-top: 20px;"> <button class="btn" onclick="importSong()">Import Song</button> <button class="btn btn-outline" onclick="closeModal()">Cancel</button> </div> </div> </div> <!-- YouTube API --> <script src="https://www.youtube.com/iframe_api"></script> <script> // Song Database with Patterns const SONG_DATABASE = { popular: [ { id: 'seven-nation-army', title: 'Seven Nation Army', artist: 'The White Stripes', youtubeId: '0J2QdDbelmY', bpm: 124, difficulty: 'easy', pattern: { kick: '1---1---1---1---', snare: '----1-------1---', hihat: '----------------', crash: '1---------------', ride: '----------------' } }, { id: 'billie-jean', title: 'Billie Jean', artist: 'Michael Jackson', youtubeId: 'Zi_XLOBDo_Y', bpm: 118, difficulty: 'medium', pattern: { kick: '1---1---1---1---', snare: '----1-------1---', hihat: '1-1-1-1-1-1-1-1-', crash: '----------------', ride: '----------------' } }, { id: 'smells-like-teen-spirit', title: 'Smells Like Teen Spirit', artist: 'Nirvana', youtubeId: 'hTWKbfoikeg', bpm: 117, difficulty: 'medium', pattern: { kick: '1--1--1-1--1--1-', snare: '----1-------1---', hihat: '1-1---1-1-1---1-', crash: '1---------------', ride: '----------------' } } ], rock: [ { id: 'back-in-black', title: 'Back In Black', artist: 'AC/DC', youtubeId: 'pAgnJDJN4VA', bpm: 92, difficulty: 'medium', pattern: { kick: '1---1---1---1---', snare: '----1-------1---', hihat: '1-1-1-1-1-1-1-1-', crash: '1---------------', ride: '----------------' } }, { id: 'enter-sandman', title: 'Enter Sandman', artist: 'Metallica', youtubeId: 'CD-E-LDc384', bpm: 123, difficulty: 'hard', pattern: { kick: '1-1---1-1-1---1-', snare: '----1-------1---', hihat: '--1---1---1---1-', crash: '1---------------', ride: '----------------' } } ], pop: [ { id: 'blinding-lights', title: 'Blinding Lights', artist: 'The Weeknd', youtubeId: '4NRXx6U8ABQ', bpm: 171, difficulty: 'easy', pattern: { kick: '1-1-1-1-1-1-1-1-', snare: '----1-------1---', hihat: '1111111111111111', crash: '----------------', ride: '----------------' } }, { id: 'uptown-funk', title: 'Uptown Funk', artist: 'Mark Ronson ft. Bruno Mars', youtubeId: 'OPf0YbXqDm0', bpm: 115, difficulty: 'medium', pattern: { kick: '1----1--1----1--', snare: '----1-------1---', hihat: '1-1-1-1-1-1-1-1-', crash: '----------------', ride: '----------------' } } ], metal: [ { id: 'toxicity', title: 'Toxicity', artist: 'System of a Down', youtubeId: 'iywaBOMvYLI', bpm: 134, difficulty: 'hard', pattern: { kick: '11--11--11--11--', snare: '----1-------1---', hihat: '----------------', crash: '1---------------', ride: '1-1-1-1-1-1-1-1-' } }, { id: 'chop-suey', title: 'Chop Suey!', artist: 'System of a Down', youtubeId: 'CSvFpBOe8eY', bpm: 127, difficulty: 'hard', pattern: { kick: '1-1-1-1-1-1-1-1-', snare: '--1---1---1---1-', hihat: '111111----------', crash: '------1---------', ride: '--------11111111' } } ], custom: [] }; // YouTube Player let player; let isPlayerReady = false; let currentSong = null; let isPlaying = false; let currentBeat = 0; let playInterval = null; let stats = { accuracy: 0, combo: 0, score: 0, hits: 0, misses: 0 }; // MIDI variables let midiAccess = null; let midiInput = null; // Initialize YouTube Player function onYouTubeIframeAPIReady() { player = new YT.Player('youtube-player', { height: '100%', width: '100%', videoId: '', playerVars: { 'playsinline': 1, 'controls': 1 }, events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } function onPlayerReady(event) { isPlayerReady = true; console.log('YouTube player ready'); } function onPlayerStateChange(event) { if (event.data == YT.PlayerState.PLAYING) { startPatternSync(); } else if (event.data == YT.PlayerState.PAUSED) { stopPatternSync(); } } // Load YouTube Video function loadYouTubeVideo() { const url = document.getElementById('youtubeUrl').value; const videoId = extractYouTubeId(url); if (videoId && isPlayerReady) { player.loadVideoById(videoId); } } function extractYouTubeId(url) { const regex = /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/; const match = url.match(regex); return match ? match[1] : null; } // Pattern Synchronization function startPatternSync() { if (!currentSong) return; isPlaying = true; const bpm = currentSong.bpm; const beatDuration = 60000 / (bpm * 4); // 16th notes playInterval = setInterval(() => { updatePatternDisplay(); checkUpcomingNotes(); currentBeat = (currentBeat + 1) % 16; }, beatDuration); } function stopPatternSync() { isPlaying = false; if (playInterval) { clearInterval(playInterval); playInterval = null; } } function updatePatternDisplay() { const tracks = document.getElementById('patternTracks'); tracks.innerHTML = ''; if (!currentSong || !currentSong.pattern) return; Object.entries(currentSong.pattern).forEach(([drum, pattern]) => { const track = document.createElement('div'); track.className = 'track'; track.innerHTML = ` <span class="track-label">${drum.charAt(0).toUpperCase() + drum.slice(1)}</span> <div class="track-beats"> ${pattern.split('').map((beat, index) => `<div class="beat ${beat === '1' ? 'active' : ''} ${index === currentBeat ? 'upcoming' : ''}" data-beat="${index}"></div>` ).join('')} </div> `; tracks.appendChild(track); }); // Update playhead position const playhead = document.getElementById('playhead'); playhead.style.left = (currentBeat / 16 * 100) + '%'; } function checkUpcomingNotes() { if (!currentSong || !currentSong.pattern) return; // Check 2 beats ahead for visual cue const lookAhead = (currentBeat + 2) % 16; Object.entries(currentSong.pattern).forEach(([drum, pattern]) => { if (pattern[lookAhead] === '1') { highlightDrum(drum); } }); } function highlightDrum(drumType) { const drumMap = { kick: 36, snare: 38, hihat: 46, crash: 49, ride: 51 }; const note = drumMap[drumType]; const pad = document.querySelector(`.drum-pad[data-note="${note}"]`); if (pad) { pad.classList.add('target'); setTimeout(() => pad.classList.remove('target'), 500); } } // MIDI Connection async function connectMIDI() { try { midiAccess = await navigator.requestMIDIAccess(); const inputs = midiAccess.inputs.values(); for (let input of inputs) { midiInput = input; midiInput.onmidimessage = handleMIDIMessage; break; } if (midiInput) { const status = document.getElementById('connectionStatus'); status.classList.add('connected'); status.innerHTML = '<span class="status-dot">🟢</span><span>MIDI Connected</span>'; } } catch (error) { console.error('MIDI connection failed:', error); alert('MIDI not supported in this browser. Try Chrome or Edge.'); } } function handleMIDIMessage(message) { const [status, note, velocity] = message.data; if (status >= 144 && status <= 159 && velocity > 0) { handleDrumHit(note, velocity); } } function handleDrumHit(note, velocity) { // Visual feedback const pad = document.querySelector(`.drum-pad[data-note="${note}"]`); if (pad) { pad.classList.add('active'); setTimeout(() => pad.classList.remove('active'), 200); } // Check accuracy if playing if (isPlaying && currentSong) { checkHitAccuracy(note); } } function checkHitAccuracy(note) { const drumMap = { 36: 'kick', 38: 'snare', 46: 'hihat', 49: 'crash', 51: 'ride' }; const drumType = drumMap[note]; if (!drumType || !currentSong.pattern[drumType]) return; // Check if hit is within timing window const pattern = currentSong.pattern[drumType]; const expectedHit = pattern[currentBeat] === '1' || pattern[(currentBeat + 1) % 16] === '1'; if (expectedHit) { stats.hits++; stats.combo++; stats.score += 10 * (1 + stats.combo * 0.1); // Visual feedback const beats = document.querySelectorAll(`.track-beats .beat[data-beat="${currentBeat}"]`); beats.forEach(beat => beat.classList.add('hit')); } else { stats.misses++; stats.combo = 0; } updateStats(); } function updateStats() { const total = stats.hits + stats.misses; stats.accuracy = total > 0 ? Math.round((stats.hits / total) * 100) : 0; document.getElementById('accuracy').textContent = stats.accuracy + '%'; document.getElementById('combo').textContent = stats.combo; document.getElementById('score').textContent = Math.round(stats.score); } // Song Library function loadSongLibrary(category = 'popular') { const songList = document.getElementById('songList'); songList.innerHTML = ''; const songs = SONG_DATABASE[category] || []; songs.forEach(song => { const card = document.createElement('div'); card.className = 'song-card'; card.onclick = () => selectSong(song); card.innerHTML = ` <div class="song-title">${song.title}</div> <div class="song-artist">${song.artist}</div> <div class="song-stats"> <span class="difficulty-badge difficulty-${song.difficulty}">${song.difficulty}</span> <span>${song.bpm} BPM</span> </div> `; songList.appendChild(card); }); } function selectSong(song) { currentSong = song; currentBeat = 0; // Update UI document.querySelectorAll('.song-card').forEach(card => card.classList.remove('active')); event.currentTarget.classList.add('active'); // Load YouTube video if (isPlayerReady) { player.loadVideoById(song.youtubeId); } // Display pattern updatePatternDisplay(); // Update BPM display document.getElementById('bpm').textContent = song.bpm; } // Video Controls function playPause() { if (!player) return; if (isPlaying) { player.pauseVideo(); } else { player.playVideo(); } } function restart() { if (!player) return; player.seekTo(0); currentBeat = 0; stats = { accuracy: 0, combo: 0, score: 0, hits: 0, misses: 0 }; updateStats(); } function toggleLoop() { // Implementation for loop functionality const loopStart = document.getElementById('loopStart').value; const loopEnd = document.getElementById('loopEnd').value; // Convert time to seconds and set loop console.log('Loop from', loopStart, 'to', loopEnd); } function setLoop() { // Set specific loop points const start = timeToSeconds(document.getElementById('loopStart').value); const end = timeToSeconds(document.getElementById('loopEnd').value); if (player && start < end) { // Implement loop logic console.log('Setting loop:', start, '-', end); } } function timeToSeconds(time) { const parts = time.split(':'); return parseInt(parts[0]) * 60 + parseInt(parts[1]); } // Import Modal Functions function showImportModal() { document.getElementById('importModal').classList.add('active'); } function closeModal() { document.getElementById('importModal').classList.remove('active'); } function importSong() { const activeTab = document.querySelector('.tab-tab.active').dataset.tab; if (activeTab === 'youtube') { const song = { id: 'custom-' + Date.now(), title: document.getElementById('importTitle').value, artist: document.getElementById('importArtist').value, youtubeId: extractYouTubeId(document.getElementById('importYoutubeUrl').value), bpm: parseInt(document.getElementById('importBPM').value), difficulty: document.getElementById('importDifficulty').value, pattern: generateBasicPattern(parseInt(document.getElementById('importBPM').value)) }; SONG_DATABASE.custom.push(song); loadSongLibrary('custom'); closeModal(); } } function generateBasicPattern(bpm) { // Generate a basic pattern based on BPM return { kick: '1---1---1---1---', snare: '----1-------1---', hihat: '1-1-1-1-1-1-1-1-', crash: '1---------------', ride: '----------------' }; } // Tab switching document.querySelectorAll('.tab-tab').forEach(tab => { tab.addEventListener('click', (e) => { document.querySelectorAll('.tab-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); e.target.classList.add('active'); const content = document.querySelector(`[data-content="${e.target.dataset.tab}"]`); if (content) content.classList.add('active'); }); }); // Category switching document.querySelectorAll('.category-tab').forEach(tab => { tab.addEventListener('click', (e) => { document.querySelectorAll('.category-tab').forEach(t => t.classList.remove('active')); e.target.classList.add('active'); loadSongLibrary(e.target.dataset.category); }); }); // Tempo control document.getElementById('tempoSlider').addEventListener('input', (e) => { const tempo = e.target.value; document.getElementById('tempoValue').textContent = tempo; if (player) { player.setPlaybackRate(tempo / 100); } }); // Search functionality document.getElementById('songSearch').addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); const cards = document.querySelectorAll('.song-card'); cards.forEach(card => { const title = card.querySelector('.song-title').textContent.toLowerCase(); const artist = card.querySelector('.song-artist').textContent.toLowerCase(); if (title.includes(query) || artist.includes(query)) { card.style.display = 'block'; } else { card.style.display = 'none'; } }); }); // Initialize document.addEventListener('DOMContentLoaded', () => { loadSongLibrary('popular'); }); // Keyboard support document.addEventListener('keydown', (e) => { const keyMap = { ' ': 36, // Space: Kick 's': 38, // S: Snare 'h': 46, // H: Hi-hat 'c': 49, // C: Crash 'r': 51, // R: Ride }; const note = keyMap[e.key.toLowerCase()]; if (note) { e.preventDefault(); handleDrumHit(note, 100); } }); function startPractice() { if (!currentSong) { alert('Please select a song first!'); return; } // Reset stats stats = { accuracy: 0, combo: 0, score: 0, hits: 0, misses: 0 }; updateStats(); // Start video if ready if (player) { player.playVideo();

Search equations, builds, manuscript text, and recovered sources.