From 2f57eb70a361b10d4e0b71568bb7b2f3d22afb11 Mon Sep 17 00:00:00 2001 From: Martin Guy Date: Sat, 12 Dec 2015 17:19:25 +0100 Subject: [PATCH] Add --precise to compensate for audio start being truncated to sample The starting time for audio read for a pixel column is truncated to an integer (a sample number) which introduces a semi-random time jitter of up to one sample period and making the actual start time up to one sample period earlier than requested. This patch tries to compensate for that by linear interpolation between each sample and the following one. --- src/spectrogram.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/spectrogram.c b/src/spectrogram.c index 3541bfe..3172b6c 100644 --- a/src/spectrogram.c +++ b/src/spectrogram.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,11 @@ typedef struct double spec_floor_db ; } RENDER ; +/* Interpolate between samples to eliminate the + * 1-sample-period jitter due to time quantization? + */ +static bool precise_timing = 0; + typedef struct { int left, top, width, height ; } RECT ; @@ -157,11 +163,17 @@ static void read_mono_audio (SNDFILE * file, sf_count_t filelen, double * data, int datalen, int indx, int total) { sf_count_t start ; + double error ; /* How much "start" is wrong by + * (start == exact_value_of_start + error) */ + sf_count_t k; memset (data, 0, datalen * sizeof (data [0])) ; start = (indx * filelen) / total - datalen / 2 ; + error = start - (((double)indx * filelen) / total - datalen / 2.0) ; + assert (-1.0 < error && error <= 0.0) ; + if (start >= 0) sf_seek (file, start, SEEK_SET) ; else @@ -171,7 +183,23 @@ read_mono_audio (SNDFILE * file, sf_count_t filelen, double * data, int datalen, datalen -= start ; } ; - sfx_mix_mono_read_double (file, data, datalen) ; + sfx_mix_mono_read_double (file, data, datalen+(precise_timing?1:0)) ; + + /* The starting time is truncated to a sample boundary and is slighty + * earlier that the exact starting time. + * We compensate by reading in one more sample than this and + * interpolating between each sample and the following one. + */ + if ( precise_timing && error != 0.0 ) + { + /* Proportion to take of this sample and of the following one. + * this + next == 1.0 + */ + double this = 1.0 + error, next = -error ; + + for (k=0; k < datalen; k++) + data[k] = data[k]*this + data[k+1]*next; + } return ; } /* read_mono_audio */ @@ -589,7 +617,9 @@ render_to_surface (const RENDER * render, SNDFILE *infile, int samplerate, sf_co } } - time_domain = calloc (2 * speclen, sizeof (double)) ; + /* + 1 because with --precise, read_mono_audio reads the following + * sample and interpolates to get the starting moment exact. */ + time_domain = calloc ((2 * speclen + 1), sizeof (double)) ; freq_domain = calloc (2 * speclen, sizeof (double)) ; single_mag_spec = calloc (speclen, sizeof (double)) ; mag_spec = calloc (width, sizeof (float *)) ; @@ -738,6 +768,7 @@ usage_exit (const char * argv0, int error) " --kaiser : Use a Kaiser window function (the default)\n" " --nuttall : Use a Nuttall window function\n" " --hann : Use a Hann window function\n" + " --precise : Interpolate between samples for precise timing\n" ) ; exit (error) ; @@ -795,6 +826,11 @@ main (int argc, char * argv []) continue ; } ; + if (strcmp (argv [k], "--precise") == 0) + { precise_timing = 1; + continue ; + } ; + printf ("\nError : Bad command line argument '%s'\n", argv [k]) ; usage_exit (argv [0], 1) ; } ;