Resampler: don't translate complex technical strings
[vlc.git] / modules / audio_filter / resampler / soxr.c
blob2db9921a5a7617c91d01ac06841c3f5aba8a7f27
1 /*****************************************************************************
2 * soxr.c: resampler/converter using The SoX Resampler library
3 *****************************************************************************
4 * Copyright (C) 2015 VLC authors, VideoLAN and VideoLabs
6 * Authors: Thomas Guillem <thomas@gllm.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_aout.h>
33 #include <vlc_filter.h>
34 #include <vlc_plugin.h>
36 #include <assert.h>
37 #include <math.h>
38 #include <soxr.h>
40 #define SOXR_QUALITY_TEXT N_( "Resampling quality" )
41 #define SOXR_QUALITY_LONGTEXT N_( "Resampling quality, from worst to best" )
43 static const int soxr_resampler_quality_vlclist[] = { 0, 1, 2, 3, 4 };
44 static const char *const soxr_resampler_quality_vlctext[] =
46 "Quick cubic interpolation",
47 "Low 16-bit with larger roll-off",
48 "Medium 16-bit with medium roll-off",
49 "High quality",
50 "Very high quality"
52 static const soxr_datatype_t soxr_resampler_quality_list[] =
54 SOXR_QQ,
55 SOXR_LQ,
56 SOXR_MQ,
57 SOXR_HQ,
58 SOXR_VHQ
60 #define MAX_SOXR_QUALITY 4
62 static int OpenConverter( vlc_object_t * );
63 static int OpenResampler( vlc_object_t * );
64 static void Close( vlc_object_t * );
66 vlc_module_begin ()
67 set_shortname( N_("SoX Resampler") )
68 set_category( CAT_AUDIO )
69 set_subcategory( SUBCAT_AUDIO_RESAMPLER )
70 add_integer( "soxr-resampler-quality", 2,
71 SOXR_QUALITY_TEXT, NULL, true )
72 change_integer_list( soxr_resampler_quality_vlclist,
73 soxr_resampler_quality_vlctext )
74 set_capability ( "audio converter", 0 )
75 set_callbacks( OpenConverter, Close )
77 add_submodule()
78 set_capability( "audio resampler", 0 )
79 set_callbacks( OpenResampler, Close )
80 add_shortcut( "soxr" )
81 vlc_module_end ()
83 struct filter_sys_t
85 soxr_t soxr;
86 soxr_t vr_soxr;
87 soxr_t last_soxr;
88 double f_fixed_ratio;
89 size_t i_last_olen;
90 mtime_t i_last_pts;
93 static block_t *Resample( filter_t *, block_t * );
94 static block_t *Drain( filter_t * );
95 static void Flush( filter_t * );
97 static bool
98 SoXR_GetFormat( vlc_fourcc_t i_format, soxr_datatype_t *p_type )
100 switch( i_format )
102 case VLC_CODEC_FL64:
103 *p_type = SOXR_FLOAT64_I;
104 return true;
105 case VLC_CODEC_FL32:
106 *p_type = SOXR_FLOAT32_I;
107 return true;
108 case VLC_CODEC_S32N:
109 *p_type = SOXR_INT32_I;
110 return true;
111 case VLC_CODEC_S16N:
112 *p_type = SOXR_INT16_I;
113 return true;
114 default:
115 return false;
119 static int
120 Open( vlc_object_t *p_obj, bool b_change_ratio )
122 filter_t *p_filter = (filter_t *)p_obj;
124 /* Cannot remix */
125 if( p_filter->fmt_in.audio.i_channels != p_filter->fmt_out.audio.i_channels )
126 return VLC_EGENERIC;
128 /* Get SoXR input/output format */
129 soxr_datatype_t i_itype, i_otype;
130 if( !SoXR_GetFormat( p_filter->fmt_in.audio.i_format, &i_itype )
131 || !SoXR_GetFormat( p_filter->fmt_out.audio.i_format, &i_otype ) )
132 return VLC_EGENERIC;
134 filter_sys_t *p_sys = calloc( 1, sizeof( struct filter_sys_t ) );
135 if( unlikely( p_sys == NULL ) )
136 return VLC_ENOMEM;
138 /* Setup SoXR */
139 int64_t i_vlc_q = var_InheritInteger( p_obj, "soxr-resampler-quality" );
140 if( i_vlc_q < 0 )
141 i_vlc_q = 0;
142 else if( i_vlc_q > MAX_SOXR_QUALITY )
143 i_vlc_q = MAX_SOXR_QUALITY;
144 const unsigned long i_recipe = soxr_resampler_quality_list[i_vlc_q];
145 const unsigned i_channels = p_filter->fmt_in.audio.i_channels;
146 const double f_ratio = p_filter->fmt_out.audio.i_rate
147 / (double) p_filter->fmt_in.audio.i_rate;
149 p_sys->f_fixed_ratio = f_ratio;
150 soxr_error_t error;
151 /* IO spec */
152 soxr_io_spec_t io_spec = soxr_io_spec( i_itype, i_otype );
153 /* Quality spec */
154 soxr_quality_spec_t q_spec = soxr_quality_spec( i_recipe, 0 );
155 /* Create SoXR */
156 p_sys->soxr = soxr_create( 1, f_ratio, i_channels,
157 &error, &io_spec, &q_spec, NULL );
158 if( error )
160 msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) );
161 free( p_sys );
162 return VLC_EGENERIC;
165 /* Create a 'variable-rate' SoXR if needed: it is slower than the fixed
166 * one, but it will be only used when the input rate is changing (to catch
167 * up a delay). */
168 if( b_change_ratio )
170 soxr_quality_spec_t q_spec = soxr_quality_spec( SOXR_LQ, SOXR_VR );
171 p_sys->vr_soxr = soxr_create( 1, f_ratio, i_channels,
172 &error, &io_spec, &q_spec, NULL );
173 if( error )
175 msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) );
176 soxr_delete( p_sys->soxr );
177 free( p_sys );
178 return VLC_EGENERIC;
180 soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, 0 );
183 msg_Dbg( p_filter, "Using SoX Resampler with '%s' engine and '%s' quality "
184 "to convert %4.4s/%dHz to %4.4s/%dHz.",
185 soxr_engine( p_sys->soxr ), soxr_resampler_quality_vlctext[i_vlc_q],
186 (const char *)&p_filter->fmt_in.audio.i_format,
187 p_filter->fmt_in.audio.i_rate,
188 (const char *)&p_filter->fmt_out.audio.i_format,
189 p_filter->fmt_out.audio.i_rate );
191 p_filter->p_sys = p_sys;
192 p_filter->pf_audio_filter = Resample;
193 p_filter->pf_flush = Flush;
194 p_filter->pf_audio_drain = Drain;
195 return VLC_SUCCESS;
198 static int
199 OpenResampler( vlc_object_t *p_obj )
201 filter_t *p_filter = (filter_t *)p_obj;
203 /* A resampler doesn't convert the format */
204 if( p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format )
205 return VLC_EGENERIC;
206 return Open( p_obj, true );
209 static int
210 OpenConverter( vlc_object_t *p_obj )
212 filter_t *p_filter = (filter_t *)p_obj;
214 /* Don't use SoXR to convert format. Prefer to use converter/format.c that
215 * has better performances */
216 if( p_filter->fmt_in.audio.i_rate == p_filter->fmt_out.audio.i_rate )
217 return VLC_EGENERIC;
218 return Open( p_obj, false );
221 static void
222 Close( vlc_object_t *p_obj )
224 filter_t *p_filter = (filter_t *)p_obj;
225 filter_sys_t *p_sys = p_filter->p_sys;
227 soxr_delete( p_sys->soxr );
228 if( p_sys->vr_soxr )
229 soxr_delete( p_sys->vr_soxr );
231 free( p_sys );
234 static block_t *
235 SoXR_Resample( filter_t *p_filter, soxr_t soxr, block_t *p_in, size_t i_olen )
237 filter_sys_t *p_sys = p_filter->p_sys;
238 size_t i_idone, i_odone;
239 const size_t i_oframesize = p_filter->fmt_out.audio.i_bytes_per_frame;
240 const size_t i_ilen = p_in ? p_in->i_nb_samples : 0;
242 block_t *p_out = i_ilen >= i_olen ? p_in
243 : block_Alloc( i_olen * i_oframesize );
245 soxr_error_t error = soxr_process( soxr, p_in ? p_in->p_buffer : NULL,
246 i_ilen, &i_idone, p_out->p_buffer,
247 i_olen, &i_odone );
248 if( error )
250 msg_Err( p_filter, "soxr_process failed: %s", soxr_strerror( error ) );
251 block_Release( p_out );
252 goto error;
254 if( unlikely( i_idone < i_ilen ) )
255 msg_Err( p_filter, "lost %zd of %zd input frames",
256 i_ilen - i_idone, i_idone);
258 p_out->i_buffer = i_odone * i_oframesize;
259 p_out->i_nb_samples = i_odone;
260 p_out->i_length = i_odone * CLOCK_FREQ / p_filter->fmt_out.audio.i_rate;
262 if( p_in )
264 p_sys->i_last_olen = i_olen;
265 p_sys->last_soxr = soxr;
267 else
269 soxr_clear( soxr );
270 p_sys->i_last_olen = 0;
271 p_sys->last_soxr = NULL;
274 error:
275 if( p_in && p_out != p_in )
276 block_Release( p_in );
278 return p_out;
281 static size_t
282 SoXR_GetOutLen( size_t i_ilen, double f_ratio )
284 /* Processed output len might be a little bigger than expected. Indeed SoXR
285 * can hold a few samples to meet the need of the resampling filter. */
286 return lrint( ( i_ilen + 2 ) * f_ratio * 11. / 10. );
289 static block_t *
290 Resample( filter_t *p_filter, block_t *p_in )
292 filter_sys_t *p_sys = p_filter->p_sys;
293 const mtime_t i_pts = p_in->i_pts;
295 if( p_sys->vr_soxr )
297 /* "audio resampler" with variable ratio: use the fixed resampler when
298 * the ratio is the same than the fixed one, otherwise use the variable
299 * resampler. */
301 soxr_t soxr;
302 block_t *p_flushed_out = NULL, *p_out = NULL;
303 const double f_ratio = p_filter->fmt_out.audio.i_rate
304 / (double) p_filter->fmt_in.audio.i_rate;
305 const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples, f_ratio );
307 if( f_ratio != p_sys->f_fixed_ratio )
309 /* using variable resampler */
310 soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, i_olen );
311 soxr = p_sys->vr_soxr;
313 else if( f_ratio == 1.0f )
315 /* not using any resampler */
316 soxr = NULL;
317 p_out = p_in;
319 else
321 /* using fixed resampler */
322 soxr = p_sys->soxr;
325 /* If the new soxr is different than the last one, flush it */
326 if( p_sys->last_soxr && soxr != p_sys->last_soxr && p_sys->i_last_olen )
328 p_flushed_out = SoXR_Resample( p_filter, p_sys->last_soxr,
329 NULL, p_sys->i_last_olen );
330 if( soxr )
331 msg_Dbg( p_filter, "Using '%s' engine", soxr_engine( soxr ) );
334 if( soxr )
336 assert( !p_out );
337 p_out = SoXR_Resample( p_filter, soxr, p_in, i_olen );
338 if( !p_out )
339 return NULL;
342 if( p_flushed_out )
344 /* Prepend the flushed output data to p_out */
345 const unsigned i_nb_samples = p_flushed_out->i_nb_samples
346 + p_out->i_nb_samples;
348 block_ChainAppend( &p_flushed_out, p_out );
349 p_out = block_ChainGather( p_flushed_out );
350 if( !p_out )
351 return NULL;
352 p_out->i_nb_samples = i_nb_samples;
354 p_out->i_pts = i_pts;
355 return p_out;
357 else
359 /* "audio converter" with fixed ratio */
361 const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples,
362 p_sys->f_fixed_ratio );
363 block_t *p_out = SoXR_Resample( p_filter, p_sys->soxr, p_in, i_olen );
364 if( p_out )
365 p_out->i_pts = i_pts;
366 return p_out;
370 static block_t *
371 Drain( filter_t *p_filter )
373 filter_sys_t *p_sys = p_filter->p_sys;
375 if( p_sys->last_soxr && p_sys->i_last_olen )
376 return SoXR_Resample( p_filter, p_sys->last_soxr, NULL,
377 p_sys->i_last_olen );
378 else
379 return NULL;
382 static void
383 Flush( filter_t *p_filter )
385 filter_sys_t *p_sys = p_filter->p_sys;
387 if( p_sys->last_soxr )
389 soxr_clear( p_sys->last_soxr );
390 p_sys->i_last_olen = 0;
391 p_sys->last_soxr = NULL;