从FFT中有效地提取信号频率

时间:2014-01-21 03:12:09

标签: r performance audio signal-processing fft

我正在使用R并试图通过对每个声波应用快速傅里叶变换并确定频率来从大量声波(1000个音频文件)中恢复频率(实际上,只是接近实际频率的数字)每个文件的最大幅度。我希望能够尽快恢复这些峰值频率。 FFT方法是我最近学到的一种方法,我认为它应该适用于这项任务,但我愿意接受不依赖于FFT的答案。我已经尝试了一些应用FFT并获得最高幅度频率的方法,自从我的第一种方法以来,我已经看到了显着的性能提升,但如果可能的话,我想更快地加快执行时间。

以下是示例数据:

s.rate<-44100                        # sampling frequency
t <- 2                               # seconds, for my situation, I've got 1000s of 1 - 5 minute files to go through
ind <- seq(s.rate*t)/s.rate          # time indices for each step
                                     # let's add two sin waves together to make the sound wave
f1 <- 600                            # Hz: freq of sound wave 1
y <- 100*sin(2*pi*f1*ind)            # sine wave 1
f2 <- 1500                           # Hz: freq of sound wave 2
z <- 500*sin(2*pi*f2*ind+1)          # sine wave 2
s <- y+z                             # the sound wave: my data isn't this nice, but I think this is an OK example

我尝试的第一种方法是使用fpeaks包中的specseewave函数,它似乎有效。但是,它太慢了。

library(seewave)
fpeaks(spec(s, f=s.rate), nmax=1, plot=F) * 1000  # *1000 in order to recover freq in Hz
[1] 1494
# pretty close, quite slow

在做了一些阅读之后,我尝试了下一种方法,其中

spec(s, f=s.rate, plot=F)[which(spec(s, f=s.rate, plot=F)[,2]==max(spec(s, f=s.rate, plot=F)[,2])),1] * 1000 # again need to *1000 to get Hz
   x 
1494 
# pretty close, definitely faster

经过一番调查后,我发现这种方法运作得相当好。

which(Mod(fft(s)) == max(abs(Mod(fft(s))))) * s.rate / length(s)
[1] 1500  
# recovered the exact frequency, and quickly!

以下是一些效果数据:

library(microbenchmark)
microbenchmark(
  WHICH.MOD = which(Mod(fft(s))==max(abs(Mod(fft(s))))) * s.rate / length(s),
  SPEC.WHICH = spec(s,f=s.rate,plot=F)[which(spec(s,f=s.rate,plot=F)[,2] == max(spec(s,f=s.rate,plot=F)[,2])),1] * 1000,   # this is spec from the seewave package
  # to recover a number around 1500, you have to multiply by 1000
  FPEAKS.SPEC = fpeaks(spec(s,f=s.rate),nmax=1,plot=F)[,1] * 1000, # fpeaks is from the seewave package... again, need to multiply by 1000
  times=10)

Unit: milliseconds
        expr       min        lq    median        uq       max neval
   WHICH.MOD     10.78     10.81     11.07     11.43     12.33    10
  SPEC.WHICH     64.68     65.83     66.66     67.18     78.74    10
 FPEAKS.SPEC 100297.52 100648.50 101056.05 101737.56 102927.06    10

良好的解决方案是将频率接近(±10 Hz)恢复到实际频率最快的解决方案。

更多上下文

我有很多文件(几个GB),每个文件都包含一个每秒调制几次的音调,有时信号实际上完全消失,所以只有沉默。我想确定未调制音调的频率。我知道它们应该都低于6000赫兹,但我不知道比这更精确。如果(大的话)我理解正确,我在这里有一个好的方法,这只是让它更快的问题。只是fyi,我以前没有数字信号处理方面的经验,所以除了有关如何以编程方式更好地处理这些数学/方法的建议之外,我还要感谢任何有关数学/方法的提示和指示。

1 个答案:

答案 0 :(得分:1)

在更好地理解了这项任务和所涉及的一些术语之后,我遇到了一些其他的方法,我将在这里介绍。这些额外的方法允许窗口功能和更多,真的,并且在我的问题中最快的方法不是。我还通过将一些函数的结果赋给对象并索引对象而不是再次运行函数来加快速度。

#i.e.
(ms<-meanspec(s,f=s.rate,wl=1024,plot=F))[which.max(ms[,2]),1]*1000 
# instead of 
meanspec(s,f=s.rate,wl=1024,plot=F)[which.max(meanspec(s,f=s.rate,wl=1024,plot=F)[,2]),1]*1000

我有自己喜欢的方法,但我欢迎建设性的警告,反馈和意见。

microbenchmark(
  WHICH.MOD = which((mfft<-Mod(fft(s)))[1:(length(s)/2)] == max(abs(mfft[1:(length(s)/2)]))) * s.rate / length(s),
  MEANSPEC = (ms<-meanspec(s,f=s.rate,wl=1024,plot=F))[which.max(ms[,2]),1]*1000,
  DFREQ.HIST = (h<-hist(dfreq(s,f=s.rate,wl=1024,plot=F)[,2],200,plot=F))$mids[which.max(h$density)]*1000,
  DFREQ.DENS = (dens <- density(dfreq(s,f=s.rate,wl=1024,plot=F)[,2],na.rm=T))$x[which.max(dens$y)]*1000,
  FPEAKS.MSPEC = fpeaks(meanspec(s,f=s.rate,wl=1024,plot=F),nmax=1,plot=F)[,1]*1000 , 
  times=100)

Unit: milliseconds
         expr       min        lq    median        uq      max neval
    WHICH.MOD  8.119499  8.394254  8.513992  8.631377 10.81916   100
     MEANSPEC  7.748739  7.985650  8.069466  8.211654 10.03744   100
   DFREQ.HIST  9.720990 10.186257 10.299152 10.492016 12.07640   100
   DFREQ.DENS 10.086190 10.413116 10.555305 10.721014 12.48137   100
 FPEAKS.MSPEC 33.848135 35.441716 36.302971 37.089605 76.45978   100

DFREQ.DENS返回距离实际值最远的频率值。其他方法返回接近实际值的值。

使用我的一个音频文件(即真实数据),性能结果略有不同(见下文)。上面使用的数据和下面用于性能数据的实际数据之间的一个潜在相关差异是,数据上方只是数字的向量,我的实际数据存储在Wave对象中,来自tuneR包。

library(Rmpfr) # to avoid an integer overflow problem in `WHICH.MOD`
microbenchmark(
  WHICH.MOD = which((mfft<-Mod(fft(d@left)))[1:(length(d@left)/2)] == max(abs(mfft[1:(length(d@left)/2)]))) * mpfr(s.rate,100) / length(d@left),
  MEANSPEC = (ms<-meanspec(d,f=s.rate,wl=1024,plot=F))[which.max(ms[,2]),1]*1000,
  DFREQ.HIST = (h<-hist(dfreq(d,f=s.rate,wl=1024,plot=F)[,2],200,plot=F))$mids[which.max(h$density)]*1000,
  DFREQ.DENS = (dens <- density(dfreq(d,f=s.rate,wl=1024,plot=F)[,2],na.rm=T))$x[which.max(dens$y)]*1000,
  FPEAKS.MSPEC = fpeaks(meanspec(d,f=s.rate,wl=1024,plot=F),nmax=1,plot=F)[,1]*1000 , 
  times=25) 

Unit: seconds
         expr      min       lq   median       uq      max neval
    WHICH.MOD 3.249395 3.320995 3.361160 3.421977 3.768885    25
     MEANSPEC 1.180119 1.234359 1.263213 1.286397 1.315912    25
   DFREQ.HIST 1.468117 1.519957 1.534353 1.563132 1.726012    25
   DFREQ.DENS 1.432193 1.489323 1.514968 1.553121 1.713296    25
 FPEAKS.MSPEC 1.207205 1.260006 1.277846 1.308961 1.390722    25

WHICH.MOD实际上必须运行两次以考虑左右音频通道(即我的数据是立体声),因此它需要比输出指示的时间更长。注意:我需要使用Rmpfr库以使WHICH.MOD方法处理我的实际数据,因为我遇到整数溢出问题。

有趣的是,FPEAKS.MSPEC对我的数据表现非常好,并且它似乎返回了一个相当准确的频率(基于我对光谱图的视觉检查)。 DFREQ.HISTDFREQ.DENS速度很快,但输出频率并不像我判断的那样接近实际值,两者都是相对难看的解决方案。到目前为止,我最喜欢的解决方案MEANSPEC使用meanspecwhich.max。我会将此标记为答案,因为我没有任何其他答案,但请随意提供其他答案。我会投票支持它,如果能提供更好的解决方案,可以选择它作为答案。