这是一段利用 PyAudio 库来录音的代码(出处 ):
import pyaudio
import wave
chunk = 1024 # Record in chunks of 1024 samples
sample_format = pyaudio.paInt16 # 16 bits per sample
channels = 2
fs = 44100 # Record at 44100 samples per second
seconds = 3
filename = "output.wav"
p = pyaudio.PyAudio() # Create an interface to PortAudio
print('Recording')
stream = p.open(format=sample_format,
channels=channels,
rate=fs,
frames_per_buffer=chunk,
input=True)
frames = [] # Initialize array to store frames
# Store data in chunks for 3 seconds
for i in range(0, int(fs / chunk * seconds)):
data = stream.read(chunk)
frames.append(data)
# Stop and close the stream
stream.stop_stream()
stream.close()
# Terminate the PortAudio interface
p.terminate()
print('Finished recording')
# Save the recorded data as a WAV file
wf = wave.open(filename, 'wb')
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()
这里用的是传统的阻塞模式。当收集到的采样数少于 chunk
时,stream.read()
语句将会阻塞。但是,有个问题随即而来——如果当 stream.read()
返回时,收集到的采样没有及时被程序读取,那么这些采样会被保留吗?
一个相似的情况是网络套接字通讯。假设一个程序每次只从套接字读取 A 个字节,而网卡缓冲区的大小是 B 字节(通常情况下,缓冲区都是足够大的,也就是 A < B)。在不断有新的数据传入的情况下,如果程序没有及时读取数据,那么这些数据就会留在网卡缓冲区中;直到网卡缓冲区满了,才会发生数据丢失的情况。那么从程序的角度来看,无论相邻的两次读取的操作间隔了多久,只要缓冲区没有溢出,读取得到的数据都会是连续的。这是因为网卡缓冲区起到了暂时保管数据的作用。
为了试验 PyAudio 在录音时是不是拥有相同的机制,我在每次读取操作之间加入了延迟,使得程序总是不会及时读取返回的数据。计算可得每次读取的数据的时长为:1024 ÷ 44100 = 0.0232秒
,所以我加入了 0.1秒
的延迟。新的代码如下:
chunk = 1024
sample_format = pyaudio.paInt16
channels = 1
fs = 44100
read = 0
p = pyaudio.PyAudio()
stream = p.open(format=sample_format,
channels=channels,
rate=fs,
frames_per_buffer=chunk,
input=True)
while read < 10 * 44100:
data = stream.read(chunk)
read += chunk
time.sleep(0.1)
data = np.fromstring(data, dtype=np.int16)
self.audio_frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open('delay_test.wav', 'wb')
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(self.audio_frames))
wf.close()
播放所得的音频,发现有明显的断续感,也就是有丢帧的情况。这说明,PyAudio 在录音时的数据缓冲机制和网络通讯的不同,如果返回的数据没有被及时读取,最老的数据就会被新的数据覆盖,底层机制只会为程序保留 chunk
个采样。