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