-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplot_music_sync.py
More file actions
141 lines (111 loc) · 4.23 KB
/
Copy pathplot_music_sync.py
File metadata and controls
141 lines (111 loc) · 4.23 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import numpy as np
import librosa
import soundfile as sf
import matplotlib.pyplot as plt
def align_audio_with_dtw(x, wp, fs, hop_length=512):
"""
Align an audio signal using a DTW warping path.
Parameters:
-----------
x : np.ndarray
Audio signal to align
wp : np.ndarray
Warping path (frames)
fs : int
Sampling rate
hop_length : int, optional
Hop length used for feature extraction
Returns:
--------
np.ndarray
Aligned audio signal
"""
# Map frames to samples
time_warped = librosa.frames_to_samples(wp[:, 1], hop_length=hop_length)
time_original = librosa.frames_to_samples(wp[:, 0], hop_length=hop_length)
# Interpolate the audio signal based on the warping path
aligned_audio = np.interp(
np.arange(time_original[-1] + 1),
time_warped,
x[:min(len(x), len(time_warped))],
)
return aligned_audio
def create_full_transition(x_1, x_2, wp, fs, crossfade_duration=1.0):
"""
Create a full audio file containing the entire first track that transitions into the second track.
Parameters:
-----------
x_1 : np.ndarray
First audio signal (played in full)
x_2 : np.ndarray
Second audio signal (transitioned into)
wp : np.ndarray
Warping path (frames)
fs : int
Sampling rate
crossfade_duration : float, optional
Duration of the crossfade between tracks in seconds
Returns:
--------
np.ndarray
The complete audio signal with transition
"""
# Convert crossfade duration to samples
crossfade_samples = int(crossfade_duration * fs)
# Align the second track using the warping path
aligned_x2 = align_audio_with_dtw(x_2, wp, fs)
# Ensure the first track is long enough for the crossfade
transition_start = len(x_1) - crossfade_samples
if transition_start < 0:
raise ValueError("First audio signal is too short for the specified crossfade duration.")
# Calculate the total length of the output signal
total_length = max(len(x_1), transition_start + len(aligned_x2))
# Create the output array
output = np.zeros(total_length)
# Copy the first signal
output[:len(x_1)] = x_1
# Create crossfade weights
fade_out = np.linspace(1, 0, crossfade_samples)
fade_in = np.linspace(0, 1, crossfade_samples)
# Adjust lengths to match the transition region
aligned_x2 = aligned_x2[:len(output) - transition_start]
# Debugging shape mismatches
print(f"Output shape: {output[transition_start:transition_start + crossfade_samples].shape}")
print(f"Fade out shape: {fade_out.shape}")
print(f"Aligned x2 fade in shape: {aligned_x2[:crossfade_samples].shape}")
# Apply the crossfade
output[transition_start:transition_start + crossfade_samples] *= fade_out
aligned_x2[:crossfade_samples] *= fade_in
# Add the second signal starting at the transition point
output[transition_start:transition_start + len(aligned_x2)] += aligned_x2
return output
def main():
try:
# Load audio files
print("Loading audio files...")
x_1, fs = librosa.load("bunny.mp3", sr=None)
x_2, fs = librosa.load("music.mp3", sr=None)
# Ensure mono audio
if x_1.ndim > 1:
x_1 = np.mean(x_1, axis=1)
if x_2.ndim > 1:
x_2 = np.mean(x_2, axis=1)
print("Calculating chroma features...")
# Calculate chroma features
hop_length = 1024
x_1_chroma = librosa.feature.chroma_cqt(y=x_1, sr=fs, hop_length=hop_length)
x_2_chroma = librosa.feature.chroma_cqt(y=x_2, sr=fs, hop_length=hop_length)
print("Computing DTW...")
# Compute DTW
D, wp = librosa.sequence.dtw(X=x_1_chroma, Y=x_2_chroma, metric="cosine")
print("Creating transition...")
# Create full audio with transition
full_audio = create_full_transition(x_1, x_2, wp, fs, crossfade_duration=2.0)
print("Saving output file...")
# Save the result
sf.write("full_transition.wav", full_audio, fs)
print("Done! Output saved as 'full_transition.wav'")
except Exception as e:
print(f"An error occurred: {str(e)}")
if __name__ == "__main__":
main()