基于频谱处理的音频分离方法

基于频谱处理的音频分离方法

在音频处理领域,音频分离是一个重要的任务,尤其是在语音识别、音乐制作和通信等应用中。音频分离的目标是从混合信号中提取出单独的音频源。通过频谱处理进行音频分离是一种有效的方法,本文将介绍其基本原理、公式以及如何通过降噪作为一个具体的例子来实现音频分离。

1. 频谱处理音频分离的原理

频谱处理音频分离的基本思想是将音频信号从时间域转换到频率域,通过分析频谱中的成分,去除或抑制不需要的成分,从而恢复目标音频信号。常用的频谱分析方法是短时傅里叶变换(STFT)。

1.1 短时傅里叶变换(STFT)

短时傅里叶变换将时间域信号分成多个短时间段,并对每个段进行傅里叶变换。对于离散时间信号 (xn),STFT 的离散形式可以表示为:

X m , k = ∑ n = − ∞ ∞ x n w n − m e − j 2 π N k n Xm, k = \sum_{n=-\infty}^{\infty} xn wn - m e^{-j \frac{2\pi}{N}kn} Xm,k=n=−∞∑∞xnwn−me−jN2πkn

其中:

  • X m , k Xm, k Xm,k 是在时间帧 (m) 和频率 (k) 的复数频谱。
  • x n xn xn 是输入信号。
  • w n wn wn 是窗函数,用于限制信号的时间范围。
  • N N N是窗的长度。

1.2 逆短时傅里叶变换(ISTFT)

逆短时傅里叶变换用于将频谱信息转换回时间域信号。对于离散时间信号,ISTFT 的离散形式可以表示为:

x n = ∑ m = − ∞ ∞ X m , k w n − m e j 2 π N k n xn = \sum_{m=-\infty}^{\infty} Xm, k wn - m e^{j \frac{2\pi}{N}kn} xn=∑m=−∞∞Xm,kwn−mejN2πkn

其中:

  • x n xn xn 是恢复后的离散时间信号。
  • X m , k Xm, k Xm,k是去噪后的频谱。

1.3 音频分离算法

在频谱域中,音频分离的基本步骤如下:

  1. 计算 STFT:对混合信号(包含多个音源)进行 STFT,得到频谱表示。
  2. 源信号估计:通过分析混合信号的频谱,估计各个源信号的频谱成分。
  3. 频谱相减或掩码:从混合信号的频谱中减去或应用掩码来提取目标音源的频谱。
  4. 逆 STFT:对提取后的频谱进行逆 STFT,恢复到时间域信号。

2. 降噪作为音频分离的例子

降噪是音频分离中的一个具体应用场景。在降噪过程中,我们的目标是从包含噪声的混合信号中提取出干净的音频信号。降噪的基本步骤如下:

2.1 完全降噪

在完全降噪的情况下,我们假设噪声的频谱可以被准确估计并完全从混合信号中去除。此时,频谱相减的公式为:

Y ( f ) = X ( f ) − N ( f ) Y(f) = X(f) - N(f) Y(f)=X(f)−N(f)

在这种情况下,重建的音频信号将尽可能接近原始的干净信号。

2.2 不完全降噪

在不完全降噪的情况下,噪声的频谱可能无法完全准确地估计,或者在去噪过程中可能会对语音信号造成一定的抑制。此时,我们可以引入一个随机因子来模拟这种情况:

Y ( f ) = X ( f ) − N ( f ) ⋅ factor Y(f) = X(f) - N(f) \cdot \text{factor} Y(f)=X(f)−N(f)⋅factor

其中,factor 是一个在 0.8 到 1.2 之间的随机值。这个因子可以模拟噪声没有完全消除的情况,或者在去噪过程中对语音信号的抑制。

3. 代码实现

python 复制代码
import numpy as np
import librosa
import matplotlib.pyplot as plt
import soundfile as sf
import librosa.display

# 1. 读取两个文件并进行时域混合
def load_and_mix(audio_file, noise_file, duration):
    audio, sr_audio = librosa.load(audio_file, sr=None)
    noise, sr_noise = librosa.load(noise_file, sr=None)

    if sr_audio != sr_noise:
        raise ValueError("Sampling rates of the audio and noise files must be the same.")

    min_length = min(len(audio), len(noise), duration * sr_audio)
    audio = audio[:min_length]
    noise = noise[:min_length]

    mixed_signal = audio + noise
    return audio, noise, mixed_signal, sr_audio

# 2. 计算 STFT
def compute_stft(signal):
    return librosa.stft(signal)

# 3. 计算噪声的 STFT
def compute_noise_stft(noise):
    return librosa.stft(noise)

# 4. 频谱相减
def subtract_stft(mixed_stft, noise_stft, factor):
    return mixed_stft - (noise_stft * factor)

# 5. ISTFT 重新生成语音
def reconstruct_audio(subtracted_stft):
    return librosa.istft(subtracted_stft)

# 6. 可视化过程
def visualize_process(original, noise, mixed, reconstructed_best, reconstructed_partial, sr):
    plt.figure(figsize=(12, 10))

    plt.subplot(5, 1, 1)
    plt.title('Original Audio Signal')
    librosa.display.waveshow(original, sr=sr, alpha=1)
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')

    plt.subplot(5, 1, 2)
    plt.title('Noise Signal')
    librosa.display.waveshow(noise, sr=sr, alpha=1)
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')

    plt.subplot(5, 1, 3)
    plt.title('Mixed Audio Signal')
    librosa.display.waveshow(mixed, sr=sr, alpha=1)
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')

    plt.subplot(5, 1, 4)
    plt.title('Reconstructed Audio Signal (Best Case)')
    librosa.display.waveshow(reconstructed_best, sr=sr, alpha=1)
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')

    plt.subplot(5, 1, 5)
    plt.title('Reconstructed Audio Signal (Partial Case)')
    librosa.display.waveshow(reconstructed_partial, sr=sr, alpha=1)
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')

    plt.tight_layout()
    plt.show()

    # 绘制语谱图
    plt.figure(figsize=(12, 10))

    original_stft = compute_stft(original)
    noise_stft = compute_noise_stft(noise)
    mixed_stft = compute_stft(mixed)
    reconstructed_best_stft = compute_stft(reconstructed_best)
    reconstructed_partial_stft = compute_stft(reconstructed_partial)

    plt.subplot(5, 1, 1)
    plt.title('Original Audio Spectrogram')
    librosa.display.specshow(librosa.amplitude_to_db(np.abs(original_stft), ref=np.max), sr=sr, y_axis='log', x_axis='time')
    plt.colorbar(format='%+2.0f dB')

    plt.subplot(5, 1, 2)
    plt.title('Noise Spectrogram')
    librosa.display.specshow(librosa.amplitude_to_db(np.abs(noise_stft), ref=np.max), sr=sr, y_axis='log', x_axis='time')
    plt.colorbar(format='%+2.0f dB')

    plt.subplot(5, 1, 3)
    plt.title('Mixed Audio Spectrogram')
    librosa.display.specshow(librosa.amplitude_to_db(np.abs(mixed_stft), ref=np.max), sr=sr, y_axis='log', x_axis='time')
    plt.colorbar(format='%+2.0f dB')

    plt.subplot(5, 1, 4)
    plt.title('Reconstructed Audio Spectrogram (Best Case)')
    librosa.display.specshow(librosa.amplitude_to_db(np.abs(reconstructed_best_stft), ref=np.max), sr=sr, y_axis='log', x_axis='time')
    plt.colorbar(format='%+2.0f dB')

    plt.subplot(5, 1, 5)
    plt.title('Reconstructed Audio Spectrogram (Partial Case)')
    librosa.display.specshow(librosa.amplitude_to_db(np.abs(reconstructed_partial_stft), ref=np.max), sr=sr, y_axis='log', x_axis='time')
    plt.colorbar(format='%+2.0f dB')

    plt.tight_layout()
    plt.show()

# 主程序
def main():
    audio_file = '1.wav'  # 语音文件
    noise_file = '3.wav'  # 噪声文件
    duration = 20  # 取样长度(秒)

    original_audio, noise, mixed_signal, sr = load_and_mix(audio_file, noise_file, duration)

    mixed_stft = compute_stft(mixed_signal)
    noise_stft = compute_noise_stft(noise)

    # 完全降噪
    subtracted_stft_best = subtract_stft(mixed_stft, noise_stft, factor=np.ones_like(noise_stft))
    reconstructed_best_audio = reconstruct_audio(subtracted_stft_best)

    # 不完全降噪
    random_factor = np.random.uniform(0.8, 1.2, noise_stft.shape)
    subtracted_stft_partial = subtract_stft(mixed_stft, noise_stft, factor=random_factor)
    reconstructed_partial_audio = reconstruct_audio(subtracted_stft_partial)

    sf.write('mixed_signal.wav', mixed_signal, sr)
    sf.write('reconstructed_audio_best.wav', reconstructed_best_audio, sr)
    sf.write('reconstructed_audio_partial.wav', reconstructed_partial_audio, sr)

    visualize_process(original_audio, noise, mixed_signal, reconstructed_best_audio, reconstructed_partial_audio, sr)

if __name__ == "__main__":
    main()
相关推荐
cxr8285 小时前
第14章 创意音乐与视频的多模态智能体创作
人工智能·音视频·智能体·hermes
梦帮科技8 小时前
基于EVM架构的Web3音频确权与RNS Token智能合约设计深度解析
人工智能·python·架构·web3·区块链·音视频·智能合约
最爱老式锅包肉10 小时前
《HarmonyOS技术精讲-Core Speech Kit(基础语音服务)》第1篇:音频管理基础与语音服务入门
华为·音视频·harmonyos
“码”力全开21 小时前
AI视频分析飞书告警常见问题和排查清单
人工智能·音视频·飞书
西安老张(AIGC&ComfyUI)21 小时前
第021章:ComfyUI文生音频Qwen3-TTS模型数字人音色设计(二)
aigc·音视频·comfyui
关于作业的二三事1 天前
图像和视频处理的核心概念(在图像上画矩形)
音视频
ai产品老杨1 天前
【边云协同视频分析项目实战记录】多站点AI视频分析平台部署手册
人工智能·音视频
丹宇码农1 天前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
Mapleay1 天前
音视频上层架构
音视频
H Journey1 天前
音视频原理
音视频·h264