使用Python进行实时音频信号处理

12
我一直在尝试使用Python中的'pyAudio'模块进行实时音频信号处理。我所做的是从麦克风读取音频数据并通过耳机播放的简单案例。我尝试了以下代码(包括Python和Cython版本)。虽然它能工作,但不幸的是它会卡顿且不够流畅。我该如何改进代码以使其运行更顺畅?我的电脑是i7,8GB内存。
import pyaudio
import numpy as np

RATE    = 16000
CHUNK   = 256
    
p               =   pyaudio.PyAudio()

player = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, output=True, 
frames_per_buffer=CHUNK)
stream = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, input=True, frames_per_buffer=CHUNK)

for i in range(int(20*RATE/CHUNK)): #do this for 10 seconds
    player.write(np.fromstring(stream.read(CHUNK),dtype=np.int16))
stream.stop_stream()
stream.close()
p.terminate()

Cython版本
import pyaudio
import numpy as np

cdef int RATE   = 16000
cdef int CHUNK  = 1024
cdef int i      
p               =   pyaudio.PyAudio()

player = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, output=True, frames_per_buffer=CHUNK)
stream = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, input=True, frames_per_buffer=CHUNK)

for i in range(500): #do this for 10 seconds
    player.write(np.fromstring(stream.read(CHUNK),dtype=np.int16))
stream.stop_stream()
stream.close()
p.terminate()

1
不知道你所说的“ stalls”是什么意思以及你的期望。使用Cython毫无益处-因为没有Python计算,一切都是由库内的C代码完成的。你称它为实时,但使用阻塞IO- 应该如何工作?请使用非阻塞版本https://people.csail.mit.edu/hubert/pyaudio/docs/#example-callback-mode-audio-i-o。 - ead
“Stalls” 指的是音频之间的中断。阻塞模式和非阻塞模式有什么区别?谢谢提供链接。 - Sajil C K
在您的情况下,“阻塞”意味着当它播放时不会录制,当它录制时不会播放。 - ead
1
@ead,虽然非阻塞可以直接将输入(麦克风)连接到输出(耳机/扬声器),但您无法对音频进行任何处理,因为您无法访问/控制它。对于任何中间处理,OP将需要使用阻塞版本(他正在使用的版本)。 - Anil_M
@SAJIL C K,请检查我的答案是否正确。 - Anil_M
你为什么没有使用多线程应用程序呢?我看到的所有Python实时音频处理示例都使用了多线程。 - Tyler Hilbert
3个回答

10

我认为你在调用 player.write 时缺少第二个参数 CHUNK

player.write(np.fromstring(stream.read(CHUNK),dtype=np.int16),CHUNK)

此外,不确定是否为格式错误。但是player.write需要缩进到for循环中。
根据pyaudio网站,应该使用RATE / CHUNK * RECORD_SECONDS而不是RECORD *RATE/CHUNK,因为python在执行*乘法之前会先执行/除法运算。
for i in range(int(20*RATE/CHUNK)): #do this for 10 seconds
    player.write(np.fromstring(stream.read(CHUNK),dtype=np.int16),CHUNK)

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

最后,您可能希望将 rate 增加到 44100 ,将 CHUNK 增加到 1024 并将 CHANNEL 增加到 2 ,以获得更好的保真度。

谢谢你的帮助。我尝试了你的建议。将“CHUNK”作为“player”对象的参数后,情况好多了,但仍然出现“ALSA lib pcm.c:7843:(snd_pcm_recover) underrun occurred”错误。 我做了一些研究,发现这与缓冲区大小有关。所以我尝试了不同的“CHUNK”大小,最终找到 128 是一个很好的值。它仍然会在开始几秒钟内出现相同的错误,但之后就完美地运行了。 - Sajil C K
我尝试给你的答案点赞,但是我没有足够的声望来这样做。 - Sajil C K
如果Stack Overflow上的回答对您有所帮助,通常会选择它作为最佳答案。这有助于解决问题,并且还可以为您的账户增加积分。请参阅此处以了解如何接受答案:https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work - Anil_M
不需要这样做。如果您查看统计数据,会发现很多问题即使有有效答案也没有得到解决。这会产生“噪音”并扭曲统计数据。我也在指导新手。希望这可以帮到您。 - Anil_M
很多在线示例,包括其他问题,似乎都缺少write()中的参数CHUNK - 添加此参数解决了我的问题。我之前遇到了奇怪/嘈杂的声音问题,与提问者的问题不同。 - FreelanceConsultant

8
下面的代码将使用默认输入设备,将录制的内容输出到默认输出设备。
import PyAudio
import numpy as np

p = pyaudio.PyAudio()

CHANNELS = 2
RATE = 44100

def callback(in_data, frame_count, time_info, flag):
    # using Numpy to convert to array for processing
    # audio_data = np.fromstring(in_data, dtype=np.float32)
    return in_data, pyaudio.paContinue

stream = p.open(format=pyaudio.paFloat32,
                channels=CHANNELS,
                rate=RATE,
                output=True,
                input=True,
                stream_callback=callback)

stream.start_stream()

while stream.is_active():
    time.sleep(20)
    stream.stop_stream()
    print("Stream is stopped")

stream.close()

p.terminate()

这将运行20秒钟并停止。方法回调是您可以处理信号的地方: audio_data = np.fromstring(in_data, dtype=np.float32) return in_data是您将后处理数据发送回输出设备的地方。
请注意,块具有默认参数1024,如PyAudio文档中所述: http://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.PyAudio.open

5

我正在进行类似的项目。我修改了你的代码,现在没有卡顿了。块越大,延迟就越大。这就是为什么我把它保持得很低的原因。

import pyaudio
import numpy as np

CHUNK = 2**5
RATE = 44100
LEN = 10

p = pyaudio.PyAudio()

stream = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, input=True, frames_per_buffer=CHUNK)
player = p.open(format=pyaudio.paInt16, channels=1, rate=RATE, output=True, frames_per_buffer=CHUNK)


for i in range(int(LEN*RATE/CHUNK)): #go for a LEN seconds
    data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
    player.write(data,CHUNK)


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接