在pyaudio中破解正弦音

时间:2016-11-02 11:19:19

标签: python audio alsa pyaudio

我使用python和pyaudio使用回调方法传输纯正弦音,以便稍后通过用户输入调制声音。一切都很好,除了当我运行代码时,我得到1-2秒的与警告信息相关的破解 - 嗡嗡声 ALSA lib 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()

干杯

2 个答案:

答案 0 :(得分:2)

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,无需你进行手动转换。

答案 1 :(得分:0)

接受的答案仍然没有提供完美的音质。从我所听到的(没有测量)来看,正弦曲线中有时会出现辍学和/或相位跳跃。基于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()

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