sounddevice 进行gradio控制录音

server/2025/2/9 12:18:23/

Sounddevice

python">import timeimport sounddevice as sd
import numpy as np
from scipy.io.wavfile import writeSAMPLE_RATE = 16000  # 采样率(Hz)
DURATION = 5  # 录音时长(秒)def save():# 录音参数print("开始录音...")audio_data = sd.rec(int(SAMPLE_RATE * DURATION), samplerate=SAMPLE_RATE, channels=1, dtype=np.int16, device=0)sd.wait()  # 等待录音结束print("录音完成!")# 保存到本地write("recorded_audio.wav", SAMPLE_RATE, audio_data)print("音频已保存为 recorded_audio.wav")def main():audio_data = []start_tiem = time.time()with sd.InputStream(samplerate=SAMPLE_RATE, channels=1, dtype=np.int16) as stream:while time.time() - start_tiem < DURATION:data, _ = stream.read(1024)  # 每次读取 1024 帧audio_data.append(data)# 录音结束后保存音频if not audio_data:returnaudio_np = np.concatenate(audio_data, axis=0)write("recorded_audio.wav", SAMPLE_RATE, audio_np)print("音频已保存为 recorded_audio.wav")if __name__ == "__main__":save()# main()

第一种写法:

使用 sd.rec() 直接录制整个音频

sd.rec() 直接申请一个完整录音时长的 NumPy 数组,并将音频数据填充进去。
录音过程中,数据会自动存入 audio_data 数组,无需 while 循环和 append()。
录音过程中不会实时处理数据

sd.rec() 录音时,Python 代码会暂停执行,直到 sd.wait() 结束录音。
适用于无需实时处理的情况,比如普通语音录制。
计算开销低

直接使用 NumPy 数组存储数据,避免 append() 和 list → NumPy 转换,提高效率。
适用于短时录音(几秒到几十秒),不适合长时间录音(因为 sd.rec() 一次性分配内存)。

第二种写法:

使用 sd.InputStream() 逐块读取数据

通过 stream.read(1024) 每次读取 1024 帧(≈ 64ms),直到录音时间结束。
适用于实时处理音频数据(例如语音识别、音频波形分析)。
适用于长时间录音

由于数据是分块读取并存入 audio_data,不会一次性占用大量内存。
适合长时间录音(如 1 分钟、10 分钟甚至更长)。
计算开销较高

audio_data.append(data) 每次循环都会增加列表长度,最终需要 np.concatenate() 转换为 NumPy 数组,可能会增加内存碎片和计算开销。
适用于需要边录边处理的场景,而不是一次性录音。

Gradio控制

python">import gradio as gr
import sounddevice as sd
import numpy as np
from scipy.io.wavfile import write
import threading
import os
import time
import whisper
import noisereduce as nr  # 导入噪声去除库# 录音参数
SAMPLE_RATE = 16000  # 采样率(Hz)
CHANNELS = 1  # 单声道
audio_data = []  # 存储音频数据
recording = False  # 录音状态
denoise = False
audio_file_path = "recorded_audio.wav"  # 音频文件路径
recording_thread = None  # 录音线程# 加载 Whisper 模型
model = whisper.load_model("small")  # 可以选择 "tiny", "base", "small", "medium", "large"def create_empty_audio():""" 如果音频文件不存在,则创建一个 1 秒的静音 WAV 文件 """if not os.path.exists(audio_file_path):print("🔍 'recorded_audio.wav' 不存在,创建空白音频...")silent_audio = np.zeros(SAMPLE_RATE, dtype=np.int16)  # 生成 1 秒的静音write(audio_file_path, SAMPLE_RATE, silent_audio)def record_audio():""" 录音线程函数 """global recording, audio_data, audio_file_pathaudio_data = []with sd.InputStream(samplerate=SAMPLE_RATE, channels=CHANNELS, dtype=np.int16) as stream:while recording:data, _ = stream.read(1024)  # 每次读取 1024 帧audio_data.append(data)# 录音结束后保存音频if audio_data:audio_np = np.concatenate(audio_data, axis=0)if denoise:# 使用 noisereduce 去噪print("🎧 正在去除背景噪声...")audio_np = nr.reduce_noise(y=audio_np, sr=SAMPLE_RATE)  # 去噪处理write(audio_file_path, SAMPLE_RATE, audio_np)  # 保存去噪后的音频文件def transcribe_audio(file_path):""" 使用 Whisper 模型进行语音识别 """prompt = "如果使用了中文,请使用简体中文来表示文本内容"result = model.transcribe(file_path, initial_prompt=prompt)  # 传入录音文件进行识别return result['text']  # 返回识别结果def toggle_recording():""" 控制录音开始/停止,并在结束后刷新音频文件 """global recording, recording_threadif not recording:recording = Truerecording_thread = threading.Thread(target=record_audio, daemon=True)recording_thread.start()return "录音中... 点击停止", None, ""  # 录音时不更新音频和识别文本else:recording = Falseif recording_thread:recording_thread.join()  # ✅ 等待录音线程完成,确保音频已写入time.sleep(0.2)  # ✅ 额外等待 200ms,确保文件完全写入transcription = transcribe_audio(audio_file_path)  # 获取识别结果return "开始录音", audio_file_path, transcription  # 返回文件路径和识别结果# 启动时检查音频文件是否存在
create_empty_audio()# Gradio UI
with gr.Blocks() as app:gr.Markdown("## 🎙️ 语音录制与识别")# 开始/停止录音按钮record_btn = gr.Button("开始录音")audio_player = gr.Audio(audio_file_path, label="最新录音", interactive=False)transcription_output = gr.Textbox(label="识别结果", interactive=False)  # 显示识别结果record_btn.click(toggle_recording, outputs=[record_btn, audio_player, transcription_output])  # 录音结束后自动更新音频和显示识别结果# 运行 Gradio
app.launch()

http://www.ppmy.cn/server/166216.html

相关文章

Java 面试真题

本题适合一到三年 Java 开发 &#xff0c;以下问题都是按照原面试官提问记录 文章目录 我要进大厂系列面试题二面 我要进大厂系列面试题 全部真题&#xff0c;欢迎投稿你的面试经验。 本篇涉及基础较多&#xff0c;但要耐性看完。 JVM内存模型垃圾回收器用的哪个gc各个算法…

用pytorch实现一个简单的图片预测类别

前言&#xff1a; 在阅读本文之前&#xff0c;你需要了解Python&#xff0c;Pytorch&#xff0c;神经网络的一些基础知识&#xff0c;比如什么是数据集&#xff0c;什么是张量&#xff0c;什么是神经网络&#xff0c;如何简单使用tensorboard,DataLoader。 本次模型训练使用的是…

DeepSeek和ChatGPT的对比

最近DeepSeek大放异彩&#xff0c;两者之间有什么差异呢&#xff1f;根据了解到的信息&#xff0c;简单做了一个对比。 DeepSeek 和 ChatGPT 是两种不同的自然语言处理&#xff08;NLP&#xff09;模型架构&#xff0c;尽管它们都基于 Transformer 架构&#xff0c;但在设计目标…

【Linux基础】Linux下常用的系统命令

一、前言 本文主要总结了工作中常用的linux指令&#xff0c;有遇到新的命令会不定期更新。 二、系统监控和进程管理指令 2.1 ps命令 作用&#xff1a;查看当前进程信息。 常用选项&#xff1a; -e: 显示所有进程&#xff0c;包括其他用户的进程。-f: 显示更详细的进程信息…

模型 冗余系统(系统科学)

系列文章分享模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。为防故障、保运行的备份机制。 1 冗余系统的应用 1.1 冗余系统在企业管理中的应用-金融行业信息安全的二倍冗余技术 在金融行业&#xff0c;信息安全是保障业务连续性和客户资产安全的关键。随着数字化…

新注册的域名无法访问,是怎么回事?

域名是企业和个人线上身份的标识&#xff0c;是对外展示信息提供服务的窗口&#xff0c;其重要性不言而喻。然而&#xff0c;不少朋友在新注册域名后&#xff0c;却遭遇了无法访问的尴尬情况&#xff0c;这到底是怎么回事呢&#xff1f; 域名解析尚未生效 域名注册完成后&…

React 生命周期函数详解

React 组件在其生命周期中有多个阶段&#xff0c;每个阶段都有特定的生命周期函数&#xff08;Lifecycle Methods&#xff09;。这些函数允许你在组件的不同阶段执行特定的操作。以下是 React 组件生命周期的主要阶段及其对应的生命周期函数&#xff0c;并结合了 React 16.3 的…

探秘数据结构之单链表:从原理到实战的深度解析

目录 一、链表的概念及结构 1.1 链表的独特定义 1.2 火车车厢式的形象类比 1.3 节点的结构体定义剖析 1.4 链表物理与逻辑结构的特性差异 二、单链表的实现 2.1 类型定义的优化策略 2.2 链表操作函数的声明框架 2.3 链表操作函数的实现细节 三、链表的分类 前言 …