id3: fix COMM frame handling
[sox.git] / src / speexdsp.c
blob309e5604010b12e3d7ed69b577fdb2c3e55b31a1
1 /* libSoX effect: SpeexDsp effect to apply processing from libspeexdsp.
3 * Copyright 1999-2009 Chris Bagwell And SoX Contributors
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.
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.
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
20 #include "sox_i.h"
22 #ifdef HAVE_SPEEXDSP
24 #include <speex/speex_types.h>
25 #include <speex/speex_preprocess.h>
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;
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)
49 *pParam = default_val;
50 if (*pArgc > 1 && (*pArgv)[1][0] != '-')
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;
57 --*pArgc;
58 ++*pArgv;
61 return 1;
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.
69 static int getopts(sox_effect_t* effp, int argc, char** argv)
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;
76 for (argc--, argv++; argc; argc--, argv++)
78 if (!strcasecmp("-agc", argv[0]))
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))
84 lsx_fail("Invalid argument \"%s\" to -agc parameter - expected number from 0 to 100.", argv[1]);
85 return lsx_usage(effp);
88 else if (!strcasecmp("-denoise", argv[0]))
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))
94 lsx_fail("Invalid argument \"%s\" to -denoise parameter - expected number from 0 to 100.", argv[1]);
95 return lsx_usage(effp);
98 else if (!strcasecmp("-dereverb", argv[0]))
100 p->dereverb = 1;
102 else if (!strcasecmp("-spf", argv[0]))
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)
108 lsx_fail("Invalid argument \"%s\" to -spf parameter - expected positive number.", argv[1]);
109 return lsx_usage(effp);
112 else if (!strcasecmp("-fps", argv[0]))
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)
119 lsx_fail("Invalid argument \"%s\" to -fps parameter - expected number from 1 to 100.", argv[1]);
120 return lsx_usage(effp);
123 else
125 lsx_fail("Invalid parameter \"%s\".", argv[0]);
126 return lsx_usage(effp);
130 if (!p->frames_per_second)
131 p->frames_per_second = fpsDefault;
133 if (!p->agc && !p->denoise && !p->dereverb)
135 lsx_report("No features specified. Enabling default settings \"-agc %u -denoise %u\".", agcDefault, denoiseDefault);
136 p->agc = agcDefault;
137 p->denoise = denoiseDefault;
140 return SOX_SUCCESS;
144 * Do anything required when you stop reading samples.
146 static int stop(sox_effect_t* effp)
148 priv_t* p = (priv_t*)effp->priv;
150 if (p->sps)
152 speex_preprocess_state_destroy(p->sps);
153 p->sps = NULL;
156 if (p->buffer)
158 free(p->buffer);
159 p->buffer = NULL;
162 return SOX_SUCCESS;
166 * Prepare processing.
167 * Do all initializations.
169 static int start(sox_effect_t* effp)
171 priv_t* p = (priv_t*)effp->priv;
172 int result = SOX_SUCCESS;
173 spx_int32_t int_val;
174 float float_val;
176 if (p->samples_per_frame)
178 p->buffer_end = p->samples_per_frame;
180 else
182 p->buffer_end = effp->in_signal.rate / p->frames_per_second;
183 if (!p->buffer_end)
185 lsx_fail("frames_per_second too large for the current sample rate.");
186 return SOX_EOF;
190 p->buffer_opos = p->buffer_end;
191 effp->out_signal.precision = 16;
193 p->buffer = lsx_malloc(p->buffer_end * sizeof(p->buffer[0]));
194 if (!p->buffer)
196 result = SOX_ENOMEM;
197 goto Done;
200 p->sps = speex_preprocess_state_init((int)p->buffer_end, (int)(effp->in_signal.rate + .5));
201 if (!p->sps)
203 lsx_fail("Failed to initialize preprocessor DSP.");
204 result = SOX_EOF;
205 goto Done;
208 int_val = p->agc ? 1 : 2;
209 speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_AGC, &int_val);
210 if (p->agc)
212 float_val = p->agc * 327.68f;
213 speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_AGC_LEVEL, &float_val);
216 int_val = p->denoise ? 1 : 2;
217 speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_DENOISE, &int_val);
218 if (p->denoise)
220 int_val = -(spx_int32_t)p->denoise;
221 speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &int_val);
224 int_val = p->dereverb ? 1 : 2;
225 speex_preprocess_ctl(p->sps, SPEEX_PREPROCESS_SET_DEREVERB, &int_val);
227 Done:
228 if (result != SOX_SUCCESS)
229 stop(effp);
231 return result;
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.
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)
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;
254 for (;;)
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. */
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. */
270 speex_preprocess_run(p->sps, p->buffer);
271 p->buffer_ipos = 0;
272 p->buffer_opos = 0;
275 *isamp = ibuf_pos;
276 *osamp = obuf_pos;
277 return SOX_SUCCESS;
281 * Drain out remaining samples if the effect generates any.
283 static int drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp)
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;
291 /* Input that hasn't been processed yet? */
292 if (p->buffer_ipos != 0)
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;
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);
307 *osamp = obuf_pos;
308 return
309 p->buffer_opos != p->buffer_end
310 ? SOX_SUCCESS
311 : SOX_EOF;
315 * Function returning effect descriptor. This should be the only
316 * externally visible object.
318 const sox_effect_handler_t* lsx_speexdsp_effect_fn(void)
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.
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)
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.",
344 static char * usage;
345 descriptor.usage = lsx_usage_lines(&usage, lines, array_length(lines));
346 return &descriptor;
349 #endif /* HAVE_SPEEXDSP */