python语言多功能录音机 - 系统内录+麦克风软件代码QZQ
·
import warnings
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import soundcard as sc
import soundfile as sf
import numpy as np
import threading
import time
# 屏蔽无关警告
warnings.filterwarnings("ignore", category=sc.SoundcardRuntimeWarning)
class AudioRecorderApp:
def __init__(self, root):
self.root = root
self.root.title("多功能录音机 - 系统内录+麦克风")
self.root.geometry("500x400")
self.root.resizable(False, False)
# 核心变量
self.is_recording = False # 是否正在录音
self.is_paused = False # 是否暂停
self.recorded_data = [] # 录制的数据
self.record_thread = None # 录音线程
self.samplerate = 44100 # 采样率
self.selected_device = None # 选中的录音设备
self.record_type = tk.StringVar(value="system") # 录音类型:system(内录)/mic(麦克风)
# 初始化UI
self._init_ui()
# 加载设备列表
self._load_devices()
def _init_ui(self):
# 1. 录音类型选择
type_frame = ttk.LabelFrame(self.root, text="录音类型")
type_frame.pack(padx=20, pady=10, fill="x")
ttk.Radiobutton(type_frame, text="系统内录", variable=self.record_type,
value="system", command=self._on_record_type_change).pack(side="left", padx=20, pady=5)
ttk.Radiobutton(type_frame, text="麦克风录音", variable=self.record_type,
value="mic", command=self._on_record_type_change).pack(side="left", padx=20, pady=5)
# 2. 设备选择
device_frame = ttk.LabelFrame(self.root, text="录音设备")
device_frame.pack(padx=20, pady=10, fill="x")
ttk.Label(device_frame, text="选择设备:").pack(side="left", padx=10, pady=5)
self.device_combobox = ttk.Combobox(device_frame, state="readonly", width=30)
self.device_combobox.pack(side="left", padx=10, pady=5)
self.device_combobox.bind("<<ComboboxSelected>>", self._on_device_selected)
# 3. 录制时长设置
time_frame = ttk.LabelFrame(self.root, text="录制设置")
time_frame.pack(padx=20, pady=10, fill="x")
ttk.Label(time_frame, text="录制时长(秒):").pack(side="left", padx=10, pady=5)
self.time_entry = ttk.Entry(time_frame, width=10)
self.time_entry.insert(0, "5") # 默认5秒
self.time_entry.pack(side="left", padx=5, pady=5)
ttk.Label(time_frame, text="(0为无限时长,手动停止)").pack(side="left", padx=5, pady=5)
# 4. 状态显示
status_frame = ttk.LabelFrame(self.root, text="录制状态")
status_frame.pack(padx=20, pady=10, fill="x")
self.status_label = ttk.Label(status_frame, text="未开始录制", foreground="gray")
self.status_label.pack(padx=10, pady=10)
# 5. 控制按钮
btn_frame = ttk.Frame(self.root)
btn_frame.pack(padx=20, pady=20, fill="x")
self.start_btn = ttk.Button(btn_frame, text="开始录制", command=self.start_recording)
self.start_btn.pack(side="left", padx=10, pady=5, fill="x", expand=True)
self.pause_btn = ttk.Button(btn_frame, text="暂停", command=self.pause_recording, state="disabled")
self.pause_btn.pack(side="left", padx=10, pady=5, fill="x", expand=True)
self.stop_btn = ttk.Button(btn_frame, text="停止录制", command=self.stop_recording, state="disabled")
self.stop_btn.pack(side="left", padx=10, pady=5, fill="x", expand=True)
self.save_btn = ttk.Button(btn_frame, text="保存录音", command=self.save_recording, state="disabled")
self.save_btn.pack(side="left", padx=10, pady=5, fill="x", expand=True)
def _load_devices(self):
"""加载录音设备列表(根据录音类型)"""
if self.record_type.get() == "system":
# 系统内录:加载扬声器设备
devices = sc.all_speakers()
else:
# 麦克风录音:加载麦克风设备
devices = sc.all_microphones()
# 提取设备名
device_names = [dev.name for dev in devices]
self.device_combobox["values"] = device_names
if device_names:
self.device_combobox.current(0)
self.selected_device = devices[0]
def _on_record_type_change(self):
"""切换录音类型时重新加载设备列表"""
self._load_devices()
self.status_label.config(text="切换录音类型,请重新选择设备", foreground="blue")
def _on_device_selected(self, event):
"""选择设备后更新选中的设备"""
selected_idx = self.device_combobox.current()
if self.record_type.get() == "system":
self.selected_device = sc.all_speakers()[selected_idx]
else:
self.selected_device = sc.all_microphones()[selected_idx]
self.status_label.config(text=f"已选中设备:{self.selected_device.name}", foreground="green")
def _record_task(self):
"""后台录音线程任务"""
try:
# 获取录制时长
try:
record_seconds = int(self.time_entry.get())
except ValueError:
messagebox.showerror("错误", "录制时长请输入数字!")
self.is_recording = False
return
# 创建录音器
if self.record_type.get() == "system":
# 系统内录:开启loopback
mic = sc.get_microphone(id=str(self.selected_device.name), include_loopback=True)
else:
# 麦克风录音:普通麦克风
mic = sc.get_microphone(id=str(self.selected_device.name))
with mic.recorder(samplerate=self.samplerate, blocksize=1024) as recorder:
self.recorded_data = []
total_frames = self.samplerate * record_seconds if record_seconds > 0 else float("inf")
recorded_frames = 0
self.status_label.config(text="正在录制...", foreground="red")
while self.is_recording and recorded_frames < total_frames:
if not self.is_paused:
# 每次录制1024帧
chunk = recorder.record(numframes=1024)
self.recorded_data.append(chunk)
recorded_frames += len(chunk)
# 更新录制时长显示
elapsed = recorded_frames / self.samplerate
self.status_label.config(text=f"正在录制... 已录制:{elapsed:.1f}秒", foreground="red")
else:
time.sleep(0.1) # 暂停时休眠
# 录制结束
self.is_recording = False
self.root.after(0, lambda: self.status_label.config(
text=f"录制完成!总时长:{recorded_frames / self.samplerate:.1f}秒", foreground="green"))
self.root.after(0, lambda: self._update_btn_state(after_stop=True))
except Exception as e:
self.root.after(0, lambda: messagebox.showerror("录制错误", f"录音失败:{str(e)}"))
self.root.after(0, lambda: self.status_label.config(text="录制失败", foreground="red"))
self.is_recording = False
self.root.after(0, lambda: self._update_btn_state(after_stop=True))
def start_recording(self):
"""开始录制"""
if not self.selected_device:
messagebox.showwarning("警告", "请先选择录音设备!")
return
if self.is_recording and not self.is_paused:
messagebox.showinfo("提示", "已经在录制中了!")
return
if self.is_paused:
# 恢复暂停
self.is_paused = False
self.status_label.config(text="恢复录制...", foreground="red")
self.pause_btn.config(text="暂停")
return
# 开始新的录制
self.is_recording = True
self.is_paused = False
self._update_btn_state(after_start=True)
# 启动后台录音线程
self.record_thread = threading.Thread(target=self._record_task, daemon=True)
self.record_thread.start()
def pause_recording(self):
"""暂停/恢复录制"""
if not self.is_recording:
return
self.is_paused = not self.is_paused
if self.is_paused:
self.status_label.config(text="录制已暂停", foreground="orange")
self.pause_btn.config(text="恢复")
else:
self.status_label.config(text="恢复录制...", foreground="red")
self.pause_btn.config(text="暂停")
def stop_recording(self):
"""停止录制"""
if self.is_recording:
self.is_recording = False
self.status_label.config(text="正在停止录制...", foreground="orange")
# 等待线程结束
if self.record_thread:
self.record_thread.join(timeout=2)
self._update_btn_state(after_stop=True)
self.status_label.config(text="录制已停止", foreground="green")
def save_recording(self):
"""保存录制的音频"""
if not self.recorded_data:
messagebox.showwarning("警告", "没有可保存的录音数据!")
return
# 选择保存路径
file_path = filedialog.asksaveasfilename(
defaultextension=".wav",
filetypes=[("WAV音频文件", "*.wav"), ("所有文件", "*.*")],
title="保存录音"
)
if not file_path:
return
try:
# 合并录制的数据
data = np.concatenate(self.recorded_data, axis=0)
# 立体声转单声道(兼容更多播放器)
data = data.mean(axis=1)
# 保存为WAV文件
sf.write(file_path, data, self.samplerate)
messagebox.showinfo("成功", f"录音已保存到:\n{file_path}")
except Exception as e:
messagebox.showerror("保存失败", f"保存出错:{str(e)}")
def _update_btn_state(self, after_start=False, after_stop=False):
"""更新按钮状态"""
if after_start:
self.start_btn.config(state="normal") # 开始按钮仍可用(用于恢复暂停)
self.pause_btn.config(state="normal")
self.stop_btn.config(state="normal")
self.save_btn.config(state="disabled")
elif after_stop:
self.start_btn.config(state="normal")
self.pause_btn.config(state="disabled")
self.stop_btn.config(state="disabled")
self.save_btn.config(state="normal")
else:
# 默认状态
self.start_btn.config(state="normal")
self.pause_btn.config(state="disabled")
self.stop_btn.config(state="disabled")
self.save_btn.config(state="disabled")
if __name__ == "__main__":
root = tk.Tk()
app = AudioRecorderApp(root)
root.mainloop()
更多推荐



所有评论(0)