关于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 = ...
随机推荐
- SpringMVC_Two
SpringMVC_Two 响应数据和结果视图 创建工厂 导坐标: </load-on-startup> </servlet> <servlet-mapping> ...
- BFS(二):数的变换
[例1]整数变换(POJ 3278 “Catch That Cow”) 给定两个整数a和b(0 ≤a,b≤100,000),要求把a变换到b.变换规则为:(1)当前数加1:(2)当前数减1:(3)当前 ...
- JAVA写入大文件DEMO
/** * 读取filePath的文件 * @param filePath 文件的路径 * @return List集合 文件中一行一行的数据 ...
- HDU 6060:RXD and dividing(DFS)
题目链接 题意 给出n个点,要把除1以外的点分成k个集合,然后对于每个集合要和1这个点一起求一个最小生成树,然后问这k个最小生成树的最大总和是多少. 思路 因为每个集合都包含1这个点,因此对于每个点都 ...
- redis 基础数据结构实现
参考文献 redis数据结构分析 Skip List(跳跃表)原理详解 redis 源码分析之内存布局 Redis 基础数据结构与对象 Redis设计与实现-第7章-压缩列表 在redis中构建了自己 ...
- django的命令, 配置,以及django使用mysql的流程
1.Django的命令: 下载 pip install django==1.11.16 pip install django==1.11.16 -i 源 创建项目 django-admin start ...
- 【POJ - 2718】Smallest Difference(搜索 )
-->Smallest Difference 直接写中文了 Descriptions: 给定若干位十进制数,你可以通过选择一个非空子集并以某种顺序构建一个数.剩余元素可以用相同规则构建第二个数. ...
- golang开发:类库篇(四)配置文件解析器goconfig的使用
为什么要使用goconfig解析配置文件 目前各语言框架对配置文件书写基本都差不多,基本都是首先配置一些基础变量,基本变量里面有环境的配置,然后通过环境变量去获取该环境下的变量.例如,生产环境跟测试环 ...
- Python 3.5学习笔记(第二章)
本章内容 1.模块 2.数据类型与数据运算 3.进制 4.byte 与 string 的互相转换 5.列表 6.元组 7.字符串操作 8.字典 一.模块 Python 把某些常用的定义存放在文件中,为 ...
- 8086 IO读写操作
如图所示,通过8086来读写io口,实现流水灯以及开关.本电路是基于8086最小模式下的三总线结构添加的,三总线结构原理较为复杂本篇就不对其原理进行介绍了,大家可以自行查阅相关引脚的功能从而实现. 本 ...