commit 0e03fc977b211e038ecd028fee6a76d961cb767e
parent 718daecc98154df076e89f348a56ef35749dccd4
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date: Wed, 10 Apr 2013 10:40:12 -0700
C++ify FFT functions
Diffstat:
6 files changed, 100 insertions(+), 75 deletions(-)
diff --git a/src/spek-fft.cc b/src/spek-fft.cc
@@ -1,6 +1,6 @@
/* spek-fft.cc
*
- * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2013 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
@@ -16,53 +16,53 @@
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <math.h>
+#include <cmath>
#define __STDC_CONSTANT_MACROS
extern "C" {
#include <libavcodec/avfft.h>
-#include <libavutil/mem.h>
}
#include "spek-fft.h"
-struct spek_fft_plan * spek_fft_plan_new(int n)
+class FFTPlanImpl : public FFTPlan
{
- struct spek_fft_plan *p = (spek_fft_plan*)malloc(sizeof(struct spek_fft_plan));
- p->input = (float*)av_mallocz(sizeof(float) * n);
- p->output = (float*)av_mallocz(sizeof(float) * (n / 2 + 1));
- int bits = 0;
- while (n) {
- n >>= 1;
- ++bits;
- }
- p->n = 1 << --bits;
- p->cx = av_rdft_init(bits, DFT_R2C);
- return p;
+public:
+ FFTPlanImpl(int nbits);
+ ~FFTPlanImpl() override;
+
+ void execute() override;
+
+private:
+ struct RDFTContext *cx;
+};
+
+std::unique_ptr<FFTPlan> FFT::create(int nbits)
+{
+ return std::unique_ptr<FFTPlan>(new FFTPlanImpl(nbits));
+}
+
+FFTPlanImpl::FFTPlanImpl(int nbits) : FFTPlan(nbits), cx(av_rdft_init(nbits, DFT_R2C))
+{
+}
+
+FFTPlanImpl::~FFTPlanImpl()
+{
+ av_rdft_end(this->cx);
}
-void spek_fft_execute(struct spek_fft_plan *p)
+void FFTPlanImpl::execute()
{
- av_rdft_calc(p->cx, p->input);
+ av_rdft_calc(this->cx, this->get_input());
// Calculate magnitudes.
- int n = p->n;
+ int n = this->get_input_size();
float n2 = n * n;
- p->output[0] = 10.0f * log10f(p->input[0] * p->input[0] / n2);
- p->output[n / 2] = 10.0f * log10f(p->input[1] * p->input[1] / n2);
+ this->set_output(0, 10.0f * log10f(this->get_input(0) * this->get_input(0) / n2));
+ this->set_output(n / 2, 10.0f * log10f(this->get_input(1) * this->get_input(1) / n2));
for (int i = 1; i < n / 2; i++) {
- float val =
- p->input[i * 2] * p->input[i * 2] +
- p->input[i * 2 + 1] * p->input[i * 2 + 1];
- val /= n2;
- p->output[i] = 10.0f * log10f(val);
+ float re = this->get_input(i * 2);
+ float im = this->get_input(i * 2 + 1);
+ this->set_output(i, 10.0f * log10f((re * re + im * im) / n2));
}
}
-
-void spek_fft_delete(struct spek_fft_plan *p)
-{
- av_rdft_end(p->cx);
- av_free(p->input);
- av_free(p->output);
- free(p);
-}
diff --git a/src/spek-fft.h b/src/spek-fft.h
@@ -1,6 +1,6 @@
/* spek-fft.h
*
- * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2013 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
@@ -19,26 +19,44 @@
#ifndef SPEK_FFT_H_
#define SPEK_FFT_H_
-struct RDFTContext;
+#include <memory>
+#include <vector>
-struct spek_fft_plan
-{
- // Internal data.
- struct RDFTContext *cx;
- int n;
+class FFTPlan;
- // Exposed properties.
- float *input;
- float *output;
+class FFT
+{
+public:
+ FFT() {}
+ std::unique_ptr<FFTPlan> create(int nbits);
};
-// Allocate buffers and create a new FFT plan.
-struct spek_fft_plan * spek_fft_plan_new(int n);
-
-// Execute the FFT on plan->input.
-void spek_fft_execute(struct spek_fft_plan *p);
-
-// Destroy the plan and de-allocate buffers.
-void spek_fft_delete(struct spek_fft_plan *p);
+class FFTPlan
+{
+public:
+ FFTPlan(int nbits) :
+ nbits(nbits), input_size(1 << nbits), output_size((1 << (nbits - 1)) + 1),
+ input(input_size), output(output_size) {}
+ virtual ~FFTPlan() {}
+
+ int get_input_size() const { return this->input_size; }
+ int get_output_size() const { return this->output_size; }
+ float get_input(int i) const { return this->input[i]; }
+ void set_input(int i, float v) { this->input[i] = v; }
+ float get_output(int i) const { return this->output[i]; }
+ void set_output(int i, float v) { this->output[i] = v; }
+
+ virtual void execute() = 0;
+
+protected:
+ float *get_input() { return this->input.data(); }
+
+private:
+ int nbits;
+ int input_size;
+ int output_size;
+ std::vector<float> input;
+ std::vector<float> output;
+};
#endif
diff --git a/src/spek-pipeline.cc b/src/spek-pipeline.cc
@@ -49,12 +49,11 @@ enum
struct spek_pipeline
{
std::unique_ptr<AudioFile> file;
- int bands;
+ std::unique_ptr<FFTPlan> fft;
int samples;
spek_pipeline_cb cb;
void *cb_data;
- struct spek_fft_plan *fft;
float *coss; // Pre-computed cos table.
int nfft; // Size of the FFT transform.
int input_size;
@@ -84,17 +83,21 @@ static void * worker_func(void *);
static void reader_sync(struct spek_pipeline *p, int pos);
struct spek_pipeline * spek_pipeline_open(
- std::unique_ptr<AudioFile> file, int bands, int samples, spek_pipeline_cb cb, void *cb_data)
+ std::unique_ptr<AudioFile> file,
+ std::unique_ptr<FFTPlan> fft,
+ int samples,
+ spek_pipeline_cb cb,
+ void *cb_data
+)
{
spek_pipeline *p = new spek_pipeline();
p->file = std::move(file);
- p->bands = bands;
+ p->fft = std::move(fft);
p->samples = samples;
p->cb = cb;
p->cb_data = cb_data;
p->coss = NULL;
- p->fft = NULL;
p->input = NULL;
p->output = NULL;
p->has_reader_thread = false;
@@ -105,16 +108,15 @@ struct spek_pipeline * spek_pipeline_open(
p->has_worker_cond = false;
if (!p->file->get_error()) {
- p->nfft = 2 * bands - 2;
+ p->nfft = p->fft->get_input_size();
p->coss = (float*)malloc(p->nfft * sizeof(float));
float cf = 2.0f * (float)M_PI / p->nfft;
for (int i = 0; i < p->nfft; ++i) {
p->coss[i] = cosf(cf * i);
}
- p->fft = spek_fft_plan_new(p->nfft);
p->input_size = p->nfft * (NFFT * 2 + 1);
p->input = (float*)malloc(p->input_size * sizeof(float));
- p->output = (float*)malloc(bands * sizeof(float));
+ p->output = (float*)malloc(p->fft->get_output_size() * sizeof(float));
p->file->start(samples);
}
@@ -173,10 +175,6 @@ void spek_pipeline_close(struct spek_pipeline *p)
free(p->input);
p->input = NULL;
}
- if (p->fft) {
- spek_fft_delete(p->fft);
- p->fft = NULL;
- }
if (p->coss) {
free(p->coss);
p->coss = NULL;
@@ -357,7 +355,7 @@ static void * worker_func(void *pp)
int head = 0, tail = 0;
int prev_head = 0;
- memset(p->output, 0, sizeof(float) * p->bands);
+ memset(p->output, 0, sizeof(float) * p->fft->get_output_size());
while (true) {
pthread_mutex_lock(&p->reader_mutex);
@@ -402,12 +400,12 @@ static void * worker_func(void *pp)
// val *= 0.53836f - 0.46164f * coss[i];
// Hann window.
val *= 0.5f * (1.0f - p->coss[i]);
- p->fft->input[i] = val;
+ p->fft->set_input(i, val);
}
- spek_fft_execute(p->fft);
+ p->fft->execute();
num_fft++;
- for (int i = 0; i < p->bands; i++) {
- p->output[i] += p->fft->output[i];
+ for (int i = 0; i < p->fft->get_output_size(); i++) {
+ p->output[i] += p->fft->get_output(i);
}
}
@@ -419,14 +417,14 @@ static void * worker_func(void *pp)
acc_error += p->file->get_error_per_interval();
}
- for (int i = 0; i < p->bands; i++) {
+ for (int i = 0; i < p->fft->get_output_size(); i++) {
p->output[i] /= num_fft;
}
if (sample == p->samples) break;
p->cb(sample++, p->output, p->cb_data);
- memset(p->output, 0, sizeof(float) * p->bands);
+ memset(p->output, 0, sizeof(float) * p->fft->get_output_size());
frames = 0;
num_fft = 0;
}
diff --git a/src/spek-pipeline.h b/src/spek-pipeline.h
@@ -22,13 +22,18 @@
#include <memory>
#include <string>
-class Audio;
+class AudioFile;
+class FFTPlan;
struct spek_pipeline;
typedef void (*spek_pipeline_cb)(int sample, float *values, void *cb_data);
struct spek_pipeline * spek_pipeline_open(
- std::unique_ptr<AudioFile> file, int bands, int samples, spek_pipeline_cb cb, void *cb_data
+ std::unique_ptr<AudioFile> file,
+ std::unique_ptr<FFTPlan> fft,
+ int samples,
+ spek_pipeline_cb cb,
+ void *cb_data
);
void spek_pipeline_start(struct spek_pipeline *pipeline);
diff --git a/src/spek-spectrogram.cc b/src/spek-spectrogram.cc
@@ -22,6 +22,7 @@
#include "spek-audio.h"
#include "spek-events.h"
+#include "spek-fft.h"
#include "spek-palette.h"
#include "spek-pipeline.h"
#include "spek-platform.h"
@@ -43,8 +44,8 @@ enum
MIN_RANGE = -140,
URANGE = -20,
LRANGE = -120,
- NFFT = 2048,
- BANDS = NFFT / 2 + 1,
+ FFT_BITS = 11,
+ BANDS = (1 << (FFT_BITS - 1)) + 1,
LPAD = 60,
TPAD = 60,
RPAD = 90,
@@ -62,6 +63,7 @@ SpekSpectrogram::SpekSpectrogram(wxFrame *parent) :
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS
),
audio(new Audio()), // TODO: refactor
+ fft(new FFT()),
pipeline(NULL),
duration(0.0),
sample_rate(0),
@@ -365,7 +367,7 @@ void SpekSpectrogram::start()
this->image.Create(samples, BANDS);
this->pipeline = spek_pipeline_open(
this->audio->open(std::string(this->path.utf8_str())),
- BANDS,
+ this->fft->create(FFT_BITS),
samples,
pipeline_cb,
this
diff --git a/src/spek-spectrogram.h b/src/spek-spectrogram.h
@@ -24,6 +24,7 @@
#include <wx/wx.h>
class Audio;
+class FFT;
class SpekHaveSampleEvent;
struct spek_pipeline;
@@ -46,6 +47,7 @@ private:
void stop();
std::unique_ptr<Audio> audio;
+ std::unique_ptr<FFT> fft;
spek_pipeline *pipeline;
wxString path;
wxString desc;