home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio 4.94 - Over 11,000 Files
/
audio-11000.iso
/
msdos
/
sndbords
/
proaudio
/
freq3
/
freq.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-07
|
14KB
|
430 lines
/*
* Program: FREQ.C
* Author: Philip VanBaren
* Date: 15 August 1993
*
* Description: This program samples data from the ProAudio Spectrum
* sound card, performs an FFT, and displays the result.
* Can handle up to 2048 points (actually any size is possible
* with a little fiddling of buffers to get around 64k limits).
* On a 486/33 this code can perform and plot 1024-point and
* below in nearly real-time at 44100kHz. (1024 FFT=31ms)
*
* The code requires the SDK for the ProAudio Spectrum (file SDK-V3.ZIP,
* found via Internet at /ftp.uwp.edu:pub/msdos/proaudio), and is written for
* a compact or large memory model. (If you decrease the FFT size you may be
* able to get away with a small memory model.) The directories for the
* PAS SDK include files should be in the include path, and you must link with
* FFT.C, BVH?LIB.LIB, and BV?LIB.LIB (where ? is S for small, C for compact
* or L for large memory models). You must also link with the Borland C
* graphics libraries.
*
* The code was written for Borland C, but should work with Microsoft C if
* the graphics initialization and drawing functions are modified, (and conio
* functions like kbhit?) and the linked libraries are the MC versions.
*/
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pcmio.h> // Standard PCM record functions
#include <state.h> // Need this for Mixer code
#include <mixers.h> // Mixer setting functions
#include <binary.h> // Hardware values
#include <graphics.h>
#include <dos.h>
#include "freq.h"
const int DMA=-1; // Use default DMA value
const int IRQ=-1; // Use default IRQ value
const int DMASize=2; // 2k DMA buffer
const int DMADivisions=2; // 1k divisions in DMA buffer
const int Stereo=0; // Sorry, can't handle stereo (yet!)
const int Compression=0; // No squishing!
const int SampleSize=16; // Get signed integer values
/*
* Table for approximating the logarithm.
*/
int ln[]={ -10000, -4096, -3072,-2473,-2048,-1718,-1449,-1221,-1024,-850,-694,-554,-425,-307,-197,-95,
0,90,174,254,330,402,470,536,599,659,717,773,827,879,929,977 };
int flag[BUFFERS]; // Array of flags indicating fullness of buffers
int queue_buffer=0; // Pointer to next buffer to be queued
int record_buffer=0; // Pointer to next buffer to be filled
int process_buffer=0; // Pointer to next buffer to be FFTed
int *buffer[BUFFERS]; // Buffers for gathering data
int *fftdata; // Array for FFT data
int *wind; // Array storing windowing function
int x[WINDOW_RIGHT-WINDOW_LEFT+1]; // Array of bin #'s displayed
int lasty[WINDOW_RIGHT-WINDOW_LEFT+1]; // Last y position for FFT bins
unsigned int yscale[WINDOW_RIGHT-WINDOW_LEFT+1]; // Scaling factors
int ybase[WINDOW_RIGHT-WINDOW_LEFT+1]; // Scaling offset for log calculations
int shift=0; // Number of bits for gain shift
int *p1,*p2,*p3; // Various indexing pointers
unsigned int *p4;
long a2,root,mask; // Variables for computing Sqrt/Log of Amplitude^2
extern int fftlen; // Number of points for FFT
extern long SampleRate; // A/D sampling rate
extern int logfreq; // Flag set to 1 for log-based frequency scale
extern int logamp; // Flag set to 1 for log-based amplitude scale
extern int windfunc; // Flag set to selected window function
extern int yb; // Flag set for base of log scale (default=8 = -80db)
extern int ys; // Flag set for max of y-axis (default=10)
extern int gain; // Amount of db/octave gain
extern int gain3db; // Flag indicating a 3db/octave scale factor gain
extern int deriv; // Flag for doing differencing for 6db/octave gain
extern long ref_freq; // Reference frequency for n db/octave gains
extern struct rgb background,warn,graph,tick,label,border,text,darkhl,lighthl;
/*
* Callback function. This function is called every time a buffer has
* been filled. It sets a flag so the main loop recognises and processes
* the buffer.
*/
void far callback1()
{
flag[record_buffer]=1;
if(++record_buffer>=BUFFERS)
record_buffer=0;
}
void main(int argc,char *argv[])
{
int gdriver=VGA; // Settings for initializing graphics
int gmode=VGAHI; // in high-res (640x480x16) VGA mode
int i,y;
int LeftChannel; // Variables for saving/restoring mixer values
int RightChannel;
/*
* Parse the ini file and command line
*/
parse_ini_file();
parse_command(argc,argv);
/*
* Initialize the buffer info
*/
setup_buffers(fftlen);
compute_window_function();
/*
* Initalize the graph scales
*/
setup_xscale();
if(logamp)
setup_logscales();
else
setup_linscales();
/*
* Set up the required arrays in the FFT code.
*/
InitializeFFT(fftlen);
/*
* Initialize the graphics to 640x480 VGA mode
*/
initgraph(&gdriver,&gmode,"c:\\borlandc\\bgi");
setrgbpalette(0,background.red,background.green,background.blue);
setrgbpalette(LABEL_COLOR,label.red,label.green,label.blue);
setrgbpalette(BORDER_COLOR,border.red,border.green,border.blue);
setrgbpalette(TEXT_COLOR,text.red,text.green,text.blue);
setrgbpalette(GRAPH_COLOR,graph.red,graph.green,graph.blue);
setrgbpalette(DARK_HIGHLIGHT,darkhl.red,darkhl.green,darkhl.blue);
setrgbpalette(LIGHT_HIGHLIGHT,lighthl.red,lighthl.green,lighthl.blue);
setlinestyle(SOLID_LINE,0,1);
setcolor(BORDER_COLOR);
rectangle(WINDOW_LEFT-2,WINDOW_TOP-2,WINDOW_RIGHT+2,WINDOW_BOTTOM+2);
update_header();
frequency_scale();
amplitude_scale();
/*
* Initialize link to Mixer control routines
* Then save current mixer settings and turn off the PCM output
* mixer to stop feedback and clicking noises.
*/
MVInitMixerCode(0);
LeftChannel=cMVGetMixerFunction(BI_OUTPUTMIXER,BI_L_PCM);
RightChannel=cMVGetMixerFunction(BI_OUTPUTMIXER,BI_R_PCM);
cMVSetMixerFunction(0,BI_OUTPUTMIXER,BI_L_PCM);
cMVSetMixerFunction(0,BI_OUTPUTMIXER,BI_R_PCM);
setup_vga();
/*
* Attempt to initialize the DMA buffering,
* then set the sample rate/size
*/
if(OpenPCMBuffering(DMA,IRQ,DMASize,DMADivisions)!=0)
puts("Error trying to open PCM buffering.");
else if(PCMState(SampleRate,Stereo,Compression,SampleSize)!=0)
puts("Error setting sample rate.");
else
{
/*
* Queue up all but the last two buffers. The second to last is queued
* up below, with the RecordThisBlock call. The last buffer is queued
* up in the main loop, just prior to the wait for the current block
* to finish.
*/
for(i=0;i<BUFFERS-2;i++)
{
if(QueueThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)==0)
{
ClosePCMBuffering();
closegraph();
puts("Error queueing block.");
exit(1);
}
if(++queue_buffer>=BUFFERS)
queue_buffer=0;
}
/*
* This function starts the DMA process.
* Note that the length specified is in BYTES, therefore we must
* multiply the number of points by 2 since we are capturing integers.
*/
if(RecordThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)==0)
{
ClosePCMBuffering();
closegraph();
puts("Error recording block.");
exit(1);
}
if(++queue_buffer>=BUFFERS)
queue_buffer=0;
/*
* Keep getting data and plotting it.
* A space will pause, a second space will continue.
* Any other key will quit.
*/
while(!kbhit() || process_input() )
{
/*
* Wait for current buffer to fill up
*/
while(!flag[process_buffer] && !kbhit());
if(!kbhit())
{
int clip=0;
/*
* Perform windowing on the data
*/
p1=fftdata;
p2=buffer[process_buffer];
p3=wind;
if(deriv==0)
{
for(i=0;i<fftlen;i++)
{
if((*p2==32767) || (*p2==-32768))
clip=1;
*p1=((long)(*p2) * (long)(*p3)) >> 15;
p1++;
p2++;
p3++;
}
}
else if(deriv==1)
{
long last=*p2;
for(i=0;i<fftlen;i++)
{
if((*p2==32767) || (*p2==-32768))
clip=1;
*p1=(((long)(*p2)-last) * (long)(*p3)) >> 15;
last=*p2;
p1++;
p2++;
p3++;
}
}
else /* deriv==2 */
{
long back2=*p2;
long back1=*p2;
for(i=0;i<fftlen;i++)
{
if((*p2==32767) || (*p2==-32768))
clip=1;
*p1=(((long)(*p2)-2*back1+back2) * (long)(*p3)) >> 15;
back2=back1;
back1=*p2;
p1++;
p2++;
p3++;
}
}
if(clip)
setrgbpalette(0,warn.red,warn.green,warn.blue);
else
setrgbpalette(0,background.red,background.green,background.blue);
/*
* Free up the buffer we just processed.
*/
flag[process_buffer]=0;
if(++process_buffer>=BUFFERS)
process_buffer=0;
/*
* Now that we have processed the buffer, queue it up again.
* Note that the length specified is in BYTES, therefore we
* must multiply the number of points by 2 since we are
* capturing integers.
*/
if(QueueThisBlock((char *)buffer[queue_buffer],fftlen*2,callback1)!=0)
{
ClosePCMBuffering();
closegraph();
puts("Error recording block.");
exit(1);
}
if(++queue_buffer>=BUFFERS)
queue_buffer=0;
/*
* The real meat of the code lies elsewhere!
*/
realfft(fftdata);
/*
* Use pointers for indexing to speed things up a bit.
*/
p1=lasty;
p2=x;
p3=ybase;
p4=yscale;
for(i=WINDOW_LEFT;i<WINDOW_RIGHT+1;i++)
{
/*
* If this line is the same as the previous one,
* just use the previous y value.
* Else go ahead and compute the value.
*/
if(*p2!=-1)
{
register int bri=BitReversed[*p2];
register long re=fftdata[bri];
register long im=fftdata[bri+1];
/*
* Compute the squared amplitude
*/
a2=re*re+im*im;
if(logamp)
{
/*
* Logarithm code
*/
root=4096;
while(a2>=32)
{
root+=1024;
a2>>=1;
}
if((root+=ln[a2]-*p3)<0)
root=0;
}
else
{
/*
* Square root code:
*/
root=32;
do
{
mask=a2/root;
root=(root+mask)>>1;
} while(abs(root-mask)>1);
}
y=(WINDOW_BOTTOM) - ((unsigned long)((unsigned long)root * (unsigned long)*p4) >> shift) ;
if(y<WINDOW_TOP) y=WINDOW_TOP;
}
if(y>*p1)
{
/*
* Draw a black line
*/
unsigned char bit=~(0x80 >> (i&0x07));
unsigned int endbase=y*80;
unsigned int base=*p1*80+(i>>3);
while(base<endbase)
{
screen(base)&=bit;
base+=80;
}
}
else
{
/*
* Draw a blue line.
*/
unsigned char bit=0x80 >> (i&0x07);
unsigned int endbase=(*p1+1)*80;
unsigned base=y*80+(i>>3);
while(base<endbase)
{
screen(base)|=bit;
base+=80;
}
}
*p1=y;
p1++;
p2++;
p3++;
p4++;
}
}
}
}
/*
* Shut down the DMA system.
*/
ClosePCMBuffering();
/*
* Restore mixers to their previous value.
*/
cMVSetMixerFunction(LeftChannel,BI_OUTPUTMIXER,BI_L_PCM);
cMVSetMixerFunction(RightChannel,BI_OUTPUTMIXER,BI_R_PCM);
closegraph();
}