Mr7Explorer commited on
Commit
77bde25
Β·
verified Β·
1 Parent(s): 42ea287

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -80
app.py CHANGED
@@ -10,7 +10,7 @@ import soundfile as sf
10
  import matplotlib.pyplot as plt
11
  import scipy.signal as sps
12
 
13
- # Local Modules
14
  from io_utils import read_audio_info
15
  from time_domain import compute_time_domain_stats
16
  from spectral import compute_spectral_analysis
@@ -20,10 +20,6 @@ from synthetic_detector import detect_synthetic_voice
20
  from report_generator import create_report
21
 
22
 
23
- # ============================================================
24
- # MAIN FORENSIC ANALYSIS FUNCTION
25
- # ============================================================
26
-
27
  def analyze_audio(audio_file, progress=gr.Progress()):
28
  if audio_file is None:
29
  return None, "⚠️ Please upload an audio file."
@@ -31,62 +27,47 @@ def analyze_audio(audio_file, progress=gr.Progress()):
31
  try:
32
  path = Path(audio_file)
33
 
34
- # ------------------------------------------------------
35
- # FILE INFO + LOAD AUDIO
36
- # ------------------------------------------------------
37
  progress(0.10, desc="Reading file...")
38
  info = read_audio_info(str(path))
39
 
40
  progress(0.25, desc="Loading waveform...")
41
  y, sr = librosa.load(str(path), sr=None, mono=True)
42
 
43
- # ------------------------------------------------------
44
- # TIME-DOMAIN ANALYSIS
45
- # ------------------------------------------------------
46
  progress(0.35, desc="Time-domain analysis...")
47
  time_stats = compute_time_domain_stats(y)
48
 
49
- # ------------------------------------------------------
50
- # SPECTRAL ANALYSIS
51
- # ------------------------------------------------------
52
  progress(0.50, desc="Spectral analysis...")
53
  spectral = compute_spectral_analysis(y, sr)
54
 
55
- # ------------------------------------------------------
56
- # LOUDNESS
57
- # ------------------------------------------------------
58
- progress(0.60, desc="Computing LUFS loudness...")
59
  lufs = compute_loudness(y, sr) if LOUDNESS_AVAILABLE else None
60
 
61
- # ------------------------------------------------------
62
- # ISSUE DETECTION
63
- # ------------------------------------------------------
64
- progress(0.70, desc="Detecting spectral & time issues...")
65
  issues = detect_audio_issues(spectral, time_stats)
66
 
67
- # ------------------------------------------------------
68
- # SYNTHETIC VOICE ESTIMATION (INFO ONLY)
69
- # ------------------------------------------------------
70
- progress(0.78, desc="Synthetic voice probability...")
71
  synthetic = detect_synthetic_voice(y, sr, spectral)
72
 
73
- # ======================================================
74
- # SCORING SYSTEM (FINAL, CLEAN VERSION)
75
- # ======================================================
76
- progress(0.82, desc="Scoring audio quality...")
77
-
78
  critical = sum(1 for _, sev, _ in issues if sev == "CRITICAL")
79
- high = sum(1 for _, sev, _ in issues if sev == "HIGH")
80
- medium = sum(1 for _, sev, _ in issues if sev == "MEDIUM")
81
- low = sum(1 for _, sev, _ in issues if sev == "LOW")
82
 
83
  score_value = 100 - (critical * 30) - (high * 15) - (medium * 5)
84
  score_value = max(0, score_value)
85
 
86
- # SAFE, MATPLOTLIB-COMPATIBLE COLORS
87
  if score_value >= 90:
88
  grade, quality = "A", "EXCELLENT"
89
- color = "#b3ffb3" # soft green
90
  recommendation = "Excellent for TTS dataset"
91
  elif score_value >= 75:
92
  grade, quality = "B", "GOOD"
@@ -108,7 +89,6 @@ def analyze_audio(audio_file, progress=gr.Progress()):
108
  cleanliness_score = max(0, 100 - (medium * 5 + low * 3))
109
  processing_severity = (critical * 3) + (high * 2) + medium
110
 
111
- # Score Dictionary
112
  score_dict = {
113
  "score": score_value,
114
  "grade": grade,
@@ -123,9 +103,7 @@ def analyze_audio(audio_file, progress=gr.Progress()):
123
  "color": color
124
  }
125
 
126
- # ======================================================
127
- # ASSEMBLE FINAL audio_data PAYLOAD
128
- # ======================================================
129
  audio_data = {
130
  "filename": path.name,
131
  "info": info,
@@ -138,21 +116,15 @@ def analyze_audio(audio_file, progress=gr.Progress()):
138
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
139
  }
140
 
141
- # ======================================================
142
- # PNG REPORT GENERATION
143
- # ======================================================
144
- progress(0.92, desc="Rendering PNG forensic report...")
145
-
146
  report_dir = Path("reports")
147
  report_dir.mkdir(exist_ok=True)
148
  output_file = report_dir / f"{path.stem}_report.png"
149
 
150
  create_report(audio_data, str(output_file))
151
 
152
- # ======================================================
153
- # SUMMARY MARKDOWN OUTPUT
154
- # ======================================================
155
-
156
  s = score_dict
157
  e = spectral["energy_distribution"]
158
 
@@ -168,15 +140,16 @@ def analyze_audio(audio_file, progress=gr.Progress()):
168
  ---
169
 
170
  ## 🎚 Loudness (ITU-R BS.1770-3)
171
- - **Integrated LUFS:** {lufs:.2f} LUFS
172
  """
173
 
174
- # Loudness Compliance
175
  if lufs is not None:
 
176
  if -25 <= lufs <= -21:
177
  md += f" - **Status:** PASS βœ… (Compliant βˆ’23 LUFS Β±2)\n"
178
  else:
179
  md += f" - **Status:** FAIL ❌ (Not compliant with βˆ’23 LUFS Β±2)\n"
 
 
180
 
181
  md += f"""
182
  ---
@@ -219,14 +192,14 @@ def analyze_audio(audio_file, progress=gr.Progress()):
219
  | 500–2k Hz | {e['500_2khz']:.2f}% |
220
  | 2k–8k Hz | {e['2k_8khz']:.2f}% |
221
  | 8k–12k Hz | {e['8k_12khz']:.2f}% |
222
- | 12k–16k Hz | {e['12k_16hz']:.2f}% |
223
  | >16k Hz | {e['above_16khz']:.2f}% |
224
 
225
  ---
226
 
227
- ## πŸ€– Synthetic Voice Estimate (For Information Only)
228
- - **Probability:** {synthetic['synthetic_probability']:.3f}
229
- - **Label:** {synthetic['synthetic_label']}
230
 
231
  ---
232
 
@@ -236,7 +209,7 @@ def analyze_audio(audio_file, progress=gr.Progress()):
236
  if issues:
237
  icons = {"CRITICAL":"πŸ”΄","HIGH":"🟠","MEDIUM":"🟑","LOW":"🟒"}
238
  for issue, sev, desc in issues:
239
- md += f"- {icons[sev]} **[{sev}] {issue}** β€” {desc}\n"
240
  else:
241
  md += "- βœ… No issues detected.\n"
242
 
@@ -255,43 +228,31 @@ def analyze_audio(audio_file, progress=gr.Progress()):
255
  return None, f"# ❌ Analysis Failed\n{str(e)}"
256
 
257
 
258
-
259
- # ============================================================
260
- # GRADIO UI
261
- # ============================================================
262
-
263
  with gr.Blocks(title="Audio Forensic Analyzer", theme="soft") as demo:
264
-
265
  gr.Markdown("""
266
  # 🎧 AUDIO FORENSIC ANALYZER
267
- Upload an audio file to generate a **full forensic spectral report**:
268
- - HF/LF rolloff
269
- - Filtering detection (HPF / LPF / Brickwall)
270
- - Noise reduction artifacts
271
- - Clipping & compression indicators
272
- - Spectral notches
273
- - LUFS (ITU-R BS.1770-3)
274
- - Synthetic speech probability
275
- Outputs a **PNG forensic report + Markdown summary**
276
- """)
277
 
278
  with gr.Row():
279
  with gr.Column(scale=1):
280
  audio_in = gr.Audio(label="πŸ“ Upload Audio", type="filepath")
281
  analyze_btn = gr.Button("πŸ” Analyze Audio", variant="primary")
282
-
283
  with gr.Column(scale=2):
284
  png_out = gr.Image(label="πŸ“Š Forensic PNG Report", type="filepath", height=600)
285
 
286
  summary_out = gr.Markdown(label="πŸ“‹ Summary Report")
287
 
288
- analyze_btn.click(
289
- fn=analyze_audio,
290
- inputs=[audio_in],
291
- outputs=[png_out, summary_out]
292
- )
293
-
294
 
295
- # Run Space
296
  if __name__ == "__main__":
297
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
10
  import matplotlib.pyplot as plt
11
  import scipy.signal as sps
12
 
13
+ # Local Modules (must exist in repo root)
14
  from io_utils import read_audio_info
15
  from time_domain import compute_time_domain_stats
16
  from spectral import compute_spectral_analysis
 
20
  from report_generator import create_report
21
 
22
 
 
 
 
 
23
  def analyze_audio(audio_file, progress=gr.Progress()):
24
  if audio_file is None:
25
  return None, "⚠️ Please upload an audio file."
 
27
  try:
28
  path = Path(audio_file)
29
 
30
+ # File info & load
 
 
31
  progress(0.10, desc="Reading file...")
32
  info = read_audio_info(str(path))
33
 
34
  progress(0.25, desc="Loading waveform...")
35
  y, sr = librosa.load(str(path), sr=None, mono=True)
36
 
37
+ # Time-domain
 
 
38
  progress(0.35, desc="Time-domain analysis...")
39
  time_stats = compute_time_domain_stats(y)
40
 
41
+ # Spectral
 
 
42
  progress(0.50, desc="Spectral analysis...")
43
  spectral = compute_spectral_analysis(y, sr)
44
 
45
+ # Loudness
46
+ progress(0.60, desc="Computing LUFS...")
 
 
47
  lufs = compute_loudness(y, sr) if LOUDNESS_AVAILABLE else None
48
 
49
+ # Issue detection
50
+ progress(0.70, desc="Detecting issues...")
 
 
51
  issues = detect_audio_issues(spectral, time_stats)
52
 
53
+ # Synthetic detection (informational)
54
+ progress(0.78, desc="Synthetic voice estimation...")
 
 
55
  synthetic = detect_synthetic_voice(y, sr, spectral)
56
 
57
+ # Scoring
58
+ progress(0.82, desc="Scoring...")
 
 
 
59
  critical = sum(1 for _, sev, _ in issues if sev == "CRITICAL")
60
+ high = sum(1 for _, sev, _ in issues if sev == "HIGH")
61
+ medium = sum(1 for _, sev, _ in issues if sev == "MEDIUM")
62
+ low = sum(1 for _, sev, _ in issues if sev == "LOW")
63
 
64
  score_value = 100 - (critical * 30) - (high * 15) - (medium * 5)
65
  score_value = max(0, score_value)
66
 
67
+ # Matplotlib-safe colors
68
  if score_value >= 90:
69
  grade, quality = "A", "EXCELLENT"
70
+ color = "#b3ffb3"
71
  recommendation = "Excellent for TTS dataset"
72
  elif score_value >= 75:
73
  grade, quality = "B", "GOOD"
 
89
  cleanliness_score = max(0, 100 - (medium * 5 + low * 3))
90
  processing_severity = (critical * 3) + (high * 2) + medium
91
 
 
92
  score_dict = {
93
  "score": score_value,
94
  "grade": grade,
 
103
  "color": color
104
  }
105
 
106
+ # Build audio_data payload
 
 
107
  audio_data = {
108
  "filename": path.name,
109
  "info": info,
 
116
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
117
  }
118
 
119
+ # Create reports dir
120
+ progress(0.92, desc="Rendering PNG report...")
 
 
 
121
  report_dir = Path("reports")
122
  report_dir.mkdir(exist_ok=True)
123
  output_file = report_dir / f"{path.stem}_report.png"
124
 
125
  create_report(audio_data, str(output_file))
126
 
127
+ # Build Markdown summary (with spectral block)
 
 
 
128
  s = score_dict
129
  e = spectral["energy_distribution"]
130
 
 
140
  ---
141
 
142
  ## 🎚 Loudness (ITU-R BS.1770-3)
 
143
  """
144
 
 
145
  if lufs is not None:
146
+ md += f"- **Integrated LUFS:** {lufs:.2f} LUFS \n"
147
  if -25 <= lufs <= -21:
148
  md += f" - **Status:** PASS βœ… (Compliant βˆ’23 LUFS Β±2)\n"
149
  else:
150
  md += f" - **Status:** FAIL ❌ (Not compliant with βˆ’23 LUFS Β±2)\n"
151
+ else:
152
+ md += "- **Integrated LUFS:** Not available (pyloudnorm missing) \n"
153
 
154
  md += f"""
155
  ---
 
192
  | 500–2k Hz | {e['500_2khz']:.2f}% |
193
  | 2k–8k Hz | {e['2k_8khz']:.2f}% |
194
  | 8k–12k Hz | {e['8k_12khz']:.2f}% |
195
+ | 12k–16k Hz | {e['12k_16khz']:.2f}% |
196
  | >16k Hz | {e['above_16khz']:.2f}% |
197
 
198
  ---
199
 
200
+ ## πŸ€– Synthetic Voice Estimate (Informational Only)
201
+ - **Probability:** {synthetic.get('synthetic_probability', 0.0):.3f}
202
+ - **Label:** {synthetic.get('synthetic_label', 'unknown')}
203
 
204
  ---
205
 
 
209
  if issues:
210
  icons = {"CRITICAL":"πŸ”΄","HIGH":"🟠","MEDIUM":"🟑","LOW":"🟒"}
211
  for issue, sev, desc in issues:
212
+ md += f"- {icons.get(sev,'βšͺ')} **[{sev}] {issue}** β€” {desc}\n"
213
  else:
214
  md += "- βœ… No issues detected.\n"
215
 
 
228
  return None, f"# ❌ Analysis Failed\n{str(e)}"
229
 
230
 
231
+ # Gradio UI
 
 
 
 
232
  with gr.Blocks(title="Audio Forensic Analyzer", theme="soft") as demo:
 
233
  gr.Markdown("""
234
  # 🎧 AUDIO FORENSIC ANALYZER
235
+ Upload an audio file to generate a forensic-quality report:
236
+ - HF/LF rolloff detection
237
+ - LPF/HPF / Brickwall detection
238
+ - Noise-reduction artifacts
239
+ - Compression and clipping indicators
240
+ - Spectral notches
241
+ - LUFS (ITU-R BS.1770-3) check
242
+ - Synthetic voice estimation (informational)
243
+ Outputs a PNG report + Markdown summary
244
+ """)
245
 
246
  with gr.Row():
247
  with gr.Column(scale=1):
248
  audio_in = gr.Audio(label="πŸ“ Upload Audio", type="filepath")
249
  analyze_btn = gr.Button("πŸ” Analyze Audio", variant="primary")
 
250
  with gr.Column(scale=2):
251
  png_out = gr.Image(label="πŸ“Š Forensic PNG Report", type="filepath", height=600)
252
 
253
  summary_out = gr.Markdown(label="πŸ“‹ Summary Report")
254
 
255
+ analyze_btn.click(fn=analyze_audio, inputs=[audio_in], outputs=[png_out, summary_out])
 
 
 
 
 
256
 
 
257
  if __name__ == "__main__":
258
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)