aboutsummaryrefslogtreecommitdiffstats
path: root/fft.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fft.cpp')
-rw-r--r--fft.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/fft.cpp b/fft.cpp
new file mode 100644
index 0000000..525a584
--- /dev/null
+++ b/fft.cpp
@@ -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();
+}