【问题标题】:Extract Fast Fourier Transform data from file从文件中提取快速傅里叶变换数据
【发布时间】:2013-02-08 12:08:02
【问题描述】:

我正在构建一个应该在服务器上运行并分析声音文件的工具。我想用 Ruby 来做这件事,因为我所有的其他工具也是用 Ruby 编写的。但我很难找到完成此任务的好方法。

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

首先我尝试使用 ruby-audiofftw3,但我从未真正将这两者结合使用。文档也不好,所以我真的不知道什么数据被打乱了。 接下来我尝试使用 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 上找到更多好的结果。所以也许你们可以帮助我?

谢谢!

【问题讨论】:

  • 也许之前的讨论可能会有所帮助:stackoverflow.com/questions/2834548/…
  • 您能否详细说明您被卡住的原因?请包括错误消息或您对事情应该如何工作的理解中的差距。
  • 到目前为止我已经添加了我的代码。在使用 ruby​​-audio 读取数据和使用 fftw3 提取 FFT 之间存在巨大差距。请参阅带有三个问号的评论。我在 buf 中有 wav 数据,但我不知道数据到底是什么/代表什么。里面有标头吗?它是压缩/编码的吗?等等,等等。我想将数据放入 get_fft (这几乎是从另一个 SO 帖子中逐字提取的)。

标签: ruby audio mp3 fft wav


【解决方案1】:

这是我试图实现的最终解决方案,非常感谢 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 用于发布您的代码。我很高兴你畅通无阻并且可以创造出有效的东西。顺便说一句,在 Stack Overflow 上表示感谢的一个好方法是投票和/或接受答案,如果你还没有这样做的话。 ;)
  • 我赞成您的帖子,但必须等待一段时间才能接受我自己的答案。 :)
  • @ChristofferBrodd-Reijer 你的代码非常适合指纹 wav 文件,但是指纹太大了。您找到提高速度和缩小指纹的解决方案了吗?
  • 是的,我做到了。我只在歌曲的开头、中间和结尾的一小部分(3-10秒)上做了指纹。事实证明,这足以解决我的问题。
【解决方案2】:

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

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

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

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

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

【讨论】:

  • 感谢您的长回答。这几乎就是我一直在想的。但我无法真正将所有这些放在一起。我添加了一些代码,以便显示我在使用 ruby​​-audio(您链接的那个)和 fftw3 gem 时得到的最远距离。
  • 通常当我无法将东西放在一起时,我会从很小的地方开始,一次只添加一个步骤,添加大量诊断代码(或在调试器中仔细检查变量)以确保一切正常按预期工作:我可以打开文件吗?我可以读取数据吗?数据的格式是我所期望的吗?我可以转换数据吗?它看起来仍然正确吗?等
  • 是的,但我被困住了:我正在查看的数据是什么,我应该如何将其输入 FFT 函数?我应该只给它缓冲区的内容(在 buf 上调用 to_a)还是需要先处理它?我不确定从 ruby​​-audio 获得的数据代表什么。
  • 知道了。准备好进行 FFT 的未压缩数字音频通常是一个 16 位整数数组,每个整数代表(不完全是,但一个很好的概念模型)模拟音频电缆上传输信号的电压。我建议将数据打印为整数数组,然后看看你得到了什么。你应该看到很多接近于零的数字。您甚至可以将这些数字(作为文本)加载到电子表格/Matlab/Octave 中并绘制图表。你应该看到一个声波。您可以使用像 Audacity(免费和开源)这样的数字音频编辑器来查看源音频和 . . .
  • 。 . .将其与您提取的内容进行比较。如果它们匹配,您就可以继续了。如果没有,您必须仔细查看信号路径,了解每一步发生了什么。
猜你喜欢
  • 2011-07-12
  • 1970-01-01
  • 2017-09-14
  • 2012-12-10
  • 2013-03-31
  • 2020-03-29
  • 2010-12-13
  • 2012-05-25
  • 2015-08-19
相关资源
最近更新 更多