- 根据采样定理设置好采样周期,至少大于2倍要采样的信号周期。这里以吉他6根弦为例
名称 |
频率(hz) |
E1 | 329.6276 |
2B | 246.9417 |
3G | 195.9977 |
4D | 146.8324 |
5A | 110 |
6E | 82.4069 |
这里最高是329.6276Hz 最低是82.4069 ,顾采样周期一点要大于329.6276*2=659.2552Hz,考虑到好有紧弦,频率可能会更高,这里我设置了4kHz采样周期以及8kHz采样周期,采集压电陶瓷经过运放放大后的信号,两种方案。均大于659.2552。其它信号自己类比一下,类似的。设置好ADC采样周期,这里我使用定时器设置4kH或者8khz触发ADC+DMA的形式进行采样,采样4096个点,具体配置可以自己查看别的帖子。这里主要讲算法。
2.下面进入到yin算法部分
首先在一段信号中确定一个起始点,记为Xi(这个起始点如何确定一会再说),然后在这段信号中定一段信号范围,这里可以是从前面的4096个点里的1024个点作为算法的作用范围,因为采样到的4096个点很多是无用点,或者是信号周期性不明显的点,一会再说如何找出这个需要yin处理的1024个点的范围)。
这是单个yin的计算公式,其中Len是由Fv(一会再说这个Fv,以及如何确定len)确定的
我们的最终目标计算得出yin的序列,即:,这里可以视为一个yin序列数组,根据这一个数组就可以得出信号周期。
第一步:找出1024个有用点,以及Xi起始点的第一个点:方法思路:从4096个ADC采样点钟逐次找xi~xi+1023的方差或者是标准差,符合大于某个方差(标准差)阈值的数据则视为有效数据,方差、标准差反应了数据样本的离散程度,越大则代表信号越强,幅值越大,这个阈值需要自己确定,可以打印出来对比,以及吉他为例,我在不拨动琴弦的情况下,计算出来的方差均为100以下的数值,一但拨动了琴弦则上升到300以上,那么方差大于300的则为有效的数据。,在这段数据的第一个数则Xi的起点了。
第二步:确定Fv,以及len,以吉他为例子,根据上表我们可以大致确定一个吉他空弦可能的范围,(弦可能松也可能紧),这里我们定为,这里我们的采样率为Fs(4k或者8k),则每两个采样点之间的时间间隔为,由于len是一个变化的值,则假设Xi~X(i+len)这个间隔刚好等于信号的一个周期T,这里的话Xi-X(i+len)则得到最低值,(yin的核心思想就是找最低值),那么有,而这个T周期肯定是满足Fv频率范围的,因为周期与频率是倒数关系所以有,即
,假设采样率Fs为8000,则len的最小值为8,最大值为400,那么Yin序列内的个数就应该是400-8=393个yin数据也就是yin序列数组的大小,
//开始yin序列
for(len=8;len<=400;len++) //计算len范围内的序列 Ylen=I到N的累加|Xi-Xi+len| (小写为下标)
{ //BUG
for(i=Start_x;i<=(max2-len);i++)//累加
{
Yin_Seq[n]+=abs(input[i]-input[i+len]);
mean++;
}
Yin_Seq[n]/=mean;
mean=0;
// printf("%ld\n",Yin_Seq[n]);
n++;
}
其中input为输入的1024个有效样本,最后得出的大小为393的Yin_Seq序列数组。
假设采样率设置为4000k,(因为8k的采集波形我没有做记录,只做了4k采样率的波形图记录,这里采样率4k\8k不影响最终的结果,影响的只是精度以及yin序列数组的大小)这里拨动一下吉他空弦的6弦(频率为82.4069Hz,周期为0.0121349061789729s),采样一组数据打印,放到excel作分析,如下图所示:
可以看到在X轴的第95、143的点取得最低(Xi-X(i+len)),那么这两个点的间隔就是该弦目前的频率,143-95=48,我们的采样率是4000Hz,即为0.00025s,0.0121349061789729/0.00025=48.53962471589151,刚好吻合,其他琴弦计算方法也是一样,这里就不一一列举了。
上图的这种波形用C语言找最小值不太好找,所以甚至有可能,有一个点也是比较低的,但是相邻的却不是你想要的点,这种情况下我们可以再做多一步处理,加多一次限幅,这个限幅阈值可以自己根据打印出来的波形自己分析确定,我的思路是先排序按照升序的方法,然后找出前n个最小值,这个n自己确定。然后取平均,超过这个n的幅值则统统设为一个较大的数字,这里我设为400,限幅处理后的波形如下图所示:
怎么样,这样一来是不是就很好处理了,简单写一段代码就可以找出两个极值点的间隔了。
最后总结一下:该过程涉及到了奈奎斯特采样定理、方差(标准差)、排序、yin、限幅滤波等一系列算法,由于算法代码是用到工作上的,出于保密,不方便透露,本着互联网开放共享的原则,这里和大家分享,这里的总体思路已经非常的明朗了,可以自己写代码实现。这里不单单是可以用在音频处理上,也可以用在很多信号的处理上,具体的可以自己去研究。