Ensures that segments without voice in the waveform remain no longer than a threshold determined by the VAD parameters in params.py. :param wav: the raw waveform as a numpy array of floats :param vad_max_silence_length: Maximum number of consecutive silent frames a segment can have.
(path, sr=None, return_raw_wav=False, norm=True, vad_max_silence_length=12)
| 25 | |
| 26 | |
| 27 | def trim_long_silences(path, sr=None, return_raw_wav=False, norm=True, vad_max_silence_length=12): |
| 28 | """ |
| 29 | Ensures that segments without voice in the waveform remain no longer than a |
| 30 | threshold determined by the VAD parameters in params.py. |
| 31 | :param wav: the raw waveform as a numpy array of floats |
| 32 | :param vad_max_silence_length: Maximum number of consecutive silent frames a segment can have. |
| 33 | :return: the same waveform with silences trimmed away (length <= original wav length) |
| 34 | """ |
| 35 | |
| 36 | ## Voice Activation Detection |
| 37 | # Window size of the VAD. Must be either 10, 20 or 30 milliseconds. |
| 38 | # This sets the granularity of the VAD. Should not need to be changed. |
| 39 | sampling_rate = 16000 |
| 40 | wav_raw, sr = librosa.core.load(path, sr=sr) |
| 41 | |
| 42 | if norm: |
| 43 | meter = pyln.Meter(sr) # create BS.1770 meter |
| 44 | loudness = meter.integrated_loudness(wav_raw) |
| 45 | wav_raw = pyln.normalize.loudness(wav_raw, loudness, -20.0) |
| 46 | if np.abs(wav_raw).max() > 1.0: |
| 47 | wav_raw = wav_raw / np.abs(wav_raw).max() |
| 48 | |
| 49 | wav = librosa.resample(wav_raw, sr, sampling_rate, res_type='kaiser_best') |
| 50 | |
| 51 | vad_window_length = 30 # In milliseconds |
| 52 | # Number of frames to average together when performing the moving average smoothing. |
| 53 | # The larger this value, the larger the VAD variations must be to not get smoothed out. |
| 54 | vad_moving_average_width = 8 |
| 55 | |
| 56 | # Compute the voice detection window size |
| 57 | samples_per_window = (vad_window_length * sampling_rate) // 1000 |
| 58 | |
| 59 | # Trim the end of the audio to have a multiple of the window size |
| 60 | wav = wav[:len(wav) - (len(wav) % samples_per_window)] |
| 61 | |
| 62 | # Convert the float waveform to 16-bit mono PCM |
| 63 | pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16)) |
| 64 | |
| 65 | # Perform voice activation detection |
| 66 | voice_flags = [] |
| 67 | vad = webrtcvad.Vad(mode=3) |
| 68 | for window_start in range(0, len(wav), samples_per_window): |
| 69 | window_end = window_start + samples_per_window |
| 70 | voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2], |
| 71 | sample_rate=sampling_rate)) |
| 72 | voice_flags = np.array(voice_flags) |
| 73 | |
| 74 | # Smooth the voice detection with a moving average |
| 75 | def moving_average(array, width): |
| 76 | array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2))) |
| 77 | ret = np.cumsum(array_padded, dtype=float) |
| 78 | ret[width:] = ret[width:] - ret[:-width] |
| 79 | return ret[width - 1:] / width |
| 80 | |
| 81 | audio_mask = moving_average(voice_flags, vad_moving_average_width) |
| 82 | audio_mask = np.round(audio_mask).astype(np.bool) |
| 83 | |
| 84 | # Dilate the voiced regions |
no test coverage detected