Moves volume icon update in the timer
[vlc.git] / modules / audio_filter / channel_mixer / dolby.c
1 /*****************************************************************************
2  * dolby.c : simple decoder for dolby surround encoded streams
3  *****************************************************************************
4  * Copyright (C) 2005, 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Boris Dorès <babal@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29 #include <vlc_aout.h>
30
31 /*****************************************************************************
32  * Local prototypes
33  *****************************************************************************/
34 static int  Create    ( vlc_object_t * );
35 static void Destroy   ( vlc_object_t * );
36
37 static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
38                         aout_buffer_t * );
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 vlc_module_begin();
44     set_description( _("Simple decoder for Dolby Surround encoded streams") );
45     set_shortname( _("Dolby Surround decoder") );
46     set_category( CAT_INPUT );
47     set_subcategory( SUBCAT_INPUT_ACODEC );
48     set_capability( "audio filter", 5 );
49     set_callbacks( Create, Destroy );
50 vlc_module_end();
51
52 /*****************************************************************************
53  * Internal data structures
54  *****************************************************************************/
55 struct aout_filter_sys_t
56 {
57     int i_left;
58     int i_center;
59     int i_right;
60     int i_rear_left;
61     int i_rear_center;
62     int i_rear_right;
63 };
64
65 /* our internal channel order (WG-4 order) */
66 static const uint32_t pi_channels[] =
67 { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
68   AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
69   AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
70
71 /*****************************************************************************
72  * Create: allocate headphone downmixer
73  *****************************************************************************/
74 static int Create( vlc_object_t *p_this )
75 {
76     int i = 0;
77     int i_offset = 0;
78     aout_filter_t * p_filter = (aout_filter_t *)p_this;
79
80     /* Validate audio filter format */
81     if ( p_filter->input.i_physical_channels != (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
82        || ! ( p_filter->input.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
83        || aout_FormatNbChannels( &p_filter->output ) <= 2
84        || ( p_filter->input.i_original_channels & ~AOUT_CHAN_DOLBYSTEREO )
85           != ( p_filter->output.i_original_channels & ~AOUT_CHAN_DOLBYSTEREO ) )
86     {
87         return VLC_EGENERIC;
88     }
89
90     if ( p_filter->input.i_rate != p_filter->output.i_rate )
91     {
92         return VLC_EGENERIC;
93     }
94
95     if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
96           || p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
97     {
98         return VLC_EGENERIC;
99     }
100
101     /* Allocate the memory needed to store the module's structure */
102     p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
103     if ( p_filter->p_sys == NULL )
104     {
105         msg_Err( p_filter, "out of memory" );
106         return VLC_EGENERIC;
107     }
108     p_filter->p_sys->i_left = -1;
109     p_filter->p_sys->i_center = -1;
110     p_filter->p_sys->i_right = -1;
111     p_filter->p_sys->i_rear_left = -1;
112     p_filter->p_sys->i_rear_center = -1;
113     p_filter->p_sys->i_rear_right = -1;
114
115     while ( pi_channels[i] )
116     {
117         if ( p_filter->output.i_physical_channels & pi_channels[i] )
118         {
119             switch ( pi_channels[i] )
120             {
121                 case AOUT_CHAN_LEFT:
122                     p_filter->p_sys->i_left = i_offset;
123                     break;
124                 case AOUT_CHAN_CENTER:
125                     p_filter->p_sys->i_center = i_offset;
126                     break;
127                 case AOUT_CHAN_RIGHT:
128                     p_filter->p_sys->i_right = i_offset;
129                     break;
130                 case AOUT_CHAN_REARLEFT:
131                     p_filter->p_sys->i_rear_left = i_offset;
132                     break;
133                 case AOUT_CHAN_REARCENTER:
134                     p_filter->p_sys->i_rear_center = i_offset;
135                     break;
136                 case AOUT_CHAN_REARRIGHT:
137                     p_filter->p_sys->i_rear_right = i_offset;
138                     break;
139             }
140             ++i_offset;
141         }
142         ++i;
143     }
144
145     p_filter->pf_do_work = DoWork;
146     p_filter->b_in_place = 0;
147
148     return VLC_SUCCESS;
149 }
150
151 /*****************************************************************************
152  * Destroy: deallocate resources associated with headphone downmixer
153  *****************************************************************************/
154 static void Destroy( vlc_object_t *p_this )
155 {
156     aout_filter_t * p_filter = (aout_filter_t *)p_this;
157
158     if ( p_filter->p_sys != NULL )
159     {
160         free ( p_filter->p_sys );
161         p_filter->p_sys = NULL;
162     }
163 }
164
165 /*****************************************************************************
166  * DoWork: convert a buffer
167  *****************************************************************************/
168 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
169                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
170 {
171     float * p_in = (float*) p_in_buf->p_buffer;
172     float * p_out = (float*) p_out_buf->p_buffer;
173     size_t i_nb_samples = p_in_buf->i_nb_samples;
174     size_t i_nb_channels = aout_FormatNbChannels( &p_filter->output );
175
176     p_out_buf->i_nb_samples = i_nb_samples;
177     p_out_buf->i_nb_bytes = sizeof(float) * i_nb_samples
178                             * aout_FormatNbChannels( &p_filter->output );
179     memset ( p_out , 0 , p_out_buf->i_nb_bytes );
180
181     if ( p_filter->p_sys != NULL )
182     {
183         struct aout_filter_sys_t * p_sys = p_filter->p_sys;
184         size_t i_nb_rear = 0;
185         size_t i;
186
187         if ( p_sys->i_rear_left >= 0 )
188         {
189             ++i_nb_rear;
190         }
191         if ( p_sys->i_rear_center >= 0 )
192         {
193             ++i_nb_rear;
194         }
195         if ( p_sys->i_rear_right >= 0 )
196         {
197             ++i_nb_rear;
198         }
199
200         for ( i = 0; i < i_nb_samples; ++i )
201         {
202             float f_left = p_in[ i * 2 ];
203             float f_right = p_in[ i * 2 + 1 ];
204             float f_rear = ( f_left - f_right ) / i_nb_rear;
205
206             if ( p_sys->i_center >= 0 )
207             {
208                 float f_center = f_left + f_right;
209                 f_left -= f_center / 2;
210                 f_right -= f_center / 2;
211
212                 p_out[ i * i_nb_channels + p_sys->i_center ] = f_center;
213             }
214
215             if ( p_sys->i_left >= 0 )
216             {
217                 p_out[ i * i_nb_channels + p_sys->i_left ] = f_left;
218             }
219             if ( p_sys->i_right >= 0 )
220             {
221                 p_out[ i * i_nb_channels + p_sys->i_right ] = f_right;
222             }
223             if ( p_sys->i_rear_left >= 0 )
224             {
225                 p_out[ i * i_nb_channels + p_sys->i_rear_left ] = f_rear;
226             }
227             if ( p_sys->i_rear_center >= 0 )
228             {
229                 p_out[ i * i_nb_channels + p_sys->i_rear_center ] = f_rear;
230             }
231             if ( p_sys->i_rear_right >= 0 )
232             {
233                 p_out[ i * i_nb_channels + p_sys->i_rear_right ] = f_rear;
234             }
235         }
236     }
237 }