19 Commits 5e509d42d4 ... a281de9bcc

Author SHA1 Message Date
  Emilia Blåsten a281de9bcc Give a short description of the centroid in the readme 5 years ago
  Emilia Blåsten ba645b6ee1 Add command-line parameter for toggling drawing the centroid line 5 years ago
  Emilia Blåsten 8305e124a0 Add description for command line option -f and -v 5 years ago
  Emilia Blåsten ceb5221d73 Zero-initialize centroids 5 years ago
  Emilia Blåsten ac76e4609a Draw lines from centroid to centroid 5 years ago
  Emilia Blåsten 4708eb1b78 Fix possible missing index in loop 5 years ago
  Emilia Blåsten f8b3101404 Fix frequency lines being drawn also in bottom left box 5 years ago
  Emilia Blåsten c1d44427d9 Draw frequency lines from x0 to x1, simpler 5 years ago
  Emilia Blåsten de6faa0592 Calculate and display centroid as pixels 5 years ago
  Emilia Blåsten 402abcae7b Fix program name in various locations 5 years ago
  Emilia Blåsten e159641e89 Plot line at a given frequency 5 years ago
  Emilia Blåsten 29183dcdee Test coloring a certain pixel a certain color in the spectrogram 5 years ago
  Emilia Blåsten f2e9bb8a03 Update readme about -sf option 5 years ago
  Emilia Blåsten 08bbcf0bda Mention in help that several -fl options can be used simultaneously 5 years ago
  Emilia Blåsten 0e0f805b14 Add copyright for 2019 5 years ago
  Emilia Blåsten 1cf832bc89 Removed default female range lines 5 years ago
  Emilia Blåsten 4946abc1ed Allow drawing multiple frequency lines from command line input 5 years ago
  Emilia Blåsten 6a22afc762 Add option for drawing a line at a given frequency 5 years ago
  Emilia Blåsten 38d78f448c Allow building a debug version in Makefile 5 years ago
3 changed files with 91 additions and 18 deletions
  1. 4 0
      Makefile
  2. 10 2
      README.md
  3. 77 16
      sspect.cpp

+ 4 - 0
Makefile

@@ -2,12 +2,16 @@ LD_FLAGS =
 LD_LIBS = -lasound -lfftw3f -lm -pthread  # note single-prec FFTW library
 CXX = g++
 CXX_FLAGS = -O2
+DEBUG_FLAGS = -g
 
 all:	sspect
 
 sspect: sspect.cpp Makefile
 	${CXX} ${CXX_FLAGS} sspect.cpp -lglut -lGL ${LD_LIBS} -o sspect
 
+debug: sspect.cpp Makefile
+	${CXX} ${CXX_FLAGS} ${DEBUG_FLAGS} sspect.cpp -lglut -lGL ${LD_LIBS} -o sspect
+
 clean:
 	rm -f core *.o sspect
 

+ 10 - 2
README.md

@@ -15,7 +15,11 @@ with time on the horizontal axis, frequency on the vertical axis, and
 the color intensity shows the amplitude (i.e. energy) of that
 particular frequency at that particular time. Higher pitched sounds
 will produce curves higher up in the display, and lower pitched ones
-will show lower. In addition there are two smaller graphs on the
+will show lower. Optionally the spectrogram will draw the centroid,
+i.e. a line which shows at which frequency the average energy is
+distributed in the current moment. In other words the brighter the
+sound (i.e. the more energy in the high frequencies) the higher the
+centroid would be. In addition there are two smaller graphs on the
 bottom of the screen. The left-side one shows the real-time spectrum
 of the sound, in other words a vertical slice of the main plot if you
 will. The right-side graph shows the instantanous signal, i.e. the
@@ -29,9 +33,11 @@ The spectrogram is started from the command line and the various
 arguments are as follows:
 
 ```text
-sspect  [-f] [-v] [-d <device_number>] [-sf <scroll_factor>] [-w <windowtype>] [-t twowinsize]
+sspect  [-f] [-v] [-d <device_number>] [-sf <scroll_factor>] [-w <windowtype>] [-t twowinsize]  [-fl freqline] [-c]
 
 Command line arguments:
+-f fullscreen
+-v verbose mode
 device_number = 0,1,... the ALSA input device number (default 0)
 windowtype =    0 (no window) (will be crappy)
                 1 (Hann)
@@ -40,6 +46,8 @@ scroll_factor = 1,2,...   How many vSyncs (@ 60Hz) to wait per scroll pixel
                   (default 1)
 twowinsize = 11,12,...,16  is the power of 2 giving FFT win_size N (default 13)
         (Note: this controls the vertical frequency resolution and range)
+freqline   draws at line at frequency freqline. Several -fl options can be used to draw several lines
+-c draws the centroid line
 
 Keys & mouse:   arrows or middle button drag - brightness/contrast
        		i - step through colormaps (B/W, inverse B/W, color)

+ 77 - 16
sspect.cpp

@@ -1,6 +1,6 @@
 /* 
    sspect - A simple spectrogram
-   Copyright (C) 2018  Emily, emily@countermail.com
+   Copyright (C) 2018, 2019  Emily, emily@countermail.com
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
@@ -48,6 +48,10 @@ struct Param {           // parameters object
   int devicenumber;      // ALSA device number
   int windowtype;        // DFT windowing function type
   int twowinsize;        // power of 2 giving DFT win_size (N) in samples
+  float freqline[100];   // at which frequencies to draw a line
+  bool drawline = false; // has freqline been used?
+  int nfreqlines = 0;    // number of actual frequencies added to freqline
+  bool drawcentroid = false; // has freqline been used?
 };                       // note trailing ;
 Param param;           // global parameters object
 
@@ -81,7 +85,7 @@ void smallText(float x, float y, char *string) {
 class audioInput {
   public:
   char *chunk;
-  float *b, *specslice, *bwin, *winf, *sg;
+  float *b, *specslice, *bwin, *winf, *sg, *centroids;
   char *sgb;
   int b_ind, b_size, n_f, n_tw, sg_size, win_size;
   float dt, t_memory, Hz_per_pixel;
@@ -162,6 +166,9 @@ class audioInput {
     n_f = 560;  // # freqs   ...spectrogram stuff
     specslice = new float[n_f];
     n_tw = 940; // # time windows: should be multiple of 4 for glDrawPixels
+    centroids = new float[n_tw]; // array for storing centroids
+    for (int i=0; i<n_tw; ++i)
+      centroids[i]=0;
     sg_size = n_f * n_tw;
     sg = new float[sg_size];  // spectrogram array float
     sgb = new char[sg_size];  // spectrogram array 8-bit
@@ -463,9 +470,6 @@ class scene
     for (i=0;i<n_tics;++i) {
       glVertex2d(ytic_bot, tics[i]); glVertex2d(ytic_top, tics[i]);
     }
-    glVertex2d(ytic_top,175); glVertex2d(x1,175); // lower female range, F3
-    glVertex2d(ytic_top,220); glVertex2d(x1,220); // average female range, A3
-    glVertex2d(ytic_top,262); glVertex2d(x1,262); // middle C, C4
     glEnd();
     for (i=0;i<n_tics;++i) {
       sprintf(label,"%.6g",tics[i]);
@@ -474,6 +478,32 @@ class scene
     }
   }
 
+  void drawLines(float x0, float x1)  // draws lines in [x0,x1]x{param.freqlines}
+  {
+    glColor4f(0.7, 1.0, 1.0, 1);
+    glDisable(GL_LINE_SMOOTH); glLineWidth(1);
+    glBegin(GL_LINES);
+    if(param.drawline) {
+      for (int n=0; n<param.nfreqlines; ++n) {
+	glVertex2d(x0,param.freqline[n]); glVertex2d(x1,param.freqline[n]); // draw line at frequency freqline[n]
+      }
+    }
+    glEnd();
+  }
+
+  void drawCentroids(float x0, float x1)  // draws lines in [x0,x1]x{param.freqlines}
+  {
+    glColor4f(0.7, 1.0, 1.0, 1);
+    glDisable(GL_LINE_SMOOTH); glLineWidth(1);
+    glBegin(GL_LINES);
+    float dt = (x1-x0)/ai->n_tw;
+    for (int i=0; i < ai->n_tw; ++i) {
+      glVertex2d(x0+i*dt, ai->centroids[i]);
+      glVertex2d(x0+(i+1)*dt, ai->centroids[i+1]);
+    }
+    glEnd();
+  }
+
   void spectrogram() // show spectrogram as 2D pixel array, w/ axes............
   {
     float x0=0.05, y0=0.22;    // bot-left location in viewport (as unit square)
@@ -512,6 +542,9 @@ class scene
     // plots now occur in physical t (s) and f (Hz) units, relative to start_t..
     char xlab[] = "t(s)", ylab[] = "f(Hz)";
     drawAxes(run_time-max_t, run_time, 0 - 1e-7, max_Hz, 2.0, 2.0, 2, 0, xlab, ylab);
+    drawLines(run_time-max_t, run_time);
+    if(param.drawcentroid)
+      drawCentroids(run_time-max_t, run_time);
     // 1e-7 is hack to show 0 Hz
     if (freqind) {        // horiz freq readout line, in Hz/sec coords, 1/17/16
       // reverse-engineer the freq from current mouse y in pixels (yuk)...
@@ -672,22 +705,39 @@ void computespecslice(audioInput *ai)   // windowed spectral slice!
     }
   }
 
-void scroll_sg(audioInput *ai, float *newcol)
+void scroll_sg(audioInput *ai, float *newcol, float *centroids)
 // Scroll spectrogram data 1 pixel in t direction, add new col & make 8bit
 {
   int i, j, n = ai->n_tw;
 
-  for ( i=0; i<n-1; ++i)    // float: NB x (ie t) is the fast storage direc
+  for ( i=0; i<n; ++i)    // float: NB x (ie t) is the fast storage direc
     for ( j=0; j<ai->n_f; ++j)
       ai->sg[j*n + i] = ai->sg[j*n + i + 1];
 
   for ( i=0; i<n; ++i)    // scroll the 8bit char data too
     for ( j=0; j<ai->n_f; ++j)
       ai->sgb[j*n + i] = ai->sgb[j*n + i + 1];
-  
-  for ( j=0; j<ai->n_f; ++j)   // set the last col of float data
+
+
+  for ( i=0; i<n; ++i)
+    centroids[i] = centroids[i+1];
+
+  // Calculate the new centroid
+  float num = 0;
+  float denom = 0;
+  float curr_freq;
+  for (j=0; j<ai->n_f; ++j) {
+    curr_freq = ai->Hz_per_pixel * j; // frequency whose amplitude is newcol[j]
+    denom += newcol[j];
+    num += newcol[j] * curr_freq;
+  }
+  float centroid = num / denom;
+  centroids[n] = centroid;
+
+  for ( j=0; j<ai->n_f; ++j) {   // set the last col of float data
     ai->sg[j*n + n - 1] = newcol[j];
-  
+  }
+
   for ( j=0; j<ai->n_f; ++j) {        // color xform for last col of 8bit data
     ai->sgb[j*n + n - 1] = scn.color_byte(newcol[j]);
   }
@@ -713,7 +763,7 @@ void idle(void)   //    put numerical intensive part here? only scrolling
 
     ++scn.scroll_count;
     if (scn.scroll_count==scn.scroll_fac) {
-      scroll_sg(scn.ai, scn.ai->specslice);   // add spec slice to sg & scroll
+      scroll_sg(scn.ai, scn.ai->specslice, scn.ai->centroids);   // add spec slice to sg & scroll
       scn.scroll_count = 0;
     }
   }
@@ -792,14 +842,18 @@ void motion(int x, int y) // handles mouse drag effects
 }
 
 const char *helptext[] = {
-  " glSpect: real-time OpenGL spectrogram.  Alex Barnett, Dec 2010\n",
-  "                                         (based on Luke Campagnola glScope)\n\n",
-  "Usage: glspect  [-f] [-v] [-d <device_number>] [-sf <scroll_factor>] [-w <windowtype>] [-t twowinsize]\n\n",
+  " Simple Spectrogram: real-time OpenGL spectrogram.\n",
+  "                                         (based on Alex Barnett glSpect, Dec 2010 and Luke Campagnola glScope)\n\n",
+  "Usage: sspect  [-f] [-v] [-d <device_number>] [-sf <scroll_factor>] [-w <windowtype>] [-t twowinsize] [-fl freqline] [-c]\n\n",
   "Command line arguments:\n",
+  "-f fullscreen\n",
+  "-v verbose mode\n",
   "device_number = 0,1,... the ALSA input device number (default 0)\n",
   "windowtype = \t0 (no window)\n\t\t1 (Hann)\n\t\t2 (Gaussian trunc at +-4sigma) (default)\n",
   "scroll_factor = 1,2,... # vSyncs (60Hz) to wait per scroll pixel (default 1)\n",
-  "twowinsize = 11,12,...,16 is power of 2 giving FFT win_size N (default 12)\n\t(Note: this controls the vertical frequency resolution and range)\n\n", 
+  "twowinsize = 11,12,...,16 is power of 2 giving FFT win_size N (default 12)\n\t(Note: this controls the vertical frequency resolution and range)\n",
+  "freqline   draws at line at frequency freqline. Several -fl options can be used to draw several lines\n",
+  "-c draws the centroid line\n\n",
   "Keys & mouse: \tarrows or middle button drag - brightness/contrast\n",
   "\t\tleft button shows horizontal frequency readoff line\n",
   "\t\tright button shows horizontal frequency readoff with multiples\n",
@@ -817,6 +871,7 @@ int main(int argc, char** argv)
   param.devicenumber = 0; // ALSA input device number
   param.windowtype = 2;  // Gaussian
   param.twowinsize = 12; // 4096 samples
+  param.drawcentroid = false;
 
   for (int i=1; i<argc; ++i) {  // .....Parse cmd line options....
     if (!strcmp(argv[i], "-f"))  // option -f makes full screen
@@ -836,6 +891,12 @@ int main(int argc, char** argv)
     } else if (!strcmp(argv[i], "-d")) {
       sscanf(argv[++i], "%d", &param.devicenumber);  // read in devicenumber
       if (param.devicenumber<0) param.devicenumber=0; // sanitize it
+    } else if (!strcmp(argv[i], "-fl")) {
+      sscanf(argv[++i], "%f", &param.freqline[param.nfreqlines]);  // read in at which frequency to draw a line
+      param.drawline = true;
+      param.nfreqlines++;
+    } else if (!strcmp(argv[i], "-c")) {
+      param.drawcentroid = true;
     } else {            // misuse (or -h): print out usage text...
       fprintf(stderr, "bad command line option %s\n\n", argv[i]);
       for (int i=0; helptext[i]; i++) fprintf(stderr, "%s", helptext[i]);
@@ -852,7 +913,7 @@ int main(int argc, char** argv)
     glutEnterGameMode();                     // start fullscreen game mode
   } else {
     glutInitWindowSize(1024, 768);  // window same size as XGA
-    int mainWindow = glutCreateWindow("glSpect by Alex Barnett, Dec 2010");
+    int mainWindow = glutCreateWindow("sspect");
     //  glutFullScreen();    // maximizes window, but is not game mode
   }