针对这一篇和上一篇内容,如果您需要特殊要求,请按情况自行调整参数,谢谢您的观看和支持。

 

import numpy as np

import scipy.io.wavfile as wav

from scipy import signal

import matplotlib.pyplot as plt

import simpleaudio as sa

import os

import tkinter as tk

from tkinter import filedialog, messagebox, ttk

import threading

 

class AudioDenoiser:

    def __init__(self, root):

        self.root = root

        self.root.title("音频去噪工具")

        self.root.geometry("600x500")

        self.root.resizable(False, False)

        

        # 变量

        self.input_file = ""

        self.output_file = ""

        self.audio_data = None

        self.sample_rate = None

        

        self.setup_ui()

        

    def setup_ui(self):

        # 标题

        title = tk.Label(self.root, text="智能音频去噪工具", font=("Arial", 18, "bold"))

        title.pack(pady=20)

        

        # 文件选择框架

        file_frame = tk.Frame(self.root)

        file_frame.pack(pady=10)

        

        tk.Label(file_frame, text="输入文件:", font=("Arial", 12)).grid(row=0, column=0, padx=5)

        self.input_label = tk.Label(file_frame, text="未选择", width=40, bg="white", relief="sunken")

        self.input_label.grid(row=0, column=1, padx=5)

        tk.Button(file_frame, text="浏览", command=self.select_input).grid(row=0, column=2, padx=5)

        

        # 输出文件

        output_frame = tk.Frame(self.root)

        output_frame.pack(pady=10)

        

        tk.Label(output_frame, text="输出文件:", font=("Arial", 12)).grid(row=0, column=0, padx=5)

        self.output_label = tk.Label(output_frame, text="未选择", width=40, bg="white", relief="sunken")

        self.output_label.grid(row=0, column=1, padx=5)

        tk.Button(output_frame, text="浏览", command=self.select_output).grid(row=0, column=2, padx=5)

        

        # 参数设置

        param_frame = tk.LabelFrame(self.root, text="去噪参数", font=("Arial", 12))

        param_frame.pack(pady=15, padx=20, fill="x")

        

        # 去噪方法

        tk.Label(param_frame, text="去噪方法:", font=("Arial", 11)).grid(row=0, column=0, padx=10, pady=5, sticky="w")

        self.method_var = tk.StringVar(value="spectral_subtraction")

        methods = [("谱减法", "spectral_subtraction"), ("维纳滤波", "wiener_filter")]

        for i, (text, value) in enumerate(methods):

            tk.Radiobutton(param_frame, text=text, variable=self.method_var, 

                          value=value, font=("Arial", 10)).grid(row=0, column=i+1, padx=10, pady=5)

        

        # 噪声估计时间

        tk.Label(param_frame, text="噪声估计时长(秒):", font=("Arial", 11)).grid(row=1, column=0, padx=10, pady=5, sticky="w")

        self.noise_duration = tk.Scale(param_frame, from_=0.1, to=2.0, resolution=0.1, 

                                       orient=tk.HORIZONTAL, length=200)

        self.noise_duration.set(0.5)

        self.noise_duration.grid(row=1, column=1, columnspan=2, padx=10, pady=5)

        

        # 去噪强度

        tk.Label(param_frame, text="去噪强度:", font=("Arial", 11)).grid(row=2, column=0, padx=10, pady=5, sticky="w")

        self.strength = tk.Scale(param_frame, from_=0.5, to=3.0, resolution=0.1, 

                                 orient=tk.HORIZONTAL, length=200)

        self.strength.set(1.0)

        self.strength.grid(row=2, column=1, columnspan=2, padx=10, pady=5)

        

        # 按钮

        btn_frame = tk.Frame(self.root)

        btn_frame.pack(pady=20)

        

        self.process_btn = tk.Button(btn_frame, text="开始去噪", font=("Arial", 12), 

                                     bg="#4CAF50", fg="white", width=15,

                                     command=self.process_audio)

        self.process_btn.pack(side=tk.LEFT, padx=10)

        

        self.play_btn = tk.Button(btn_frame, text="播放原音频", font=("Arial", 12), 

                                  bg="#2196F3", fg="white", width=15,

                                  command=self.play_original, state="disabled")

        self.play_btn.pack(side=tk.LEFT, padx=10)

        

        self.play_denoised_btn = tk.Button(btn_frame, text="播放去噪音频", font=("Arial", 12), 

                                           bg="#FF9800", fg="white", width=15,

                                           command=self.play_denoised, state="disabled")

        self.play_denoised_btn.pack(side=tk.LEFT, padx=10)

        

        # 状态栏

        self.status_label = tk.Label(self.root, text="就绪", font=("Arial", 10), relief="sunken", anchor="w")

        self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10)

        

        # 进度条

        self.progress = ttk.Progressbar(self.root, length=400, mode='indeterminate')

        self.progress.pack(pady=10)

        

        self.denoised_data = None

    

    def select_input(self):

        file_path = filedialog.askopenfilename(

            title="选择音频文件",

            filetypes=[("音频文件", "*.wav *.mp3 *.flac"), ("所有文件", "*.*")]

        )

        if file_path:

            self.input_file = file_path

            self.input_label.config(text=os.path.basename(file_path))

            self.load_audio()

    

    def select_output(self):

        file_path = filedialog.asksaveasfilename(

            title="保存去噪音频",

            defaultextension=".wav",

            filetypes=[("WAV文件", "*.wav"), ("所有文件", "*.*")]

        )

        if file_path:

            self.output_file = file_path

            self.output_label.config(text=os.path.basename(file_path))

    

    def load_audio(self):

        try:

            self.sample_rate, self.audio_data = wav.read(self.input_file)

            if len(self.audio_data.shape) > 1:

                self.audio_data = self.audio_data[:, 0] # 转为单声道

            self.status_label.config(text=f"音频已加载:{len(self.audio_data)/self.sample_rate:.1f}秒")

            self.play_btn.config(state="normal")

        except Exception as e:

            messagebox.showerror("错误", f"加载音频失败:{str(e)}")

    

    def spectral_subtraction(self, audio, sample_rate, noise_duration=0.5, strength=1.0):

        """谱减法去噪"""

        # 估计噪声

        noise_samples = int(noise_duration * sample_rate)

        if len(audio) < noise_samples:

            noise_samples = len(audio) // 4

        

        noise = audio[:noise_samples]

        

        # STFT

        n_fft = 2048

        hop_length = 512

        

        f, t, Zxx = signal.stft(audio, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        f_noise, t_noise, Zxx_noise = signal.stft(noise, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        

        # 计算噪声功率谱

        noise_power = np.mean(np.abs(Zxx_noise)**2, axis=1, keepdims=True)

        

        # 谱减法

        magnitude = np.abs(Zxx)

        phase = np.angle(Zxx)

        

        # 减去噪声

        magnitude_clean = magnitude - strength * np.sqrt(noise_power)

        magnitude_clean = np.maximum(magnitude_clean, 0) # 确保非负

        

        # 重建信号

        Zxx_clean = magnitude_clean * np.exp(1j * phase)

        _, audio_clean = signal.istft(Zxx_clean, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        

        return audio_clean

    

    def wiener_filter(self, audio, sample_rate, noise_duration=0.5, strength=1.0):

        """维纳滤波去噪"""

        # 估计噪声

        noise_samples = int(noise_duration * sample_rate)

        if len(audio) < noise_samples:

            noise_samples = len(audio) // 4

        

        noise = audio[:noise_samples]

        

        # STFT

        n_fft = 2048

        hop_length = 512

        

        f, t, Zxx = signal.stft(audio, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        f_noise, t_noise, Zxx_noise = signal.stft(noise, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        

        # 计算功率谱

        signal_power = np.abs(Zxx)**2

        noise_power = np.mean(np.abs(Zxx_noise)**2, axis=1, keepdims=True)

        

        # 维纳滤波

        # H = signal_power / (signal_power + noise_power)

        wiener_gain = signal_power / (signal_power + strength * noise_power)

        wiener_gain = np.clip(wiener_gain, 0, 1)

        

        # 应用增益

        Zxx_clean = Zxx * wiener_gain

        

        # 重建信号

        _, audio_clean = signal.istft(Zxx_clean, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)

        

        return audio_clean

    

    def process_audio(self):

        if self.input_file == "":

            messagebox.showwarning("警告", "请先选择输入音频文件")

            return

        

        if self.output_file == "":

            # 自动生成输出文件名

            base_name = os.path.splitext(os.path.basename(self.input_file))[0]

            output_dir = os.path.dirname(self.input_file)

            self.output_file = os.path.join(output_dir, f"{base_name}_denoised.wav")

            self.output_label.config(text=os.path.basename(self.output_file))

        

        # 在后台线程处理

        self.process_btn.config(state="disabled")

        self.progress.start()

        self.status_label.config(text="正在处理音频...")

        

        thread = threading.Thread(target=self._process_thread)

        thread.daemon = True

        thread.start()

    

    def _process_thread(self):

        try:

            method = self.method_var.get()

            noise_dur = self.noise_duration.get()

            strength = self.strength.get()

            

            # 归一化音频

            audio = self.audio_data.astype(np.float32)

            if np.max(np.abs(audio)) > 0:

                audio = audio / np.max(np.abs(audio))

            

            # 应用选定的去噪方法

            if method == "spectral_subtraction":

                self.denoised_data = self.spectral_subtraction(audio, self.sample_rate, noise_dur, strength)

            else: # wiener_filter

                self.denoised_data = self.wiener_filter(audio, self.sample_rate, noise_dur, strength)

            

            # 确保数据在有效范围内

            self.denoised_data = np.clip(self.denoised_data, -1, 1)

            

            # 保存去噪音频

            denoised_int16 = (self.denoised_data * 32767).astype(np.int16)

            wav.write(self.output_file, self.sample_rate, denoised_int16)

            

            self.root.after(0, self._process_complete)

            

        except Exception as e:

            self.root.after(0, lambda: self._process_error(str(e)))

    

    def _process_complete(self):

        self.progress.stop()

        self.process_btn.config(state="normal")

        self.play_denoised_btn.config(state="normal")

        self.status_label.config(text=f"去噪完成!保存至:{os.path.basename(self.output_file)}")

        messagebox.showinfo("成功", f"音频去噪完成!\n保存位置:{self.output_file}")

    

    def _process_error(self, error_msg):

        self.progress.stop()

        self.process_btn.config(state="normal")

        self.status_label.config(text="处理失败")

        messagebox.showerror("错误", f"处理失败:{error_msg}")

    

    def play_original(self):

        if self.audio_data is not None:

            audio_int16 = self.audio_data.astype(np.int16)

            play_obj = sa.play_buffer(audio_int16, 1, 2, self.sample_rate)

    

    def play_denoised(self):

        if self.denoised_data is not None:

            denoised_int16 = (self.denoised_data * 32767).astype(np.int16)

            play_obj = sa.play_buffer(denoised_int16, 1, 2, self.sample_rate)

 

def main():

    root = tk.Tk()

    app = AudioDenoiser(root)

    root.mainloop()

 

if __name__ == "__main__":

    main()

 

 

 

 

求三连求三连求三连呀。

认可我的人请点赞关注评论收藏。

有可能有需要的请及时下载保存。

如果不喜欢我内容的,评论区一位,请说出你的不满。

 

更多推荐