Back to "ऑडियोसममारीजर: संकीर्ण अस्पष्ट दांडिक ऑडियो अक्षरीकरण"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI Architecture Audio LLM ONNX Patterns Speaker Diarization

ऑडियोसममारीजर: संकीर्ण अस्पष्ट दांडिक ऑडियो अक्षरीकरण

Saturday, 10 January 2026

स्थिति: AudioSummarizer.Core का वर्तमान में विकास के रूप में है सुस्पष्टRAG, एक बहुविध के लिए एक भावी mostlylucid उत्पाद है।

स्रोत: github.comM SK1scottgal/lucidrag 0branch v2)

जहाँ यह ठीक है: ऑडियोसममारीजर का हिस्सा है सुस्पष्टRAG कम RAG क्रियान्वयन के परिवार

सभी एक ही अनुसरण करें कम RAG पैटर्न: एक बार सिग्नलों को निकालने के लिए , साक्ष्य भंडारित करें


कई डेमो ध्वनि विश्लेषण पाइपलाइनें एक ही गलती करते हैं

वे नमूने में तरंग रूपों को खिसकाते हैं।

ऑडियोसममारीजर दो अनुपूरक पैटर्नों को जोड़ता है

  1. कम RAG पुनःप्राप्ति के लिए - एक बार सिग्नल बाहर निकालने के लिए
  2. अवरोधित अस्पष्टता वादन के लिए - तरंगM SK1 आधारित पाइपलाइन, निर्णायक आधार संभाव्यात्मक मॉडलों को प्रतिबंधित करता है

एक LLM को क्वेरी समय पर कच्चा ऑडियो फीड करने के बजाय घटाता है प्रत्येक ऑडियो फ़ाइल को एक संकेत खाता RMS ध्वनि जैसे निर्धारक संकेत निष्कर्षित साक्ष्य (ट्रैंस्क्रिप्ट्सM SK1 स्पीकर नमूना क्लिपs, डायराइजेशन टर्न्स ). यह खाता एक बार जारी रहता है

@item: inlistbox Adjective, language क्वेरी समयएक एलएलएम प्रमुख संकेतों पर पुनर्प्राप्ति और तर्क द्वारा प्रतिक्रियाओं को संश्लेषित करता है।

पैटर्न रचना: कम RAG हैंडल क्या भंडारण और प्राप्त करने के लिए. संकीर्ण धुंधलापन हैंडल कैसे संकेतों को विश्वसनीय रूप से निकालने के लिए

Reduced RAG पैटर्न के बारे में अधिक जानें: Reduced RAG: Signal-Driven Document Understanding

कुंजी शब्दावली

  • संकेतविश्वास प्राप्तियों के साथ टाइप किए गए तथ्य audio.content_type = "speech"विश्वास
  • प्रमाणश्रव्य नमूना क्लिप
  • सिग्नल लेजर: सभी संकेतों के स्थायी बंडल
  • स्पीकर आईडीएकल फ़ाइल के भीतर स्थानीय पहचानकर्ता SPEAKER_00)
  • ध्वनि प्रिंट आईडीफ़ाइल स्थिर हैश voice embedding से व्युत्पन्न vprint:a3f9c2e1)
  • व्यक्तिस्पष्ट रूप से अनुमानित नहीं है

पहचान मॉडल: SPEAKER_00 एक ऑडियो फ़ाइल में स्थानीय है. VoiceprintId फ़ाइलों में स्थिर है लेकिन अनाम

दांडिक आवश्यकताएं: प्रत्येक संकेत में शामिल है उद्गम (किसी तरंग ने इसे उत्पन्न किया है विश्वास (निश्चय की डिग्री संस्करण (modelM SK1threshold versions). This enables reproducibility()

परिणाम है

  • फास्ट, गोपनीयता-फॉरेंसिक ऑडियो वर्णन संरक्षण
  • पाइथोन निर्भरताओं के बिना स्पीकर डियराइजेशन
  • अनाम स्पीकर समानता पता लगाना (no PII
  • के लिए आवाज एम्बेडिंग "सदृश स्पीकरों को ढूंढें
  • इनग्रास के दौरान क्लाउड APIs को ऑडियो भेजने के बिना सभी

यह सीधे पर बनाता है प्रतिबंधित अस्पष्टता पैटर्न और तरंग-- से आधारित वास्तुकला ImageSummarizerComment.

कोर अंतर्दृष्टि एलएलएम को ध्वनिक तथ्यों पर तर्क करना चाहिए

इस लेख में शामिल है

  • कैसे ऑडियोसममैरिजर Reduced RAG पैटर्न को लागू करता है
  • संकेत निष्कर्षन: निर्णायक ध्वनिक तथ्य M SK1एलएलएम सारांश नहीं
  • प्रमाण भंडारण
  • शुद्ध .NET स्पीकर डायराइजेशन (नहीं पाइथोन
  • Query-time synthesisM SK1 filter signals → retrieve evidence → LLM synthesizes

संबंधित लेख:

  • कम RAG - यह लागू करता है कोर पैटर्न
  • प्रतिबंधित अस्पष्टता पैटर्न - आधारभूत पैटर्न
  • कम RAG कार्यान्वयन
    • डॉक-सममैरिजर - दस्तावेज आरएजी के साथ इकाई निष्कर्षण और ज्ञान ग्राफ
    • डेटा-सममारीजरName - डेटा प्रोफाइलिंग और स्कीमा निष्कर्ष RAG
    • ImageSummarizerComment - छवि RAG के साथ 22- ध्वनि दृश्य अभिज्ञान पाइपलाइन
    • ऑडियोसममारीजर (इस अनुच्छेद) - स्पीकर डायराइजेशन के साथ ऑडियो फ़ोरेंसिक कैरेक्ट्रीज़ेशन

समस्या: ऑडियो विश्लेषण कठिन है ( और सांस्कृतिक रूप से लोड

ऑडियो विश्लेषण पूर्वानुमानित तरीके से असफल होता है

  • सांस्कृतिक दावेयह जॉज़ है
  • स्पीकर नामस्पीकर जॉन स्मिथ है
  • संगीत पहचानSandstorm by Darude
  • त्वरित अनुमानspokeer has British accent
  • पाइथोन निर्भरताएँMost diarization tools require pyannote

पारंपरिक दृष्टिकोण

समस्यायह या तो PII छिपाता है ( स्पीकर नामों को | ), | बहुत अधिक खर्च करता है ( | क्लाउड APIs |), | या पाइथोन रनटाइम की आवश्यकता होती है

समाधानएक शुद्ध पीपलाइन बनाना जो ऑडियो संरचनात्मक और ध्वनित्मक रूप से वर्णित करता है


कम RAG ऑडियो फ्रेमवर्क

ऑडियोसममैरिजर कम RAG पैटर्न ऑडियो फ़ाइलों के लिए इसके तीन मूल सिद्धांतों के अनुरूप

1. सिग्नल निष्कर्षण

एक बार निकाले गए निर्णायक संकेत

  • अस्थायी: अवधि
  • ध्वनिक: आरएमएस ध्वनि
  • पहचान: SHA
  • गुणवत्ता: ट्रांसक्रिप्शन विश्वास
  • स्पीकर: ध्वनि प्रिंट आईडी
  • श्रेणिकसामग्री क़िस्म (speech/music/silence), speaker classification

पोडकास्ट के लिए उदाहरण संकेत सेट:

{
  "audio.hash.sha256": "3f2a9c8b1e4d...",
  "audio.duration_seconds": 912.0,
  "audio.rms_db": -18.2,
  "audio.spectral_centroid_hz": 2418.3,
  "audio.content_type": "speech",
  "speaker.count": 2,
  "speaker.classification": "two_speakers",
  "transcription.confidence": 0.87,
  "voice.voiceprint_id.speaker_00": "vprint:a3f9c2e1d4b8"
}

ये संकेत हैं निर्णायक (सही ऑडियो अनुक्रमणिका योग्य (से फ़िल्टर कर सकता है LLMs के बिना संगणनीय pure संकेत प्रसंस्करण

2. साक्ष्य भंडारण

सिर्फ भंडारण के बजाय "chunks

  • संकेतनिर्णायक फिल्टरिंग के लिए : संरचनात्मक क्षेत्र
  • सम्मिलितस्पीकर समानता के लिए : ध्वनि सम्मिलन
    • गोपनीयता नोट: एम्बेडिंग इस प्रणाली में व्यवहार्य रूप से उल्टा नहीं जा सकता है, लेकिन उन्हें संवेदनशील डेटा के रूप में समझें
  • साक्ष्य अवयव:
    • पूरा ट्रांस्क्रिप्ट पाठ (खोजी योग्य
    • स्पीकर नमूना क्लिप्स (Base64 WAVM SK2 | | 2- | सत्यापन के लिए सेकेंड क्लिप
    • डियराइजेशन टर्न्स (JSON के साथ स्पीकर | _ | ID |, | आरंभ & #44; / | समाप्ति समयtamps |
  • संकेतक: फाइल हैश | auditable provenance के लिए साक्ष्य आईडी

साक्ष्य लेखापरीक्षा योग्य हैउपयोगकर्ता स्पीकर नमूने बजा सकते हैं

Query

क्वेरी के समय, LLM कभी नहीं नकली ऑडियो को देखता है. इसके बजाय:

  1. निर्धारणात्मक रूप से फ़िल्टर करें (डेटाबेस जहां खंड

    WHERE audio.content_type = 'speech'
      AND speaker.count >= 2
      AND audio.rms_db > -25.0
      AND transcription.confidence > 0.8
    
  2. हाइब्रिड खोज (BM

    • ट्रांस्क्रिप्ट पाठ पर BM25
    • आवाज एम्बेडिंग पर वेक्टर समानता (स्पीकर समानता
    • वापस ऊपर 5 ऑडियो फ़ाइलें
  3. संकेतों से समन्वयित करें (LLM संरचनात्मक साक्ष्य पैक को देखता है

    Audio 1: podcast_ep42.mp3
    - Duration: 15m 12s
    - Speakers: 2 (SPEAKER_00: 52%, SPEAKER_01: 48%)
    - Quality: RMS -18.2dB, no clipping
    - Transcript: "Welcome to Tech Insights. Today we're discussing..."
    - Entities: ["quantum computing", "Google", "IBM"]
    

एलएलएम प्राप्त करता है 5 संरचनात्मक साक्ष्य पैकेज के बजाय 500 कच्चे ऑडियो मेटाडेटा टुकड़े. संदर्भ विंडो कमन

लागत प्रभाव यदि भुगतानित LLM APIs का उपयोग किया जाता है तो (प्रसंस्करण करने के लिए \100ऑडियो फ़ाइलें \ ~$5\ से होती हैं | (\ पुनः -\ हर क्वेरी को ऑडियो विश्लेषित करने के लिये
नोटः: lucidRAG स्थानीय है।

यह मूल Reduced RAG अंतर्दृष्टि है पहले से तय करें कि क्या है सिग्नल यह निर्णायक रूप से भंडारित करें (evidence केवल संश्लेषण के लिए एलएलएम शामिल करें क्वेरी


प्रतिबंधित अस्पष्टता ऑडियो पाइपलाइन

तंत्र पहले क्रम में तरंग चलाता है

Wave Priority Order:
  100: IdentityWave          → SHA-256, file metadata, duration
   90: FingerprintWave       → Chromaprint perceptual hash (optional)
   80: AcousticProfileWave   → RMS, spectral features, SNR
   70: ContentClassifierWave → Speech vs music heuristics (routing)
   65: TranscriptionWave     → Whisper.NET (optional)
   60: SpeakerDiarizationWave→ Pure .NET speaker separation
   30: VoiceEmbeddingWave    → ECAPA-TDNN speaker similarity

नोट: संवेदन/संवेदन पता लगाने को जानबूझकर बाहर निकाला गयाM SK2 सांस्कृतिक रूप से भारित और दांडिक लक्षणीकरण का हिस्सा नहीं है

वास्तुकला

flowchart LR
    A[Audio file] --> B[IdentityWave]
    B --> C[AcousticProfileWave]
    C --> D[ContentClassifierWave]
    D --> E[TranscriptionWave]
    E --> F[SpeakerDiarizationWave]
    F --> G[VoiceEmbeddingWave]
    G --> H[Signal Ledger]
    H --> I[Optional LLM Synthesis]

    style B stroke:#333,stroke-width:4px
    style D stroke:#333,stroke-width:4px
    style F stroke:#333,stroke-width:4px
    style H stroke:#333,stroke-width:4px

यह परिचित को सुरक्षित करता है तरंग → संकेत → वैकल्पिक एलएलएम loop, लेकिन यह निर्णायक तथ्यों में एङ्कर करता है

  • ध्वनि प्रोफाइलिंग निर्धारणात्मक है।
  • एलएलएम साक्ष्य पर कार्य करता है-, नकली ऑडियो नहीं

वास्तविक-विश्व उदाहरण: पोडकास्ट श्रृङ्खला को प्रक्रमित करना

पाइपलाइन के माध्यम से एक मिनट का पॉडकास्ट श्रृङ्खला ट्रेस करें

Input: podcast_ep42.mp3
  - File size: 14.2 MB
  - Format: MP3, 44.1kHz stereo, 192 kbps
  - Duration: 15m 12s (912 seconds)
  - Content: 2-person interview

Wave Execution (priority order 100 → 30):

1. IdentityWave (Priority 100, 87ms):
   ✓ SHA-256: 3f2a9c8b1e4d...
   ✓ Duration: 912.0s
   ✓ Channels: 2 (stereo)
   ✓ Sample rate: 44100 Hz
   ✓ File size: 14,897,234 bytes

2. AcousticProfileWave (Priority 80, 142ms):
   ✓ RMS loudness: -18.2 dB (good mastering)
   ✓ Peak amplitude: 0.94 (no clipping)
   ✓ Dynamic range: 22.1 dB
   ✓ Spectral centroid: 2418 Hz (speech-like)
   ✓ Spectral rolloff: 7892 Hz

3. ContentClassifierWave (Priority 70, 89ms):
   ✓ Zero-crossing rate: 0.17 (high → speech)
   ✓ Spectral flux: 0.28 (low → not music)
   → Classification: "speech" (confidence: 0.85)

4. TranscriptionWave (Priority 65, 12.3s):
   ✓ Whisper.NET base model
   ✓ Segments: 142
   ✓ Total words: 2,341
   ✓ Confidence: 0.87 (high)
   ✓ Text: "Welcome to Tech Insights. Today we're discussing..."

5. SpeakerDiarizationWave (Priority 60, 3.8s):
   ✓ VAD detected: 47 speech segments
   ✓ Embeddings extracted: 47 × 512-dim vectors
   ✓ Clustering (threshold 0.75): 2 speakers
   ✓ Turns before merge: 47
   ✓ Turns after merge: 23
   ✓ SPEAKER_00 participation: 52% (474s)
   ✓ SPEAKER_01 participation: 48% (438s)
   ✓ Sample clips extracted: 2 (Base64 WAV, ~30KB each)

6. VoiceEmbeddingWave (Priority 30, 178ms):
   ✓ ECAPA-TDNN inference
   ✓ Embedding dimension: 512
   ✓ Voiceprint ID (SPEAKER_00): "vprint:a3f9c2e1d4b8"
   ✓ Voiceprint ID (SPEAKER_01): "vprint:7e2d8f1a9c3b"

Total processing time: 16.6s
Signals emitted: 47
LLM calls: 0 (fully offline)
Cost: $0 (local processing only)

सिग्नल लेजर क्या डाटाबेस में स्थायी हो जाता है

सिग्नल लेजर (excerpt

{
  "identity.filename": "podcast_ep42.mp3",
  "audio.hash.sha256": "3f2a9c8b1e4d...",
  "audio.duration_seconds": 912.0,
  "audio.format": "mp3",
  "audio.sample_rate": 44100,
  "audio.channels": 2,
  "audio.channel_layout": "stereo",
  "audio.rms_db": -18.2,
  "audio.dynamic_range_db": 22.1,
  "audio.spectral_centroid_hz": 2418.3,
  "audio.spectral_rolloff_hz": 7892.1,
  "audio.content_type": "speech",
  "content.confidence": 0.85,
  "speaker.count": 2,
  "speaker.classification": "two_speakers",
  "speaker.turn_count": 23,
  "speaker.avg_turn_duration": 39.7,
  "speaker.diarization_method": "agglomerative_clustering",
  "speaker.participation": {
    "SPEAKER_00": 52.0,
    "SPEAKER_01": 48.0
  },
  "transcription.full_text": "Welcome to Tech Insights...",
  "transcription.word_count": 2341,
  "transcription.confidence": 0.87,
  "voice.embedding.speaker_00": [0.023, -0.511, 0.882, ...],
  "voice.voiceprint_id.speaker_00": "vprint:a3f9c2e1d4b8",
  "speaker.sample.speaker_00": "UklGRiQAAABXQVZF...",
  "speaker.sample.speaker_01": "UklGRiQBBBXQVZF..."
}

क्या यह सक्षम करता है

  • के लिए खोज "Tech Insights discusses cloud infrastructure
  • Query "Voiceprint vprint के साथ ऑडियो ढूंढें
  • पूछें "क्या
  • फ़िल्टर "नहीं ढूंढें-गुणवत्ता अभिलेखों को ढूँढेंM SK2 → RMS | < | \ -25 | dB या क्लिपिंग
  • सत्यापित करें "डिड स्पीकर पहचान कार्य speaker.sample.speaker_00 क्लिप

क्यों संकेत महत्वपूर्ण हैं (जैसे LLM के बिना भी

एक सिग्नल लाईडर बोरिंग उत्तर देता है

  • क्या यह ऑडियो भाषण है
  • कितने स्पीकर पहचाने गए हैं
  • क्या ध्वनि अनुपात
  • क्या वहाँ क्लिपिंग आर्टिफैक्ट या विकृतियाँ हैं
  • क्या है स्पेक्ट्रल केंद्रक
  • क्या है गतिशील दायरा

इन सवालों को आप सामान्यतः खोजते हैं

संकेत खाता आपको उन्हें सेकेंड में देता है


वेव पाइपलाइन

तरंग 1: पहचान

आधारलाइन. क्रिप्टोग्राफिक पहचान और फ़ाइल मेटाडेटा

public class IdentityWave : IAudioWave
{
    public string Name => "IdentityWave";
    public int Priority => 100;  // Runs first

    public async Task<IEnumerable<Signal>> AnalyzeAsync(
        string audioPath,
        AnalysisContext context,
        CancellationToken ct)
    {
        var signals = new List<Signal>();

        // Cryptographic hash (deterministic identity)
        var fileHash = await ComputeSha256Async(audioPath, ct);
        signals.Add(new Signal
        {
            Name = "audio.hash.sha256",
            Value = fileHash,
            Type = SignalType.Identity,
            Confidence = 1.0,
            Source = Name
        });

        // File-level metadata
        var fileInfo = new FileInfo(audioPath);
        signals.Add(new Signal
        {
            Name = "audio.file_size_bytes",
            Value = fileInfo.Length,
            Type = SignalType.Metadata,
            Source = Name
        });

        // Audio format metadata
        using var reader = new AudioFileReader(audioPath);

        signals.Add(new Signal
        {
            Name = "audio.duration_seconds",
            Value = reader.TotalTime.TotalSeconds,
            Type = SignalType.Metadata,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.sample_rate",
            Value = reader.WaveFormat.SampleRate,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.channels",
            Value = reader.WaveFormat.Channels,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.format",
            Value = Path.GetExtension(audioPath).TrimStart('.'),
            Type = SignalType.Metadata,
            Source = Name
        });

        return signals;
    }
}

जारी कुंजी संकेत

  • audio.hash.sha256 - कूटशब्द पहचान
  • audio.duration_seconds - लंबाई
  • audio.sample_rate -
  • audio.channels -\x97\x98\x99 1\x95\x92(\mono),\x96\x93\x94 2\x91 (\stereo),\x89 \x90 6\x88
  • audio.format एमएसके0 एमपीएसके1 वावएमएसके2 फ्लैक एमएसका3 आदि एमएसक4

क्यों निर्णायक

  • एक ही फ़ाइल → एक ही हैश
  • नमूना अनियमितता नहीं
  • विश्वसनीयता =

तरंग 2: ध्वनि प्रोफाइल ( संकेत प्रसंस्करण

structural acoustic properties using NAudio and FftSharp

public class AcousticProfileWave : IAudioWave
{
    private readonly ILogger<AcousticProfileWave> _logger;

    public string Name => "AcousticProfileWave";
    public int Priority => 80;

    public async Task<IEnumerable<Signal>> AnalyzeAsync(
        string audioPath,
        AnalysisContext context,
        CancellationToken ct)
    {
        var signals = new List<Signal>();

        using var reader = new AudioFileReader(audioPath);

        // Convert to mono for analysis
        ISampleProvider sampleProvider = reader.WaveFormat.Channels == 1
            ? reader
            : new StereoToMonoSampleProvider(reader) { LeftVolume = 0.5f, RightVolume = 0.5f };

        // Read all samples
        var samples = ReadAllSamples(sampleProvider);

        // Time-domain analysis
        var rms = CalculateRms(samples);
        var peakAmplitude = samples.Max(Math.Abs);
        var dynamicRange = CalculateDynamicRange(samples);
        var clippingRatio = CalculateClippingRatio(samples, threshold: 0.99);

        signals.Add(new Signal
        {
            Name = "audio.rms_db",
            Value = 20 * Math.Log10(rms),  // Convert to decibels
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.peak_amplitude",
            Value = peakAmplitude,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.dynamic_range_db",
            Value = dynamicRange,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.clipping_ratio",
            Value = clippingRatio,
            Type = SignalType.Acoustic,
            Confidence = clippingRatio > 0.01 ? 0.9 : 1.0,  // Low confidence if clipping detected
            Source = Name
        });

        // Frequency-domain analysis (FFT)
        var spectralFeatures = CalculateSpectralFeatures(samples, reader.WaveFormat.SampleRate);

        signals.Add(new Signal
        {
            Name = "audio.spectral_centroid_hz",
            Value = spectralFeatures.Centroid,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.spectral_rolloff_hz",
            Value = spectralFeatures.Rolloff,
            Type = SignalType.Acoustic,
            Source = Name
        });

        signals.Add(new Signal
        {
            Name = "audio.spectral_bandwidth_hz",
            Value = spectralFeatures.Bandwidth,
            Type = SignalType.Acoustic,
            Source = Name
        });

        return signals;
    }

    private SpectralFeatures CalculateSpectralFeatures(float[] samples, int sampleRate)
    {
        // Use FftSharp for frequency analysis
        int fftSize = 2048;
        var fftInput = new double[fftSize];

        // Take middle section of audio
        int offset = Math.Max(0, (samples.Length - fftSize) / 2);
        for (int i = 0; i < fftSize; i++)
        {
            fftInput[i] = samples[offset + i];
        }

        // Apply Hamming window
        var window = FftSharp.Window.Hamming(fftSize);
        for (int i = 0; i < fftSize; i++)
        {
            fftInput[i] *= window[i];
        }

        // Compute FFT
        var fft = FftSharp.Transform.FFT(fftInput);
        var magnitudes = fft.Select(c => Math.Sqrt(c.Real * c.Real + c.Imaginary * c.Imaginary)).ToArray();

        // Calculate spectral centroid (brightness)
        double sumWeightedFreq = 0;
        double sumMagnitude = 0;
        for (int i = 0; i < magnitudes.Length / 2; i++)
        {
            double freq = i * sampleRate / (double)fftSize;
            sumWeightedFreq += freq * magnitudes[i];
            sumMagnitude += magnitudes[i];
        }
        double centroid = sumMagnitude > 0 ? sumWeightedFreq / sumMagnitude : 0;

        // Calculate spectral rolloff (85% energy threshold)
        double totalEnergy = magnitudes.Take(magnitudes.Length / 2).Sum(m => m * m);
        double cumulativeEnergy = 0;
        double rolloff = 0;
        for (int i = 0; i < magnitudes.Length / 2; i++)
        {
            cumulativeEnergy += magnitudes[i] * magnitudes[i];
            if (cumulativeEnergy >= 0.85 * totalEnergy)
            {
                rolloff = i * sampleRate / (double)fftSize;
                break;
            }
        }

        return new SpectralFeatures
        {
            Centroid = centroid,
            Rolloff = rolloff,
            Bandwidth = CalculateBandwidth(magnitudes, centroid, sampleRate, fftSize)
        };
    }
}

उदाहरण आउटपुट

Input: podcast.mp3 (15 minutes, 44.1kHz stereo)

Signals emitted:
  audio.rms_db = -18.2 dB (good loudness)
  audio.peak_amplitude = 0.94 (no clipping)
  audio.dynamic_range_db = 22 dB (moderate dynamics)
  audio.clipping_ratio = 0.003 (0.3% clipping, minimal)
  audio.spectral_centroid_hz = 2400 Hz (mid-brightness, speech-like)
  audio.spectral_rolloff_hz = 8000 Hz (most energy below 8kHz)
  audio.spectral_bandwidth_hz = 4200 Hz (moderate spread)

यह क्यों महत्वपूर्ण है

  • आरएमएस ध्वनि गुणवत्ता को मास्टरिंग इंगित करता है
  • क्लिपिंग अनुपात रिकॉर्डिंग आर्टिफैक्ट पता लगाता है
  • स्पेक्ट्रल केंद्रक ध्वनि को संगीत से भिन्न करता है
  • सभी deterministic—same ऑडियो एक ही मान उत्पन्न करता है

तरंग 3: सामग्री वर्गीकरण

संकीर्ण ह्रासीय वर्गीकरण रुटिंग प्रयोजनों के लिए शून्य का उपयोग करते हुए

नोटThese heuristics are genre/context dependentMSC2not reliably accurate across all content typesM SK3 Used only for wave routing decisions (eMST5gMSP6 skip diarization for musicMSL7 not as ground truthMLSK8

public class ContentClassifierWave : IAudioWave
{
    public string Name => "ContentClassifierWave";
    public int Priority => 70;

    public async Task<IEnumerable<Signal>> AnalyzeAsync(
        string audioPath,
        AnalysisContext context,
        CancellationToken ct)
    {
        var signals = new List<Signal>();

        using var reader = new AudioFileReader(audioPath);
        var samples = ReadAllSamples(reader);

        // Zero-crossing rate (heuristic: speech tends higher ZCR - not universal)
        var zcr = CalculateZeroCrossingRate(samples);

        // Spectral flux (heuristic: music tends more consistent - varies by genre)
        var spectralFlux = CalculateSpectralFlux(samples, reader.WaveFormat.SampleRate);

        // Simple heuristic classifier (for routing, not identity)
        // Thresholds calibrated for typical podcast/interview content
        // Production: calibrate on your corpus for best routing accuracy
        string contentType;
        double confidence;

        if (zcr > 0.15 && spectralFlux < 0.3)  // Speech heuristic
        {
            contentType = "speech";
            confidence = 0.85;
        }
        else if (zcr < 0.10 && spectralFlux > 0.5)
        {
            contentType = "music";
            confidence = 0.80;
        }
        else
        {
            contentType = "mixed";
            confidence = 0.70;
        }

        // Check for silence
        var rmsDb = context.GetValue<double>("audio.rms_db");
        if (rmsDb < -50)
        {
            contentType = "silence";
            confidence = 0.95;
        }

        signals.Add(new Signal
        {
            Name = "audio.content_type",
            Value = contentType,
            Type = SignalType.Classification,
            Confidence = confidence,
            Source = Name,
            Metadata = new Dictionary<string, object>
            {
                ["zero_crossing_rate"] = zcr,
                ["spectral_flux"] = spectralFlux,
                ["rms_db"] = rmsDb
            }
        });

        return signals;
    }
}

उदाहरण रूटिंग

Speech (ZCR=0.18, flux=0.25):
  → audio.content_type = "speech"
  → Enables: TranscriptionWave, SpeakerDiarizationWave

Music (ZCR=0.08, flux=0.65):
  → audio.content_type = "music"
  → Disables: SpeakerDiarizationWave (no speakers to detect)

Silence (RMS=-52dB):
  → audio.content_type = "silence"
  → Disables: All downstream waves (early exit)

शुद्ध .NET स्पीकर डियराइजेशन

lucidRAG's विन्यास प्रतिबंधों के लिए: पाइआनोट के बिना स्पीकर डायराइजेशन

पाइथोन के साथ समस्या

पारंपरिक दृष्टिकोण

pyannote.audio (Python) → ONNX export (experimental) → C# wrapper (fragile)

समस्याएँ

  • pyannote 3.1+ हटाया ऑन nc समर्थन (pure PyTorch में खिसकाया गया
  • पाइथन रनटाइम आवश्यक है (implementation nightmare
  • एचटीटीपी रैपर लेटेंसी और जटिलता जोड़ता है
  • ऑफ़लाइन समर्थन नहीं

समाधान मौजूदा आवाज एम्बेडिंग मॉडलों का उपयोग करके शुद्ध .NET में डायराइजेशन लागू करें

शुद्ध .NET एल्गोरिथ्म

1. Voice Activity Detection (VAD) → Detect speech segments (energy-based RMS)
2. Segment Embedding → Extract ECAPA-TDNN embeddings for each segment
3. Agglomerative Clustering → Group segments by speaker (cosine similarity)
4. Speaker Turns → Merge consecutive turns from same speaker

क्रियान्वयन

csharp public class SpeakerDiarizationService { private readonly ILogger _logger; private readonly VoiceEmbeddingService _embeddingService; private readonly AudioConfig _config;

public virtual async Task<DiarizationResult> DiarizeAsync(
    string audioPath,
    CancellationToken ct = default)
{
    _logger.LogInformation("Starting speaker diarization for {AudioPath}", audioPath);

    // Step 1: Detect speech segments using VAD
    var segments = DetectSpeechSegments(audioPath);
    _logger.LogDebug("Detected {Count} speech segments", segments.Count);

    if (segments.Count == 0)
    {
        return new DiarizationResult
        {
            Turns = new List<SpeakerTurn>(),
            SpeakerCount = 0
        };
    }

    // Step 2: Extract embeddings for each segment
    var embeddings = new List<(SpeechSegment Segment, float[] Embedding)>();
    foreach (var segment in segments)
    {
        try
        {
            var embedding = await ExtractSegmentEmbeddingAsync(audioPath, segment, ct);
            embeddings.Add((segment, embedding));
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to extract embedding for segment {Start}-{End}",
                segment.StartSeconds, segment.EndSeconds);
        }
    }

    // Step 3: Cluster embeddings to identify speakers
    var speakerClusters = ClusterSpeakers(embeddings);
    _logger.LogInformation("Identified {Count} speakers", speakerClusters.Keys.Count);

    // Step 4: Create speaker turns
    var turns = new List<SpeakerTurn>();
    foreach (var (segment, embedding) in embeddings)
    {
        var speakerId = FindSpeakerForEmbedding(embedding, speakerClusters);
        turns.Add(new SpeakerTurn
        {
            SpeakerId = speakerId,
            StartSeconds = segment.StartSeconds,
            EndSeconds = segment.EndSeconds,
            Confidence = 1.0  // TODO: Calculate based on cluster distance
        });
    }

    // Step 5: Merge consecutive turns from same speaker
    var mergedTurns = MergeConsecutiveTurns(turns);

    return new DiarizationResult
    {
        Turns = mergedTurns,
        SpeakerCount = speakerClusters.Keys.Count
    };
}

// Simple VAD using energy-based speech detection
private List<SpeechSegment> DetectSpeechSegments(string audioPath)
{
    using var reader = new AudioFileReader(audioPath);
    ISampleProvider sampleProvider = reader.WaveFormat.Channels == 1
        ? reader
        : new StereoToMonoSampleProvider(reader) { LeftVolume = 0.5f, RightVolume = 0.5f };

    var sampleRate = sampleProvider.WaveFormat.SampleRate;
    var windowSize = sampleRate / 10; // 100ms windows
    var buffer = new float[windowSize];

    var segments = new List<SpeechSegment>();
    SpeechSegment? currentSegment = null;

    double timeSeconds = 0;
    int samplesRead;

    while ((samplesRead = sampleProvider.Read(buffer, 0, buffer.Length)) > 0)
    {
        // Calculate RMS energy for this window
        double rms = Math.Sqrt(buffer.Take(samplesRead).Sum(s => s * s) / samplesRead);

        // Speech detection threshold (simple baseline - fragile across gain levels)
        // Production: use relative threshold (noise floor / percentile) or per-file calibration
        bool isSpeech = rms > 0.02;  // Fixed threshold for demonstration

        if (isSpeech)
        {
            if (currentSegment == null)
            {
                // Start new segment
                currentSegment = new SpeechSegment
                {
                    Star
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.