关于FFT分析音频的学习
|
本文部分知识从以下文章学习: |
最近工作上在做关于音乐游戏的内容,其中需要分析音频找节奏点(或者说是重音点)。
学习了一系列相关知识后,了解到一段音乐的波形图可以分解成不同频率的波形图,也就是由时域到频域的转换。
借用其他博主的图就比较容易理解了,如下所示。

波从时域到频域的转换可以通过傅里叶变换实现,关于傅里叶变换的知识可以从最上面的链接学习或者自行查找(傅里叶真厉害!!!)。
计算机处理的音频在时域上是离散的数据,我们可以使用离散傅里叶变换DFT(傅里叶变换在时域和频域上都呈离散的形式)获得频域上的离散数据。
快速傅立叶变换FFT是DFT的快速算法,其核心思路就是分治法的DFT,具体推导可以查看上面的第二个链接。
FFT 代码如下:
static void BitReverse(Complex[] cpData, int n)
{
Complex temp;
int lim = ;
while (( << lim) < n) lim++;
for (int i = ; i < n; i++)
{
int t = ;
for (int j = ; j < lim; j++)
{ if (((i >> j) & ) != )
t |= ( << (lim - j - ));
}
if (i < t)
{
temp = cpData[i];
cpData[i] = cpData[t];
cpData[t] = temp;
} // i < t 防止交换两次
}
} static void FFT1(Complex[] cpData, bool forward)
{
var n = cpData.Length; BitReverse(cpData, n);//位反转 Complex[] omg = new Complex[n];
for (int i = ; i < n; i++)
{
omg[i] = new Complex((float)Math.Cos( * Math.PI * i / n), (float)Math.Sin( * Math.PI * i / n));
}
Complex temp ;
for (int step = ; step <= n; step *= )
{
int m = step / ;
for (int j = ; j < n; j += step)
for (int i = ; i < m; i++)
{//蝶形运算
if(forward)
temp = omg[n / step * i] * cpData[j + i + m];
else
temp = omg[n / step * i].Conjugate() * cpData[j + i + m];
cpData[j + i + m] = cpData[j + i] - temp;
cpData[j + i] = cpData[j + i] + temp;
}
}
}
Complex是封装的复数类,偷懒不是自己写的,来自这位老哥https://blog.csdn.net/u011583927/article/details/46974341
这个FFT,new了好多对象,效率不是很高。。。
再贴一个直接把复数的实部虚部轮流放在一个数组里直接算的,能快一些。
static void Reverse(float[] data, int n)
{ int j = , k = ;
var top = n / ;
while (true)
{ var t = data[j + ];
data[j + ] = data[k + n];
data[k + n] = t;
t = data[j + ];
data[j + ] = data[k + n + ];
data[k + n + ] = t;
if (j > k)
{
t = data[j];
data[j] = data[k];
data[k] = t;
t = data[j + ];
data[j + ] = data[k + ];
data[k + ] = t; t = data[j + n + ];
data[j + n + ] = data[k + n + ];
data[k + n + ] = t;
t = data[j + n + ];
data[j + n + ] = data[k + n + ];
data[k + n + ] = t;
} k += ;
if (k >= n)
break; var h = top;
while (j >= h)
{
j -= h;
h /= ;
}
j += h;
}
}
static void FFT2(float[] data, bool forward)
{
var n = data.Length;
n /= ; Reverse(data, n); float sign = forward ? : -;
var mmax = ;
while (n > mmax)
{
var istep = * mmax;
var theta = sign * (float)Math.PI / mmax;
float wr = , wi = ;
var wpr = (float)Math.Cos(theta);
var wpi = (float)Math.Sin(theta);
for (var m = ; m < istep; m += )
{
for (var k = m; k < * n; k += * istep)
{
var j = k + istep;
var tempr = wr * data[j] - wi * data[j + ];
var tempi = wi * data[j] + wr * data[j + ];
data[j] = data[k] - tempr;
data[j + ] = data[k + ] - tempi;
data[k] = data[k] + tempr;
data[k + ] = data[k + ] + tempi;
}
var t = wr;
wr = wr * wpr - wi * wpi;
wi = wi * wpr + t * wpi;
}
mmax = istep;
} }
static void Main(string[] args)
{ int n =*;
float[] data = new float[ * n];
for (int i = ; i < n; i++)
{
data[ * i] = i;
data[ * i + ] = ;
} Complex[] cpData = new Complex[n];
for (int i = ; i < n; i++)
{
cpData[i] = new Complex(data[ * i], data[ * i + ]);
} long s = DateTime.Now.Ticks;
FFT1(cpData, true);
Console.WriteLine("time:" + (DateTime.Now.Ticks - s) / ); s = DateTime.Now.Ticks;
FFT2(data, true);
Console.WriteLine("time:" + (DateTime.Now.Ticks - s) / ); Console.Read();
}
速度上还是差挺多的。。。

好了获得频率数据之后的流程就不再那么烧脑了(都怪自己早早把傅里叶变换还给课本了)。。。
找节奏点的逻辑大概如下(代码有点多就不贴了):
1.根据采样率依次获取数据,每次通过FFT得到一组复数数组。
2.计算出复数的模长,可以表示此频率下的声音大小,可以把一定范围的声音累加起来,可以用来表示低音、中音、高音。
3.对比每一帧的数据变化就可以判断出节奏点(声音变化大,可以表示是一个节奏点)。
其实能得到频域的值,针对不同的功能,大家后面就可以自由发挥了。
关于FFT分析音频的学习的更多相关文章
- I2S音频总线学习
IIS音频总线学习(一)数字音频技术 一.声音的基本概念 声音是通过一定介质传播的连续的波. 图1 声波 重要指标: 振幅:音量的大小 周期:重复出现的时间间隔 频率:指信号每秒钟变化的次数 声音按频 ...
- LINUX内核分析第一周学习总结——计算机是如何工作的
LINUX内核分析第一周学习总结——计算机是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/ ...
- LINUX内核分析第二周学习总结——操作系统是如何工作的
LINUX内核分析第二周学习总结——操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...
- LINUX内核分析第四周学习总结——扒开系统调用的“三层皮”
LINUX内核分析第四周学习总结--扒开系统调用的"三层皮" 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC ...
- linux内核分析第四周学习笔记
linux内核分析第四周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...
- Linux内核分析第二周学习笔记
linux内核分析第二周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...
- linux内核分析第一周学习笔记
linux内核分析第一周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...
- Linux内核分析第一周学习博客 --- 通过反汇编方式学习计算机工作过程
Linux内核分析第一周学习博客 通过反汇编方式学习计算机工作过程 总结: 通过这次对一个简单C程序的反汇编学习,我了解到计算机在实际工作工程中要涉及大量的跳转指针操作.计算机通常是顺序执行一条一条的 ...
- Linux内核分析第二周学习博客——完成一个简单的时间片轮转多道程序内核代码
Linux内核分析第二周学习博客 本周,通过实现一个简单的操作系统内核,我大致了解了操作系统运行的过程. 实验主要步骤如下: 代码分析: void my_process(void) { int i = ...
随机推荐
- 4.shell编程-文本处理三剑客之sed
4.1.sed的选项 sed,流编辑器.对标准输出或文件进行逐行处理. 语法格式 第一种:stdout | sed [option] "pattern command" 第二种:s ...
- Bzoj 2013 [Ceoi2010] A huge tower 题解
2013: [Ceoi2010]A huge tower Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 471 Solved: 321[Submit ...
- tomcat配置https以及配置完成后提示服务器缺少中间证书(已解决)
#### tomcat配置https 准备工作 下载好证书文件,下载的时候可以选择为tomcat文件.我这下载下来是压缩包.解压后就是下图的样子. 以.key结尾的文件是证书的key 以.pem结尾的 ...
- 跨站脚本攻击(反射型xss)笔记(一)
环境:一个微信端(所以用浏览器演示UI有点变形) 下图是未插任何脚本时的原页面. 按惯例,上一波["><script>alert(1)</script>] 无弹 ...
- Golang 高效实践之并发实践context篇
前言 在上篇Golang高效实践之并发实践channel篇中我给大家介绍了Golang并发模型,详细的介绍了channel的用法,和用select管理channel.比如说我们可以用channel来控 ...
- duilib加消息
一.加消息 1. public INotifyUI, 2. void Notify(TNotifyUI& msg); 3. Notify实现 4. m_pManager->AddNoti ...
- USACO-修理牛棚
#include<cstdio> #include<algorithm> using namespace std; int a[201],b[201]; int main() ...
- NOIp 2018 普及&提高组试题答案
你们考的咋样呢?在评论区说出自己的分数吧!
- 从7点到9点写的小程序(用了模块导入,python终端颜色显示,用了点局部和全局可变和不可变作用域,模块全是自定义)
未完待续的小程序 要是能做的好看为啥不做的好看 在同目录下生成程序 1.程序文件 run.py from login import login from register import registe ...
- bugku安卓First_Mobile wp
1 打开题目,下载apk 2 将下载好的apk拖进android killer中,提示文件名过长,随便更改一下文件名即可 3 查看入口文件源码(点击android killer工具栏咖啡杯图标) 4 ...