PyAudio库基本知识详解——为自制PCM音频播放器做准备

news/2024/12/29 9:11:06/

前言

结合前段时间我们做的音频编解码器,这样我们就可以将获取到的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的常见用途

  1. 录音(Recording Audio):可以从麦克风获取音频数据。
  2. 播放音频(Playing Audio):可以播放 WAV 或其他格式的音频文件。
  3. 实时音频处理:处理音频数据流(例如,分析麦克风输入,实时音效等)。

安装 PyAudio

首先需要安装 PyAudio。可以使用 pip 来安装:

python">pip install pyaudio

PyAudio的核心概念

  1. 流(Stream): PyAudio 的核心概念是“流”(Stream)。流是数据的一个持续流动的通道,用于音频输入或输出。你可以创建一个输入流(录音)或输出流(播放音频),然后使用流来传输音频数据。
  2. 音频格式和数据: PyAudio 支持多种音频格式,例如 paInt16(16 位整数格式)和 paFloat32(32 位浮点格式)。音频数据通常是原始的字节流数据,可以通过 NumPy 数组或其他方式进行处理。
  3. 回调函数: 在实时音频流的场景中,通常使用回调函数来处理音频数据。回调函数会在每次采集到一块音频数据时被调用。

主要功能

  1. 录音示例: 以下是一个使用 PyAudio 录音的简单示例:

    python">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()
    

    这里有值得一提的一个点:

    python"># 录制数据
    for _ in range(0, int(RATE * RECORD_SECONDS / CHUNK)):data = stream.read(CHUNK)frames.append(data)
    

    这个循环的主要目的是从流中读取数据,并添加到frams列表中,可以看到RATE * RECORD_SECONDS采样率 * 录音时长,这可以得到在本次录音中一共采样的次数,/ CHUNK表明采样的数据被分块读取并写入frames。这里其实隐式的处理了采样格式,即paInt16

  2. 播放音频示例: 以下是一个播放 WAV 文件的简单示例:

    python">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 提供了回调模式来处理音频流。在这种模式下,你可以通过定义回调函数来实时处理录制或播放的音频数据。例如,你可以实时对音频数据进行处理或应用某些效果(如滤波、增益等)。

    示例:

    python">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的应用场景

  1. 音频录制与播放:用来创建简单的音频应用程序,进行声音的录制和回放。
  2. 语音识别:结合其他语音识别库(如 speech_recognition)可以实现语音识别功能。
  3. 实时音频处理:可以实时分析、修改或增强音频数据。
  4. 声音特效处理:可以在录制或播放过程中加入音效,如回声、变调等。

PyAudio与其他音频库对比

  • vs. wave: wave 模块主要用于处理静态的音频文件,而 PyAudio 支持音频流,能够进行实时录音与播放。
  • vs. soundfile: soundfile 更适用于处理已保存的音频文件的读写操作,而 PyAudio 更擅长实时音频流的处理。

注意事项

  • 在不同平台上安装 PyAudio 可能需要额外的步骤。例如,Windows 上可能需要安装 PortAudio 的依赖项。
  • PyAudio 并不支持直接播放 MP3 等压缩音频格式。如果需要播放其他格式的音频,可能需要使用其他库如 pygamepydub

小结

总的来说,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() 有时没被显式调用

  1. 回调模式(Callback Mode): 如果使用了回调模式(即通过设置 stream_callback 参数来传递回调函数),那么在调用 open() 方法时,音频流已经自动开始了。回调机制会在后台处理音频数据的输入和输出。因此,在这种模式下,start_stream() 是不必要的,因为流会自动启动。

    例如:

    python">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 会在底层自动处理流的启动和停止。

  2. 默认自动启动(在非回调模式下): 在非回调模式(例如通过 stream.read()stream.write() 进行音频数据的读取和写入)中,start_stream() 也是隐式调用的。在创建音频流时,open() 方法会自动启动流,并开始数据的录制或播放。

    python">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() 方法来完成的。具体的过程是这样的:

调用麦克风硬件的关键步骤:

  1. pyaudio.PyAudio(): 这个步骤创建了一个 PyAudio 对象。这个对象是与音频硬件交互的主要接口,它会管理音频流(包括录音流和播放流),并负责配置音频设备。

  2. p.open(): open() 方法是用来打开一个音频流的,这个流可以是输入流(例如录音)或者输出流(例如播放音频)。在上面的代码中,p.open() 被用来打开一个输入流,用于录制音频。

    python">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 来处理与硬件的交互。

  3. stream.read(CHUNK): stream.read(CHUNK) 方法是用来从音频流中读取音频数据的。这里的 CHUNK 表示每次读取的音频数据的大小,也就是说它会以一定的时间间隔(根据 CHUNK 的大小)从麦克风捕捉音频数据。

    通过连续调用 stream.read(),程序不断地从麦克风获取音频数据,并将这些数据存储到 frames 列表中。

总结:

  • 谁调用了麦克风硬件?
    实际上是 pyaudio 库调用了麦克风硬件。pyaudio 作为一个高层的音频处理库,封装了与操作系统音频接口的交互。操作系统的音频驱动程序在幕后管理与麦克风硬件的连接和数据传输。
  • 如何调用的?
    • 通过 pyaudio.PyAudio() 创建的 PyAudio 实例。
    • 使用 p.open() 方法创建音频输入流(input=True),并通过该流接收来自麦克风的音频数据。
    • 最终使用 stream.read() 不断地从麦克风获取音频样本。

这样,pyaudio 库就可以通过操作系统和驱动程序,控制麦克风硬件进行录音了。


http://www.ppmy.cn/news/1559033.html

相关文章

嵌入式硬件面试题

1、请问什么是通孔、盲孔和埋孔?孔径多大可以做机械孔,孔径多小必须做激光孔?请问激光微型孔可以直接打在元件焊盘上吗,为什么? 通孔是贯穿整个PCB的过孔,盲孔是从PCB表层连接到内层的过孔,埋孔…

路由器RIP动态路由配置

路由器RIP动态路由配置 目录 实验目的 实验背景 技术原理 配置RIP动态路由的一般步骤 学习任务 实验设备 实验拓扑 实验步骤 实验目的 掌握RIP协议的配置方法:掌握查看通过动态路由协议RIP学习产生的路由;熟悉广域网线缆的链接方式;…

xdoj 分别计算奇数和偶数之和

问题描述 接受用户输入的正整数 n,用程序实现 1 到正整数 n 之间的奇数和偶数之和。 输入说明 正整数 n 输出说明 分别输出 1 到 n 之间的奇数和偶数之和(第一个数为奇数之和,第二个数为偶数之和,两个 数之间用空格间隔&…

Word论文交叉引用一键上标

Word论文交叉引用一键上标 1.进入Microsoft word使用CtrlH快捷键或单击替换按钮 2.在查找内容中输入[^#] 3.鼠标点击,标签为“替换为:”的文本框,注意光标一定要打在图红色方框圈中的文本框中! 4.点击格式选择字体 5.勾选上标…

突发!GitLab(国际版)将停止对中国区用户提供 GitLab.com 账号服务

消息称: 目前,为了更加严格的遵循中国网络数据安全管理的相关要求,GitLab SaaS(国际版)已逐步停止向国内用户提供服务与支持,国内用户亦无法注册或使用 GitLab SaaS(国际版)。自您的…

什么是数据库的锁?怎么实现?

数据库的锁是数据库管理系统(DBMS)用来控制多个并发事务访问共享数据的一种机制。它通过限制对数据的访问,确保在并发环境下数据的完整性和一致性。锁的主要目的是防止多个事务在同一时间对相同的数据进行修改,避免出现数据冲突和…

Linux(Centos 7.6)目录结构详解

Linux(Centos 7.6)是一个操作系统,其核心设计理念是将一切资源抽象为文件,即一切皆文件。比如系统中的硬件设备硬盘、网络接口等都被视为文件。Windows系统一般是分为C、D、E盘。而Linux(Centos 7.6)是以斜线"/"作为文件系统的开始目录&#x…

基于Spring Boot的高校请假管理系统

一、系统背景与意义 随着高校规模的扩大和学生数量的增加,传统的请假管理方式已经难以满足高校管理的需求。人工请假流程繁琐、耗时长,且容易出现信息错误或遗漏。因此,开发一套基于Spring Boot的高校请假管理系统具有重要意义,它…