1 /* noiseprof - SoX Noise Profiling Effect.
2  *
3  * Written by Ian Turner (vectro@vectro.org)
4  * Copyright 1999 Ian Turner and others
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or (at
9  * your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "noisered.h"
22 
23 #include <assert.h>
24 #include <string.h>
25 #include <errno.h>
26 
27 typedef struct {
28     float *sum;
29     int   *profilecount;
30 
31     float *window;
32 } chandata_t;
33 
34 typedef struct {
35     char* output_filename;
36     FILE* output_file;
37 
38     chandata_t *chandata;
39     size_t bufdata;
40 } priv_t;
41 
42 /*
43  * Get the filename, if any. We don't open it until sox_noiseprof_start.
44  */
sox_noiseprof_getopts(sox_effect_t * effp,int argc,char ** argv)45 static int sox_noiseprof_getopts(sox_effect_t * effp, int argc, char **argv)
46 {
47     priv_t * data = (priv_t *) effp->priv;
48   --argc, ++argv;
49 
50     if (argc == 1) {
51         data->output_filename = argv[0];
52     } else if (argc > 1)
53       return lsx_usage(effp);
54 
55     return (SOX_SUCCESS);
56 }
57 
58 /*
59  * Prepare processing.
60  * Do all initializations.
61  */
sox_noiseprof_start(sox_effect_t * effp)62 static int sox_noiseprof_start(sox_effect_t * effp)
63 {
64   priv_t * data = (priv_t *) effp->priv;
65   unsigned channels = effp->in_signal.channels;
66   unsigned i;
67 
68   /* Note: don't fall back to stderr if stdout is unavailable
69    * since we already use stderr for diagnostics. */
70   if (!data->output_filename || !strcmp(data->output_filename, "-")) {
71     if (effp->global_info->global_info->stdout_in_use_by) {
72       lsx_fail("stdout already in use by `%s'", effp->global_info->global_info->stdout_in_use_by);
73       return SOX_EOF;
74     }
75     effp->global_info->global_info->stdout_in_use_by = effp->handler.name;
76     data->output_file = stdout;
77   }
78   else if ((data->output_file = fopen(data->output_filename, "wb")) == NULL) {
79     lsx_fail("Couldn't open profile file %s: %s", data->output_filename, strerror(errno));
80     return SOX_EOF;
81   }
82 
83   data->chandata = lsx_calloc(channels, sizeof(*(data->chandata)));
84   data->bufdata = 0;
85   for (i = 0; i < channels; i ++) {
86     data->chandata[i].sum = lsx_calloc(FREQCOUNT, sizeof(float));
87     data->chandata[i].profilecount = lsx_calloc(FREQCOUNT, sizeof(int));
88     data->chandata[i].window = lsx_calloc(WINDOWSIZE, sizeof(float));
89   }
90 
91   return SOX_SUCCESS;
92 }
93 
94 /* Collect statistics from the complete window on channel chan. */
collect_data(chandata_t * chan)95 static void collect_data(chandata_t* chan) {
96     float *out = lsx_calloc(FREQCOUNT, sizeof(float));
97     int i;
98 
99     lsx_power_spectrum_f(WINDOWSIZE, chan->window, out);
100 
101     for (i = 0; i < FREQCOUNT; i ++) {
102         if (out[i] > 0) {
103             float value = log(out[i]);
104             chan->sum[i] += value;
105             chan->profilecount[i] ++;
106         }
107     }
108 
109     free(out);
110 }
111 
112 /*
113  * Grab what we can from ibuf, and process if we have a whole window.
114  */
sox_noiseprof_flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)115 static int sox_noiseprof_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
116                     size_t *isamp, size_t *osamp)
117 {
118   priv_t * p = (priv_t *) effp->priv;
119   size_t samp = min(*isamp, *osamp);
120   size_t chans = effp->in_signal.channels;
121   size_t i, j, n = min(samp / chans, WINDOWSIZE - p->bufdata);
122 
123   memcpy(obuf, ibuf, n * chans * sizeof(*obuf)); /* Pass on audio unaffected */
124   *isamp = *osamp = n * chans;
125 
126   /* Collect data for every channel. */
127   for (i = 0; i < chans; i ++) {
128     SOX_SAMPLE_LOCALS;
129     chandata_t * chan = &(p->chandata[i]);
130     for (j = 0; j < n; j ++)
131       chan->window[j + p->bufdata] =
132         SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i + j * chans],);
133     if (n + p->bufdata == WINDOWSIZE)
134       collect_data(chan);
135   }
136 
137   p->bufdata += n;
138   assert(p->bufdata <= WINDOWSIZE);
139   if (p->bufdata == WINDOWSIZE)
140     p->bufdata = 0;
141 
142   return SOX_SUCCESS;
143 }
144 
145 /*
146  * Finish off the last window.
147  */
148 
sox_noiseprof_drain(sox_effect_t * effp,sox_sample_t * obuf UNUSED,size_t * osamp)149 static int sox_noiseprof_drain(sox_effect_t * effp, sox_sample_t *obuf UNUSED, size_t *osamp)
150 {
151     priv_t * data = (priv_t *) effp->priv;
152     int tracks = effp->in_signal.channels;
153     int i;
154 
155     *osamp = 0;
156 
157     if (data->bufdata == 0) {
158         return SOX_EOF;
159     }
160 
161     for (i = 0; i < tracks; i ++) {
162         int j;
163         for (j = data->bufdata+1; j < WINDOWSIZE; j ++) {
164             data->chandata[i].window[j] = 0;
165         }
166         collect_data(&(data->chandata[i]));
167     }
168 
169     if (data->bufdata == WINDOWSIZE || data->bufdata == 0)
170         return SOX_EOF;
171     else
172         return SOX_SUCCESS;
173 }
174 
175 /*
176  * Print profile and clean up.
177  */
sox_noiseprof_stop(sox_effect_t * effp)178 static int sox_noiseprof_stop(sox_effect_t * effp)
179 {
180     priv_t * data = (priv_t *) effp->priv;
181     size_t i;
182 
183     for (i = 0; i < effp->in_signal.channels; i ++) {
184         int j;
185         chandata_t* chan = &(data->chandata[i]);
186 
187         fprintf(data->output_file, "Channel %lu: ", (unsigned long)i);
188 
189         for (j = 0; j < FREQCOUNT; j ++) {
190             double r = chan->profilecount[j] != 0 ?
191                     chan->sum[j] / chan->profilecount[j] : 0;
192             fprintf(data->output_file, "%s%f", j == 0 ? "" : ", ", r);
193         }
194         fprintf(data->output_file, "\n");
195 
196         free(chan->sum);
197         free(chan->profilecount);
198     }
199 
200     free(data->chandata);
201 
202     if (data->output_file != stdout)
203         fclose(data->output_file);
204 
205     return (SOX_SUCCESS);
206 }
207 
208 static sox_effect_handler_t sox_noiseprof_effect = {
209   "noiseprof",
210   "[profile-file]",
211   SOX_EFF_MCHAN | SOX_EFF_MODIFY,
212   sox_noiseprof_getopts,
213   sox_noiseprof_start,
214   sox_noiseprof_flow,
215   sox_noiseprof_drain,
216   sox_noiseprof_stop,
217   NULL, sizeof(priv_t)
218 };
219 
lsx_noiseprof_effect_fn(void)220 const sox_effect_handler_t *lsx_noiseprof_effect_fn(void)
221 {
222     return &sox_noiseprof_effect;
223 }
224