Updates
This commit is contained in:
parent
e324be7268
commit
510e5ccc90
3
Makefile
3
Makefile
|
|
@ -1,4 +1,7 @@
|
||||||
|
-include .config
|
||||||
|
|
||||||
CFLAGS += -Wall -MMD
|
CFLAGS += -Wall -MMD
|
||||||
|
LDLIBS += -lm
|
||||||
|
|
||||||
tempo: tempo.o
|
tempo: tempo.o
|
||||||
|
|
||||||
|
|
|
||||||
142
tempo.c
142
tempo.c
|
|
@ -1,7 +1,13 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define BANNER "tempo 0.1 (C) Copyright 2011 Mark Hills <mark@pogo.org.uk>"
|
||||||
|
#define NAME "tempo"
|
||||||
|
|
||||||
#define RATE 44100 /* of input data */
|
#define RATE 44100 /* of input data */
|
||||||
|
|
||||||
|
|
@ -13,27 +19,21 @@
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sample from the metered energy with interpolation
|
* Sample from the metered energy
|
||||||
|
*
|
||||||
|
* No need to interpolate and it makes a tiny amount of difference; we
|
||||||
|
* take a random sample of samples, any errors are averaged out.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static double sample(float nrg[], size_t len, double offset)
|
static double sample(float nrg[], size_t len, double offset)
|
||||||
{
|
{
|
||||||
double f, a, b;
|
double n;
|
||||||
size_t n;
|
size_t i;
|
||||||
|
|
||||||
n = (size_t)offset;
|
n = floor(offset);
|
||||||
|
i = (size_t)n;
|
||||||
|
|
||||||
f = offset - n;
|
return (n >= 0.0 && n < (double)len) ? nrg[i] : 0.0;
|
||||||
if (f == 0.0)
|
|
||||||
return (n < len) ? nrg[n] : 0.0;
|
|
||||||
|
|
||||||
/* Linear interpolation */
|
|
||||||
|
|
||||||
a = (n < len) ? nrg[n] : 0.0;
|
|
||||||
n++;
|
|
||||||
b = (n < len) ? nrg[n] : 0.0;
|
|
||||||
|
|
||||||
return (1.0 - f) * a + f * b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -43,22 +43,28 @@ static double sample(float nrg[], size_t len, double offset)
|
||||||
double autodifference(float nrg[], size_t len, double interval)
|
double autodifference(float nrg[], size_t len, double interval)
|
||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
double mid, v, diff = 0.0;
|
double mid, v, diff;
|
||||||
static const int beats[] = { -32, -16, -8, -4, -4, -4, -2, -2, -1,
|
static const double beats[] = { -32, -16, -8, -4, -2, -1, 1, 2, 4, 8, 16, 32 },
|
||||||
1, 2, 2, 4, 4, 4, 8, 16, 32 };
|
nobeats[] = { -0.5, -0.25, 0.25, 0.5 };
|
||||||
|
|
||||||
mid = drand() * len;
|
mid = drand() * len;
|
||||||
v = sample(nrg, len, mid);
|
v = sample(nrg, len, mid);
|
||||||
|
|
||||||
|
diff = 0.0;
|
||||||
for (n = 0; n < ARRAY_SIZE(beats); n++) {
|
for (n = 0; n < ARRAY_SIZE(beats); n++) {
|
||||||
int beat;
|
|
||||||
double y;
|
double y;
|
||||||
|
|
||||||
beat = beats[n];
|
y = sample(nrg, len, mid + beats[n] * interval);
|
||||||
y = sample(nrg, len, mid + beat * interval);
|
|
||||||
diff += fabs(y - v);
|
diff += fabs(y - v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < ARRAY_SIZE(nobeats); n++) {
|
||||||
|
double y;
|
||||||
|
|
||||||
|
y = sample(nrg, len, mid + nobeats[n] * interval);
|
||||||
|
diff -= fabs(y - v);
|
||||||
|
}
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +102,8 @@ double interval_to_bpm(double interval)
|
||||||
double scan_for_bpm(float nrg[], size_t len,
|
double scan_for_bpm(float nrg[], size_t len,
|
||||||
double slowest, double fastest,
|
double slowest, double fastest,
|
||||||
unsigned int steps,
|
unsigned int steps,
|
||||||
unsigned int samples)
|
unsigned int samples,
|
||||||
|
bool graph)
|
||||||
{
|
{
|
||||||
double step, interval, trough, height;
|
double step, interval, trough, height;
|
||||||
unsigned int s;
|
unsigned int s;
|
||||||
|
|
@ -107,17 +114,16 @@ double scan_for_bpm(float nrg[], size_t len,
|
||||||
|
|
||||||
height = INFINITY;
|
height = INFINITY;
|
||||||
|
|
||||||
for (;;) {
|
for (interval = fastest; interval <= slowest; interval += step) {
|
||||||
double t;
|
double t;
|
||||||
|
|
||||||
interval += step;
|
|
||||||
if (interval > slowest)
|
|
||||||
break;
|
|
||||||
|
|
||||||
t = 0.0;
|
t = 0.0;
|
||||||
for (s = 0; s < samples; s++)
|
for (s = 0; s < samples; s++)
|
||||||
t += autodifference(nrg, len, interval);
|
t += autodifference(nrg, len, interval);
|
||||||
|
|
||||||
|
if (graph)
|
||||||
|
printf("%lf\t%lf\n", interval_to_bpm(interval), t);
|
||||||
|
|
||||||
/* Track the lowest value */
|
/* Track the lowest value */
|
||||||
|
|
||||||
if (t < height) {
|
if (t < height) {
|
||||||
|
|
@ -129,13 +135,70 @@ double scan_for_bpm(float nrg[], size_t len,
|
||||||
return interval_to_bpm(trough - (step / 2));
|
return interval_to_bpm(trough - (step / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void usage(FILE *f)
|
||||||
|
{
|
||||||
|
fprintf(f, "Usage: " NAME " [options]\n"
|
||||||
|
"Analyse the tempo (in beats-per-minute, BPM) of incoming audio\n\n"
|
||||||
|
" -g Output autodifference graph to stdout\n"
|
||||||
|
" -f Print format for final BPM value (default \"%%0.1f\")\n"
|
||||||
|
" -v Print progress information to stderr\n"
|
||||||
|
" -h Display this help message and exit\n\n");
|
||||||
|
|
||||||
|
fprintf(f, "Incoming audio is raw audio at %dHz, mono, 32-bit float; eg.\n"
|
||||||
|
" sox file.mp3 -t raw -r %d -e float -c 1 | ./" NAME "\n",
|
||||||
|
RATE, RATE);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
float *nrg = NULL;
|
float *nrg = NULL;
|
||||||
size_t len = 0, buf = 0;
|
size_t len = 0, buf = 0;
|
||||||
off_t n = 0;
|
off_t n = 0;
|
||||||
double bpm;
|
float bpm, v = 0.0;
|
||||||
float v = 0.0;
|
const char *format = "%0.1f";
|
||||||
|
bool verbose = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = getopt(argc, argv, "vf:gh");
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'v':
|
||||||
|
verbose = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
format = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
format = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
usage(stdout);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return EX_USAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argv += optind;
|
||||||
|
argc -= optind;
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
fprintf(stderr, "%s: Too many arguments\n", NAME);
|
||||||
|
return EX_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fputs(BANNER "\n", stderr);
|
||||||
|
fprintf(stderr, "Metering incoming audio\n");
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
float z;
|
float z;
|
||||||
|
|
@ -164,21 +227,32 @@ int main(int argc, char *argv[])
|
||||||
if (len == buf) {
|
if (len == buf) {
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
fprintf(stderr, "Reallocating at %zd\n", len);
|
|
||||||
|
|
||||||
n = buf + BLOCK;
|
n = buf + BLOCK;
|
||||||
nrg = realloc(nrg, n * sizeof(*nrg));
|
nrg = realloc(nrg, n * sizeof(*nrg));
|
||||||
assert(nrg != NULL);
|
if (nrg == NULL) {
|
||||||
|
perror("realloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
buf = n;
|
buf = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
nrg[len++] = v;
|
nrg[len++] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpm = scan_for_bpm(nrg, len, 60.0, 180.0, 400, 1000);
|
if (verbose)
|
||||||
printf("BPM = %f\n", bpm);
|
fprintf(stderr, "Sampling tempo range\n");
|
||||||
|
|
||||||
|
bpm = scan_for_bpm(nrg, len, 60.0, 180.0, 400, 1000, (format == NULL));
|
||||||
|
|
||||||
|
if (format != NULL) {
|
||||||
|
printf(format, bpm);
|
||||||
|
putc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
free(nrg);
|
free(nrg);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fprintf(stderr, "Done\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue