Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Sam Audio Isolate | AI Sound Separation</title> | |
| <meta name="description" content="Advanced AI audio tool to isolate vocals, drums, bass, and other instruments from any track."> | |
| <!-- Import Google Fonts --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> | |
| <!-- Import FontAwesome for Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary: #6366f1; | |
| --primary-glow: rgba(99, 102, 241, 0.5); | |
| --secondary: #ec4899; | |
| --bg-dark: #0f172a; | |
| --bg-card: #1e293b; | |
| --text-main: #f8fafc; | |
| --text-muted: #94a3b8; | |
| --success: #10b981; | |
| --border: #334155; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| line-height: 1.6; | |
| overflow-x: hidden; | |
| background-image: | |
| radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 20%), | |
| radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.15) 0%, transparent 20%); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* Header */ | |
| header { | |
| padding: 1.5rem 5%; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| border-bottom: 1px solid var(--border); | |
| background: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(10px); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .logo { | |
| font-size: 1.5rem; | |
| font-weight: 800; | |
| letter-spacing: -0.05em; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .logo i { | |
| color: var(--primary); | |
| font-size: 1.8rem; | |
| } | |
| .logo span { | |
| background: linear-gradient(to right, var(--primary), var(--secondary)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .built-with-link { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| transition: color 0.3s; | |
| cursor: pointer; | |
| } | |
| .built-with-link:hover { | |
| color: var(--primary); | |
| } | |
| /* Main Layout */ | |
| main { | |
| flex: 1; | |
| display: grid; | |
| grid-template-columns: 1fr 1.2fr; | |
| gap: 2rem; | |
| padding: 3rem 5%; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| @media (max-width: 968px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| padding: 2rem 1rem; | |
| } | |
| } | |
| /* Left Column: Controls */ | |
| .control-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 2rem; | |
| } | |
| .hero-text h1 { | |
| font-size: 3rem; | |
| line-height: 1.1; | |
| margin-bottom: 1rem; | |
| font-weight: 800; | |
| } | |
| .hero-text p { | |
| color: var(--text-muted); | |
| font-size: 1.1rem; | |
| max-width: 400px; | |
| } | |
| .input-group { | |
| background: var(--bg-card); | |
| padding: 2rem; | |
| border-radius: 1rem; | |
| border: 1px solid var(--border); | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.3); | |
| } | |
| .label { | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| color: var(--text-muted); | |
| margin-bottom: 0.5rem; | |
| display: block; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| } | |
| .text-input-wrapper { | |
| position: relative; | |
| margin-bottom: 2rem; | |
| } | |
| textarea { | |
| width: 100%; | |
| background: #0f172a; | |
| border: 1px solid var(--border); | |
| color: var(--text-main); | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| resize: vertical; | |
| min-height: 100px; | |
| font-family: 'Inter', sans-serif; | |
| transition: all 0.3s ease; | |
| font-size: 1rem; | |
| } | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 2px var(--primary-glow); | |
| } | |
| .options-grid { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 1rem; | |
| } | |
| .option-card { | |
| background: #0f172a; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| cursor: pointer; | |
| border: 1px solid var(--border); | |
| transition: all 0.3s; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| text-align: center; | |
| gap: 10px; | |
| } | |
| .option-card:hover { | |
| border-color: var(--primary); | |
| background: rgba(99, 102, 241, 0.05); | |
| } | |
| .option-card.active { | |
| background: var(--primary); | |
| border-color: var(--primary); | |
| color: white; | |
| box-shadow: 0 0 15px var(--primary-glow); | |
| } | |
| .option-card i { | |
| font-size: 1.5rem; | |
| } | |
| .option-card span { | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| } | |
| .file-upload-area { | |
| border: 2px dashed var(--border); | |
| padding: 2rem; | |
| border-radius: 1rem; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| position: relative; | |
| } | |
| .file-upload-area:hover { | |
| border-color: var(--primary); | |
| background: rgba(99, 102, 241, 0.05); | |
| } | |
| .file-upload-area i { | |
| font-size: 3rem; | |
| color: var(--text-muted); | |
| margin-bottom: 1rem; | |
| } | |
| .file-name { | |
| margin-top: 10px; | |
| font-family: 'JetBrains Mono', monospace; | |
| color: var(--primary); | |
| font-size: 0.9rem; | |
| word-break: break-all; | |
| } | |
| .btn-primary { | |
| width: 100%; | |
| padding: 1rem; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| border: none; | |
| border-radius: 0.5rem; | |
| color: white; | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| cursor: pointer; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 20px rgba(236, 72, 153, 0.3); | |
| } | |
| .btn-primary:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| /* Right Column: Visualization */ | |
| .visual-panel { | |
| background: var(--bg-card); | |
| border-radius: 1.5rem; | |
| border: 1px solid var(--border); | |
| padding: 2rem; | |
| display: flex; | |
| flex-direction: column; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .panel-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 2rem; | |
| } | |
| .status-badge { | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 2rem; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| background: rgba(16, 185, 129, 0.1); | |
| color: var(--success); | |
| border: 1px solid var(--success); | |
| } | |
| .status-badge.processing { | |
| background: rgba(236, 72, 153, 0.1); | |
| color: var(--secondary); | |
| border-color: var(--secondary); | |
| } | |
| .waveform-container { | |
| flex: 1; | |
| background: #0f172a; | |
| border-radius: 1rem; | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border: 1px solid var(--border); | |
| min-height: 300px; | |
| } | |
| canvas { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .track-info { | |
| margin-top: 2rem; | |
| text-align: center; | |
| } | |
| .track-title { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| margin-bottom: 0.5rem; | |
| } | |
| .track-subtitle { | |
| color: var(--text-muted); | |
| font-size: 0.9rem; | |
| } | |
| /* Player Controls */ | |
| .player-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 1.5rem; | |
| margin-top: 2rem; | |
| padding-top: 1.5rem; | |
| border-top: 1px solid var(--border); | |
| } | |
| .play-btn { | |
| width: 50px; | |
| height: 50px; | |
| border-radius: 50%; | |
| background: white; | |
| color: var(--bg-dark); | |
| border: none; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: transform 0.2s; | |
| font-size: 1.5rem; | |
| } | |
| .play-btn:hover { | |
| transform: scale(1.1); | |
| } | |
| .time-display { | |
| font-family: 'JetBrains Mono', monospace; | |
| color: var(--text-muted); | |
| font-size: 0.9rem; | |
| min-width: 80px; | |
| } | |
| .volume-control { | |
| margin-left: auto; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| color: var(--text-muted); | |
| } | |
| /* Loading Animation */ | |
| .loader-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(15, 23, 42, 0.95); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 50; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.5s; | |
| } | |
| .loader-overlay.active { | |
| opacity: 1; | |
| pointer-events: all; | |
| } | |
| .loader-circle { | |
| width: 60px; | |
| height: 60px; | |
| border: 4px solid var(--border); | |
| border-top: 4px solid var(--primary); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-bottom: 1.5rem; | |
| } | |
| .loader-text { | |
| font-family: 'JetBrains Mono', monospace; | |
| color: var(--primary); | |
| animation: pulse 1.5s infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 0.5; } | |
| 50% { opacity: 1; } | |
| 100% { opacity: 0.5; } | |
| } | |
| /* Result Section */ | |
| .result-section { | |
| display: none; /* Hidden by default */ | |
| flex-direction: column; | |
| gap: 1rem; | |
| margin-top: 2rem; | |
| } | |
| .download-btn { | |
| padding: 0.75rem; | |
| border-radius: 0.5rem; | |
| border: 1px solid var(--border); | |
| background: transparent; | |
| color: var(--text-main); | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| justify-content: center; | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| } | |
| .download-btn:hover { | |
| background: var(--border); | |
| color: white; | |
| } | |
| .isolate-note { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| text-align: center; | |
| font-style: italic; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="logo"> | |
| <i class="fa-solid fa-wave-square"></i> | |
| <span>Sam Audio</span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with-link" title="Built with AnyCoder"> | |
| <i class="fa-solid fa-code"></i> Built with anycoder | |
| </a> | |
| </header> | |
| <main> | |
| <!-- Left Side: Inputs --> | |
| <section class="control-panel"> | |
| <div class="hero-text"> | |
| <h1>Isolate any sound.<br>Describe your wish.</h1> | |
| <p>Upload an audio file and describe the element you want to extract using our AI processing engine.</p> | |
| </div> | |
| <div class="input-group"> | |
| <label class="label">Describe what you want to isolate</label> | |
| <div class="text-input-wrapper"> | |
| <textarea id="aiPrompt" placeholder="e.g., Isolate the acoustic guitar and remove all background noise...">Isolate the vocals and remove the instrumental track</textarea> | |
| </div> | |
| <label class="label">Select Sound Source</label> | |
| <div class="options-grid"> | |
| <div class="option-card active" onclick="selectOption(this, 'vocals')"> | |
| <i class="fa-solid fa-microphone-lines"></i> | |
| <span>Vocals</span> | |
| </div> | |
| <div class="option-card" onclick="selectOption(this, 'instrumental')"> | |
| <i class="fa-solid fa-guitar"></i> | |
| <span>Instrumental</span> | |
| </div> | |
| <div class="option-card" onclick="selectOption(this, 'drums')"> | |
| <i class="fa-solid fa-drum"></i> | |
| <span>Drums</span> | |
| </div> | |
| <div class="option-card" onclick="selectOption(this, 'bass')"> | |
| <i class="fa-solid fa-sliders"></i> | |
| <span>Bass</span> | |
| </div> | |
| </div> | |
| <label class="label" style="margin-top: 2rem;">Upload Audio</label> | |
| <div class="file-upload-area" onclick="document.getElementById('fileInput').click()"> | |
| <i class="fa-solid fa-cloud-arrow-up"></i> | |
| <p>Click to upload or drag and drop</p> | |
| <input type="file" id="fileInput" accept="audio/*" style="display: none;" onchange="handleFileUpload(this)"> | |
| <div class="file-name" id="fileName">No file selected</div> | |
| </div> | |
| <button id="processBtn" class="btn-primary" onclick="startProcessing()"> | |
| <i class="fa-solid fa-wand-magic-sparkles"></i> | |
| Process & Isolate | |
| </button> | |
| </div> | |
| </section> | |
| <!-- Right Side: Visualization --> | |
| <section class="visual-panel"> | |
| <div class="loader-overlay" id="loader"> | |
| <div class="loader-circle"></div> | |
| <div class="loader-text" id="loaderText">Initializing AI Model...</div> | |
| </div> | |
| <div class="panel-header"> | |
| <h2>Audio Workspace</h2> | |
| <div class="status-badge" id="statusBadge">Ready</div> | |
| </div> | |
| <div class="waveform-container"> | |
| <canvas id="waveformCanvas"></canvas> | |
| </div> | |
| <div class="track-info" id="trackInfo"> | |
| <div class="track-title" id="currentTrackName">Demo Track: "Sunny Morning"</div> | |
| <div class="track-subtitle">Sample Audio (Standard License)</div> | |
| </div> | |
| <div class="result-section" id="resultSection"> | |
| <h3 style="color: var(--success);">Isolation Complete!</h3> | |
| <p class="isolate-note">Based on your prompt "Isolate the vocals...", the AI has successfully separated the vocal track from the instrumental.</p> | |
| <a href="#" class="download-btn" onclick="event.preventDefault()"> | |
| <i class="fa-solid fa-download"></i> Download Isolated Vocals (MP3) | |
| </a> | |
| <a href="#" class="download-btn" onclick="event.preventDefault()"> | |
| <i class="fa-solid fa-code"></i> Download Stem (WAV) | |
| </a> | |
| </div> | |
| <div class="player-controls"> | |
| <button class="play-btn" id="playBtn" onclick="togglePlay()"> | |
| <i class="fa-solid fa-play" id="playIcon"></i> | |
| </button> | |
| <div class="time-display" id="timeDisplay">00:00 / 00:00</div> | |
| <audio id="audioPlayer" crossorigin="anonymous"> | |
| <!-- Using a reliable public domain sample audio --> | |
| <source src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" type="audio/mpeg"> | |
| </audio> | |
| <div class="volume-control"> | |
| <i class="fa-solid fa-volume-high"></i> | |
| <input type="range" min="0" max="1" step="0.01" value="1" onchange="updateVolume(this.value)" style="width: 80px;"> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <script> | |
| // --- DOM Elements --- | |
| const canvas = document.getElementById('waveformCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const playBtn = document.getElementById('playBtn'); | |
| const playIcon = document.getElementById('playIcon'); | |
| const audioPlayer = document.getElementById('audioPlayer'); | |
| const loader = document.getElementById('loader'); | |
| const loaderText = document.getElementById('loaderText'); | |
| const statusBadge = document.getElementById('statusBadge'); | |
| const processBtn = document.getElementById('processBtn'); | |
| const resultSection = document.getElementById('resultSection'); | |
| const fileNameDisplay = document.getElementById('fileName'); | |
| const trackInfo = document.getElementById('trackInfo'); | |
| // --- Canvas Setup --- | |
| let animationId; | |
| let audioContext; | |
| let analyser; | |
| let source; | |
| let isPlaying = false; | |
| function resizeCanvas() { | |
| canvas.width = canvas.parentElement.clientWidth; | |
| canvas.height = canvas.parentElement.clientHeight; | |
| drawWaveform(); | |
| } | |
| window.addEventListener('resize', resizeCanvas); | |
| resizeCanvas(); | |
| // --- Audio Context Initialization --- | |
| function initAudioContext() { | |
| if (!audioContext) { | |
| audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| } | |
| if (audioContext.state === 'suspended') { | |
| audioContext.resume(); | |
| } | |
| if (!analyser) { | |
| analyser = audioContext.createAnalyser(); | |
| source = audioContext.createMediaElementSource(audioPlayer); | |
| source.connect(analyser); | |
| analyser.connect(audioContext.destination); | |
| analyser.fftSize = 256; | |
| } | |
| } | |
| // --- Visualization Logic --- | |
| const bufferLength = analyser ? analyser.frequencyBinCount : 128; | |
| const dataArray = new Uint8Array(bufferLength); | |
| function drawWaveform() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Background Gradient | |
| const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); | |
| gradient.addColorStop(0, '#0f172a'); | |
| gradient.addColorStop(1, '#1e293b'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| if (!isPlaying && !analyser) { | |
| // Draw static idle waves | |
| drawIdleWaves(); | |
| return; | |
| } | |
| analyser.getByteFrequencyData(dataArray); | |
| const barWidth = (canvas.width / bufferLength) * 2.5; | |
| let barHeight; | |
| let x = 0; | |
| // Create vertical gradient for bars | |
| const barGradient = ctx.createLinearGradient(0, canvas.height, 0, 0); | |
| barGradient.addColorStop(0, '#ec4899'); // Pink | |
| barGradient.addColorStop(0.5, '#6366f1'); // Indigo | |
| barGradient.addColorStop(1, '#10b981'); // Green | |
| ctx.fillStyle = barGradient; | |
| for (let i = 0; i < bufferLength; i++) { | |
| barHeight = dataArray[i] / 2; // Scale down | |
| // Smooth curve effect | |
| ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); | |
| x += barWidth + 1; | |
| } | |
| animationId = requestAnimationFrame(drawWaveform); | |
| } | |
| function drawIdleWaves() { | |
| ctx.fillStyle = 'rgba(99, 102, 241, 0.1)'; // Faint Indigo | |
| const time = Date.now() * 0.002; | |
| ctx.beginPath(); | |
| for (let i = 0; i < canvas.width; i+=5) { | |
| const y = (canvas.height / 2) + Math.sin(i * 0.02 + time) * 30 + Math.sin(i * 0.05 - time * 2) * 10; | |
| if (i === 0) ctx.moveTo(i, y); | |
| else ctx.lineTo(i, y); | |
| } | |
| ctx.lineWidth = 2; | |
| ctx.strokeStyle = '#6366f1'; | |
| ctx.stroke(); | |
| } | |
| // --- Interactions --- | |
| function togglePlay() { | |
| initAudioContext(); | |
| if (audioPlayer.paused) { | |
| audioPlayer.play(); | |
| isPlaying = true; | |
| playIcon.classList.remove('fa-play'); | |
| playIcon.classList.add('fa-pause'); | |
| drawWaveform(); | |
| statusBadge.textContent = "Analyzing Audio..."; | |
| statusBadge.classList.add('processing'); | |
| } else { | |
| audioPlayer.pause(); | |
| isPlaying = false; | |
| playIcon.classList.remove('fa-pause'); | |
| playIcon.classList.add('fa-play'); | |
| cancelAnimationFrame(animationId); | |
| drawWaveform(); // Redraw idle | |
| statusBadge.textContent = "Ready"; | |
| statusBadge.classList.remove('processing'); | |
| } | |
| } | |
| function updateVolume(val) { | |
| audioPlayer.volume = val; | |
| } | |
| audioPlayer.addEventListener('timeupdate', () => { | |
| const current = audioPlayer.currentTime; | |
| const duration = audioPlayer.duration; | |
| if (!isNaN(duration)) { | |
| const currentMin = Math.floor(current / 60); | |
| const currentSec = Math.floor(current % 60); | |
| const durationMin = Math.floor(duration / 60); | |
| const durationSec = Math.floor(duration % 60); | |
| document.getElementById('timeDisplay').textContent = | |
| `${currentMin.toString().padStart(2, '0')}:${currentSec.toString().padStart(2, '0')} / ${durationMin.toString().padStart(2, '0')}:${durationSec.toString().padStart(2, '0')}`; | |
| } | |
| }); | |
| // --- File Handling --- | |
| function handleFileUpload(input) { | |
| if (input.files && input.files[0]) { | |
| const file = input.files[0]; | |
| fileNameDisplay.textContent = file.name; | |
| // Create object URL for the uploaded file | |
| const fileURL = URL.createObjectURL(file); | |
| audioPlayer.src = fileURL; | |
| // Reset UI | |
| resultSection.style.display = 'none'; | |
| trackInfo.innerHTML = `<div class="track-title" style="color: var(--primary)">${file.name}</div><div class="track-subtitle">User Uploaded File</div>`; | |
| // Auto play for preview (optional, usually better to wait for user action) | |
| // audioPlayer.play(); | |
| } | |
| } | |
| // --- Option Selection --- | |
| window.selectOption = function(element, type) { | |
| // Remove active class from all | |
| document.querySelectorAll('.option-card').forEach(card => { | |
| card.classList.remove('active'); | |
| }); | |
| // Add active to clicked | |
| element.classList.add('active'); | |
| // Update prompt text based on selection | |
| const promptInput = document.getElementById('aiPrompt'); | |
| let promptText = promptInput.value; | |
| // Simple logic to replace key terms in the prompt | |
| if (type === 'vocals') promptInput.value = "Isolate the vocals and remove the instrumental track"; | |
| if (type === 'instrumental') promptInput.value = "Remove vocals to leave only the music track"; | |
| if (type === 'drums') promptInput.value = "Extract the drum kit and percussive elements"; | |
| if (type === 'bass') promptInput.value = "Isolate the bass guitar and low frequency elements"; | |
| }; | |
| // --- Processing Simulation --- | |
| window.startProcessing = function() { | |
| const prompt = document.getElementById('aiPrompt').value; | |
| if (audioPlayer.src === "") { | |
| alert("Please upload an audio file first."); | |
| return; | |
| } | |
| // 1. UI Updates | |
| processBtn.disabled = true; | |
| processBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Processing...'; | |
| loader.classList.add('active'); | |
| statusBadge.textContent = "AI Processing..."; | |
| statusBadge.classList.add('processing'); | |
| resultSection.style.display = 'none'; | |
| // 2. Simulated Steps | |
| const steps = [ | |
| "Uploading audio to secure environment...", | |
| "Analyzing frequency spectrum...", | |
| "Loading 'VocalIsolate-v4' model...", | |
| "Separating audio stems...", | |
| `Processing: "${prompt.substring(0, 30)}..."`, | |
| "Refining audio edges...", | |
| "Finalizing output..." | |
| ]; | |
| let stepIndex = 0; | |
| const interval = setInterval(() => { | |
| if (stepIndex < steps.length) { | |
| loaderText.textContent = steps[stepIndex]; | |
| stepIndex++; | |
| } else { | |
| clearInterval(interval); | |
| finishProcessing(); | |
| } | |
| }, 800); // Simulate 800ms per step | |
| }; | |
| function finishProcessing() { | |
| setTimeout(() => { | |
| loader.classList.remove('active'); | |
| processBtn.disabled = false; | |
| processBtn.innerHTML = '<i class="fa-solid fa-wand-magic-sparkles"></i> Process & Isolate'; | |
| statusBadge.textContent = "Processing Complete"; | |
| statusBadge.style.backgroundColor = "rgba(16, 185, 129, 0.1)"; | |
| statusBadge.style.color = "var(--success)"; | |
| statusBadge.style.border = "1px solid var(--success)"; | |
| // Show Result | |
| resultSection.style.display = 'flex'; | |
| // Play a "success" sound effect (optional, using a subtle oscillator beep) | |
| playSuccessSound(); | |
| }, 500); | |
| } | |
| function playSuccessSound() { | |
| const ctx = new (window.AudioContext || window.webkitAudioContext)(); | |
| const osc = ctx.createOscillator(); | |
| const gain = ctx.createGain(); | |
| osc.type = 'sine'; | |
| osc.frequency.setValueAtTime(523.25, ctx.currentTime); // C5 | |
| osc.frequency.exponentialRampToValueAtTime(1046.5, ctx.currentTime + 0.1); // C6 | |
| gain.gain.setValueAtTime(0.1, ctx.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.5); | |
| osc.connect(gain); | |
| gain.connect(ctx.destination); | |
| osc.start(); | |
| osc.stop(ctx.currentTime + 0.5); | |
| } | |
| // Initialize idle animation | |
| drawWaveform(); | |
| </script> | |
| </body> | |
| </html> |