forked from Sneeds-Feed-and-Seed/sneedacity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInterpolateAudio.cpp
204 lines (173 loc) · 6.08 KB
/
InterpolateAudio.cpp
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
/**********************************************************************
Sneedacity: A Digital Audio Editor
InterpolateAudio.cpp
Dominic Mazzoni
**********************************************************************/
#include "InterpolateAudio.h"
#include <math.h>
#include <stdlib.h>
#include <wx/defs.h>
#include "Matrix.h"
static inline int imin(int x, int y)
{
return x<y? x: y;
}
static inline int imax(int x, int y)
{
return x>y? x: y;
}
// This function is a really dumb, simple way to interpolate audio,
// if the more general InterpolateAudio function below doesn't have
// enough data to work with. If the bad samples are in the middle,
// it's literally linear. If it's on either edge, we add some decay
// back to zero.
static void LinearInterpolateAudio(float *buffer, int len,
int firstBad, int numBad)
{
int i;
float decay = 0.9f;
if (firstBad==0) {
float delta = buffer[numBad] - buffer[numBad+1];
float value = buffer[numBad];
i = numBad - 1;
while (i >= 0) {
value += delta;
buffer[i] = value;
value *= decay;
delta *= decay;
i--;
}
}
else if (firstBad + numBad == len) {
float delta = buffer[firstBad-1] - buffer[firstBad-2];
float value = buffer[firstBad-1];
i = firstBad;
while (i < firstBad + numBad) {
value += delta;
buffer[i] = value;
value *= decay;
delta *= decay;
i++;
}
}
else {
float v1 = buffer[firstBad-1];
float v2 = buffer[firstBad+numBad];
float value = v1;
float delta = (v2 - v1) / (numBad+1);
i = firstBad;
while (i < firstBad + numBad) {
value += delta;
buffer[i] = value;
i++;
}
}
}
// Here's the main interpolate function, using
// Least Squares AutoRegression (LSAR):
void InterpolateAudio(float *buffer, const size_t len,
size_t firstBad, size_t numBad)
{
const auto N = len;
wxASSERT(len > 0 &&
firstBad >= 0 &&
numBad < len &&
firstBad+numBad <= len);
if(numBad >= len)
return; //should never have been called!
if (firstBad == 0) {
// The algorithm below has a weird asymmetry in that it
// performs poorly when interpolating to the left. If
// we're asked to interpolate the left side of a buffer,
// we just reverse the problem and try it that way.
Floats buffer2{ len };
for(size_t i=0; i<len; i++)
buffer2[len-1-i] = buffer[i];
InterpolateAudio(buffer2.get(), len, len-numBad, numBad);
for(size_t i=0; i<len; i++)
buffer[len-1-i] = buffer2[i];
return;
}
Vector s(len, buffer);
// Choose P, the order of the autoregression equation
const int IP =
imin(imin(numBad * 3, 50), imax(firstBad - 1, len - (firstBad + numBad) - 1));
if (IP < 3 || IP >= (int)N) {
LinearInterpolateAudio(buffer, len, firstBad, numBad);
return;
}
size_t P(IP);
// Add a tiny amount of random noise to the input signal -
// this sounds like a bad idea, but the amount we're adding
// is only about 1 bit in 16-bit audio, and it's an extremely
// effective way to avoid nearly-singular matrices. If users
// run it more than once they get slightly different results;
// this is sometimes even advantageous.
for(size_t i=0; i<N; i++)
s[i] += (rand()-(RAND_MAX/2))/(RAND_MAX*10000.0);
// Solve for the best autoregression coefficients
// using a least-squares fit to all of the non-bad
// data we have in the buffer
Matrix X(P, P);
Vector b(P);
for(size_t i = 0; i + P < len; i++)
if (i+P < firstBad || i >= (firstBad + numBad))
for(size_t row=0; row<P; row++) {
for(size_t col=0; col<P; col++)
X[row][col] += (s[i+row] * s[i+col]);
b[row] += s[i+P] * s[i+row];
}
Matrix Xinv(P, P);
if (!InvertMatrix(X, Xinv)) {
// The matrix is singular! Fall back on linear...
// In practice I have never seen this happen if
// we add the tiny bit of random noise.
LinearInterpolateAudio(buffer, len, firstBad, numBad);
return;
}
// This vector now contains the autoregression coefficients
const Vector &a = Xinv * b;
// Create a matrix (a "Toeplitz" matrix, as it turns out)
// which encodes the autoregressive relationship between
// elements of the sequence.
Matrix A(N-P, N);
for(size_t row=0; row<N-P; row++) {
for(size_t col=0; col<P; col++)
A[row][row+col] = -a[col];
A[row][row+P] = 1;
}
// Split both the Toeplitz matrix and the signal into
// two pieces. Note that this code could be made to
// work even in the case where the "bad" samples are
// not contiguous, but currently it assumes they are.
// "u" is for unknown (bad)
// "k" is for known (good)
Matrix Au = MatrixSubset(A, 0, N-P, firstBad, numBad);
Matrix A_left = MatrixSubset(A, 0, N-P, 0, firstBad);
Matrix A_right = MatrixSubset(A, 0, N-P,
firstBad+numBad, N-(firstBad+numBad));
Matrix Ak = MatrixConcatenateCols(A_left, A_right);
const Vector &s_left = VectorSubset(s, 0, firstBad);
const Vector &s_right = VectorSubset(s, firstBad+numBad,
N-(firstBad+numBad));
const Vector &sk = VectorConcatenate(s_left, s_right);
// Do some linear algebra to find the best possible
// values that fill in the "bad" area
Matrix AuT = TransposeMatrix(Au);
Matrix X1 = MatrixMultiply(AuT, Au);
Matrix X2(X1.Rows(), X1.Cols());
if (!InvertMatrix(X1, X2)) {
// The matrix is singular! Fall back on linear...
LinearInterpolateAudio(buffer, len, firstBad, numBad);
return;
}
Matrix X2b = X2 * -1.0;
Matrix X3 = MatrixMultiply(X2b, AuT);
Matrix X4 = MatrixMultiply(X3, Ak);
// This vector contains our best guess as to the
// unknown values
const Vector &su = X4 * sk;
// Put the results into the return buffer
for(size_t i=0; i<numBad; i++)
buffer[firstBad+i] = (float)su[i];
}