pyaudio 中的破解声音正弦音调

cracking sound sine tone in pyaudio

我正在使用 python 和 pyaudio 使用回调方法流式传输纯正弦音,以便稍后通过用户输入调制声音。一切都很好,除了当我 运行 代码时,我得到 1-2 秒的与警告消息相关的破裂的嗡嗡声 ALSA 库 pcm.c:7339:(snd_pcm_recover) 在 运行 下发生 之后,正弦音被正确传输。关于如何消除初始爆裂声的任何提示? 这是播放一秒钟声音的代码

import pyaudio
import time
import numpy as np

CHANNELS = 1
RATE = 44100
freq = 600
CHUNK = 1024
lastchunk = 0
def sine(current_time):
    global freq,lastchunk
    length = CHUNK
    factor = float(freq)*2*np.pi/RATE
    this_chunk = np.arange(length)+lastchunk
    lastchunk = this_chunk[-1]
    return np.sin(this_chunk*factor)

def get_chunk(): 
    data  = sine(time.time())
    return data * 0.1


def callback(in_data, frame_count, time_info, status):
    chunk = get_chunk() * 0.25
    data = chunk.astype(np.float32).tostring()
    return (data, pyaudio.paContinue)

p = pyaudio.PyAudio()

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

stream.start_stream()
time.sleep(1)   




stream.stop_stream()
stream.close()

干杯

PortAudio(PyAudio 背后的库)允许您指定块大小,在 PyAudio 示例中通常称为 CHUNK。如果不指定,默认为 0,在 PortAudio 术语中,这意味着块大小将自动选择,甚至会从回调更改为回调!

要检查这一点,请尝试在回调中打印 frame_count(这是块大小的另一个名称)。我怀疑 PortAudio 一开始选择的块大小太小,当这导致欠载时,它会增加块大小。我说得对吗?

为避免这种情况,您应该从一开始就指定一个固定的块大小,使用:

stream = p.open(..., frames_per_buffer=CHUNK, ...)

... 其中 frames_per_buffer 是块大小的另一个名称。

这也更有意义,因为到目前为止,您在不知道实际块大小的情况下在代码中使用 length = CHUNK

如果这仍然导致数据不足,您可以尝试将块大小进一步增加到 2048

最后,让我冒昧地为我自己的 PortAudio 包装器 sounddevice 模块制作一个不要脸的插件。它基本上与 PyAudio 一样,但更容易安装,恕我直言,它有更好的 API 并且它直接支持 NumPy,无需您进行手动转换。

接受的答案仍然没有提供完美的音频质量。从我听到的(没有测量)来看,正弦中有时会出现 and/or 相位跳变。基于 PyAudio 示例中的代码和可以找到的内容 here 我找到了这个解决方案:

"""PyAudio Example: Play a wave file (callback version)."""

import pyaudio
import time
import math
from itertools import count
import numpy as np

RATE = 96000

# More efficient calculation but period = int(framer... causes high granularity for higher frequencies (15kHz becoming 16kHz for instance)
# def sine_wave(frequency=1000, framerate=RATE, amplitude=0.5):
#     period = int(framerate / frequency)
#     amplitude = max(min(amplitude, 1), 0)
#     lookup_table = [float(amplitude) * math.sin(2.0 * math.pi * float(frequency) *
#                                                 (float(i % period) / float(framerate))) for i in xrange(period)]
#     return (lookup_table[i % period] for i in count(0))

def sine_wave(frequency=440.0, framerate=RATE, amplitude=0.5):
    amplitude = max(min(amplitude, 1), 0)
    return (float(amplitude) * math.sin(2.0*math.pi*float(frequency)*(float(i)/float(framerate))) for i in count(0))

sine = [sine_wave(150), sine_wave(1500), sine_wave(15000)]

# instantiate PyAudio (1)
p = pyaudio.PyAudio()

# define callback (2)
def callback(in_data, frame_count, time_info, status):
    wave = sine[0]
    data = [wave.next()]
    for i in range(frame_count - 1):
        data.append(wave.next())
    ret_array =np.array(data).astype(np.float32).tostring()
    return (ret_array, pyaudio.paContinue)

# open stream using callback (3)
stream = p.open(format=pyaudio.paFloat32,
                channels=1,
                rate=RATE,
                frames_per_buffer=1024,
                output=True,
                stream_callback=callback)

# start the stream (4)
stream.start_stream()

# Insert your own solution to end the sound
time.sleep(3)

# stop stream (6)
stream.stop_stream()
stream.close()

# close PyAudio (7)
p.terminate()

这应该可以播放正弦波直到你的硬件死机或下一次断电...但我只测试了半小时;-)