🎛️ Analiza Audio con Essentia.js

Extrae BPM, tonalidad y energía en el navegador sin servidor

📋 ¿Qué aprenderás?

🚀 Guía Paso a Paso

1

Instalar Essentia.js

Essentia.js es una librería de análisis de audio que corre completamente en el navegador.

npm install essentia.js
ℹ️ Nota: Essentia.js utiliza WebAssembly, por lo que el archivo es algo pesado (~2MB). Considera la carga asíncrona.
2

Inicializar Essentia.js

Espera a que la librería cargue completamente antes de usarla:

// Inicializar Essentia.js let essentia = null; async function initEssentia() { try { // Esperar a que cargue el módulo WASM essentia = new Essentia.EssentiaJS(Essentia.EssentiaWASM); await essentia.initialize(); console.log('Essentia.js cargado correctamente'); return true; } catch (error) { console.error('Error cargando Essentia.js:', error); return false; } } // Uso window.addEventListener('load', async () => { const loaded = await initEssentia(); if (loaded) { // Iniciar análisis analyzeAudioFile(); } });
3

Cargar y procesar archivo de audio

Convierte el archivo de audio a formato que Essentia pueda procesar:

// Cargar archivo de audio async function loadAudioFile(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = async (e) => { try { // Decodificar audio con Web Audio API const audioContext = new AudioContext(); const audioBuffer = await audioContext.decodeAudioData(e.target.result); // Convertir a array de floats const audioData = audioBuffer.getChannelData(0); // Canal mono resolve({ data: audioData, sampleRate: audioBuffer.sampleRate, duration: audioBuffer.duration }); } catch (error) { reject(error); } }; reader.onerror = reject; reader.readAsArrayBuffer(file); }); } // Procesar con Essentia async function processAudioWithEssentia(audioData, sampleRate) { // Asegurar que Essentia esté inicializado if (!essentia) { throw new Error('Essentia.js no está inicializado'); } // Convertir a vector Essentia const vector = essentia.arrayToVector(audioData); return { vector, sampleRate }; }
4

Extraer BPM (Ritmo)

Calcula los beats por minuto de la pista de audio:

// Extraer BPM function extractBPM(audioVector, sampleRate) { try { // Usar el algoritmo PercivalBpmEstimator const bpm = essentia.PercivalBpmEstimator(audioVector); // También puedes usar otros algoritmos const bpmDegara = essentia.DegaraBpmEstimator(audioVector); return { bpm: bpm.bpm, confidence: bpm.confidence || 0.8, algorithm: 'PercivalBpmEstimator' }; } catch (error) { console.error('Error extrayendo BPM:', error); // Fallback: estimación básica return estimateBPMBasic(audioVector, sampleRate); } } // Estimación básica de BPM (fallback) function estimateBPMBasic(audioVector, sampleRate) { // Implementación simple basada en picos de energía const hopSize = 512; const frameSize = 2048; const onsetThreshold = 0.3; let onsets = []; // Detectar onsets (ataques) for (let i = 0; i < audioVector.length - frameSize; i += hopSize) { const frame = audioVector.slice(i, i + frameSize); const energy = frame.reduce((sum, sample) => sum + sample * sample, 0) / frameSize; if (energy > onsetThreshold) { onsets.push(i / sampleRate); // Tiempo en segundos } } // Calcular intervalos entre onsets const intervals = []; for (let i = 1; i < onsets.length; i++) { intervals.push(onsets[i] - onsets[i-1]); } // Promedio de intervalos const avgInterval = intervals.reduce((sum, interval) => sum + interval, 0) / intervals.length; // Convertir a BPM const bpm = Math.round(60 / avgInterval); return { bpm: Math.max(60, Math.min(200, bpm)), // Limitar a rango razonable confidence: 0.5, algorithm: 'Basic Onset Detection' }; }
5

Detectar tonalidad y escala

Identifica la tonalidad musical y la escala (mayor/menor):

// Extraer tonalidad function extractKey(audioVector, sampleRate) { try { // Usar HPCP (Harmonic Pitch Class Profile) const hpcp = essentia.HPCP(audioVector, { sampleRate: sampleRate, maxFrequency: 5000, minFrequency: 80, size: 36 // 3 bins por nota (cromático) }); // Extraer Key/Scale const keyResult = essentia.KeyExtractor(hpcp); return { key: keyResult.key, scale: keyResult.scale, // 'major' o 'minor' strength: keyResult.strength, tuningFrequency: keyResult.tuningFrequency }; } catch (error) { console.error('Error extrayendo tonalidad:', error); return null; } } // Mapeo de notas const noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; // Función para obtener información legible function getKeyInfo(keyData) { if (!keyData) return 'Tonalidad no detectada'; const keyName = noteNames[keyData.key] || keyData.key; const scaleName = keyData.scale === 'major' ? 'Mayor' : 'Menor'; return `${keyName} ${scaleName} (confianza: ${(keyData.strength * 100).toFixed(1)}%)`; }
6

Analizar energía y dinámica

Calcula la energía y dinámica del audio:

// Extraer energía function extractEnergy(audioVector) { try { // Calcular energía RMS (Root Mean Square) const rms = essentia.RMS(audioVector); // Calcular energía total const energy = essentia.Energy(audioVector); // Calcular Zero Crossing Rate (indica frecuencia) const zcr = essentia.ZeroCrossingRate(audioVector); // Calcular Loudness EBU R128 const loudness = essentia.LoudnessEBUR128(audioVector); return { rms: rms.rms, energy: energy.energy, zeroCrossingRate: zcr.zeroCrossingRate, loudness: loudness.integratedLoudness, // Normalizar valores para comparación normalizedEnergy: Math.min(1, energy.energy / audioVector.length), normalizedRMS: rms.rms }; } catch (error) { console.error('Error extrayendo energía:', error); return calculateBasicEnergy(audioVector); } } // Cálculo básico de energía (fallback) function calculateBasicEnergy(audioVector) { // Energía total const totalEnergy = audioVector.reduce((sum, sample) => sum + sample * sample, 0); // RMS const rms = Math.sqrt(totalEnergy / audioVector.length); // Zero Crossing Rate let zeroCrossings = 0; for (let i = 1; i < audioVector.length; i++) { if ((audioVector[i] >= 0) !== (audioVector[i-1] >= 0)) { zeroCrossings++; } } const zcr = zeroCrossings / audioVector.length; return { rms: rms, energy: totalEnergy, zeroCrossingRate: zcr, normalizedEnergy: Math.min(1, totalEnergy / audioVector.length), normalizedRMS: rms, loudness: 20 * Math.log10(rms), // Aproximación simple algorithm: 'Basic Energy' }; }

🎯 Demo en Vivo

Sube un archivo de audio y analiza sus características:

📁

Arrastra aquí tu archivo de audio

O haz clic para seleccionar

Formatos soportados: MP3, WAV, OGG, M4A

💡 Mejores Prácticas

🚀 Próximos Pasos

Una vez que domines el análisis básico, puedes:

← Volver a Tutoriales