如何使用内部生成的输入实现Pyaudio回调

问题描述 投票:0回答:2

我具有各种频率的音频音调,用于不同的时间长度。我试图将这些输入切成块(无论输入频率如何,大小均相同),并在输出中清楚地再现这些块。当我使用阻止方法时,例如stream.write(),我得到一个干净的输出,但没有太多处理能力可以处理其他任务。因此,我已使用callback()函数将代码转换为非阻塞方法,如下所示。

这篇文章之后,我尝试过对代码进行模式化:https://stackoverflow.com/a/22354593/12076263但这根本不起作用。

# Generate a 250 Hz tone in chunks amounting to 100 chunks per second,
# using a non-blocking method.
#
# How does one fit a 250 Hz wave into 100 Hz packets?
# Slice up a sine wave into segments of two-and-a-half waves apiece:
#
#               |              |              |              |
#  _     _     _|    _     _   | _     _     _|    _     _   | 
# / \   / \   / \   / \   / \   / \   / \   / \   / \   / \   
#    \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/
#               |              |              |              |
#  <----X1----> | <----X2----> | <----X1----> | <----X2----> |
#              
# Play back each segment in alternating fashion.
# This simulates continuous audio where zero crossings do not necessarily
# occur at the edge of a chunk.

import numpy as np
import pyaudio
import time

# based on examples from https://people.csail.mit.edu/hubert/pyaudio/docs/
def callback(in_data, frame_count, time_info, status):
    waveData = Y.tobytes()
    return (waveData, pyaudio.paContinue)

Y = np.zeros((441), dtype = np.int16)   # initialize audio array Y
p = pyaudio.PyAudio()
stream = p.open(format = 8,             # 8 is code for int16 format
           channels = 1,
           rate = 44100,
           frames_per_buffer = 441,
           stream_callback = callback, # !! COMMENT OUT to enable blocking
           output = True)

stream.start_stream()

X1 = np.linspace(0, 5 * np.pi, num = 441, endpoint = False)
Y1 = (5000 * np.sin(X1)).astype(np.int16) # Generate two-and-a-half waves

X2 = np.linspace(5 * np.pi, 10 * np.pi, num = 441, endpoint = False)
Y2 = (5000 * np.sin(X2)).astype(np.int16) # The "other" two-and-a-half waves

# Play the two wave segments one after the other
# Result should be a clean 250 Hz tone
while True:
    Y = Y1                              # First set of waves
    time.sleep(0.0015)                  # This 1.5 millisecond delay
                                        #   simulates other stuff happening
                                        #   within the While loop.
    #stream.write(Y.tobytes())          # !! UNCOMMENT to use blocking method
    Y = Y2                              # Second set of waves
    time.sleep(0.0015)                  # More miscellaneous delays
    #stream.write(Y.tobytes())          # !! UNCOMMENT to use blocking method

[预期无失真(随着数字音频的发展)正弦波,并产生了严重失真的声音,就像一个巨大的电影怪兽的吼声。通常没有错误消息,但是如果增加time.sleep值,则可能发生欠载。如果完全删除了time.sleep语句,问题就消失了,但是将使用该语句的完整应用程序每个块至少要有1.5毫秒的处理负载。

python callback pyaudio
2个回答
0
投票

要部分回答我自己的问题,关键是使用队列在主循环和回调函数之间传输数据。现在声音没有失真。但是,阻塞似乎对While循环中运行的每个语句的执行时间有重大影响。当然,整个循环的速度会变慢,但是它会以正确的刷新率循环。我希望看到语句inwith循环以正常速度运行,但是它们每个都以正常速度的1/5运行。

# Generate a 250 Hz tone in chunks amounting to 100 chunks per second,
# using a non-blocking method.
#
# How does one fit a 250 Hz wave into 100 Hz packets?
# Slice up a sine wave into segments of two-and-a-half waves apiece:
#
#               |              |              |              |
#  _     _     _|    _     _   | _     _     _|    _     _   | 
# / \   / \   / \   / \   / \   / \   / \   / \   / \   / \   
#    \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/   \_/
#               |              |              |              |
#  <----X1----> | <----X2----> | <----X1----> | <----X2----> |
#              
# Play back each segment in alternating fashion.
# This simulates continuous audio where zero crossings do not necessarily
# occur at the edge of a chunk.

import numpy as np
import pyaudio
import time
import queue

# based on examples from https://people.csail.mit.edu/hubert/pyaudio/docs/
def callback(in_data, frame_count, time_info, status):
    Y = qu.get()
    waveData = Y.tobytes()
    return (waveData, pyaudio.paContinue)

qu = queue.Queue(maxsize = 1)           # 1 complete buffer (of length 441)
Y = np.zeros((441), dtype = np.int16)   # initialize audio array Y
p = pyaudio.PyAudio()
stream = p.open(format = 8,             # 8 is code for int16 format
           channels = 1,
           rate = 44100,
           frames_per_buffer = 441,
           stream_callback = callback,
           output = True)

stream.start_stream()

X1 = np.linspace(0, 5 * np.pi, num = 441, endpoint = False)
Y1 = (5000 * np.sin(X1)).astype(np.int16) # Generate two-and-a-half waves

X2 = np.linspace(5 * np.pi, 10 * np.pi, num = 441, endpoint = False)
Y2 = (5000 * np.sin(X2)).astype(np.int16) # The "other" two-and-a-half waves

# Play the two wave segments one after the other
# Result should be a clean 250 Hz tone
while True:
    qu.put(Y1)                          # First set of waves
    time.sleep(0.0015)                  # This 1.5 millisecond delay
                                        #   simulates other stuff happening
                                        #   within the While loop.
    qu.put(Y2)                              # Second set of waves
    time.sleep(0.0015)                  # More miscellaneous delays

0
投票

我的问题的另一部分是如何加快执行速度,因为while循环内发生了很多处理。但是,某些事情正在使时间紧迫的代码减慢大约5倍。代码的“ while”部分已被下面的代码替换。如果将qu.put语句注释掉,则t2减去t1快5倍,这很奇怪,因为qu.put在时间关键代码测量间隔之外。

import time
from multiprocessing import Process

def whileLoop():
    while True:
        qu.put(Y1)
        t1 = time.time()
        # time-critical processing code goes here
        # must run in < 10ms to prevent audio underrun
        t2 = time.time()

        qu.put(Y2)
        t3 = time.time()
        # more time-critical processing code goes here
        # must run in < 10ms to prevent audio underrun
        t4 = time.time()

if __name__ == '__main__':
    pr = Process(target = whileLoop)
    pr.start()
    pr.join()
© www.soinside.com 2019 - 2024. All rights reserved.