You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
12 KiB
437 lines
12 KiB
#include<stdio.h> |
|
#include<stdbool.h> |
|
#include <time.h> |
|
#include <math.h> |
|
#include <alsa/asoundlib.h> |
|
#include <sys/ioctl.h> |
|
#include <unistd.h> |
|
#include <fftw3.h> |
|
#define PI 3.14159265358979323846 |
|
#include<unistd.h> |
|
#include<signal.h> |
|
#include<string.h> |
|
#include <getopt.h> |
|
|
|
struct sigaction old_action; |
|
|
|
|
|
void sigint_handler(int sig_no) |
|
{ |
|
printf("\033[0m\n"); |
|
system("setfont /usr/share/consolefonts/Lat2-Fixed16.psf.gz "); |
|
system("setterm -cursor on"); |
|
system("clear"); |
|
printf("CTRL-C pressed -- goodbye\n"); |
|
sigaction(SIGINT, &old_action, NULL); |
|
kill(0, SIGINT); |
|
} |
|
|
|
|
|
int main(int argc, char **argv) |
|
{ |
|
int M=4096; |
|
signed char *buffer; |
|
snd_pcm_t *handle; |
|
snd_pcm_hw_params_t *params; |
|
unsigned int val; |
|
snd_pcm_uframes_t frames; |
|
char *device = "hw:1,1"; |
|
float fc[200];//={150.223,297.972,689.062,1470,3150,5512.5,11025,18000}; |
|
float fr[200];//={0.00340905,0.0067567,0.015625,0.0333,0.07142857,0.125,0.25,0.4}; |
|
int lcf[200], hcf[200]; |
|
double f[200]; |
|
double x[M]; |
|
double peak[201]; |
|
float y[M/2+1]; |
|
long int lpeak,hpeak; |
|
int bands=20; |
|
int sleep=0; |
|
float h; |
|
int i, n, o, size, dir, err,xb,yb,bw,format,rate,width,height,c,rest,virt; |
|
int autoband=1; |
|
//long int peakhist[bands][400]; |
|
double temp; |
|
double sum=0; |
|
int16_t hi; |
|
int q=0; |
|
val=44100; |
|
int debug=0; |
|
struct winsize w; |
|
double in[2*(M/2+1)]; |
|
fftw_complex out[M/2+1][2]; |
|
fftw_plan p; |
|
struct timespec start, stop; |
|
double accum; |
|
char *color; |
|
int col = 37; |
|
//---------- THIS IS THE END OF INIT, MUST STOP PUTTING INIT BELOW |
|
|
|
|
|
//**arg handler**// |
|
while ((c = getopt (argc, argv, "b:d:c:t:B")) != -1) |
|
switch (c) |
|
{ |
|
case 'b': |
|
bands = atoi(optarg); |
|
autoband=0; //dont automaticly add bands to fill frame |
|
if (bands>200)bands=200; |
|
break; |
|
case 'd': |
|
device = optarg; |
|
break; |
|
case 'c': |
|
col=0; |
|
color = optarg; |
|
if(strcmp(color,"red")==0) col=31; |
|
if(strcmp(color,"green")==0) col=32; |
|
if(strcmp(color,"yellow")==0) col=33; |
|
if(strcmp(color,"blue")==0) col=34; |
|
if(strcmp(color,"magenta")==0) col=35; |
|
if(strcmp(color,"cyan")==0) col=36; |
|
if(strcmp(color,"white")==0) col=37; |
|
if(col==0) |
|
{ |
|
printf("color %s not suprted\n",color); |
|
exit(1); |
|
} |
|
break; |
|
case '?': |
|
printf ("\nUsage : ./cava [options]\n\nOptions:\n\t-b 1..(console columns/2-1) or 200, number of bars in the spectrum (default 20 + fills up the console), program wil auto adjust to maxsize if input is to high)\n\n\t-d 'alsa device', name of alsa capture device (default 'hw:1,1')\n\n\t-c color\tsuported colors: red, green, yellow, magenta, cyan, white, blue (default: cyan)\n\n\""); |
|
return 1; |
|
default: |
|
abort (); |
|
} |
|
|
|
|
|
//**ctrl c handler**// |
|
|
|
struct sigaction action; |
|
memset(&action, 0, sizeof(action)); |
|
action.sa_handler = &sigint_handler; |
|
sigaction(SIGINT, &action, &old_action); |
|
|
|
|
|
//**drawing frame**// |
|
if(debug==0){ |
|
virt = system("setfont cava.psf"); |
|
system("setterm -cursor off"); |
|
} |
|
|
|
|
|
//getting h*w of term |
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); |
|
if(bands>(int)w.ws_col/2-1)bands=(int)w.ws_col/2-1; //handle for user setting to many bars |
|
height=(int)w.ws_row-1; |
|
width=(int)w.ws_col-bands-1; |
|
bool matrix[width][height]; |
|
bw=width/bands; |
|
|
|
//if no bands are selected it tries to padd the default 20 if there is extra room |
|
if(autoband==1) bands=bands+(((w.ws_col)-(bw*bands+bands-1))/(bw+1)); |
|
|
|
//checks if there is stil extra room, will use this to center |
|
rest=(((w.ws_col)-(bw*bands+bands-1))); |
|
if(rest<0)rest=0; |
|
|
|
//resetting console |
|
printf("\033[0m\n"); |
|
system("clear"); |
|
|
|
printf("\033[%dm",col);//setting volor |
|
|
|
|
|
|
|
|
|
/* |
|
if(debug==0){ |
|
for(yb=0;yb<w.ws_row;yb++) |
|
{ |
|
for(xb=0;xb<w.ws_col;xb++) |
|
{ |
|
if(yb==0) |
|
{ |
|
if(xb==0)printf("\u250c");//top left |
|
else if(xb==w.ws_col-1)printf("\u2510");//top right |
|
else printf("\u2500");//top |
|
} |
|
else if(yb==w.ws_row-1) |
|
{ |
|
if(xb==0)printf("\u2514");//bottom left |
|
else if(xb==w.ws_col-1)printf("\u2518");//bottom right |
|
else printf("\u2500");//bottom |
|
} |
|
else |
|
{ |
|
if (xb==0||xb==w.ws_col-1)printf("\u2502");//left and right |
|
else printf("%1c",32); |
|
} |
|
} |
|
if(yb!=w.ws_row-1)printf("\n"); |
|
} |
|
printf("\r\033[%dC",1); |
|
printf("%c[%dA",27,w.ws_row-2);//backup |
|
fflush(stdout); |
|
} |
|
*/ |
|
|
|
|
|
//**init sound device***// |
|
|
|
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE , 0) < 0)) |
|
printf("error opening stream: %s\n",snd_strerror(err) ); |
|
else |
|
if(debug==1){ printf("open stream succes\n"); } |
|
snd_pcm_hw_params_alloca(¶ms); |
|
snd_pcm_hw_params_any (handle, params); |
|
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); |
|
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); |
|
snd_pcm_hw_params_set_channels(handle, params, 2); |
|
val = 44100; |
|
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); |
|
frames = 32; |
|
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); |
|
|
|
err = snd_pcm_hw_params(handle, params); |
|
if (err < 0) { |
|
fprintf(stderr, |
|
"unable to set hw parameters: %s\n", |
|
snd_strerror(err)); |
|
exit(1); |
|
} |
|
|
|
|
|
snd_pcm_hw_params_get_period_size(params,&frames, &dir); |
|
snd_pcm_hw_params_get_period_time(params, &val, &dir); |
|
|
|
snd_pcm_hw_params_get_format(params, (snd_pcm_format_t * )&val); //getting format |
|
|
|
if(val<6)format=16; |
|
else if(val>5&&val<10)format=24; |
|
else if(val>9)format=32; |
|
|
|
size = frames * (format/8)*2; /* bytes/sample * 2 channels */ |
|
buffer = (char *) malloc(size); |
|
|
|
if(debug==1)printf("detected format: %d\n",format); |
|
|
|
snd_pcm_hw_params_get_rate( params, &rate, &dir); //getting rate |
|
if(debug==1)printf("detected rate: %d\n",rate); |
|
|
|
|
|
|
|
//**calculating cutof frequencies**/ |
|
for(n=0;n<bands+1;n++) |
|
{ |
|
fc[n]=8000*pow(10,-2+(((float)n/(float)bands)*2));//decided to cut it at 10k, little interesting to hear above |
|
fr[n]=fc[n]/(rate); //remember nyquist |
|
lcf[n]=fr[n]*(M/2+1); |
|
|
|
if(n!=0)hcf[n-1]=lcf[n]; |
|
|
|
if(debug==1&&n!=0){printf("%d: %f -> %f (%d -> %d) \n",n,fc[n-1],fc[n],lcf[n-1],hcf[n-1]);} |
|
} |
|
|
|
|
|
|
|
p = fftw_plan_dft_r2c_1d(M, in, *out, FFTW_MEASURE); //planning to rock |
|
|
|
//**start main loop**// |
|
while (1) |
|
{ |
|
/* |
|
if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) { |
|
perror( "clock gettime" ); |
|
exit( EXIT_FAILURE ); |
|
} |
|
*/ |
|
|
|
//**filling of the buffer**// |
|
for(o=0;o<M/32;o++) |
|
{ |
|
err = snd_pcm_readi(handle, buffer, frames); |
|
if (err == -EPIPE) { |
|
/* EPIPE means overrun */ |
|
if(debug==1){ fprintf(stderr, "overrun occurred\n");} |
|
snd_pcm_prepare(handle); |
|
} else if (err < 0) { |
|
if(debug==1){ fprintf(stderr, "error from read: %s\n", |
|
snd_strerror(err));} |
|
} else if (err != (int)frames) { |
|
if(debug==1){ fprintf(stderr, "short read, read %d %d frames\n", err,(int)frames);} |
|
} |
|
|
|
//sorting out one channel and only biggest octet |
|
n=0;//frame counter |
|
for (i=0; i<size ; i=i+(format/8)*2) |
|
{ |
|
// left right |
|
//structuere [litte],[litte],[big],[big],[litte],[litte],[big],[big]... |
|
|
|
x[n+(int)frames*o] = (buffer[i+(format/4)-1]+buffer[i+(format/8)-1])/2;//avg of left and right |
|
|
|
if(x[n+(int)frames*o]>hpeak) hpeak=x[o]; |
|
if(x[n+(int)frames*o]<lpeak) lpeak=x[o]; |
|
n++; |
|
} |
|
} |
|
|
|
|
|
if(debug==1){ system("clear");} |
|
|
|
|
|
//**populating input buffer & checking if there is sound**// |
|
lpeak=0; |
|
hpeak=0; |
|
for (i=0;i<(2*(M/2+1));i++) |
|
{ |
|
if(i<M) |
|
{ |
|
in[i]=x[i]; |
|
if(x[i]>hpeak) hpeak=x[i]; |
|
if(x[i]<lpeak) lpeak=x[i]; |
|
} |
|
else in[i]=0; |
|
// if(debug==1) printf("%d %f\n",i,in[i]); |
|
} |
|
peak[bands]=(hpeak+abs(lpeak)); |
|
|
|
//**if sound go ahead with fft**// |
|
if (peak[bands]!=0) |
|
{ |
|
sleep=0; //wake if was sleepy |
|
|
|
fftw_execute(p); //applying FFT to signal |
|
|
|
//saving freq domian of input signal |
|
for (i=0;i<M/2+1;i++) |
|
{ |
|
y[i]=pow(pow(*out[i][0],2)+pow(*out[i][1],2),0.5); |
|
} |
|
|
|
//seperating freq bands |
|
for(o=0;o<bands;o++) |
|
{ |
|
peak[o]=0; |
|
|
|
//getting peaks |
|
for (i=lcf[o];i<=hcf[o];i++) |
|
{ |
|
if(y[i]>peak[o]) peak[o]=y[i]; |
|
} |
|
|
|
//if (peak[o]>sum)sum=peak[o]; peakest values are ussaly around 50k but i have seen 113000 |
|
//divides on 200 because of complex mplification |
|
//mulitplise by log of frequency probably because of eq master cd standard, riaa...? |
|
|
|
|
|
f[o]=((peak[o]*(float)height*log(hcf[o]+10)*log(hcf[o]+10))) /(254*(M/8)*(log(hcf[bands-1]))) ; //weighing signal to height and frequency |
|
|
|
if(f[o]>height)f[o]=height;//just in case |
|
|
|
if(debug==1){ printf("%d: f:%f->%f peak:%f adjpeak: %f \n",o,fc[o],fc[o+1],peak[o],f[o]);} |
|
} |
|
if(debug==1){ printf("topp overall unfiltered:%f \n",peak[bands]); |
|
|
|
} |
|
//if(debug==1){ printf("topp overall alltime:%f \n",sum);} |
|
} |
|
else//**if no signal don't bother**// |
|
{ |
|
if (sleep>(rate*5)/M)//if no signal for 5 sec, go to sleep mode |
|
{ |
|
if(debug==1)printf("no sound detected for 5 sec, going to sleep mode\n"); |
|
usleep(1*1000000);//wait one sec, then check sound again. Maybe break, close and reopen? |
|
continue; |
|
} |
|
if(debug==1)printf("no sound detected, trying again\n"); |
|
sleep++; |
|
continue; |
|
} |
|
|
|
//**DRAWING**// -- put in function file maybe? |
|
if (debug==0) |
|
{ |
|
for (n=(height-1);n>=0;n--) |
|
{ |
|
o=0; |
|
for (i=0;i<width;i++) |
|
{ |
|
|
|
//next bar? make a space |
|
if(i!=0&&i%bw==0){ |
|
o++; |
|
if(o<bands)printf(" "); |
|
} |
|
|
|
//draw color or blank |
|
if(o<bands){ //watch so it doesnt draw to far |
|
if(f[o]-n<0.125) printf(" "); //blank |
|
else if (f[o]-n>1) printf("\u2588");//color |
|
else//top color, finding fraction |
|
{ |
|
c=((((f[o]-(float)n)-0.125)/0.875*7)+1); |
|
switch (c) |
|
{ |
|
case 1: |
|
if(virt==0)printf("1"); |
|
else printf("\u2581"); |
|
break; |
|
case 2: |
|
if(virt==0)printf("2"); |
|
else printf("\u2582"); |
|
break; |
|
case 3: |
|
if(virt==0)printf("3"); |
|
else printf("\u2583"); |
|
break; |
|
case 4: |
|
if(virt==0)printf("4"); |
|
else printf("\u2584"); |
|
break; |
|
case 5: |
|
if(virt==0)printf("5"); |
|
else printf("\u2585"); |
|
break; |
|
case 6: |
|
if(virt==0)printf("6"); |
|
else printf("\u2586"); |
|
break; |
|
case 7: |
|
if(virt==0)printf("7"); |
|
else printf("\u2587"); |
|
break; |
|
default: |
|
printf(" "); |
|
} |
|
} |
|
} |
|
|
|
|
|
} |
|
printf("\n\033[%dC",(rest/2));//next line and one to the right |
|
} |
|
|
|
printf("\033[%dA",height);//backup |
|
|
|
|
|
} |
|
// |
|
|
|
//snd_pcm_drain(handle); |
|
//snd_pcm_drop(handle); |
|
//snd_pcm_close(handle); |
|
/* //tried to make this frameramet limit to save cpu.... |
|
if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) { |
|
perror( "clock gettime" ); |
|
exit( EXIT_FAILURE ); |
|
} |
|
*/ |
|
//accum = (( stop.tv_sec - start.tv_sec )+ ( stop.tv_nsec - start.tv_nsec ))/1000000; |
|
//if(accum<50000&&accum>0)usleep(50000-accum); //16666.666666667 usec = 60 fps |
|
//printf("time used: %f\n",accum); |
|
} |
|
|
|
fftw_destroy_plan(p); |
|
return 0; |
|
} |
|
|
|
|