1 /*
2  *  Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
3  *  This file is part of KISS FFT - https://github.com/mborgerding/kissfft
4  *
5  *  SPDX-License-Identifier: BSD-3-Clause
6  *  See COPYING file for more information.
7  */
8 
9 #include <stdlib.h>
10 #include <math.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <png.h>
15 
16 #include "kiss_fft.h"
17 #include "kiss_fftr.h"
18 
19 int nfft=1024;
20 FILE * fin=NULL;
21 FILE * fout=NULL;
22 
23 int navg=20;
24 int remove_dc=0;
25 int nrows=0;
26 float * vals=NULL;
27 int stereo=0;
28 
29 static
config(int argc,char ** argv)30 void config(int argc,char** argv)
31 {
32     while (1) {
33         int c = getopt (argc, argv, "n:r:as");
34         if (c == -1)
35             break;
36         switch (c) {
37         case 'n': nfft=(int)atoi(optarg);break;
38         case 'r': navg=(int)atoi(optarg);break;
39         case 'a': remove_dc=1;break;
40         case 's': stereo=1;break;
41         case '?':
42             fprintf (stderr, "usage options:\n"
43                      "\t-n d: fft dimension(s) [1024]\n"
44                      "\t-r d: number of rows to average [20]\n"
45                      "\t-a : remove average from each fft buffer\n"
46                      "\t-s : input is stereo, channels will be combined before fft\n"
47                      "16 bit machine format real input is assumed\n"
48                      );
49         default:
50             fprintf (stderr, "bad %c\n", c);
51             exit (1);
52             break;
53         }
54     }
55     if ( optind < argc ) {
56         if (strcmp("-",argv[optind]) !=0)
57             fin = fopen(argv[optind],"rb");
58         ++optind;
59     }
60 
61     if ( optind < argc ) {
62         if ( strcmp("-",argv[optind]) !=0 )
63             fout = fopen(argv[optind],"wb");
64         ++optind;
65     }
66     if (fin==NULL)
67         fin=stdin;
68     if (fout==NULL)
69         fout=stdout;
70 }
71 
72 #define CHECKNULL(p) if ( (p)==NULL ) do { fprintf(stderr,"CHECKNULL failed @ %s(%d): %s\n",__FILE__,__LINE__,#p );exit(1);} while(0)
73 
74 typedef struct
75 {
76     png_byte r;
77     png_byte g;
78     png_byte b;
79 } rgb_t;
80 
81 static
val2rgb(float x,rgb_t * p)82 void val2rgb(float x,rgb_t *p)
83 {
84     const double pi = 3.14159265358979;
85     p->g = (int)(255*sin(x*pi));
86     p->r = (int)(255*abs(sin(x*pi*3/2)));
87     p->b = (int)(255*abs(sin(x*pi*5/2)));
88     //fprintf(stderr,"%.2f : %d,%d,%d\n",x,(int)p->r,(int)p->g,(int)p->b);
89 }
90 
91 static
cpx2pixels(rgb_t * res,const float * fbuf,size_t n)92 void cpx2pixels(rgb_t * res,const float * fbuf,size_t n)
93 {
94     size_t i;
95     float minval,maxval,valrange;
96     minval=maxval=fbuf[0];
97 
98     for (i = 0; i < n; ++i) {
99         if (fbuf[i] > maxval) maxval = fbuf[i];
100         if (fbuf[i] < minval) minval = fbuf[i];
101     }
102 
103     fprintf(stderr,"min ==%f,max=%f\n",minval,maxval);
104     valrange = maxval-minval;
105     if (valrange == 0) {
106         fprintf(stderr,"min == max == %f\n",minval);
107         exit (1);
108     }
109 
110     for (i = 0; i < n; ++i)
111         val2rgb( (fbuf[i] - minval)/valrange , res+i );
112 }
113 
114 static
transform_signal(void)115 void transform_signal(void)
116 {
117     short *inbuf;
118     kiss_fftr_cfg cfg=NULL;
119     kiss_fft_scalar *tbuf;
120     kiss_fft_cpx *fbuf;
121     float *mag2buf;
122     int i;
123     int n;
124     int avgctr=0;
125 
126     int nfreqs=nfft/2+1;
127 
128     CHECKNULL( cfg=kiss_fftr_alloc(nfft,0,0,0) );
129     CHECKNULL( inbuf=(short*)malloc(sizeof(short)*2*nfft ) );
130     CHECKNULL( tbuf=(kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar)*nfft ) );
131     CHECKNULL( fbuf=(kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*nfreqs ) );
132     CHECKNULL( mag2buf=(float*)malloc(sizeof(float)*nfreqs ) );
133 
134     memset(mag2buf,0,sizeof(mag2buf)*nfreqs);
135 
136     while (1) {
137         if (stereo) {
138             n = fread(inbuf,sizeof(short)*2,nfft,fin);
139             if (n != nfft )
140                 break;
141             for (i=0;i<nfft;++i)
142                 tbuf[i] = inbuf[2*i] + inbuf[2*i+1];
143         }else{
144             n = fread(inbuf,sizeof(short),nfft,fin);
145             if (n != nfft )
146                 break;
147             for (i=0;i<nfft;++i)
148                 tbuf[i] = inbuf[i];
149         }
150 
151         if (remove_dc) {
152             float avg = 0;
153             for (i=0;i<nfft;++i)  avg += tbuf[i];
154             avg /= nfft;
155             for (i=0;i<nfft;++i)  tbuf[i] -= (kiss_fft_scalar)avg;
156         }
157 
158         /* do FFT */
159         kiss_fftr(cfg,tbuf,fbuf);
160 
161         for (i=0;i<nfreqs;++i)
162             mag2buf[i] += fbuf[i].r * fbuf[i].r + fbuf[i].i * fbuf[i].i;
163 
164         if (++avgctr == navg) {
165             avgctr=0;
166             ++nrows;
167             vals = (float*)realloc(vals,sizeof(float)*nrows*nfreqs);
168             float eps = 1;
169             for (i=0;i<nfreqs;++i)
170                 vals[(nrows - 1) * nfreqs + i] = 10 * log10 ( mag2buf[i] / navg + eps );
171             memset(mag2buf,0,sizeof(mag2buf[0])*nfreqs);
172         }
173     }
174 
175     free(cfg);
176     free(inbuf);
177     free(tbuf);
178     free(fbuf);
179     free(mag2buf);
180 }
181 
182 static
make_png(void)183 void make_png(void)
184 {
185     png_bytepp row_pointers=NULL;
186     rgb_t * row_data=NULL;
187     int i;
188     int nfreqs = nfft/2+1;
189 
190     png_structp png_ptr=NULL;
191     png_infop info_ptr=NULL;
192 
193     CHECKNULL( png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,0,0,0) );
194     CHECKNULL( info_ptr = png_create_info_struct(png_ptr) );
195 
196 
197     png_init_io(png_ptr, fout );
198     png_set_IHDR(png_ptr, info_ptr ,nfreqs,nrows,8,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT );
199 
200 
201     row_data = (rgb_t*)malloc(sizeof(rgb_t) * nrows * nfreqs) ;
202     cpx2pixels(row_data, vals, nfreqs*nrows );
203 
204     row_pointers = realloc(row_pointers, nrows*sizeof(png_bytep));
205     for (i=0;i<nrows;++i) {
206         row_pointers[i] = (png_bytep)(row_data + i*nfreqs);
207     }
208     png_set_rows(png_ptr, info_ptr, row_pointers);
209 
210 
211     fprintf(stderr,"creating %dx%d png\n",nfreqs,nrows);
212     fprintf(stderr,"bitdepth %d \n",png_get_bit_depth(png_ptr,info_ptr ) );
213 
214     png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY , NULL);
215 
216 }
217 
main(int argc,char ** argv)218 int main(int argc,char ** argv)
219 {
220     config(argc,argv);
221 
222     transform_signal();
223 
224     make_png();
225 
226     if (fout!=stdout) fclose(fout);
227     if (fin!=stdin) fclose(fin);
228     return 0;
229 }
230