ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

如何在没有相位跳跃的情况下连接正弦波

2019-07-31 04:05:00  阅读:254  来源: 互联网

标签:python numpy signal-processing pyaudio sine


我需要创建一个生成给定频率的正弦波并使用pyaudio(阻塞模式)播放它们的python脚本,我还需要能够在运行时更改此频率,调制它并使用pyqtgraph绘制它.现在我有一个生成数据块的线程,我的’连接’那些正弦的方法是得到fft然后计算角度(numpy.angle),将它存储在一个变量中并用它作为相位偏移到下一个大块,但我没有得到我预期的结果,也许我错过了什么或混合起来.

import matplotlib.pyplot as plt
import numpy as np
import pyaudio

#-----------------------
CHUNK = 1024
RATE =  44100
CHANNELS = 2
FORMAT = pyaudio.paFloat32
#-----------------------

samples = int(CHUNK)
t = np.arange(samples) / RATE
con = 0


def generate_sine(a: float = 0.5, freq: float = 440.0):

    global con

    sine = a * np.sin(2.0 * np.pi * freq * t + con)

    # get the angle of the wave

    phase = np.angle(np.fft.fft(sine))

    # update ref var to generate subsequent sines
    # begining where the last ended

    con = phase[-1]

    return sine


def play_sine(data):

    pa = pyaudio.PyAudio()

    stream = pa.open(format=FORMAT,
                         channels=CHANNELS,
                         rate=RATE,
                         input=False,
                         output=True,
                         frames_per_buffer=CHUNK)

    stream.write(np.array(data).astype(np.float32).tostring())

    stream.close()

if __name__ == '__main__':

    f = 80

    chunks = generate_sine(freq=f)

    for i in range(0,4):

        chunks = np.concatenate((chunks, generate_sine(freq=f)))

    #for i in range(0,10):

    #play_sine(chunks)

    plt.plot(chunks)

    plt.show()

演示情节

您可以在链接的图像中看到x = 1024,x = 2048周围存在不连续性,依此类推.

解决方法:

你正在生成你的信号

    a * sin(2πf * t + con)

其中t的范围超过[0 .. CHUNK / RATE).

当您启动下一个块时,t将重置为零.要生成连续波形,您需要修改con以生成与上一个样本相同的结果相位值.

使用FFT不会起作用,因为您生成的信号不是采样窗口的精确倍数,而且您实际上对采样窗口末端的相位感兴趣,而不是开始时的相位.

相反,您只需要在t = t_end处生成函数的相位值,模2π.

即,你可以简单地使用:

con = 2.0 * np.pi * f * CHUNK/RATE + con

但是这个值会增长,如果你将很多块连接在一起,频率很高,可能会导致最终的数值问题.由于正弦函数是周期性的,您只需要将结束阶段标准化为0到2π范围:

con = math.fmod(2.0 * np.pi * f * CHUNK/RATE + con, 2.0 * np.pi)

如果将生成函数修改为:

    a * sin(2π * (f * t + con))

然后con表示从前一个卡盘向前移动的完整循环的分数,并且可以避免模数除以2π,这可能会略微提高精度.

con = math.modf(f * CHUNK/RATE + con)[0]

尝试更清楚的解释:

注意:此技术仅适用,因为您确切知道前一个块的生成方程,并生成以下块.如果这些条件中的任何一个发生变化,您将需要一种不同的技术来匹配这些块.

使用以下序列的sin()生成上一个块:

2πf₁*0/RATE+con₁, 2πf₁*1/RATE+con₁, ..., 2πf₁*1022/RATE+con₁, 2πf₁*1023/RATE+con₁

应该清楚的是,为了平滑过渡到下一个块,该块应该以2πf1* 1024 / RATE con 1的sin()开始.

下一个块以2πf2* 0 / RATE con 2的sin()开始.

因此,如果符合以下条件,我们将顺利过渡:

2πf₂*0/RATE + con₂ = 2πf₁*1024/RATE + con₁

要么

     0      + con₂ = 2πf₁*1024/RATE + con₁

要么

              con₂ = 2πf₁*1024/RATE + con₁

可以在generate_sine函数中写入:

con = 2.0 * np.pi * f * CHUNK/RATE + con

在我的上述答案中,这是“无处不在”的等式.从那时起,由于sin()函数是2π周期性的,我只是执行模2π约简,以使sin()的参数不受限制地增长,导致数值不准确.

希望能让事情更加清晰.

标签:python,numpy,signal-processing,pyaudio,sine
来源: https://codeday.me/bug/20190731/1587061.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有