File size: 5,549 Bytes
c7e434a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
Tempo Analysis Service
Analisis tempo dan jeda bicara menggunakan Silero VAD
"""

import torch
from typing import Dict, List
import warnings
warnings.filterwarnings('ignore')


class TempoService:
    """Analisis tempo dan jeda bicara"""
    
    def __init__(self):
        """Initialize Silero VAD model"""
        print("πŸ”„ Loading Silero VAD model...")
        torch.set_num_threads(1)
        self.model, utils = torch.hub.load(
            repo_or_dir='snakers4/silero-vad', 
            model='silero_vad', 
            force_reload=False
        )
        (self.get_speech_timestamps, 
         self.save_audio, 
         self.read_audio, 
         self.VADIterator, 
         self.collect_chunks) = utils
        print("βœ… Silero VAD model loaded!\n")
    
    def analyze(self, audio_path: str, transcription: str, sampling_rate: int = 16000) -> Dict:
        """
        Analisis tempo berdasarkan jumlah kata per menit dan deteksi jeda panjang
        
        Kriteria penilaian:
        - Poin 5 (Sangat Baik): 140-150 kata dalam 48-60 detik, tidak ada jeda >3 detik
        - Poin 4 (Baik): 110-139 kata dalam 36-60 detik, tidak ada jeda >3 detik
        - Poin 3 (Cukup): 60-109 kata dalam 60 detik, tidak ada jeda >3 detik
        - Poin 2 (Buruk): <60 kata dalam 60 detik, tidak ada jeda >3 detik
        - Poin 1 (Perlu Ditingkatkan): Berhenti sebelum 60 detik ATAU ada jeda >3 detik
        
        Args:
            audio_path: Path ke file audio
            transcription: Teks hasil transcription untuk hitung jumlah kata
            sampling_rate: Sample rate audio (default: 16000)
            
        Returns:
            Dict berisi hasil analisis lengkap
        """
        print(f"🎧 Analyzing tempo: {audio_path}")
        
        # Load audio
        wav = self.read_audio(audio_path)
        
        # Deteksi segmen bicara
        speech_timestamps = self.get_speech_timestamps(
            wav, self.model, sampling_rate=sampling_rate
        )
        
        # Hitung total durasi audio
        total_duration_sec = len(wav) / sampling_rate
        
        # Hitung jumlah kata dari transcription
        word_count = len(transcription.split())
        
        # Hitung kata per menit (normalize ke 60 detik)
        words_per_minute = (word_count / total_duration_sec) * 60 if total_duration_sec > 0 else 0
        
        # Deteksi jeda panjang (>3 detik)
        long_pauses = []
        has_long_pause = False
        
        data = []
        for i, seg in enumerate(speech_timestamps):
            start_time = seg['start'] / sampling_rate
            end_time = seg['end'] / sampling_rate
            duration = end_time - start_time
            
            if i == 0:
                pause_before = start_time
            else:
                pause_before = start_time - (speech_timestamps[i - 1]['end'] / sampling_rate)
            
            # Check jeda panjang
            if pause_before > 3.0:
                has_long_pause = True
                long_pauses.append({
                    'after_segment': i,
                    'pause_duration': round(pause_before, 2)
                })
            
            data.append({
                'segment': i + 1,
                'start_sec': round(start_time, 2),
                'end_sec': round(end_time, 2),
                'duration_sec': round(duration, 2),
                'pause_before_sec': round(pause_before, 2)
            })
        
        # Tentukan skor berdasarkan kriteria
        if total_duration_sec < 60 or has_long_pause:
            # Poin 1: Berhenti sebelum 60 detik ATAU ada jeda >3 detik
            poin = 1
            kategori = "Perlu Ditingkatkan"
            if total_duration_sec < 60:
                alasan = f"Durasi bicara hanya {round(total_duration_sec, 1)} detik (kurang dari 60 detik)"
            else:
                alasan = f"Terdapat {len(long_pauses)} jeda lebih dari 3 detik"
        elif words_per_minute >= 140 and words_per_minute <= 150 and total_duration_sec >= 48:
            # Poin 5: 140-150 kata dalam 48-60 detik
            poin = 5
            kategori = "Sangat Baik"
            alasan = f"Tempo ideal: {round(words_per_minute, 1)} kata/menit dalam {round(total_duration_sec, 1)} detik"
        elif words_per_minute >= 110 and words_per_minute <= 139 and total_duration_sec >= 36:
            # Poin 4: 110-139 kata dalam 36-60 detik
            poin = 4
            kategori = "Baik"
            alasan = f"Tempo baik: {round(words_per_minute, 1)} kata/menit dalam {round(total_duration_sec, 1)} detik"
        elif words_per_minute >= 60 and words_per_minute <= 109:
            # Poin 3: 60-109 kata dalam 60 detik
            poin = 3
            kategori = "Cukup"
            alasan = f"Tempo cukup: {round(words_per_minute, 1)} kata/menit"
        else:
            # Poin 2: <60 kata dalam 60 detik
            poin = 2
            kategori = "Buruk"
            alasan = f"Tempo lambat: hanya {round(words_per_minute, 1)} kata/menit"
        
        print("βœ… Tempo analysis complete!\n")
        
        return {
            'score': poin,
            'category': kategori,
            'reason': alasan,
            'total_duration_sec': round(total_duration_sec, 2),
            'word_count': word_count,
            'words_per_minute': round(words_per_minute, 1),
            'has_long_pause': has_long_pause,
            'long_pauses': long_pauses,
            'total_segments': len(speech_timestamps),
            # 'segments': data
        }