124 lines
4.8 KiB
Python
124 lines
4.8 KiB
Python
import numpy as np
|
|
import soundfile as sf
|
|
import librosa
|
|
import os
|
|
from scipy.signal import butter, filtfilt, stft, istft
|
|
|
|
# Step 1: Load audio files
|
|
def load_audio(audio_path):
|
|
audio, sr = librosa.load(audio_path, sr=48000)
|
|
#audio, fs = sf.read(audio_path)
|
|
return audio, sr
|
|
|
|
# Step 2: Detect effective signal bandwidth
|
|
def detect_bandwidth_org(signal, fs, energy_threshold=0.95):
|
|
f, t, Zxx = stft(signal, fs=fs)
|
|
psd = np.abs(Zxx)**2
|
|
total_energy = np.sum(psd)
|
|
cumulative_energy = np.cumsum(np.sum(psd, axis=1)) / total_energy
|
|
f_low = f[np.argmax(cumulative_energy > (1 - energy_threshold))]
|
|
f_high = f[np.argmax(cumulative_energy >= energy_threshold)]
|
|
return f_low, f_high
|
|
|
|
def detect_bandwidth(signal, fs, energy_threshold=0.99):
|
|
f, t, Zxx = stft(signal, fs=fs)
|
|
psd = np.abs(Zxx)**2
|
|
total_energy = np.sum(psd)
|
|
cumulative_energy = np.cumsum(np.sum(psd, axis=1)) / total_energy
|
|
|
|
# Exclude DC component (0 Hz)
|
|
valid_indices = np.where(f > 0)[0]
|
|
f_low = f[valid_indices][np.argmax(cumulative_energy[valid_indices] > (1 - energy_threshold))]
|
|
f_high = f[np.argmax(cumulative_energy >= energy_threshold)]
|
|
return f_low, f_high
|
|
|
|
# Step 3: Apply bandpass and lowpass filters
|
|
def bandpass_filter(signal, fs, f_low, f_high):
|
|
nyquist = 0.5 * fs
|
|
low = f_low / nyquist
|
|
high = f_high / nyquist
|
|
b, a = butter(N=4, Wn=[low, high], btype='band')
|
|
return filtfilt(b, a, signal)
|
|
|
|
def lowpass_filter(signal, fs, cutoff):
|
|
nyquist = 0.5 * fs
|
|
cutoff_normalized = cutoff / nyquist
|
|
b, a = butter(N=4, Wn=cutoff_normalized, btype='low')
|
|
return filtfilt(b, a, signal)
|
|
|
|
def highpass_filter(signal, fs, cutoff):
|
|
nyquist = 0.5 * fs
|
|
cutoff_normalized = cutoff / nyquist
|
|
b, a = butter(N=4, Wn=cutoff_normalized, btype='high')
|
|
return filtfilt(b, a, signal)
|
|
|
|
# Step 4: Replace bandwidth
|
|
def replace_bandwidth(signal1, signal2, fs, f_low, f_high):
|
|
# Extract effective band from signal1
|
|
#effective_band = bandpass_filter(signal1, fs, f_low, f_high)
|
|
effective_band = lowpass_filter(signal1, fs, f_high)
|
|
# Extract lowpass band from signal2
|
|
#signal2_lowpass = lowpass_filter(signal2, fs, f_high)
|
|
signal2_highpass = highpass_filter(signal2, fs, f_high)
|
|
|
|
# Match lengths of the two signals
|
|
min_length = min(len(effective_band), len(signal2_highpass))
|
|
effective_band = effective_band[:min_length]
|
|
signal2_highpass = signal2_highpass[:min_length]
|
|
|
|
# Combine the two signals
|
|
return signal2_highpass + effective_band
|
|
|
|
# Step 5: Smooth transitions
|
|
def smooth_transition(signal1, signal2, fs, transition_band=100):
|
|
fade = np.linspace(0, 1, int(transition_band * fs / 1000))
|
|
crossfade = np.concatenate([fade, np.ones(len(signal1) - len(fade))])
|
|
min_length = min(len(signal1), len(signal2))
|
|
smoothed_signal = (1 - crossfade) * signal2[:min_length] + crossfade * signal1[:min_length]
|
|
return smoothed_signal
|
|
|
|
# Step 6: Save audio
|
|
def save_audio(file_path, audio, fs):
|
|
sf.write(file_path, audio, fs)
|
|
|
|
|
|
def bandwidth_sub(low_bandwidth_audio, high_bandwidth_audio, fs=48000):
|
|
# Detect effective bandwidth of the first signal
|
|
f_low, f_high = detect_bandwidth(low_bandwidth_audio, fs)
|
|
|
|
# Replace the lower frequency of the second audio
|
|
substituted_audio = replace_bandwidth(low_bandwidth_audio, high_bandwidth_audio, fs, f_low, f_high)
|
|
|
|
# Optional: Smooth the transition
|
|
smoothed_audio = smooth_transition(substituted_audio, low_bandwidth_audio, fs)
|
|
return smoothed_audio
|
|
|
|
# Main process
|
|
if __name__ == "__main__":
|
|
low_spectra_dir = 'LJSpeech_22k'
|
|
upper_spectra_dir = 'LJSpeech_22k_hifi-sr_speech_g_03925000'
|
|
output_dir = upper_spectra_dir+'_restored'
|
|
if not os.path.exists(output_dir):
|
|
os.mkdir(output_dir)
|
|
|
|
filelist = [file for file in os.listdir(low_spectra_dir) if file.endswith('.wav')]
|
|
for audio_name in filelist:
|
|
audio1, fs1 = load_audio(low_spectra_dir + "/" + audio_name) # Source for effective bandwidth
|
|
audio2, fs2 = load_audio(upper_spectra_dir + "/" + audio_name.replace('.wav', '_generated.wav')) # Target audio to replace lower frequencies
|
|
|
|
if fs1 != 48000 or fs2 != 48000:
|
|
raise ValueError("Both audio files must have a sampling rate of 48 kHz.")
|
|
|
|
# Detect effective bandwidth of the first signal
|
|
f_low, f_high = detect_bandwidth(audio1, fs1)
|
|
print(f"Effective bandwidth: {f_low} Hz to {f_high} Hz")
|
|
|
|
# Replace the lower frequency of the second audio
|
|
replaced_audio = replace_bandwidth(audio1, audio2, fs2, f_low, f_high)
|
|
|
|
# Optional: Smooth the transition
|
|
smoothed_audio = smooth_transition(replaced_audio, audio1, fs1)
|
|
|
|
# Save the result
|
|
save_audio(output_dir+"/"+audio_name, smoothed_audio, fs2)
|