CoolFace
Apppublic

dev2008/audio-separation

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
app.js387 linesDownload Raw Back to static
1// Frontend JavaScript for audio separation app2 3let currentFiles = [];4let currentFileId = null;5let isProcessing = false;6let uploadedFile = null;7 8// Initialize on page load9document.addEventListener('DOMContentLoaded', () => {10    loadFiles();11    setupEventListeners();12});13 14// Setup event listeners15function setupEventListeners() {16    // File selection and upload17    document.getElementById('fileSelect').addEventListener('change', onFileChange);18    document.getElementById('uploadBtn').addEventListener('click', () => {19        document.getElementById('fileUpload').click();20    });21    document.getElementById('fileUpload').addEventListener('change', handleFileUpload);22    23    // Sliders24    document.getElementById('startTime').addEventListener('input', updateStartTimeLabel);25    document.getElementById('endTime').addEventListener('input', updateEndTimeLabel);26    document.getElementById('components').addEventListener('input', updateComponentsLabel);27    document.getElementById('maskContrast').addEventListener('input', updateMaskContrastLabel);28    document.getElementById('maskThreshold').addEventListener('input', updateMaskThresholdLabel);29    document.getElementById('enhanceOutput').addEventListener('change', toggleEnhanceSuboptions);30    31    // Buttons32    document.getElementById('runBtn').addEventListener('click', runSeparation);33    document.getElementById('detectBtn').addEventListener('click', detectSounds);34}35 36// Handle file upload37async function handleFileUpload(event) {38    const file = event.target.files[0];39    if (!file) return;40    41    const uploadStatus = document.getElementById('uploadStatus');42    uploadStatus.style.display = 'block';43    uploadStatus.textContent = '⏳ Uploading...';44    uploadStatus.style.background = '#ffc107';45    46    try {47        const formData = new FormData();48        formData.append('file', file);49        50        const response = await fetch('/api/upload', {51            method: 'POST',52            body: formData53        });54        55        if (!response.ok) {56            const error = await response.json();57            throw new Error(error.detail || 'Upload failed');58        }59        60        uploadedFile = await response.json();61        62        // Clear file select63        document.getElementById('fileSelect').value = '';64        65        // Update UI66        currentFileId = uploadedFile.id;67        document.getElementById('classInfo').textContent = `Uploaded: ${uploadedFile.filename}`;68        document.getElementById('mixPlayer').src = `/api/audio/${uploadedFile.id}`;69        70        uploadStatus.textContent = `✅ Uploaded: ${uploadedFile.filename}`;71        uploadStatus.style.background = '#28a745';72        73        // Clear old detection results74        document.getElementById('detectionResults').innerHTML = '';75        76        // Load spectrogram77        loadSpectrogram(uploadedFile.id);78        79        // Hide results80        document.getElementById('resultsSection').style.display = 'none';81        82        showToast('File uploaded successfully! Click "Detect Classes" to analyze.', 'success');83        84    } catch (error) {85        uploadStatus.textContent = `❌ Error: ${error.message}`;86        uploadStatus.style.background = '#dc3545';87        showToast('Upload failed: ' + error.message, 'error');88    }89}90 91// Use a suggested prompt92function usePrompt(prompt) {93    document.getElementById('promptInput').value = prompt;94    showToast(`Prompt set: "${prompt}"`, 'success');95    // Scroll to separation section96    document.querySelector('.panel:nth-child(2)').scrollIntoView({ behavior: 'smooth', block: 'start' });97}98 99// Load available files from API100async function loadFiles() {101    try {102        const response = await fetch('/api/files');103        if (!response.ok) throw new Error('Failed to load files');104        105        currentFiles = await response.json();106        107        const select = document.getElementById('fileSelect');108        select.innerHTML = '<option value="">-- Select a file --</option>';109        110        currentFiles.forEach(file => {111            const option = document.createElement('option');112            option.value = file.id;113            option.textContent = `${file.id} (${file.class_label})`;114            select.appendChild(option);115        });116        117    } catch (error) {118        showToast('Error loading files: ' + error.message, 'error');119    }120}121 122// Handle file selection change123async function onFileChange(event) {124    const fileId = event.target.value;125    if (!fileId) {126        currentFileId = null;127        document.getElementById('mixPlayer').src = '';128        return;129    }130    131    // Clear uploaded file status132    uploadedFile = null;133    document.getElementById('uploadStatus').style.display = 'none';134    document.getElementById('fileUpload').value = '';135    136    // Clear old detection results137    document.getElementById('detectionResults').innerHTML = '';138    139    currentFileId = fileId;140    const file = currentFiles.find(f => f.id === fileId);141    142    // Update class info143    document.getElementById('classInfo').textContent = `Class: ${file.class_label}`;144    145    // Update audio player with actual audio file146    document.getElementById('mixPlayer').src = file.audio_url || `/api/audio/${fileId}`;147    148    // Load spectrogram149    loadSpectrogram(fileId);150    151    // Hide results section152    document.getElementById('resultsSection').style.display = 'none';153}154 155// Load and display spectrogram156async function loadSpectrogram(fileId) {157    const img = document.getElementById('mixSpectrogram');158    const loading = document.getElementById('mixLoading');159    160    img.style.display = 'none';161    loading.style.display = 'block';162    163    try {164        const response = await fetch(`/api/spectrogram?file_id=${fileId}`);165        if (!response.ok) throw new Error('Failed to load spectrogram');166        167        const data = await response.json();168        img.src = data.url;169        img.style.display = 'block';170        loading.style.display = 'none';171        172    } catch (error) {173        loading.textContent = 'Error loading spectrogram';174        showToast('Error loading spectrogram: ' + error.message, 'error');175    }176}177 178// Update slider labels179function updateStartTimeLabel() {180    const value = document.getElementById('startTime').value;181    document.getElementById('startTimeValue').textContent = value;182}183 184function updateEndTimeLabel() {185    const value = document.getElementById('endTime').value;186    const label = value >= 5 ? `${value}s (full duration)` : `${value}s`;187    document.getElementById('endTimeValue').textContent = label;188}189 190function updateComponentsLabel() {191    const value = document.getElementById('components').value;192    document.getElementById('componentsValue').textContent = value;193}194 195function updateMaskContrastLabel() {196    const value = document.getElementById('maskContrast').value;197    document.getElementById('maskContrastValue').textContent = value;198}199 200function updateMaskThresholdLabel() {201    const value = parseFloat(document.getElementById('maskThreshold').value).toFixed(2);202    document.getElementById('maskThresholdValue').textContent = value;203}204 205function toggleEnhanceSuboptions() {206    const checked = document.getElementById('enhanceOutput').checked;207    document.getElementById('enhanceSuboptions').style.display = checked ? 'block' : 'none';208}209 210// Run audio separation211async function runSeparation() {212    if (isProcessing) return;213    214    // Validate inputs215    if (!currentFileId) {216        showToast('Please select an audio file', 'error');217        return;218    }219    220    const prompt = document.getElementById('promptInput').value.trim();221    if (!prompt) {222        showToast('Please enter a text prompt', 'error');223        return;224    }225    226    const mode = document.querySelector('input[name="mode"]:checked').value;227    const method = document.querySelector('input[name="method"]:checked').value;228    const t0 = parseFloat(document.getElementById('startTime').value);229    const t1 = parseFloat(document.getElementById('endTime').value);230    const k_components = parseInt(document.getElementById('components').value);231    const mask_contrast = parseFloat(document.getElementById('maskContrast').value);232    const mask_threshold = parseFloat(document.getElementById('maskThreshold').value);233    const enhance_output = document.getElementById('enhanceOutput').checked;234    const enhance_denoise = document.getElementById('enhanceDenoise').checked;235    const enhance_dereverb = document.getElementById('enhanceDereverb').checked;236    const enhance_eq = document.getElementById('enhanceEq').checked;237    238    if (t1 <= t0) {239        showToast('End time must be greater than start time', 'error');240        return;241    }242    243    // Show spinner, disable button244    isProcessing = true;245    document.getElementById('runBtn').disabled = true;246    document.getElementById('spinner').style.display = 'block';247    248    try {249        // Choose endpoint based on method250        const endpoint = method === 'unet' ? '/api/separate_unet' : '/api/separate';251        252        const response = await fetch(endpoint, {253            method: 'POST',254            headers: {255                'Content-Type': 'application/json'256            },257            body: JSON.stringify({258                file_id: currentFileId,259                prompt: prompt,260                mode: mode,261                t0: t0,262                t1: t1 >= 5 ? null : t1,  // null means full duration263                k_components: k_components,264                mask_contrast: mask_contrast,265                mask_threshold: mask_threshold,266                enhance_output: enhance_output,267                enhance_denoise: enhance_denoise,268                enhance_dereverb: enhance_dereverb,269                enhance_eq: enhance_eq270            })271        });272        273        if (!response.ok) {274            const error = await response.json();275            throw new Error(error.detail || 'Separation failed');276        }277        278        const result = await response.json();279        displayResults(result);280        281        const methodName = method === 'unet' ? 'UNet (Trained Model)' : 'NMF (Baseline)';282        showToast(`Separation completed with ${methodName}!`, 'success');283        284    } catch (error) {285        showToast('Separation error: ' + error.message, 'error');286    } finally {287        isProcessing = false;288        document.getElementById('runBtn').disabled = false;289        document.getElementById('spinner').style.display = 'none';290    }291}292 293// Display separation results294function displayResults(result) {295    // Show results section296    const resultsSection = document.getElementById('resultsSection');297    resultsSection.style.display = 'block';298    299    // Update audio players300    document.getElementById('outPlayer').src = result.out_wav;301    document.getElementById('residualPlayer').src = result.residual_wav;302    303    // Update images304    document.getElementById('outSpectrogram').src = result.out_spec_png;305    document.getElementById('maskImage').src = result.mask_png;306    307    // Scroll to results308    resultsSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });309}310 311// Detect sound classes312async function detectSounds() {313    if (!currentFileId) {314        showToast('Please select an audio file', 'error');315        return;316    }317    318    const btn = document.getElementById('detectBtn');319    btn.disabled = true;320    btn.textContent = 'Detecting...';321    322    try {323        const response = await fetch('/api/classes', {324            method: 'POST',325            headers: {326                'Content-Type': 'application/json'327            },328            body: JSON.stringify({329                file_id: currentFileId,330                k_components: 10331            })332        });333        334        if (!response.ok) throw new Error('Detection failed');335        336        const results = await response.json();337        displayDetectionResults(results);338        339    } catch (error) {340        showToast('Detection error: ' + error.message, 'error');341    } finally {342        btn.disabled = false;343        btn.textContent = 'Detect Classes';344    }345}346 347// Display detection results as bar chart348function displayDetectionResults(results) {349    const container = document.getElementById('detectionResults');350    container.innerHTML = '';351    352    results.forEach(result => {353        const bar = document.createElement('div');354        bar.className = 'detection-bar';355        356        const label = document.createElement('div');357        label.className = 'detection-label';358        label.style.cursor = 'pointer';359        label.title = 'Click to use this prompt';360        label.onclick = () => usePrompt(result.class);361        label.innerHTML = `<span>🔍 ${result.class}</span><span>${(result.score * 100).toFixed(1)}%</span>`;362        363        const progress = document.createElement('div');364        progress.className = 'detection-progress';365        366        const fill = document.createElement('div');367        fill.className = 'detection-fill';368        fill.style.width = `${result.score * 100}%`;369        370        progress.appendChild(fill);371        bar.appendChild(label);372        bar.appendChild(progress);373        container.appendChild(bar);374    });375}376 377// Show toast notification378function showToast(message, type = 'info') {379    const toast = document.getElementById('toast');380    toast.textContent = message;381    toast.className = 'toast show ' + type;382    383    setTimeout(() => {384        toast.className = 'toast ' + type;385    }, 3000);386}387