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.
617 lines
18 KiB
617 lines
18 KiB
#include<stdio.h> |
|
#include<stdbool.h> |
|
#include <math.h> |
|
#include <alsa/asoundlib.h> |
|
#include <sys/ioctl.h> |
|
#include <fftw3.h> |
|
#define PI 3.14159265358979323846 |
|
#include<unistd.h> |
|
#include<signal.h> |
|
#include<string.h> |
|
#include <getopt.h> |
|
#include <pthread.h> |
|
|
|
struct sigaction old_action; |
|
int M=2048; |
|
int shared[2048]; |
|
int debug=0; |
|
int format=-1; |
|
int rate=-1; |
|
|
|
|
|
void sigint_handler(int sig_no) |
|
{ |
|
printf("\033[0m\n"); |
|
system("setfont /usr/share/consolefonts/Lat2-Fixed16.psf.gz "); |
|
system("setterm -cursor on"); |
|
system("setterm -blank 10"); |
|
system("clear"); |
|
system("stty echo"); |
|
printf("CTRL-C pressed -- goodbye\n"); |
|
sigaction(SIGINT, &old_action, NULL); |
|
kill(0, SIGINT); |
|
} |
|
|
|
//ALSA audio listner |
|
void* |
|
music(void* data) |
|
{ |
|
signed char *buffer; |
|
snd_pcm_t *handle; |
|
snd_pcm_hw_params_t *params; |
|
unsigned int val; |
|
snd_pcm_uframes_t frames; |
|
char *device = ((char*)data); |
|
val=44100; |
|
int i, n, o, size, dir, err,lo; |
|
int tempr,templ; |
|
int radj,ladj; |
|
|
|
//**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);//assembling params |
|
snd_pcm_hw_params_any (handle, params);//setting defaults or something |
|
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);//interleeaved mode right left right left |
|
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); //trying to set 16bit |
|
snd_pcm_hw_params_set_channels(handle, params, 2);//asuming stereo |
|
val = 44100; |
|
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);//trying 44100 rate |
|
frames = 256; |
|
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); //number of frames pr read |
|
|
|
err = snd_pcm_hw_params(handle, params); //atempting to set params |
|
if (err < 0) { |
|
fprintf(stderr, |
|
"unable to set hw parameters: %s\n", |
|
snd_strerror(err)); |
|
exit(1); |
|
} |
|
|
|
snd_pcm_hw_params_get_format(params, (snd_pcm_format_t * )&val); //getting actual format |
|
//convverting result to number of bits |
|
if(val<6)format=16; |
|
else if(val>5&&val<10)format=24; |
|
else if(val>9)format=32; |
|
|
|
|
|
snd_pcm_hw_params_get_rate( params, &rate, &dir); //getting rate |
|
|
|
snd_pcm_hw_params_get_period_size(params,&frames, &dir); |
|
snd_pcm_hw_params_get_period_time(params, &val, &dir); |
|
|
|
size = frames * (format/8)*2; // frames * bits/8 * 2 channels |
|
buffer = (char *) malloc(size); |
|
radj=format/4;//adjustments for interleaved |
|
ladj=format/8; |
|
o=0; |
|
while(1){ |
|
|
|
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+(ladj)*2) |
|
{ |
|
// left right |
|
//structuere [litte],[litte],[big],[big],[litte],[litte],[big],[big]... |
|
|
|
//first channel |
|
tempr = ((buffer[i+(radj)-1 ] << 2));//using the 10 upper bits this whould give me a vert res of 1024, enough... |
|
lo=((buffer[i+(radj)-2] >> 6)); |
|
if (lo<0)lo=lo+4; |
|
if(tempr>=0)tempr= tempr + lo; |
|
if(tempr<0)tempr= tempr - lo; |
|
|
|
//other channel |
|
templ=(buffer[i+(ladj)-1] << 2); |
|
lo=(buffer[i+(ladj)-2] >> 6); |
|
if(lo<0)lo=lo+4; |
|
if(templ>=0)templ=templ+lo; |
|
else templ=templ-lo; |
|
|
|
//adding channels and storing it int the buffer |
|
shared[o]=(tempr+templ)/2; |
|
o++; |
|
if(o==M-1)o=0; |
|
|
|
//shifing ringbuffer one to the left, this ended up using to much cpu.. |
|
//for(o=0;o<M-1;o++) shared[o]=shared[o+1]; |
|
n++; |
|
} |
|
} |
|
} |
|
|
|
|
|
//FIFO audio listner |
|
void* |
|
fifomusic(void* data) |
|
{ |
|
FILE *fp; |
|
|
|
int fifo; |
|
int n=0; |
|
signed char buf[1024]; |
|
int tempr,templ,lo; |
|
int i,q; |
|
int size=1024; |
|
char *path = ((char*)data); |
|
fp = fopen(path,"r"); |
|
|
|
while (1) |
|
{ |
|
|
|
fread(&buf, sizeof(char),size,fp); |
|
|
|
for (q=0;q<(size/4);q++) |
|
{ |
|
//printf("%d, %d\n",buf[q],buf[q+1]); |
|
|
|
tempr = ( buf[size - 4*q-1] << 2); |
|
lo = ( buf[size - 4*q ] >> 6); |
|
//printf("%d\n",buf[4*q+1]); |
|
if (lo<0)lo=lo+4; |
|
if(tempr>=0)tempr= tempr + lo; |
|
if(tempr<0)tempr= tempr - lo; |
|
|
|
|
|
templ = ( buf[size - 4*q - 3] << 2); |
|
lo = ( buf[size - 4*q - 2] >> 6); |
|
|
|
if(lo<0)lo=lo+4; |
|
if(templ>=0)templ=templ+lo; |
|
else templ=templ-lo; |
|
|
|
//printf("%d\n",((tempr+templ)/2)); |
|
shared[n]=(tempr+templ)/2; |
|
|
|
n++; |
|
if(n==M-1)n=0; |
|
} |
|
} |
|
fclose(fp); |
|
} |
|
|
|
|
|
int main(int argc, char **argv) |
|
{ |
|
pthread_t p_thread; |
|
int thr_id; |
|
char *input = "alsa"; |
|
int im = 1; |
|
char *device = "hw:1,1"; |
|
char *path = "/tmp/mpd.fifo"; |
|
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]; |
|
float f[200]; |
|
int flast[200]; |
|
float peak[201]; |
|
int y[M/2+1]; |
|
long int lpeak,hpeak; |
|
int bands=25; |
|
int sleep=0; |
|
int i, n, o, size, dir, err,bw,width,height,c,rest,virt; |
|
int autoband=1; |
|
//long int peakhist[bands][400]; |
|
float temp; |
|
double sum=0; |
|
struct winsize w; |
|
double in[2*(M/2+1)]; |
|
fftw_complex out[M/2+1][2]; |
|
fftw_plan p; |
|
char *color; |
|
int col = 36; |
|
int bgcol = 0; |
|
int sens = 100; |
|
int move=0; |
|
int fall[200]; |
|
float fpeak[200]; |
|
float k[200]; |
|
float g; |
|
int framerate=60; |
|
char *usage="\nUsage : ./cava [options]\n\nOptions:\n\t-b 1..(console columns/2-1) or 200\t number of bars in the spectrum (default 25 + fills up the console), program wil auto adjust to maxsize if input is to high)\n\n\t-i 'input method'\t\t\t method used for listnening to audio, supports 'alsa' and 'fifo'\n\n\t-d 'alsa device'\t\t\t name of alsa capture device (default 'hw:1,1')\n\n\t-p 'fifo path'\t\t\t\t path to fifo (default '/tmp/mpd.fifo')\n\n\t-c color\t\t\t\t suported colors: red, green, yellow, magenta, cyan, white, blue, black (default: cyan)\n\n\t-C backround color\t\t\t supported colors: same as above (default: no change) \n\n\t-s sensitivity %\t\t\t sensitivity in percent, 0 means no respons 100 is normal 50 half 200 double and so forth\n\n\t-f framerate \t\t\t\t max frames per second to be drawn, if you are experiencing high CPU usage, try redcing this (default: 60)\n\n"; |
|
//**END INIT |
|
|
|
for (i=0;i<200;i++) |
|
{ |
|
flast[i]=0; |
|
fall[i]=0; |
|
fpeak[i]=0; |
|
} |
|
for (i=0;i<M;i++)shared[M]=0; |
|
|
|
|
|
//**arg handler**// |
|
while ((c = getopt (argc, argv, "p:i:b:d:s:f:c:C:h")) != -1) |
|
switch (c) |
|
{ |
|
case 'p': |
|
path = optarg; |
|
break; |
|
case 'i': |
|
im=0; |
|
input = optarg; |
|
if(strcmp(input,"alsa")==0) im=1; |
|
if(strcmp(input,"fifo")==0) im=2; |
|
if(im==0) |
|
{ |
|
printf("input method %s not supprted, supported methods are: 'alsa' and 'fifo'\n",input); |
|
exit(1); |
|
} |
|
break; |
|
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 's': |
|
sens = atoi(optarg); |
|
break; |
|
case 'f': |
|
framerate = atoi(optarg); |
|
break; |
|
case 'c': |
|
col=0; |
|
color = optarg; |
|
if(strcmp(color,"black")==0) col=30; |
|
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 'C': |
|
bgcol=0; |
|
color = optarg; |
|
if(strcmp(color,"black")==0) bgcol=40; |
|
if(strcmp(color,"red")==0) bgcol=41; |
|
if(strcmp(color,"green")==0) bgcol=42; |
|
if(strcmp(color,"yellow")==0) bgcol=43; |
|
if(strcmp(color,"blue")==0) bgcol=44; |
|
if(strcmp(color,"magenta")==0) bgcol=45; |
|
if(strcmp(color,"cyan")==0) bgcol=46; |
|
if(strcmp(color,"white")==0) bgcol=47; |
|
if(bgcol==0) |
|
{ |
|
printf("color %s not suprted\n",color); |
|
exit(1); |
|
} |
|
break; |
|
case 'h': |
|
printf ("%s",usage); |
|
return 0; |
|
case '?': |
|
printf ("%s",usage); |
|
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); |
|
|
|
|
|
|
|
//**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; |
|
int matrix[width][height]; |
|
for(i=0;i<width;i++) |
|
{ |
|
for(n=0;n<height;n++) |
|
{ |
|
matrix[i][n]=0; |
|
} |
|
} |
|
bw=width/bands; |
|
|
|
|
|
g=((float)height/1000)*pow((60/(float)framerate),2.5);//calculating gravity |
|
|
|
//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; |
|
|
|
|
|
printf("hoyde: %d bredde: %d bands:%d bandbredde: %d rest: %d\n",(int)w.ws_row,(int)w.ws_col,bands,bw,rest); |
|
|
|
n=0; |
|
|
|
if(im==1) |
|
{ |
|
//**watintg for audio to be ready**// |
|
thr_id = pthread_create(&p_thread, NULL, music,(void*)device); //starting alsamusic listner |
|
while (format==-1||rate==-1) |
|
{ |
|
usleep(1000); |
|
n++; |
|
if(n>2000) |
|
{ |
|
if(debug==1)printf("could not get rate and or format, problems with audoi thread? quiting...\n"); |
|
exit(1); |
|
} |
|
} |
|
if(debug==1)printf("got format: %d and rate %d\n",format,rate); |
|
debug=0; |
|
} |
|
|
|
if(im==2) |
|
{ |
|
thr_id = pthread_create(&p_thread, NULL, fifomusic,(void*)path); //starting fifomusic listner |
|
rate=44100; |
|
format=16; |
|
} |
|
|
|
//**calculating cutof frequencies**/ |
|
for(n=0;n<bands+1;n++) |
|
{ |
|
fc[n]=10000*pow(10,-2.37+((((float)n+1)/((float)bands+1))*2.37));//decided to cut it at 10k, little interesting to hear above |
|
fr[n]=fc[n]/(rate/2); //remember nyquist!, pr my calculations this should be rate/2 and nyquist freq in M/2 but testing shows it is not... or maybe the nq freq is in M/4 |
|
lcf[n]=fr[n]*(M/4); //lfc stores the lower cut frequency fro each band in the fft out buffer |
|
|
|
if(n!=0) |
|
{ |
|
hcf[n-1]=lcf[n]-1; |
|
if(lcf[n]<=lcf[n-1])lcf[n]=lcf[n-1]+1;//pushing the spectrum up if the expe function gets "clumped" |
|
hcf[n-1]=lcf[n]-1; |
|
} |
|
|
|
if(debug==1&&n!=0){printf("%d: %f -> %f (%d -> %d) \n",n,fc[n-1],fc[n],lcf[n-1],hcf[n-1]);} |
|
} |
|
//exit(1); |
|
|
|
//constants to wigh signal to frequency |
|
for(n=0;n<bands;n++)k[n]=((float)height*pow(log(lcf[n]+1),1.3))/(1024*(M/6));// the log(lcf[n]+2) is because higher frequencys are usally lower ine effect in music |
|
|
|
|
|
|
|
p = fftw_plan_dft_r2c_1d(M, in, *out, FFTW_MEASURE); //planning to rock |
|
|
|
|
|
//**preparing screen**// |
|
if(debug==0){ |
|
virt = system("setfont cava.psf"); |
|
system("setterm -cursor off"); |
|
system("setterm -blank 0"); |
|
//resetting console |
|
printf("\033[0m\n"); |
|
system("clear"); |
|
system("stty -echo"); |
|
|
|
printf("\033[%dm",col);//setting color |
|
|
|
printf("\033[1m"); //setting "bright" color mode, looks cooler... I think |
|
if(bgcol!=0) |
|
printf("\033[%dm",bgcol); |
|
{ |
|
for (n=(height);n>=0;n--) |
|
{ |
|
for (i=0;i<width+bands;i++) |
|
{ |
|
|
|
printf(" ");//setting backround volor |
|
|
|
} |
|
printf("\n");//setting volor |
|
} |
|
printf("\033[%dA",height);//backup |
|
} |
|
} |
|
|
|
|
|
//debug=1; |
|
//**start main loop**// |
|
while (1) |
|
{ |
|
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]=shared[i]; |
|
if(shared[i]>hpeak) hpeak=shared[i]; |
|
if(shared[i]<lpeak) lpeak=shared[i]; |
|
} |
|
else in[i]=0; |
|
// if(debug==1) printf("%d %f\n",i,in[i]); |
|
} |
|
peak[bands]=(hpeak+abs(lpeak)); |
|
if (peak[bands]==0)sleep++; |
|
else sleep=0; |
|
|
|
//**if sound for the last 5 sec go ahead with fft**// |
|
if (sleep<framerate*5) |
|
{ |
|
|
|
|
|
fftw_execute(p); //applying FFT to signal |
|
|
|
//seperating freq bands |
|
for(o=0;o<bands;o++) |
|
{ |
|
peak[o]=0; |
|
|
|
//getting peaks |
|
for (i=lcf[o];i<=hcf[o];i++) |
|
{ |
|
y[i]=pow(pow(*out[i][0],2)+pow(*out[i][1],2),0.5); //getting r of compex |
|
if(y[i]>peak[o]) peak[o]=y[i]; |
|
} |
|
|
|
temp=peak[o]*k[o]*((float)sens/100); //multiplying with k and adjusting to sens settings |
|
if(temp>height)temp=height;//just in case |
|
|
|
//**falloff function**// |
|
if(temp<flast[o]) |
|
{ |
|
f[o]=fpeak[o]-(g*fall[o]*fall[o]); |
|
fall[o]++; |
|
} |
|
else if (temp>=flast[o]) |
|
{ |
|
f[o]=temp; |
|
fpeak[o]=f[o]; |
|
fall[o]=0; |
|
} |
|
|
|
flast[o]=f[o]; //memmory for falloff func |
|
|
|
|
|
if(f[o]<0.125)f[o]=0.125; |
|
if(debug==1){ printf("%d: f:%f->%f (%d->%d)peak:%f adjpeak: %f \n",o,fc[o],fc[o+1],lcf[o],hcf[o],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 in sleep mode wait and continiue**// |
|
{ |
|
if(debug==1)printf("no sound detected for 3 sec, going to sleep mode\n"); |
|
//for (i=0;i<200;i++)flast[i]=0; //zeroing memory no more nesceseary after faloff on pauses |
|
//pthread_cancel(thr_id);// this didnt work to well, killing sound thread |
|
usleep(1*1000000);//wait 1 sec, then check sound again. |
|
continue; |
|
} |
|
|
|
//**DRAWING**// -- put in function file maybe? |
|
if (debug==0) |
|
{ |
|
for (n=(height-1);n>=0;n--) |
|
{ |
|
o=0; |
|
move=rest/2;//center adjustment |
|
//if(rest!=0)printf("\033[%dC",(rest/2));//center adjustment |
|
for (i=0;i<width;i++) |
|
{ |
|
|
|
//next bar? make a space |
|
if(i!=0&&i%bw==0){ |
|
o++; |
|
if(o<bands)move++; |
|
} |
|
|
|
|
|
|
|
//draw color or blank or move+1 |
|
if(o<bands){ //watch so it doesnt draw to far |
|
if(f[o]-n<0.125) //blank |
|
{ |
|
if(matrix[i][n]!=0)//change? |
|
{ |
|
if(move!=0)printf("\033[%dC",move); |
|
move=0; |
|
printf(" "); |
|
} |
|
else move++; //no change, moving along |
|
matrix[i][n]=0; |
|
} |
|
else if (f[o]-n>1)//color |
|
{ |
|
if(matrix[i][n]!=1)//change? |
|
{ |
|
if(move!=0)printf("\033[%dC",move); |
|
move=0; |
|
printf("\u2588"); |
|
} |
|
else move++; //no change, moving along |
|
matrix[i][n]=1; |
|
} |
|
else//top color, finding fraction |
|
{ |
|
if(move!=0)printf("\033[%dC",move); |
|
move=0; |
|
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(" "); |
|
} |
|
matrix[i][n]=2; |
|
} |
|
} |
|
|
|
|
|
} |
|
|
|
printf("\n");//next line |
|
|
|
} |
|
|
|
printf("\033[%dA",height);//backup |
|
|
|
usleep((1/(float)framerate)*1000000);//sleeping for set us |
|
|
|
} |
|
|
|
|
|
} |
|
return 0; |
|
} |
|
|
|
|