commit 4e186744ad2c4d8f595e662885c6705c0a8f672f
parent 68cbf6335f9265fe0593b3969718559b9d5e6da7
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date: Sat, 3 Jul 2010 20:00:51 +1000
Error correction + misc fixes
Diffstat:
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 ();
}