Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

headphone.c

00001 /*****************************************************************************
00002  * headphone.c : headphone virtual spatialization channel mixer module
00003  *               -> gives the feeling of a real room with a simple headphone
00004  *****************************************************************************
00005  * Copyright (C) 2002-2005 the VideoLAN team
00006  * $Id: headphone.c 13684 2005-12-11 21:27:01Z babal $
00007  *
00008  * Authors: Boris Dorès <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 /*****************************************************************************
00026  * Preamble
00027  *****************************************************************************/
00028 #include <stdlib.h>                                      /* malloc(), free() */
00029 #include <string.h>
00030 #include <math.h>                                        /* sqrt */
00031 
00032 #include <vlc/vlc.h>
00033 #include "audio_output.h"
00034 #include "aout_internal.h"
00035 
00036 /*****************************************************************************
00037  * Local prototypes
00038  *****************************************************************************/
00039 static int  Create    ( vlc_object_t * );
00040 static void Destroy   ( vlc_object_t * );
00041 
00042 static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
00043                         aout_buffer_t * );
00044 
00045 /*****************************************************************************
00046  * Module descriptor
00047  *****************************************************************************/
00048 #define MODULE_DESCRIPTION N_ ( \
00049      "This effect gives you the feeling that you are standing in a room " \
00050      "with a complete 7.1 speaker set when using only a headphone, " \
00051      "providing a more realistic sound experience. It should also be " \
00052      "more comfortable and less tiring when listening to music for " \
00053      "long periods of time.\nIt works with any source format from mono " \
00054      "to 7.1.")
00055 
00056 #define HEADPHONE_DIM_TEXT N_("Characteristic dimension")
00057 #define HEADPHONE_DIM_LONGTEXT N_( \
00058      "Distance between front left speaker and listener in meters.")
00059 
00060 #define HEADPHONE_COMPENSATE_TEXT N_("Compensate delay")
00061 #define HEADPHONE_COMPENSATE_LONGTEXT N_( \
00062      "The delay which is introduced by the physical algorithm may "\
00063      "sometimes be disturbing for the lipsync. In that case, turn "\
00064      "this on to compensate.")
00065 
00066 #define HEADPHONE_DOLBY_TEXT N_("No decoding of Dolby Surround")
00067 #define HEADPHONE_DOLBY_LONGTEXT N_( \
00068      "If this option is turned on (not recommended), Dolby Surround "\
00069      "encoded streams won't be decoded before being processed by this "\
00070      "filter.")
00071 
00072 vlc_module_begin();
00073     set_description( N_("Headphone channel mixer with virtual spatialization effect") );
00074     set_shortname( _("Headphone effect") );
00075     set_category( CAT_AUDIO );
00076     set_subcategory( SUBCAT_AUDIO_AFILTER );
00077 
00078     add_integer( "headphone-dim", 10, NULL, HEADPHONE_DIM_TEXT,
00079                  HEADPHONE_DIM_LONGTEXT, VLC_FALSE );
00080     add_bool( "headphone-compensate", 0, NULL, HEADPHONE_COMPENSATE_TEXT,
00081               HEADPHONE_COMPENSATE_LONGTEXT, VLC_TRUE );
00082     add_bool( "headphone-dolby", 0, NULL, HEADPHONE_DOLBY_TEXT,
00083               HEADPHONE_DOLBY_LONGTEXT, VLC_TRUE );
00084 
00085     set_capability( "audio filter", 0 );
00086     set_callbacks( Create, Destroy );
00087     add_shortcut( "headphone" );
00088 vlc_module_end();
00089 
00090 
00091 /*****************************************************************************
00092  * Internal data structures
00093  *****************************************************************************/
00094 struct atomic_operation_t
00095 {
00096     int i_source_channel_offset;
00097     int i_dest_channel_offset;
00098     unsigned int i_delay;/* in sample unit */
00099     double d_amplitude_factor;
00100 };
00101 
00102 struct aout_filter_sys_t
00103 {
00104     size_t i_overflow_buffer_size;/* in bytes */
00105     byte_t * p_overflow_buffer;
00106     unsigned int i_nb_atomic_operations;
00107     struct atomic_operation_t * p_atomic_operations;
00108 };
00109 
00110 /*****************************************************************************
00111  * Init: initialize internal data structures
00112  * and computes the needed atomic operations
00113  *****************************************************************************/
00114 /* x and z represent the coordinates of the virtual speaker
00115  *  relatively to the center of the listener's head, measured in meters :
00116  *
00117  *  left              right
00118  *Z
00119  *-
00120  *a          head
00121  *x
00122  *i
00123  *s
00124  *  rear left    rear right
00125  *
00126  *          x-axis
00127  *  */
00128 static void ComputeChannelOperations ( struct aout_filter_sys_t * p_data
00129         , unsigned int i_rate , unsigned int i_next_atomic_operation
00130         , int i_source_channel_offset , double d_x , double d_z
00131         , double d_compensation_length , double d_channel_amplitude_factor )
00132 {
00133     double d_c = 340; /*sound celerity (unit: m/s)*/
00134     double d_compensation_delay = (d_compensation_length-0.1) / d_c * i_rate;
00135 
00136     /* Left ear */
00137     p_data->p_atomic_operations[i_next_atomic_operation]
00138         .i_source_channel_offset = i_source_channel_offset;
00139     p_data->p_atomic_operations[i_next_atomic_operation]
00140         .i_dest_channel_offset = 0;/* left */
00141     p_data->p_atomic_operations[i_next_atomic_operation]
00142         .i_delay = (int)( sqrt( (-0.1-d_x)*(-0.1-d_x) + (0-d_z)*(0-d_z) )
00143                           / d_c * i_rate - d_compensation_delay );
00144     if ( d_x < 0 )
00145     {
00146         p_data->p_atomic_operations[i_next_atomic_operation]
00147             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
00148     }
00149     else if ( d_x > 0 )
00150     {
00151         p_data->p_atomic_operations[i_next_atomic_operation]
00152             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
00153     }
00154     else
00155     {
00156         p_data->p_atomic_operations[i_next_atomic_operation]
00157             .d_amplitude_factor = d_channel_amplitude_factor / 2;
00158     }
00159 
00160     /* Right ear */
00161     p_data->p_atomic_operations[i_next_atomic_operation + 1]
00162         .i_source_channel_offset = i_source_channel_offset;
00163     p_data->p_atomic_operations[i_next_atomic_operation + 1]
00164         .i_dest_channel_offset = 1;/* right */
00165     p_data->p_atomic_operations[i_next_atomic_operation + 1]
00166         .i_delay = (int)( sqrt( (0.1-d_x)*(0.1-d_x) + (0-d_z)*(0-d_z) )
00167                           / d_c * i_rate - d_compensation_delay );
00168     if ( d_x < 0 )
00169     {
00170         p_data->p_atomic_operations[i_next_atomic_operation + 1]
00171             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
00172     }
00173     else if ( d_x > 0 )
00174     {
00175         p_data->p_atomic_operations[i_next_atomic_operation + 1]
00176             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
00177     }
00178     else
00179     {
00180         p_data->p_atomic_operations[i_next_atomic_operation + 1]
00181             .d_amplitude_factor = d_channel_amplitude_factor / 2;
00182     }
00183 }
00184 
00185 static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
00186         , unsigned int i_nb_channels , uint32_t i_physical_channels
00187         , unsigned int i_rate )
00188 {
00189     double d_x = config_GetInt ( p_filter , "headphone-dim" );
00190     double d_z = d_x;
00191     double d_z_rear = -d_x/3;
00192     double d_min = 0;
00193     unsigned int i_next_atomic_operation;
00194     int i_source_channel_offset;
00195     unsigned int i;
00196 
00197     if ( p_data == NULL )
00198     {
00199         msg_Dbg ( p_filter, "passing a null pointer as argument" );
00200         return 0;
00201     }
00202 
00203     if ( config_GetInt ( p_filter , "headphone-compensate" ) )
00204     {
00205         /* minimal distance to any speaker */
00206         if ( i_physical_channels & AOUT_CHAN_REARCENTER )
00207         {
00208             d_min = d_z_rear;
00209         }
00210         else
00211         {
00212             d_min = d_z;
00213         }
00214     }
00215 
00216     /* Number of elementary operations */
00217     p_data->i_nb_atomic_operations = i_nb_channels * 2;
00218     if ( i_physical_channels & AOUT_CHAN_CENTER )
00219     {
00220         p_data->i_nb_atomic_operations += 2;
00221     }
00222     p_data->p_atomic_operations = malloc ( sizeof(struct atomic_operation_t)
00223             * p_data->i_nb_atomic_operations );
00224     if ( p_data->p_atomic_operations == NULL )
00225     {
00226         msg_Err( p_filter, "out of memory" );
00227         return -1;
00228     }
00229 
00230     /* For each virtual speaker, computes elementary wave propagation time
00231      * to each ear */
00232     i_next_atomic_operation = 0;
00233     i_source_channel_offset = 0;
00234     if ( i_physical_channels & AOUT_CHAN_LEFT )
00235     {
00236         ComputeChannelOperations ( p_data , i_rate
00237                 , i_next_atomic_operation , i_source_channel_offset
00238                 , -d_x , d_z , d_min , 2.0 / i_nb_channels );
00239         i_next_atomic_operation += 2;
00240         i_source_channel_offset++;
00241     }
00242     if ( i_physical_channels & AOUT_CHAN_RIGHT )
00243     {
00244         ComputeChannelOperations ( p_data , i_rate
00245                 , i_next_atomic_operation , i_source_channel_offset
00246                 , d_x , d_z , d_min , 2.0 / i_nb_channels );
00247         i_next_atomic_operation += 2;
00248         i_source_channel_offset++;
00249     }
00250     if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
00251     {
00252         ComputeChannelOperations ( p_data , i_rate
00253                 , i_next_atomic_operation , i_source_channel_offset
00254                 , -d_x , 0 , d_min , 1.5 / i_nb_channels );
00255         i_next_atomic_operation += 2;
00256         i_source_channel_offset++;
00257     }
00258     if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
00259     {
00260         ComputeChannelOperations ( p_data , i_rate
00261                 , i_next_atomic_operation , i_source_channel_offset
00262                 , d_x , 0 , d_min , 1.5 / i_nb_channels );
00263         i_next_atomic_operation += 2;
00264         i_source_channel_offset++;
00265     }
00266     if ( i_physical_channels & AOUT_CHAN_REARLEFT )
00267     {
00268         ComputeChannelOperations ( p_data , i_rate
00269                 , i_next_atomic_operation , i_source_channel_offset
00270                 , -d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
00271         i_next_atomic_operation += 2;
00272         i_source_channel_offset++;
00273     }
00274     if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
00275     {
00276         ComputeChannelOperations ( p_data , i_rate
00277                 , i_next_atomic_operation , i_source_channel_offset
00278                 , d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
00279         i_next_atomic_operation += 2;
00280         i_source_channel_offset++;
00281     }
00282     if ( i_physical_channels & AOUT_CHAN_REARCENTER )
00283     {
00284         ComputeChannelOperations ( p_data , i_rate
00285                 , i_next_atomic_operation , i_source_channel_offset
00286                 , 0 , -d_z , d_min , 1.5 / i_nb_channels );
00287         i_next_atomic_operation += 2;
00288         i_source_channel_offset++;
00289     }
00290     if ( i_physical_channels & AOUT_CHAN_CENTER )
00291     {
00292         /* having two center channels increases the spatialization effect */
00293         ComputeChannelOperations ( p_data , i_rate
00294                 , i_next_atomic_operation , i_source_channel_offset
00295                 , d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
00296         i_next_atomic_operation += 2;
00297         ComputeChannelOperations ( p_data , i_rate
00298                 , i_next_atomic_operation , i_source_channel_offset
00299                 , -d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
00300         i_next_atomic_operation += 2;
00301         i_source_channel_offset++;
00302     }
00303     if ( i_physical_channels & AOUT_CHAN_LFE )
00304     {
00305         ComputeChannelOperations ( p_data , i_rate
00306                 , i_next_atomic_operation , i_source_channel_offset
00307                 , 0 , d_z_rear , d_min , 5.0 / i_nb_channels );
00308         i_next_atomic_operation += 2;
00309         i_source_channel_offset++;
00310     }
00311 
00312     /* Initialize the overflow buffer
00313      * we need it because the process induce a delay in the samples */
00314     p_data->i_overflow_buffer_size = 0;
00315     for ( i = 0 ; i < p_data->i_nb_atomic_operations ; i++ )
00316     {
00317         if ( p_data->i_overflow_buffer_size
00318                 < p_data->p_atomic_operations[i].i_delay * 2 * sizeof (float) )
00319         {
00320             p_data->i_overflow_buffer_size
00321                 = p_data->p_atomic_operations[i].i_delay * 2 * sizeof (float);
00322         }
00323     }
00324     p_data->p_overflow_buffer = malloc ( p_data->i_overflow_buffer_size );
00325     if ( p_data->p_atomic_operations == NULL )
00326     {
00327         msg_Err( p_filter, "out of memory" );
00328         return -1;
00329     }
00330     memset ( p_data->p_overflow_buffer , 0 , p_data->i_overflow_buffer_size );
00331 
00332     /* end */
00333     return 0;
00334 }
00335 
00336 /*****************************************************************************
00337  * Create: allocate headphone downmixer
00338  *****************************************************************************/
00339 static int Create( vlc_object_t *p_this )
00340 {
00341     aout_filter_t * p_filter = (aout_filter_t *)p_this;
00342     vlc_bool_t b_fit = VLC_TRUE;
00343 
00344     /* Activate this filter only with stereo devices */
00345     if ( p_filter->output.i_physical_channels
00346             != (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) )
00347     {
00348         msg_Dbg( p_filter, "Filter discarded (incompatible format)" );
00349         return VLC_EGENERIC;
00350     }
00351 
00352     /* Request a specific format if not already compatible */
00353     if ( p_filter->input.i_original_channels
00354             != p_filter->output.i_original_channels )
00355     {
00356         b_fit = VLC_FALSE;
00357         p_filter->input.i_original_channels =
00358                                         p_filter->output.i_original_channels;
00359     }
00360     if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
00361           || p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
00362     {
00363         b_fit = VLC_FALSE;
00364         p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
00365         p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
00366     }
00367     if ( p_filter->input.i_rate != p_filter->output.i_rate )
00368     {
00369         b_fit = VLC_FALSE;
00370         p_filter->input.i_rate = p_filter->output.i_rate;
00371     }
00372     if ( p_filter->input.i_physical_channels == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
00373           && ( p_filter->input.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
00374           && ! config_GetInt ( p_filter , "headphone-dolby" ) )
00375     {
00376         b_fit = VLC_FALSE;
00377         p_filter->input.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
00378                                               AOUT_CHAN_CENTER |
00379                                               AOUT_CHAN_REARLEFT |
00380                                               AOUT_CHAN_REARRIGHT;
00381     }
00382     if ( ! b_fit )
00383     {
00384         msg_Dbg( p_filter, "Requesting specific format" );
00385         return VLC_EGENERIC;
00386     }
00387 
00388     /* Allocate the memory needed to store the module's structure */
00389     p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
00390     if ( p_filter->p_sys == NULL )
00391     {
00392         msg_Err( p_filter, "Out of memory" );
00393         return VLC_EGENERIC;
00394     }
00395     p_filter->p_sys->i_overflow_buffer_size = 0;
00396     p_filter->p_sys->p_overflow_buffer = NULL;
00397     p_filter->p_sys->i_nb_atomic_operations = 0;
00398     p_filter->p_sys->p_atomic_operations = NULL;
00399 
00400     if ( Init( p_filter , p_filter->p_sys
00401                 , aout_FormatNbChannels ( &p_filter->input )
00402                 , p_filter->input.i_physical_channels
00403                 , p_filter->input.i_rate ) < 0 )
00404     {
00405         return VLC_EGENERIC;
00406     }
00407 
00408     p_filter->pf_do_work = DoWork;
00409     p_filter->b_in_place = 0;
00410 
00411     return VLC_SUCCESS;
00412 }
00413 
00414 /*****************************************************************************
00415  * Destroy: deallocate resources associated with headphone downmixer
00416  *****************************************************************************/
00417 static void Destroy( vlc_object_t *p_this )
00418 {
00419     aout_filter_t * p_filter = (aout_filter_t *)p_this;
00420 
00421     if ( p_filter->p_sys != NULL )
00422     {
00423         if ( p_filter->p_sys->p_overflow_buffer != NULL )
00424         {
00425             free ( p_filter->p_sys->p_overflow_buffer );
00426         }
00427         if ( p_filter->p_sys->p_atomic_operations != NULL )
00428         {
00429             free ( p_filter->p_sys->p_atomic_operations );
00430         }
00431         free ( p_filter->p_sys );
00432         p_filter->p_sys = NULL;
00433     }
00434 }
00435 
00436 /*****************************************************************************
00437  * DoWork: convert a buffer
00438  *****************************************************************************/
00439 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
00440                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
00441 {
00442     int i_input_nb = aout_FormatNbChannels( &p_filter->input );
00443     int i_output_nb = aout_FormatNbChannels( &p_filter->output );
00444 
00445     float * p_in = (float*) p_in_buf->p_buffer;
00446     byte_t * p_out;
00447     byte_t * p_overflow;
00448     byte_t * p_slide;
00449 
00450     size_t i_overflow_size;/* in bytes */
00451     size_t i_out_size;/* in bytes */
00452 
00453     unsigned int i, j;
00454 
00455     int i_source_channel_offset;
00456     int i_dest_channel_offset;
00457     unsigned int i_delay;
00458     double d_amplitude_factor;
00459 
00460 
00461     /* out buffer characterisitcs */
00462     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
00463     p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes * i_output_nb / i_input_nb;
00464     p_out = p_out_buf->p_buffer;
00465     i_out_size = p_out_buf->i_nb_bytes;
00466 
00467     if ( p_filter->p_sys != NULL )
00468     {
00469         /* Slide the overflow buffer */
00470         p_overflow = p_filter->p_sys->p_overflow_buffer;
00471         i_overflow_size = p_filter->p_sys->i_overflow_buffer_size;
00472 
00473         memset ( p_out , 0 , i_out_size );
00474         if ( i_out_size > i_overflow_size )
00475             memcpy ( p_out , p_overflow , i_overflow_size );
00476         else
00477             memcpy ( p_out , p_overflow , i_out_size );
00478 
00479         p_slide = p_filter->p_sys->p_overflow_buffer;
00480         while ( p_slide < p_overflow + i_overflow_size )
00481         {
00482             if ( p_slide + i_out_size < p_overflow + i_overflow_size )
00483             {
00484                 memset ( p_slide , 0 , i_out_size );
00485                 if ( p_slide + 2 * i_out_size < p_overflow + i_overflow_size )
00486                     memcpy ( p_slide , p_slide + i_out_size , i_out_size );
00487                 else
00488                     memcpy ( p_slide , p_slide + i_out_size
00489                       , p_overflow + i_overflow_size - ( p_slide + i_out_size ) );
00490             }
00491             else
00492             {
00493                 memset ( p_slide , 0 , p_overflow + i_overflow_size - p_slide );
00494             }
00495             p_slide += i_out_size;
00496         }
00497 
00498         /* apply the atomic operations */
00499         for ( i = 0 ; i < p_filter->p_sys->i_nb_atomic_operations ; i++ )
00500         {
00501             /* shorter variable names */
00502             i_source_channel_offset
00503                 = p_filter->p_sys->p_atomic_operations[i].i_source_channel_offset;
00504             i_dest_channel_offset
00505                 = p_filter->p_sys->p_atomic_operations[i].i_dest_channel_offset;
00506             i_delay = p_filter->p_sys->p_atomic_operations[i].i_delay;
00507             d_amplitude_factor
00508                 = p_filter->p_sys->p_atomic_operations[i].d_amplitude_factor;
00509 
00510             if ( p_out_buf->i_nb_samples > i_delay )
00511             {
00512                 /* current buffer coefficients */
00513                 for ( j = 0 ; j < p_out_buf->i_nb_samples - i_delay ; j++ )
00514                 {
00515                     ((float*)p_out)[ (i_delay+j)*i_output_nb + i_dest_channel_offset ]
00516                         += p_in[ j * i_input_nb + i_source_channel_offset ]
00517                            * d_amplitude_factor;
00518                 }
00519 
00520                 /* overflow buffer coefficients */
00521                 for ( j = 0 ; j < i_delay ; j++ )
00522                 {
00523                     ((float*)p_overflow)[ j*i_output_nb + i_dest_channel_offset ]
00524                         += p_in[ (p_out_buf->i_nb_samples - i_delay + j)
00525                            * i_input_nb + i_source_channel_offset ]
00526                            * d_amplitude_factor;
00527                 }
00528             }
00529             else
00530             {
00531                 /* overflow buffer coefficients only */
00532                 for ( j = 0 ; j < p_out_buf->i_nb_samples ; j++ )
00533                 {
00534                     ((float*)p_overflow)[ (i_delay - p_out_buf->i_nb_samples + j)
00535                         * i_output_nb + i_dest_channel_offset ]
00536                         += p_in[ j * i_input_nb + i_source_channel_offset ]
00537                            * d_amplitude_factor;
00538                 }
00539             }
00540         }
00541     }
00542     else
00543     {
00544         memset ( p_out , 0 , i_out_size );
00545     }
00546 }

Generated on Tue Dec 20 10:14:27 2005 for vlc-0.8.4a by  doxygen 1.4.2