-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsync_audio.py
More file actions
117 lines (97 loc) · 4.19 KB
/
sync_audio.py
File metadata and controls
117 lines (97 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python3
import numpy as np
from scipy import signal
from scipy.io import wavfile
import subprocess
import sys
def load_audio(filepath):
"""Load audio file and convert to mono if needed"""
if filepath.endswith('.mp3'):
# Convert MP3 to WAV temporarily
temp_wav = 'temp_audio.wav'
subprocess.run(['ffmpeg', '-i', filepath, '-ar', '44100', '-ac', '1', temp_wav, '-y'],
capture_output=True, check=True)
rate, data = wavfile.read(temp_wav)
subprocess.run(['rm', temp_wav])
else:
rate, data = wavfile.read(filepath)
# Convert to mono if stereo
if len(data.shape) > 1:
data = np.mean(data, axis=1)
# Normalize
data = data.astype(np.float32)
data = data / np.max(np.abs(data))
return rate, data
def find_offset(audio1, audio2, sample_rate):
"""Find the best offset between two audio signals using cross-correlation"""
# Use a segment of audio for correlation (first 30 seconds)
segment_length = min(30 * sample_rate, len(audio1), len(audio2))
audio1_segment = audio1[:segment_length]
audio2_segment = audio2[:segment_length]
# Compute cross-correlation
correlation = signal.correlate(audio2_segment, audio1_segment, mode='full')
# Find the peak
peak_index = np.argmax(np.abs(correlation))
# Convert to time offset
offset_samples = peak_index - (len(audio1_segment) - 1)
offset_seconds = offset_samples / sample_rate
# Calculate confidence score
max_correlation = np.max(np.abs(correlation))
mean_correlation = np.mean(np.abs(correlation))
confidence = max_correlation / mean_correlation if mean_correlation > 0 else 0
return offset_seconds, confidence
def main():
print("Loading video audio...")
video_rate, video_audio = load_audio('video_audio.wav')
print("Loading MP3 audio...")
mp3_rate, mp3_audio = load_audio('TOO_MUCH_aura.mp3')
# Ensure same sample rate
if video_rate != mp3_rate:
print(f"Warning: Sample rates differ ({video_rate} vs {mp3_rate})")
# Resample if needed
if video_rate != 44100:
video_audio = signal.resample(video_audio, int(len(video_audio) * 44100 / video_rate))
video_rate = 44100
if mp3_rate != 44100:
mp3_audio = signal.resample(mp3_audio, int(len(mp3_audio) * 44100 / mp3_rate))
mp3_rate = 44100
print("Finding synchronization offset...")
offset, confidence = find_offset(video_audio, mp3_audio, video_rate)
print(f"\nResults:")
print(f"Offset: {offset:.3f} seconds")
print(f"Confidence: {confidence:.2f}")
if offset > 0:
print(f"MP3 audio should start {offset:.3f} seconds later")
else:
print(f"MP3 audio should start {-offset:.3f} seconds earlier")
# Create synchronized output
if confidence > 2.0: # Reasonable confidence threshold
print("\nCreating synchronized video...")
if offset > 0:
# Delay the MP3 audio
cmd = [
'ffmpeg', '-i', 'video source.mp4', '-i', 'TOO_MUCH_aura.mp3',
'-filter_complex', f'[1:a]adelay={int(offset*1000)}|{int(offset*1000)}[delayed]',
'-map', '0:v', '-map', '[delayed]',
'-c:v', 'copy', '-c:a', 'aac', '-shortest',
'synchronized_video.mp4', '-y'
]
else:
# Trim the beginning of MP3
cmd = [
'ffmpeg', '-i', 'video source.mp4',
'-ss', str(-offset), '-i', 'TOO_MUCH_aura.mp3',
'-map', '0:v', '-map', '1:a',
'-c:v', 'copy', '-c:a', 'aac', '-shortest',
'synchronized_video.mp4', '-y'
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("Synchronized video created: synchronized_video.mp4")
else:
print(f"Error creating synchronized video: {result.stderr}")
else:
print("\nWarning: Low confidence score. The audio tracks may not be related.")
print("Manual synchronization may be required.")
if __name__ == "__main__":
main()