ahk-d commited on
Commit
2bd85f5
·
verified ·
1 Parent(s): 75b3f19

Update src/audio_preprocessing.py

Browse files
Files changed (1) hide show
  1. src/audio_preprocessing.py +179 -3
src/audio_preprocessing.py CHANGED
@@ -4,6 +4,160 @@ import numpy as np
4
  import webrtcvad
5
  from pydub import AudioSegment
6
  import subprocess
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
 
9
  VAD_SR = 16000
@@ -149,12 +303,34 @@ def assess_pronunciation_quality(dist_matrix, path, threshold=0.4, wav_type="ref
149
  def denoise_audio(input_audio_path):
150
  assert isinstance(input_audio_path, str), "Input path must be a string"
151
  output_audio_path = input_audio_path.replace(".wav", "_denoised.wav")
 
 
 
152
  try:
153
- result = subprocess.run(["denoise", input_audio_path, output_audio_path, "--plot"], check=True, capture_output=True, text=True)
 
 
 
 
 
 
 
 
 
 
 
 
154
  print(result.stdout)
 
 
 
155
  except subprocess.CalledProcessError as e:
156
  print(f"Error: {e}")
157
  print(f"Stdout: {e.stdout}")
158
  print(f"Stderr: {e.stderr}")
159
- # just for testing, revert to output_audio_path once fixed.
160
- return input_audio_path
 
 
 
 
 
4
  import webrtcvad
5
  from pydub import AudioSegment
6
  import subprocess
7
+ import numpy as np
8
+ import soundfile as sf
9
+ import os
10
+
11
+
12
+ VAD_SR = 16000
13
+ VAD_MODE = 3 # Aggressiveness level (0-3, where 3 is the most aggressive)
14
+ VAD_FRAME_DURATION = 10 # Frame duration in milliseconds
15
+
16
+ def get_speech_segments_webrtcvad(audio_array, sample_rate, frame_duration, vad_mode):
17
+ vad = webrtcvad.Vad(vad_mode)
18
+
19
+ # Convert the frame duration to samples
20
+ frame_duration_samples = int(sample_rate * frame_duration / 1000)
21
+
22
+ # Detect speech regions using VAD
23
+ speech_segments = []
24
+ start = -1
25
+ for i in range(0, len(audio_array), frame_duration_samples):
26
+ frame = audio_array[i : i + frame_duration_samples]
27
+
28
+ if len(frame) < 160:
29
+ is_speech = False
30
+ else:
31
+ frame = frame.tobytes()
32
+ is_speech = vad.is_speech(frame, sample_rate)
33
+
34
+ if is_speech and start == -1:
35
+ start = i
36
+ elif not is_speech and start != -1:
37
+ end = i
38
+ speech_segments.append((start, end))
39
+ start = -1
40
+
41
+ return speech_segments
42
+
43
+
44
+ def get_start_end_using_vad(audio, sample_rate):
45
+ audio_array = np.array(audio.get_array_of_samples())
46
+
47
+ speech_segments = get_speech_segments_webrtcvad(audio_array, sample_rate, VAD_FRAME_DURATION, VAD_MODE)
48
+ if len(speech_segments) == 0:
49
+ speech_segments = get_speech_segments_webrtcvad(audio_array, sample_rate, VAD_FRAME_DURATION, VAD_MODE - 1)
50
+
51
+ start_sample = speech_segments[0][0]
52
+ end_sample = speech_segments[-1][1]
53
+
54
+ start_time = float(start_sample / VAD_SR)
55
+ end_time = float(end_sample / VAD_SR)
56
+
57
+ return start_time, end_time
58
+
59
+
60
+ def trim_silences(audio, target_sr):
61
+ audio_copy = audio[:]
62
+
63
+ audio_copy = audio_copy.set_frame_rate(VAD_SR)
64
+
65
+ start_time, end_time = get_start_end_using_vad(audio_copy, VAD_SR)
66
+
67
+ start_sample_orig_sr = int(start_time * target_sr)
68
+ end_sample_orig_sr = int(end_time * target_sr)
69
+
70
+ filtered_audio_array = np.array(audio.get_array_of_samples())
71
+ filtered_audio_array = filtered_audio_array[start_sample_orig_sr:end_sample_orig_sr]
72
+
73
+ filtered_audio = AudioSegment(
74
+ filtered_audio_array.tobytes(),
75
+ frame_rate=target_sr,
76
+ sample_width=audio.sample_width,
77
+ channels=audio.channels,
78
+ )
79
+
80
+ return filtered_audio
81
+
82
+
83
+ def match_target_amplitude(audio, target_dBFS):
84
+ change_in_dBFS = target_dBFS - audio.dBFS
85
+ return audio.apply_gain(change_in_dBFS)
86
+
87
+
88
+ def process_wav(wav_path, target_sr, do_trim_silences=True):
89
+ audio = AudioSegment.from_file(wav_path)
90
+
91
+ # Convert audio to mono
92
+ if audio.channels > 1:
93
+ audio = audio.set_channels(1)
94
+
95
+ # Resample audio
96
+ audio = audio.set_frame_rate(target_sr)
97
+
98
+ # Convert the audio to 16-bit PCM format
99
+ audio = audio.set_sample_width(2)
100
+
101
+ # Remove silences
102
+ if do_trim_silences:
103
+ audio = trim_silences(audio, target_sr)
104
+
105
+ # Loudness normalization to -20dB
106
+ audio = match_target_amplitude(audio, -20.0)
107
+
108
+ return audio
109
+
110
+
111
+ def get_red_green_segments(dist_matrix, path, wav_type='ref', threshold=0.4):
112
+ if wav_type == "ref":
113
+ num_wav_frames = len(dist_matrix)
114
+ else:
115
+ num_wav_frames = len(dist_matrix[0])
116
+ wav_distances = [0] * num_wav_frames
117
+ for (i, j) in zip(*path):
118
+ wav_distances[i] = dist_matrix[i, j]
119
+
120
+ red_segments = [i for i, d in enumerate(wav_distances) if d >= threshold]
121
+ green_segments = [i for i, d in enumerate(wav_distances) if d < threshold]
122
+
123
+ return red_segments, green_segments, wav_distances
124
+
125
+
126
+ def assess_pronunciation_quality(dist_matrix, path, threshold=0.4, wav_type="ref"):
127
+ # _ is green_segments
128
+ red_segments, _, wav_distances = get_red_green_segments(dist_matrix, path, wav_type=wav_type, threshold=threshold)
129
+
130
+ # Analyze normalized distances
131
+ num_red_segments = len(red_segments)
132
+ total_segments = len(wav_distances)
133
+ red_percentage = num_red_segments / total_segments if total_segments > 0 else 0.0
134
+
135
+ # Calculate quality score and repetition need
136
+ quality_score = 1 - red_percentage
137
+ needs_repeat = red_percentage > 0.5
138
+
139
+ # Print debug information
140
+ print(f"Raw distance stats:")
141
+ print(f" Min distance: {min(wav_distances):.4f}")
142
+ print(f" Max distance: {max(wav_distances):.4f}")
143
+ print(f" Mean distance: {np.mean(wav_distances):.4f}")
144
+ print(f"\nNormalized distance stats:")
145
+ print(f" Number of red segments (>= 0.5): {num_red_segments}")
146
+ print(f" Total segments: {total_segments}")
147
+ print(f"\nRed percentage: {red_percentage * 100:.2f}%")
148
+
149
+ return quality_score, needs_repeat
150
+
151
+
152
+ # SPDX-FileContributor: Karl El Hajal
153
+
154
+ import numpy as np
155
+ import webrtcvad
156
+ from pydub import AudioSegment
157
+ import subprocess
158
+ import numpy as np
159
+ import soundfile as sf
160
+ import os
161
 
162
 
163
  VAD_SR = 16000
 
303
  def denoise_audio(input_audio_path):
304
  assert isinstance(input_audio_path, str), "Input path must be a string"
305
  output_audio_path = input_audio_path.replace(".wav", "_denoised.wav")
306
+
307
+
308
+
309
  try:
310
+ # Read the audio file and ensure float32 dtype
311
+ audio_data, sample_rate = sf.read(input_audio_path)
312
+ audio_data = audio_data.astype(np.float32)
313
+
314
+ # Write the audio data back as float32
315
+ sf.write('temp.wav', audio_data, sample_rate, subtype='FLOAT')
316
+
317
+ result = subprocess.run(
318
+ ["denoise", 'temp.wav', output_audio_path, "--plot"],
319
+ check=True,
320
+ capture_output=True,
321
+ text=True
322
+ )
323
  print(result.stdout)
324
+
325
+ os.remove('temp.wav')
326
+
327
  except subprocess.CalledProcessError as e:
328
  print(f"Error: {e}")
329
  print(f"Stdout: {e.stdout}")
330
  print(f"Stderr: {e.stderr}")
331
+ return input_audio_path
332
+ except Exception as e:
333
+ print(f"Unexpected error: {e}")
334
+ return input_audio_path
335
+
336
+ return output_audio_path