11

我正在构建一个应该在服务器上运行并分析声音文件的工具。我想用 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

所以现在我被困住了,在谷歌上找不到更多好的结果。所以也许你们可以帮助我吗?

谢谢!

4

2 回答 2

9

这是我试图实现的最终解决方案,非常感谢 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...
于 2013-02-25T06:39:03.433 回答
8

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

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

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

您可能知道这一点,但 FFT 需要复杂的输入并生成复杂的输出。当然,音频信号是真实的,因此输入的虚部应始终为零 ( a + 0*i)。由于您的输入是真实的,因此输出将关于输出数组的中点对称。您可以放心地忽略上半部分。如果您想要特定频率箱中的能量(它们线性间隔最多为采样率的一半),您需要计算复数值 ( sqrt(real*real + imag*imag)) 的大小。

还有一件事:因为频率零(信号的直流偏移)和奈奎斯特频率(采样率的一半)没有相位分量,一些 FFT 实现将它们放在同一个复数 bin 中(一个在实部,一个在虚部,通常是第一个 bin)。您可以创建一些简单的信号(所有 1 仅表示 DC 信号,交替 +1、-1 表示 Nyquist 信号)并查看 FFT 输出的样子。

于 2013-02-22T23:53:55.983 回答