qt: playlist: use item title if available
[vlc.git] / modules / audio_filter / resampler / soxr.c
1 /*****************************************************************************
2  * soxr.c: resampler/converter using The SoX Resampler library
3  *****************************************************************************
4  * Copyright (C) 2015 VLC authors, VideoLAN and VideoLabs
5  *
6  * Authors: Thomas Guillem <thomas@gllm.fr>
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_aout.h>
33 #include <vlc_filter.h>
34 #include <vlc_plugin.h>
35
36 #include <assert.h>
37 #include <math.h>
38 #include <soxr.h>
39
40 #define SOXR_QUALITY_TEXT N_( "Resampling quality" )
41 #define SOXR_QUALITY_LONGTEXT N_( "Resampling quality, from worst to best" )
42
43 static const int soxr_resampler_quality_vlclist[] = { 0, 1, 2, 3, 4 };
44 static const char *const soxr_resampler_quality_vlctext[] =
45 {
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"
51 };
52 static const soxr_datatype_t soxr_resampler_quality_list[] =
53 {
54     SOXR_QQ,
55     SOXR_LQ,
56     SOXR_MQ,
57     SOXR_HQ,
58     SOXR_VHQ
59 };
60 #define MAX_SOXR_QUALITY 4
61
62 static int OpenConverter( vlc_object_t * );
63 static int OpenResampler( vlc_object_t * );
64 static void Close( filter_t * );
65
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", 51 )
75     set_callback( OpenConverter )
76
77     add_submodule()
78     set_capability( "audio resampler", 51 )
79     set_callback( OpenResampler )
80     add_shortcut( "soxr" )
81 vlc_module_end ()
82
83 typedef struct
84 {
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     vlc_tick_t i_last_pts;
91 } filter_sys_t;
92
93 static block_t *Resample( filter_t *, block_t * );
94 static block_t *Drain( filter_t * );
95 static void     Flush( filter_t * );
96
97 static bool
98 SoXR_GetFormat( vlc_fourcc_t i_format, soxr_datatype_t *p_type )
99 {
100     switch( i_format )
101     {
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;
116     }
117 }
118
119 static int
120 Open( vlc_object_t *p_obj, bool b_change_ratio )
121 {
122     filter_t *p_filter = (filter_t *)p_obj;
123
124     /* Cannot remix */
125     if( p_filter->fmt_in.audio.i_channels != p_filter->fmt_out.audio.i_channels )
126         return VLC_EGENERIC;
127
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;
133
134     filter_sys_t *p_sys = calloc( 1, sizeof( filter_sys_t ) );
135     if( unlikely( p_sys == NULL ) )
136         return VLC_ENOMEM;
137
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;
148
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 )
159     {
160         msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) );
161         free( p_sys );
162         return VLC_EGENERIC;
163     }
164
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 )
169     {
170         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 )
174         {
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;
179         }
180         soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, 0 );
181     }
182
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 );
190
191     static const struct vlc_filter_operations filter_ops =
192     {
193         .filter_audio = Resample,
194         .drain_audio = Drain,
195         .flush = Flush,
196         .close = Close,
197     };
198     p_filter->ops = &filter_ops;
199     p_filter->p_sys = p_sys;
200     return VLC_SUCCESS;
201 }
202
203 static int
204 OpenResampler( vlc_object_t *p_obj )
205 {
206     filter_t *p_filter = (filter_t *)p_obj;
207
208     /* A resampler doesn't convert the format */
209     if( p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format )
210         return VLC_EGENERIC;
211     return Open( p_obj, true );
212 }
213
214 static int
215 OpenConverter( vlc_object_t *p_obj )
216 {
217     filter_t *p_filter = (filter_t *)p_obj;
218
219     /* Don't use SoXR to convert format. Prefer to use converter/format.c that
220      * has better performances */
221     if( p_filter->fmt_in.audio.i_rate == p_filter->fmt_out.audio.i_rate )
222         return VLC_EGENERIC;
223     return Open( p_obj, false );
224 }
225
226 static void
227 Close( filter_t *p_filter )
228 {
229     filter_sys_t *p_sys = p_filter->p_sys;
230
231     soxr_delete( p_sys->soxr );
232     if( p_sys->vr_soxr )
233         soxr_delete( p_sys->vr_soxr );
234
235     free( p_sys );
236 }
237
238 static block_t *
239 SoXR_Resample( filter_t *p_filter, soxr_t soxr, block_t *p_in, size_t i_olen )
240 {
241     filter_sys_t *p_sys = p_filter->p_sys;
242     size_t i_idone, i_odone;
243     const size_t i_oframesize = p_filter->fmt_out.audio.i_bytes_per_frame;
244     const size_t i_ilen = p_in ? p_in->i_nb_samples : 0;
245
246     block_t *p_out;
247     if( i_ilen >= i_olen )
248     {
249         i_olen = i_ilen;
250         p_out = p_in;
251     }
252     else
253     {
254         p_out = block_Alloc( i_olen * i_oframesize );
255         if( p_out == NULL )
256             goto error;
257     }
258
259     soxr_error_t error = soxr_process( soxr, p_in ? p_in->p_buffer : NULL,
260                                        i_ilen, &i_idone, p_out->p_buffer,
261                                        i_olen, &i_odone );
262     if( error )
263     {
264         msg_Err( p_filter, "soxr_process failed: %s", soxr_strerror( error ) );
265         block_Release( p_out );
266         goto error;
267     }
268     if( unlikely( i_idone < i_ilen ) )
269         msg_Err( p_filter, "lost %zd of %zd input frames",
270                  i_ilen - i_idone, i_idone);
271
272     p_out->i_buffer = i_odone * i_oframesize;
273     p_out->i_nb_samples = i_odone;
274     p_out->i_length = vlc_tick_from_samples(i_odone, p_filter->fmt_out.audio.i_rate);
275
276     if( p_in )
277     {
278         p_sys->i_last_olen = i_olen;
279         p_sys->last_soxr = soxr;
280     }
281     else
282     {
283         soxr_clear( soxr );
284         p_sys->i_last_olen = 0;
285         p_sys->last_soxr = NULL;
286     }
287
288 error:
289     if( p_in && p_out != p_in )
290         block_Release( p_in );
291
292     return p_out;
293 }
294
295 static size_t
296 SoXR_GetOutLen( size_t i_ilen, double f_ratio )
297 {
298     /* Processed output len might be a little bigger than expected. Indeed SoXR
299      * can hold a few samples to meet the need of the resampling filter. */
300     return lrint( ( i_ilen + 2 ) * f_ratio * 11. / 10. );
301 }
302
303 static block_t *
304 Resample( filter_t *p_filter, block_t *p_in )
305 {
306     filter_sys_t *p_sys = p_filter->p_sys;
307     const vlc_tick_t i_pts = p_in->i_pts;
308
309     if( p_sys->vr_soxr )
310     {
311         /* "audio resampler" with variable ratio: use the fixed resampler when
312          * the ratio is the same than the fixed one, otherwise use the variable
313          * resampler. */
314
315         soxr_t soxr;
316         block_t *p_flushed_out = NULL, *p_out = NULL;
317         const double f_ratio = p_filter->fmt_out.audio.i_rate
318                              / (double) p_filter->fmt_in.audio.i_rate;
319         size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples,
320             f_ratio > p_sys->f_fixed_ratio ? f_ratio : p_sys->f_fixed_ratio );
321
322         if( f_ratio != p_sys->f_fixed_ratio )
323         {
324             /* using variable resampler */
325             soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, 0 /* instant change */ );
326             soxr = p_sys->vr_soxr;
327         }
328         else if( f_ratio == 1.0f )
329         {
330             /* not using any resampler */
331             soxr = NULL;
332             p_out = p_in;
333         }
334         else
335         {
336             /* using fixed resampler */
337             soxr = p_sys->soxr;
338         }
339
340         /* If the new soxr is different than the last one, flush it */
341         if( p_sys->last_soxr && soxr != p_sys->last_soxr && p_sys->i_last_olen )
342         {
343             p_flushed_out = SoXR_Resample( p_filter, p_sys->last_soxr,
344                                            NULL, p_sys->i_last_olen );
345             if( soxr )
346                 msg_Dbg( p_filter, "Using '%s' engine", soxr_engine( soxr ) );
347         }
348
349         if( soxr )
350         {
351             assert( !p_out );
352             p_out = SoXR_Resample( p_filter, soxr, p_in, i_olen );
353             if( !p_out )
354                 goto error;
355         }
356
357         if( p_flushed_out )
358         {
359             /* Prepend the flushed output data to p_out */
360             const unsigned i_nb_samples = p_flushed_out->i_nb_samples
361                                         + p_out->i_nb_samples;
362
363             block_ChainAppend( &p_flushed_out, p_out );
364             p_out = block_ChainGather( p_flushed_out );
365             if( !p_out )
366                 goto error;
367             p_out->i_nb_samples = i_nb_samples;
368         }
369         p_out->i_pts = i_pts;
370         return p_out;
371     }
372     else
373     {
374         /* "audio converter" with fixed ratio */
375
376         const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples,
377                                               p_sys->f_fixed_ratio );
378         block_t *p_out = SoXR_Resample( p_filter, p_sys->soxr, p_in, i_olen );
379         if( p_out )
380             p_out->i_pts = i_pts;
381         return p_out;
382     }
383 error:
384     block_Release( p_in );
385     return NULL;
386 }
387
388 static block_t *
389 Drain( filter_t *p_filter )
390 {
391     filter_sys_t *p_sys = p_filter->p_sys;
392
393     if( p_sys->last_soxr && p_sys->i_last_olen )
394         return SoXR_Resample( p_filter, p_sys->last_soxr, NULL,
395                               p_sys->i_last_olen );
396     else
397         return NULL;
398 }
399
400 static void
401 Flush( filter_t *p_filter )
402 {
403     filter_sys_t *p_sys = p_filter->p_sys;
404
405     if( p_sys->last_soxr )
406     {
407         soxr_clear( p_sys->last_soxr );
408         p_sys->i_last_olen = 0;
409         p_sys->last_soxr = NULL;
410     }
411 }