1 /*****************************************************************************
2  * libvlc_audio.c: New libvlc audio control API
3  *****************************************************************************
4  * Copyright (C) 2006 VLC authors and VideoLAN
5  * $Id: 877666473a15ad8687070fe064243b3049d874bf $
6  *
7  * Authors: Filippo Carone <filippo@carone.org>
8  *          Jean-Paul Saman <jpsaman _at_ m2x _dot_ nl>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 
29 #include <assert.h>
30 #include <math.h>
31 
32 #include <vlc/libvlc.h>
33 #include <vlc/libvlc_renderer_discoverer.h>
34 #include <vlc/libvlc_media.h>
35 #include <vlc/libvlc_media_player.h>
36 
37 #include <vlc_common.h>
38 #include <vlc_input.h>
39 #include <vlc_aout.h>
40 #include <vlc_modules.h>
41 
42 #include "libvlc_internal.h"
43 #include "media_player_internal.h"
44 
45 /*
46  * Remember to release the returned audio_output_t since it is locked at
47  * the end of this function.
48  */
GetAOut(libvlc_media_player_t * mp)49 static audio_output_t *GetAOut( libvlc_media_player_t *mp )
50 {
51     assert( mp != NULL );
52 
53     audio_output_t *p_aout = input_resource_HoldAout( mp->input.p_resource );
54     if( p_aout == NULL )
55         libvlc_printerr( "No active audio output" );
56     return p_aout;
57 }
58 
59 /*****************************************
60  * Get the list of available audio outputs
61  *****************************************/
62 libvlc_audio_output_t *
libvlc_audio_output_list_get(libvlc_instance_t * p_instance)63         libvlc_audio_output_list_get( libvlc_instance_t *p_instance )
64 {
65     size_t count;
66     module_t **module_list = module_list_get( &count );
67     libvlc_audio_output_t *list = NULL;
68 
69     for (size_t i = 0; i < count; i++)
70     {
71         module_t *module = module_list[i];
72 
73         if( !module_provides( module, "audio output" ) )
74             continue;
75 
76         libvlc_audio_output_t *item = malloc( sizeof( *item ) );
77         if( unlikely(item == NULL) )
78         {
79     error:
80             libvlc_printerr( "Not enough memory" );
81             libvlc_audio_output_list_release( list );
82             list = NULL;
83             break;
84         }
85 
86         item->psz_name = strdup( module_get_object( module ) );
87         item->psz_description = strdup( module_get_name( module, true ) );
88         if( unlikely(item->psz_name == NULL || item->psz_description == NULL) )
89         {
90             free( item->psz_name );
91             free( item->psz_description );
92             free( item );
93             goto error;
94         }
95         item->p_next = list;
96         list = item;
97     }
98     module_list_free( module_list );
99 
100     VLC_UNUSED( p_instance );
101     return list;
102 }
103 
104 /********************************************
105  * Free the list of available audio outputs
106  ***********************************************/
libvlc_audio_output_list_release(libvlc_audio_output_t * list)107 void libvlc_audio_output_list_release( libvlc_audio_output_t *list )
108 {
109     while( list != NULL )
110     {
111         libvlc_audio_output_t *next = list->p_next;
112 
113         free( list->psz_name );
114         free( list->psz_description );
115         free( list );
116         list = next;
117     }
118 }
119 
120 
121 /***********************
122  * Set the audio output.
123  ***********************/
libvlc_audio_output_set(libvlc_media_player_t * mp,const char * psz_name)124 int libvlc_audio_output_set( libvlc_media_player_t *mp, const char *psz_name )
125 {
126     char *value;
127 
128     if( !module_exists( psz_name )
129      || asprintf( &value, "%s,none", psz_name ) == -1 )
130         return -1;
131     var_SetString( mp, "aout", value );
132     free( value );
133 
134     /* Forget the existing audio output */
135     input_resource_ResetAout(mp->input.p_resource);
136 
137     /* Create a new audio output */
138     audio_output_t *aout = input_resource_GetAout(mp->input.p_resource);
139     if( aout != NULL )
140         input_resource_PutAout(mp->input.p_resource, aout);
141 
142     return 0;
143 }
144 
145 libvlc_audio_output_device_t *
libvlc_audio_output_device_enum(libvlc_media_player_t * mp)146 libvlc_audio_output_device_enum( libvlc_media_player_t *mp )
147 {
148     audio_output_t *aout = GetAOut( mp );
149     if( aout == NULL )
150         return NULL;
151 
152     libvlc_audio_output_device_t *list, **pp = &list;
153     char **values, **texts;
154 
155     int n = aout_DevicesList( aout, &values, &texts );
156     vlc_object_release( aout );
157     if( n < 0 )
158         goto err;
159 
160     for (int i = 0; i < n; i++)
161     {
162         libvlc_audio_output_device_t *item = malloc( sizeof(*item) );
163         if( unlikely(item == NULL) )
164         {
165             free( texts[i] );
166             free( values[i] );
167             continue;
168         }
169 
170         *pp = item;
171         pp = &item->p_next;
172         item->psz_device = values[i];
173         item->psz_description = texts[i];
174     }
175 
176     free( texts );
177     free( values );
178 err:
179     *pp = NULL;
180     return list;
181 }
182 
183 libvlc_audio_output_device_t *
libvlc_audio_output_device_list_get(libvlc_instance_t * p_instance,const char * aout)184 libvlc_audio_output_device_list_get( libvlc_instance_t *p_instance,
185                                      const char *aout )
186 {
187     char varname[32];
188     if( (size_t)snprintf( varname, sizeof(varname), "%s-audio-device", aout )
189                                                            >= sizeof(varname) )
190         return NULL;
191 
192     if( config_GetType(varname) != VLC_VAR_STRING )
193         return NULL;
194 
195     libvlc_audio_output_device_t *list = NULL, **pp = &list;
196     char **values, **texts;
197     ssize_t count = config_GetPszChoices( VLC_OBJECT(p_instance->p_libvlc_int),
198                                           varname, &values, &texts );
199     for( ssize_t i = 0; i < count; i++ )
200     {
201         libvlc_audio_output_device_t *item = malloc( sizeof(*item) );
202         if( unlikely(item == NULL) )
203             break;
204 
205         *pp = item;
206         pp = &item->p_next;
207         item->psz_device = values[i];
208         item->psz_description = texts[i];
209     }
210 
211     *pp = NULL;
212     free( texts );
213     free( values );
214     (void) p_instance;
215     return list;
216 }
217 
libvlc_audio_output_device_list_release(libvlc_audio_output_device_t * l)218 void libvlc_audio_output_device_list_release( libvlc_audio_output_device_t *l )
219 {
220     while( l != NULL )
221     {
222         libvlc_audio_output_device_t *next = l->p_next;
223 
224         free( l->psz_description );
225         free( l->psz_device );
226         free( l );
227         l = next;
228     }
229 }
230 
libvlc_audio_output_device_count(libvlc_instance_t * p_instance,const char * psz_audio_output)231 int libvlc_audio_output_device_count( libvlc_instance_t *p_instance,
232                                       const char *psz_audio_output )
233 {
234     (void) p_instance; (void) psz_audio_output;
235     return 0;
236 }
237 
libvlc_audio_output_device_longname(libvlc_instance_t * p_instance,const char * psz_audio_output,int i_device)238 char *libvlc_audio_output_device_longname( libvlc_instance_t *p_instance,
239                                            const char *psz_audio_output,
240                                            int i_device )
241 {
242     (void) p_instance; (void) psz_audio_output; (void) i_device;
243     return NULL;
244 }
245 
libvlc_audio_output_device_id(libvlc_instance_t * p_instance,const char * psz_audio_output,int i_device)246 char *libvlc_audio_output_device_id( libvlc_instance_t *p_instance,
247                                      const char *psz_audio_output,
248                                      int i_device )
249 {
250     (void) p_instance; (void) psz_audio_output; (void) i_device;
251     return NULL;
252 }
253 
254 /*****************************
255  * Set device for using
256  *****************************/
libvlc_audio_output_device_set(libvlc_media_player_t * mp,const char * module,const char * devid)257 void libvlc_audio_output_device_set( libvlc_media_player_t *mp,
258                                      const char *module, const char *devid )
259 {
260     if( devid == NULL )
261         return;
262 
263     if( module != NULL )
264     {
265         char *cfg_name;
266 
267         if( asprintf( &cfg_name, "%s-audio-device", module ) == -1 )
268             return;
269 
270         if( !var_Type( mp, cfg_name ) )
271             /* Don't recreate the same variable over and over and over... */
272             var_Create( mp, cfg_name, VLC_VAR_STRING );
273         var_SetString( mp, cfg_name, devid );
274         free( cfg_name );
275         return;
276     }
277 
278     audio_output_t *aout = GetAOut( mp );
279     if( aout == NULL )
280         return;
281 
282     aout_DeviceSet( aout, devid );
283     vlc_object_release( aout );
284 }
285 
libvlc_audio_output_device_get(libvlc_media_player_t * mp)286 char *libvlc_audio_output_device_get( libvlc_media_player_t *mp )
287 {
288     audio_output_t *aout = GetAOut( mp );
289     if( aout == NULL )
290         return NULL;
291 
292     char *devid = aout_DeviceGet( aout );
293 
294     vlc_object_release( aout );
295 
296     return devid;
297 }
298 
libvlc_audio_output_get_device_type(libvlc_media_player_t * mp)299 int libvlc_audio_output_get_device_type( libvlc_media_player_t *mp )
300 {
301     (void) mp;
302     return libvlc_AudioOutputDevice_Error;
303 }
304 
libvlc_audio_output_set_device_type(libvlc_media_player_t * mp,int device_type)305 void libvlc_audio_output_set_device_type( libvlc_media_player_t *mp,
306                                           int device_type )
307 {
308     (void) mp; (void) device_type;
309 }
310 
libvlc_audio_toggle_mute(libvlc_media_player_t * mp)311 void libvlc_audio_toggle_mute( libvlc_media_player_t *mp )
312 {
313     int mute = libvlc_audio_get_mute( mp );
314     if( mute != -1 )
315         libvlc_audio_set_mute( mp, !mute );
316 }
317 
libvlc_audio_get_mute(libvlc_media_player_t * mp)318 int libvlc_audio_get_mute( libvlc_media_player_t *mp )
319 {
320     int mute = -1;
321 
322     audio_output_t *aout = GetAOut( mp );
323     if( aout != NULL )
324     {
325         mute = aout_MuteGet( aout );
326         vlc_object_release( aout );
327     }
328     return mute;
329 }
330 
libvlc_audio_set_mute(libvlc_media_player_t * mp,int mute)331 void libvlc_audio_set_mute( libvlc_media_player_t *mp, int mute )
332 {
333     audio_output_t *aout = GetAOut( mp );
334     if( aout != NULL )
335     {
336         mute = aout_MuteSet( aout, mute );
337         vlc_object_release( aout );
338     }
339 }
340 
libvlc_audio_get_volume(libvlc_media_player_t * mp)341 int libvlc_audio_get_volume( libvlc_media_player_t *mp )
342 {
343     int volume = -1;
344 
345     audio_output_t *aout = GetAOut( mp );
346     if( aout != NULL )
347     {
348         float vol = aout_VolumeGet( aout );
349         vlc_object_release( aout );
350         volume = lroundf( vol * 100.f );
351     }
352     return volume;
353 }
354 
libvlc_audio_set_volume(libvlc_media_player_t * mp,int volume)355 int libvlc_audio_set_volume( libvlc_media_player_t *mp, int volume )
356 {
357     float vol = volume / 100.f;
358     if (!isgreaterequal(vol, 0.f))
359     {
360         libvlc_printerr( "Volume out of range" );
361         return -1;
362     }
363 
364     int ret = -1;
365     audio_output_t *aout = GetAOut( mp );
366     if( aout != NULL )
367     {
368         ret = aout_VolumeSet( aout, vol );
369         vlc_object_release( aout );
370     }
371     return ret;
372 }
373 
374 /*****************************************************************************
375  * libvlc_audio_get_track_count : Get the number of available audio tracks
376  *****************************************************************************/
libvlc_audio_get_track_count(libvlc_media_player_t * p_mi)377 int libvlc_audio_get_track_count( libvlc_media_player_t *p_mi )
378 {
379     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
380     int i_track_count;
381 
382     if( !p_input_thread )
383         return -1;
384 
385     i_track_count = var_CountChoices( p_input_thread, "audio-es" );
386 
387     vlc_object_release( p_input_thread );
388     return i_track_count;
389 }
390 
391 /*****************************************************************************
392  * libvlc_audio_get_track_description : Get the description of available audio tracks
393  *****************************************************************************/
394 libvlc_track_description_t *
libvlc_audio_get_track_description(libvlc_media_player_t * p_mi)395         libvlc_audio_get_track_description( libvlc_media_player_t *p_mi )
396 {
397     return libvlc_get_track_description( p_mi, "audio-es" );
398 }
399 
400 /*****************************************************************************
401  * libvlc_audio_get_track : Get the current audio track
402  *****************************************************************************/
libvlc_audio_get_track(libvlc_media_player_t * p_mi)403 int libvlc_audio_get_track( libvlc_media_player_t *p_mi )
404 {
405     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
406     if( !p_input_thread )
407         return -1;
408 
409     int id = var_GetInteger( p_input_thread, "audio-es" );
410     vlc_object_release( p_input_thread );
411     return id;
412 }
413 
414 /*****************************************************************************
415  * libvlc_audio_set_track : Set the current audio track
416  *****************************************************************************/
libvlc_audio_set_track(libvlc_media_player_t * p_mi,int i_track)417 int libvlc_audio_set_track( libvlc_media_player_t *p_mi, int i_track )
418 {
419     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
420     vlc_value_t val_list;
421     int i_ret = -1;
422 
423     if( !p_input_thread )
424         return -1;
425 
426     var_Change( p_input_thread, "audio-es", VLC_VAR_GETCHOICES, &val_list, NULL );
427     for( int i = 0; i < val_list.p_list->i_count; i++ )
428     {
429         if( i_track == val_list.p_list->p_values[i].i_int )
430         {
431             if( var_SetInteger( p_input_thread, "audio-es", i_track ) < 0 )
432                 break;
433             i_ret = 0;
434             goto end;
435         }
436     }
437     libvlc_printerr( "Track identifier not found" );
438 end:
439     var_FreeList( &val_list, NULL );
440     vlc_object_release( p_input_thread );
441     return i_ret;
442 }
443 
444 /*****************************************************************************
445  * libvlc_audio_get_channel : Get the current audio channel
446  *****************************************************************************/
libvlc_audio_get_channel(libvlc_media_player_t * mp)447 int libvlc_audio_get_channel( libvlc_media_player_t *mp )
448 {
449     audio_output_t *p_aout = GetAOut( mp );
450     if( !p_aout )
451         return 0;
452 
453     int val = var_GetInteger( p_aout, "stereo-mode" );
454     vlc_object_release( p_aout );
455     return val;
456 }
457 
458 /*****************************************************************************
459  * libvlc_audio_set_channel : Set the current audio channel
460  *****************************************************************************/
libvlc_audio_set_channel(libvlc_media_player_t * mp,int channel)461 int libvlc_audio_set_channel( libvlc_media_player_t *mp, int channel )
462 {
463     audio_output_t *p_aout = GetAOut( mp );
464     int ret = 0;
465 
466     if( !p_aout )
467         return -1;
468 
469     if( var_SetInteger( p_aout, "stereo-mode", channel ) < 0 )
470     {
471         libvlc_printerr( "Audio channel out of range" );
472         ret = -1;
473     }
474     vlc_object_release( p_aout );
475     return ret;
476 }
477 
478 /*****************************************************************************
479  * libvlc_audio_get_delay : Get the current audio delay
480  *****************************************************************************/
libvlc_audio_get_delay(libvlc_media_player_t * p_mi)481 int64_t libvlc_audio_get_delay( libvlc_media_player_t *p_mi )
482 {
483     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
484     int64_t val = 0;
485     if( p_input_thread != NULL )
486     {
487       val = var_GetInteger( p_input_thread, "audio-delay" );
488       vlc_object_release( p_input_thread );
489     }
490     return val;
491 }
492 
493 /*****************************************************************************
494  * libvlc_audio_set_delay : Set the current audio delay
495  *****************************************************************************/
libvlc_audio_set_delay(libvlc_media_player_t * p_mi,int64_t i_delay)496 int libvlc_audio_set_delay( libvlc_media_player_t *p_mi, int64_t i_delay )
497 {
498     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
499     int ret = 0;
500     if( p_input_thread != NULL )
501     {
502       var_SetInteger( p_input_thread, "audio-delay", i_delay );
503       vlc_object_release( p_input_thread );
504     }
505     else
506     {
507       ret = -1;
508     }
509     return ret;
510 }
511 
512 /*****************************************************************************
513  * libvlc_audio_equalizer_get_preset_count : Get the number of equalizer presets
514  *****************************************************************************/
libvlc_audio_equalizer_get_preset_count(void)515 unsigned libvlc_audio_equalizer_get_preset_count( void )
516 {
517     return NB_PRESETS;
518 }
519 
520 /*****************************************************************************
521  * libvlc_audio_equalizer_get_preset_name : Get the name for a preset
522  *****************************************************************************/
libvlc_audio_equalizer_get_preset_name(unsigned u_index)523 const char *libvlc_audio_equalizer_get_preset_name( unsigned u_index )
524 {
525     if ( u_index >= NB_PRESETS )
526         return NULL;
527 
528     return preset_list_text[ u_index ];
529 }
530 
531 /*****************************************************************************
532  * libvlc_audio_equalizer_get_band_count : Get the number of equalizer frequency bands
533  *****************************************************************************/
libvlc_audio_equalizer_get_band_count(void)534 unsigned libvlc_audio_equalizer_get_band_count( void )
535 {
536     return EQZ_BANDS_MAX;
537 }
538 
539 /*****************************************************************************
540  * libvlc_audio_equalizer_get_band_frequency : Get the frequency for a band
541  *****************************************************************************/
libvlc_audio_equalizer_get_band_frequency(unsigned u_index)542 float libvlc_audio_equalizer_get_band_frequency( unsigned u_index )
543 {
544     if ( u_index >= EQZ_BANDS_MAX )
545         return -1.f;
546 
547     return f_iso_frequency_table_10b[ u_index ];
548 }
549 
550 /*****************************************************************************
551  * libvlc_audio_equalizer_new : Create a new audio equalizer with zeroed values
552  *****************************************************************************/
libvlc_audio_equalizer_new(void)553 libvlc_equalizer_t *libvlc_audio_equalizer_new( void )
554 {
555     libvlc_equalizer_t *p_equalizer;
556     p_equalizer = malloc( sizeof( *p_equalizer ) );
557     if ( unlikely( p_equalizer == NULL ) )
558         return NULL;
559 
560     p_equalizer->f_preamp = 0.f;
561     for ( unsigned i = 0; i < EQZ_BANDS_MAX; i++ )
562         p_equalizer->f_amp[ i ] = 0.f;
563 
564     return p_equalizer;
565 }
566 
567 /*****************************************************************************
568  * libvlc_audio_equalizer_new_from_preset : Create a new audio equalizer based on a preset
569  *****************************************************************************/
libvlc_audio_equalizer_new_from_preset(unsigned u_index)570 libvlc_equalizer_t *libvlc_audio_equalizer_new_from_preset( unsigned u_index )
571 {
572     libvlc_equalizer_t *p_equalizer;
573 
574     if ( u_index >= NB_PRESETS )
575         return NULL;
576 
577     p_equalizer = malloc( sizeof( *p_equalizer ) );
578     if ( unlikely( p_equalizer == NULL ) )
579         return NULL;
580 
581     p_equalizer->f_preamp = eqz_preset_10b[ u_index ].f_preamp;
582 
583     for ( unsigned i = 0; i < EQZ_BANDS_MAX; i++ )
584         p_equalizer->f_amp[ i ] = eqz_preset_10b[ u_index ].f_amp[ i ];
585 
586     return p_equalizer;
587 }
588 
589 /*****************************************************************************
590  * libvlc_audio_equalizer_release : Release a previously created equalizer
591  *****************************************************************************/
libvlc_audio_equalizer_release(libvlc_equalizer_t * p_equalizer)592 void libvlc_audio_equalizer_release( libvlc_equalizer_t *p_equalizer )
593 {
594     free( p_equalizer );
595 }
596 
597 /*****************************************************************************
598  * libvlc_audio_equalizer_set_preamp : Set the preamp value for an equalizer
599  *****************************************************************************/
libvlc_audio_equalizer_set_preamp(libvlc_equalizer_t * p_equalizer,float f_preamp)600 int libvlc_audio_equalizer_set_preamp( libvlc_equalizer_t *p_equalizer, float f_preamp )
601 {
602     if( isnan(f_preamp) )
603         return -1;
604     if( f_preamp < -20.f )
605         f_preamp = -20.f;
606     else if( f_preamp > 20.f )
607         f_preamp = 20.f;
608 
609     p_equalizer->f_preamp = f_preamp;
610     return 0;
611 }
612 
613 /*****************************************************************************
614  * libvlc_audio_equalizer_get_preamp : Get the preamp value for an equalizer
615  *****************************************************************************/
libvlc_audio_equalizer_get_preamp(libvlc_equalizer_t * p_equalizer)616 float libvlc_audio_equalizer_get_preamp( libvlc_equalizer_t *p_equalizer )
617 {
618     return p_equalizer->f_preamp;
619 }
620 
621 /*****************************************************************************
622  * libvlc_audio_equalizer_set_amp_at_index : Set the amplification value for an equalizer band
623  *****************************************************************************/
libvlc_audio_equalizer_set_amp_at_index(libvlc_equalizer_t * p_equalizer,float f_amp,unsigned u_band)624 int libvlc_audio_equalizer_set_amp_at_index( libvlc_equalizer_t *p_equalizer, float f_amp, unsigned u_band )
625 {
626     if( u_band >= EQZ_BANDS_MAX || isnan(f_amp) )
627         return -1;
628 
629 
630     if( f_amp < -20.f )
631         f_amp = -20.f;
632     else if( f_amp > 20.f )
633         f_amp = 20.f;
634 
635     p_equalizer->f_amp[ u_band ] = f_amp;
636     return 0;
637 }
638 
639 /*****************************************************************************
640  * libvlc_audio_equalizer_get_amp_at_index : Get the amplification value for an equalizer band
641  *****************************************************************************/
libvlc_audio_equalizer_get_amp_at_index(libvlc_equalizer_t * p_equalizer,unsigned u_band)642 float libvlc_audio_equalizer_get_amp_at_index( libvlc_equalizer_t *p_equalizer, unsigned u_band )
643 {
644     if ( u_band >= EQZ_BANDS_MAX )
645         return nanf("");
646 
647     return p_equalizer->f_amp[ u_band ];
648 }
649