forked from ashokfernandez/Yin-Pitch-Tracking
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Yin.c
222 lines (185 loc) · 7.04 KB
/
Yin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <stdint.h> /* For standard interger types (int16_t) */
#include <stdlib.h> /* For call to malloc */
#include "Yin.h"
/* ------------------------------------------------------------------------------------------
--------------------------------------------------------------------------- PRIVATE FUNCTIONS
-------------------------------------------------------------------------------------------*/
/**
* Step 1: Calculates the squared difference of the signal with a shifted version of itself.
* @param buffer Buffer of samples to process.
*
* This is the Yin algorithms tweak on autocorellation. Read http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf
* for more details on what is in here and why it's done this way.
*/
void Yin_difference(Yin *yin, int16_t* buffer){
int16_t i;
int16_t tau;
float delta;
/* Calculate the difference for difference shift values (tau) for the half of the samples */
for(tau = 0 ; tau < yin->halfBufferSize; tau++){
/* Take the difference of the signal with a shifted version of itself, then square it.
* (This is the Yin algorithm's tweak on autocorellation) */
for(i = 0; i < yin->halfBufferSize; i++){
delta = buffer[i] - buffer[i + tau];
yin->yinBuffer[tau] += delta * delta;
}
}
}
/**
* Step 2: Calculate the cumulative mean on the normalised difference calculated in step 1
* @param yin #Yin structure with information about the signal
*
* This goes through the Yin autocorellation values and finds out roughly where shift is which
* produced the smallest difference
*/
void Yin_cumulativeMeanNormalizedDifference(Yin *yin){
int16_t tau;
float runningSum = 0;
yin->yinBuffer[0] = 1;
/* Sum all the values in the autocorellation buffer and nomalise the result, replacing
* the value in the autocorellation buffer with a cumulative mean of the normalised difference */
for (tau = 1; tau < yin->halfBufferSize; tau++) {
runningSum += yin->yinBuffer[tau];
yin->yinBuffer[tau] *= tau / runningSum;
}
}
/**
* Step 3: Search through the normalised cumulative mean array and find values that are over the threshold
* @return Shift (tau) which caused the best approximate autocorellation. -1 if no suitable value is found over the threshold.
*/
int16_t Yin_absoluteThreshold(Yin *yin){
int16_t tau;
/* Search through the array of cumulative mean values, and look for ones that are over the threshold
* The first two positions in yinBuffer are always so start at the third (index 2) */
for (tau = 2; tau < yin->halfBufferSize ; tau++) {
if (yin->yinBuffer[tau] < yin->threshold) {
while (tau + 1 < yin->halfBufferSize && yin->yinBuffer[tau + 1] < yin->yinBuffer[tau]) {
tau++;
}
/* found tau, exit loop and return
* store the probability
* From the YIN paper: The yin->threshold determines the list of
* candidates admitted to the set, and can be interpreted as the
* proportion of aperiodic power tolerated
* within a periodic signal.
*
* Since we want the periodicity and and not aperiodicity:
* periodicity = 1 - aperiodicity */
yin->probability = 1 - yin->yinBuffer[tau];
break;
}
}
/* if no pitch found, tau => -1 */
if (tau == yin->halfBufferSize || yin->yinBuffer[tau] >= yin->threshold) {
tau = -1;
yin->probability = 0;
}
return tau;
}
/**
* Step 5: Interpolate the shift value (tau) to improve the pitch estimate.
* @param yin [description]
* @param tauEstimate [description]
* @return [description]
*
* The 'best' shift value for autocorellation is most likely not an interger shift of the signal.
* As we only autocorellated using integer shifts we should check that there isn't a better fractional
* shift value.
*/
float Yin_parabolicInterpolation(Yin *yin, int16_t tauEstimate) {
float betterTau;
int16_t x0;
int16_t x2;
/* Calculate the first polynomial coeffcient based on the current estimate of tau */
if (tauEstimate < 1) {
x0 = tauEstimate;
}
else {
x0 = tauEstimate - 1;
}
/* Calculate the second polynomial coeffcient based on the current estimate of tau */
if (tauEstimate + 1 < yin->halfBufferSize) {
x2 = tauEstimate + 1;
}
else {
x2 = tauEstimate;
}
/* Algorithm to parabolically interpolate the shift value tau to find a better estimate */
if (x0 == tauEstimate) {
if (yin->yinBuffer[tauEstimate] <= yin->yinBuffer[x2]) {
betterTau = tauEstimate;
}
else {
betterTau = x2;
}
}
else if (x2 == tauEstimate) {
if (yin->yinBuffer[tauEstimate] <= yin->yinBuffer[x0]) {
betterTau = tauEstimate;
}
else {
betterTau = x0;
}
}
else {
float s0, s1, s2;
s0 = yin->yinBuffer[x0];
s1 = yin->yinBuffer[tauEstimate];
s2 = yin->yinBuffer[x2];
// fixed AUBIO implementation, thanks to Karl Helgason:
// (2.0f * s1 - s2 - s0) was incorrectly multiplied with -1
betterTau = tauEstimate + (s2 - s0) / (2 * (2 * s1 - s2 - s0));
}
return betterTau;
}
/* ------------------------------------------------------------------------------------------
---------------------------------------------------------------------------- PUBLIC FUNCTIONS
-------------------------------------------------------------------------------------------*/
/**
* Initialise the Yin pitch detection object
* @param yin Yin pitch detection object to initialise
* @param bufferSize Length of the audio buffer to analyse
* @param threshold Allowed uncertainty (e.g 0.05 will return a pitch with ~95% probability)
*/
void Yin_init(Yin *yin, int16_t bufferSize, float threshold){
/* Initialise the fields of the Yin structure passed in */
yin->bufferSize = bufferSize;
yin->halfBufferSize = bufferSize / 2;
yin->probability = 0.0;
yin->threshold = threshold;
/* Allocate the autocorellation buffer and initialise it to zero */
yin->yinBuffer = (float *) malloc(sizeof(float)* yin->halfBufferSize);
int16_t i;
for(i = 0; i < yin->halfBufferSize; i++){
yin->yinBuffer[i] = 0;
}
}
/**
* Runs the Yin pitch detection algortihm
* @param yin Initialised Yin object
* @param buffer Buffer of samples to analyse
* @return Fundamental frequency of the signal in Hz. Returns -1 if pitch can't be found
*/
float Yin_getPitch(Yin *yin, int16_t* buffer){
int16_t tauEstimate = -1;
float pitchInHertz = -1;
/* Step 1: Calculates the squared difference of the signal with a shifted version of itself. */
Yin_difference(yin, buffer);
/* Step 2: Calculate the cumulative mean on the normalised difference calculated in step 1 */
Yin_cumulativeMeanNormalizedDifference(yin);
/* Step 3: Search through the normalised cumulative mean array and find values that are over the threshold */
tauEstimate = Yin_absoluteThreshold(yin);
/* Step 5: Interpolate the shift value (tau) to improve the pitch estimate. */
if(tauEstimate != -1){
pitchInHertz = YIN_SAMPLING_RATE / Yin_parabolicInterpolation(yin, tauEstimate);
}
return pitchInHertz;
}
/**
* Certainty of the pitch found
* @param yin Yin object that has been run over a buffer
* @return Returns the certainty of the note found as a decimal (i.e 0.3 is 30%)
*/
float Yin_getProbability(Yin *yin){
return yin->probability;
}