File: f0.cpp

package info (click to toggle)
audacity 2.1.2-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 86,844 kB
  • sloc: ansic: 225,005; cpp: 221,240; sh: 27,327; python: 16,896; makefile: 8,186; lisp: 8,002; perl: 317; xml: 307; sed: 16
file content (139 lines) | stat: -rw-r--r-- 3,703 bytes parent folder | download | duplicates (6)
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
// f0 -- frequency estimation

#include <stdio.h>



// Estimate a local minimum (or maximum) using parabolic 
// interpolation. The parabola is defined by the points 
// (x1,y1),(x2,y2), and (x3,y3).
float parabolic_interp(float x1, float x2, float x3, float y1, float y2, float y3, float *min)
{
  float a, b, c;
  float pos;

  //  y1=a*x1^2+b*x1+c
  //  y2=a*x2^2+b*x2+c
  //  y3=a*x3^2+b*x3+c

  //  y1-y2=a*(x1^2-x2^2)+b*(x1-x2)
  //  y2-y3=a*(x2^2-x3^2)+b*(x2-x3)

  //  (y1-y2)/(x1-x2)=a*(x1+x2)+b
  //  (y2-y3)/(x2-x3)=a*(x2+x3)+b

  a= ((y1-y2)/(x1-x2)-(y2-y3)/(x2-x3))/(x1-x3);
  b= (y1-y2)/(x1-x2) - a*(x1+x2);
  c= y1-a*x1*x1-b*x1;

  *min= c;

  // dy/dx = 2a*x + b = 0
  
  pos= -b/2.0/a;

  return pos;

}



float f0_estimate(float *samples, int n, int m, float threshold, float *results, float *min)
    // samples is a buffer of samples
    // n is the number of samples, equals twice longest period, must be even
    // m is the shortest period in samples
    // results is an array of size n/2 - m + 1, the number of different lags
{
    // work from the middle of the buffer:
    int middle = n / 2;
    int i, j; // loop counters
    // how many different lags do we compute?
    float left_energy = 0;
    float right_energy = 0;
    // for each window, we keep the energy so we can compute the next one 
    // incrementally. First, we need to compute the energies for lag m-1:
    for (i = 0; i < m - 1; i++) {
        float left = samples[middle - 1 - i];
        left_energy += left * left;
        float right = samples[middle + i];
        right_energy += right * right;
    }
    for (i = m; i <= middle; i++) {
        // i is the lag and the length of the window
        // compute the energy for left and right
        float left = samples[middle - i];
        left_energy += left * left;
        float right = samples[middle - 1 + i];
 
        right_energy += right * right;
        //  compute the autocorrelation
        float auto_corr = 0;
        for (j = 0; j < i; j++) {
            auto_corr += samples[middle - i + j] * samples[middle + j];
        }
        float non_periodic = (left_energy + right_energy - 2 * auto_corr);// / i;
        results[i - m] = non_periodic;

    }


    // normalize by the cumulative sum
    float cum_sum=0.0;
    for (i = m; i <= middle; i++) {
      cum_sum+=results[i-m];
      results[i-m]=results[i-m]/(cum_sum/(i-m+1));

    }

    int min_i=m;  // value of initial estimate
    for (i = m; i <= middle; i++) {
      if (results[i - m] < threshold) {
	min_i=i;
	break;
      } else if (results[i-m]<results[min_i-m])
	min_i=i;

    }
    


    // use parabolic interpolation to improve estimate
    float freq;
    if (i>m && i<middle) {
      freq=parabolic_interp((float)(min_i-1),(float)(min_i),(float)(min_i+1),
      				results[min_i-1-m],results[min_i-m],results[min_i+1-m], min);
      //freq=(float)min_i;
      printf("%d %f\n",min_i,freq);
    } else {
      freq=(float)min_i;
      *min=results[min_i-m];
    }
    return freq;
}



float best_f0(float *samples, int n, int m, float threshold, int Tmax)
 // samples is a buffer of samples
 // n is the number of samples, equals twice longest period plus Tmax, must be even
 // m is the shortest period in samples
 // threshold is the 
 // results is an array of size n/2 - m + 1, the number of different lags
  // Tmax is the length of the search 
{
  float* results=new float[n/2-m+1];
  float min=10000000.0;
  float temp;
  float best_f0;
  float f0;

  for (int i=0; i<Tmax; i++) {
    f0=f0_estimate(&samples[i], n, m, threshold, results, &temp);
    if (temp<min) {
      min=temp;
      best_f0=f0;
    }
  }
  delete(results);
  return best_f0;
}