diff options
Diffstat (limited to 'fft.cpp')
-rw-r--r-- | fft.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
@@ -0,0 +1,218 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <math.h> + +#include <qwidget.h> + +#include "sound.h" +#include "fft.moc" + +#include <X11/Xlib.h> + +extern "C" { + #include "soundfft.h" +} + +#define FFT_MAX 8192 +#define FFT_LEFT 100 +#define FFT_RIGHT 10000 +#define DB_MAX 0.0 +#define DB_MIN -100.0 + +/* ---------------------------------------------------------------------- */ + +FFTWindow::FFTWindow(QWidget *parent, char *name) : QWidget(parent,name,0) +{ + XColor color,dummy; + Colormap map = DefaultColormapOfScreen + (DefaultScreenOfDisplay(x11Display())); + + gc = XCreateGC(x11Display(),winId(),0,NULL); + XAllocNamedColor(x11Display(),map,"white",&color,&dummy); + back = color.pixel; + XAllocNamedColor(x11Display(),map,"red",&color,&dummy); + fore = color.pixel; + + /* flag: no init so far... */ + channels = 0; + logmap = NULL; + ylog = false; +} + +void +FFTWindow::make_logmap() +{ + int i,w,h; + float freq,up; + + w = width(); + h = height(); + + if (!w || !h || !channels) + return; + + if (logmap) { + free(logmap); + free(segments); + free(buffer); + EndFFT(); + } + logmap = (int*) malloc((w+1)*sizeof(int)); + segments = (XSegment*)malloc((w)*sizeof(XSegment)); + buffer = (short*)malloc(fft_size*sizeof(short)*channels); + InitializeFFT(fft_size); + + up = log(FFT_RIGHT/FFT_LEFT)/log(2); + for (i = 0; i <= w; i++) { + freq = pow(2,up*i/w)* FFT_LEFT; + logmap[i] = (int)(fft_size*freq/rate); + } + for (i = 0; i < w; i++) { + segments[i].x1 = i; + segments[i].x2 = i; + segments[i].y2 = h; + } +} + +void +FFTWindow::calculate(unsigned char *data) +{ + int i,j; + + if (NULL == logmap) { + fprintf(stderr,"Oops: fft no logmap (yet)\n"); + return; + } + + switch (afmt) { + case FMT_8BIT: + for (i = 0; i < audio_size; i++) + buffer[i] = (data[i] ^ 0x80) << 8; + break; +#if 0 + case AFMT_S8: + for (i = 0; i < audio_size; i++) + buffer[i] = data[i] << 8; + break; +#endif + case FMT_16BIT: + memcpy(buffer,data,audio_size); +#if 0 + /* byte swap */ + for (i = 0; i < fft_size; i++) + buffer[i] = + ((buffer[i] & 0xff00) >> 8) | + ((buffer[i] & 0x00ff) << 8); + /* unsigned -> signed */ + if (afmt == AFMT_U16_LE || afmt == AFMT_U16_BE) + for (i = 0; i < fft_size; i++) + buffer[i] ^= 0x8000; +#endif + break; + default: + fprintf(stderr,"oops(fft): unknown sound format\n"); + exit(1); + } + + for (i = 0, lmax = 0; i < fft_size; i++) + if ((j = abs((signed short)buffer[i])) > lmax) + lmax = j; + + if (channels == 2) { + for (i = 0, j = 0; i < fft_size; i++, j+=2) + buffer[i] = (buffer[j]+buffer[j+1])>>1; + } + RealFFT(buffer); +} + +void +FFTWindow::drawhist() +{ + XGCValues gcval; + + int re,im,ab; + int i,j,max,w,h; + int len = fft_size>>1; + + w = width(); + h = height(); + for (i = 0; i < w; i++) { + max = 0; + j = logmap[i]; + do { + if (j >= len) + break; + re = buffer[BitReversed[j]]; + im = buffer[BitReversed[j]+1]; + ab = re*re+im*im; + if (ab > max) + max = ab; + + j++; + } while (j < logmap[i+1]); + if (ylog) { + float dBVal = 20 * log10( sqrt(max)/FFT_MAX ); + max = (int)( h * ( dBVal-DB_MIN )/( DB_MAX-DB_MIN )); + } else { + max = (int)(sqrt(max) * h / FFT_MAX); + } + segments[i].y1 = h-max; + } + + gcval.foreground = back; + XChangeGC(x11Display(),gc,GCForeground,&gcval); + XFillRectangle(x11Display(),winId(),gc,0,0,width(),height()); + + gcval.foreground = fore; + XChangeGC(x11Display(),gc,GCForeground,&gcval); + XDrawSegments(x11Display(),winId(),gc,segments,w); + if (ylog) { + float dBVal = 20 * log10( (double)lmax/32768 ); + max = h - (int)( h * ( dBVal-DB_MIN )/( DB_MAX-DB_MIN )); + } else { + max = height() - lmax*height()/32768; + } + XDrawLine(x11Display(),winId(),gc,0,max,width(),max); +} + +/* ---------------------------------------------------------------------- */ + +void +FFTWindow::resizeEvent(QResizeEvent *event) +{ + make_logmap(); +} + +void +FFTWindow::new_params(struct SOUNDPARAMS *p) +{ + afmt = p->format; + channels = p->channels; + rate = p->rate; + audio_size = p->blocksize; + + fft_size = audio_size/channels/2; + if (afmt != FMT_16BIT) + fft_size *= 2; + + make_logmap(); +} + +void +FFTWindow::new_data(void *data) +{ + if (!channels || !isVisible()) + return; + + calculate((unsigned char*)data); + drawhist(); +} + +void +FFTWindow::set_ylog(int linear) +{ + ylog = (linear==0); + drawhist(); +} |