如何消除频率之间的'click'?



我编写了一个生成频率的脚本,并将它们写入波形文件。我的问题是在每个频率之前和之后都有"咔嗒"声。

我知道出现这种点击,然后正弦波就坏了。但是我怎样才能避免这种情况呢?我想创建一个这样的欧元信号发生器: https://wellen.uber.space/doku.php/user:vicuni:amateurfunk:software:eurosignal

链接文件的问题在于它已经有 10 多年的历史了,我无法让它在我的 Python 上运行。我正在使用Python 3.8

这是我的Python脚本:

import structimport numpy as np
from scipy import signal as sg
import wave
# Parameters
sampleRate = 44100
frequency = 400
duration = 1

# define wave file
file = wave.open('test.wav', 'w')
file.setnchannels(1)
file.setsampwidth(2)
file.setframerate(sampleRate)

def generate(output, freq, dur):
# create sine wave
samples = dur*sampleRate
x = np.arange(samples)
sineWave = (32767.0*np.sin(2 * np.pi * freq * x / sampleRate))
for i in sineWave:
value = i
data = struct.pack('<h', int(value))
output.writeframesraw(data);

for x in range(1):
generate(file, 1153.1, 1.0)
generate(file, 1062.9, 0.1)
generate(file, 1153.1, 1.0)
generate(file, 1062.9, 0.1)
generate(file, 1153.1, 1.0)
generate(file, 1062.9, 0.1)
generate(file, 510.7, 0.1)
generate(file, 832.5, 0.1)
generate(file, 652.0, 0.1)
generate(file, 554.0, 0.1)
generate(file, 470.8, 0.1)
generate(file, 510.7, 0.1)
generate(file, 0.0, 0.22)
generate(file, 1153.1, 0.22)
generate(file, 510.7, 0.1)
generate(file, 832.5, 0.1)
generate(file, 652.0, 0.1)
generate(file, 554.0, 0.1)
generate(file, 470.8, 0.1)
generate(file, 510.7, 0.1)
generate(file, 0.0, 0.22)
generate(file, 1153.1, 0.22)
generate(file, 510.7, 0.1)
generate(file, 832.5, 0.1)
generate(file, 652.0, 0.1)
generate(file, 554.0, 0.1)
generate(file, 470.8, 0.1)
generate(file, 510.7, 0.1)
generate(file, 0.0, 0.22)
generate(file, 1153.1, 0.22)
generate(file, 510.7, 0.1)
generate(file, 832.5, 0.1)
generate(file, 652.0, 0.1)
generate(file, 554.0, 0.1)
generate(file, 470.8, 0.1)
generate(file, 510.7, 0.1)


file.close();

Python 3 的eurosignal generator转换代码如下:

#!/usr/bin/env python
# eurosig.py -- generates a random or predefined Eurosignal as WAV file
# Written Feb 2005 by Gerhard Grimm <gerhard dot grimm at gmx dot net>
# Donated to the Public Domain. Have fun!
import sys
from math import *
import struct
import random
class EurosignalEncoder:
"Converts a sequence of 6-digit numbers to a Eurosignal PCM waveform"
freq = [979.8, 903.1, 832.5, 767.4, 707.4, 652.0, 601.0, 554.0, 510.7, 470.8, 1062.9, 1153.1]
duration = 0.1
pause = [10, 11, 11, 11, 11, 11, 11, 11]
def __init__(self, Rate, Load, Limit, NumFile, WavFile):
"Initialize internal encoder state and set parameters"
self.rate = Rate        # samples per second
self.load = Load        # 0.0 .. 1.0
self.limit = Limit      # how many numbers to produce
self.count = 0
self.numfile = NumFile  # text file with numbers, read in player mode,
# written in random generator mode
self.wavfile = WavFile  # WAV file (PCM), always written
self.phase = 0.0
self.lastnum = self.pause
self.lastcount = 0
def EncodeDigit(self, Digit):
"Encode a single digit to PCM. 10 = repetition, 11 = pause."
pcm = b''
samples = int(self.duration * self.rate)
omega = 2 * pi * self.freq[Digit]
for n in range(samples):
t = n / float(self.rate)
sample = 32000 * sin(omega * t + self.phase)
pcm += struct.pack('<h', int(sample))
endphase = samples / float(self.rate) * omega + self.phase
self.phase = fmod(endphase, 2 * pi)
return pcm
def DigitSequence(self, Number):
"Encode a number to a digit sequence, looking for repetitions."
if len(Number) < 6:
if self.lastcount == 3:
self.lastnum = self.pause
self.lastcount += 1
return self.lastnum
digits = []
for i in range(6):
digit = int(Number[i])
if (i > 0) and (digits[-1] == digit):
digit = 10
digits += [digit]
digits += [11, 11]
self.lastnum = digits
self.lastcount = 1
return digits
def WAVHeader(self):
"Provide the WAV header for the selected sample rate, 16 bit, mono."
return struct.pack('<4sL4s4sLHHLLHH4sL', b'RIFF', 36, b'WAVE', b'fmt ',
16, 1, 1, self.rate, 2 * self.rate,
2, 16, b'data', 0)
def NextNumber(self):
"Get the next number from input file or random generator."
if self.numfile.mode == 'r':
# Player mode
return self.numfile.readline()
# Random generator mode
self.count += 1
if self.count > self.limit:
return ''
result = ''
if random.random() < self.load:
for i in range(6):
result += chr(random.randint(48, 57))
result += 'n'
self.numfile.write(result)
return result
def DoGenerate(self):
"Perform the actual encoding."
self.wavfile.write(self.WAVHeader())
number = self.NextNumber()
while number:
for digit in self.DigitSequence(number):
self.wavfile.write(self.EncodeDigit(digit))
number = self.NextNumber()
# Adjust sizes in WAV header
size = self.wavfile.tell()
self.wavfile.seek(4)
self.wavfile.write(struct.pack('<L', size - 8))
self.wavfile.seek(40)
self.wavfile.write(struct.pack('<L', size - 44))
def Usage():
print('Usage: eurosig.py <numbers file> <wav file>')
print('(to "play" an existing numbers file to a wav file)')
print('or:    eurosig.py <numbers file> <wav file> <# numbers> [<load>]')
print('(to generate a random sequence of numbers which will be logged')
print('to a numbers file and "played" to a wav file -- load will be')
print('assumed at 0.5 if not given)')
if __name__ == '__main__':
limit = 0
load = 0.5
if len(sys.argv) == 3:
# Player mode
num = open(sys.argv[1], 'r')
elif len(sys.argv) < 6:
# Random generator mode
num = open(sys.argv[1], 'w')
limit = int(sys.argv[3])
if len(sys.argv) == 5:
load = float(sys.argv[4])
else:
# wrong number of parameters
usage()
sys.exit(1)
wav = open(sys.argv[2], 'wb')
es = EurosignalEncoder(4000, load, limit, num, wav)
es.DoGenerate()
num.close()
wav.close()

你要确保你的罪恶波在sin(x(=0的点结束。因此,表达式2 * np.pi * freq * x / sampleRate必须是 pi 的倍数。这反过来意味着freq * samples / sampleRate必须是整数,因此freq * dur必须是整数。

实现此目的的最简单方法是首先找到周期数并调整持续时间,以便获得整数个周期

def generate(output, freq, dur):
# create sine wave
n_cycles = math.floor(freq*dur)  # number of cycle
dur2 = n_cycles /freq            # adjusted duration
samples = dur2*sampleRate
x = np.arange(samples+1)         # want inclusive range
sineWave = (32767.0*np.sin(2 * np.pi * freq * x / sampleRate))

所以对于你的第一个例子

generate(file, 1153.1, 1.0)

调整后的持续时间为 0.999913277 秒。

在变化频率下生成连续正弦波的更好方法是增加相位而不是时间,并从每个样本的新相位计算sin((或cos((,而不是从时间或样本索引计算。

当频率发生变化时,只有相位增量会发生变化,这可能只会在下一个正弦波样本中产生很小的变化,对于新的频率,以足够高的采样率。

这允许随时更改频率,而不仅仅是过零,并且仍然产生波形连续性。

为了防止 sin(( 函数进行过多的范围缩减,可以将递增的相位包装为保持在 +-pi 或 +-2pi 之间。

最新更新