Python实现WAV音频拉伸/压缩失效,输出音频混乱求助
音频时间拉伸/压缩代码功能异常的修复方案
尝试将WAV文件拉伸至2倍(理论支持任意k值),但输出的output.wav未被拉伸且音频混乱;k<1的压缩场景同样失效。代码已能运行无报错,但功能不符合预期,作为音频处理新手不知如何解决。
原代码
import numpy as np from scipy.io import wavfile sampling_rate, signal = wavfile.read("test.wav") NFFT = 1024 overlap_factor = 4 stretch_factor = 2 hop_size = int(NFFT / overlap_factor) num_hops = int(np.ceil(float(len(signal)) / hop_size)) pad_size = num_hops * hop_size - len(signal) z = np.zeros((pad_size,)) pad_signal = np.concatenate((signal, z)) frames = np.array_split(pad_signal, num_hops) output = np.zeros((0,), dtype=np.float32) previous_phase = np.zeros((NFFT,), dtype=np.float32) summed_phase = np.zeros((NFFT,), dtype=np.float32) for i, frame in enumerate(frames): spectrum = np.fft.fft(frame, n=NFFT) magnitude = np.abs(spectrum) phase = np.angle(spectrum) frequencies = np.fft.fftfreq(NFFT, d=1.0/sampling_rate) frequencies = np.repeat(frequencies, stretch_factor) phase_diff = (frequencies[1] - frequencies[0]) * hop_size / sampling_rate expected_phase = previous_phase + phase_diff * 2 * np.pi delta_phase = phase - expected_phase previous_phase = phase.copy() summed_phase += delta_phase phase_advances = hop_size * summed_phase / (2 * np.pi) phase_advances = np.round(phase_advances).astype(int) new_spectrum = np.zeros((NFFT,), dtype=np.complex64) for j in range(NFFT): j2 = j + phase_advances[j] if j2 >= 0 and j2 < NFFT: new_spectrum[j2] += spectrum[j] new_frame = np.fft.ifft(new_spectrum).real output = np.concatenate((output, new_frame[:hop_size])) wavfile.write("output.wav", sampling_rate, output.astype(np.int16))
原代码核心问题
- 时间轴调整逻辑缺失:未根据
stretch_factor修改输出帧的数量或步长,导致总输出长度与输入一致,完全无法实现拉伸/压缩效果。 - 相位计算逻辑错误:重复频率轴无实际意义,相位差计算未结合拉伸因子,导致相位累积混乱,还原音频时出现严重失真。
- 频谱处理方式错误:通过移位频谱调整相位的做法不符合相位声码器标准流程,破坏了频率成分的对应关系,引发音频混乱。
修复后的代码
import numpy as np from scipy.io import wavfile # 读取音频并处理立体声 sampling_rate, signal = wavfile.read("test.wav") if len(signal.shape) > 1: signal = signal.mean(axis=1) signal = signal.astype(np.float32) # 参数配置 NFFT = 1024 overlap_factor = 4 stretch_factor = 2 # >1为拉伸,<1为压缩 # 输入帧参数 hop_size_input = int(NFFT / overlap_factor) # 输出帧参数:根据拉伸因子调整步长 hop_size_output = int(hop_size_input / stretch_factor) # 补零使输入信号长度适配输入帧步长 pad_length = (len(signal) + hop_size_input - 1) // hop_size_input * hop_size_input - len(signal) pad_signal = np.pad(signal, (0, pad_length), mode='constant') # 生成带重叠的输入帧 frames = np.array([pad_signal[i:i+NFFT] for i in range(0, len(pad_signal)-NFFT+1, hop_size_input)]) num_frames = len(frames) # 初始化相位相关变量(仅保留正频率,减少计算量) prev_phase = np.zeros(NFFT // 2 + 1, dtype=np.float32) cum_phase = np.zeros(NFFT // 2 + 1, dtype=np.float32) freqs = np.fft.fftfreq(NFFT, d=1/sampling_rate)[:NFFT//2+1] # 初始化输出信号与汉明窗(减少频谱泄漏和拼接失真) window = np.hamming(NFFT) output_length = int(len(pad_signal) * stretch_factor) output = np.zeros(output_length, dtype=np.float32) output_hop_count = 0 for i in range(num_frames): frame = frames[i] * window spectrum = np.fft.rfft(frame) # 实信号FFT,仅返回正频率 mag = np.abs(spectrum) phase = np.angle(spectrum) # 计算输入帧的预期相位变化 phase_diff = freqs * hop_size_input / sampling_rate * 2 * np.pi expected_phase = prev_phase + phase_diff # 计算相位差并取模2π,避免累积溢出 delta_phase = phase - expected_phase delta_phase = np.mod(delta_phase + np.pi, 2*np.pi) - np.pi # 更新累积相位与上一帧相位 cum_phase += delta_phase + phase_diff prev_phase = phase.copy() # 根据拉伸因子生成对应数量的输出帧 output_phase_inc = freqs * hop_size_output / sampling_rate * 2 * np.pi loop_count = int(stretch_factor) if stretch_factor >=1 else 1 for _ in range(loop_count): # 构建输出频谱:幅度不变,相位基于累积值 output_spectrum = mag * np.exp(1j * cum_phase) # 逆FFT生成输出帧并加窗 output_frame = np.fft.irfft(output_spectrum) * window # 重叠相加到输出信号 start = output_hop_count * hop_size_output end = start + NFFT if end <= len(output): output[start:end] += output_frame # 更新累积相位与输出步长计数 cum_phase += output_phase_inc output_hop_count +=1 # 压缩场景下提前终止,避免输出过长 if stretch_factor <1 and output_hop_count * hop_size_output >= len(output): break # 归一化输出到int16范围,避免削波失真 output = output / np.max(np.abs(output)) * 32767 wavfile.write("output.wav", sampling_rate, output.astype(np.int16))
关键修改说明
- 立体声处理:自动将双声道音频转为单声道,避免维度不匹配问题。
- 输出步长适配:根据
stretch_factor动态调整输出帧步长,拉伸时生成更多帧,压缩时减少帧数量,确保输出长度符合预期。 - 加窗与重叠相加:使用汉明窗减少频谱泄漏,重叠相加法保证帧拼接时音频平滑过渡。
- 正确相位计算:基于正频率计算相位差与累积相位,结合拉伸因子调整输出相位增量,保证相位连续性,消除音频混乱。
- 归一化处理:将输出信号映射到int16范围,避免音频削波失真。
内容的提问来源于stack exchange,提问作者EgorCry
相关产品推荐
相关产品推荐

