【问题标题】:Gaps in between waveforms after generating wave file using numpy and scipy使用 numpy 和 scipy 生成波形文件后波形之间的间隙
【发布时间】:2019-08-30 23:44:14
【问题描述】:

我使用 python 3.7、numpy 和 scipy 制作了一个程序,它使用 pi 的数字生成波形,并将它们拼接在一起以制作一首“歌曲”。我唯一的问题是每个音符之间都有间隙。

我尝试过使用数学函数使每个音符的波浪逐渐消失,我尝试让音符重叠一点(运气不好),还有一些没有做任何事情的疯狂事情...

import numpy as np
from scipy.io.wavfile import write

pi = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848"
piarray = list(pi)
piarray.remove(".")

print(piarray)

# Samples per second
sps = 44100

# Frequency / pitch of the sine wave
freq_hz = 440.0

# Duration
duration_s = 0.2

each_sample_number = np.arange(duration_s * sps)

for i in range(len(piarray)):
    if(piarray[i] == "0"):
        freq_hz = 277.18
    elif(piarray[i] == "1"):
        freq_hz = 311.13
    elif(piarray[i] == "2"):
        freq_hz = 369.99
    elif(piarray[i] == "3"):
        freq_hz = 415.30
    elif(piarray[i] == "4"):
        freq_hz = 466.16
    elif(piarray[i] == "5"):
        freq_hz = 554.37
    elif(piarray[i] == "6"):
        freq_hz = 622.25
    elif(piarray[i] == "7"):
        freq_hz = 739.99
    elif(piarray[i] == "8"):
        freq_hz = 830.61
    else:
        freq_hz = 932.33

    waveform = np.sin(2 * np.pi * each_sample_number * freq_hz / sps)*0.3
    #The line above and below this one make an individual note.
    waveform_integers = np.int16(waveform * 32767)

    if(i == 0):
        waveformc = waveform_integers
        print(waveformc)
    else:
        waveformc = np.append(waveformc, waveform_integers, axis=None)

write('song.wav', sps, waveformc)
print("DONE")


我已尝试寻找此特定问题的解决方案,但在任何地方都没有找到任何相关内容。我只是希望波形文件在每个音符之间没有间隙,但是有。感谢您能给我的任何帮助!

【问题讨论】:

    标签: python numpy audio scipy wave


    【解决方案1】:

    波形之间没有任何间隙。您可以从 Reaper 的结果视图中看到您有连续的声音:

    每次开始新音符时,波形中都会出现不连续性。每次音符更改时,都会听到咔哒声或爆裂声。由于每个音符的波形都是根据底层数据结构计算的,因此它们都会在 0 处有一个过零,然后从那里快速地相互得出一个相位。

    要解决此问题,您可以尝试适当地逐渐淡入/淡出每个声音,或者确保跟踪波形的相位并在音符变化时保持一致。

    对于衰减函数,您需要类似于(frames - each_sample_number) / frames)**n 的内容,以便最终达到零。您可以尝试使用此功能,看看它如何影响声音的持续时间和音符之间的感知剪辑。

    import numpy as np
    from scipy.io.wavfile import write
    
    pi = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848"
    piarray = list(pi)
    piarray.remove(".")
    
    print(piarray)
    
    # Samples per second
    sps = 44100
    
    # Frequency / pitch of the sine wave
    freq_hz = 440.0
    
    # Duration
    duration_s = 0.2
    frames = duration_s * sps # counting how many frames for a note
    each_sample_number = np.arange(duration_s * sps)
    
    
    
    for i in range(len(piarray)):
        if(piarray[i] == "0"):
            freq_hz = 277.18
        elif(piarray[i] == "1"):
            freq_hz = 311.13
        elif(piarray[i] == "2"):
            freq_hz = 369.99
        elif(piarray[i] == "3"):
            freq_hz = 415.30
        elif(piarray[i] == "4"):
            freq_hz = 466.16
        elif(piarray[i] == "5"):
            freq_hz = 554.37
        elif(piarray[i] == "6"):
            freq_hz = 622.25
        elif(piarray[i] == "7"):
            freq_hz = 739.99
        elif(piarray[i] == "8"):
            freq_hz = 830.61
        else:
            freq_hz = 932.33
    
        # added fall off feature
        waveform = (((frames - each_sample_number) / frames)**0.5) * np.sin(
                            np.pi+ 2 * np.pi * each_sample_number * freq_hz / sps)*0.3
    
        #The line above and below this one make an individual note.
        waveform_integers = np.int16(waveform * 32767)
    
        if(i == 0):
            waveformc = waveform_integers
            print(waveformc)
        else:
            waveformc = np.append(waveformc, waveform_integers, axis=None)
    
    write('song.wav', sps, waveformc)
    print("DONE")
    

    可以在波形上看到当前设置的结果:

    【讨论】:

    • 就像我说的,我试图让它淡入/淡出。我尝试使用数学函数 sin(x)/x 使其淡出,但并没有解决问题。你知道更好的方法吗?我搜索了对淡出的正弦函数进行建模的方法,但 sin(x)/x 是我发现的唯一真正有用的方法。我也试过 sin(x)/(x^1/2),但也没用。
    • 我为你添加了一个淡出方法,它应该给你一个很好的起点来尝试那里的其他功能。
    • @Spencer1O1 我用淡出方法更新了。关键是它应该在每个音符到达它的结尾时实际上达到零,否则它不会解决问题。
    • 感谢实现淡出功能!
    【解决方案2】:

    我为频率添加了一个“校正因子”,以确保每个波都以零幅度结束,并且不会出现不连续性。它会稍微改变频率,但不超过 1%。我就是这样做的:

    cor_fac = round(each_sample_number[-1] * freq_hz / sps)/(each_sample_number[-1] * freq_hz / sps)
    cor_factors.append(cor_fac)
    waveform = np.sin(2 * np.pi * each_sample_number * freq_hz / sps * cor_fac)*0.3
    

    我认为它解决了问题。

    如果频率上的这种变化是不可接受的,您可以尝试更改样本阵列的开始,使其以与最后一个波形完成的相同幅度开始。

    我会尝试做并在这里发布。

    让我知道它对你有用。

    编辑: 代码不改变声音频率:

    import numpy as np
    from scipy.io.wavfile import write
    import matplotlib.pyplot as plt
    import wave
    import sys
    
    
    pi = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848"
    piarray = list(pi)
    piarray.remove(".")
    
    print(piarray)
    
    # Samples per second
    sps = 44100
    
    # Frequency / pitch of the sine wave
    freq_hz = 440.0
    
    # Duration
    duration_s = 0.2
    
    last_amp = 0
    cor_factors = []
    direction_down = False
    
    for i in range(len(piarray)):
        if(piarray[i] == "0"):
            freq_hz = 277.18
        elif(piarray[i] == "1"):
            freq_hz = 311.13
        elif(piarray[i] == "2"):
            freq_hz = 369.99
        elif(piarray[i] == "3"):
            freq_hz = 415.30
        elif(piarray[i] == "4"):
            freq_hz = 466.16
        elif(piarray[i] == "5"):
            freq_hz = 554.37
        elif(piarray[i] == "6"):
            freq_hz = 622.25
        elif(piarray[i] == "7"):
            freq_hz = 739.99
        elif(piarray[i] == "8"):
            freq_hz = 830.61
        else:
            freq_hz = 932.33
    
        # cor_fac = round(each_sample_number[-1] * freq_hz / sps)/(each_sample_number[-1] * freq_hz / sps)
        # cor_factors.append(cor_fac)
    
        start = np.arcsin(last_amp/0.3)    
        if direction_down:
            start = np.pi - start   
        start = start/(2 * np.pi * freq_hz / sps)
    
        each_sample_number = np.arange(start, start + duration_s * sps)
        waveform = np.sin(2 * np.pi * each_sample_number * freq_hz / sps)*0.3
        print(waveform[0]-last_amp)
        last_amp = waveform[-1]
        direction_down = waveform[-1]<waveform[-2]
        #The line above and below this one make an individual note.
        waveform_integers = np.int16(waveform * 32767)
    
        if(i == 0):
            waveformc = waveform_integers
            print(waveformc)
        else:
            waveformc = np.append(waveformc, waveform_integers, axis=None)
    
    write('song_2.wav', sps, waveformc)
    print("DONE")
    

    【讨论】:

    • 在我遇到的大多数情况下,改变声音的频率是不可接受的。在音乐中,我认为 1% 的频率变化大约是一个音符的 20 美分,这足以打扰演奏者或人群。如果你有一种方法来匹配相位,那会更有用。
    • 这行得通,但我认为 Mike Sperry 有一个更好的解决方案,因为频率根本没有改变。不过感谢您的帮助!这是个好主意!
    • 我添加了更改以匹配阶段,但我不确定它是否完美运行。无论如何我都会把它留在这里。
    • 我喜欢相位匹配。确保你知道波浪是向上还是向下。我将使用该部分添加对您的代码的编辑。当我尝试时仍然有些点击,但比没有相位匹配有所改进。
    • 现在听起来好多了。我找不到相位匹配有什么问题,但肯定是这样。感谢您的编辑!
    猜你喜欢
    • 2015-11-22
    • 2016-05-19
    • 2019-01-20
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 2011-11-16
    • 1970-01-01
    • 2014-11-08
    相关资源
    最近更新 更多