Run the pipeline

This commit is contained in:
Alexander Kojevnikov 2012-08-15 00:56:20 -07:00
parent 4df42e8258
commit f81ce84826
5 changed files with 77 additions and 45 deletions

View File

@ -47,6 +47,7 @@ struct spek_pipeline
int samples;
int threshold;
spek_pipeline_cb cb;
void *cb_data;
struct spek_fft_plan *fft;
float *coss; // Pre-computed cos table.
@ -79,7 +80,7 @@ static void reader_sync(struct spek_pipeline *p, int pos);
static float average_input(const struct spek_pipeline *p, void *buffer);
struct spek_pipeline * spek_pipeline_open(
const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb)
const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb, void *cb_data)
{
struct spek_pipeline *p = malloc(sizeof(struct spek_pipeline));
p->cx = spek_audio_open(path);
@ -88,6 +89,7 @@ struct spek_pipeline * spek_pipeline_open(
p->samples = samples;
p->threshold = threshold;
p->cb = cb;
p->cb_data = cb_data;
p->coss = NULL;
p->fft = NULL;
@ -115,6 +117,11 @@ struct spek_pipeline * spek_pipeline_open(
}
}
const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline)
{
return pipeline->properties;
}
void spek_pipeline_start(struct spek_pipeline *p)
{
if (!p->properties->error) return;
@ -176,6 +183,7 @@ void spek_pipeline_close(struct spek_pipeline *p) {
spek_audio_close(p->cx);
p->cx = NULL;
}
free(p);
}
static void * reader_func (void *pp) {
@ -310,7 +318,7 @@ static void * worker_func (void *pp) {
}
if (sample == p->samples) break;
p->cb(sample++, p->output);
p->cb(sample++, p->output, p->cb_data);
memset(p->output, 0, sizeof(float) * p->bands);
frames = 0;

View File

@ -26,15 +26,15 @@ extern "C" {
struct spek_pipeline;
struct spek_audio_properties;
typedef void (*spek_pipeline_cb)(int sample, float *values);
typedef void (*spek_pipeline_cb)(int sample, float *values, void *cb_data);
struct spek_pipeline * spek_pipeline_open(
const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb);
void spek_pipeline_start(struct spek_pipeline *pipeline);
const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb, void *cb_data);
const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline);
void spek_pipeline_start(struct spek_pipeline *pipeline);
void spek_pipeline_close(struct spek_pipeline *pipeline);
#ifdef __cplusplus

View File

@ -16,8 +16,12 @@
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cmath>
#include <wx/dcbuffer.h>
#include "spek-audio-desc.hh"
#include "spek-pipeline.h"
#include "spek-spectrogram.hh"
BEGIN_EVENT_TABLE(SpekSpectrogram, wxPanel)
@ -39,7 +43,9 @@ enum
SpekSpectrogram::SpekSpectrogram(wxFrame *parent) :
wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
palette(RULER, BANDS)
pipeline(NULL),
palette(RULER, BANDS),
image()
{
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
@ -122,8 +128,57 @@ void SpekSpectrogram::render(wxDC& dc)
dc.DrawBitmap(bmp, w - RPAD + GAP, TPAD);
}
void SpekSpectrogram::pipeline_cb(int sample, float *values, void *cb_data)
{
static double log10_threshold = log10(-THRESHOLD);
SpekSpectrogram *s = (SpekSpectrogram *)cb_data;
for (int y = 0; y < BANDS; y++) {
double level = log10(1.0 - THRESHOLD + values[y]) / log10_threshold;
if (level > 1.0) level = 1.0;
uint32_t color = get_color(level);
s->image.SetRGB(
sample,
y,
color >> 16,
(color >> 8) & 0xFF,
color & 0xFF
);
}
s->Refresh(false); // TODO: refresh only one pixel column
}
void SpekSpectrogram::start()
{
if(this->pipeline) {
spek_pipeline_close(this->pipeline);
this->pipeline = NULL;
}
// The number of samples is the number of pixels available for the image.
// The number of bands is fixed, FFT results are very different for
// different values but we need some consistency.
wxSize size = GetClientSize();
int samples = size.GetWidth() - LPAD - RPAD;
if (samples > 0) {
this->image.Create(samples, BANDS);
this->pipeline = spek_pipeline_open(
this->path.utf8_str(),
BANDS,
samples,
THRESHOLD,
pipeline_cb,
this
);
spek_pipeline_start(this->pipeline);
this->desc = spek_audio_desc(spek_pipeline_properties(this->pipeline));
} else {
this->image.Create(1, 1);
}
Refresh(false);
}
// Modified version of Dan Bruton's algorithm:

View File

@ -21,6 +21,8 @@
#include <wx/wx.h>
struct spek_pipeline;
class SpekSpectrogram : public wxPanel
{
public:
@ -33,11 +35,14 @@ private:
void render(wxDC& dc);
void start();
uint32_t get_color(double level);
static void pipeline_cb(int sample, float *values, void *cb_data);
static uint32_t get_color(double level);
spek_pipeline *pipeline;
wxString path;
wxString info;
wxString desc;
wxImage palette;
wxImage image;
DECLARE_EVENT_TABLE()
};

View File

@ -24,8 +24,6 @@ using Pango;
namespace Spek {
class Spectrogram : DrawingArea {
private Pipeline pipeline;
private ImageSurface image;
public Spectrogram () {
@ -38,30 +36,6 @@ namespace Spek {
surface.write_to_png (file_name);
}
private void start () {
if (pipeline != null) {
pipeline.stop ();
}
// The number of samples is the number of pixels available for the image.
// The number of bands is fixed, FFT results are very different for
// different values but we need some consistency.
Allocation allocation;
get_allocation (out allocation);
int samples = allocation.width - LPAD - RPAD;
if (samples > 0) {
image = new ImageSurface (Format.RGB24, samples, BANDS);
pipeline = new Pipeline (file_name, BANDS, samples, THRESHOLD, data_cb);
pipeline.start ();
info = pipeline.description;
} else {
image = null;
pipeline = null;
}
queue_draw ();
}
private int prev_width = -1;
protected override void size_allocate (Gdk.Rectangle allocation) {
base.size_allocate (allocation);
@ -74,16 +48,6 @@ namespace Spek {
}
}
private double log10_threshold = Math.log10 (-THRESHOLD);
private void data_cb (int sample, float[] values) {
for (int y = 0; y < BANDS; y++) {
var level = double.min (
1.0, Math.log10 (1.0 - THRESHOLD + values[y]) / log10_threshold);
put_pixel (image, sample, y, get_color (level));
}
Idle.add (() => { queue_draw (); return false; });
}
protected override bool expose_event (EventExpose event) {
var window = get_window ();
var cr = cairo_create (window);