mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
... note the swap of target_link_libraries lines in src/CMakeLists.txt, needed to build at least on macOS, becuase FFT.h must be looked up first in lib-math, not in lib-src/twolame Also making a dependency cycle of SampleFormat and Dither! But we will tolerate that within one small library.
205 lines
6.1 KiB
C++
205 lines
6.1 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: 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];
|
|
}
|