spek

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

commit 0c0f7c29701e7f1ecb20867c244ffa5fc977fa10
parent 9c1a32213b9516a18b20cb75ec46bc4eaa9f3fc3
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date:   Fri,  2 Jul 2010 22:06:58 +1000

Add FFTW bindings, use it in the pipeline

Diffstat:
M.gitignore | 1+
Mconfigure.ac | 5+++--
Msrc/Makefile.am | 5++++-
Asrc/spek-fft.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/spek-fft.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/spek-pipeline.vala | 25++++++++++++++++++++++++-
Msrc/spek-spectrogram.vala | 1+
Mvapi/Makefile.am | 3++-
Avapi/spek-fft.vapi | 16++++++++++++++++
9 files changed, 151 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -29,6 +29,7 @@ po/stamp-it samples/ src/*.c !src/spek-audio.c +!src/spek-fft.c src/*.o src/*.stamp src/spek diff --git a/configure.ac b/configure.ac @@ -24,11 +24,12 @@ AM_PROG_VALAC([0.7.0]) AC_PROG_INSTALL AC_PROG_INTLTOOL([0.35]) -pkg_modules="gtk+-2.0 >= 2.14.0 gstreamer-0.10 >= 0.10.17 libavformat libavcodec" -SPEK_PACKAGES="--pkg gtk+-2.0 --pkg gstreamer-0.10" +pkg_modules="gtk+-2.0 >= 2.14.0 gstreamer-0.10 >= 0.10.17 libavformat libavcodec fftw3f" PKG_CHECK_MODULES(SPEK, [$pkg_modules]) AC_SUBST(SPEK_CFLAGS) AC_SUBST(SPEK_LIBS) + +SPEK_PACKAGES="--pkg gtk+-2.0 --pkg gstreamer-0.10" AC_SUBST(SPEK_PACKAGES) AC_CHECK_LIB(m, log10f) diff --git a/src/Makefile.am b/src/Makefile.am @@ -3,6 +3,7 @@ bin_PROGRAMS = spek spek_SOURCES = \ spek.vala \ spek-audio.c \ + spek-fft.c \ spek-pipeline.vala \ spek-ruler.vala \ spek-source.vala \ @@ -21,6 +22,7 @@ VALAFLAGS = \ --vapidir=$(srcdir)/../vapi \ --pkg config \ --pkg spek-audio \ + --pkg spek-fft \ @SPEK_PACKAGES@ spek_LDADD = \ @@ -28,4 +30,5 @@ spek_LDADD = \ $(IGE_MAC_LIBS) EXTRA_DIST = \ - spek-audio.h + spek-audio.h \ + spek-fft.h diff --git a/src/spek-fft.c b/src/spek-fft.c @@ -0,0 +1,55 @@ +/* spek-fft.c + * + * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com> + * + * Spek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Spek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Spek. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "spek-fft.h" + +SpekFftPlan * spek_fft_plan_new (gint n) { + 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->plan = fftwf_plan_dft_r2c_1d (n, p->input, p->result, FFTW_ESTIMATE); + return p; +} + +void spek_fft_execute (SpekFftPlan *p) { + int i; + int bands = p->n / 2 + 1; + + fftwf_execute (p->plan); + + /* Calculate magnitudes */ + for (i = 0; i < bands; i++) { + gfloat val; + 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; + } +} + +void spek_fft_destroy (SpekFftPlan *p) { + fftwf_destroy_plan (p->plan); + fftwf_free (p->result); + fftwf_free (p->output); + fftwf_free (p->input); + g_free (p); +} diff --git a/src/spek-fft.h b/src/spek-fft.h @@ -0,0 +1,45 @@ +/* spek-fft.h + * + * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com> + * + * Spek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Spek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Spek. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __SPEK_FFT_H__ +#define __SPEK_FFT_H__ + +#include <glib.h> +#include <fftw3.h> + +typedef struct { + /* Internal data */ + fftwf_plan plan; + gint n; + fftwf_complex *result; + + /* Exposed properties */ + gfloat *input; + gfloat *output; +} SpekFftPlan; + +/* Allocate buffers and create a new FFT plan */ +SpekFftPlan * spek_fft_plan_new (gint n); + +/* Execute the FFT on plan->input */ +void spek_fft_execute (SpekFftPlan *p); + +/* Destroy the plan and de-allocate buffers */ +void spek_fft_destroy (SpekFftPlan *p); + +#endif diff --git a/src/spek-pipeline.vala b/src/spek-pipeline.vala @@ -77,9 +77,13 @@ namespace Spek { public void start () { int nfft = 2 * bands - 2; var input = new float[nfft]; + var output = new float[bands]; int pos = 0; int frames = 0; int size; + // TODO: make it a static class variable and experiment with FFTW_MEASURE. + var fft = new Fft.Plan (nfft); + while ((size = cx.read (this.buffer)) > 0) { uint8 *buffer = (uint8 *) this.buffer; var block_size = cx.width * cx.channels; @@ -90,7 +94,26 @@ namespace Spek { pos = (pos + 1) % nfft; frames++; - // TODO + // If we have enough frames for an FFT or we + // 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. + if (frames % nfft == 0 + // TODO: error correction +// || ((spectrum->accumulated_error < GST_SECOND +// && spectrum->num_frames == spectrum->frames_per_interval) +// || +// (spectrum->accumulated_error >= GST_SECOND +// && spectrum->num_frames - 1 == spectrum->frames_per_interval)) + ) { + for (int i = 0; i < nfft; i++) { + fft.input[i] = input[(pos + i) % nfft]; + } + fft.execute (); + for (int i = 0; i < bands; i++) { + output[i] += fft.output[i]; + } + } } assert (size == 0); } diff --git a/src/spek-spectrogram.vala b/src/spek-spectrogram.vala @@ -91,6 +91,7 @@ namespace Spek { // TODO var pipeline = new Pipeline (file_name, BANDS, samples, THRESHOLD, data_cb); + pipeline.start (); print ("\n%s:\n%s\n", file_name, pipeline.description); queue_draw (); diff --git a/vapi/Makefile.am b/vapi/Makefile.am @@ -1,6 +1,7 @@ noinst_DATA = \ config.vapi \ - spek-audio.vapi + spek-audio.vapi \ + spek-fft.vapi EXTRA_DIST = \ $(noinst_DATA) diff --git a/vapi/spek-fft.vapi b/vapi/spek-fft.vapi @@ -0,0 +1,16 @@ +[CCode (cprefix = "SpekFft", lower_case_cprefix = "spek_fft_", cheader_filename = "spek-fft.h")] +namespace Spek.Fft { + [Compact] + [CCode (free_function = "spek_fft_destroy")] + public class Plan { + [CCode (array_length = false)] + public unowned float[] input; + [CCode (array_length = false)] + public unowned float[] output; + + [CCode (cname = "spek_fft_plan_new")] + public Plan (int n); + [CCode (cname = "spek_fft_execute")] + public void execute (); + } +}