kassaby commited on
Commit
0ba63c7
·
verified ·
1 Parent(s): a87f1dd

Upload 2 files

Browse files
Files changed (2) hide show
  1. recitation_segmenter_app.py +247 -0
  2. requirements_txt.txt +9 -0
recitation_segmenter_app.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import torch
4
+ import soundfile as sf
5
+ import librosa
6
+ from matplotlib import pyplot as plt
7
+ from transformers import AutoFeatureExtractor, AutoModelForAudioFrameClassification
8
+ from recitations_segmenter import segment_recitations, clean_speech_intervals
9
+ import io
10
+ from PIL import Image
11
+
12
+ # Setup device and model
13
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
14
+ dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32
15
+
16
+ print(f"Loading model on {device}...")
17
+ processor = AutoFeatureExtractor.from_pretrained("obadx/recitation-segmenter-v2")
18
+ model = AutoModelForAudioFrameClassification.from_pretrained(
19
+ "obadx/recitation-segmenter-v2",
20
+ torch_dtype=dtype,
21
+ device_map=device
22
+ )
23
+ print("Model loaded successfully!")
24
+
25
+ def read_audio(path, sampling_rate=16000):
26
+ """قراءة ملف صوتي وتحويله"""
27
+ audio, sr = sf.read(path)
28
+ if len(audio.shape) > 1:
29
+ audio = audio.mean(axis=1)
30
+ if sr != sampling_rate:
31
+ audio = librosa.resample(audio, orig_sr=sr, target_sr=sampling_rate)
32
+ return torch.tensor(audio).float()
33
+
34
+ def get_interval(x: np.ndarray, intervals: list[list[int]], idx: int, sr=16000, delta=0.3, exact_boundries=False):
35
+ """استخراج مقطع صوتي من الفواصل"""
36
+ start = int((intervals[idx][0] - delta) * sr)
37
+ end = int(intervals[idx][1] * sr)
38
+ if not exact_boundries:
39
+ start = 0 if idx == 0 else int((intervals[idx][0] - delta) * sr)
40
+ end = len(x) if idx == len(intervals) - 1 else int((intervals[idx + 1][0] - delta) * sr)
41
+ return x[start: end]
42
+
43
+ def plot_signal(x: np.ndarray, intervals: list[list[float]], log_min_count=5, sr=16000):
44
+ """رسم الإشارة الصوتية مع الفواصل"""
45
+ fig, ax = plt.subplots(figsize=(20, 4))
46
+ if isinstance(x, torch.Tensor):
47
+ x = x.numpy()
48
+ ax.plot(x, linewidth=0.5)
49
+
50
+ intervals_flat = np.array(intervals).reshape(-1)
51
+ diffs = np.diff(intervals_flat)
52
+
53
+ min_silence_diffs_idx = float('-inf')
54
+ info_text = ""
55
+
56
+ if len(intervals_flat) > 2:
57
+ silence_diffs = diffs[1: len(diffs): 2]
58
+ min_silence_diffs_ids = silence_diffs.argsort()[: log_min_count]
59
+ min_silence_diffs_idx = min_silence_diffs_ids[0] * 2 + 1
60
+
61
+ info_text += f'Minimum Silence Interval IDs: {min_silence_diffs_ids}\n'
62
+ info_text += f'Minimum Silence Intervals: {silence_diffs[min_silence_diffs_ids]}\n'
63
+
64
+ speech_diffs = diffs[0: len(diffs): 2]
65
+ min_speech_diffs_ids = speech_diffs.argsort()[: log_min_count]
66
+ info_text += f'Minimum Speech Interval IDs: {min_speech_diffs_ids}\n'
67
+ info_text += f'Minimum Speech Intervals: {speech_diffs[min_speech_diffs_ids]}\n'
68
+
69
+ ymin = x.min()
70
+ ymax = x.max()
71
+
72
+ for idx, val in enumerate(intervals_flat):
73
+ color = 'red'
74
+ if idx in [min_silence_diffs_idx, min_silence_diffs_idx + 1]:
75
+ color = 'green'
76
+ ax.axvline(x=val * sr, ymin=0, ymax=1, color=color, alpha=0.6, linewidth=1)
77
+
78
+ ax.set_xlabel('Samples')
79
+ ax.set_ylabel('Amplitude')
80
+ ax.set_title('Audio Signal with Detected Intervals')
81
+ ax.grid(True, alpha=0.3)
82
+ plt.tight_layout()
83
+
84
+ buf = io.BytesIO()
85
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
86
+ buf.seek(0)
87
+ img = Image.open(buf)
88
+ plt.close()
89
+
90
+ return img, info_text
91
+
92
+ def process_audio(audio_file, min_silence_ms, min_speech_ms, pad_ms):
93
+ """معالجة الملف الصوتي وتقطيعه"""
94
+
95
+ if audio_file is None:
96
+ return None, "⚠️ من فضلك ارفع ملف صوتي", []
97
+
98
+ try:
99
+ # قراءة الملف
100
+ wav = read_audio(audio_file)
101
+
102
+ # تقسيم التلاوة
103
+ sampled_outputs = segment_recitations(
104
+ [wav],
105
+ model,
106
+ processor,
107
+ device=device,
108
+ dtype=dtype,
109
+ batch_size=4,
110
+ )
111
+
112
+ # تنظيف الفواصل
113
+ clean_out = clean_speech_intervals(
114
+ sampled_outputs[0].speech_intervals,
115
+ sampled_outputs[0].is_complete,
116
+ min_silence_duration_ms=min_silence_ms,
117
+ min_speech_duration_ms=min_speech_ms,
118
+ pad_duration_ms=pad_ms,
119
+ return_seconds=True,
120
+ )
121
+
122
+ intervals = clean_out.clean_speech_intervals
123
+
124
+ # رسم الإشارة
125
+ plot_img, stats_text = plot_signal(wav, intervals)
126
+
127
+ # استخراج المقاطع الصوتية
128
+ audio_segments = []
129
+ num_segments = len(intervals)
130
+
131
+ result_text = f"✅ تم التقطيع بنجاح!\n\n"
132
+ result_text += f"📊 عدد المقاطع: {num_segments}\n"
133
+ result_text += f"⏱️ طول الملف الأصلي: {len(wav)/16000:.2f} ثانية\n\n"
134
+ result_text += "=" * 50 + "\n"
135
+ result_text += stats_text
136
+ result_text += "=" * 50 + "\n\n"
137
+
138
+ for idx in range(num_segments):
139
+ audio_seg = get_interval(
140
+ x=wav,
141
+ intervals=intervals,
142
+ idx=idx,
143
+ delta=0.050,
144
+ exact_boundries=True
145
+ )
146
+
147
+ if isinstance(audio_seg, torch.Tensor):
148
+ audio_seg = audio_seg.cpu().numpy()
149
+
150
+ duration = len(audio_seg) / 16000
151
+ result_text += f"مقطع {idx + 1}: من {intervals[idx][0]:.2f}s إلى {intervals[idx][1]:.2f}s (المدة: {duration:.2f}s)\n"
152
+
153
+ audio_segments.append((16000, audio_seg))
154
+
155
+ return plot_img, result_text, audio_segments
156
+
157
+ except Exception as e:
158
+ return None, f"❌ حدث خطأ: {str(e)}", []
159
+
160
+ # إنشاء واجهة Gradio
161
+ with gr.Blocks(title="تقطيع التلاوات القرآنية", theme=gr.themes.Soft()) as demo:
162
+
163
+ gr.Markdown("""
164
+ # 🕌 تقطيع التلاوات القرآنية
165
+
166
+ أداة لتقطيع ملفات التلاوات القرآنية تلقائياً باستخدام AI
167
+
168
+ **استخدم Model:** `obadx/recitation-segmenter-v2`
169
+ """)
170
+
171
+ with gr.Row():
172
+ with gr.Column(scale=1):
173
+ audio_input = gr.Audio(
174
+ label="📤 ارفع ملف التلاوة",
175
+ type="filepath"
176
+ )
177
+
178
+ with gr.Accordion("⚙️ إعدادات التقطيع", open=True):
179
+ min_silence = gr.Slider(
180
+ minimum=10,
181
+ maximum=500,
182
+ value=30,
183
+ step=10,
184
+ label="أقل مدة للسكوت (ميلي ثانية)"
185
+ )
186
+
187
+ min_speech = gr.Slider(
188
+ minimum=10,
189
+ maximum=500,
190
+ value=30,
191
+ step=10,
192
+ label="أقل مدة للكلام (ميلي ثانية)"
193
+ )
194
+
195
+ padding = gr.Slider(
196
+ minimum=0,
197
+ maximum=200,
198
+ value=30,
199
+ step=10,
200
+ label="Padding (ميلي ثانية)"
201
+ )
202
+
203
+ process_btn = gr.Button("🚀 ابدأ التقطيع", variant="primary", size="lg")
204
+
205
+ with gr.Column(scale=2):
206
+ plot_output = gr.Image(label="📈 الإشارة الصوتية")
207
+ result_text = gr.Textbox(
208
+ label="📋 النتائج",
209
+ lines=15,
210
+ max_lines=20
211
+ )
212
+
213
+ gr.Markdown("### 🎵 المقاطع الصوتية المستخرجة")
214
+
215
+ audio_outputs = []
216
+ for i in range(20): # عدد أقصى من المقاطع
217
+ audio_outputs.append(gr.Audio(label=f"مقطع {i+1}", visible=False))
218
+
219
+ def process_and_show(audio, min_sil, min_sp, pad):
220
+ plot, text, segments = process_audio(audio, min_sil, min_sp, pad)
221
+
222
+ outputs = [plot, text]
223
+ for i in range(20):
224
+ if i < len(segments):
225
+ outputs.append(gr.Audio(value=segments[i], visible=True))
226
+ else:
227
+ outputs.append(gr.Audio(visible=False))
228
+
229
+ return outputs
230
+
231
+ process_btn.click(
232
+ fn=process_and_show,
233
+ inputs=[audio_input, min_silence, min_speech, padding],
234
+ outputs=[plot_output, result_text] + audio_outputs
235
+ )
236
+
237
+ gr.Markdown("""
238
+ ---
239
+ ### 💡 معلومات
240
+
241
+ - الأداة تستخدم نموذج AI مدرب خصيصاً لتقطيع التلاوات القرآنية
242
+ - يتم اكتشاف فترات الكلام والسكوت تلقائياً
243
+ - يمكنك تعديل الإعدادات للحصول على نتائج أفضل
244
+ """)
245
+
246
+ if __name__ == "__main__":
247
+ demo.launch()
requirements_txt.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ torch
3
+ transformers
4
+ soundfile
5
+ librosa
6
+ matplotlib
7
+ numpy
8
+ Pillow
9
+ recitations_segmenter