可能只阻止PyAudio的stream.write函数?

时间:2016-09-20 00:48:25

标签: python audio blocking pyaudio

我试图编写一个数字波表合成器,它最终会包含实时音高/音量输入,但我仍然坚持让音频正常工作。基本思想是程序检查音调和音量输入大约每秒100次,使用scipy的插值函数来改变记录的波形周期的音高和音量,然后写入由改变的波形组成的信号对Pyaudio流重复持续至少0.01秒的时间。我最初尝试使用pygame,但是pygame很容易崩溃。

尝试使用回调模式和sleep()来计算每个周期的时间,由于音频悬崖或周期信号重叠,导致声音非常波动和粗糙,甚至当我测试那些不会有音频悬崖的音高时#39 ;在原始波形中不是一个奇怪的高音分量。

使用阻止模式可以产生更忠实的信号,因为不需要对周期进行计时;上一个周期必须在执行恢复之前完成。然而,产生每个周期信号的计算确实导致跳过,改变了波形的音色。变化很小,因为计算时间远小于0.01s周期,但如果我想增加程序的音高/音量更新频率会更糟。

有没有办法让stream.write()函数返回ASAP并允许信号计算但是在下一个stream.write()暂停执行直到所有样本都被播放?

import scipy as sp
from scipy import interpolate
from time import sleep, perf_counter
import pyaudio

def pyaudioplay():
    # sampled waveform and linear interpolation
    waveform = sp.array([ 884,   8343,  18370,  27897,  32767,  30861,  23163,  12692,
         2225,  -6780, -13815, -17404, -16557, -12404,  -6286,    792,
         7448,  12458,  15086,  15770,  15699,  15031,  13224,  10705,
         8734,   7790,   7621,   7800,   7784,   6964,   5444,   4174,
         4435,   6335,   8696,  10156,  10460,  10352,   9950,   9006,
         7594,   5835,   3680,    -21,  -6574, -15732, -25302, -31550,
       -31968, -25916, -15352,  -3702,   6536,  14429,  19103,  19244,
        15471,   9717,   2882,  -4359, -10835, -15449, -17816, -18788,
       -18685, -16665, -12778,  -8588,  -5434,  -3593,  -2882,  -3311,
        -4370,  -5157,  -5531,  -6112,  -7410,  -9000, -10042, -10042,
        -9206,  -8213,  -7268,  -6188,  -4489,  -1167], dtype=sp.int16)
    size = len(waveform)
    wavefunc = interpolate.interp1d(range(size), waveform, kind = 'linear', axis = -1, copy = True, bounds_error = False, fill_value = (waveform[0], waveform[-1]))
    # pitch and volume 1.5s time series for testing
    length = 150; sample_pitch = 512.79069767441854; max_volume = 1.
    t = sp.linspace(0., 3*sp.pi, num = length, endpoint = False)
    pitches = (sample_pitch*3/4)+(sample_pitch*1/4)*sp.cos(t) # pitch goes down and up
    volumes = max_volume*sp.ones(length) # hold at same volume
    # PyAudio stream: blocking mode
    p = pyaudio.PyAudio()
    stream = p.open(format = pyaudio.paInt16,
                    channels = 1,
                    rate = 44100,
                    output = True,
                    frames_per_buffer = 0) # also specifies frame_count int for callback
    updatePeriod = 441
    begin = perf_counter()
    record = 0
    index = 0
    while index < length:
        start = perf_counter()
        # get pitch and volume
        Apitch = pitches[index]
        Avolume = volumes[index]
        # period with altered width and amplitude to change pitch and volume
        Aperiod = (Avolume*wavefunc(sp.arange(0, size, Apitch/sample_pitch))).astype(sp.int16)
        cycle = sp.tile(Aperiod, 1+updatePeriod//len(Aperiod)).tobytes()
        index += 1
        runtime = perf_counter() - start
        if runtime > record:
            record = runtime
        stream.write(cycle)
    print('total time:', perf_counter()-begin)
    print('largest runtime lag:', record)
    stream.stop_stream()
    stream.close()
    p.terminate()

0 个答案:

没有答案