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 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
33 #include <vlc_filter.h>
34 #include <vlc_plugin.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",
52 static const soxr_datatype_t soxr_resampler_quality_list
[] =
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
* );
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
)
78 set_capability( "audio resampler", 0 )
79 set_callbacks( OpenResampler
, Close
)
80 add_shortcut( "soxr" )
93 static block_t
*Resample( filter_t
*, block_t
* );
94 static block_t
*Drain( filter_t
* );
95 static void Flush( filter_t
* );
98 SoXR_GetFormat( vlc_fourcc_t i_format
, soxr_datatype_t
*p_type
)
103 *p_type
= SOXR_FLOAT64_I
;
106 *p_type
= SOXR_FLOAT32_I
;
109 *p_type
= SOXR_INT32_I
;
112 *p_type
= SOXR_INT16_I
;
120 Open( vlc_object_t
*p_obj
, bool b_change_ratio
)
122 filter_t
*p_filter
= (filter_t
*)p_obj
;
125 if( p_filter
->fmt_in
.audio
.i_channels
!= p_filter
->fmt_out
.audio
.i_channels
)
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
) )
134 filter_sys_t
*p_sys
= calloc( 1, sizeof( struct filter_sys_t
) );
135 if( unlikely( p_sys
== NULL
) )
139 int64_t i_vlc_q
= var_InheritInteger( p_obj
, "soxr-resampler-quality" );
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
;
152 soxr_io_spec_t io_spec
= soxr_io_spec( i_itype
, i_otype
);
154 soxr_quality_spec_t q_spec
= soxr_quality_spec( i_recipe
, 0 );
156 p_sys
->soxr
= soxr_create( 1, f_ratio
, i_channels
,
157 &error
, &io_spec
, &q_spec
, NULL
);
160 msg_Err( p_filter
, "soxr_create failed: %s", soxr_strerror( error
) );
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
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
);
175 msg_Err( p_filter
, "soxr_create failed: %s", soxr_strerror( error
) );
176 soxr_delete( p_sys
->soxr
);
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
;
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
)
206 return Open( p_obj
, true );
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
)
218 return Open( p_obj
, false );
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
);
229 soxr_delete( p_sys
->vr_soxr
);
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
,
250 msg_Err( p_filter
, "soxr_process failed: %s", soxr_strerror( error
) );
251 block_Release( p_out
);
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
;
264 p_sys
->i_last_olen
= i_olen
;
265 p_sys
->last_soxr
= soxr
;
270 p_sys
->i_last_olen
= 0;
271 p_sys
->last_soxr
= NULL
;
275 if( p_in
&& p_out
!= p_in
)
276 block_Release( p_in
);
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. );
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
;
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
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 */
321 /* using fixed resampler */
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
);
331 msg_Dbg( p_filter
, "Using '%s' engine", soxr_engine( soxr
) );
337 p_out
= SoXR_Resample( p_filter
, soxr
, p_in
, i_olen
);
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
);
352 p_out
->i_nb_samples
= i_nb_samples
;
354 p_out
->i_pts
= i_pts
;
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
);
365 p_out
->i_pts
= i_pts
;
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
);
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
;