Sprecherdiarisierung — Diarisierung in Swift
Identifiziere, wer wann in einer Mehrsprecheraufnahme gesprochen hat. Zwei Diarisierungs-Engines sind verfügbar: eine zweistufige Pyannote-Pipeline (Segmentierung + aktivitätsbasierte Sprecherverkettung, dann Post-hoc-Embedding) und ein durchgängiges Sortformer-Modell (CoreML, Neural Engine).
Engines
Wähle die Engine mit --engine pyannote (Standard) oder --engine sortformer.
Pyannote (Standard)
Zweistufige Pipeline: Pyannote-Segmentierung verarbeitet überlappende Fenster mit aktivitätsbasierter Sprecherverkettung (Pearson-Korrelation in Überlappungsbereichen), um globale Sprecherlabels zu vergeben. Die Post-hoc-WeSpeaker-Embedding-Extraktion ermöglicht die Identifikation von Zielsprechern per Enrollment-Audio.
Sortformer (CoreML)
NVIDIAs durchgängiges neuronales Diarisierungsmodell. Sagt direkt Per-Frame-Sprecheraktivität für bis zu 4 Sprecher voraus, ohne separate Embedding- oder Clustering-Stufen. Läuft auf der Neural Engine über CoreML mit Streaming-Zustandspuffern (FIFO + Sprecher-Cache).
Sortformer erzeugt keine Sprechereinbettungen. Die Flags --target-speaker und --embedding-engine sind nur mit der Pyannote-Engine verfügbar.
Pyannote-Pipeline
Die Standard-Pipeline läuft in zwei Stufen:
Stufe 1: Segmentierung + Sprecherverkettung
Pyannote-segmentation-3.0 verarbeitet 10-Sekunden-Gleitfenster mit 50 % Überlappung. Ein Powerset-Decoder wandelt die 7-Klassen-Ausgabe in Per-Sprecher-Wahrscheinlichkeiten um (bis zu 3 lokale Sprecher pro Fenster). Benachbarte Fenster teilen sich eine 5-Sekunden-Überlappung — die Sprecheridentität wird über Fenster hinweg propagiert, indem die Pearson-Korrelation zwischen Wahrscheinlichkeitsspuren im Überlappungsbereich berechnet wird, mit gieriger exklusiver Zuordnung für konsistente globale Sprecher-IDs.
Stufe 2: Post-hoc-Embedding
Nach der Diarisierung extrahiert WeSpeaker ResNet34-LM ein 256-dimensionales Zentroid-Embedding pro Sprecher. Diese Embeddings ermöglichen die Zielsprecher-Extraktion (--target-speaker), steuern aber nicht die Sprecherzuordnung selbst.
Migration von pyannote.audio
Wenn du von der Python-Bibliothek pyannote.audio kommst — eine Pipeline-Subklasse mit pipeline.segmentation = ... ersetzt oder von einem Server mit pyannote/speaker-diarization-3.1 migrierst — Soniqo umhüllt dasselbe Pyannote-Segmentation-3.0-Modell und führt es vollständig auf dem Gerät auf Apple Silicon aus. Keine Python-Runtime, kein CUDA, kein Hugging-Face-Token zur Inferenzzeit.
| pyannote.audio (Python) | Soniqo (Swift) |
|---|---|
Pipeline.from_pretrained("pyannote/speaker-diarization-3.1") |
DiarizationPipeline.fromPretrained() |
pipeline(audio_file) |
pipeline.diarize(audio: samples, sampleRate: 16000) |
pipeline.segmentation = ... (benutzerdefinierte Subklasse) |
Fest: Pyannote-Segmentation-3.0 (MLX oder CoreML, automatisch gewählt) |
diarization.itertracks(yield_label=True) |
for seg in result.segments { ... } |
diarization.write_rttm(file) |
CLI: --rttm |
pyannote.metrics.diarization.DiarizationErrorRate |
CLI: --score-against reference.rttm |
Die Pyannote-Segmentation-3.0-Gewichte sind aus dem Upstream-HuggingFace-Checkpoint konvertiert, sodass die Segmentation-Logits innerhalb der Float-Präzisionstoleranz numerisch äquivalent sind. Die Nachsegmentierungsverkettung (Pearson-Korrelation über überlappende Fenster + gieriges exklusives Matching) und die Post-hoc-WeSpeaker-Embedding-Stufen sind in Swift reimplementiert, erzeugen aber RTTM-Ausgabe vergleichbar mit der Python-Referenzpipeline.
Es gibt kein Streaming-Äquivalent zu OnlineSpeakerDiarization für die Pyannote-Engine. Für Echtzeit-Diarisierung verwende stattdessen --engine sortformer, das das Sortformer-Modell mit FIFO- und Sprecher-Cache-Zustandspuffern ausführt.
CLI-Verwendung
# Einfache Diarisierung (pyannote, Standard)
.build/release/speech diarize meeting.wav
# Durchgängiger Sortformer (CoreML)
.build/release/speech diarize meeting.wav --engine sortformer
# RTTM-Ausgabeformat (für Auswertung)
.build/release/speech diarize meeting.wav --rttm
# JSON-Ausgabe
.build/release/speech diarize meeting.wav --json
Zielsprecher-Extraktion
Gib Enrollment-Audio eines bekannten Sprechers an, um nur dessen Segmente aus einer Aufnahme zu extrahieren. Die Pipeline berechnet das Sprecher-Embedding des Enrollment-Audios und findet das Cluster mit der höchsten Kosinusähnlichkeit.
# Segmente für einen bestimmten Sprecher extrahieren
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav
DER-Scoring
Bewerte die Diarisierungsqualität durch Vergleich mit einer Referenz-RTTM-Datei. Die Pipeline berechnet die Diarization Error Rate (DER), die den Anteil der falsch zugeordneten Zeit misst.
# Gegen Referenz-RTTM scoren
.build/release/speech diarize meeting.wav --score-against reference.rttm
RTTM-Ausgabe
Das Flag --rttm erzeugt eine Rich-Transcription-Time-Marked-Ausgabe, ein Standardformat zur Diarisierungsauswertung. Jede Zeile folgt dem Format:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
Optionen
| Option | Beschreibung |
|---|---|
--target-speaker | Enrollment-Audio für die Zielsprecher-Extraktion (nur pyannote) |
--embedding-engine | Sprecher-Embedding-Engine: mlx oder coreml (nur pyannote) |
--vad-filter | Vorfilterung mit Silero VAD (nur pyannote) |
--rttm | Ausgabe im RTTM-Format |
--json | JSON-Ausgabeformat |
--score-against | Referenz-RTTM-Datei für DER-Auswertung |
Diarisierung funktioniert am besten mit Aufnahmen, die klare Sprecherwechsel aufweisen. Stark überlappende Sprache kann die Genauigkeit verringern. Die Sprecherzahl wird automatisch bestimmt.
Modell-Downloads
Modelle werden beim ersten Gebrauch automatisch heruntergeladen:
| Komponente | Modell | Größe | HuggingFace |
|---|---|---|---|
| Segmentierung | Pyannote-Segmentation-3.0 | ~5,7 MB | aufklarer/Pyannote-Segmentation-MLX |
| Sprecher-Embedding | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| Sprecher-Embedding | WeSpeaker-ResNet34-LM (CoreML) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
| Sortformer | Sortformer Diarization (CoreML) | ~240 MB | aufklarer/Sortformer-Diarization-CoreML |
Swift-API
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]")
}
// Zielsprecher-Extraktion
let targetEmb = pipeline.embeddingModel.embed(audio: enrollmentAudio, sampleRate: 16000)
let segments = pipeline.extractSpeaker(
audio: meetingAudio, sampleRate: 16000,
targetEmbedding: targetEmb
)