diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | galsa.c | 160 | ||||
-rw-r--r-- | galsa.h | 49 | ||||
-rw-r--r-- | gbeat.c | 370 | ||||
-rw-r--r-- | gbeat.glade | 359 | ||||
-rw-r--r-- | gfft.c | 100 | ||||
-rw-r--r-- | gfft.h | 39 |
8 files changed, 1097 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fae0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +gbeat +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8a4edfd --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +love: gbeat +.PHONY: love clean + +CC=gcc +PKGS=glib-2.0 gtk+-2.0 libglade-2.0 gthread-2.0 fftw3 alsa +CFLAGS=-std=gnu99 -Wall -Wextra -pedantic -Wno-unused-parameter -g -O0 \ + `pkg-config --cflags $(PKGS)` +LDFLAGS=-g -lm `pkg-config --libs $(PKGS)` -rdynamic + +clean: + rm -f *.o gbeat + +gbeat: galsa.o gfft.o gbeat.o + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.c *.h + $(CC) $(CFLAGS) -c -o $@ $< + @@ -0,0 +1,160 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <ctype.h> + +#include "galsa.h" + +struct device *device_open(const char *path, unsigned rate, int chan, size_t pktsize) +{ + struct device *dev; + snd_pcm_hw_params_t *hw_params = NULL; + snd_pcm_t *hdl = NULL; + char errmsg[256]; + +#define E(x) do { int err; if ((err = x) < 0) { \ + snprintf(errmsg, sizeof(errmsg), "%s:%d/%s(): %s() failed: %s (%d)\n",\ + __FILE__, __LINE__, __func__, #x, snd_strerror(err), err); \ + goto out_err; \ + } } while (0) + + printf("Opening alsa://%s with %d ch %dHz...\n", path, chan, rate); + E(snd_pcm_open(&hdl, path, SND_PCM_STREAM_CAPTURE, 0)); + E(snd_pcm_hw_params_malloc(&hw_params)); + E(snd_pcm_hw_params_any(hdl, hw_params)); + E(snd_pcm_hw_params_set_access(hdl, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); + E(snd_pcm_hw_params_set_format(hdl, hw_params, SND_PCM_FORMAT_S16_LE)); + E(snd_pcm_hw_params_set_rate_near(hdl, hw_params, &rate, 0)); + E(snd_pcm_hw_params_set_channels(hdl, hw_params, chan)); + E(snd_pcm_hw_params(hdl, hw_params)); + snd_pcm_hw_params_free(hw_params); + hw_params = NULL; + + E(snd_pcm_prepare(hdl)); + printf("... Ok.\n"); + + dev = (struct device *)malloc(sizeof(struct device)); + dev->path = strdup(path); + dev->rate = rate; + dev->chan = chan; + dev->handle = hdl; + dev->thread = NULL; + dev->queue = g_async_queue_new(); + dev->state = DEV_OPEN; + dev->pktsize = pktsize; + return dev; + +out_err: + if (hw_params) + snd_pcm_hw_params_free(hw_params); + if (hdl) + snd_pcm_close(hdl); + return NULL; +} + +static gpointer threadf(gpointer param) +{ + struct device *dev = (struct device *)param; + struct qe *ent, *entx; + int pcmreturn; + ptrdiff_t pos; + + do { + ent = malloc(sizeof(struct qe) + + dev->pktsize * sizeof(sample) * dev->chan); + ent->message = QE_DATA; + ent->size = dev->pktsize; + pos = 0; + + do { + while ((pcmreturn = snd_pcm_readi(dev->handle, + ent->samples + pos, ent->size - pos)) < 0) { + + entx = calloc(sizeof(struct qe), 1); + entx->message = QE_ERROR; + entx->errcode = pcmreturn; + asprintf(&entx->errp, + "PCM read failed: %s (%d)", + snd_strerror(pcmreturn), pcmreturn); + g_async_queue_push(dev->queue, entx); + + if (dev->state != DEV_RUNNING) { + free(ent); + goto out; + } + + snd_pcm_prepare(dev->handle); + } + pos += pcmreturn; + } while (pos < ent->size); + g_async_queue_push(dev->queue, ent); + } while (dev->state == DEV_RUNNING); + +out: + entx = calloc(sizeof(struct qe), 1); + entx->message = QE_STOPPED; + g_async_queue_push(dev->queue, entx); + + return NULL; +} + +void device_start(struct device *dev) +{ + GError *error = NULL; + if (dev->thread) + return; + dev->state = DEV_RUNNING; + dev->thread = g_thread_create(threadf, dev, 0, &error); +} + +void device_stop(struct device *dev) +{ + if (dev->state == DEV_RUNNING) + dev->state = DEV_STOPREQ; +} + +void device_stop_wait(struct device *dev) +{ + int got_stop = 0, iter = 0; + struct qe *qe; + + if (dev->state == DEV_OPEN) + return; + + device_stop(dev); + + while (!got_stop && iter++ < 10) { + qe = (struct qe *)g_async_queue_timeout_pop(dev->queue, 250000); + switch (qe->message) { + case QE_DATA: + break; + case QE_ERROR: + free(qe->errp); + break; + case QE_STOPPED: + got_stop = 1; + break; + } + free(qe); + } + if (!got_stop) + fprintf(stderr, "failed to terminate recording thread!\n"); + dev->state = DEV_OPEN; +} + +void device_stop_done(struct device *dev) +{ + dev->state = DEV_OPEN; +} + +void device_close(struct device *dev) +{ + device_stop_wait(dev); + + snd_pcm_close(dev->handle); + free(dev->path); + free(dev); +} + + @@ -0,0 +1,49 @@ +#ifndef _GALSA_H +#define _GALSA_H + +#include <alsa/asoundlib.h> +#include <glib.h> + +typedef short sample; + +enum dev_runstate { + DEV_OPEN, + DEV_RUNNING, + DEV_STOPREQ, +}; +enum qe_message { + QE_DATA, + QE_ERROR, + QE_STOPPED, +}; + +struct device { + char *path; + unsigned rate; + int chan; + snd_pcm_t *handle; + GAsyncQueue *queue; + size_t pktsize; + + GThread *thread; + enum dev_runstate state; +}; + +struct qe { + enum qe_message message; + int errcode; + char *errp; + + ssize_t size; + sample samples[]; +}; + +extern struct device *device_open(const char *path, + unsigned rate, int chan, size_t pktsize); +extern void device_start(struct device *dev); +extern void device_stop(struct device *dev); +extern void device_stop_wait(struct device *dev); +extern void device_stop_done(struct device *dev); +extern void device_close(struct device *dev); + +#endif @@ -0,0 +1,370 @@ +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <math.h> +#include <ctype.h> + +#include "galsa.h" +#include "gfft.h" + +#define gladewidget(x) GtkWidget *x = glade_xml_get_widget(xml, #x); +GladeXML *xml; + +static GdkPixmap *pxm; +//static GdkImage *img_buffer; +//static GdkPixbuf *pxb; +double ofac_s = 110./64000.; // 1./(perpixel); + +static gboolean bother(gpointer param) +{ + gladewidget(img) + gladewidget(status) + char buf[256]; + gtk_widget_queue_draw(img); + snprintf(buf, sizeof(buf), "scaling: %lf", ofac_s); + gtk_label_set_text(GTK_LABEL(status), buf); + return FALSE; +} + +static double clen(fftw_complex c) +{ + return sqrt(c[0] * c[0] + c[1] * c[1]); +} + +static double degs(fftw_complex c, double radbase) +{ + double len = clen(c); + double a = c[0] / len, b = c[1] / len; + double alpha = asin(b); + if (a < 0.) + alpha = M_PI - alpha; + alpha -= radbase; + while (alpha < 0) + alpha += 2 * M_PI; + while (alpha >= 2 * M_PI) + alpha -= 2 * M_PI; + return alpha * 180 / M_PI; +} + +static double hsvcos(double i) +{ + i /= 180; + while (i < -1) + i += 2; + while (i > 1) + i -= 2; + if (i < -0.667 || i > 0.667) + return 0.0; + return cos(i * 0.75 * M_PI); +} + +static GdkColor complex2color(fftw_complex c, double lscale) +{ + double l = clen(c), d = degs(c, 0.0); + GdkColor ret; + + l /= lscale; + if (l > 1.0) + l = 1.0; + l *= l; + + ret.red = 65535 * l * hsvcos(d); + ret.green = 65535 * l * hsvcos(d + 120); + ret.blue = 65535 * l * hsvcos(d + 240); + return ret; +} + +static GdkColor colorinv(GdkColor c) +{ + c.red = 65535 - c.red; + c.green = 65535 - c.green; + c.blue = 65535 - c.blue; + return c; +} + +static gboolean errbox(gpointer data) +{ + struct fe *fe = data; + + gladewidget(mainwnd) + GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW(mainwnd), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "capture error:\n%s", fe->errp); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + + free(fe->errp); + free(fe); + return FALSE; +} + +#define NBANDS 32 +#define EVALBANDS 32 +#define FPS 60 +#define HISTSIZE FPS * 7 + +struct bdctx { + double bandenergy[EVALBANDS][HISTSIZE]; +// double boffs[EVALBANDS]; + double goffs; +}; + +struct renderargs { + struct fft_runner *fftr; + struct bdctx bdctx; +}; + +size_t bd_nbands, bd_bandlim; +double bd_charge, bd_decay; + +static void logscale(fftw_complex *in, size_t isz, fftw_complex *out, size_t osz) +{ + double tlog = 1. / log(isz + 1.0); + double *divs = calloc(sizeof(double), osz); + memset(out, 0, osz * sizeof(fftw_complex)); + + for (size_t i = 0; i < isz; i++) { + double bpos = log(i + 1.0) * tlog; + double tpos = log(i + 2.0) * tlog; + double len = tpos - bpos; + + double xdiv = 0.25 * log(i + 2.) / log(isz + 2.); + + for (size_t o = 0; o < osz; o++) { + double dpos = o / (double) osz; + if (dpos < bpos || dpos > tpos) + continue; + double factor = 0.5 * (1. - cos ((dpos - bpos) / len)); + out[o][0] += in[i][0] * factor * xdiv; + out[o][1] += in[i][1] * factor * xdiv; + divs[o] += factor; + } + } + for (size_t o = 0; o < osz; o++) { + double d = divs[o]; + out[o][0] /= d; + out[o][1] /= d; + } +} + +static bool bd_perform(struct bdctx *ctx, fftw_complex *data, size_t size) +{ + size_t bandsize = size / NBANDS; + size_t beatbands = 0; + + for (size_t i = 0; i < bd_bandlim; i++) { + double energy = 0.0, average = 0.0; + for (size_t j = 0; j < bandsize; j++) { + energy += sqrt( + pow(data[i * bandsize + j][0], 2) + + pow(data[i * bandsize + j][1], 2)); + } + energy /= bandsize; + + for (size_t j = 0; j < HISTSIZE; j++) + average += ctx->bandenergy[i][j]; + average /= HISTSIZE; + + if (energy > average * 1.5 * (1.0 + ctx->goffs)) + beatbands++; + + memmove(&ctx->bandenergy[i][0], &ctx->bandenergy[i][1], + (HISTSIZE - 1) * sizeof(double)); + ctx->bandenergy[i][HISTSIZE - 1] = energy; + } + + if (beatbands > bd_nbands) + ctx->goffs = bd_charge; + else + ctx->goffs *= bd_decay; + + return beatbands > bd_nbands; +} + +static gpointer renderf(gpointer param) +{ + struct renderargs *arg = param; + struct fe *fe; + + size_t logsz = 400; + fftw_complex *pos, *logsc; + + GdkDrawable *drawto = GDK_DRAWABLE(pxm); + GdkGC *gc = gdk_gc_new(drawto); + + gint w, h, y; + GdkColor col; + + logsc = fftw_malloc(sizeof(fftw_complex) * logsz); + + gdk_drawable_get_size(drawto, &w, &h); +#if 0 + if (h > (gint)arg->fftr->dev->pktsize) + h = arg->fftr->dev->pktsize; +#endif + if (h > (gint)logsz) + h = logsz; + + while (1) { + fe = g_async_queue_pop(arg->fftr->queue); + + if (fe->message == FE_STOPPED) { + free(fe); + break; + } else if (fe->message == FE_DEV_ERROR) { + g_idle_add(errbox, fe); + continue; + } + + logscale(fe->data, arg->fftr->dev->pktsize, logsc, logsz); + + // bool beat = bd_perform(&arg->bdctx, fe->data, fe->size); + bool beat = bd_perform(&arg->bdctx, logsc, logsz); + + if (beat) { + FILE *f = fopen("/home/equinox/python/.dmxbeat", "w"); + if (f) + fclose(f); + } + + gdk_threads_enter(); + gdk_draw_drawable(drawto, gc, drawto, 1, 0, 0, 0, w - 1, h); + + pos = logsc; // fe->data; + for (y = h - 1; y >= 0; y--) { + fftw_complex c; + c[0] = c[1] = sqrt( + pow(pos[0][0], 2.0) + + pow(pos[0][1], 2.0)); + pos++; + col = complex2color(c, 4.0); + if (beat) + col = colorinv(col); + + gdk_gc_set_rgb_fg_color(gc, &col); + gdk_draw_point(drawto, gc, w - 1, y); + } + gdk_threads_leave(); + g_idle_add(bother, NULL); + + fftw_free(fe->data); + free(fe); + }; + + fftw_free(logsc); + return NULL; +} + + +struct device *dev; + +void on_bdapply(GtkWidget *widget, gpointer user_data) +{ + gladewidget(bdnbands); + gladewidget(bdbandlim); + gladewidget(bdcharge); + gladewidget(bddecay); + + const gchar *nbands = gtk_entry_get_text(GTK_ENTRY(bdnbands)); + const gchar *bandlim = gtk_entry_get_text(GTK_ENTRY(bdbandlim)); + const gchar *charge = gtk_entry_get_text(GTK_ENTRY(bdcharge)); + const gchar *decay = gtk_entry_get_text(GTK_ENTRY(bddecay)); + + bd_nbands = strtoul(nbands, NULL, 10); + bd_bandlim = strtoul(bandlim, NULL, 10); + if (bd_bandlim > EVALBANDS) + bd_bandlim = EVALBANDS; + bd_charge = strtod(charge, NULL); + bd_decay = strtod(decay, NULL); +} + +void on_open_toggled(GtkWidget *widget, gpointer user_data) +{ + //gladewidget(mainwnd) + gladewidget(bopen) + gladewidget(brun) + gboolean state; + + if (!!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bopen)) == !!(dev != NULL)) + return; + + if (dev) { + device_close(dev); + dev = NULL; + } else do { + gladewidget(edev) + gladewidget(erate) + const gchar *udev = gtk_entry_get_text(GTK_ENTRY(edev)); + const gchar *rate = gtk_entry_get_text(GTK_ENTRY(erate)); + gchar *errptr; + unsigned long urate; + + urate = strtoul(rate, &errptr, 0); +/* if (*errptr || !*rate) { + strcpy(errmsg, "Invalid rate"); + break; + } */ + + dev = device_open(udev, urate, 2, urate / FPS); + } while (0); + + state = dev != NULL; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(brun), 0); + gtk_widget_set_sensitive(brun, state); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bopen), state); + +/* if (*errmsg) { + GtkWidget *dlg = gtk_message_dialog_new(GTK_WINDOW(mainwnd), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "Error: %s", errmsg); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + } */ +} + +void on_run_toggled(GtkWidget *widget, gpointer user_data) +{ + gladewidget(brun) + GError *error = NULL; + + if (!dev) + return; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(brun))) { + struct fft_runner *fftr = fftrun_attach(dev, window_hard, 6); + struct renderargs *arg = calloc(sizeof(*arg), 1); + arg->fftr = fftr; + g_thread_create(renderf, arg, 0, &error); + + device_start(dev); + } else { + device_stop(dev); + } +} + +int main(int argc, char **argv) +{ + g_thread_init(NULL); + gtk_init(&argc, &argv); + gdk_threads_init(); + xml = glade_xml_new("gbeat.glade", NULL, NULL); + glade_xml_signal_autoconnect(xml); + { + gladewidget(edev) + gladewidget(erate) + gladewidget(img) + gtk_entry_set_text(GTK_ENTRY(edev), "hw:0,0"); + gtk_entry_set_text(GTK_ENTRY(erate), "48000"); + //pxb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 0, 8, 300, 720); + //gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxb); + //img_buffer = gdk_image_new(GDK_IMAGE_FASTEST, gdk_visual_get_system(), 300, 740); + //gtk_image_set_from_image(GTK_IMAGE(img), img_buffer, NULL); + pxm = gdk_pixmap_new(NULL, 300, 720, 24); + gtk_image_set_from_pixmap(GTK_IMAGE(img), pxm, NULL); + } + on_bdapply(NULL, NULL); + gtk_main (); + return 0; +} + diff --git a/gbeat.glade b/gbeat.glade new file mode 100644 index 0000000..ac0cc39 --- /dev/null +++ b/gbeat.glade @@ -0,0 +1,359 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="mainwnd"> + <property name="visible">True</property> + <property name="title" translatable="yes">GtkAlsaViewer</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <signal name="delete_event" handler="gtk_false" last_modification_time="Mon, 04 Dec 2006 16:48:01 GMT"/> + <signal name="destroy" handler="gtk_main_quit" last_modification_time="Mon, 04 Dec 2006 16:48:32 GMT"/> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">3</property> + + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">3</property> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Device:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="edev"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Rate:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="erate"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">5</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + <property name="width_chars">5</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkToggleButton" id="bopen"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Open</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <signal name="toggled" handler="on_open_toggled" last_modification_time="Mon, 04 Dec 2006 16:49:00 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkToggleButton" id="brun"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Capture</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <signal name="toggled" handler="on_run_toggled" last_modification_time="Mon, 04 Dec 2006 16:51:14 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">3</property> + + <child> + <widget class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="label" translatable="yes">nb, c, d</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="bdnbands"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">3</property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + <property name="width_chars">7</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="bdbandlim"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">12</property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + <property name="width_chars">7</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="bdcharge"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">1.4</property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + <property name="width_chars">7</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="bddecay"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">0.6</property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + <property name="width_chars">7</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="bdapply"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Apply</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_bdapply" last_modification_time="Mon, 04 Dec 2006 16:49:00 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHSeparator" id="hseparator1"> + <property name="visible">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkImage" id="img"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="status"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> @@ -0,0 +1,100 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <ctype.h> + +#include "gfft.h" + +void window_hard(sample *in, double *out, size_t size, int ch, int nch) +{ + for (size_t i = 0; i < size; i++) { + sample val = in[i * nch + ch]; + out[i] = (1./32768.) * val; + } +} + +void window_hann(sample *in, double *out, size_t size, int ch, int nch) +{ + double cfac = 2. * M_PI / ((double) size - 1); + for (size_t i = 0; i < size; i++) { + sample val = in[i * nch + ch]; + out[i] = (1./32768.) * val + * 0.5 * (1. - cos (cfac * (double)i)); + } +} + +static gpointer fftrun_threadf(gpointer param) +{ + struct fft_runner *fftr = param; + fftw_plan p; + double *in; + fftw_complex *out; + struct qe *qe; + struct fe *fe; + int stopped = 0; + sample *hist; + size_t span = fftr->span; + size_t pktsize = fftr->dev->pktsize; + + hist = malloc(sizeof(sample) * pktsize * span * fftr->dev->chan); + in = fftw_malloc(sizeof(double) * pktsize * span); + out = fftw_malloc(sizeof(fftw_complex) * pktsize * span); + p = fftw_plan_dft_r2c_1d(pktsize * span, in, out, + FFTW_MEASURE | FFTW_DESTROY_INPUT); + free(out); + + while (!stopped) { + qe = g_async_queue_pop(fftr->dev->queue); + fe = calloc(sizeof(struct fe), 1); + + switch (qe->message) { + case QE_STOPPED: + fe->message = FE_STOPPED; + stopped = 1; + break; + + case QE_ERROR: + fe->message = FE_DEV_ERROR; + fe->errcode = qe->errcode; + fe->errp = qe->errp; + break; + + case QE_DATA: + memmove(hist, hist + pktsize, + pktsize * (span - 1) * fftr->dev->chan * sizeof(*hist)); + memmove(hist + pktsize * (span - 1), qe->samples, + pktsize * fftr->dev->chan * sizeof(*hist)); + + fe->message = FE_DATA; + fe->data = fftw_malloc(sizeof(fftw_complex) + * pktsize * span); + fe->size = pktsize * span; + fftr->winf(hist, in, pktsize * span, 0, fftr->dev->chan); + fftw_execute_dft_r2c(p, in, fe->data); + break; + } + free(qe); + + g_async_queue_push(fftr->queue, fe); + } + + free(in); + fftw_destroy_plan(p); + return NULL; +} + +struct fft_runner *fftrun_attach(struct device *device, + window_func *winf, size_t span) +{ + GError *error = NULL; + struct fft_runner *fftr; + + fftr = malloc(sizeof(*fftr)); + fftr->dev = device; + fftr->queue = g_async_queue_new(); + fftr->thread = g_thread_create(fftrun_threadf, fftr, 0, &error); + fftr->winf = winf; + fftr->span = span; + return fftr; +} + @@ -0,0 +1,39 @@ +#ifndef _GFFT_H +#define _GFFT_H + +#include <glib.h> +#include <fftw3.h> + +#include "galsa.h" + +typedef void window_func(sample *in, double *out, size_t size, int ch, int nch); + +struct fft_runner { + struct device *dev; + GAsyncQueue *queue; + GThread *thread; + window_func *winf; + size_t span; +}; + +enum fe_message { + FE_DATA, + FE_DEV_ERROR, + FE_STOPPED, +}; + +struct fe { + enum fe_message message; + int errcode; + char *errp; + fftw_complex *data; + size_t size; +}; + +extern void window_hard(sample *in, double *out, size_t size, int ch, int nch); +extern void window_hann(sample *in, double *out, size_t size, int ch, int nch); + +extern struct fft_runner *fftrun_attach(struct device *device, + window_func *winf, size_t span); + +#endif |