1 /*
2 Copyright (c) 2003-2004, Mark Borgerding
3 
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7 
8     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10     * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 
12 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 */
14 
15 #include <stdlib.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <png.h>
21 
22 #include "kiss_fft.h"
23 #include "kiss_fftr.h"
24 
25 int nfft=1024;
26 FILE * fin=NULL;
27 FILE * fout=NULL;
28 
29 int navg=20;
30 int remove_dc=0;
31 int nrows=0;
32 float * vals=NULL;
33 int stereo=0;
34 
35 static
config(int argc,char ** argv)36 void config(int argc,char** argv)
37 {
38     while (1) {
39         int c = getopt (argc, argv, "n:r:as");
40         if (c == -1)
41             break;
42         switch (c) {
43         case 'n': nfft=(int)atoi(optarg);break;
44         case 'r': navg=(int)atoi(optarg);break;
45         case 'a': remove_dc=1;break;
46         case 's': stereo=1;break;
47         case '?':
48             fprintf (stderr, "usage options:\n"
49                      "\t-n d: fft dimension(s) [1024]\n"
50                      "\t-r d: number of rows to average [20]\n"
51                      "\t-a : remove average from each fft buffer\n"
52                      "\t-s : input is stereo, channels will be combined before fft\n"
53                      "16 bit machine format real input is assumed\n"
54                      );
55         default:
56             fprintf (stderr, "bad %c\n", c);
57             exit (1);
58             break;
59         }
60     }
61     if ( optind < argc ) {
62         if (strcmp("-",argv[optind]) !=0)
63             fin = fopen(argv[optind],"rb");
64         ++optind;
65     }
66 
67     if ( optind < argc ) {
68         if ( strcmp("-",argv[optind]) !=0 )
69             fout = fopen(argv[optind],"wb");
70         ++optind;
71     }
72     if (fin==NULL)
73         fin=stdin;
74     if (fout==NULL)
75         fout=stdout;
76 }
77 
78 #define CHECKNULL(p) if ( (p)==NULL ) do { fprintf(stderr,"CHECKNULL failed @ %s(%d): %s\n",__FILE__,__LINE__,#p );exit(1);} while(0)
79 
80 typedef struct
81 {
82     png_byte r;
83     png_byte g;
84     png_byte b;
85 } rgb_t;
86 
87 static
val2rgb(float x,rgb_t * p)88 void val2rgb(float x,rgb_t *p)
89 {
90     const double pi = 3.14159265358979;
91     p->g = (int)(255*sin(x*pi));
92     p->r = (int)(255*abs(sin(x*pi*3/2)));
93     p->b = (int)(255*abs(sin(x*pi*5/2)));
94     //fprintf(stderr,"%.2f : %d,%d,%d\n",x,(int)p->r,(int)p->g,(int)p->b);
95 }
96 
97 static
cpx2pixels(rgb_t * res,const float * fbuf,size_t n)98 void cpx2pixels(rgb_t * res,const float * fbuf,size_t n)
99 {
100     size_t i;
101     float minval,maxval,valrange;
102     minval=maxval=fbuf[0];
103 
104     for (i = 0; i < n; ++i) {
105         if (fbuf[i] > maxval) maxval = fbuf[i];
106         if (fbuf[i] < minval) minval = fbuf[i];
107     }
108 
109     fprintf(stderr,"min ==%f,max=%f\n",minval,maxval);
110     valrange = maxval-minval;
111     if (valrange == 0) {
112         fprintf(stderr,"min == max == %f\n",minval);
113         exit (1);
114     }
115 
116     for (i = 0; i < n; ++i)
117         val2rgb( (fbuf[i] - minval)/valrange , res+i );
118 }
119 
120 static
transform_signal(void)121 void transform_signal(void)
122 {
123     short *inbuf;
124     kiss_fftr_cfg cfg=NULL;
125     kiss_fft_scalar *tbuf;
126     kiss_fft_cpx *fbuf;
127     float *mag2buf;
128     int i;
129     int n;
130     int avgctr=0;
131 
132     int nfreqs=nfft/2+1;
133 
134     CHECKNULL( cfg=kiss_fftr_alloc(nfft,0,0,0) );
135     CHECKNULL( inbuf=(short*)malloc(sizeof(short)*2*nfft ) );
136     CHECKNULL( tbuf=(kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar)*nfft ) );
137     CHECKNULL( fbuf=(kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*nfreqs ) );
138     CHECKNULL( mag2buf=(float*)malloc(sizeof(float)*nfreqs ) );
139 
140     memset(mag2buf,0,sizeof(mag2buf)*nfreqs);
141 
142     while (1) {
143         if (stereo) {
144             n = fread(inbuf,sizeof(short)*2,nfft,fin);
145             if (n != nfft )
146                 break;
147             for (i=0;i<nfft;++i)
148                 tbuf[i] = inbuf[2*i] + inbuf[2*i+1];
149         }else{
150             n = fread(inbuf,sizeof(short),nfft,fin);
151             if (n != nfft )
152                 break;
153             for (i=0;i<nfft;++i)
154                 tbuf[i] = inbuf[i];
155         }
156 
157         if (remove_dc) {
158             float avg = 0;
159             for (i=0;i<nfft;++i)  avg += tbuf[i];
160             avg /= nfft;
161             for (i=0;i<nfft;++i)  tbuf[i] -= (kiss_fft_scalar)avg;
162         }
163 
164         /* do FFT */
165         kiss_fftr(cfg,tbuf,fbuf);
166 
167         for (i=0;i<nfreqs;++i)
168             mag2buf[i] += fbuf[i].r * fbuf[i].r + fbuf[i].i * fbuf[i].i;
169 
170         if (++avgctr == navg) {
171             avgctr=0;
172             ++nrows;
173             vals = (float*)realloc(vals,sizeof(float)*nrows*nfreqs);
174             float eps = 1;
175             for (i=0;i<nfreqs;++i)
176                 vals[(nrows - 1) * nfreqs + i] = 10 * log10 ( mag2buf[i] / navg + eps );
177             memset(mag2buf,0,sizeof(mag2buf[0])*nfreqs);
178         }
179     }
180 
181     free(cfg);
182     free(inbuf);
183     free(tbuf);
184     free(fbuf);
185     free(mag2buf);
186 }
187 
188 static
make_png(void)189 void make_png(void)
190 {
191     png_bytepp row_pointers=NULL;
192     rgb_t * row_data=NULL;
193     int i;
194     int nfreqs = nfft/2+1;
195 
196     png_structp png_ptr=NULL;
197     png_infop info_ptr=NULL;
198 
199     CHECKNULL( png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,0,0,0) );
200     CHECKNULL( info_ptr = png_create_info_struct(png_ptr) );
201 
202 
203     png_init_io(png_ptr, fout );
204     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 );
205 
206 
207     row_data = (rgb_t*)malloc(sizeof(rgb_t) * nrows * nfreqs) ;
208     cpx2pixels(row_data, vals, nfreqs*nrows );
209 
210     row_pointers = realloc(row_pointers, nrows*sizeof(png_bytep));
211     for (i=0;i<nrows;++i) {
212         row_pointers[i] = (png_bytep)(row_data + i*nfreqs);
213     }
214     png_set_rows(png_ptr, info_ptr, row_pointers);
215 
216 
217     fprintf(stderr,"creating %dx%d png\n",nfreqs,nrows);
218     fprintf(stderr,"bitdepth %d \n",png_get_bit_depth(png_ptr,info_ptr ) );
219 
220     png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY , NULL);
221 
222 }
223 
main(int argc,char ** argv)224 int main(int argc,char ** argv)
225 {
226     config(argc,argv);
227 
228     transform_signal();
229 
230     make_png();
231 
232     if (fout!=stdout) fclose(fout);
233     if (fin!=stdin) fclose(fin);
234     return 0;
235 }
236