]> git.sesse.net Git - vlc/blob - modules/audio_filter/channel_mixer/headphone.c
Improvements to preferences
[vlc] / modules / audio_filter / channel_mixer / headphone.c
1 /*****************************************************************************
2  * headphone.c : headphone virtual spatialization channel mixer module
3  *               -> gives the feeling of a real room with a simple headphone
4  *****************************************************************************
5  * Copyright (C) 2002 VideoLAN
6  * $Id$
7  *
8  * Authors: Boris Dorès <babal@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 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 General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30 #include <math.h>                                        /* sqrt */
31
32 #include <vlc/vlc.h>
33 #include "audio_output.h"
34 #include "aout_internal.h"
35
36 /*****************************************************************************
37  * Local prototypes
38  *****************************************************************************/
39 static int  Create    ( vlc_object_t * );
40 static void Destroy   ( vlc_object_t * );
41
42 static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
43                         aout_buffer_t * );
44
45 /*****************************************************************************
46  * Module descriptor
47  *****************************************************************************/
48 #define MODULE_DESCRIPTION N_ ( \
49      "This effect gives you the feeling that you are standing in a room " \
50      "with a complete 5.1 speaker set when using only a headphone, " \
51      "providing a more realistic sound experience. It should also be " \
52      "more comfortable and less tiring when listening to music for " \
53      "long periods of time.\nIt works with any source format from mono " \
54      "to 5.1.")
55
56 #define HEADPHONE_DIM_TEXT N_("Characteristic dimension")
57 #define HEADPHONE_DIM_LONGTEXT N_( \
58      "Distance between front left speaker and listener in meters.")
59
60 vlc_module_begin();
61     set_description( N_("headphone channel mixer with virtual spatialization effect") );
62     set_category( CAT_AUDIO );
63     set_subcategory( SUBCAT_AUDIO_AFILTER );
64
65     add_integer( "headphone-dim", 10, NULL, HEADPHONE_DIM_TEXT,
66                  HEADPHONE_DIM_LONGTEXT, VLC_FALSE );
67
68     set_capability( "audio filter", 0 );
69     set_callbacks( Create, Destroy );
70     add_shortcut( "headphone" );
71 vlc_module_end();
72
73
74 /*****************************************************************************
75  * Internal data structures
76  *****************************************************************************/
77 struct atomic_operation_t
78 {
79     int i_source_channel_offset;
80     int i_dest_channel_offset;
81     unsigned int i_delay;/* in sample unit */
82     double d_amplitude_factor;
83 };
84
85 struct aout_filter_sys_t
86 {
87     size_t i_overflow_buffer_size;/* in bytes */
88     byte_t * p_overflow_buffer;
89     unsigned int i_nb_atomic_operations;
90     struct atomic_operation_t * p_atomic_operations;
91 };
92
93 /*****************************************************************************
94  * Init: initialize internal data structures
95  * and computes the needed atomic operations
96  *****************************************************************************/
97 /* x and z represent the coordinates of the virtual speaker
98  *  relatively to the center of the listener's head, measured in meters :
99  *
100  *  left              right
101  *Z
102  *-
103  *a          head
104  *x
105  *i
106  *s
107  *  rear left    rear right
108  *
109  *          x-axis
110  *  */
111 static void ComputeChannelOperations ( struct aout_filter_sys_t * p_data
112         , unsigned int i_rate , unsigned int i_next_atomic_operation
113         , int i_source_channel_offset , double d_x , double d_z
114         , double d_channel_amplitude_factor )
115 {
116     double d_c = 340; /*sound celerity (unit: m/s)*/
117
118     /* Left ear */
119     p_data->p_atomic_operations[i_next_atomic_operation]
120         .i_source_channel_offset = i_source_channel_offset;
121     p_data->p_atomic_operations[i_next_atomic_operation]
122         .i_dest_channel_offset = 0;/* left */
123     p_data->p_atomic_operations[i_next_atomic_operation]
124         .i_delay = (int)( sqrt( (-0.1-d_x)*(-0.1-d_x) + (0-d_z)*(0-d_z) )
125                           / d_c * i_rate );
126     if ( d_x < 0 )
127     {
128         p_data->p_atomic_operations[i_next_atomic_operation]
129             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
130     }
131     else if ( d_x > 0 )
132     {
133         p_data->p_atomic_operations[i_next_atomic_operation]
134             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
135     }
136     else
137     {
138         p_data->p_atomic_operations[i_next_atomic_operation]
139             .d_amplitude_factor = d_channel_amplitude_factor / 2;
140     }
141
142     /* Right ear */
143     p_data->p_atomic_operations[i_next_atomic_operation + 1]
144         .i_source_channel_offset = i_source_channel_offset;
145     p_data->p_atomic_operations[i_next_atomic_operation + 1]
146         .i_dest_channel_offset = 1;/* right */
147     p_data->p_atomic_operations[i_next_atomic_operation + 1]
148         .i_delay = (int)( sqrt( (0.1-d_x)*(0.1-d_x) + (0-d_z)*(0-d_z) )
149                           / d_c * i_rate );
150     if ( d_x < 0 )
151     {
152         p_data->p_atomic_operations[i_next_atomic_operation + 1]
153             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
154     }
155     else if ( d_x > 0 )
156     {
157         p_data->p_atomic_operations[i_next_atomic_operation + 1]
158             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
159     }
160     else
161     {
162         p_data->p_atomic_operations[i_next_atomic_operation + 1]
163             .d_amplitude_factor = d_channel_amplitude_factor / 2;
164     }
165 }
166
167 static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
168         , unsigned int i_nb_channels , uint32_t i_physical_channels
169         , unsigned int i_rate )
170 {
171     double d_x = config_GetInt ( p_filter , "headphone-dim" );
172     double d_z = d_x;
173     double d_z_rear = -d_x/3;
174     unsigned int i_next_atomic_operation;
175     int i_source_channel_offset;
176     unsigned int i;
177
178     if ( p_data == NULL )
179     {
180         msg_Dbg ( p_filter, "passing a null pointer as argument" );
181         return 0;
182     }
183
184     /* Number of elementary operations */
185     p_data->i_nb_atomic_operations = i_nb_channels * 2;
186     p_data->p_atomic_operations = malloc ( sizeof(struct atomic_operation_t)
187             * p_data->i_nb_atomic_operations );
188     if ( p_data->p_atomic_operations == NULL )
189     {
190         msg_Err( p_filter, "out of memory" );
191         return -1;
192     }
193
194     /* For each virtual speaker, computes elementary wave propagation time
195      * to each ear */
196     i_next_atomic_operation = 0;
197     i_source_channel_offset = 0;
198     if ( i_physical_channels & AOUT_CHAN_LEFT )
199     {
200         ComputeChannelOperations ( p_data , i_rate
201                 , i_next_atomic_operation , i_source_channel_offset
202                 , -d_x , d_z , 2.0 / i_nb_channels );
203         i_next_atomic_operation += 2;
204         i_source_channel_offset++;
205     }
206     if ( i_physical_channels & AOUT_CHAN_RIGHT )
207     {
208         ComputeChannelOperations ( p_data , i_rate
209                 , i_next_atomic_operation , i_source_channel_offset
210                 , d_x , d_z , 2.0 / i_nb_channels );
211         i_next_atomic_operation += 2;
212         i_source_channel_offset++;
213     }
214     if ( i_physical_channels & AOUT_CHAN_REARLEFT )
215     {
216         ComputeChannelOperations ( p_data , i_rate
217                 , i_next_atomic_operation , i_source_channel_offset
218                 , -d_x , d_z_rear , 1.5 / i_nb_channels );
219         i_next_atomic_operation += 2;
220         i_source_channel_offset++;
221     }
222     if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
223     {
224         ComputeChannelOperations ( p_data , i_rate
225                 , i_next_atomic_operation , i_source_channel_offset
226                 , d_x , d_z_rear , 1.5 / i_nb_channels );
227         i_next_atomic_operation += 2;
228         i_source_channel_offset++;
229     }
230     if ( i_physical_channels & AOUT_CHAN_REARCENTER )
231     {
232         ComputeChannelOperations ( p_data , i_rate
233                 , i_next_atomic_operation , i_source_channel_offset
234                 , 0 , -d_z , 1.5 / i_nb_channels );
235         i_next_atomic_operation += 2;
236         i_source_channel_offset++;
237     }
238     if ( i_physical_channels & AOUT_CHAN_CENTER )
239     {
240         ComputeChannelOperations ( p_data , i_rate
241                 , i_next_atomic_operation , i_source_channel_offset
242                 , 0 , d_z , 1.5 / i_nb_channels );
243         i_next_atomic_operation += 2;
244         i_source_channel_offset++;
245     }
246     if ( i_physical_channels & AOUT_CHAN_LFE )
247     {
248         ComputeChannelOperations ( p_data , i_rate
249                 , i_next_atomic_operation , i_source_channel_offset
250                 , 0 , d_z_rear , 5.0 / i_nb_channels );
251         i_next_atomic_operation += 2;
252         i_source_channel_offset++;
253     }
254     if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
255     {
256         ComputeChannelOperations ( p_data , i_rate
257                 , i_next_atomic_operation , i_source_channel_offset
258                 , -d_x , 0 , 1.5 / i_nb_channels );
259         i_next_atomic_operation += 2;
260         i_source_channel_offset++;
261     }
262     if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
263     {
264         ComputeChannelOperations ( p_data , i_rate
265                 , i_next_atomic_operation , i_source_channel_offset
266                 , d_x , 0 , 1.5 / i_nb_channels );
267         i_next_atomic_operation += 2;
268         i_source_channel_offset++;
269     }
270
271     /* Initialize the overflow buffer
272      * we need it because the process induce a delay in the samples */
273     p_data->i_overflow_buffer_size = 0;
274     for ( i = 0 ; i < p_data->i_nb_atomic_operations ; i++ )
275     {
276         if ( p_data->i_overflow_buffer_size
277                 < p_data->p_atomic_operations[i].i_delay * i_nb_channels
278                 * sizeof (float) )
279         {
280             p_data->i_overflow_buffer_size
281                 = p_data->p_atomic_operations[i].i_delay * i_nb_channels
282                 * sizeof (float);
283         }
284     }
285     p_data->p_overflow_buffer = malloc ( p_data->i_overflow_buffer_size );
286     if ( p_data->p_atomic_operations == NULL )
287     {
288         msg_Err( p_filter, "out of memory" );
289         return -1;
290     }
291     memset ( p_data->p_overflow_buffer , 0 , p_data->i_overflow_buffer_size );
292
293     /* end */
294     return 0;
295 }
296
297 /*****************************************************************************
298  * Create: allocate headphone downmixer
299  *****************************************************************************/
300 static int Create( vlc_object_t *p_this )
301 {
302     aout_filter_t * p_filter = (aout_filter_t *)p_this;
303
304     if ( p_filter->output.i_physical_channels != ( AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT )
305           || p_filter->input.i_format != p_filter->output.i_format
306           || p_filter->input.i_rate != p_filter->output.i_rate
307           || (p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
308                && p_filter->input.i_format != VLC_FOURCC('f','i','3','2')) )
309     {
310         msg_Dbg( p_filter, "Filter discarded (invalid format)" );
311         return -1;
312     }
313
314     /* Allocate the memory needed to store the module's structure */
315     p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
316     if ( p_filter->p_sys == NULL )
317     {
318         msg_Err( p_filter, "out of memory" );
319         return -1;
320     }
321     p_filter->p_sys->i_overflow_buffer_size = 0;
322     p_filter->p_sys->p_overflow_buffer = NULL;
323     p_filter->p_sys->i_nb_atomic_operations = 0;
324     p_filter->p_sys->p_atomic_operations = NULL;
325
326     if ( Init( p_filter , p_filter->p_sys
327                 , aout_FormatNbChannels ( &p_filter->input )
328                 , p_filter->input.i_physical_channels
329                 ,  p_filter->input.i_rate ) < 0 )
330     {
331         return -1;
332     }
333
334     p_filter->pf_do_work = DoWork;
335     p_filter->b_in_place = 0;
336
337     return 0;
338 }
339
340 /*****************************************************************************
341  * Destroy: deallocate resources associated with headphone downmixer
342  *****************************************************************************/
343 static void Destroy( vlc_object_t *p_this )
344 {
345     aout_filter_t * p_filter = (aout_filter_t *)p_this;
346
347     if ( p_filter->p_sys != NULL )
348     {
349         if ( p_filter->p_sys->p_overflow_buffer != NULL )
350         {
351             free ( p_filter->p_sys->p_overflow_buffer );
352         }
353         if ( p_filter->p_sys->p_atomic_operations != NULL )
354         {
355             free ( p_filter->p_sys->p_atomic_operations );
356         }
357         free ( p_filter->p_sys );
358         p_filter->p_sys = NULL;
359     }
360 }
361
362 /*****************************************************************************
363  * DoWork: convert a buffer
364  *****************************************************************************/
365 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
366                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
367 {
368     int i_input_nb = aout_FormatNbChannels( &p_filter->input );
369     int i_output_nb = aout_FormatNbChannels( &p_filter->output );
370
371     float * p_in = (float*) p_in_buf->p_buffer;
372     byte_t * p_out;
373     byte_t * p_overflow;
374     byte_t * p_slide;
375
376     size_t i_overflow_size;/* in bytes */
377     size_t i_out_size;/* in bytes */
378
379     unsigned int i, j;
380
381     int i_source_channel_offset;
382     int i_dest_channel_offset;
383     unsigned int i_delay;
384     double d_amplitude_factor;
385
386
387     /* out buffer characterisitcs */
388     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
389     p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes * i_output_nb / i_input_nb;
390     p_out = p_out_buf->p_buffer;
391     i_out_size = p_out_buf->i_nb_bytes;
392
393     if ( p_filter->p_sys != NULL )
394     {
395         /* Slide the overflow buffer */
396         p_overflow = p_filter->p_sys->p_overflow_buffer;
397         i_overflow_size = p_filter->p_sys->i_overflow_buffer_size;
398
399         memset ( p_out , 0 , i_out_size );
400         if ( i_out_size > i_overflow_size )
401             memcpy ( p_out , p_overflow , i_overflow_size );
402         else
403             memcpy ( p_out , p_overflow , i_out_size );
404
405         p_slide = p_filter->p_sys->p_overflow_buffer;
406         while ( p_slide < p_overflow + i_overflow_size )
407         {
408             if ( p_slide + i_out_size < p_overflow + i_overflow_size )
409             {
410                 memset ( p_slide , 0 , i_out_size );
411                 if ( p_slide + 2 * i_out_size < p_overflow + i_overflow_size )
412                     memcpy ( p_slide , p_slide + i_out_size , i_out_size );
413                 else
414                     memcpy ( p_slide , p_slide + i_out_size
415                       , p_overflow + i_overflow_size - ( p_slide + i_out_size ) );
416             }
417             else
418             {
419                 memset ( p_slide , 0 , p_overflow + i_overflow_size - p_slide );
420             }
421             p_slide += i_out_size;
422         }
423
424         /* apply the atomic operations */
425         for ( i = 0 ; i < p_filter->p_sys->i_nb_atomic_operations ; i++ )
426         {
427             /* shorter variable names */
428             i_source_channel_offset
429                 = p_filter->p_sys->p_atomic_operations[i].i_source_channel_offset;
430             i_dest_channel_offset
431                 = p_filter->p_sys->p_atomic_operations[i].i_dest_channel_offset;
432             i_delay = p_filter->p_sys->p_atomic_operations[i].i_delay;
433             d_amplitude_factor
434                 = p_filter->p_sys->p_atomic_operations[i].d_amplitude_factor;
435
436             if ( p_out_buf->i_nb_samples > i_delay )
437             {
438                 /* current buffer coefficients */
439                 for ( j = 0 ; j < p_out_buf->i_nb_samples - i_delay ; j++ )
440                 {
441                     ((float*)p_out)[ (i_delay+j)*i_output_nb + i_dest_channel_offset ]
442                         += p_in[ j * i_input_nb + i_source_channel_offset ]
443                            * d_amplitude_factor;
444                 }
445
446                 /* overflow buffer coefficients */
447                 for ( j = 0 ; j < i_delay ; j++ )
448                 {
449                     ((float*)p_overflow)[ j*i_output_nb + i_dest_channel_offset ]
450                         += p_in[ (p_out_buf->i_nb_samples - i_delay + j)
451                            * i_input_nb + i_source_channel_offset ]
452                            * d_amplitude_factor;
453                 }
454             }
455             else
456             {
457                 /* overflow buffer coefficients only */
458                 for ( j = 0 ; j < p_out_buf->i_nb_samples ; j++ )
459                 {
460                     ((float*)p_overflow)[ (i_delay - p_out_buf->i_nb_samples + j)
461                         * i_output_nb + i_dest_channel_offset ]
462                         += p_in[ j * i_input_nb + i_source_channel_offset ]
463                            * d_amplitude_factor;
464                 }
465             }
466         }
467     }
468     else
469     {
470         memset ( p_out , 0 , i_out_size );
471     }
472 }