【基頻提取算法-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+W?xj?xj+τ??(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 W?1 項是完全相同的,開源代碼實現如下:

// 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?+21?2y0??y?1??y+1?y+1??y?1???

2.5. 最優局部估計

開源代碼中暫且沒有該步驟的實現。

3. 總結

結合公式和開源代碼的實現可以發現,差分函數的快速實現以及拋物線插值的簡化版本這塊是值得學習和吸收的,其余部分基本按照公式實現,相對而言比較簡單。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714696.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714696.shtml
英文地址,請注明出處:http://en.pswp.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…