mirror of
https://github.com/alexkay/spek.git
synced 2025-04-15 16:20:33 +03:00
C++ify FFT functions
This commit is contained in:
parent
718daecc98
commit
0e03fc977b
@ -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));
|
||||
}
|
||||
|
||||
void spek_fft_execute(struct spek_fft_plan *p)
|
||||
FFTPlanImpl::FFTPlanImpl(int nbits) : FFTPlan(nbits), cx(av_rdft_init(nbits, DFT_R2C))
|
||||
{
|
||||
av_rdft_calc(p->cx, p->input);
|
||||
}
|
||||
|
||||
FFTPlanImpl::~FFTPlanImpl()
|
||||
{
|
||||
av_rdft_end(this->cx);
|
||||
}
|
||||
|
||||
void FFTPlanImpl::execute()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
class FFTPlan;
|
||||
|
||||
class FFT
|
||||
{
|
||||
// Internal data.
|
||||
struct RDFTContext *cx;
|
||||
int n;
|
||||
|
||||
// Exposed properties.
|
||||
float *input;
|
||||
float *output;
|
||||
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);
|
||||
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() {}
|
||||
|
||||
// Execute the FFT on plan->input.
|
||||
void spek_fft_execute(struct spek_fft_plan *p);
|
||||
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; }
|
||||
|
||||
// Destroy the plan and de-allocate buffers.
|
||||
void spek_fft_delete(struct spek_fft_plan *p);
|
||||
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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user