NativeAngels's picture
Add 3 files
ddd03df verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>YouTube Chord Analyzer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/tonal@4.6.2/dist/tonal.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@magenta/music@1.23.1/es6/core.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.piano-key {
position: relative;
border: 1px solid #333;
display: flex;
align-items: flex-end;
justify-content: center;
cursor: pointer;
user-select: none;
}
.white-key {
width: 40px;
height: 160px;
background-color: white;
z-index: 1;
border-radius: 0 0 5px 5px;
box-shadow: 0 5px 5px rgba(0,0,0,0.2);
}
.black-key {
width: 24px;
height: 100px;
background-color: black;
margin-left: -12px;
margin-right: -12px;
z-index: 2;
border-radius: 0 0 3px 3px;
}
.active-key {
background-color: #3b82f6;
}
.active-black-key {
background-color: #1d4ed8;
}
.chord-progression {
background-color: rgba(59, 130, 246, 0.2);
border-left: 3px solid #3b82f6;
}
.visualizer-container {
position: relative;
height: 200px;
background-color: #1e293b;
border-radius: 8px;
overflow: hidden;
}
.audio-wave {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
}
.audio-bar {
flex-grow: 1;
background-color: rgba(59, 130, 246, 0.6);
margin-right: 2px;
transition: height 0.05s ease;
}
.audio-bar:last-child {
margin-right: 0;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.analyzing {
animation: pulse 1.5s infinite;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="mb-8 text-center">
<h1 class="text-4xl font-bold text-blue-600 mb-2">YouTube Chord Analyzer</h1>
<p class="text-gray-600 text-lg">Detect chords and keys from any YouTube video in real-time</p>
</header>
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<div class="flex flex-col md:flex-row gap-6">
<div class="flex-1">
<div class="mb-4">
<label for="youtube-url" class="block text-sm font-medium text-gray-700 mb-1">YouTube Video URL</label>
<div class="flex gap-2">
<input type="text" id="youtube-url" placeholder="https://www.youtube.com/watch?v=..."
class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500">
<button id="analyze-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-medium transition-colors">
<i class="fas fa-play mr-2"></i> Analyze
</button>
</div>
</div>
<div id="video-container" class="aspect-w-16 aspect-h-9 bg-gray-200 rounded-lg overflow-hidden mb-4">
<div class="flex items-center justify-center h-full text-gray-500">
<i class="fas fa-music text-4xl"></i>
</div>
</div>
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-2">
<button id="play-btn" class="bg-blue-600 hover:bg-blue-700 text-white p-2 rounded-full disabled:opacity-50" disabled>
<i class="fas fa-play"></i>
</button>
<button id="pause-btn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 p-2 rounded-full disabled:opacity-50" disabled>
<i class="fas fa-pause"></i>
</button>
<span id="time-display" class="text-sm text-gray-600">00:00 / 00:00</span>
</div>
<div id="status-indicator" class="flex items-center gap-2">
<span class="text-sm font-medium text-gray-500">Ready</span>
<div class="w-3 h-3 rounded-full bg-gray-400"></div>
</div>
</div>
<div class="visualizer-container mb-4">
<div id="audio-wave" class="audio-wave"></div>
</div>
</div>
<div class="flex-1">
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="font-medium text-lg text-gray-800 mb-3">Current Analysis</h3>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="bg-white p-3 rounded-lg shadow-sm">
<p class="text-sm text-gray-500 mb-1">Detected Key</p>
<p id="current-key" class="text-2xl font-bold text-blue-600">-</p>
</div>
<div class="bg-white p-3 rounded-lg shadow-sm">
<p class="text-sm text-gray-500 mb-1">Current Chord</p>
<p id="current-chord" class="text-2xl font-bold text-blue-600">-</p>
</div>
</div>
<div class="mb-4">
<p class="text-sm text-gray-500 mb-2">Chord Notes</p>
<div id="piano" class="flex relative h-40 mb-2"></div>
</div>
<div>
<p class="text-sm text-gray-500 mb-2">Chord Progression</p>
<div id="chord-progression" class="flex gap-2 overflow-x-auto py-2">
<div class="text-center">
<div class="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mx-auto mb-1">
<span class="text-gray-600">?</span>
</div>
<span class="text-xs text-gray-500">0:00</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">Chord Analysis Results</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Bar</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Measure</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chord</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Notes</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Duration</th>
</tr>
</thead>
<tbody id="results-table" class="bg-white divide-y divide-gray-200">
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">No analysis data yet. Enter a YouTube URL and click "Analyze"</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
// Initialize variables
let player;
let audioContext;
let analyser;
let dataArray;
let animationId;
let currentChord = '';
let detectedKey = '';
let chordHistory = [];
let barCount = 1;
let measureCount = 1;
let lastChordChangeTime = 0;
// DOM elements
const youtubeUrlInput = document.getElementById('youtube-url');
const analyzeBtn = document.getElementById('analyze-btn');
const videoContainer = document.getElementById('video-container');
const playBtn = document.getElementById('play-btn');
const pauseBtn = document.getElementById('pause-btn');
const timeDisplay = document.getElementById('time-display');
const statusIndicator = document.getElementById('status-indicator');
const statusText = statusIndicator.querySelector('span');
const statusDot = statusIndicator.querySelector('div');
const audioWave = document.getElementById('audio-wave');
const currentKeyDisplay = document.getElementById('current-key');
const currentChordDisplay = document.getElementById('current-chord');
const piano = document.getElementById('piano');
const chordProgression = document.getElementById('chord-progression');
const resultsTable = document.getElementById('results-table');
// Initialize piano keyboard
function initPiano() {
piano.innerHTML = '';
const whiteKeys = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
const blackKeys = ['C#', 'D#', '', 'F#', 'G#', 'A#', ''];
whiteKeys.forEach((note, i) => {
const key = document.createElement('div');
key.className = 'piano-key white-key';
key.dataset.note = note;
key.innerHTML = `<span class="text-xs text-gray-500 mb-1">${note}</span>`;
piano.appendChild(key);
// Add black keys between appropriate white keys
if (i < 6 && blackKeys[i] !== '') {
const blackKey = document.createElement('div');
blackKey.className = 'piano-key black-key';
blackKey.dataset.note = blackKeys[i];
piano.appendChild(blackKey);
}
});
}
// Highlight piano keys for current chord
function highlightChord(chord) {
// Reset all keys
document.querySelectorAll('.piano-key').forEach(key => {
if (key.classList.contains('white-key')) {
key.classList.remove('active-key');
} else {
key.classList.remove('active-black-key');
}
});
if (!chord
</html>