录制语音信号时的块是什么

信息处理 声音的 语音
2022-01-17 22:56:11

我无法理解音频录制的示例程序,它带有 PyAudio 模块。

我是使用外接声卡录制声音,显然,声音应该转换成数字形式呈现给计算机。但我不明白这段代码如何将输入的模拟语音转换为数字形式然后保存。

这是代码,

import pyaudio
import wave
import sys

CHUNK = 1024
# What is CHUNKS here ?
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "test.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
print("start....")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("done...")


stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

执行代码时,它使用带有麦克风的外部声卡记录声音 5 秒。

我从代码中了解到的是,

速率为 44Kbps,这意味着每秒从模拟音频信号生成 44 千字节。

然后会生成一个空帧,frame[]其中将存储所有记录的数据。

接下来是for循环,

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)

答案RATE/CHUNK*RECORD_SECONDS215.33这意味着将有 215 次迭代。

我不明白为什么RATE要除以CHUNKS然后乘以RECORD_SECONDS记录5秒数。(RECORD_SECONDS = 5)

另外,究竟是CHUNK什么?

4个回答

所以本质上,@porten 指出了什么是声音块。

让我们看看你的代码:

CHUNK = 1024
# What is CHUNKS here ?

块就像一个缓冲区,因此每个缓冲区将包含 1024 个样本,然后您可以保留或丢弃这些样本。由于处理能力的原因,我们使用 CHUNKS 数据,而不是连续数量的音频。(假设这是从麦克风接收到的连续数据流,并且正在记录和保存)然后它只会占用处理器,从而导致潜在的崩溃。在 Raspberry Pi / Arduino 开发(RAM 非常小)方面,像这样对数据进行分块会使流更容易流动,从而防止内存泄漏。

另一个原因:

让我们假设您想要实现一种算法来确定某物是语音还是仅仅是噪音。您将/如何使用恒定的声音数据流来做到这一点?这将是非常困难的。因此,通过将其存储到数组(或您的情况下的列表)中,您可以对此数据执行分析,例如 RMS。然后你可以有一些阈值来确定是否要保留数据,或者数据不好。例如

CHUNK = 1024

var input[CHUNK] = readAudio(CHUNK); // reads in 1024 of data

// compute RMS 
a = RMS(input); 
if(a >= THRES_VALUE)
{ 
    actual += input;
}

所以在上面的这个例子中,我们记录了 1024 字节的数据,然后我们计算 RMS 并检查声音是语音还是白噪声。

同样要确认,块包含数据。

循环for的目的是记录确定的秒数。此数字由 指定RECORD_SECONDS

为了做到这一点,有必要知道我们必须采取多少样本。在您的示例RECORD_SECONDS = 5中,我们要记录 5 秒。

此外,该变量RATE表示每秒采集多少个样本。它的单位是 Hz = 样本/秒。

最后,CHUNCK = 1024说一段声音将有多少字节。

知道了这一点,现在我们可以理解为什么要进行操作RATE / CHUNK * RECORD_SECONDS了。

正如我所说,

  • 速率:Hz = 样本/秒
  • 块 = 字节
  • RECORD_SECONDS = 秒

此操作负责控制循环进行多少次迭代for每次迭代,记录 1024 个字节 ( data = stream.read(CHUNK))

因此,要记录 5 秒,我们必须采集 16 000 个样本/秒 * 5 秒 = 80 000 个样本。最后,如果每次迭代需要 1024 个样本,for则必须循环 80 000/1024 次。

这就是为什么有必要编写表达式RATE / CHUNK * RECORD_SECONDS以记录所需的秒数。

通常一个数据流被分解成数据块,其中每个块是一些样本。实际上,模数转换器 (ADC) 有一个缓冲区,您可以从中读取数据。所以缓冲区有时也称为数据块。在实践中通常更快地读取一个块中的许多样本,然后处理该块,而不是逐个样本地读取数据。

您会看到单词块在音频处理中出现了很多。有时一个块可以引用元数据和数据。例如,这篇Wikipedia AIFF文章以 AIFF 格式描述了几个不同的块。

这是对波特恩答案的补充。

您对费率的描述并不完全正确。速率以赫兹为单位,在您的情况下意味着 44100 Hz - 或每秒 44100 个样本。因此,您基本上是在从您的设备中读出许多数字化值。因此,如果您想记录 5 秒,则必须保存 $5\cdot 44100$ 个样本。因为,正如 porten 在他的回答中所写,大多数音频系统都使用块(也称为帧或块),程序现在将读取数据块。在您的情况下,一大块是 1024 个样本。所以基本上系统会从缓冲区中读取 215.33 个块,直到它保存了整个 5 秒的音频数据。544100 samples. Because, as porten wrote in his answer, most audio systems work with chunks (also called frames or blocks), the program will now read chunks of data. One chunk in your case is 1024 samples. So basicly the system reads 215.33 chunks out of the buffer until it has saved the whole 5 seconds of audio data.