使用 python 为音调添加随机噪声

Add random noise to tone using python

我正在尝试检测录音中突然发出的巨响。我发现这样做的一种方法是创建音频的频谱图并添加每一列的值。通过绘制每列中值的总和,每次突然出现巨响时,都可以看到尖峰。问题是,在我的用例中,我需要在录制音频时播放蜂鸣音(频率为 2350 Hz)。蜂鸣声的频谱图如下所示:

如您所见,在这个蜂鸣声(这是一种频率为 2350 Hz 的简单音调)的开始和结束处,存在其他频率,但我未能成功去除。在对频谱图的列求和时,在蜂鸣声的开始和结束时,这些不需要的频率会导致尖峰。我想避免这种情况,因为我不想让我的哔哔声被检测为突然的巨响。请参阅下面的频谱图以供参考:

这是频谱图中每列总和的图形:

显然,我想避免在我的算法中出现误报。所以我需要一些方法来消除由哔哔声开始和结束引起的尖峰。到目前为止,我的一个想法是在上面的蜂鸣声谱图中的 2350 Hz 线以下添加具有高于 and/or 的低分贝值的随机噪声。理想情况下,这会创建一个听起来与原始音调非常相似的音调,但当我将列中的所有值相加时,它不会创建尖峰,而是会创建更多的平台。这个想法是解决我的问题的可行方法吗?如果是这样,我将如何使用 python 来创建具有随机噪声的蜂鸣声?对于我忽略的问题,还有其他更简单的解决方案吗?

目前,我正在使用以下代码生成提示音:

import math
import wave
import struct

audio = []
sample_rate = 44100.0

def append_sinewave(
        freq=440.0, 
        duration_milliseconds=500, 
        volume=1.0):
    """
    The sine wave generated here is the standard beep.  If you want something
    more aggresive you could try a square or saw tooth waveform.   Though there
    are some rather complicated issues with making high quality square and
    sawtooth waves... which we won't address here :) 
    """ 

    global audio # using global variables isn't cool.

    num_samples = duration_milliseconds * (sample_rate / 1000.0)

    for x in range(int(num_samples)):
        audio.append(volume * math.sin(2 * math.pi * freq * ( x / sample_rate )))

    return


def save_wav(file_name):
    # Open up a wav file
    wav_file=wave.open(file_name,"w")

    # wav params
    nchannels = 1

    sampwidth = 2

    # 44100 is the industry standard sample rate - CD quality.  If you need to
    # save on file size you can adjust it downwards. The stanard for low quality
    # is 8000 or 8kHz.
    nframes = len(audio)
    comptype = "NONE"
    compname = "not compressed"
    wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))

    # WAV files here are using short, 16 bit, signed integers for the 
    # sample size.  So we multiply the floating point data we have by 32767, the
    # maximum value for a short integer.  NOTE: It is theortically possible to
    # use the floating point -1.0 to 1.0 data directly in a WAV file but not
    # obvious how to do that using the wave module in python.
    for sample in audio:
        wav_file.writeframes(struct.pack('h', int( sample * 32767.0 )))

    wav_file.close()

    return


append_sinewave(volume=1, freq=2350)
save_wav("output.wav")

不是真正的答案 - 更多的是问题。

您要求扬声器瞬间从静止波变为正弦波 - 这很难做到(尽管频率不是那么高)。如果它确实管理它,那么接收到的信号应该是大礼帽和正弦波的卷积(有点像你所看到的,但是没有一些数据并且不知道你在为频谱图做什么很难说).

在任何一种情况下,您都可以通过平滑音调的开始和结束来检查这一点。类似这样的音调生成:

tr = 0.05  # rise time, in seconds
tf = duration_milliseconds / 1000  # finish time of tone, in seconds

for x in range(int(num_samples)):
    t = x / sample_rate  # Time of sample in seconds
    
    # Calculate a bump function
    bump_function = 1
    if 0 < t < tr:  # go smoothly from 0 to 1 at the start of the tone
        tp = 1 - t / tr
        bump_function = math.e * math.exp(1/(tp**2 - 1))
    elif tf - tr < t < tf:  # go smoothly from 1 to 0 at the end of the tone
        tp = 1 + (t - tf) / tr
        bump_function = math.e * math.exp(1/(tp**2 - 1))

    audio.append(volume * bump_function * math.sin(2 * math.pi * freq * t))

您可能需要稍微调整一下上升时间。使用这种形式的碰撞功能,您知道从开始后的 tr 到结束前的 tr 之间有一个完整的音量。存在许多其他功能,但如果这可以平滑频谱图中的 start/stop 效果,那么您至少知道它们为何存在。并且预防通常比尝试在 post-processing.

中消除影响更好。