spek

Acoustic spectrum analyser
git clone http://git.hanabi.in/repos/spek.git
Log | Files | Refs | README

commit 4e186744ad2c4d8f595e662885c6705c0a8f672f
parent 68cbf6335f9265fe0593b3969718559b9d5e6da7
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date:   Sat,  3 Jul 2010 20:00:51 +1000

Error correction + misc fixes

Diffstat:
Msrc/spek-fft.c | 5+++--
Msrc/spek-fft.h | 3++-
Msrc/spek-pipeline.vala | 50++++++++++++++++++++++++++------------------------
Msrc/spek-spectrogram.vala | 7+++----
Mvapi/spek-fft.vapi | 2+-
5 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/src/spek-fft.c b/src/spek-fft.c @@ -20,12 +20,13 @@ #include "spek-fft.h" -SpekFftPlan * spek_fft_plan_new (gint n) { +SpekFftPlan * spek_fft_plan_new (gint n, gint threshold) { SpekFftPlan *p = g_new0 (SpekFftPlan, 1); p->input = (gfloat *) fftwf_malloc (sizeof (gfloat) * n); p->output = (gfloat *) fftwf_malloc (sizeof (gfloat) * (n / 2 + 1)); p->result = (fftwf_complex *) fftwf_malloc (sizeof (fftwf_complex) * (n / 2 + 1)); p->n = n; + p->threshold = threshold; p->plan = fftwf_plan_dft_r2c_1d (n, p->input, p->result, FFTW_ESTIMATE); return p; } @@ -42,7 +43,7 @@ void spek_fft_execute (SpekFftPlan *p) { val = p->result[i][0] * p->result[i][0] + p->result[i][1] * p->result[i][1]; val /= p->n * p->n; val = 10.0 * log10f (val); - p->output[i] = val; + p->output[i] = val < p->threshold ? p->threshold : val; } } diff --git a/src/spek-fft.h b/src/spek-fft.h @@ -26,6 +26,7 @@ typedef struct { /* Internal data */ fftwf_plan plan; gint n; + gint threshold; fftwf_complex *result; /* Exposed properties */ @@ -34,7 +35,7 @@ typedef struct { } SpekFftPlan; /* Allocate buffers and create a new FFT plan */ -SpekFftPlan * spek_fft_plan_new (gint n); +SpekFftPlan * spek_fft_plan_new (gint n, gint threshold); /* Execute the FFT on plan->input */ void spek_fft_execute (SpekFftPlan *p); diff --git a/src/spek-pipeline.vala b/src/spek-pipeline.vala @@ -41,7 +41,6 @@ namespace Spek { private int nfft; float[] input; float[] output; - float[] values; public Pipeline (string file_name, int bands, int samples, int threshold, Callback cb) { this.cx = new Audio.Context (file_name); @@ -80,25 +79,25 @@ namespace Spek { this.sample_rate = cx.sample_rate; this.buffer = new uint8[cx.buffer_size]; this.nfft = 2 * bands - 2; - this.fft = new Fft.Plan (nfft); + this.fft = new Fft.Plan (nfft, threshold); this.input = new float[nfft]; this.output = new float[bands]; - this.values = new float[bands]; this.cx.start (samples); } public void start () { int pos = 0; - int frames = 0; - int num_fft = 0; int sample = 0; + int64 frames = 0; + int64 num_fft = 0; + int64 acc_error = 0; int size; - clear_buffers (); + Posix.memset (output, 0, sizeof (float) * bands); while ((size = cx.read (this.buffer)) > 0) { uint8 *buffer = (uint8 *) this.buffer; - var block_size = cx.width * cx.channels; + var block_size = cx.width * cx.channels / 8; while (size >= block_size) { input[pos] = average_input (buffer); buffer += block_size; @@ -110,8 +109,9 @@ namespace Spek { // have all frames required for the interval run // an FFT. In the last case we probably take the // FFT of frames that we already handled. - // TODO: error correction - if (frames % nfft == 0) { + if (frames % nfft == 0 || + acc_error < cx.error_base && frames == cx.frames_per_interval || + acc_error >= cx.error_base && frames == 1 + cx.frames_per_interval) { for (int i = 0; i < nfft; i++) { fft.input[i] = input[(pos + i) % nfft]; } @@ -122,13 +122,21 @@ namespace Spek { } } // Do we have the FFTs for one interval? - // TODO: error correction - if (frames == cx.frames_per_interval) { + if (acc_error < cx.error_base && frames == cx.frames_per_interval || + acc_error >= cx.error_base && frames == 1 + cx.frames_per_interval) { + + if (acc_error >= cx.error_base) { + acc_error -= cx.error_base; + } else { + acc_error += cx.error_per_interval; + } + for (int i = 0; i < bands; i++) { output[i] /= num_fft; } + cb (sample++, output); - clear_buffers (); + Posix.memset (output, 0, sizeof (float) * bands); frames = 0; num_fft = 0; } @@ -137,14 +145,8 @@ namespace Spek { } } - private void clear_buffers () { - Posix.memset (input, 0, sizeof (float) * nfft); - Posix.memset (output, 0, sizeof (float) * bands); - } - private float average_input (uint8 *buffer) { float res = 0f; - float max_value = cx.bits_per_sample > 1 ? (1UL << (cx.bits_per_sample - 1)) - 1 : 0; if (cx.fp && cx.width == 32) { float *p = (float *) buffer; for (int i = 0; i < cx.channels; i++) { @@ -155,15 +157,15 @@ namespace Spek { for (int i = 0; i < cx.channels; i++) { res += (float) p[i]; } - } else if (!cx.fp && cx.width == 32) { - int32 *p = (int32 *) buffer; + } else if (!cx.fp && cx.width == 16) { + int16 *p = (int16 *) buffer; for (int i = 0; i < cx.channels; i++) { - res += p[i] / (max_value == 0 ? int32.MAX : max_value); + res += p[i] / (float) int16.MAX; } - } else if (!cx.fp && cx.width == 16) { - int64 *p = (int64 *) buffer; + } else if (!cx.fp && cx.width == 32) { + int32 *p = (int32 *) buffer; for (int i = 0; i < cx.channels; i++) { - res += p[i] / (max_value == 0 ? int16.MAX : max_value); + res += p[i] / (float) int32.MAX; } } else { assert_not_reached (); diff --git a/src/spek-spectrogram.vala b/src/spek-spectrogram.vala @@ -27,9 +27,9 @@ namespace Spek { public string file_name { get; private set; } private Pipeline pipeline; private string info; - private const int THRESHOLD = -92; - // For faster FFT BANDS*2-2 should be a multiple of 2,3,5 - private const int BANDS = 1025; + private const int THRESHOLD = -86; + private const int NFFT = 2048; + private const int BANDS = NFFT / 2 + 1; private ImageSurface image; private ImageSurface palette; @@ -103,7 +103,6 @@ namespace Spek { } private void data_cb (int sample, float[] values) { - print ("%d: %f %f %f %f\n", sample, values[50], values[100], values[150], values[200]); for (int y = 0; y < values.length; y++) { var level = double.min ( 1.0, Math.log10 (1.0 - THRESHOLD + values[y]) / Math.log10 (-THRESHOLD)); diff --git a/vapi/spek-fft.vapi b/vapi/spek-fft.vapi @@ -9,7 +9,7 @@ namespace Spek.Fft { public unowned float[] output; [CCode (cname = "spek_fft_plan_new")] - public Plan (int n); + public Plan (int n, int threshold); [CCode (cname = "spek_fft_execute")] public void execute (); }