gstreamer/gst/synaesthesia/synaescope.c
Thomas Vander Stichele ffe763c043 expand tabs
Original commit message from CVS:
expand tabs
2005-12-06 19:48:07 +00:00

542 lines
13 KiB
C

/* synaescope.cpp
* Copyright (C) 1999,2002 Richard Boulton <richard@tartarus.org>
*
* Much code copied from Synaesthesia - a program to display sound
* graphically, by Paul Francis Harrison <pfh@yoyo.cc.monash.edu.au>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "synaescope.h"
#include <pthread.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#define SCOPE_BG_RED 0
#define SCOPE_BG_GREEN 0
#define SCOPE_BG_BLUE 0
#define FFT_BUFFER_SIZE_LOG 9
#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG)
#define syn_width 320
#define syn_height 200
#define brightMin 200
#define brightMax 2000
#define brightDec 10
#define brightInc 6
#define brTotTargetLow 5000
#define brTotTargetHigh 15000
static int autobrightness = 1; /* Whether to use automatic brightness adjust */
static unsigned int brightFactor = 400;
static unsigned char output[syn_width * syn_height * 2];
static guint32 display[syn_width * syn_height];
static gint16 pcmt_l[FFT_BUFFER_SIZE];
static gint16 pcmt_r[FFT_BUFFER_SIZE];
static gint16 pcm_l[FFT_BUFFER_SIZE];
static gint16 pcm_r[FFT_BUFFER_SIZE];
static double fftout_l[FFT_BUFFER_SIZE];
static double fftout_r[FFT_BUFFER_SIZE];
static double fftmult[FFT_BUFFER_SIZE / 2 + 1];
static double corr_l[FFT_BUFFER_SIZE];
static double corr_r[FFT_BUFFER_SIZE];
static int clarity[FFT_BUFFER_SIZE]; /* Surround sound */
static double cosTable[FFT_BUFFER_SIZE];
static double negSinTable[FFT_BUFFER_SIZE];
static int bitReverse[FFT_BUFFER_SIZE];
static int scaleDown[256];
static void synaes_fft (double *x, double *y);
static void synaescope_coreGo (void);
#define SYNAESCOPE_DOLOOP() \
while (running) { \
gint bar; \
guint val; \
gint val2; \
unsigned char *outptr = output; \
int w; \
\
synaescope_coreGo(); \
\
outptr = output; \
for (w=0; w < syn_width * syn_height; w++) { \
bits[w] = colEq[(outptr[0] >> 4) + (outptr[1] & 0xf0)]; \
outptr += 2; \
} \
\
GDK_THREADS_ENTER(); \
gdk_draw_image(win,gc,image,0,0,0,0,-1,-1); \
gdk_flush(); \
GDK_THREADS_LEAVE(); \
dosleep(SCOPE_SLEEP); \
}
static inline void
addPixel (unsigned char *output, int x, int y, int br1, int br2)
{
unsigned char *p;
if (x < 0 || x >= syn_width || y < 0 || y >= syn_height)
return;
p = output + x * 2 + y * syn_width * 2;
if (p[0] < 255 - br1)
p[0] += br1;
else
p[0] = 255;
if (p[1] < 255 - br2)
p[1] += br2;
else
p[1] = 255;
}
static inline void
addPixelFast (unsigned char *p, int br1, int br2)
{
if (p[0] < 255 - br1)
p[0] += br1;
else
p[0] = 255;
if (p[1] < 255 - br2)
p[1] += br2;
else
p[1] = 255;
}
static void
synaescope_coreGo (void)
{
int i, j;
register unsigned long *ptr;
register unsigned long *end;
int heightFactor;
int actualHeight;
int heightAdd;
double brightFactor2;
long int brtot;
memcpy (pcm_l, pcmt_l, sizeof (pcm_l));
memcpy (pcm_r, pcmt_r, sizeof (pcm_r));
for (i = 0; i < FFT_BUFFER_SIZE; i++) {
fftout_l[i] = pcm_l[i];
fftout_r[i] = pcm_r[i];
}
synaes_fft (fftout_l, fftout_r);
for (i = 0 + 1; i < FFT_BUFFER_SIZE; i++) {
double x1 = fftout_l[bitReverse[i]];
double y1 = fftout_r[bitReverse[i]];
double x2 = fftout_l[bitReverse[FFT_BUFFER_SIZE - i]];
double y2 = fftout_r[bitReverse[FFT_BUFFER_SIZE - i]];
double aa, bb;
corr_l[i] = sqrt (aa = (x1 + x2) * (x1 + x2) + (y1 - y2) * (y1 - y2));
corr_r[i] = sqrt (bb = (x1 - x2) * (x1 - x2) + (y1 + y2) * (y1 + y2));
clarity[i] = (int) (
((x1 + x2) * (x1 - x2) + (y1 + y2) * (y1 - y2)) / (aa + bb) * 256);
}
/* Asger Alstrupt's optimized 32 bit fade */
/* (alstrup@diku.dk) */
ptr = (unsigned long *) output;
end = (unsigned long *) (output + syn_width * syn_height * 2);
do {
/*Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4; */
if (*ptr) {
if (*ptr & 0xf0f0f0f0) {
*ptr = *ptr - ((*ptr & 0xf0f0f0f0) >> 4) - ((*ptr & 0xe0e0e0e0) >> 5);
} else {
*ptr = (*ptr * 14 >> 4) & 0x0f0f0f0f;
/*Should be 29/32 to be consistent. Who cares. This is totally */
/* hacked anyway. */
/*unsigned char *subptr = (unsigned char*)(ptr++); */
/*subptr[0] = (int)subptr[0] * 29 / 32; */
/*subptr[1] = (int)subptr[0] * 29 / 32; */
/*subptr[2] = (int)subptr[0] * 29 / 32; */
/*subptr[3] = (int)subptr[0] * 29 / 32; */
}
}
ptr++;
} while (ptr < end);
heightFactor = FFT_BUFFER_SIZE / 2 / syn_height + 1;
actualHeight = FFT_BUFFER_SIZE / 2 / heightFactor;
heightAdd = (syn_height + actualHeight) >> 1;
/* Correct for window size */
brightFactor2 = (brightFactor / 65536.0 / FFT_BUFFER_SIZE) *
sqrt (actualHeight * syn_width / (320.0 * 200.0));
brtot = 0;
for (i = 1; i < FFT_BUFFER_SIZE / 2; i++) {
/*int h = (int)( corr_r[i]*280 / (corr_l[i]+corr_r[i]+0.0001)+20 ); */
if (corr_l[i] > 0 || corr_r[i] > 0) {
int h = (int) (corr_r[i] * syn_width / (corr_l[i] + corr_r[i]));
/* int h = (int)( syn_width - 1 ); */
int br1, br2, br = (int) ((corr_l[i] + corr_r[i]) * i * brightFactor2);
int px = h, py = heightAdd - i / heightFactor;
brtot += br;
br1 = br * (clarity[i] + 128) >> 8;
br2 = br * (128 - clarity[i]) >> 8;
if (br1 < 0)
br1 = 0;
else if (br1 > 255)
br1 = 255;
if (br2 < 0)
br2 = 0;
else if (br2 > 255)
br2 = 255;
/*unsigned char *p = output+ h*2+(164-((i<<8)>>FFT_BUFFER_SIZE_LOG))*(syn_width*2); */
if (px < 30 || py < 30 || px > syn_width - 30 || py > syn_height - 30) {
addPixel (output, px, py, br1, br2);
for (j = 1; br1 > 0 || br2 > 0;
j++, br1 = scaleDown[br1], br2 = scaleDown[br2]) {
addPixel (output, px + j, py, br1, br2);
addPixel (output, px, py + j, br1, br2);
addPixel (output, px - j, py, br1, br2);
addPixel (output, px, py - j, br1, br2);
}
} else {
unsigned char *p = output + px * 2 + py * syn_width * 2, *p1 = p, *p2 =
p, *p3 = p, *p4 = p;
addPixelFast (p, br1, br2);
for (; br1 > 0 || br2 > 0; br1 = scaleDown[br1], br2 = scaleDown[br2]) {
p1 += 2;
addPixelFast (p1, br1, br2);
p2 -= 2;
addPixelFast (p2, br1, br2);
p3 += syn_width * 2;
addPixelFast (p3, br1, br2);
p4 -= syn_width * 2;
addPixelFast (p4, br1, br2);
}
}
}
}
/* Apply autoscaling: makes quiet bits brighter, and loud bits
* darker, but still keeps loud bits brighter than quiet bits. */
if (brtot != 0 && autobrightness) {
long int brTotTarget = brTotTargetHigh;
if (brightMax != brightMin) {
brTotTarget -= ((brTotTargetHigh - brTotTargetLow) *
(brightFactor - brightMin)) / (brightMax - brightMin);
}
if (brtot < brTotTarget) {
brightFactor += brightInc;
if (brightFactor > brightMax)
brightFactor = brightMax;
} else {
brightFactor -= brightDec;
if (brightFactor < brightMin)
brightFactor = brightMin;
}
/* printf("brtot: %ld\tbrightFactor: %d\tbrTotTarget: %d\n",
brtot, brightFactor, brTotTarget); */
}
}
#define BOUND(x) ((x) > 255 ? 255 : (x))
#define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2)
static void
synaescope32 ()
{
unsigned char *outptr;
guint32 colEq[256];
int i;
guint32 bg_color;
for (i = 0; i < 256; i++) {
int red = PEAKIFY ((i & 15 * 16));
int green = PEAKIFY ((i & 15) * 16 + (i & 15 * 16) / 4);
int blue = PEAKIFY ((i & 15) * 16);
colEq[i] = (red << 16) + (green << 8) + blue;
}
bg_color = (SCOPE_BG_RED << 16) + (SCOPE_BG_GREEN << 8) + SCOPE_BG_BLUE;
synaescope_coreGo ();
outptr = output;
for (i = 0; i < syn_width * syn_height; i++) {
display[i] = colEq[(outptr[0] >> 4) + (outptr[1] & 0xf0)];
outptr += 2;
}
}
#if 0
static void
synaescope16 (void *data)
{
guint16 *bits;
guint16 colEq[256];
int i;
GdkWindow *win;
GdkColormap *c;
GdkVisual *v;
GdkGC *gc;
GdkColor bg_color;
win = (GdkWindow *) data;
GDK_THREADS_ENTER ();
c = gdk_colormap_get_system ();
gc = gdk_gc_new (win);
v = gdk_window_get_visual (win);
for (i = 0; i < 256; i++) {
GdkColor color;
color.red = PEAKIFY ((i & 15 * 16)) << 8;
color.green = PEAKIFY ((i & 15) * 16 + (i & 15 * 16) / 4) << 8;
color.blue = PEAKIFY ((i & 15) * 16) << 8;
gdk_color_alloc (c, &color);
colEq[i] = color.pixel;
}
/* Create render image */
if (image) {
gdk_image_destroy (image);
image = NULL;
}
image = gdk_image_new (GDK_IMAGE_FASTEST, v, syn_width, syn_height);
bg_color.red = SCOPE_BG_RED << 8;
bg_color.green = SCOPE_BG_GREEN << 8;
bg_color.blue = SCOPE_BG_BLUE << 8;
gdk_color_alloc (c, &bg_color);
GDK_THREADS_LEAVE ();
assert (image);
assert (image->bpp == 2);
bits = (guint16 *) image->mem;
running = 1;
SYNAESCOPE_DOLOOP ();
}
static void
synaescope8 (void *data)
{
unsigned char *outptr;
guint8 *bits;
guint8 colEq[256];
int i;
GdkWindow *win;
GdkColormap *c;
GdkVisual *v;
GdkGC *gc;
GdkColor bg_color;
win = (GdkWindow *) data;
GDK_THREADS_ENTER ();
c = gdk_colormap_get_system ();
gc = gdk_gc_new (win);
v = gdk_window_get_visual (win);
for (i = 0; i < 64; i++) {
GdkColor color;
color.red = PEAKIFY ((i & 7 * 8) * 4) << 8;
color.green = PEAKIFY ((i & 7) * 32 + (i & 7 * 8) * 2) << 8;
color.blue = PEAKIFY ((i & 7) * 32) << 8;
gdk_color_alloc (c, &color);
colEq[i * 4] = color.pixel;
colEq[i * 4 + 1] = color.pixel;
colEq[i * 4 + 2] = color.pixel;
colEq[i * 4 + 3] = color.pixel;
}
/* Create render image */
if (image) {
gdk_image_destroy (image);
image = NULL;
}
image = gdk_image_new (GDK_IMAGE_FASTEST, v, syn_width, syn_height);
bg_color.red = SCOPE_BG_RED << 8;
bg_color.green = SCOPE_BG_GREEN << 8;
bg_color.blue = SCOPE_BG_BLUE << 8;
gdk_color_alloc (c, &bg_color);
GDK_THREADS_LEAVE ();
assert (image);
assert (image->bpp == 1);
bits = (guint8 *) image->mem;
running = 1;
SYNAESCOPE_DOLOOP ();
}
#endif
#if 0
static void
run_synaescope (void *data)
{
switch (depth) {
case 8:
synaescope8 (win);
break;
case 16:
synaescope16 (win);
break;
case 24:
case 32:
synaescope32 (win);
break;
}
}
static void
start_synaescope (void *data)
{
init_synaescope_window ();
}
#endif
static int
bitReverser (int i)
{
int sum = 0;
int j;
for (j = 0; j < FFT_BUFFER_SIZE_LOG; j++) {
sum = (i & 1) + sum * 2;
i >>= 1;
}
return sum;
}
static void
init_synaescope ()
{
int i;
for (i = 0; i <= FFT_BUFFER_SIZE / 2 + 1; i++) {
double mult = (double) 128 / ((FFT_BUFFER_SIZE * 16384) ^ 2);
/* Result now guaranteed (well, almost) to be in range 0..128 */
/* Low values represent more frequencies, and thus get more */
/* intensity - this helps correct for that. */
mult *= log (i + 1) / log (2);
mult *= 3; /* Adhoc parameter, looks about right for me. */
fftmult[i] = mult;
}
for (i = 0; i < FFT_BUFFER_SIZE; i++) {
negSinTable[i] = -sin (M_PI * 2 / FFT_BUFFER_SIZE * i);
cosTable[i] = cos (M_PI * 2 / FFT_BUFFER_SIZE * i);
bitReverse[i] = bitReverser (i);
}
for (i = 0; i < 256; i++)
scaleDown[i] = i * 200 >> 8;
memset (output, 0, syn_width * syn_height * 2);
}
static void
synaes_fft (double *x, double *y)
{
int n2 = FFT_BUFFER_SIZE;
int n1;
int twoToTheK;
int j;
for (twoToTheK = 1; twoToTheK < FFT_BUFFER_SIZE; twoToTheK *= 2) {
n1 = n2;
n2 /= 2;
for (j = 0; j < n2; j++) {
double c = cosTable[j * twoToTheK & (FFT_BUFFER_SIZE - 1)];
double s = negSinTable[j * twoToTheK & (FFT_BUFFER_SIZE - 1)];
int i;
for (i = j; i < FFT_BUFFER_SIZE; i += n1) {
int l = i + n2;
double xt = x[i] - x[l];
double yt = y[i] - y[l];
x[i] = (x[i] + x[l]);
y[i] = (y[i] + y[l]);
x[l] = xt * c - yt * s;
y[l] = xt * s + yt * c;
}
}
}
}
static void
synaescope_set_data (gint16 data[2][512])
{
int i;
gint16 *newset_l = pcmt_l;
gint16 *newset_r = pcmt_r;
for (i = 0; i < FFT_BUFFER_SIZE; i++) {
newset_l[i] = data[0][i];
newset_r[i] = data[1][i];
}
}
void
synaesthesia_init (guint32 resx, guint32 resy)
{
init_synaescope ();
}
guint32 *
synaesthesia_update (gint16 data[2][512])
{
synaescope_set_data (data);
synaescope32 ();
return display;
}
void
synaesthesia_close ()
{
}