从文件中提取快速傅里叶变换数据

时间:2013-02-22 19:12:42

标签: ruby audio mp3 fft wav

我正在构建一个应该在服务器上运行并分析声音文件的工具。我想在Ruby中这样做,因为我的所有其他工具也都是用Ruby编写的。但我无法找到一种实现这一目标的好方法。

我发现的很多例子都是可视化器和图形化的东西。我只需要FFT数据,仅此而已。我需要获取音频数据,并对其进行FFT。我的最终目标是计算一些东西,如平均值/中位数/模式,第25百分位数和第75百分位数,所有频率(加权幅度),BPM,以及可能还有一些其他好的特性,以便以后能够将类似的声音聚集在一起

首先,我尝试使用 ruby​​-audio fftw3 ,但我从来没有让两者真正合作。文档也不好,所以我真的不知道什么数据被洗牌。 接下来我尝试使用 bplay / brec 并限制我的Ruby脚本只使用STDIN并对其执行FFT(仍然使用fftw3)。但我无法让bplay / brec工作,因为服务器没有声卡而且我没有设法直接将音频直接发送到STDOUT而不先去音频设备。

这是我最接近的地方:

# extracting audio from wav with ruby-audio
buf = RubyAudio::Buffer.float(1024)
RubyAudio::Sound.open(fname) do |snd|
    while snd.read(buf) != 0
        # ???
    end
end

# performing FFT on audio
def get_fft(input, window_size)
    data = input.read(window_size).unpack("s*")
    na = NArray.to_na(data)
    fft = FFTW3.fft(na).to_a[0, window_size/2]
    return fft
end

所以现在我陷入困境,无法在Google上找到更好的结果。那么也许你们这些家伙可以帮助我吗?

谢谢!

2 个答案:

答案 0 :(得分:8)

这是我努力实现的最终解决方案,非常感谢Randall Cook的有用建议。在Ruby中提取wav文件的声波和FFT的代码:

require "ruby-audio"
require "fftw3"

fname = ARGV[0]
window_size = 1024
wave = Array.new
fft = Array.new(window_size/2,[])

begin
    buf = RubyAudio::Buffer.float(window_size)
    RubyAudio::Sound.open(fname) do |snd|
        while snd.read(buf) != 0
            wave.concat(buf.to_a)
            na = NArray.to_na(buf.to_a)
            fft_slice = FFTW3.fft(na).to_a[0, window_size/2]
            j=0
            fft_slice.each { |x| fft[j] << x; j+=1 }
        end
    end

rescue => err
    log.error "error reading audio file: " + err
    exit
end

# now I can work on analyzing the "fft" and "wave" arrays...

答案 1 :(得分:7)

我认为这里有两个问题。一个是获取样本,另一个是执行FFT。

要获取样本,有两个主要步骤:解码和缩混。要解码wav文件,您只需要解析标题,以便了解如何解释样本。对于mp3文件,您需要进行完全解码。音频解码后,如果您对单独处理立体声声道不感兴趣,可能需要将其缩混为单声道,因为FFT需要单个声道作为输入。如果你不介意在Ruby之外冒险,那么sox tool就可以轻松实现。例如,sox song.mp3 -b 16 song.raw channels 1应该将mp3转换为纯PCM样本的单声道文件(即16位整数)。顺便说一句,快速搜索显示ruby/audio库(也许是你帖子中提到的那个)。它看起来很不错,特别是因为它包装了libsndfile。

要执行FFT,我会看到三个选项。一种是使用执行FFT的this snippet代码。我不是Ruby专家,但它看起来可能没问题。第二种选择是使用NArray。它有很多数学方法,包括FFTW,可以在一个单独的模块中使用,在NArray页面的中间链接一个tarball。第三种选择是编写自己的FFT代码。它不是一个特别复杂的算法,可以为您提供Ruby中数值处理的丰富经验(如果您需要)。

您可能已经意识到这一点,但FFT需要复杂的输入并产生复杂的输出。当然,音频信号是真实的,因此输入的虚部应始终为零(a + 0*i)。由于输入是实数,输出将关于输出数组的中点对称。你可以安全地忽略上半部分。如果您希望特定频率仓中的能量(它们线性间隔高达采样率的一半),则需要计算复数值的大小(sqrt(real*real + imag*imag))。

还有一件事:因为频率零(信号的DC偏移)和奈奎斯特频率(采样率的一半)没有相位分量,一些FFT实现将它们组合到同一个复杂的bin中(实际组件中的一个) ,一个在虚构组件中,通常是第一个箱子中的一个。你可以创建一些简单的信号(所有1只用于直流信号,交替+1,-1用于奈奎斯特信号),看看FFT输出是什么样的。