PyAudio库基本知识详解——为自制PCM音频播放器做准备
前言
结合前段时间我们做的音频编解码器,这样我们就可以将获取到的ADPCM数据,转换成PCM数据,然后播放出来,得到一个完整的音频数据,因此,接下来几篇文章中,我们想做一个播放PCM格式的音频播放器。待到音频编解码器完成,播放器完成,或许我们就能做一个语音识别功能模块,通过一个小收音器的BLE蓝牙连接,连接到电脑上,实时完成音频传输,解码,以及识别的整套流程。可以完成语音写入文本,不仅彻底解放双手,还能解放双脚,让你离开电脑桌也能输入文本(不过也是作者的美好幻想, 先挖个坑,距离实现遥遥无期,尽请期待!)
在此之前呢,我们先脚踏实地的,了解一下我们必要的第三方库,PyAudio
。
前置知识
在介绍PyAudio
库之前我们需要先了解一点点前置内容
- 采样率:采样率是指机器每秒采样的次数,是音频处理中的重要参数。如:采样率为44100,即1秒钟采样44100次,常见的采样率为44100、48000,在小型设备中,如只专注语音的识别,不在乎音质,通常会选用更小的采样率,如8000、16000等,换取硬件设备的成本优势
- 音频格式:指的是每个采样点的长度,如:
paInt16
,表示每个采样点为16比特,一次采样获取的数据为16比特,常见的采样点大小为8、16、32等。单位为比特。通常采样率与采样点大小即可确定一定时间内,采样获取的数据大小,如:当音频格式为paInt16
,采样率为8KHz,那么1s中,将采样到8000 * 16bit = 128kb
的数据。 - 通道:指的是声道,麦克风拾音的过程。分为单声道,多声道,立体声,联合立体声等。
- 块:块是指数据块,在PyAudio可以实时处理动态音频数据,也就是
音频流
,而处理音频流实际上也是将连续
的(实际上并非连续)输入以数据块的形式处理,并不是来一个字节处理一个字节,可以理解为缓冲区buffer
的概念。
稍稍理解这些概念,以便后续阅读中看懂PyAudio
的参数,如果没懂,可以返回来反复阅读。
PyAudio
PyAudio
是一个用于在 Python 中进行音频处理的库,它为我们提供了跨平台的接口,用于录音、播放和处理音频流。PyAudio
封装了 PortAudio 库,PortAudio 是一个跨平台的音频库,支持多种操作系统(如 Windows、macOS、Linux)。
PyAudio的常见用途
- 录音(Recording Audio):可以从麦克风获取音频数据。
- 播放音频(Playing Audio):可以播放 WAV 或其他格式的音频文件。
- 实时音频处理:处理音频数据流(例如,分析麦克风输入,实时音效等)。
安装 PyAudio
首先需要安装 PyAudio
。可以使用 pip
来安装:
pip install pyaudio
PyAudio的核心概念
- 流(Stream): PyAudio 的核心概念是“流”(Stream)。流是数据的一个持续流动的通道,用于音频输入或输出。你可以创建一个输入流(录音)或输出流(播放音频),然后使用流来传输音频数据。
- 音频格式和数据: PyAudio 支持多种音频格式,例如
paInt16
(16 位整数格式)和paFloat32
(32 位浮点格式)。音频数据通常是原始的字节流数据,可以通过 NumPy 数组或其他方式进行处理。 - 回调函数: 在实时音频流的场景中,通常使用回调函数来处理音频数据。回调函数会在每次采集到一块音频数据时被调用。
主要功能
-
录音示例: 以下是一个使用
PyAudio
录音的简单示例:import pyaudio import wave# 设置录音参数 FORMAT = pyaudio.paInt16 # 音频格式 CHANNELS = 1 # 单声道 RATE = 44100 # 采样率 CHUNK = 1024 # 每次读取的数据块大小 RECORD_SECONDS = 5 # 录音时长 OUTPUT_FILENAME = "output.wav" # 输出文件p = pyaudio.PyAudio()# 开启输入流 stream = p.open(format=FORMAT,channels=CHANNELS,rate=RATE,input=True,frames_per_buffer=CHUNK)print("开始录音...")frames = []# 录制数据 for _ in range(0, int(RATE * RECORD_SECONDS / CHUNK)):data = stream.read(CHUNK)frames.append(data)print("录音结束...")# 停止流并关闭 stream.stop_stream() stream.close() p.terminate()# 将录音保存为WAV文件 wf = wave.open(OUTPUT_FILENAME, 'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(b''.join(frames)) wf.close()
这里有值得一提的一个点:
# 录制数据 for _ in range(0, int(RATE * RECORD_SECONDS / CHUNK)):data = stream.read(CHUNK)frames.append(data)
这个循环的主要目的是从流中读取数据,并添加到
frams
列表中,可以看到RATE * RECORD_SECONDS
为采样率 * 录音时长
,这可以得到在本次录音中一共采样的次数,/ CHUNK
表明采样的数据被分块读取并写入frames
。这里其实隐式的处理了采样格式,即paInt16
。 -
播放音频示例: 以下是一个播放 WAV 文件的简单示例:
import pyaudio import wave# 打开WAV文件 wf = wave.open('output.wav', 'rb')p = pyaudio.PyAudio()# 打开输出流 stream = p.open(format=pyaudio.paInt16,channels=wf.getnchannels(),rate=wf.getframerate(),output=True)# 读取文件并播放 chunk = 1024 data = wf.readframes(chunk) while data:stream.write(data)data = wf.readframes(chunk)# 关闭流 stream.stop_stream() stream.close() p.terminate()
PyAudio的高级功能
-
回调模式(Callback Mode): PyAudio 提供了回调模式来处理音频流。在这种模式下,你可以通过定义回调函数来实时处理录制或播放的音频数据。例如,你可以实时对音频数据进行处理或应用某些效果(如滤波、增益等)。
示例:
import pyaudiodef callback(in_data, frame_count, time_info, status):# 处理音频数据print("Recording...")return (in_data, pyaudio.paContinue)p = pyaudio.PyAudio()# 打开输入流 stream = p.open(format=pyaudio.paInt16,channels=1,rate=44100,input=True,frames_per_buffer=1024,stream_callback=callback)stream.start_stream()# 等待流结束 while stream.is_active():passstream.stop_stream() stream.close() p.terminate()
PyAudio的应用场景
- 音频录制与播放:用来创建简单的音频应用程序,进行声音的录制和回放。
- 语音识别:结合其他语音识别库(如
speech_recognition
)可以实现语音识别功能。 - 实时音频处理:可以实时分析、修改或增强音频数据。
- 声音特效处理:可以在录制或播放过程中加入音效,如回声、变调等。
PyAudio与其他音频库对比
- vs. wave:
wave
模块主要用于处理静态的音频文件,而PyAudio
支持音频流,能够进行实时录音与播放。 - vs. soundfile:
soundfile
更适用于处理已保存的音频文件的读写操作,而PyAudio
更擅长实时音频流的处理。
注意事项
- 在不同平台上安装
PyAudio
可能需要额外的步骤。例如,Windows 上可能需要安装PortAudio
的依赖项。 PyAudio
并不支持直接播放 MP3 等压缩音频格式。如果需要播放其他格式的音频,可能需要使用其他库如pygame
或pydub
。
小结
总的来说,PyAudio
是一个非常强大的库,适合用于音频处理和实时音频应用,尤其在需要直接与麦克风或扬声器交互时,提供了非常简洁的接口。
stop_stream与start_stream
可能会有同学好奇,在上面的示例代码中,为什么只看见
stream.stop_stream
而没有看见stream.start_stream
呢,如果只有停止流的操作,为什么没有开启流的操作,流在哪里被开启了呢?
PyAudio
中的 stream.start_stream()
和 stream.stop_stream()
是两个重要的流控制方法,用于控制音频流的开始和停止。
在 PyAudio
的流(Stream
)中,start_stream()
和 stop_stream()
方法实际上是可选的,它们的行为是与音频流的实际使用场景有关的。让我们详细解释一下这两个方法,以及为什么在某些情况下,可能看不到显式调用 start_stream()
。
1. stream.start_stream() 的作用
start_stream()
方法用于启动音频流,开始录制或播放音频。它是用来显式告诉程序 “现在开始处理音频数据”。
2. stream.stop_stream() 的作用
stop_stream()
方法用于停止音频流,表示录制或播放过程结束。这对于需要显式控制音频流的结束时机的程序非常有用。
为什么 start_stream()
有时没被显式调用
-
回调模式(Callback Mode): 如果使用了回调模式(即通过设置
stream_callback
参数来传递回调函数),那么在调用open()
方法时,音频流已经自动开始了。回调机制会在后台处理音频数据的输入和输出。因此,在这种模式下,start_stream()
是不必要的,因为流会自动启动。例如:
import pyaudiodef callback(in_data, frame_count, time_info, status):# 处理音频数据print("Recording...")return (in_data, pyaudio.paContinue)p = pyaudio.PyAudio()# 打开输入流并设置回调函数 stream = p.open(format=pyaudio.paInt16,channels=1,rate=44100,input=True,frames_per_buffer=1024,stream_callback=callback)# 注意: 这里不需要显式调用 start_stream() stream.start_stream() # 这行可以省略,回调模式会自动启动# 等待流结束 while stream.is_active():passstream.stop_stream() stream.close() p.terminate()
在这个例子中,
start_stream()
虽然可以显式调用,但在使用回调模式时,可以省略,因为PyAudio
会在底层自动处理流的启动和停止。 -
默认自动启动(在非回调模式下): 在非回调模式(例如通过
stream.read()
或stream.write()
进行音频数据的读取和写入)中,start_stream()
也是隐式调用的。在创建音频流时,open()
方法会自动启动流,并开始数据的录制或播放。import pyaudiop = pyaudio.PyAudio()# 设置流参数 stream = p.open(format=pyaudio.paInt16,channels=1,rate=44100,input=True,frames_per_buffer=1024)# 此时,流已经自动启动 data = stream.read(1024) # 录音 stream.stop_stream() stream.close() p.terminate()
在这个例子中,
open()
已经自动启动了流,调用stream.read()
只是触发音频数据的读取过程,而不需要显式调用start_stream()
。
小结
- 回调模式:当你使用回调函数时,
PyAudio
会自动管理音频流的开始和停止。因此,通常不需要显式调用start_stream()
,而只需要设置stream_callback
并开始流的处理。 - 非回调模式:即便是非回调模式,
open()
方法有时也会自动开始音频流,尤其是在调用stream.read()
或stream.write()
时,不需要显式调用start_stream()
。
但是,在一些场景下,尤其是自定义更复杂的音频控制时,start_stream()
和 stop_stream()
还是很有用的,特别是当你需要手动控制音频流的启动与停止时。
谁开启的麦克风或扬声器
在上面的示例代码中,敏锐的读者可能会发现,似乎没有发现那里调用了麦克风或扬声器,合理猜测,直接告诉我们,应该是PyAudio库调用了硬件设备,但真是如此吗,如果是,那么又是怎么调用的呢?
我们以上面的代码为例,究竟是谁调用了麦克风,开启了录音
音频的录制是通过 pyaudio
库来实现的,调用麦克风硬件的部分是由 pyaudio
库中的 PyAudio
类和它的 open()
方法来完成的。具体的过程是这样的:
调用麦克风硬件的关键步骤:
-
pyaudio.PyAudio()
: 这个步骤创建了一个PyAudio
对象。这个对象是与音频硬件交互的主要接口,它会管理音频流(包括录音流和播放流),并负责配置音频设备。 -
p.open()
:open()
方法是用来打开一个音频流的,这个流可以是输入流(例如录音)或者输出流(例如播放音频)。在上面的代码中,p.open()
被用来打开一个输入流,用于录制音频。stream = p.open(format=FORMAT,channels=CHANNELS,rate=RATE,input=True,frames_per_buffer=CHUNK)
关键参数:
format=FORMAT
:指定音频数据的格式,通常为 16 位 PCM 编码(pyaudio.paInt16
)。channels=CHANNELS
:指定录音的通道数,1 表示单声道,2 表示立体声。rate=RATE
:指定采样率,通常选择 44100 Hz,这是 CD 音质的标准采样率。input=True
:这告诉pyaudio
创建一个输入流,也就是通过麦克风录音。如果将其设置为False
,则会创建输出流,用于播放音频。frames_per_buffer=CHUNK
:指定每次读取的音频块的大小,通常是 1024 或 2048。
此时,
pyaudio
会通过系统的音频接口与麦克风硬件进行连接,并开始接收音频数据。具体而言,pyaudio
会通过操作系统调用音频驱动程序来访问麦克风硬件。操作系统(如 Windows、Linux、macOS)会提供接口给pyaudio
来处理与硬件的交互。 -
stream.read(CHUNK)
:stream.read(CHUNK)
方法是用来从音频流中读取音频数据的。这里的CHUNK
表示每次读取的音频数据的大小,也就是说它会以一定的时间间隔(根据CHUNK
的大小)从麦克风捕捉音频数据。通过连续调用
stream.read()
,程序不断地从麦克风获取音频数据,并将这些数据存储到frames
列表中。
总结:
- 谁调用了麦克风硬件?
实际上是pyaudio
库调用了麦克风硬件。pyaudio
作为一个高层的音频处理库,封装了与操作系统音频接口的交互。操作系统的音频驱动程序在幕后管理与麦克风硬件的连接和数据传输。 - 如何调用的?
- 通过
pyaudio.PyAudio()
创建的 PyAudio 实例。 - 使用
p.open()
方法创建音频输入流(input=True
),并通过该流接收来自麦克风的音频数据。 - 最终使用
stream.read()
不断地从麦克风获取音频样本。
- 通过
这样,pyaudio
库就可以通过操作系统和驱动程序,控制麦克风硬件进行录音了。
相关文章:
PyAudio库基本知识详解——为自制PCM音频播放器做准备
前言 结合前段时间我们做的音频编解码器,这样我们就可以将获取到的ADPCM数据,转换成PCM数据,然后播放出来,得到一个完整的音频数据,因此,接下来几篇文章中,我们想做一个播放PCM格式的音频播放器…...
Git如何添加子仓库
背景 项目中经常使用别人维护的模块,在git中使用子模块的功能能够大大提高开发效率。 使用子模块后,不必负责子模块的维护,只需要在必要的时候同步更新子模块即可。 本文主要讲解子模块相关的基础命令,详细使用请参考main page…...

001__VMware软件和ubuntu系统安装(镜像)
[ 基本难度系数 ]:★☆☆☆☆ 一、Vmware软件和Ubuntu系统说明: a、Vmware软件的说明: 官网: 历史版本: 如何下载? b、Ubuntu系统的说明: 4、linux系统的其他版本:红旗(redhat)、dibian、cent…...

在国产电脑上运行PDFSAM软件使用pdf分割合并交替混合处理pdf文档
软件下载地址: https://sourceforge.net/projects/pdfsam/files/ 需要注意事项,系统需要java环境,确认系统有java环境,根据软件版本需求安装对应的java运行环境。 下载pdfsam-4.3.4-linux.tar.gz安装包,解压,将runt…...

STM32完全学习——FATFS0.15移植SD卡
一、下载FATFS源码 大家都知道使用CubMAX可以很快的将,FATFS文件管理系统移植到单片机上,但是别的芯片没有这么好用的工具,就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题,然后再移植FATFS文件管理系统。 二、SD…...

Linux -- 生产消费模型之环形队列、信号量
目录 前言 环形队列 如何用环形队列实现生产消费模型? 信号量 sem_t sem_init(初始化信号量) sem_destroy(销毁信号量) 什么是PV操作? sem_wait(P操作,减少信号量ÿ…...

Ashy的考研游记
文章目录 摘要12.1112.2012.21 DAY1(政治/英语)政治英语 12.22 DAY2(数学/专业课)数学专业课 结束估分 摘要 在24年的12月里,Ashy完成了他的考研冲刺,顺利的结束了他本年度的考研之旅。 在十二月里&#…...

MySQL线上事故:使用`WHERE`条件`!=xxx`无法查询到NULL数据
前言 在一次 MySQL 的线上查询操作中,因为 ! 的特性导致未能正确查询到为 NULL 的数据,险些引发严重后果。本文将详细解析 NULL 在 SQL 中的行为,如何避免类似问题,并提供实际操作建议。 1. 为什么NULL会查询不到? 在…...

vue3学习笔记(11)-组件通信
1.props 父传子 子传夫 父传子 接收用defineProps([]) 空字符串也是假 2.自定义事件 $event:事件对象 ref定义的数据在模板里面引用的时候可以不用.value 3.子传父 宏函数 触发事件 声明事件 defineEmits() 挂载之后3s钟触发 4.命名 肉串命名 5.任意组件通信 mitt pubs…...

【PDF物流单据提取明细】批量PDF提取多个区域内容导出表格或用区域内容对文件改名,批量提取PDF物流单据单号及明细导出表格并改名的技术难点及小节
相关阅读及下载: PDF电子物流单据: 批量PDF提取多个区域局部内容重命名PDF或者将PDF多个局部内容导出表格,具体使用步骤教程和实际应用场景的说明演示https://mp.weixin.qq.com/s/uCvqHAzKglfr40YPO_SyNg?token720634989&langzh_CN扫描…...
张量与数据类型
Pytorch最基本的操作对象——张量(tensor),张量是Pytorch中重要的数据结构,可认为是一个高维数组。一般的,标量(scalar)是只有大小没有方向的量,如1、2、3等;向量&#x…...
torchvision.utils.make_grid 解释下
torchvision.utils.make_grid 是 PyTorch 中 torchvision 库提供的一个实用函数,用于将多个图像拼接成一个网格,方便进行可视化。 主要功能 make_grid 将一批图片组织成一个网格形式,输出一个单一的张量,便于使用可视化工具(如 Matplotlib)查看图像。 参数解释 torchvi…...
Android原生Widget使用步骤
需要创建三个XML文件以及一个Class文件 三个XML文件分别是 Widget布局文件 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_p…...
实验八 指针2
7-1 利用指针返回多个函数值 分数 30 全屏浏览 切换布局 作者 陈晓梅 单位 广东外语外贸大学 读入n个整数,调用max_min()函数求这n个数中的最大值和最小值。 输入格式: 输入有两行: 第一行是n值; 第二行是n个数。 输出格式: 输出最大…...

1 数据库(下):多表设计 、多表查询 + SQL中的with查询语法(MySQL8.0以后版本才支持这种新语法)+ 数据库优化(索引优化)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、多表设计1 多表设计-概述2 三种多表关系一对多(多对一)(1)无外键约束(逻辑外键)&…...
什么是.net framework,什么是.net core,什么是.net5~8,版本对应关系
我不知道有多少人和我一样,没学习过.netCore,想要学习,但是版本号太多就蒙了,不知道学什么了,这里解释下各个版本的关系 我们一般开始学习微软的时候,都是开始学习的.netframework,常用的就是4…...

vulhub-wordpress靶场
一.主题上传漏洞 来到靶场点击主题选择add new 这里有一个上传主题的地方 我们可以去网上找到wordpress主题下载一个 wordpress模板 网页设计模板 免费 免费下载 - 爱给网 下载完成后对我们有用的东西只有这一个目录,把它拖出来 点开moban目录后,创建…...
安装与配置
《PHP Libxml》是一个在PHP中处理XML和HTML文档的重要库。它提供了丰富的API,支持DOM、SimpleXML和XMLReader等多种解析方式,广泛应用于各种编程语言和项目中。 安装与配置 安装: 在PHP中,libxml扩展通常是默认启用的。如果你需要手动安装&…...
斗鱼Android面试题及参考答案
常用的图片框架有哪些? Glide:是一个快速高效的 Android 图片加载库,专注于平滑滚动。它支持多种图片格式,包括 GIF,具有高效的缓存策略,能自动管理图片的生命周期,避免内存泄漏和 OOM 错误。其 API 简洁易用,可轻松实现图片的加载、显示和缓存等功能,如一行代码即可实…...

Could not install Gradle distribution from 的解决办法
在安装完成AndroidStudio之后,运行工程出现如下错误 Could not install Gradle distribution from https://services.gradle.org/distributions/gradle-6.5-bin.zip. 错误原因是:对应版本的Gradle文件下载失败了,我这里是gradle-6.5-bin.zip,不同版本的android studio也可…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...