#include #include #include #include #include #include #include "sound.h" #include "fft.moc" #include 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(); }