home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR41 / FREQ3.ZIP / FREQ.C < prev    next >
C/C++ Source or Header  |  1993-10-07  |  14KB  |  430 lines

  1. /*
  2.  *     Program: FREQ.C
  3.  *      Author: Philip VanBaren
  4.  *        Date: 15 August 1993
  5.  *
  6.  * Description: This program samples data from the ProAudio Spectrum
  7.  *              sound card, performs an FFT, and displays the result.
  8.  *              Can handle up to 2048 points (actually any size is possible
  9.  *              with a little fiddling of buffers to get around 64k limits).
  10.  *              On a 486/33 this code can perform and plot 1024-point and
  11.  *              below in nearly real-time at 44100kHz.  (1024 FFT=31ms)
  12.  *
  13.  *  The code requires the SDK for the ProAudio Spectrum (file SDK-V3.ZIP,
  14.  *  found via Internet at /ftp.uwp.edu:pub/msdos/proaudio), and is written for
  15.  *  a compact or large memory model.  (If you decrease the FFT size you may be
  16.  *  able to get away with a small memory model.)  The directories for the
  17.  *  PAS SDK include files should be in the include path, and you must link with
  18.  *  FFT.C, BVH?LIB.LIB, and BV?LIB.LIB (where ? is S for small, C for compact
  19.  *  or L for large memory models).  You must also link with the Borland C
  20.  *  graphics libraries.
  21.  *
  22.  *  The code was written for Borland C, but should work with Microsoft C if
  23.  *  the graphics initialization and drawing functions are modified, (and conio
  24.  *  functions like kbhit?) and the linked libraries are the MC versions.
  25.  */
  26.  
  27. #include <conio.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <math.h>
  31. #include <pcmio.h>                   // Standard PCM record functions
  32. #include <state.h>                   // Need this for Mixer code
  33. #include <mixers.h>                  // Mixer setting functions
  34. #include <binary.h>                  // Hardware values
  35. #include <graphics.h>
  36. #include <dos.h>
  37. #include "freq.h"
  38.  
  39. const int DMA=-1;                    // Use default DMA value
  40. const int IRQ=-1;                    // Use default IRQ value
  41. const int DMASize=2;                 // 2k DMA buffer
  42. const int DMADivisions=2;            // 1k divisions in DMA buffer
  43. const int Stereo=0;                  // Sorry, can't handle stereo (yet!)
  44. const int Compression=0;             // No squishing!
  45. const int SampleSize=16;             // Get signed integer values
  46.  
  47. /*
  48.  *  Table for approximating the logarithm.
  49.  */
  50. int ln[]={ -10000, -4096, -3072,-2473,-2048,-1718,-1449,-1221,-1024,-850,-694,-554,-425,-307,-197,-95,
  51.            0,90,174,254,330,402,470,536,599,659,717,773,827,879,929,977 };
  52.  
  53. int flag[BUFFERS];      // Array of flags indicating fullness of buffers
  54. int queue_buffer=0;     // Pointer to next buffer to be queued
  55. int record_buffer=0;    // Pointer to next buffer to be filled
  56. int process_buffer=0;   // Pointer to next buffer to be FFTed
  57.  
  58. int *buffer[BUFFERS];   // Buffers for gathering data
  59. int *fftdata;           // Array for FFT data
  60. int *wind;              // Array storing windowing function
  61.  
  62. int x[WINDOW_RIGHT-WINDOW_LEFT+1];     // Array of bin #'s displayed
  63. int lasty[WINDOW_RIGHT-WINDOW_LEFT+1]; // Last y position for FFT bins
  64. unsigned int yscale[WINDOW_RIGHT-WINDOW_LEFT+1]; // Scaling factors
  65. int ybase[WINDOW_RIGHT-WINDOW_LEFT+1];           // Scaling offset for log calculations
  66. int shift=0;            // Number of bits for gain shift
  67.  
  68. int *p1,*p2,*p3;        // Various indexing pointers
  69. unsigned int *p4;
  70.  
  71. long a2,root,mask;      // Variables for computing Sqrt/Log of Amplitude^2
  72.  
  73. extern int fftlen;       // Number of points for FFT
  74. extern long SampleRate;  // A/D sampling rate
  75. extern int logfreq;      // Flag set to 1 for log-based frequency scale
  76. extern int logamp;       // Flag set to 1 for log-based amplitude scale
  77. extern int windfunc;     // Flag set to selected window function
  78. extern int yb;           // Flag set for base of log scale (default=8 = -80db)
  79. extern int ys;           // Flag set for max of y-axis (default=10)
  80. extern int gain;         // Amount of db/octave gain
  81. extern int gain3db;      // Flag indicating a 3db/octave scale factor gain
  82. extern int deriv;        // Flag for doing differencing for 6db/octave gain
  83. extern long ref_freq;    // Reference frequency for n db/octave gains
  84.  
  85. extern struct rgb background,warn,graph,tick,label,border,text,darkhl,lighthl;
  86.  
  87. /*
  88.  *  Callback function.  This function is called every time a buffer has
  89.  *  been filled.  It sets a flag so the main loop recognises and processes
  90.  *  the buffer.
  91.  */
  92. void far callback1()
  93. {
  94.    flag[record_buffer]=1;
  95.    if(++record_buffer>=BUFFERS)
  96.       record_buffer=0;
  97. }
  98.  
  99. void main(int argc,char *argv[])
  100. {
  101.    int gdriver=VGA;        // Settings for initializing graphics
  102.    int gmode=VGAHI;        // in high-res (640x480x16) VGA mode
  103.    int i,y;
  104.  
  105.    int LeftChannel;        // Variables for saving/restoring mixer values
  106.    int RightChannel;
  107.  
  108.    /*
  109.     *  Parse the ini file and command line
  110.     */
  111.    parse_ini_file();
  112.    parse_command(argc,argv);
  113.  
  114.    /*
  115.     *  Initialize the buffer info
  116.     */
  117.    setup_buffers(fftlen);
  118.  
  119.    compute_window_function();
  120.  
  121.    /*
  122.     *  Initalize the graph scales
  123.     */
  124.    setup_xscale();
  125.  
  126.    if(logamp)
  127.       setup_logscales();
  128.    else
  129.       setup_linscales();
  130.  
  131.    /*
  132.     *  Set up the required arrays in the FFT code.
  133.     */
  134.    InitializeFFT(fftlen);
  135.  
  136.    /*
  137.     *  Initialize the graphics to 640x480 VGA mode
  138.     */
  139.    initgraph(&gdriver,&gmode,"c:\\borlandc\\bgi");
  140.  
  141.    setrgbpalette(0,background.red,background.green,background.blue);
  142.    setrgbpalette(LABEL_COLOR,label.red,label.green,label.blue);
  143.    setrgbpalette(BORDER_COLOR,border.red,border.green,border.blue);
  144.    setrgbpalette(TEXT_COLOR,text.red,text.green,text.blue);
  145.    setrgbpalette(GRAPH_COLOR,graph.red,graph.green,graph.blue);
  146.    setrgbpalette(DARK_HIGHLIGHT,darkhl.red,darkhl.green,darkhl.blue);
  147.    setrgbpalette(LIGHT_HIGHLIGHT,lighthl.red,lighthl.green,lighthl.blue);
  148.  
  149.    setlinestyle(SOLID_LINE,0,1);
  150.    setcolor(BORDER_COLOR);
  151.    rectangle(WINDOW_LEFT-2,WINDOW_TOP-2,WINDOW_RIGHT+2,WINDOW_BOTTOM+2);
  152.  
  153.    update_header();
  154.  
  155.    frequency_scale();
  156.    amplitude_scale();
  157.  
  158.    /*
  159.     *  Initialize link to Mixer control routines
  160.     *  Then save current mixer settings and turn off the PCM output
  161.     *  mixer to stop feedback and clicking noises.
  162.     */
  163.    MVInitMixerCode(0);
  164.  
  165.    LeftChannel=cMVGetMixerFunction(BI_OUTPUTMIXER,BI_L_PCM);
  166.    RightChannel=cMVGetMixerFunction(BI_OUTPUTMIXER,BI_R_PCM);
  167.  
  168.    cMVSetMixerFunction(0,BI_OUTPUTMIXER,BI_L_PCM);
  169.    cMVSetMixerFunction(0,BI_OUTPUTMIXER,BI_R_PCM);
  170.  
  171.    setup_vga();
  172.  
  173.    /*
  174.     *  Attempt to initialize the DMA buffering,
  175.     *  then set the sample rate/size
  176.     */
  177.    if(OpenPCMBuffering(DMA,IRQ,DMASize,DMADivisions)!=0)
  178.       puts("Error trying to open PCM buffering.");
  179.    else if(PCMState(SampleRate,Stereo,Compression,SampleSize)!=0)
  180.       puts("Error setting sample rate.");
  181.    else
  182.    {
  183.       /*
  184.        *  Queue up all but the last two buffers.  The second to last is queued
  185.        *  up below, with the RecordThisBlock call.  The last buffer is queued
  186.        *  up in the main loop, just prior to the wait for the current block
  187.        *  to finish.
  188.        */
  189.       for(i=0;i<BUFFERS-2;i++)
  190.       {
  191.          if(QueueThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)==0)
  192.          {
  193.             ClosePCMBuffering();
  194.             closegraph();
  195.             puts("Error queueing block.");
  196.             exit(1);
  197.          }
  198.          if(++queue_buffer>=BUFFERS)
  199.             queue_buffer=0;
  200.       }
  201.  
  202.       /*
  203.        *  This function starts the DMA process.
  204.        *  Note that the length specified is in BYTES, therefore we must
  205.        *  multiply the number of points by 2 since we are capturing integers.
  206.        */
  207.       if(RecordThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)==0)
  208.       {
  209.          ClosePCMBuffering();
  210.          closegraph();
  211.          puts("Error recording block.");
  212.          exit(1);
  213.       }
  214.       if(++queue_buffer>=BUFFERS)
  215.          queue_buffer=0;
  216.  
  217.       /*
  218.        *  Keep getting data and plotting it.
  219.        *  A space will pause, a second space will continue.
  220.        *  Any other key will quit.
  221.        */
  222.       while(!kbhit() || process_input() )
  223.       {
  224.          /*
  225.           *  Wait for current buffer to fill up
  226.           */
  227.          while(!flag[process_buffer] && !kbhit());
  228.  
  229.          if(!kbhit())
  230.          {
  231.             int clip=0;
  232.             /*
  233.              *  Perform windowing on the data
  234.              */
  235.             p1=fftdata;
  236.             p2=buffer[process_buffer];
  237.             p3=wind;
  238.  
  239.             if(deriv==0)
  240.             {
  241.                for(i=0;i<fftlen;i++)
  242.                {
  243.                   if((*p2==32767) || (*p2==-32768))
  244.                      clip=1;
  245.                   *p1=((long)(*p2) * (long)(*p3)) >> 15;
  246.                   p1++;
  247.                   p2++;
  248.                   p3++;
  249.                }
  250.             }
  251.             else if(deriv==1)
  252.             {
  253.                long last=*p2;
  254.  
  255.                for(i=0;i<fftlen;i++)
  256.                {
  257.                   if((*p2==32767) || (*p2==-32768))
  258.                      clip=1;
  259.                   *p1=(((long)(*p2)-last) * (long)(*p3)) >> 15;
  260.                   last=*p2;
  261.                   p1++;
  262.                   p2++;
  263.                   p3++;
  264.                }
  265.             }
  266.             else   /* deriv==2 */
  267.             {
  268.                long back2=*p2;
  269.                long back1=*p2;
  270.  
  271.                for(i=0;i<fftlen;i++)
  272.                {
  273.                   if((*p2==32767) || (*p2==-32768))
  274.                      clip=1;
  275.                   *p1=(((long)(*p2)-2*back1+back2) * (long)(*p3)) >> 15;
  276.                   back2=back1;
  277.                   back1=*p2;
  278.                   p1++;
  279.                   p2++;
  280.                   p3++;
  281.                }
  282.             }
  283.  
  284.             if(clip)
  285.                setrgbpalette(0,warn.red,warn.green,warn.blue);
  286.             else
  287.                setrgbpalette(0,background.red,background.green,background.blue);
  288.  
  289.             /*
  290.              *  Free up the buffer we just processed.
  291.              */
  292.             flag[process_buffer]=0;
  293.             if(++process_buffer>=BUFFERS)
  294.                process_buffer=0;
  295.  
  296.             /*
  297.              *  Now that we have processed the buffer, queue it up again.
  298.              *  Note that the length specified is in BYTES, therefore we
  299.              *  must multiply the number of points by 2 since we are
  300.              *  capturing integers.
  301.              */
  302.             if(QueueThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)!=0)
  303.             {
  304.                ClosePCMBuffering();
  305.                closegraph();
  306.                puts("Error recording block.");
  307.                exit(1);
  308.             }
  309.             if(++queue_buffer>=BUFFERS)
  310.                queue_buffer=0;
  311.  
  312.             /*
  313.              *  The real meat of the code lies elsewhere!
  314.              */
  315.             realfft(fftdata);
  316.  
  317.             /*
  318.              *  Use pointers for indexing to speed things up a bit.
  319.              */
  320.             p1=lasty;
  321.             p2=x;
  322.             p3=ybase;
  323.             p4=yscale;
  324.  
  325.             for(i=WINDOW_LEFT;i<WINDOW_RIGHT+1;i++)
  326.             {
  327.                /*
  328.                 *  If this line is the same as the previous one,
  329.                 *  just use the previous y value.
  330.                 *  Else go ahead and compute the value.
  331.                 */
  332.                if(*p2!=-1)
  333.                {
  334.                   register int bri=BitReversed[*p2];
  335.                   register long re=fftdata[bri];
  336.                   register long im=fftdata[bri+1];
  337.  
  338.                   /*
  339.                    *  Compute the squared amplitude
  340.                    */
  341.                   a2=re*re+im*im;
  342.  
  343.                   if(logamp)
  344.                   {
  345.                      /*
  346.                       *  Logarithm code
  347.                       */
  348.                      root=4096;
  349.  
  350.                      while(a2>=32)
  351.                      {
  352.                         root+=1024;
  353.                         a2>>=1;
  354.                      }
  355.  
  356.                      if((root+=ln[a2]-*p3)<0)
  357.                         root=0;
  358.                   }
  359.                   else
  360.                   {
  361.                      /*
  362.                       * Square root code:
  363.                       */
  364.                      root=32;
  365.                      do
  366.                      {
  367.                         mask=a2/root;
  368.                         root=(root+mask)>>1;
  369.                      } while(abs(root-mask)>1);
  370.                   }
  371.  
  372.                   y=(WINDOW_BOTTOM) - ((unsigned long)((unsigned long)root * (unsigned long)*p4) >> shift) ;
  373.                   if(y<WINDOW_TOP) y=WINDOW_TOP;
  374.                }
  375.  
  376.                if(y>*p1)
  377.                {
  378.                   /*
  379.                    *  Draw a black line
  380.                    */
  381.                   unsigned char bit=~(0x80 >> (i&0x07));
  382.                   unsigned int endbase=y*80;
  383.                   unsigned int base=*p1*80+(i>>3);
  384.  
  385.                   while(base<endbase)
  386.                   {
  387.                      screen(base)&=bit;
  388.                      base+=80;
  389.                   }
  390.                }
  391.                else
  392.                {
  393.                   /*
  394.                    *  Draw a blue line.
  395.                    */
  396.                   unsigned char bit=0x80 >> (i&0x07);
  397.                   unsigned int endbase=(*p1+1)*80;
  398.                   unsigned base=y*80+(i>>3);
  399.  
  400.                   while(base<endbase)
  401.                   {
  402.                      screen(base)|=bit;
  403.                      base+=80;
  404.                   }
  405.                }
  406.                *p1=y;
  407.                p1++;
  408.                p2++;
  409.                p3++;
  410.                p4++;
  411.             }
  412.          }
  413.       }
  414.    }
  415.    /*
  416.     *  Shut down the DMA system.
  417.     */
  418.    ClosePCMBuffering();
  419.  
  420.    /*
  421.     *  Restore mixers to their previous value.
  422.     */
  423.    cMVSetMixerFunction(LeftChannel,BI_OUTPUTMIXER,BI_L_PCM);
  424.    cMVSetMixerFunction(RightChannel,BI_OUTPUTMIXER,BI_R_PCM);
  425.  
  426.    closegraph();
  427. }
  428.  
  429.  
  430.