【基频提取算法-YIN】

本文对基频提取算法 YIN 做以介绍。如有表述不当之处欢迎批评指正。欢迎任何形式的转载,但请务必注明出处。

文章目录

  • 1. 引言
  • 2. YIN 各模块代码讲解
    • 2.1. 差分函数的实现
    • 2.2. 累积均值归一化差分函数的实现
    • 2.3. 绝对阈值
    • 2.4. 抛物线插值
    • 2.5. 最优局部估计
  • 3. 总结

1. 引言

之前的好几个项目都用到了基频提取算法 Pyin,实验结果显示其准确度较高,能满足项目需求。Pyin算法是在 YIN 算法的基础上改进而来,因此,有必要深入研究学习下 YIN 算法。

本文结合开源代码详细介绍了 YIN 算法各个模块的实现细节,如果不了解该算法,可以先阅读笔者另一篇关于该算法论文的博客 https://blog.csdn.net/wjrenxinlei/article/details/136306282。开源代码的地址为:https://code.soundsoftware.ac.uk/projects/pyin/files

2. YIN 各模块代码讲解

本章将针对 YIN 算法的各个模块,结合开源代码来深入学习。

2.1. 差分函数的实现

代码实现的差分函数是论文中的公式 ( 7 ) (7) (7),如下:
d t ( τ ) = r t ( 0 ) + r t + τ ( 0 ) − 2 r t ( τ ) \begin{align} d_t(\tau) = r_t(0) + r_{t+\tau}(0) - 2r_t(\tau)\tag{7} \end{align} dt(τ)=rt(0)+rt+τ(0)2rt(τ)(7)

该差分函数由三项构成,都可以由公式 ( 1 ) (1) (1) 计算得到:
r t ( τ ) = ∑ j = t + 1 t + W x j x j + τ \begin{align} r_t(\tau) = \sum_{j=t+1}^{t+W} x_j x_{j+\tau} \tag{1} \end{align} rt(τ)=j=t+1t+Wxjxj+τ(1)

其中,第一项可以根据公式 ( 1 ) (1) (1) 直接计算,开源代码实现如下:

// POWER TERM CALCULATION
// ... for the power terms in equation (7) in the Yin paper
powerTerms[0] = 0.0;
for (size_t j = 0; j < yinBufferSize; ++j) {powerTerms[0] += in[j] * in[j];
}

第二项可以递归计算,因为 r t ( τ ) r_t(\tau) rt(τ) r t ( τ + 1 ) r_t(\tau+1) rt(τ+1) W W W 个求和项中,只有一项是不一样的,其余 W − 1 W-1 W1 项是完全相同的,开源代码实现如下:

// now iteratively calculate all others (saves a few multiplications)
for (size_t tau = 1; tau < yinBufferSize; ++tau) {powerTerms[tau] = powerTerms[tau-1] - in[tau-1] * in[tau-1] + in[tau+yinBufferSize] * in[tau+yinBufferSize];  
}
// 笔者注:源码实现有误,上述等号右边最后一项应该是 in[tau+yinBufferSize-1] * in[tau+yinBufferSize-1];

这儿需要注意的是,窗长 W W W 和 延迟 τ \tau τ 的取值。窗长应该满足 2 W ≤ 2W \leq 2W 音频帧长,这块取最大值 yinBufferSize(该值是音频帧长的一半,具体可见源码)。窗长确定好之后, τ \tau τ 能取到的最大值也就随之确定了,其能取到的最大值应该是 帧长 - 窗长 = yinBufferSize,这块可以看到源码实现中 τ \tau τ 的最大取值为 yinBuferSize - 1。即在源码实现中 τ = 0 , 1 , ⋯ , \tau = 0, 1, \cdots, τ=0,1,, yinBuferSize - 1

第三项是序列的自(互)相关,这块的计算用到了数字信号处理中的一个基本知识点,即可以使用 FFT 来加速计算自(互)相关。下面详细描述下该计算过程。
首先,得到参与相关运算的两个序列。第一个序列是输入给算法的音频帧。第二个序列是由该音频帧的前 yinBufferSize 个采样点(这是由于有窗长的限制)得到的。首先,对这 yinBufferSize 个采样点先做逆序操作(这是相关和卷积的主要区别,如果不做逆序操作,那么接下来的步骤算出来的将会是卷积),然后对其补零(使其和输入给算法的音频帧长度一样)得到第二个序列。
接着,对上述得到的两个序列分别做 FFT 运算。
然后,将上述得到的两个 FFT 序列相乘,并对相乘得到的结果做 IFFT 运算。
最后,对上述得到的结果取下标(从 0 开始)为 [ yinBufferSize - 1, 2 * yinBufferSize - 2] 的结果。

开源代码实现如下:

// YIN-STYLE AUTOCORRELATION via FFT
// 1. data
esfft(uint(frameSize), false, in, nullImag, audioTransformedReal, audioTransformedImag);// 2. half of the data, disguised as a convolution kernel
for (size_t j = 0; j < yinBufferSize; ++j) {kernel[j] = in[yinBufferSize-1-j];kernel[j+yinBufferSize] = 0;
}
esfft(uint(frameSize), false, kernel, nullImag, kernelTransformedReal, kernelTransformedImag);// 3. convolution via complex multiplication -- written into
for (size_t j = 0; j < frameSize; ++j) {yinStyleACFReal[j] = audioTransformedReal[j]*kernelTransformedReal[j] - audioTransformedImag[j]*kernelTransformedImag[j]; // realyinStyleACFImag[j] = audioTransformedReal[j]*kernelTransformedImag[j] + audioTransformedImag[j]*kernelTransformedReal[j]; // imaginary
}
esfft(uint(frameSize), true, yinStyleACFReal, yinStyleACFImag, audioTransformedReal, audioTransformedImag);

其中,frameSize = yinBufferSize * 2 是音频帧长。in 是输入给算法的音频帧。

最后,将上面三项相加,得到差分函数公式 ( 7 ) (7) (7) 的最终结果。开源代码实现如下:

// CALCULATION OF difference function
// ... according to (7) in the Yin paper.
for (size_t j = 0; j < yinBufferSize; ++j) {// taking only the real partyinBuffer[j] = powerTerms[0] + powerTerms[j] - 2 * audioTransformedReal[j+yinBufferSize-1];
}

2.2. 累积均值归一化差分函数的实现

这块直接根据公式 ( 8 ) (8) (8) 的定义来实现:
d t ′ ( τ ) = { 1 , if τ = 0 , d t ( τ ) / [ ( 1 / τ ) ∑ j = 1 τ d t ( j ) ] , otherwise . (8) \begin{align} d_t^{'}(\tau) = \left\{ \begin{array}{lc} 1, & \text{if} \; \tau = 0, \\ d_t(\tau)/[(1/\tau)\sum_{j=1}^{\tau}{d_t(j)}], & \text{otherwise}. \\ \end{array} \right. \end{align} \tag{8} dt(τ)={1,dt(τ)/[(1/τ)j=1τdt(j)],ifτ=0,otherwise.(8)

开源代码实现如下:

void cumulativeDifference(double *yinBuffer, const size_t yinBufferSize)
{    size_t tau;yinBuffer[0] = 1;double runningSum = 0;for (tau = 1; tau < yinBufferSize; ++tau) {runningSum += yinBuffer[tau];if (runningSum == 0){yinBuffer[tau] = 1;} else {yinBuffer[tau] *= tau / runningSum;}}    
}

2.3. 绝对阈值

这一步的目的是,找到第一个小于阈值的谷值所对应的 τ \tau τ,如果都大于阈值,那么找到最小的谷值所对应的 τ \tau τ。开源代码实现如下:

int absoluteThreshold(const double *yinBuffer, const size_t yinBufferSize, const double thresh)
{size_t tau;size_t minTau = 0;double minVal = 1000.;// using Joren Six's "loop construct" from TarsosDSPtau = 2;while (tau < yinBufferSize){if (yinBuffer[tau] < thresh){while (tau+1 < yinBufferSize && yinBuffer[tau+1] < yinBuffer[tau]){++tau;}return tau;} else {if (yinBuffer[tau] < minVal){minVal = yinBuffer[tau];minTau = tau;}}++tau;}if (minTau > 0){return -minTau;}return 0;
}

其中,第二个 while 循环是为了找到谷值,else 部分是为了找到最小的谷值所对应的 τ \tau τ.

2.4. 抛物线插值

插值是为了找到更精确的周期估计。开源代码中是在相邻的三组样本上,使用简化的抛物线插值公式来寻找,实现如下:

double parabolicInterpolation(const double *yinBuffer, const size_t tau, const size_t yinBufferSize) 
{// this is taken almost literally from Joren Six's Java implementationif (tau == yinBufferSize) // not valid anyway.{return static_cast<double>(tau);}double betterTau = 0.0;size_t x0;size_t x2;if (tau < 1) {x0 = tau;} else {x0 = tau - 1;}if (tau + 1 < yinBufferSize) {x2 = tau + 1;} else {x2 = tau;}if (x0 == tau) {if (yinBuffer[tau] <= yinBuffer[x2]) {betterTau = tau;} else {betterTau = x2;}} else if (x2 == tau) {if (yinBuffer[tau] <= yinBuffer[x0]) {betterTau = tau;} else {betterTau = x0;}} else {float s0, s1, s2;s0 = yinBuffer[x0];s1 = yinBuffer[tau];s2 = yinBuffer[x2];// fixed AUBIO implementation, thanks to Karl Helgason:// (2.0f * s1 - s2 - s0) was incorrectly multiplied with -1betterTau = tau + (s2 - s0) / (2 * (2 * s1 - s2 - s0));// std::cerr << tau << " --> " << betterTau << std::endl;}return betterTau;
}

代码的前半部分都是在处理边界情况,核心是在最后的 else 部分,其对应的公式为:
x p e a k = x 0 + 1 2 y + 1 − y − 1 2 y 0 − y − 1 − y + 1 \begin{align} x_{peak} = x_{0} + \frac{1}{2} \frac{y_{+1}-y_{-1}}{2y_{0} - y_{-1} - y_{+1}} \notag \end{align} xpeak=x0+212y0y1y+1y+1y1

2.5. 最优局部估计

开源代码中暂且没有该步骤的实现。

3. 总结

结合公式和开源代码的实现可以发现,差分函数的快速实现以及抛物线插值的简化版本这块是值得学习和吸收的,其余部分基本按照公式实现,相对而言比较简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/714696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免杀实战-EDR对抗

文章目录 杀软分析BOF.NET 杀软分析 x64dgb简单调试发现该edr在r3环对ntdll.dll和kernel32.dll关键函数均存在hook&#xff0c;这里硬盘读取原来的dll进行重新加载&#xff0c;原理如图 loader // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #in…

DSI2协议之BTA行为理解

概念: DSI协议spec支持总线控制权在master和slave之间发生交换,即通过bus turn around来实现; BUS TURN AROUND: BTA 的实现是通过controller—>cdphy的turnrequest信号来实现; 关于控制器发出turnrequest给phy,phy通过lvds/trio线输出turnaround sequence如下图中…

LeetCode刷题笔记之二叉树(四)

一、二叉搜索树的应用 1. 700【二叉搜索树中的搜索】 题目&#xff1a; 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 null 。代码&a…

BUGKU 本地管理员

打开环境&#xff0c;先F12查看看到一串代码。Base64解码一下&#xff0c;得到的应该是密码&#xff0c;然后输入admin | test123试一下 使用BP抓包&#xff0c;修改XFF&#xff0c;得到flag

将镜像上传到私有镜像仓库Harbor

首先你需要安装Harbor服务&#xff1a; https://blog.csdn.net/qq_50247813/article/details/136388229 客户端已经安装docker&#xff1a; https://docs.docker.com/engine/install/centos/ 在docker客户端登录 Harbor 我的Harbor 服务器地址&#xff1a; 192.168.44.161 账号…

关于编写测试用例的一些思考

测试用例是QA同学的基本功&#xff0c;每个人都有一套编写测试用例的体系&#xff0c;本文是作者结合自身的工作经验以及阅读一些测试相关的书籍后的一些看法&#xff0c;欢迎大家一起讨论学习。 测试设计 测试用例格式 面试中一些常见的问题 1.APP测试与服务端测试的区别&am…

微服务中的Feign:优雅实现远程调用的秘密武器(二)

本系列文章简介&#xff1a; 本系列文章将深入探讨Feign的特点、原理以及在微服务中的应用场景&#xff0c;帮助读者更好地理解和使用这个优秀的远程调用工具。无论您是初学者还是有经验的开发人员&#xff0c;本文都将为您揭示Feign的秘密&#xff0c;并带您一起走进微服务的世…

何恺明新作 l-DAE:解构扩散模型

何恺明新作 l-DAE&#xff1a;解构扩散模型 提出背景扩散模型步骤如何在不影响数据表征能力的同时简化模型&#xff1f;如何进一步推动模型向经典DAE靠拢&#xff1f;如何去除对生成任务设计的DDM中不适用于自监督学习的部分&#xff1f;如何改进DDM以专注于清晰图像表示的学习…

2024华为软件测试笔试面试真题,抓紧收藏不然就看不到了

一、选择题 1、对计算机软件和硬件资源进行管理和控制的软件是&#xff08;D&#xff09; A.文件管理程序 B.输入输出管理程序 C.命令出来程序 D.操作系统 2、在没有需求文档和产品说明书的情况下只有哪一种测试方法可以进行的&#xff08;A&#xff09; A.错误推测法测试…

Docker 快速入门实操教程(完结)

Docker 快速入门实操教程&#xff08;完结&#xff09; Docker&#xff0c;启动&#xff01; 如果安装好Docker不知道怎么使用&#xff0c;不理解各个名词的概念&#xff0c;不太了解各个功能的用途&#xff0c;这篇文章应该会对你有帮助。 前置条件&#xff1a;已经安装Doc…

【Hadoop】在spark读取clickhouse中数据

读取clickhouse数据库数据 import scala.collection.mutable.ArrayBuffer import java.util.Properties import org.apache.spark.sql.SaveMode import org.apache.spark.sql.SparkSessiondef getCKJdbcProperties(batchSize: String "100000",socketTimeout: Strin…

IOS 发布遇到“Unable to authenticate with App Store Connect”错误咋解决?

问题&#xff1a; 在开发ios app后&#xff0c;先发布adhoc版本&#xff0c;测试通过后&#xff0c;再发布testflight版本测试&#xff0c;但是可能会遇到一下问题。 解决办法&#xff1a; 在Signing &Capabilities中&#xff0c;在ios下边要指定有发布权限的Team账号&a…

PAT (Basic Level) Practice | 判断题

判断题的评判很简单&#xff0c;本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分。 输入格式 输入在第一行给出两个不超过 100 的正整数 N 和 M&#xff0c;分别是学生人数和判断题数量。第二行给出 M 个不超过 5 的正整数&#xff0c;是每道题的满分值。第…

pytorch基础2-数据集与归一化

专题链接&#xff1a;https://blog.csdn.net/qq_33345365/category_12591348.html 本教程翻译自微软教程&#xff1a;https://learn.microsoft.com/en-us/training/paths/pytorch-fundamentals/ 初次编辑&#xff1a;2024/3/2&#xff1b;最后编辑&#xff1a;2024/3/2 本教程…

迪杰斯特拉算法的具体应用

fill与memset的区别介绍 例一 #include <iostream> #include <algorithm> using namespace std; const int maxn500; const int INF1000000000; bool isin[maxn]{false}; int G[maxn][maxn]; int path[maxn],rescue[maxn],num[maxn]; int weight[maxn]; int cityn…

【深度学习数学基础】Hebbian图(Hebbian Graph)

Hebbian图&#xff08;Hebbian Graph&#xff09;是一种基于神经科学原理的网络结构&#xff0c;它受到唐纳德赫布&#xff08;Donald Hebb&#xff09;提出的赫布学习规则&#xff08;Hebb’s rule&#xff09;的启发。赫布学习规则是神经科学中描述神经元之间突触连接如何通过…

模板方法模式 详解 设计模式

模板方法模式 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤延迟到子类中实现。这种模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 结构 抽象类&#xff08;Abstract Class&#xff09;&#xff1a;负责给出一…

JavaWeb老杜视频笔记总结,Servlet-JSP

关于直播 什么时间直播&#xff1f; 晚上8:00到10:00 每周直播几天&#xff1f; 3天&#xff08;周一、周三、周五&#xff09; 本周比较特殊&#xff1a;周四周五周六三天直播&#xff0c;从下周开始就是一三五直播。 直播什么内容&#xff1f; 从JavaWEB开始。&#xff08…

《深入浅出红黑树:一起动手实现自平衡的二叉搜索树》

一、分析 1. 红黑树的性质 红黑树是一种自平衡的二叉搜索树&#xff0c;它具有以下五个性质&#xff1a; &#xff08;1&#xff09;节点是红色或黑色。 &#xff08;2&#xff09;根节点是黑色。 &#xff08;3&#xff09;所有叶子节点&#xff08;NIL节点&#xff09;是…

探索数据宇宙:深入解析大数据分析与管理技术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…