Spaces:
Running
Running
Download audio_utils.py from suraj-ml-projects/Video_editing: direct link, hf CLI and curl.
- Browser
- Download file 3.63 kB
-
https://huggingface.co/spaces/suraj-ml-projects/Video_editing/resolve/main/audio_utils.py
- Command line
-
hf download hf://spaces/suraj-ml-projects/Video_editing/audio_utils.py
-
curl -L -o audio_utils.py https://huggingface.co/spaces/suraj-ml-projects/Video_editing/resolve/main/audio_utils.py
3.63 kB
| """ | |
| utils/audio_utils.py | |
| ──────────────────── | |
| Audio extraction, silence detection, and audio mixing helpers. | |
| Libraries: ffmpeg-python, librosa, pydub | |
| All CPU-friendly, no GPU needed. | |
| """ | |
| import os | |
| import tempfile | |
| import subprocess | |
| import numpy as np | |
| import librosa | |
| from pydub import AudioSegment | |
| from pydub.silence import detect_nonsilent | |
| def extract_audio(video_path: str) -> str: | |
| """ | |
| Pull the audio track out of a video file using ffmpeg. | |
| Returns path to a WAV file (16kHz mono — ideal for Whisper). | |
| """ | |
| out_path = os.path.join(tempfile.gettempdir(), "extracted_audio.wav") | |
| # ffmpeg command: extract audio, convert to 16kHz mono WAV | |
| cmd = [ | |
| "ffmpeg", "-y", # -y = overwrite without asking | |
| "-i", video_path, # input video | |
| "-vn", # drop video stream | |
| "-acodec", "pcm_s16le", # uncompressed WAV | |
| "-ar", "16000", # 16 kHz sample rate (Whisper prefers this) | |
| "-ac", "1", # mono channel | |
| out_path, | |
| ] | |
| subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| return out_path | |
| def detect_silence_segments(audio_path: str, min_silence_ms: int = 700, silence_thresh_db: int = -40): | |
| """ | |
| Detect silence / low-energy sections in the audio. | |
| Returns list of (start_sec, end_sec) tuples for silent regions. | |
| These mark where background music or ambient sound lives — | |
| useful so we don't try to dub over music sections. | |
| Parameters: | |
| min_silence_ms – minimum duration to count as silence (ms) | |
| silence_thresh_db – dB level below which is considered silence | |
| """ | |
| audio = AudioSegment.from_wav(audio_path) | |
| # detect_nonsilent returns [(start_ms, end_ms), ...] of SPEECH | |
| speech_intervals = detect_nonsilent( | |
| audio, | |
| min_silence_len=min_silence_ms, | |
| silence_thresh=silence_thresh_db, | |
| ) | |
| # Invert to get silence intervals | |
| total_ms = len(audio) | |
| silence_segs = [] | |
| prev_end = 0 | |
| for start, end in speech_intervals: | |
| if start > prev_end + 200: # gap > 200ms counts as silence | |
| silence_segs.append((prev_end / 1000, start / 1000)) | |
| prev_end = end | |
| if prev_end < total_ms - 200: | |
| silence_segs.append((prev_end / 1000, total_ms / 1000)) | |
| return silence_segs | |
| def mix_audio_with_video(video_path: str, speech_audio_path: str, bgm_volume: float = 0.15) -> str: | |
| """ | |
| Mix Hindi speech with the original video's background audio. | |
| Strategy: | |
| - Extract original audio, lower its volume (bgm_volume) | |
| - Overlay the Hindi speech track on top | |
| - Result: faint original BGM + clear Hindi speech | |
| """ | |
| bgm_path = os.path.join(tempfile.gettempdir(), "bgm_only.wav") | |
| # Extract original audio | |
| subprocess.run([ | |
| "ffmpeg", "-y", "-i", video_path, | |
| "-vn", "-acodec", "pcm_s16le", "-ar", "44100", "-ac", "2", | |
| bgm_path, | |
| ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| # Load both | |
| bgm = AudioSegment.from_wav(bgm_path) | |
| speech = AudioSegment.from_wav(speech_audio_path) | |
| # Lower BGM volume | |
| bgm = bgm + (20 * np.log10(bgm_volume)) # dB adjustment | |
| # Pad speech if shorter than BGM | |
| if len(speech) < len(bgm): | |
| speech = speech + AudioSegment.silent(duration=len(bgm) - len(speech)) | |
| # Overlay speech on BGM | |
| mixed = bgm.overlay(speech) | |
| mixed_path = os.path.join(tempfile.gettempdir(), "mixed_audio.wav") | |
| mixed.export(mixed_path, format="wav") | |
| return mixed_path | |