私は最近フーリエ変換を使って遊んでいます(数週間かけてその背後にある数学について学びました)。私は次のサウンドバイトにローパスフィルターを組み合わせてハックすることにしました:
特に、私はフーリエ変換を取り、周波数の最高の1/2をゼロにしてから、逆フーリエ変換を行いました。これは私が得たものです
なぜパチパチという音がするのですか?
私は最近フーリエ変換を使って遊んでいます(数週間かけてその背後にある数学について学びました)。私は次のサウンドバイトにローパスフィルターを組み合わせてハックすることにしました:
特に、私はフーリエ変換を取り、周波数の最高の1/2をゼロにしてから、逆フーリエ変換を行いました。これは私が得たものです
なぜパチパチという音がするのですか?
回答:
2つの潜在的な問題:
最初の注意点として、フーリエ変換はローパス/ハイパスフィルターには理想的ではありません。バターワースフィルターは、より野心的なものになったら、チェビシェフ/楕円フィルターを開始して追跡するのに適しています。
理想的なフィルターを実装しようとしているようです。これらの「ブリックウォール」フィルターを実装して、特定の値より上または下のすべての周波数を遮断する方法はありません。適切に開発されたすべてのフィルターは、カットオフ周波数を中心に1から0までテーパーします。
理想的なフィルターは理論的にのみ可能であり、連続フーリエ変換を使用している場合、上記の方法が機能します。
ただし、離散フーリエ変換を行っているので、さらに心配する必要があります。実装の方法がわからないので、周波数を引き出すだけでウィンドウ化DFTでパチパチ音を鳴らすことができるため、ウィンドウ化を行っていると思います。
DFTでウィンドウ処理を行う場合、ウィンドウ間の周波数振幅は比較的連続していると考えるかもしれません。たとえば、400Hzの周波数の振幅が現在のウィンドウで0.5である場合、次のウィンドウでは振幅は0.5に近くなります。残念ながらこれは真実ではないので、単純にDFTから400Hzの周波数を削除すると、ウィンドウ間に大きなポップやクラックが聞こえる場合があります。
小さな例:カットオフレートは600Hzです。ウィンドウ1は800Hzのサインを再生していますウィンドウ2はウィンドウ1に「継続的に」接続し、400Hzを再生しています。次に、ウィンドウ1がサイレントになり、ウィンドウ2がすぐにオンになるため、ウィンドウの間にポップ音が聞こえます。
もう1つ覚えておくべきことは、DFTでは有限量の周波数しか表現できないことです。DFTで2つの離散周波数の間の周波数の正弦波を含むオーディオファイルがある場合、実際にはそれを多数の離散周波数で表します。したがって、サンプルオーディオファイルにカットオフよりも低い正弦波が含まれている場合でも、その周波数がDFT周波数の間にある場合、その一部を切り取って上記の方法で歪める可能性があります。オーディオを表すにはより高い周波数が必要なためです。ファイル。
それが役に立てば幸い
いくつかの概念を説明するために小さなPythonプログラムを組み合わせました-コードはかなりひどいですが、同様の問題に対して持っていた古いコードをいくつか使用しました。コメントはほとんどありませんが、モジュールが小さいため、かなり簡単にフォローできます。2つのdft / idft関数があります。フィルター処理のために信号をDFTドメインに周波数シフトする2つの関数fshiftn / fshiftp。DFTドメインでフィルタリングを実行する関数dftlpass ; バターワースフィルターを使用してフィルター処理を行う関数zpblpass。テスト信号を形成してフィルタリングを実行する関数bbdftsig ; そして最後に小さな関数plotsigs信号をプロットします。スクリプトの最後に、さまざまなパラメーターが設定され、さまざまな数値が作成されます。
"""
Test of DFT versus scipy.signal.butter filtering with respect to
signal reconstruction.
"""
# import ############################################################ import #
import matplotlib as mpl; mpl.rcParams['backend'] = 'Agg'
import matplotlib.pyplot as mplpp
import matplotlib.mlab as mplml
import numpy as np
import scipy.signal as sps
# initialize #################################################### initialize #
try:
mpl.rc('text', usetex=False)
mpl.rc('font', family='serif')
mpl.rc('font', serif='STIXGeneral')
mpl.rc('font', size=8)
except AttributeError:
None
# dft ################################################################## dft #
def dft(xt, fs, t0):
N, d = len(xt), -2j*np.pi/len(xt)
w = np.arange(N, dtype=np.float).reshape((N,1))
c = np.exp(d*t0*fs*w)
W = np.exp(d*np.dot(w,np.transpose(w)))
xf = np.multiply(c,np.dot(W,xt)) / float(N)
f = w*fs/float(N)
return xf, f
# idft ################################################################ idft #
def idft( X, FS, T0 ):
N, d = len(X), 2j*np.pi/len(X)
w = np.arange(N, dtype=float).reshape((N,1))
cc = np.exp(d*T0*FS*w)
Wc = np.exp(d*np.dot(w, np.transpose(w)))
Y = np.dot(Wc, np.multiply(cc, X))
return Y
# fshiftn ########################################################## fshiftn #
def fshiftn( xf, f ):
assert type(f) == np.ndarray, "f must be a np.ndarray"
assert f.shape[1] == 1, "f must be a column array"
assert xf.shape[1] == 1, "xf must be a column array"
assert sum(f<0) == 0, "All frequency components must be 0 or positive"
# Determine sampling rate, tolerance, and allocate output array
fs, tol = len(f)*(np.abs(f[1,0]-f[0,0])), 1.E-2
fshift = np.zeros((len(f),1), dtype=float)
xfshift = np.zeros((len(f),1), dtype=complex)
# Determine index where f > fs/2
Nm = np.floor(len(f)/2.0)
Np = np.floor((len(f)-1.0)/2.0)
# Compute output frequency array such that -fs/2 <= f < fs/2 and the
# corresponding Fourier coefficients
fshift[:Nm,0] = f[Np+1:,0] - fs
fshift[Nm,0] = f[0,0]
fshift[Nm+1:,0] = f[1:Np+1,0]
xfshift[:Nm,0] = xf[Np+1:,0]
xfshift[Nm,0] = xf[0,0]
xfshift[Nm+1:,0] = xf[1:Np+1,0]
return xfshift, fshift
# fshiftp ########################################################## fshiftp #
def fshiftp(xf, f):
assert type(f) == np.ndarray, "f must be a np.ndarray"
assert f.shape[1] == 1, "f must be a column array"
assert xf.shape[1] == 1, "xf must be a column array"
assert sum(f<0) > 0, "Some input frequencies must be negative"
# Determine sampling rate, tolerance, and allocate output array
fs, tol = len(f)*(np.abs(f[1,0]-f[0,0])), 1.E-2
fshift = np.zeros((len(f),1), dtype=float)
xfshift = np.zeros((len(f),1), dtype=complex)
# Determine index where f > fs/2
#Nx = np.floor((len(f)+1+tol)/2)
Nm = np.floor(len(f)/2.0)
Np = np.floor((len(f)-1.0)/2.0)
# Compute output frequency array such that -fs/2 <= f < fs/2 and the
# corresponding Fourier coefficients
fshift[Np+1:,0] = f[:Nm:,0] + fs
fshift[0,0] = f[Nm,0]
fshift[1:Np+1:,0] = f[Nm+1:,0]
xfshift[Np+1:,0] = xf[:Nm:,0]
xfshift[0,0] = xf[Nm,0]
xfshift[1:Np+1:,0] = xf[Nm+1:,0]
return xfshift, fshift
# dftlpass ######################################################## dftlpass #
def dftlpass(xt, fs, fcut):
# Perform Discrete Fourier Transform
xf, f = dft(xt, fs, 0.0)
# Shift frequencies to -fs/2 <= f < fs/2 ... and coefficients
xfshift, fshift = fshiftn(xf, f)
# Perform filtration
xfshift = xfshift * (np.abs(fshift) <= fcut)
# Re-shift frequencies to 0 <= f < fs ... and coefficients
xfrecon, frecon = fshiftp(xfshift, fshift)
# Perform inverse Discrete Fourier Transform
yt = idft(xfrecon, fs, 0.0)
return yt.real
# zpblpass ######################################################## zpblpass #
def zpblpass(xn, fcal, fs, fcut):
bz, az = sps.butter(5, fcut/(fs/2))
# Gain calibration
Ncal = np.max([np.int(20*fs/fcal), 30000])
Nguard = np.int(0.1*Ncal)
t = np.arange(Ncal) / fs
x0_cal = 1.0 * np.cos(2*np.pi*fcal*t)
yi_cal = sps.filtfilt(bz, az, 2.0*x0_cal*np.cos(2*np.pi*fcal*t))
k = 1.0/np.mean(yi_cal[Nguard:Ncal-Nguard])
# Scaled output
yn = k * sps.filtfilt(bz, az, xn)
return yn
# bbdftsig ######################################################## bbdftsig #
def bbdftsig(f0, f1, f2, fcut, fs, N):
t = np.arange(N).reshape((N,1)) / fs
s0 = np.sin(2*np.pi*f0*t)
s1 = np.sin(2*np.pi*f1*t + 0.2)
s2 = 0.7 * np.sin(2*np.pi*f2*t + np.pi/3.0)
slow = s0 + s1
s = slow + s2
sf = dftlpass(s, fs, fcut)
sfdftv = sf.reshape((N))
sv = s.reshape((N))
slowv = slow.reshape((N))
sv = s.reshape((N))
sfzpbv = zpblpass(sv, f1, fs, fcut)
#sfzpbv = sfzpb.reshape((N))
return sv, slowv, sfdftv, sfzpbv
# plotsigs ######################################################## plotsigs #
def plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname):
n = np.arange(s.shape[0])
# Plot results
mplpp.figure(1, (5.0,2.25))
mplpp.clf()
mplpp.plot(n[Nstart:Nstop], s[Nstart:Nstop], 'm-',
n[Nstart:Nstop:4], s[Nstart:Nstop:4], 'mx',
n[Nstart:Nstop], slow[Nstart:Nstop], 'g-',
n[Nstart:Nstop:10], slow[Nstart:Nstop:10], 'gx',
n[Nstart:Nstop], sfdft[Nstart:Nstop], 'r-',
n[Nstart:Nstop:15], sfdft[Nstart:Nstop:15], 'rx',
n[Nstart:Nstop], sfzpb[Nstart:Nstop], 'b-',
linewidth=1.5)
mplpp.legend([r'$s$', r'$s$', r'$s_{\rm low}$', r'$s_{\rm low}$',
r'DFT', r'DFT', r'ZPB'], loc='upper right')
mplpp.ylabel(r'Signal')
mplpp.xlabel(r'$n$')
#mplpp.axis([-10.0, 10.0, 1.0E-2, 1.0E2])
mplpp.grid(True)
mplpp.savefig(fname, dpi=600,
bbox_inches='tight', pad_inches=0.05)
mplpp.close()
# __main__ ######################################################## __main__ #
if __name__ == '__main__':
# Initialize
f0 = 3.0
f1 = 11.5
f2 = 20.0
fcut = 15.0
fs = 1000.0
N = 5000
s, slow, sfdft, sfzpb = bbdftsig(f0, f1, f2, fcut, fs, N)
n = np.arange(s.shape[0])
# Fig. 1: full data set
Nstart = 0
Nstop = N
fname = 'full.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 2: beginning
Nstart = 0
Nstop = 150
fname = 'beginning.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 3: middle
Nstart = np.floor(N/2.0) - 75
Nstop = Nstart + 100
fname = 'middle.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 4: ending
Nstart = N - 150
Nstop = N
fname = 'ending.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
。このタイプの処理では非常に典型的であるように、エッジの影響と、中央セクションの両方のタイプのフィルタリング間の合理的な一致により、シーケンスの最初と最後にいくつかの違いがあります。
したがって、結論として、フーリエ係数をゼロに強制することによって直接フィルタリングを使用することが可能です。これは、圧縮センシングでも時々行われ、信号のサポートを減らして信号にスパース性を強制します。ただし、特に信号のエッジでエラーが増加するため、この影響があります。また、上記は信号全体を1つのシーケンスとして扱う最良のケースです。信号を時間フレームに分割する必要がある場合、フレーム間の信号の連続性を確保するためにウィンドウ処理またはその他の手法を検討する必要があるため、複雑になります。したがって、私のアドバイスは、通常バターワース/楕円/ ..またはその他のフィルターを使用することを推奨する他のいくつかの投稿と同様です。
これは、FFTコードを含む、迅速かつダーティなビンゼロ化FFTバンドパスフィルターです。
void FFT(int n, int inverse, double *gRe, double *gIm, double *GRe, double *GIm)
{
int m = 0;
int p = 1;
int j = 0;
int i1=0;
int k=0;
double ca=0;
double sa=0;
int l1,l2,l3;
double u1,u2;
double t1 = 0;
double t2 = 0;
int i2=0;
double z;
/* Calculate m=log_2(n) */
while(p < n)
{
p *= 2;
m++;
}
/* Bit reversal */
GRe[n - 1] = gRe[n - 1];
GIm[n - 1] = gIm[n - 1];
for(i1 = 0; i1 < n - 1; i1++)
{
GRe[i1] = gRe[j];
GIm[i1] = gIm[j];
k = n / 2;
while(k <= j)
{
j -= k;
k /= 2;
}
j += k;
}
/* Calculate the FFT */
ca = -1.0;
sa = 0.0;
l1 = 1;
l2 = 1;
l3=0;
for(l3 = 0; l3 < m; l3++)
{
l1 = l2;
l2 *= 2;
u1 = 1.0;
u2 = 0.0;
for(j = 0; j < l1; j++)
{
i2=j;
for(i2 = j; i2 < n; i2 += l2)
{
i1 = i2 + l1;
t1 = u1 * GRe[i1] - u2 * GIm[i1];
t2 = u1 * GIm[i1] + u2 * GRe[i1];
GRe[i1] = GRe[i2] - t1;
GIm[i1] = GIm[i2] - t2;
GRe[i2] += t1;
GIm[i2] += t2;
}
z = u1 * ca - u2 * sa;
u2 = u1 * sa + u2 * ca;
u1 = z;
}
sa = sqrt((1.0 - ca) / 2.0);
if(!inverse) sa =- sa;
ca = sqrt((1.0 + ca) / 2.0);
}
/* Divide through n if it isn't the IDFT */
if(!inverse)
{
int i3=0;
for(i3 = 0; i3 < n; i3++)
{
GRe[i3] /= n;
GIm[i3] /= n;
}
}
}
void mainfftBandPass(double *insamples, double *outsamples, unsigned long fftsize, long lowfreq, long highfreq, long srate)
{
static double *inbuf=NULL;
static double *realn=NULL;
static double *imags=NULL;
static double *spectr=NULL;
static double *zer0=NULL;
static double *olds=NULL;
static double *infader=NULL;
static double *outfader=NULL;
int notched=(highfreq<lowfreq) ? 1 : 0;
long incounter=0;
/* treble is the highest baseband frequency */
/* bass the the lowest baseband frequency */
/* this function is called twice per FFT block */
long midcounter=0;
long outcounter=0;
long bass=lowfreq*(fftsize/(double)srate);
long treble=(highfreq)*(fftsize/(double)srate);
static long halffft=2;
static long old_fftsize=0;
static short first=1;
if(first==1 || fftsize!=old_fftsize)
{
if(inbuf)
free(inbuf);
if(realn)
free(realn);
if(imags)
free(imags);
if(spectr)
free(spectr);
if(zer0)
free(zer0);
if(olds)
free(olds);
if(infader)
free(infader);
if(outfader)
free(outfader);
infader=(double*)malloc(fftsize*sizeof(double));
outfader=(double*)malloc(fftsize*sizeof(double));
inbuf=(double*)malloc(fftsize*sizeof(double));
realn=(double*)malloc(fftsize*sizeof(double));
imags=(double*)malloc(fftsize*sizeof(double));
spectr=(double*)malloc(fftsize*sizeof(double));
zer0=(double*)malloc(fftsize*sizeof(double));
olds=(double*)malloc(fftsize*sizeof(double));
if((!inbuf) || (!realn) ||(!imags) ||(!spectr)||(!zer0)||(!ol ds))
{
printf("Not enough memory for FFT!\n");
exit(1);
}
halffft=fftsize/2;
long infade=0;
long outfade=halffft;
for(infade=0;infade<halffft;infade++)
{
outfade--;
outfader[infade]=(0.5 * cos((infade) * M_PI/(double)(halffft))+0.5);
infader[outfade]=outfader[infade];
}
first=0;
}
memset(realn,0,sizeof(double)*fftsize);
for(incounter=0;incounter<halffft;incounter++)
{
inbuf[incounter]=inbuf[incounter+halffft];
}
for(incounter=0;incounter<halffft;incounter++)
{
inbuf[incounter+halffft]=insamples[incounter];
}
for(incounter=0;incounter<fftsize;incounter++)
{
realn[incounter]=inbuf[incounter];
}
memset(imags,0,sizeof(double)*fftsize);
FFT(fftsize, 0, realn,imags, spectr,zer0);
memset(realn,0,sizeof(double)*fftsize);
memset(imags,0,sizeof(double)*fftsize);
if(notched==0)
{
for(midcounter=bass;midcounter<treble;midcounter++)
{
realn[midcounter]=spectr[midcounter] * 2.0;
imags[midcounter]= zer0[midcounter] * 2.0;
}
if(bass==0)
realn[0]=spectr[0];
}
else if(notched==1)
{
for(midcounter=0;midcounter<halffft;midcounter++)
{
if((midcounter<treble) ||(midcounter>bass))
{
realn[midcounter]=spectr[midcounter] * 2.0;
imags[midcounter]= zer0[midcounter] * 2.0;
}
}
if(bass==0)
{
realn[0]=0;
}
else
{
realn[0]=spectr[0];
}
}
FFT(fftsize, 1, realn, imags,spectr,zer0);
for(outcounter=0;outcounter<halffft;outcounter++)
{
outsamples[outcounter]=(((spectr[outcounter] )*infader[outcounter])+(olds[outcounter+halffft]*outfader[outcounter])) ;
}
for(outcounter=0;outcounter<fftsize;outcounter++)
{
olds[outcounter]=spectr[outcounter];
}
memset(spectr,0,fftsize*sizeof(double));
memset(zer0,0,fftsize*sizeof(double));
old_fftsize=fftsize;
}
signed short mainbandpass(signed short input, double lowcut, double highcut,long rate,long fftsize)
{
double retvalue=0;
static double *insamp=NULL;
static double *outsamp=NULL;
static int first=1;
static int q=0;
if(first==1)
{
insamp=(double*)malloc(fftsize * sizeof(double));
outsamp=(double*)malloc(fftsize * sizeof(double));
if(insamp==NULL || outsamp==NULL)
{
printf("Not enough memory for FFT buffers.\n");
exit(1);
}
memset(insamp,0,fftsize * sizeof(double));
memset(outsamp,0,fftsize * sizeof(double));
first=0;
}
insamp[q]=input;
retvalue=outsamp[q];
if(retvalue> 32767)
retvalue=32767;
if(retvalue <-32768)
retvalue=-32768;
q++;
if(q>(fftsize/2)-1)
{
mainfftBandPass(insamp,outsamp, fftsize, lowcut,highcut,rate);
q=0;
}
return (signed short)retvalue;
}
入力オーディオの各サンプルについて、入力サンプルと、ローカットおよびハイカットで維持する周波数の範囲を指定してmainbandpassを呼び出します。ローカットがハイカットより大きい場合、結果はバンドリジェクトフィルターになります。循環畳み込みが行われていますが、モデムに適した帯域外の放射はありません。