Python+pyaudio实现音频控制示例详解


Posted in Python onJuly 23, 2022

简介

PyAudio是一个跨平台的音频处理工具包,使用该工具包可以在Python程序中播放和录制音频,也可以产生wav文件等

安装

pip install PyAudio

注意:使用该命令安装时可能会报错,报错内容如下:

Python+pyaudio实现音频控制示例详解

针对该问题,我们使用whl文件进行安装,首先在网址下面找到以下文件并下载,根据自己的python版本及计算机系统下载相应文件即可。

Python+pyaudio实现音频控制示例详解

下载完成后,切换到文件所在目录,使用如下命令安装即可

pip3 install PyAudio-0.2.11-cp38-cp38-win_amd64.whl

pyaudio控制指定设备,录制音频/采集音频流/播放音频

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
#------------- 音频设备操作模块 -------------------
#
#   功能:   录制/获取音频流/播放音频
#   时间:  2021-09-13
#
#--------------------------------------------------

import sys ,pyaudio, wave
from tqdm import tqdm

class UacAudioInAndOut:
    def __init__(self):
        """
            功能:   录音参数初始化
                    创建vad检测模块对象
            参数:   /
            返回值: /
        """
        self.input_format_dict = {"S8_LE":16, "S16_LE":8, "S24_LE":4, "S32_LE":2}
        self.framerate_list = [8000, 11025, 16000, 22050, 32000, 44100, 48000,
                            88200, 96000, 176400, 192000, 352800, 384000]

    def _inforPrintf(self, infor_content):
        """
            功能:   检测操作系统,使用正确编码
                    输出打印信息
            参数:   infor_content: 信息内容
            返回值: /
        """
        if sys.platform != "linux" and sys.platform != "darwin":
            infor_content = str(infor_content).encode("gbk","ignore").decode("gbk")
        print(infor_content)

    def GetAllDevInfor(self):
        """
            功能:   显示支持设备信息
            参数:   /
            返回值: /
        """
        PA = pyaudio.PyAudio()
        self._inforPrintf("----------------------< 本机支持设备 >------------------------------")
        for dev_index in range(PA.get_device_count()):
            self._inforPrintf("\n-------------------------------------------------------")
            for key in PA.get_device_info_by_index(dev_index):
                    self._inforPrintf("%s:%s"%(key, str(PA.get_device_info_by_index(dev_index)[key])))
            self._inforPrintf("========================================================")


    def GetUacDevInfor(self, devKeywordOrIndex=None):
        """
            功能:   获取UAC设备信息
            参数:   devKeywordOrIndex: 设备名称关键字或索引
            返回值: dic 设备信息字典
                    False 设备信息获取失败
        """
        PA = pyaudio.PyAudio()
        if devKeywordOrIndex == None:
            self._inforPrintf("\033[0;36;31m[UacAudioInAndOut] 未设设备, 当前使用默认设备\033[0m")
            return PA.get_default_input_device_info()
        if str(devKeywordOrIndex).isdigit():
            devKeywordOrIndex = int(devKeywordOrIndex)
            return PA.get_device_info_by_index(devKeywordOrIndex)

        uac_infor_list = []
        for uac_index in range(PA.get_device_count()):
            if PA.get_device_info_by_index(uac_index).get("name").find(str(devKeywordOrIndex)) >= 0:
                uac_infor_list.append(PA.get_device_info_by_index(uac_index))

        if len(uac_infor_list) > 1:
            self._inforPrintf("\033[0;36;33m[UacAudioInAndOut] UAC 设备有多个,\
                    请修正关键字, 当前设备如下: %s\033[0m"%str(uac_infor_list))
            return False
        else:
            return uac_infor_list.pop()

    def is_framerate_supported(self, setFramerate, UacAudioInHandle,
                                load_parame_dict, input_or_output="input"):
        """
            功能:   判断当配置在指定设备中是否支持
            参数:   setFramerate:       设置采样率
                    UacAudioInHandle:   设备句柄
                    load_parame_dict:   加载字典
                    input_or_output:    输入/输出功能
            返回值: bool True/False
        """
        try:
            if input_or_output == "input":
                UacAudioInHandle.is_format_supported(rate=float(setFramerate),
                            input_device=load_parame_dict['index'],
                            input_channels=load_parame_dict['setInputChannels'],
                            input_format=load_parame_dict['_setInputFormat'])
            else:
                UacAudioInHandle.is_format_supported(rate=float(setFramerate),
                            output_device=load_parame_dict['index'],
                            output_channels=load_parame_dict['maxOutputChannels'],
                            output_format=UacAudioInHandle.get_format_from_width(load_parame_dict['setOutputFormat']))
            return True
        except:
            return False

    def LoadUacAudioInDevice(self, maxStreamDuration=1000, setInputChannels=None,
                                        setInputFormat=None, devKeywordOrIndex=None):
        """
            功能:   加载音频获取设备
            参数:   maxStreamDuration=1000 默认一段流时长
                    setInputChannels:           通道数
                    setInputFormat:             位宽
                    devKeywordOrIndex:    录音设备关键字/索引
            返回值:
                    成功: UacAudioInHandle, StreamHandle, load_parame_dict
                    失败: False
        """
        try:
            load_parame_dict = {}
            uac_infor_dict = self.GetUacDevInfor(devKeywordOrIndex)
            if not setInputFormat:
                _Format = "S16_LE"
                self._inforPrintf("\033[0;36;33m[UacAudioInAndOut] 未设置位宽,使用默认 S16_LE \033[0m")
            else:
                _Format = setInputFormat
            setInputFormat = self.input_format_dict[_Format]

            if not setInputChannels or int(setInputChannels) > uac_infor_dict["maxInputChannels"]:
                setInputChannels = uac_infor_dict["maxInputChannels"]
                self._inforPrintf("\033[0;36;33m[UacAudioInAndOut] 输入通道未设置/超出当前设备最大值,使用默认最大通道 %s\
                                                                                    \033[0m"%setInputChannels)
            else:
                setInputChannels = int(setInputChannels)
            dev_index = uac_infor_dict["index"]
            load_parame_dict["index"]=dev_index
            load_parame_dict["setInputFormat"] = _Format
            load_parame_dict["_setInputFormat"] = setInputFormat
            load_parame_dict["setInputChannels"] = setInputChannels
            UacAudioInHandle = pyaudio.PyAudio()
            for setInputFramerate in self.framerate_list:
                if self.is_framerate_supported(setInputFramerate, UacAudioInHandle, load_parame_dict):
                    load_parame_dict["setInputFramerate"] = setInputFramerate
                    break
            #计算数据大小一段
            CHUNK_SIZE = int(setInputFramerate * maxStreamDuration / 1000)
            load_parame_dict["CHUNK_SIZE"] = CHUNK_SIZE
            self._inforPrintf("\033[0;36;38m[UacAudioInAndOut] 加载参数: %s\033[0m"%str(load_parame_dict))
            #加载设备
            StreamHandle = UacAudioInHandle.open(
                                format=load_parame_dict['_setInputFormat'],
                                channels=load_parame_dict['setInputChannels'],
                                rate=load_parame_dict['setInputFramerate'],
                                input=True,
                                input_device_index=load_parame_dict['index'],
                                start=False,
                                frames_per_buffer=int(CHUNK_SIZE))
            #开始流获取
            StreamHandle.start_stream()
            return UacAudioInHandle, StreamHandle, load_parame_dict
        except:
            self._inforPrintf("\033[0;36;31m[UacAudioInAndOut] Uac AudioIn 加载失败\033[0m")
            return False, False, False

    def LoadUacAudioOutDevice(self, devKeywordOrIndex):
        """
            功能:   加载音频输出设备
            参数:   /
            返回值: UacAudioInHandle 或 False
        """
        try:
            uac_infor_dict = self.GetUacDevInfor(devKeywordOrIndex)
            UacAudioInHandle = pyaudio.PyAudio()
            return UacAudioInHandle, uac_infor_dict
        except:
            return False


    def GetUacAudioInStream(self, StreamHandle, CHUNK_SIZE):
        """
            功能:   开始采集声卡音频
                    生成音频流
            参数:   UacAudioInHandle:   设备句柄
                    StreamHandle:       流句柄
            返回值  chunk_data 流数据
        """
        return StreamHandle.read(CHUNK_SIZE, exception_on_overflow=False) #防止溢出


    def UacAudioOutPlay(self, playWavFile, Repeat=None, Pdict=None, devKeywordOrIndex=None,):
        """
            功能:   可以循环播放指定文件
            参数:   playWavFile:            播放文件路径
                    Repeat:                 循环播放次数
                    CustomizeAudioParam:    自定义播放参数
            返回值: /
        """
        UacAudioInHandle, uac_infor_dict = self.LoadUacAudioOutDevice(devKeywordOrIndex)
        self._inforPrintf(str(uac_infor_dict).encode("gbk","ignore").decode("gbk"))
        self._inforPrintf("\033[1;36;34m[UacAudioInAndOut] 指定设备: %s\t播放文件: %s\t循环总数: %s\
                                            \033[0m"%(devKeywordOrIndex, playWavFile,Repeat))
        try:
            chunk=1024
            pfb = wave.open(playWavFile, 'rb')
            setOutputFormat = pfb.getsampwidth()
            setOutputChannels = pfb.getnchannels()
            setOutputFramerate = pfb.getframerate()
            uac_infor_dict['setOutputFormat'] = setOutputFormat

            if setOutputChannels > uac_infor_dict["maxOutputChannels"]:
                self._inforPrintf("\033[0;36;31m[UacAudioInAndOut] 当前通道数,在该设备上不支持, \
                                设备最大通道数: %s\033[0m"%uac_infor_dict["maxOutputChannels"])
                return False
            if not self.is_framerate_supported(setOutputFramerate, UacAudioInHandle, uac_infor_dict, "output"):
                self._inforPrintf("\033[0;36;31m[UacAudioInAndOut] 当前文件采样率,在该设备上不支持,\
                                    设备默认采样率: %s\033[0m"%uac_infor_dict["defaultSampleRate"])
                return False
            else:
                uac_infor_dict["defaultSampleRate"] = setOutputFramerate
            stream = UacAudioInHandle.open(
                                output_device_index=uac_infor_dict['index'],
                                format=UacAudioInHandle.get_format_from_width(setOutputFormat),
                                channels=setOutputChannels,
                                rate=setOutputFramerate,
                                output=True)

            if Repeat == "Dead_cycle":
                self._inforPrintf("\033[1;36;33m[UacAudioInAndOut] Dead cycle play !!! \033[0m")
                while True:
                    if type(Pdict) == dict and Pdict["play status"] == "stop":
                        break
                    pfb = wave.open(playWavFile, 'rb')
                    while True:
                        data = pfb.readframes(chunk)
                        if not data:
                            break
                        stream.write(data)
            else:
                for index in tqdm(range(int(Repeat))):
                    if type(Pdict) == dict and Pdict["play status"] == "stop":
                        break
                    pfb = wave.open(playWavFile, 'rb')
                    while True:
                        data = pfb.readframes(chunk)
                        if not data:
                            break
                        stream.write(data)

            stream.stop_stream()
            stream.close()
            self.CloseAudioDevice(UacAudioInHandle)
            return True
        except:
            stream.stop_stream()
            stream.close()
            return False


    def UacAudioInRecord(self, saveWavFile, recordTime, #单位秒
                        setInputChannels=None,
                        setInputFormat=None,
                        devKeywordOrIndex=None):
        """
            功能:   录制音频文件
            参数:   recordTime:         录音时长, 单位(s)
                    setInputFramerate:  采样率
                    setInputChannels:   通道数
                    setInputFormat:     位宽
                    devKeywordOrIndex:      录音设备索引
            返回值: /
        """
        maxStreamDuration=1000
        load_parame_dict = {}
        UacAudioInHandle, StreamHandle, load_parame_dict = self.LoadUacAudioInDevice(
                                                                    maxStreamDuration,
                                                                    setInputChannels,
                                                                    setInputFormat,
                                                                    devKeywordOrIndex)
        if not UacAudioInHandle or not StreamHandle:
            self._inforPrintf("\033[0;36;31m[UacAudioInAndOut] 录音失败\033[0m")
            return False

        self._inforPrintf("\033[1;36;34m[UacAudioInAndOut] 录音 -> 文件名: %s 时长: %s\
                                            \033[0m"%(saveWavFile,recordTime))
        self._inforPrintf(load_parame_dict["CHUNK_SIZE"])
        data_list = []
        for recordTime_index in range(int(recordTime)):
            data = None
            data = StreamHandle.read(load_parame_dict["CHUNK_SIZE"], exception_on_overflow=False)
            data_list.append(data)
        StreamHandle.stop_stream()
        StreamHandle.close()
        self.CloseAudioDevice(UacAudioInHandle)
        with wave.open(saveWavFile, "wb") as wavfb:
            wavfb.setnchannels(load_parame_dict["setInputChannels"])
            wavfb.setsampwidth(UacAudioInHandle.get_sample_size(load_parame_dict["_setInputFormat"]))
            wavfb.setframerate(load_parame_dict["setInputFramerate"])
            wavfb.writeframes(b''.join(data_list))

        """
            功能:   关闭音频流设备
            参数:   UacAudioInHandle
            返回值: bool True/False
        """
        try:
            StreamHandle.stop_stream()
            StreamHandle.close()
            self.CloseAudioDevice()
            return True
        except:
            return False

    def CloseAudioDevice(self, UacAudioDeviceHandle):
        """
            功能:   释放 Audio 设备
            参数:   UacAudioDeviceHandle
            返回值: bool True/False
        """
        try:
            UacAudioDeviceHandle.terminate()
            return True
        except:
            return False


if __name__=="__main__":
    asv = UacAudioInAndOut()
    asv.GetAllDevInfor()
    #asv.UacAudioOutPlay(sys.argv[1], int(sys.argv[2]), None, sys.argv[3])
    asv.UacAudioInRecord(sys.argv[1], sys.argv[2])

以上就是Python+pyaudio实现音频控制示例详解的详细内容,更多关于Python pyaudio音频控制的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
用Python实现一个简单的多线程TCP服务器的教程
May 05 Python
Python中正则表达式详解
May 17 Python
利用python求相邻数的方法示例
Aug 18 Python
基于python内置函数与匿名函数详解
Jan 09 Python
python版大富翁源代码分享
Nov 19 Python
python使用xlsxwriter实现有向无环图到Excel的转换
Dec 12 Python
python读取几个G的csv文件方法
Jan 07 Python
pytorch自定义初始化权重的方法
Aug 17 Python
Python turtle绘画象棋棋盘
Aug 21 Python
使用Python完成15位18位身份证的互转功能
Nov 06 Python
python实现猜数游戏
Mar 27 Python
python操作ini类型配置文件的实例教程
Oct 30 Python
python高温预警数据获取实例
Jul 23 #Python
Python中的socket网络模块介绍
Jul 23 #Python
python解析照片拍摄时间进行图片整理
Jul 23 #Python
Python docx库删除复制paragraph及行高设置图片插入示例
Jul 23 #Python
Python 避免字典和元组的多重嵌套问题
Jul 15 #Python
Pytorch中expand()的使用(扩展某个维度)
Jul 15 #Python
Python实现聚类K-means算法详解
Jul 15 #Python
You might like
php学习笔记(三)操作符与控制结构
2011/08/06 PHP
PHP产生不重复随机数的5个方法总结
2014/11/12 PHP
PHP使用星号隐藏用户名,手机和邮箱的实现方法
2016/09/22 PHP
PHP7如何开启Opcode打造强悍性能详解
2018/05/11 PHP
php操作redis数据库常见方法实例总结
2020/02/20 PHP
33个优秀的 jQuery 图片展示插件分享
2012/03/14 Javascript
基于Bootstrap+jQuery.validate实现Form表单验证
2014/12/16 Javascript
JQuery删除DOM节点的方法
2015/06/11 Javascript
JS把内容动态插入到DIV的实现方法
2016/07/19 Javascript
基于angularJS的表单验证指令介绍
2016/10/21 Javascript
基于构造函数的五种继承方法小结
2017/07/27 Javascript
js将当前时间格式化为 年-月-日 时:分:秒的实现代码
2018/01/20 Javascript
微信小程序实现MUI数字输入框效果
2018/01/31 Javascript
vue的全局提示框组件实例代码
2018/02/26 Javascript
Vue-CLI3.x 设置反向代理的方法
2018/12/06 Javascript
使用mixins实现elementUI表单全局验证的解决方法
2019/04/02 Javascript
Vue3项目打包后部署到服务器 请求不到后台接口解决方法
2020/02/06 Javascript
vue 中的 render 函数作用详解
2020/02/28 Javascript
node.js通过url读取文件
2020/10/16 Javascript
解决vant框架做H5时踩过的坑(下拉刷新、上拉加载等)
2020/11/11 Javascript
[41:17]完美世界DOTA2联赛PWL S3 access vs CPG 第二场 12.13
2020/12/17 DOTA
进一步探究Python中的正则表达式
2015/04/28 Python
在IPython中进行Python程序执行时间的测量方法
2018/11/01 Python
在Python中将函数作为另一个函数的参数传入并调用的方法
2019/01/22 Python
深入了解python中元类的相关知识
2019/08/29 Python
Python 类的魔法属性用法实例分析
2019/11/21 Python
Django项目创建及管理实现流程详解
2020/10/13 Python
酒店前台接待岗位职责
2013/12/03 职场文书
珍惜资源的建议书
2014/08/26 职场文书
群众路线党员个人整改措施
2014/10/27 职场文书
525心理健康活动总结
2015/05/08 职场文书
2015年领导班子工作总结
2015/05/23 职场文书
百家讲坛观后感
2015/06/12 职场文书
Springboot如何使用logback实现多环境配置?
2021/06/16 Java/Android
2021年国漫热度排行前十,完美世界上榜,第四是美国动画作品
2022/03/18 国漫
python三子棋游戏
2022/05/04 Python