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).

Nota

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ónDescripción
--target-speakerAudio de enrolamiento para la extracción de un hablante objetivo (solo pyannote)
--embedding-engineMotor de embedding de hablante: mlx o coreml (solo pyannote)
--vad-filterPre-filtrado con Silero VAD (solo pyannote)
--rttmSalida en formato RTTM
--jsonFormato de salida JSON
--score-againstArchivo RTTM de referencia para evaluación DER
Importante

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:

ComponenteModeloTamañoHuggingFace
SegmentaciónPyannote-Segmentation-3.0~5,7 MBaufklarer/Pyannote-Segmentation-MLX
Embedding de hablanteWeSpeaker-ResNet34-LM (MLX)~25 MBaufklarer/WeSpeaker-ResNet34-LM-MLX
Embedding de hablanteWeSpeaker-ResNet34-LM (CoreML)~25 MBaufklarer/WeSpeaker-ResNet34-LM-CoreML
SortformerSortformer Diarization (CoreML)~240 MBaufklarer/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
)