diff options
author | David Lamparter <equinox@diac24.net> | 2013-06-21 12:32:46 +0200 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2013-06-21 12:38:29 +0200 |
commit | e8d37e36f8b97fcc29c8f67d38f0cd5d48ec9d3f (patch) | |
tree | a3b493b069d16f4877cd43697bd194256d87c288 /gbeat.c | |
parent | 2fd3ece434eb3656f481afd4e8801637fa8ce844 (diff) |
import
Diffstat (limited to 'gbeat.c')
-rw-r--r-- | gbeat.c | 370 |
1 files changed, 370 insertions, 0 deletions
@@ -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; +} + |