Motor de recomendación musical por similitud acústica con CLAP y FAISS

por | agosto 10, 2026
A modern home studio with a DJ mixer, a laptop showing Python code and audio spectrograms, studio monitors, and cables, realistic lighting and depth of field.

¿Cansado de que tu plataforma de streaming te recomiende siempre el mismo puñado de canciones basándose en géneros o en lo que escucha la mayoría? Si eres DJ, curador o simplemente un obseso del sonido, seguro que has soñado con un sistema que entienda de verdad cómo suena una pista —su timbre, su energía, su espacio— y te sugiera gemelas ocultas en tu biblioteca. No más etiquetas, no más metadatos: solo similitud acústica real.

En esta guía vamos a construir exactamente eso. Usaremos el modelo CLAP (Contrastive Language-Audio Pre-training), que genera representaciones numéricas de audio capaces de capturar cómo percibimos el sonido, no solo sus propiedades técnicas. Luego indexaremos esas representaciones con FAISS para hacer búsquedas rapidísimas de similitud, y montaremos una interfaz sencilla con Streamlit para explorar recomendaciones al vuelo. Todo con Python y un puñado de librerías open-source.

¿Por qué CLAP y no solo espectrogramas o MFCC?

Hasta hace poco, el análisis de audio para recomendación se basaba en características de bajo nivel (coeficientes cepstrales, ritmo, tonalidad) o en etiquetas colaborativas. Pero esos métodos ignoran lo que realmente hace que dos canciones te transmitan la misma vibración. CLAP, entrenado con pares de audio y texto descriptivo, aprende a alinear ambos mundos en un espacio común. El resultado: embeddings de 512 dimensiones que, si pasas una guitarra acústica melancólica y otra similar, caen cerca; si pasas un reggaetón duro, se aleja. Puedes pensar en ello como un oído artificial que ha escuchado millones de pistas y sabe qué las une.

Motor de recomendación musical por similitud acústica con CLAP y FAISS

Manos a la obra: preparación del entorno

Necesitarás Python 3.8+, un entorno virtual (recomendado) y unos cuantos paquetes. Si no tienes GPU, no pasa nada: el modelo puede correr en CPU, aunque más lento. Crea una carpeta para el proyecto y dentro un entorno:

python -m venv music-rec
# En Windows: music-recScriptsactivate; en Linux/Mac: source music-rec/bin/activate

Instala las dependencias:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu  # o elige tu versión CUDA
pip install laion_clap faiss-cpu streamlit soundfile librosa

El paquete laion_clap trae el modelo preentrenado de LAION; FAISS nos dará indexación rápida; Streamlit, la interfaz. Ya tienes todo listo.

Paso 1: extraer embeddings de audio con CLAP

Primero, coloca todas las canciones que quieras indexar en una carpeta, por ejemplo mis_canciones/. Pueden ser MP3, WAV, FLAC… la librería soundfile se encarga de leerlas.

Creamos un script extraer_embeddings.py:

import os
import torch
import soundfile as sf
from laion_clap import CLAP_Module

# Cargar modelo CLAP (se descarga la primera vez)
clap = CLAP_Module(enable_fusion=False, amodel='HTSAT-tiny', tmodel='roberta')
clap.load_ckpt()

carpeta_audio = 'mis_canciones'
embeddings = []
nombres = []

for archivo in os.listdir(carpeta_audio):
    if archivo.lower().endswith(('.wav', '.mp3', '.flac', '.ogg')):
        ruta = os.path.join(carpeta_audio, archivo)
        audio, sample_rate = sf.read(ruta)
        # CLAP espera array float32 de duración máxima 10s, recortamos o rellenamos
        duracion_max = 480000  # 10s a 48kHz (resample si es necesario)
        if len(audio) > duracion_max:
            audio = audio[:duracion_max]
        else:
            audio = torch.nn.functional.pad(torch.tensor(audio), (0, duracion_max - len(audio)))
        audio = audio.unsqueeze(0)  # batch de 1
        with torch.no_grad():
            emb = clap.get_audio_embedding_from_data(audio, use_tensor=True)
        embeddings.append(emb.squeeze().numpy())
        nombres.append(archivo)

# Guardamos embeddings y nombres en un archivo .npy
import numpy as np
np.save('embeddings.npy', np.array(embeddings))
np.save('nombres.npy', np.array(nombres))
print(f'Extraídos {len(embeddings)} embeddings.')

Ejecuta: python extraer_embeddings.py. En unos minutos (dependiendo de tu CPU/GPU y cantidad de archivos) tendrás embeddings.npy y nombres.npy. Cada fila en embeddings.npy es un vector de 512 números, la huella acústica de cada canción.

Paso 2: indexación ultrarrápida con FAISS

Ahora construimos un índice para búsquedas de similitud por vecino más cercano. FAISS permite consultas en milisegundos incluso con millones de vectores. Creamos construir_indice.py:

import numpy as np
import faiss

embeddings = np.load('embeddings.npy').astype('float32')
dimension = embeddings.shape[1]

# Índice con producto interno normalizado (coseno)
index = faiss.IndexFlatIP(dimension)
# Normalizamos los vectores para que el producto interno = similitud coseno
faiss.normalize_L2(embeddings)
index.add(embeddings)

faiss.write_index(index, 'indice_musical.faiss')
print(f'Índice creado con {index.ntotal} vectores.')

Ejecútalo y tendrás indice_musical.faiss. Ahora, para buscar las N canciones más parecidas a una dada, necesitamos el vector de consulta normalizado y llamar a index.search.

Paso 3: interfaz interactiva con Streamlit

Un motor sin interfaz se queda a medio camino. Streamlit nos permite montar una web en minutos. Creamos app.py:

import streamlit as st
import faiss
import numpy as np
import soundfile as sf
from laion_clap import CLAP_Module
import os

# Cargamos índice y nombres (una vez al iniciar)
@st.cache_resource
def cargar_recursos():
    clap = CLAP_Module(enable_fusion=False, amodel='HTSAT-tiny', tmodel='roberta')
    clap.load_ckpt()
    index = faiss.read_index('indice_musical.faiss')
    nombres = np.load('nombres.npy')
    return clap, index, nombres

clap, index, nombres = cargar_recursos()

st.title("🎧 Recomendador por similitud acústica")
st.markdown("Sube una canción y encuentra gemelas ocultas en tu colección.")

archivo_subido = st.file_uploader("Elige un archivo de audio", type=['wav', 'mp3', 'flac', 'ogg'])
if archivo_subido is not None:
    # Guardamos temporalmente y leemos
    with open("temp_audio", "wb") as f:
        f.write(archivo_subido.read())
    audio, sr = sf.read("temp_audio")
    os.remove("temp_audio")

    # Preprocesamos igual que antes
    duracion_max = 480000
    if len(audio) > duracion_max:
        audio = audio[:duracion_max]
    else:
        audio = np.pad(audio, (0, duracion_max - len(audio)))
    audio_tensor = torch.tensor(audio).unsqueeze(0)
    with torch.no_grad():
        query_emb = clap.get_audio_embedding_from_data(audio_tensor, use_tensor=True).squeeze().numpy()

    # Normalizar para FAISS
    faiss.normalize_L2(query_emb.reshape(1, -1))
    distancias, indices = index.search(query_emb.reshape(1, -1), k=10)

    st.subheader("🔊 Tu canción")
    st.audio(archivo_subido)
    st.subheader("🎵 Recomendaciones")
    for i, (idx, dist) in enumerate(zip(indices[0], distancias[0])):
        nombre_archivo = nombres[idx]
        ruta_completa = os.path.join('mis_canciones', nombre_archivo)
        st.write(f"**{i+1}. {nombre_archivo}** (similitud: {dist:.3f})")
        if os.path.exists(ruta_completa):
            st.audio(ruta_completa)

Para lanzarla: streamlit run app.py. Sube cualquier canción, incluso una que no esté indexada, y obten al instante las 10 más cercanas en tu carpeta de origen. ¿Magia? No, matemáticas bien aplicadas.

¿Y si quiero indexar miles de canciones?

FAISS escala de maravilla. Cambia IndexFlatIP por IndexIVFFlat si tienes muchas pistas (entrena primero con un subconjunto). Para bibliotecas gigantes, explora IndexHNSWFlat. La latencia de búsqueda se mantiene en milisegundos.

Preguntas frecuentes

¿Esto funciona con cualquier formato de audio?

Sí, mientras la librería soundfile pueda leerlo. MP3, WAV, FLAC, OGG… sin problemas. La magia ocurre en la forma de onda, no en el códec.

¿Pierdo calidad al convertir o procesar?

No estás convirtiendo audio, solo analizándolo. La recomendación no altera los archivos originales.

¿Puedo afinar las recomendaciones con texto?

CLAP también acepta consultas textuales. Por ejemplo, podrías escribir «batería funk con bajo slap» y obtener pistas que suenen así. Basta con usar clap.get_text_embedding() y buscar en el índice.

¿Es mejor que las recomendaciones de Spotify?

Diferente. Spotify combina filtrado colaborativo y análisis de audio. Este sistema se basa puramente en la similitud acústica, lo que puede descubrir conexiones inesperadas que ningún humano etiquetó. Ideal para DJs que buscan transiciones suaves o curadores que quieren salir de la burbuja algorítmica.

Conclusión

Has construido un recomendador musical que realmente escucha las canciones. Con CLAP capturas la esencia acústica; con FAISS la buscas en un instante; con Streamlit le das una cara amigable. Ya sea para refrescar tus sesiones, encontrar joyas perdidas en tu disco duro o simplemente jugar con la tecnología, esta herramienta abre un mundo de posibilidades más allá de los metadatos. ¿El siguiente paso? Incorpóralo a tu flujo de trabajo y deja que el sonido hable por sí mismo.