Diarización de hablantes
Identifica quién habló cuándo en una grabación con varios hablantes. Hay dos motores de diarización disponibles: un pipeline Pyannote de dos etapas (segmentación + encadenamiento de hablantes por actividad, luego embedding posterior) y un modelo Sortformer de extremo a extremo (CoreML, Neural Engine).
Motores
Selecciona el motor con --engine pyannote (por defecto) o --engine sortformer.
Pyannote (por defecto)
Pipeline de dos etapas: la segmentación Pyannote procesa ventanas solapadas con encadenamiento de hablantes por actividad (correlación de Pearson en las zonas de solapamiento) para asignar etiquetas globales de hablante. La extracción posterior de embeddings WeSpeaker permite identificar un hablante objetivo mediante audio de enrolamiento.
Sortformer (CoreML)
Modelo neural de diarización de extremo a extremo de NVIDIA. Predice directamente la actividad por frame de hasta 4 hablantes sin etapas separadas de embedding o clustering. Se ejecuta en Neural Engine vía CoreML con buffers de estado en streaming (FIFO + caché de hablantes).
Sortformer no produce embeddings de hablante. Los flags --target-speaker y --embedding-engine solo están disponibles con el motor Pyannote.
Pipeline Pyannote
El pipeline por defecto se ejecuta en dos etapas:
Etapa 1: segmentación + encadenamiento de hablantes
Pyannote segmentation-3.0 procesa ventanas deslizantes de 10 segundos con un 50% de solapamiento. Un decodificador powerset convierte la salida de 7 clases en probabilidades por hablante (hasta 3 hablantes locales por ventana). Las ventanas adyacentes comparten un solapamiento de 5 segundos — la identidad del hablante se propaga entre ventanas calculando la correlación de Pearson entre las trayectorias de probabilidad en la zona de solapamiento, con emparejamiento exclusivo goloso para obtener IDs de hablante globales consistentes.
Etapa 2: embedding posterior
Tras la diarización, WeSpeaker ResNet34-LM extrae un embedding centroide de 256 dimensiones por hablante. Estos embeddings permiten la extracción de un hablante objetivo (--target-speaker) pero no condicionan la asignación de hablantes en sí.
Uso desde la CLI
# Diarización básica (pyannote, por defecto)
.build/release/audio diarize meeting.wav
# Sortformer de extremo a extremo (CoreML)
.build/release/audio diarize meeting.wav --engine sortformer
# Formato de salida RTTM (para evaluación)
.build/release/audio diarize meeting.wav --rttm
# Salida JSON
.build/release/audio diarize meeting.wav --json
Extracción de hablante objetivo
Proporciona audio de enrolamiento de un hablante conocido para extraer solo sus segmentos de una grabación. El pipeline calcula el embedding del audio de enrolamiento y encuentra el cluster con la mayor similitud coseno.
# Extraer los segmentos de un hablante concreto
.build/release/audio diarize meeting.wav --target-speaker enrollment.wav
Puntuación DER
Evalúa la calidad de la diarización comparándola con un archivo RTTM de referencia. El pipeline calcula la Diarization Error Rate (DER), que mide la proporción de tiempo atribuido incorrectamente.
# Puntuar contra un RTTM de referencia
.build/release/audio diarize meeting.wav --score-against reference.rttm
Salida RTTM
El flag --rttm produce salida en Rich Transcription Time Marked, un formato estándar usado para la evaluación de diarización. Cada línea sigue el formato:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
Opciones
| Opción | Descripción |
|---|---|
--target-speaker | Audio de enrolamiento para la extracción de un hablante objetivo (solo pyannote) |
--embedding-engine | Motor de embedding de hablante: mlx o coreml (solo pyannote) |
--vad-filter | Pre-filtrado con Silero VAD (solo pyannote) |
--rttm | Salida en formato RTTM |
--json | Formato de salida JSON |
--score-against | Archivo RTTM de referencia para evaluación DER |
La diarización funciona mejor con grabaciones que tienen turnos de hablante claros. Las voces altamente solapadas pueden reducir la precisión. El número de hablantes se determina automáticamente.
Descargas de modelos
Los modelos se descargan automáticamente en el primer uso:
| Componente | Modelo | Tamaño | HuggingFace |
|---|---|---|---|
| Segmentación | Pyannote-Segmentation-3.0 | ~5,7 MB | aufklarer/Pyannote-Segmentation-MLX |
| Embedding de hablante | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| Embedding de hablante | WeSpeaker-ResNet34-LM (CoreML) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
| Sortformer | Sortformer Diarization (CoreML) | ~240 MB | aufklarer/Sortformer-Diarization-CoreML |
API Swift
import SpeechVAD
let pipeline = try await DiarizationPipeline.fromPretrained()
let result = pipeline.diarize(audio: samples, sampleRate: 16000)
for seg in result.segments {
print("Speaker \(seg.speakerId): [\(seg.startTime)s - \(seg.endTime)s]")
}
// Extracción de hablante objetivo
let targetEmb = pipeline.embeddingModel.embed(audio: enrollmentAudio, sampleRate: 16000)
let segments = pipeline.extractSpeaker(
audio: meetingAudio, sampleRate: 16000,
targetEmbedding: targetEmb
)