当前位置: 首页 > article >正文

如何在多线程中安全地使用 PyAudio

1. 背景介绍

在多线程环境下使用 PyAudio 可能会导致段错误(Segmentation Fault)或其他不可预期的行为。这是因为 PyAudio 在多线程环境下可能会出现资源冲突或线程安全问题。

PyAudio 是一个用于音频输入输出的 Python 库,它依赖于 PortAudio 库。在多线程环境下,如果多个线程同时创建和销毁 PyAudio 实例,可能会导致资源冲突,从而引发段错误。

在多线程环境下,尽量避免在每个线程中创建和销毁 PyAudio 实例。相反,应该在主线程中创建一个共享的 PyAudio 实例,并在子线程中使用它。

import pyaudio
import threading# 在主线程中创建共享的 PyAudio 实例
p = pyaudio.PyAudio()def audio_thread_function():# 在子线程中使用共享的 PyAudio 实例stream = p.open(format=pyaudio.paInt16,channels=1,rate=44100,input=True,frames_per_buffer=1024)# 音频处理逻辑stream.stop_stream()stream.close()# 创建并启动多个音频线程
threads = []
for _ in range(4):thread = threading.Thread(target=audio_thread_function)thread.start()threads.append(thread)# 等待所有线程完成
for thread in threads:thread.join()# 在主线程中释放 PyAudio 资源
p.terminate()

2. 实际问题和解决方案

2.1 问题代码

import time
import pyaudio
import numpy as np
import librosa
import soundfile as sf
import threading
import os# 查找设备索引
def find_device_index(device_name, is_input=True):p = pyaudio.PyAudio()device_count = p.get_device_count()for i in range(device_count):device_info = p.get_device_info_by_index(i)if device_name in device_info['name']:if is_input and device_info['maxInputChannels'] > 0:p.terminate()return ielif not is_input and device_info['maxOutputChannels'] > 0:p.terminate()return ip.terminate()raise ValueError(f"Device '{device_name}' not found or not a {'input' if is_input else 'output'} device.")# 播放音频文件
def play_audio(file_path, device_index, start_event):start_event.wait()  # 等待事件被设置audio_data, sr = librosa.load(file_path, sr=None)# 创建 PyAudio 实例p = pyaudio.PyAudio()# 打开流stream = p.open(format=pyaudio.paFloat32,channels=1,rate=sr,output=True,output_device_index=device_index)# 播放音频stream.write(audio_data.astype(np.float32).tobytes())# 关闭流stream.stop_stream()stream.close()p.terminate()# 录音
def record_audio(device_index, output_file, start_event, stop_event):start_event.wait()  # 等待事件被设置p = pyaudio.PyAudio()# 打开输入流stream = p.open(format=pyaudio.paFloat32,channels=1,rate=44100,input=True,input_device_index=device_index,frames_per_buffer=1024)print(f"Recording to {output_file}...")frames = []while not stop_event.is_set():  # 检查停止事件data = stream.read(1024)frames.append(data)print("Recording finished.")# 关闭流stream.stop_stream()stream.close()p.terminate()# 保存录音audio_data = b''.join(frames)audio_array = np.frombuffer(audio_data, dtype=np.float32)sf.write(output_file, audio_array, 44100)# 主程序
if __name__ == "__main__":# 根据设备名称查找设备索引CUBE_4NANO_DYNA_INDEX = find_device_index("Cube 4Nano Dyna", is_input=False)SOUNDMATRIX_A10_OUTPUT_INDEX = find_device_index("SoundMatrix A10", is_input=False)SOUNDMATRIX_A10_INPUT_INDEX = find_device_index("SoundMatrix A10", is_input=True)# 获取音频文件列表audio_file1_folder = './Soundplay'audio_file2_folder = './A10play'audio_file1_list = [f for f in os.listdir(audio_file1_folder) if f.endswith('.wav')]audio_file2_list = [f for f in os.listdir(audio_file2_folder) if f.endswith('.wav')]# 创建事件对象start_event = threading.Event()stop_event = threading.Event()# 遍历每一对组合for audio_file1 in audio_file1_list:for audio_file2 in audio_file2_list:file_path1 = os.path.join(audio_file1_folder, audio_file1)file_path2 = os.path.join(audio_file2_folder, audio_file2)print(file_path1, file_path2)# 创建线程play_thread1 = threading.Thread(target=play_audio, args=(file_path1, CUBE_4NANO_DYNA_INDEX, start_event))play_thread2 = threading.Thread(target=play_audio,args=(file_path2, SOUNDMATRIX_A10_OUTPUT_INDEX, start_event))output_file_name = f"{os.path.splitext(audio_file1)[0]}_{os.path.splitext(audio_file2)[0]}_soundmatrix.wav"output_file_path = os.path.join('./A10rec', output_file_name)record_thread = threading.Thread(target=record_audio, args=(SOUNDMATRIX_A10_INPUT_INDEX, output_file_path, start_event, stop_event))# 启动录音和播放线程record_thread.start()  # 启动录音线程play_thread1.start()  # 播放第一个音频play_thread2.start()  # 播放第二个音频# 设置事件,开始播放和录音start_event.set()# 等待播放线程完成play_thread1.join()play_thread2.join()# 设置停止事件,结束录音stop_event.set()record_thread.join()# 重置事件以便下次使用start_event.clear()stop_event.clear()print("All tasks completed.")

报错:

./Soundplay/cafeteria_SNR0_副本3.wav ./A10play/SER0.wav
Recording to ./A10rec/cafeteria_SNR0_副本3_SER0_soundmatrix.wav...
Recording finished.
./Soundplay/cafeteria_SNR0_副本2.wav ./A10play/SER0.wavProcess finished with exit code 139 (interrupted by signal 11:SIGSEGV)

2.2 解决方案:共享 PyAudio 实例

import time
import pyaudio
import numpy as np
import librosa
import soundfile as sf
import threading
import os# 查找设备索引
def find_device_index(device_name, is_input=True, p=None):if p is None:p = pyaudio.PyAudio()need_terminate = Trueelse:need_terminate = Falsedevice_count = p.get_device_count()for i in range(device_count):device_info = p.get_device_info_by_index(i)if device_name in device_info['name']:if is_input and device_info['maxInputChannels'] > 0:if need_terminate:p.terminate()return ielif not is_input and device_info['maxOutputChannels'] > 0:if need_terminate:p.terminate()return iif need_terminate:p.terminate()raise ValueError(f"Device '{device_name}' not found or not a {'input' if is_input else 'output'} device.")# 播放音频文件
def play_audio(file_path, device_index, start_event, p):start_event.wait()  # 等待事件被设置audio_data, sr = librosa.load(file_path, sr=None)# 打开流stream = p.open(format=pyaudio.paFloat32,channels=1,rate=sr,output=True,output_device_index=device_index)# 播放音频stream.write(audio_data.astype(np.float32).tobytes())# 关闭流stream.stop_stream()stream.close()# 录音
def record_audio(device_index, output_file, start_event, stop_event, p):start_event.wait()  # 等待事件被设置buffer_size = 2048# 打开输入流stream = p.open(format=pyaudio.paFloat32,channels=1,rate=44100,input=True,input_device_index=device_index,frames_per_buffer=buffer_size)print(f"Recording to {output_file}...")frames = []while not stop_event.is_set():  # 检查停止事件data = stream.read(buffer_size)frames.append(data)print("Recording finished.")# 关闭流stream.stop_stream()stream.close()# 保存录音audio_data = b''.join(frames)audio_array = np.frombuffer(audio_data, dtype=np.float32)sf.write(output_file, audio_array, 44100)# 主程序
if __name__ == "__main__":# 创建共享的 PyAudio 实例p = pyaudio.PyAudio()# 根据设备名称查找设备索引CUBE_4NANO_DYNA_INDEX = find_device_index("Cube 4Nano Dyna", is_input=False, p=p)SOUNDMATRIX_A10_OUTPUT_INDEX = find_device_index("SoundMatrix A10", is_input=False, p=p)SOUNDMATRIX_A10_INPUT_INDEX = find_device_index("SoundMatrix A10", is_input=True, p=p)# 获取音频文件列表audio_file1_folder = './Soundplay'audio_file2_folder = './A10play'audio_file1_list = [f for f in os.listdir(audio_file1_folder) if f.endswith('.wav')]audio_file2_list = [f for f in os.listdir(audio_file2_folder) if f.endswith('.wav')]# 创建事件对象start_event = threading.Event()stop_event = threading.Event()# 遍历每一对组合for audio_file1 in audio_file1_list:for audio_file2 in audio_file2_list:file_path1 = os.path.join(audio_file1_folder, audio_file1)file_path2 = os.path.join(audio_file2_folder, audio_file2)print(file_path1, file_path2)# 创建线程play_thread1 = threading.Thread(target=play_audio, args=(file_path1, CUBE_4NANO_DYNA_INDEX, start_event, p))play_thread2 = threading.Thread(target=play_audio,args=(file_path2, SOUNDMATRIX_A10_OUTPUT_INDEX, start_event, p))output_file_name = f"{os.path.splitext(audio_file1)[0]}_{os.path.splitext(audio_file2)[0]}_soundmatrix.wav"output_file_path = os.path.join('./A10rec', output_file_name)record_thread = threading.Thread(target=record_audio, args=(SOUNDMATRIX_A10_INPUT_INDEX, output_file_path, start_event, stop_event, p))# 启动录音和播放线程record_thread.start()  # 启动录音线程play_thread1.start()  # 播放第一个音频play_thread2.start()  # 播放第二个音频# 设置事件,开始播放和录音start_event.set()# 等待播放线程完成play_thread1.join()play_thread2.join()# 设置停止事件,结束录音stop_event.set()record_thread.join()# 重置事件以便下次使用start_event.clear()stop_event.clear()time.sleep(3)# 释放 PyAudio 资源p.terminate()

相关文章:

如何在多线程中安全地使用 PyAudio

1. 背景介绍 在多线程环境下使用 PyAudio 可能会导致段错误(Segmentation Fault)或其他不可预期的行为。这是因为 PyAudio 在多线程环境下可能会出现资源冲突或线程安全问题。 PyAudio 是一个用于音频输入输出的 Python 库,它依赖于 PortAu…...

QAM 信号的距离以及能量归一化

QAM星座图平均功率能量_星座图功率计算-CSDN博客 正交幅度调制(QAM) - Vinson88 - 博客园 不同阶QAM调制星座图中,符号能量的归一化计算原理_qpsk的星座图归一化-CSDN博客 https://zhuanlan.zhihu.com/p/690157236...

Reactive编程框架与工具

文章目录 6.2 后端 Reactive 框架6.2.1 Spring WebFlux核心架构核心组件实际应用高级特性性能优化适用场景与限制 6.2.2 Akka(Actor模型)Actor模型基础基本用法高级特性响应式特性实现性能优化实际应用场景优势与挑战 6.2.3 Vert.x(事件驱动&…...

五子棋游戏开发:静态资源的重要性与设计思路

以下是以CSDN博客的形式整理的关于五子棋游戏静态资源需求的文章,基于我们之前的讨论,内容结构清晰,适合开发者阅读和参考。我尽量保持技术性、实用性,同时加入一些吸引读者的亮点。 五子棋游戏开发:静态资源的重要性与…...

Python爬虫第7节-requests库的高级用法

目录 前言 一、文件上传 二、Cookies 三、会话维持 四、SSL证书验证 五、代理设置 六、超时设置 七、身份认证 八、Prepared Request 前言 上一节,我们认识了requests库的基本用法,像发起GET、POST请求,以及了解Response对象是什么。…...

Maven的安装配置-项目管理工具

各位看官,大家早安午安晚安呀~~~ 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连,小编尽全力做到更好 欢迎您分享给更多人哦 今天我们来学习:Maven的安装配置-项目管理工具 目录 1.什么是Maven?Maven用来干什么的&#xff1f…...

智能 SQL 优化工具 PawSQL 月度更新 | 2025年3月

📌 更新速览 本月更新包含 21项功能增强 和 9项问题修复,重点提升SQL解析精度与优化建议覆盖率。 一、SQL解析能力扩展 ✨ 新增SQL语法解析支持 SELECT...INTO TABLE 语法解析(3/26) ALTER INDEX RENAME/VISIBLE 语句解析&#…...

Ubuntu虚拟机编译安装部分OpenCV模块方法实现——保姆级教程

Ubuntu虚拟机的安装过程可以查看另一篇文章:VMware安装Ubuntu虚拟机实现COpenCV代码在虚拟机下运行教程-CSDN博客 目前我们已经下载好了OpenCV,这里以OpenCV4.5.2为例。 在内存要求尽可能小的情况下,可以尝试只编译安装代码中使用到的OpenC…...

find指令中使用正则表达式

linux查找命令能结合正则表达式吗 find命令要使用正则表达式需要结合-regex参数 另,-type参数可以指定查找类型(f为文件,d为文件夹) rootlocalhost:~/regular_expression# ls -alh 总计 8.0K drwxr-xr-x. 5 root root 66 4月 8日 16:26 . dr-xr-…...

Java Web从入门到精通:全面探索与实战(二)

Java Web从入门到精通:全面探索与实战(一)-CSDN博客 目录 四、Java Web 开发中的数据库操作:以 MySQL 为例 4.1 MySQL 数据库基础操作 4.2 JDBC 技术深度解析 4.3 数据库连接池的应用​ 五、Java Web 中的会话技术&#xff…...

基于大模型的阵发性室上性心动过速风险预测与治疗方案研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与目标 1.3 研究方法与数据来源 二、阵发性室上性心动过速概述 2.1 定义与分类 2.2 发病机制与流行病学 2.3 临床表现与诊断方法 三、大模型在阵发性室上性心动过速预测中的应用 3.1 大模型技术原理与特点 3.2 模型构…...

秒杀业务的实现过程

一.后台创建秒杀的活动场次信息,并关联到要秒杀的商品或服务; 二.通过定时任务,将秒杀的活动信息和商品服务信息存储到redis; 三.在商品展示页的显眼位置加载秒杀活动信息; 四.用户参与秒杀,创建订单,将…...

spring mvc @ResponseBody 注解转换为 JSON 的原理与实现详解

ResponseBody 注解转换为 JSON 的原理与实现详解 1. 核心作用 ResponseBody 是 Spring MVC 的一个注解,用于将方法返回的对象直接序列化为 HTTP 响应体(如 JSON 或 XML),而不是通过视图解析器渲染为视图(如 HTML&…...

TDengine.C/C++ 连接器

简介 C/C 开发人员可以使用 TDengine 的客户端驱动,即 C/C 连接器(以下都用 TDengine 客户端驱动表示),开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。…...

[docker] 简单操作场景

Docker的简单操作场景 1 安装 暂时没空写~ 2 登陆 一共4步: ~$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d765d4c1eb5f ubuntu:24.04 "/bin/bash" …...

skynet.rawcall使用详解及应用场景

目录 核心特性函数原型使用场景场景 1:高性能二进制传输(如文件转发)场景 2:自定义序列化协议(如 Protocol Buffers)场景 3:跨服务共享内存(避免拷贝) 配套接收方实现与 …...

使用SpringSecurity下,发生重定向异常

使用SpringSecurity下,发生空转异常 环境信息: Spring Boot 3.4.4 , jdk 17 , springSecurity 6.4.4 问题背景: 没有自定义controller ,改写了login 页面,并且进行了成功后的跳转处理&#xf…...

gbase8s之逻辑导出导入脚本(完美版本)

该脚本dbexport.sh用于快速导出库和导入库(使用多并发unload,和多并发dbload的方式) #!/bin/sh #脚本功能:将数据导出成文本,迁移至其他实例 #最后更新时间:2023-12-19 #使用方法: #1.执行该脚…...

Elasticsearch | ES索引模板、索引和索引别名的创建与管理

关注:CodingTechWork 引言 在使用 Elasticsearch (ES) 和 Kibana 构建数据存储和分析系统时,索引模板、索引和索引别名的管理是关键步骤。本文将详细介绍如何通过 RESTful API 和 Kibana Dev Tools 创建索引模板、索引以及索引别名,并提供具…...

【Easylive】视频删除方法详解:重点分析异步线程池使用

【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 方法整体功能 这个deleteVideo方法是一个综合性的视频删除操作,主要完成以下功能: 权限验证:检查视频是否存在及用户是否有权限删除核心数据删除&…...

力扣hot100_回溯(2)_python版本

一、39. 组合总和(中等) 代码: class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:ans []path []def dfs(i: int, left: int) -> None:if left 0:# 找到一个合法组合ans.append(pa…...

SGLang实战:从KV缓存复用到底层优化,解锁大模型高效推理的全栈方案

在当今快速发展的人工智能领域,大型语言模型(LLM)的应用已从简单对话扩展到需要复杂逻辑控制、多轮交互和结构化输出的高级任务。面对这一趋势,如何高效地微调并部署这些大模型成为开发者面临的核心挑战。本文将深入探讨SGLang——这一专为大模型设计的高…...

LPDDR4内存颗粒命名规则全解析:三星、镁光、海力士、南亚、长鑫等厂商型号解码与选型指南

由于之前DDR的系列选型文章有很好的反馈,所以补充LPDDR4低功耗内存的选型和命名规则,总结了目前市面上常用的内存,供硬件工程师及数码爱好者参考。 在智能手机、平板电脑和低功耗设备中,LPDDR4 SDRAM凭借其高带宽、低功耗特性成为…...

特权FPGA之Johnson移位

完整代码: module johnson(clk,rst_n,led,sw1_n,sw2_n,sw3_n);input clk; //时钟信号,50MHz input rst_n; //复位信号,低电平有效 output[3:0] led; //LED控制,1--灭&#xf…...

网络安全小知识课堂(最终完结版)

网络安全入门 :从 “小白” 到 “守护者” 的蜕变之旅 写在完结之际 历经 13 篇的深度探索,我们从 DDoS 攻击的 “流量洪水” 一路闯关到 HTTPS 的 “加密堡垒”,揭开了网络安全世界的层层面纱。感谢每一位读者的陪伴与互动,你们…...

2025年AI生成引擎搜索发展现状与趋势总结​​

2025年AI生成引擎搜索发展现状与趋势总结 ​​一、国内外AI生成引擎搜索发展现状​​ ​​1. 国内动态​​ ​​社交搜索崛起​​:小红书2024年Q4日均搜索量达6亿次,用户更依赖社交平台UGC内容进行决策(如购物、旅游场景)&#…...

【杂谈】Godot4.4导出到Android平台(正式导出)

学博而后可约,事历而后知要。 目录 一、准备二、Gradle构建三、配置Java SDK四、配置Android SDK五、配置密钥 一、准备 本文在前文【杂谈】Godot4.4导出到安卓平台(调试导出)的基础上,进行正式导出。调试导出并不是真正的编译导…...

VBA将Word文档内容逐行写入Excel

如果你需要将Word文档的内容导入Excel工作表来进行数据加工,使用下面的代码可以实现: Sub ImportWordToExcel()Dim wordApp As Word.ApplicationDim wordDoc As Word.DocumentDim excelSheet As WorksheetDim filePath As VariantDim i As LongDim para…...

基于AI设计开发出来的业务系统是什么样的?没有菜单?没有表格?

基于AI设计开发出的业务系统仍然会包含菜单、表格等传统UI元素,但AI技术会显著改变它们的实现方式和交互逻辑。以下是具体分析: 一、传统元素的持续存在 功能刚需性 • 菜单承担着系统导航的核心功能,表格则是结构化数据展示的基础载体。根…...

C++ -异常之除以 0 问题(整数除以 0 编译时检测、整数除以 0 运行时检测、浮点数除以 0 编译时检测、浮点数除以 0 运行时检测)

一、整数除以 0&#xff08;编译时检测&#xff09; 1、演示 #include <iostream>using namespace std;int main() {int result 10 / 0;cout << result << endl;return 0; }程序无法运行&#xff0c;输出结果 error C2124: 被零除或对零求模2、演示解读 …...