在Python中截取波形顶部

2 投票
1 回答
2905 浏览
提问于 2025-04-18 04:11

我正在用Python生成音调,并把它们写入一个WAV文件:

def generateTone(note):
    # generates a sine wave in a note's frequency
    tone = []
    framerate = 44100
    noteLookup = {'d':293.665,'a':440,'g':391.995}
    frequency = noteLookup[note]
    for i in range(framerate):
        tone.append(math.sin(2.0*math.pi*float(frequency)*(float(i)/float(framerate))))
    return tone

不过,正如你可能想象的,这些纯正的正弦波听起来并不太好。根据我了解到的,'削波'(clipping)这些正弦波的顶部,最好是让它们的边角光滑,而不是突然截断,这样会显著改善这些音调的声音。这个想法是这样的:

削波的波形
(来源: geofex.com)

话虽如此,我不知道该如何去做。

那么,怎样才能设置一个阈值,让上面生成的正弦波在这个阈值处平滑地削波,像图中那样?

1 个回答

3

最简单的方法是建立一个映射关系,来实现“软削波”。也就是说,数值在接近某个阈值之前是线性的,接近阈值后会被压缩,直到达到一个更大的阈值。

补充一下:这个想法是不错的,但实现的效果还有待提高。我想了想,换成了更好的方法。

我们的目标是让压缩的开始和硬削波的限制之间有一个平滑的过渡。线性变换的斜率是1,而削波信号的斜率是0。从斜率1平滑过渡到斜率0,最简单的方法是使用四分之一的正弦波,所以更新后的函数是基于sin的。

from math import *

hard_limit = 32767
linear_limit = 23197  # -3 dB
clip_limit = linear_limit + int(pi/2 * (hard_limit - linear_limit))

def soft_clip(n):
    amplitude, sign = abs(n), 1 if n >= 0 else -1
    if amplitude <= linear_limit:
        return n
    if amplitude >= clip_limit:
        return hard_limit * sign
    scale = hard_limit - linear_limit
    compression = scale * sin(float(amplitude - linear_limit) / scale)
    return (linear_limit + int(compression)) * sign

这里有一个例子,展示了在一个振幅为40000的正弦波上应用上述方法,背景中是未削波的版本。

软削波演示

撰写回答