1 /* libSoX effect: SpeexDsp effect to apply processing from libspeexdsp.
2  *
3  * Copyright 1999-2009 Chris Bagwell And SoX Contributors
4  *
5  * This library is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or (at
8  * your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "sox_i.h"
21 
22 #ifdef HAVE_SPEEXDSP
23 
24 #include <speex/speex_types.h>
25 #include <speex/speex_preprocess.h>
26 
27 /* Private data for effect */
28 typedef struct speexdsp_priv_t {
29     size_t buffer_end;        /* Index of the end of the buffer. */
30     size_t buffer_ipos;       /* Index for the next input sample. */
31     size_t buffer_opos;       /* Index of the next sample that has not been drained. */
32     int16_t* buffer;          /* Work buffer. */
33     SpeexPreprocessState* sps;/* DSP state. */
34     size_t agc;               /* Param: Automatic Gain Control target volume level: 0 to disable, or 1-100 (target volume). */
35     size_t denoise;           /* Param: Denoise: 0 to disable, or maximum noise attenuation in dB. */
36     size_t dereverb;          /* Param: Dereverb: 0 to disable, 1 to enable. */
37     size_t frames_per_second; /* Param: Used to compute buffer size from sample rate. */
38     size_t samples_per_frame; /* Param: Used to compute buffer size directly. Default is to use frames_per_second instead. */
39 } priv_t;
40 
get_param(int * pArgc,char *** pArgv,size_t * pParam,size_t default_val,size_t min_valid,size_t max_valid)41 static int get_param(
42     int* pArgc,
43     char*** pArgv,
44     size_t* pParam,
45     size_t default_val,
46     size_t min_valid,
47     size_t max_valid)
48 {
49     *pParam = default_val;
50     if (*pArgc > 1 && (*pArgv)[1][0] != '-')
51     {
52         char* arg_end;
53         *pParam = strtoul((*pArgv)[1], &arg_end, 0);
54         if (!arg_end || arg_end[0] || *pParam < min_valid || max_valid <= *pParam)
55             return 0;
56 
57         --*pArgc;
58         ++*pArgv;
59     }
60 
61     return 1;
62 }
63 
64 /*
65  * Process command-line options but don't do other
66  * initialization now: effp->in_signal & effp->out_signal are not
67  * yet filled in.
68  */
getopts(sox_effect_t * effp,int argc,char ** argv)69 static int getopts(sox_effect_t* effp, int argc, char** argv)
70 {
71     priv_t* p = (priv_t*)effp->priv;
72     const size_t agcDefault = 100;
73     const size_t denoiseDefault = 15;
74     const size_t fpsDefault = 50;
75 
76     for (argc--, argv++; argc; argc--, argv++)
77     {
78         if (!strcasecmp("-agc", argv[0]))
79         {
80             /* AGC level argument is optional. If not specified, it defaults to agcDefault.
81                If specified, it must be from 0 to 100. */
82             if (!get_param(&argc, &argv, &p->agc, agcDefault, 0, 100))
83             {
84                 lsx_fail("Invalid argument \"%s\" to -agc parameter - expected number from 0 to 100.", argv[1]);
85                 return lsx_usage(effp);
86             }
87         }
88         else if (!strcasecmp("-denoise", argv[0]))
89         {
90             /* Denoise level argument is optional. If not specified, it defaults to denoiseDefault.
91                If specified, it must be from 0 to 100. */
92             if (!get_param(&argc, &argv, &p->denoise, denoiseDefault, 0, 100))
93             {
94                 lsx_fail("Invalid argument \"%s\" to -denoise parameter - expected number from 0 to 100.", argv[1]);
95                 return lsx_usage(effp);
96             }
97         }
98         else if (!strcasecmp("-dereverb", argv[0]))
99         {
100             p->dereverb = 1;
101         }
102         else if (!strcasecmp("-spf", argv[0]))
103         {
104             /* If samples_per_frame option is given, argument is required and must be
105                greater than 0. */
106             if (!get_param(&argc, &argv, &p->samples_per_frame, 0, 1, 1000000000) || !p->samples_per_frame)
107             {
108                 lsx_fail("Invalid argument \"%s\" to -spf parameter - expected positive number.", argv[1]);
109                 return lsx_usage(effp);
110             }
111         }
112         else if (!strcasecmp("-fps", argv[0]))
113         {
114             /* If frames_per_second option is given, argument is required and must be
115                from 1 to 100. This will be used later to compute samples_per_frame once
116                we know the sample rate). */
117             if (!get_param(&argc, &argv, &p->frames_per_second, 0, 1, 100) || !p->frames_per_second)
118             {
119                 lsx_fail("Invalid argument \"%s\" to -fps parameter - expected number from 1 to 100.", argv[1]);
120                 return lsx_usage(effp);
121             }
122         }
123         else
124         {
125             lsx_fail("Invalid parameter \"%s\".", argv[0]);
126             return lsx_usage(effp);
127         }
128     }
129 
130     if (!p->frames_per_second)
131         p->frames_per_second = fpsDefault;
132 
133     if (!p->agc && !p->denoise && !p->dereverb)
134     {
135         lsx_report("No features specified. Enabling default settings \"-agc %u -denoise %u\".", agcDefault, denoiseDefault);
136         p->agc = agcDefault;
137         p->denoise = denoiseDefault;
138     }
139 
140     return SOX_SUCCESS;
141 }
142 
143 /*
144  * Do anything required when you stop reading samples.
145  */
stop(sox_effect_t * effp)146 static int stop(sox_effect_t* effp)
147 {
148     priv_t* p = (priv_t*)effp->priv;
149 
150     if (p->sps)
151     {
152         speex_preprocess_state_destroy(p->sps);
153         p->sps = NULL;
154     }
155 
156     if (p->buffer)
157     {
158         free(p->buffer);
159         p->buffer = NULL;
160     }
161 
162     return SOX_SUCCESS;
163 }
164 
165 /*
166  * Prepare processing.
167  * Do all initializations.
168  */
start(sox_effect_t * effp)169 static int start(sox_effect_t* effp)
170 {
171     priv_t* p = (priv_t*)effp->priv;
172     int result = SOX_SUCCESS;
173     spx_int32_t int_val;
174     float float_val;
175 
176     if (p->samples_per_frame)
177     {
178         p->buffer_end = p->samples_per_frame;
179     }
180     else
181     {
182         p->buffer_end = effp->in_signal.rate / p->frames_per_second;
183         if (!p->buffer_end)
184         {
185             lsx_fail("frames_per_second too large for the current sample rate.");
186             return SOX_EOF;
187         }
188     }
189 
190     p->buffer_opos = p->buffer_end;
191     effp->out_signal.precision = 16;
192 
193     p->buffer = lsx_malloc(p->buffer_end * sizeof(p->buffer[0]));
194     if (!p->buffer)
195     {
196         result = SOX_ENOMEM;
197         goto Done;
198     }
199 
200     p->sps = speex_preprocess_state_init((int)p->buffer_end, (int)(effp->in_signal.rate + .5));
201     if (!p->sps)
202     {
203         lsx_fail("Failed to initialize preprocessor DSP.");
204         result = SOX_EOF;
205         goto Done;
206     }
207 
208     int_val = p->agc ? 1 : 2;
209     speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_AGC, &int_val);
210     if (p->agc)
211     {
212         float_val = p->agc * 327.68f;
213         speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_AGC_LEVEL, &float_val);
214     }
215 
216     int_val = p->denoise ? 1 : 2;
217     speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_DENOISE, &int_val);
218     if (p->denoise)
219     {
220         int_val = -(spx_int32_t)p->denoise;
221         speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &int_val);
222     }
223 
224     int_val = p->dereverb ? 1 : 2;
225     speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_DEREVERB, &int_val);
226 
227 Done:
228     if (result != SOX_SUCCESS)
229         stop(effp);
230 
231     return result;
232 }
233 
234 /*
235  * Process up to *isamp samples from ibuf and produce up to *osamp samples
236  * in obuf.  Write back the actual numbers of samples to *isamp and *osamp.
237  * Return SOX_SUCCESS or, if error occurs, SOX_EOF.
238  */
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)239 static int flow(
240     sox_effect_t* effp,
241     const sox_sample_t* ibuf,
242     sox_sample_t* obuf,
243     size_t* isamp,
244     size_t* osamp)
245 {
246     priv_t* p = (priv_t*)effp->priv;
247     size_t ibuf_pos = 0;
248     size_t ibuf_end = *isamp;
249     size_t obuf_pos = 0;
250     size_t obuf_end = *osamp;
251     size_t end_pos;
252     SOX_SAMPLE_LOCALS;
253 
254     for (;;)
255     {
256         /* Write any processed data in working buffer to the output buffer. */
257         end_pos = obuf_pos + min(p->buffer_end - p->buffer_opos, obuf_end - obuf_pos);
258         for (; obuf_pos < end_pos; obuf_pos++, p->buffer_opos++)
259             obuf[obuf_pos] = SOX_SIGNED_16BIT_TO_SAMPLE(p->buffer[p->buffer_opos], dummy);
260         if (p->buffer_opos != p->buffer_end)
261             break; /* Output buffer is full and we still have more processed data. */
262 
263         /* Fill working buffer from input buffer. */
264         end_pos = ibuf_pos + min(p->buffer_end - p->buffer_ipos, ibuf_end - ibuf_pos);
265         for (; ibuf_pos < end_pos; ibuf_pos++, p->buffer_ipos++)
266             p->buffer[p->buffer_ipos] = SOX_SAMPLE_TO_SIGNED_16BIT(ibuf[ibuf_pos], effp->clips);
267         if (p->buffer_ipos != p->buffer_end)
268             break; /* Working buffer is not full and there is no more input data. */
269 
270         speex_preprocess_run(p->sps, p->buffer);
271         p->buffer_ipos = 0;
272         p->buffer_opos = 0;
273     }
274 
275     *isamp = ibuf_pos;
276     *osamp = obuf_pos;
277     return SOX_SUCCESS;
278 }
279 
280 /*
281  * Drain out remaining samples if the effect generates any.
282  */
drain(sox_effect_t * effp,sox_sample_t * obuf,size_t * osamp)283 static int drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp)
284 {
285     priv_t* p = (priv_t*)effp->priv;
286     size_t obuf_pos = 0;
287     size_t obuf_end = *osamp;
288     size_t i;
289     size_t end_pos;
290 
291     /* Input that hasn't been processed yet? */
292     if (p->buffer_ipos != 0)
293     {
294         /* DSP only works on full frames, so fill the remaining space with 0s. */
295         for (i = p->buffer_ipos; i < p->buffer_end; i++)
296             p->buffer[i] = 0;
297         speex_preprocess_run(p->sps, p->buffer);
298         p->buffer_end = p->buffer_ipos;
299         p->buffer_ipos = 0;
300         p->buffer_opos = 0;
301     }
302 
303     end_pos = obuf_pos + min(p->buffer_end - p->buffer_opos, obuf_end - obuf_pos);
304     for (; obuf_pos < end_pos; obuf_pos++, p->buffer_opos++)
305         obuf[obuf_pos] = SOX_SIGNED_16BIT_TO_SAMPLE(p->buffer[p->buffer_opos], dummy);
306 
307     *osamp = obuf_pos;
308     return
309         p->buffer_opos != p->buffer_end
310         ? SOX_SUCCESS
311         : SOX_EOF;
312 }
313 
314 /*
315  * Function returning effect descriptor. This should be the only
316  * externally visible object.
317  */
lsx_speexdsp_effect_fn(void)318 const sox_effect_handler_t* lsx_speexdsp_effect_fn(void)
319 {
320   /*
321    * Effect descriptor.
322    * If no specific processing is needed for any of
323    * the 6 functions, then the function above can be deleted
324    * and NULL used in place of the its name below.
325    */
326   static sox_effect_handler_t descriptor = {
327     "speexdsp", 0, SOX_EFF_PREC | SOX_EFF_GAIN | SOX_EFF_ALPHA,
328     getopts, start, flow, drain, stop, NULL, sizeof(priv_t)
329   };
330   static char const * lines[] = {
331     "Uses the Speex DSP library to improve perceived sound quality.",
332     "If no options are specified, the -agc and -denoise features are enabled.",
333     "Options:",
334     "-agc [target_level]    Enable automatic gain control, and optionally specify a",
335     "                       target volume level from 1-100 (default is 100).",
336     "-denoise [max_dB]      Enable noise reduction, and optionally specify the max",
337     "                       attenuation (default is 15).",
338     "-dereverb              Enable reverb reduction.",
339     "-fps frames_per_second Specify the number of frames per second from 1-100",
340     "                       (default is 20).",
341     "-spf samples_per_frame Specify the number of samples per frame. Default is to",
342     "                       use the -fps setting.",
343   };
344   static char * usage;
345   descriptor.usage = lsx_usage_lines(&usage, lines, array_length(lines));
346   return &descriptor;
347 }
348 
349 #endif /* HAVE_SPEEXDSP */
350