关于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 = ...
随机推荐
- laravel扩展包-私有库
创建一个新的laravel项目 composer create-project --prefer-dist laravel/laravel laravel-package "5.5.*&qu ...
- linux配置多个tomcat
1.修改tomcat目录下面conf/server.xml,修改shutdown的port和http port 2.修改bin/catalina.sh 在最前面加上 export CATALINA_B ...
- vuex中使用对象展开运算符
使用场景 当需要进行vuex进行数据状态管理的时候,会使用到mapGetters,mapState,还有自身的计算属性的时候,这个时候就会用到这个了! 1.首先需要安装 npm install bab ...
- CSS元素的基本应用(附加京东面试题)
ONE! 列表~ 列表分为有序列表和无序列表还有定义列表(ul和ol,dl) ul 无序列表 ul它天生自带内边距 还有一个 p 标签也是天生就自带内边距的(内边距 padding) list-st ...
- tar命令中的 -C 作用
我用这个命令:tar zcvf chao.tar.gz /chao/* 打包文件的时候,在压缩包里把 /chao/这个路径也打包进去了. [root@yunwei-test chao]# ls / ...
- 委托在Smobiler自定义控件中运用
委托(Delegate) C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针.委托(Delegate) 是存有对某个方法的引用的一种引用类型变量.可以将方法当作另一个方法的参数来进 ...
- Flutter学习笔记(3)--Dart变量与基本数据类型
一.变量 在Dart里面,变量的声明使用var.Object或Dynamic关键字,如下所示: var name = ‘张三’: 在Dart语言里一切皆为对象,所以如果没有将变量初始化,那么它的默认值 ...
- sql锁的类型介绍:悲观锁,乐观锁,行锁,表锁,页锁,共享锁,排他锁,意向锁
1 悲观锁,乐观锁 悲观锁:顾名思义,很悲观,就是每次拿数据的时候都认为别的线程会修改数据,所以在每次拿的时候都会给数据上锁.上锁之后,当别的线程想要拿数据时,就会阻塞,直到给数据上锁的线程将事务提交 ...
- scrapy基础知识之 处理Redis里的数据:
数据爬回来了,但是放在Redis里没有处理.之前我们配置文件里面没有定制自己的ITEM_PIPELINES,而是使用了RedisPipeline,所以现在这些数据都被保存在redis的xx:items ...
- 安装Win10,ERROR_0x8007025D问题解决
Windows10安装的时候,出现ERROR CODE:0x8007025D 大概提示为:windows 无法安装所需的文件.请确保安装所需的所有文件可用,并重新启动安装. 本人在出现这个问题的原因, ...