1
0
mirror of https://github.com/cookiengineer/audacity synced 2026-04-24 15:03:53 +02:00

Extensive changes to improve NoteTrack display and (some) editing, NoteTrack playback via MIDI, and Midi-to-Audio alignment.

This commit is contained in:
rbdannenberg
2010-09-18 21:02:36 +00:00
parent f6327602e8
commit a1f0e5ed5b
96 changed files with 5679 additions and 3566 deletions

View File

@@ -8,6 +8,7 @@
*/
#include "assert.h"
#include <math.h>
#include "comp_chroma.h"
#include "sautils.h"
// the following are needed to get Scorealign
@@ -48,9 +49,15 @@ void save_path(char *filename);
class Curvefit : public Hillclimb {
public:
Curvefit(Scorealign *sa_, bool verbose_) { sa = sa_; verbose = verbose_; }
Curvefit(Scorealign *sa_, bool verbose_) {
sa = sa_;
verbose = verbose_;
p1_cache = p2_cache = d_cache = x = NULL;
}
~Curvefit();
virtual double evaluate();
void setup(int n);
void set_step_size(double ss);
double *get_x() { return x; }
private:
Scorealign *sa;
@@ -101,35 +108,41 @@ void Curvefit::setup(int segments)
// number of parameters is greater than segments because the left
// col of segment i is parameter i, so the right col of
// the last segment == parameter[segments].
n = segments + 1;
parameters = ALLOC(double, n);
Hillclimb::setup(segments + 1);
p1_cache = ALLOC(double, n);
p2_cache = ALLOC(double, n);
d_cache = ALLOC(double, n);
x = ALLOC(double, n);
step_size = ALLOC(double, n);
min_param = ALLOC(double, n);
max_param = ALLOC(double, n);
int i;
// ideal frames per segment
float seg_length = ((float) (sa->file1_frames - 1)) / segments;
float seg_length = ((float) (sa->last_x - sa->first_x)) / segments;
for (i = 0; i < n; i++) { // initialize cache keys to garbage
p1_cache[i] = p2_cache[i] = -999999.99;
// initialize x values
x[i] = ROUND(i * seg_length);
x[i] = ROUND(sa->first_x + i * seg_length);
// now initialize parameters based on pathx/pathy/time_map
// time_map has y values for each x
parameters[i] = sa->time_map[(int) x[i]];
assert(parameters[i] >= 0);
if (verbose)
printf("initial x[%d] = %g, parameters[%d] = %g\n",
i, x[i], i, parameters[i]);
step_size[i] = 0.5;
min_param[i] = 0;
max_param[i] = sa->file2_frames - 1;
max_param[i] = sa->last_y;
}
}
Curvefit::~Curvefit()
{
if (p1_cache) FREE(p1_cache);
if (p2_cache) FREE(p2_cache);
if (d_cache) FREE(d_cache);
if (x) FREE(x);
}
// distance_rc -- look up or compute distance between chroma vectors
// at row, col in similarity matrix
//
@@ -142,7 +155,7 @@ void Curvefit::setup(int segments)
// Since distance can be computed relatively quickly, a better plan
// would be to cache values along the path. Here's a brief design
// (for the future, assuming this routine is actually a hot spot):
// Allocate a matrix that is, say, 20 x file1_frames to contain distances
// Allocate a matrix that is, say, 20 x file0_frames to contain distances
// that are +/- 10 frames from the path. Initialize cells to -1.
// Allocate an array of integer offsets of size file1_frames.
// Fill in the integer offsets with the column number (pathy) value of
@@ -157,7 +170,10 @@ void Curvefit::setup(int segments)
//
double Curvefit::distance_rc(int row, int col)
{
return gen_dist(row, col, sa->chrom_energy1, sa->chrom_energy2);
double dist = sa->gen_dist(row, col);
if (dist > 20) // DEBUGGING
printf("internal error");
return dist;
}
@@ -190,6 +206,7 @@ double Curvefit::compute_dist(int i)
double dx = x2 - x1, dy = y2 - y1;
double sum = 0;
int n;
assert(x1 >= 0 && x2 >= 0 && y1 >= 0 && y2 >= 0);
if (dx > dy) { // evauate at each x
n = (int) dx;
for (int x = (int) x1; x < x2; x++) {
@@ -204,14 +221,52 @@ double Curvefit::compute_dist(int i)
}
}
// normalize using line length: sum/n is average distance. Multiply
// avg. distance (cost per unit length) by length to get total cost:
// avg. distance (cost per unit length) by length to get total cost.
// Note: this gives an advantage to direct diagonal paths without bends
// because longer path lengths result in higher total cost. This also
// gives heigher weight to longer segments, although all segments are
// about the same length.
double rslt = sqrt(dx*dx + dy*dy) * sum / n;
// printf("compute_dist %d: x1 %g y1 %g x2 %g y2 %g sum %g rslt %g\n",
// i, x1, y1, x2, y2, sum, rslt);
if (rslt < 0 || rslt > 20 * n) { // DEBUGGING
printf("internal error");
}
return rslt;
}
void Curvefit::set_step_size(double ss)
{
for (int i = 0; i < n; i++) {
step_size[i] = ss;
}
}
static long curvefit_iterations;
// This is a callback from Hillclimb::optimize to report progress
// We can't know percentage completion because we don't know how
// many iterations it will take to converge, so we just report
// iterations. The SAProgress class assumes some number based
// on experience.
//
// Normally, the iterations parameter is a good indicator of work
// expended so far, but since we call Hillclimb::optimize twice
// (second time with a finer grid to search), ignore iterations
// and use curvefit_iterations, a global counter, instead. This
// assumes that curvefit_progress is called once for each iteration.
//
void curvefit_progress(void *cookie, int iterations, double best)
{
Scorealign *sa = (Scorealign *) cookie;
if (sa->progress) {
sa->progress->set_smoothing_progress(++curvefit_iterations);
}
}
void curve_fitting(Scorealign *sa, bool verbose)
{
if (verbose)
@@ -220,12 +275,17 @@ void curve_fitting(Scorealign *sa, bool verbose)
Curvefit curvefit(sa, verbose);
double *parameters;
double *x;
curvefit_iterations = 0;
// how many segments? About total time / line_time:
int segments =
(int) (0.5 + (sa->actual_frame_period_1 * sa->file1_frames) /
(int) (0.5 + (sa->actual_frame_period_0 * (sa->last_x - sa->first_x)) /
sa->line_time);
curvefit.setup(segments);
curvefit.optimize();
curvefit.optimize(&curvefit_progress, sa);
// further optimization with smaller step sizes:
// this step size will interpolate 0.25s frames down to 10ms
curvefit.set_step_size(0.04);
curvefit.optimize(&curvefit_progress, sa);
parameters = curvefit.get_parameters();
x = curvefit.get_x();
// now, rewrite pathx and pathy according to segments