/src/vlc/src/audio_output/common.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * common.c : audio output management of common data structures |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2002-2007 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Christophe Massiot <massiot@via.ecp.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 | | #ifdef HAVE_CONFIG_H |
24 | | # include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <limits.h> |
28 | | #include <assert.h> |
29 | | |
30 | | #include <vlc_common.h> |
31 | | #include <vlc_aout.h> |
32 | | #include "aout_internal.h" |
33 | | |
34 | | /* |
35 | | * Formats management (internal and external) |
36 | | */ |
37 | | |
38 | | /***************************************************************************** |
39 | | * aout_BitsPerSample : get the number of bits per sample |
40 | | *****************************************************************************/ |
41 | | unsigned int aout_BitsPerSample( vlc_fourcc_t i_format ) |
42 | 2.22M | { |
43 | 2.22M | switch( vlc_fourcc_GetCodec( AUDIO_ES, i_format ) ) |
44 | 2.22M | { |
45 | 1.05M | case VLC_CODEC_U8: |
46 | 1.05M | case VLC_CODEC_S8: |
47 | 1.05M | case VLC_CODEC_ALAW: |
48 | 1.05M | case VLC_CODEC_MULAW: |
49 | 1.05M | return 8; |
50 | | |
51 | 0 | case VLC_CODEC_U16L: |
52 | 1.14M | case VLC_CODEC_S16L: |
53 | 1.14M | case VLC_CODEC_U16B: |
54 | 1.15M | case VLC_CODEC_S16B: |
55 | 1.15M | return 16; |
56 | | |
57 | 0 | case VLC_CODEC_U24L: |
58 | 3.71k | case VLC_CODEC_S24L: |
59 | 3.71k | case VLC_CODEC_U24B: |
60 | 4.26k | case VLC_CODEC_S24B: |
61 | 4.26k | return 24; |
62 | | |
63 | 0 | case VLC_CODEC_S24L32: |
64 | 0 | case VLC_CODEC_S24B32: |
65 | 0 | case VLC_CODEC_U32L: |
66 | 0 | case VLC_CODEC_U32B: |
67 | 4.36k | case VLC_CODEC_S32L: |
68 | 7.22k | case VLC_CODEC_S32B: |
69 | 14.1k | case VLC_CODEC_F32L: |
70 | 15.3k | case VLC_CODEC_F32B: |
71 | 15.3k | return 32; |
72 | | |
73 | 156 | case VLC_CODEC_F64L: |
74 | 278 | case VLC_CODEC_F64B: |
75 | 278 | return 64; |
76 | | |
77 | 4.52k | default: |
78 | | /* For these formats the caller has to indicate the parameters |
79 | | * by hand. */ |
80 | 4.52k | return 0; |
81 | 2.22M | } |
82 | 2.22M | } |
83 | | |
84 | | /***************************************************************************** |
85 | | * aout_FormatPrepare : compute the number of bytes per frame & frame length |
86 | | *****************************************************************************/ |
87 | | void aout_FormatPrepare( audio_sample_format_t * p_format ) |
88 | 1.98M | { |
89 | | |
90 | 1.98M | unsigned i_channels = aout_FormatNbChannels( p_format ); |
91 | 1.98M | if( i_channels > 0 ) |
92 | 1.86M | p_format->i_channels = i_channels; |
93 | 1.98M | p_format->i_bitspersample = aout_BitsPerSample( p_format->i_format ); |
94 | 1.98M | if( p_format->i_bitspersample > 0 ) |
95 | 1.98M | { |
96 | 1.98M | p_format->i_bytes_per_frame = ( p_format->i_bitspersample / 8 ) |
97 | 1.98M | * p_format->i_channels; |
98 | 1.98M | p_format->i_frame_length = 1; |
99 | 1.98M | } |
100 | 1.98M | } |
101 | | |
102 | | /***************************************************************************** |
103 | | * aout_FormatPrintChannels : print a channel in a human-readable form |
104 | | *****************************************************************************/ |
105 | | const char * aout_FormatPrintChannels( const audio_sample_format_t * p_format ) |
106 | 0 | { |
107 | 0 | if (p_format->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS) |
108 | 0 | return "Ambisonics"; |
109 | | |
110 | | /* AUDIO_CHANNEL_TYPE_BITMAP */ |
111 | 0 | switch ( p_format->i_physical_channels ) |
112 | 0 | { |
113 | 0 | case AOUT_CHAN_LEFT: |
114 | 0 | case AOUT_CHAN_RIGHT: |
115 | 0 | case AOUT_CHAN_CENTER: |
116 | 0 | if ( (p_format->i_physical_channels & AOUT_CHAN_CENTER) |
117 | 0 | || (p_format->i_physical_channels |
118 | 0 | & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) |
119 | 0 | return "Mono"; |
120 | 0 | else if ( p_format->i_physical_channels & AOUT_CHAN_LEFT ) |
121 | 0 | return "Left"; |
122 | 0 | return "Right"; |
123 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT: |
124 | 0 | if ( p_format->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO ) |
125 | 0 | return "Dolby"; |
126 | 0 | else if ( p_format->i_chan_mode & AOUT_CHANMODE_DUALMONO ) |
127 | 0 | return "Dual-mono"; |
128 | 0 | else if ( p_format->i_physical_channels == AOUT_CHAN_CENTER ) |
129 | 0 | return "Stereo/Mono"; |
130 | 0 | else if ( !(p_format->i_physical_channels & AOUT_CHAN_RIGHT) ) |
131 | 0 | return "Stereo/Left"; |
132 | 0 | else if ( !(p_format->i_physical_channels & AOUT_CHAN_LEFT) ) |
133 | 0 | return "Stereo/Right"; |
134 | 0 | return "Stereo"; |
135 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER: |
136 | 0 | return "3F"; |
137 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER: |
138 | 0 | return "2F1R"; |
139 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
140 | 0 | | AOUT_CHAN_REARCENTER: |
141 | 0 | return "3F1R"; |
142 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
143 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT: |
144 | 0 | return "2F2R"; |
145 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
146 | 0 | | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT: |
147 | 0 | return "2F2M"; |
148 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
149 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT: |
150 | 0 | return "3F2R"; |
151 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
152 | 0 | | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT: |
153 | 0 | return "3F2M"; |
154 | | |
155 | 0 | case AOUT_CHAN_CENTER | AOUT_CHAN_LFE: |
156 | 0 | if ( (p_format->i_physical_channels & AOUT_CHAN_CENTER) |
157 | 0 | || (p_format->i_physical_channels |
158 | 0 | & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) |
159 | 0 | return "Mono/LFE"; |
160 | 0 | else if ( p_format->i_physical_channels & AOUT_CHAN_LEFT ) |
161 | 0 | return "Left/LFE"; |
162 | 0 | return "Right/LFE"; |
163 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE: |
164 | 0 | if ( p_format->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO ) |
165 | 0 | return "Dolby/LFE"; |
166 | 0 | else if ( p_format->i_chan_mode & AOUT_CHANMODE_DUALMONO ) |
167 | 0 | return "Dual-mono/LFE"; |
168 | 0 | else if ( p_format->i_physical_channels == AOUT_CHAN_CENTER ) |
169 | 0 | return "Mono/LFE"; |
170 | 0 | else if ( !(p_format->i_physical_channels & AOUT_CHAN_RIGHT) ) |
171 | 0 | return "Stereo/Left/LFE"; |
172 | 0 | else if ( !(p_format->i_physical_channels & AOUT_CHAN_LEFT) ) |
173 | 0 | return "Stereo/Right/LFE"; |
174 | 0 | return "Stereo/LFE"; |
175 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_LFE: |
176 | 0 | return "3F/LFE"; |
177 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER |
178 | 0 | | AOUT_CHAN_LFE: |
179 | 0 | return "2F1R/LFE"; |
180 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
181 | 0 | | AOUT_CHAN_REARCENTER | AOUT_CHAN_LFE: |
182 | 0 | return "3F1R/LFE"; |
183 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
184 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE: |
185 | 0 | return "2F2R/LFE"; |
186 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
187 | 0 | | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: |
188 | 0 | return "2F2M/LFE"; |
189 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
190 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE: |
191 | 0 | return "3F2R/LFE"; |
192 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
193 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER |
194 | 0 | | AOUT_CHAN_LFE: |
195 | 0 | return "3F3R/LFE"; |
196 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
197 | 0 | | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: |
198 | 0 | return "3F2M/LFE"; |
199 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
200 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT |
201 | 0 | | AOUT_CHAN_MIDDLERIGHT: |
202 | 0 | return "2F2M2R"; |
203 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
204 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT |
205 | 0 | | AOUT_CHAN_MIDDLERIGHT: |
206 | 0 | return "3F2M2R"; |
207 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
208 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT |
209 | 0 | | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: |
210 | 0 | return "3F2M2R/LFE"; |
211 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
212 | 0 | | AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT |
213 | 0 | | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: |
214 | 0 | return "3F2M1R/LFE"; |
215 | 0 | case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
216 | 0 | | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER |
217 | 0 | | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: |
218 | 0 | return "3F2M3R/LFE"; |
219 | 0 | } |
220 | | |
221 | 0 | return "Unknown-chan-mask"; |
222 | 0 | } |
223 | | |
224 | | #undef aout_FormatPrint |
225 | | void aout_FormatPrint( vlc_object_t *obj, const char *psz_text, |
226 | | const audio_sample_format_t *p_format ) |
227 | 0 | { |
228 | 0 | msg_Dbg( obj, "%s '%4.4s' %d Hz %s frame=%u samples/%u bytes", psz_text, |
229 | 0 | (char *)&p_format->i_format, p_format->i_rate, |
230 | 0 | aout_FormatPrintChannels( p_format ), |
231 | 0 | p_format->i_frame_length, p_format->i_bytes_per_frame ); |
232 | 0 | } |
233 | | |
234 | | #undef aout_FormatsPrint |
235 | | /** |
236 | | * Prints two formats in a human-readable form |
237 | | */ |
238 | | void aout_FormatsPrint( vlc_object_t *obj, const char * psz_text, |
239 | | const audio_sample_format_t * p_format1, |
240 | | const audio_sample_format_t * p_format2 ) |
241 | 0 | { |
242 | 0 | msg_Dbg( obj, "%s '%4.4s'->'%4.4s' %u Hz->%u Hz %s->%s", |
243 | 0 | psz_text, |
244 | 0 | (char *)&p_format1->i_format, (char *)&p_format2->i_format, |
245 | 0 | p_format1->i_rate, p_format2->i_rate, |
246 | 0 | aout_FormatPrintChannels( p_format1 ), |
247 | 0 | aout_FormatPrintChannels( p_format2 ) ); |
248 | 0 | } |
249 | | |
250 | | /***************************************************************************** |
251 | | * aout_CheckChannelReorder : Check if we need to do some channel re-ordering |
252 | | *****************************************************************************/ |
253 | | unsigned aout_CheckChannelReorder( const uint32_t *chans_in, |
254 | | const uint32_t *chans_out, |
255 | | uint32_t mask, uint8_t *restrict table ) |
256 | 20.8k | { |
257 | 20.8k | static_assert(AOUT_CHAN_MAX <= (sizeof (mask) * CHAR_BIT), "Missing bits"); |
258 | | |
259 | 20.8k | unsigned channels = 0; |
260 | | |
261 | 20.8k | if( chans_in == NULL ) |
262 | 1 | chans_in = pi_vlc_chan_order_wg4; |
263 | 20.8k | if( chans_out == NULL ) |
264 | 20.8k | chans_out = pi_vlc_chan_order_wg4; |
265 | | |
266 | 122k | for( unsigned i = 0; chans_in[i]; i++ ) |
267 | 101k | { |
268 | 101k | const uint32_t chan = chans_in[i]; |
269 | 101k | if( !(mask & chan) ) |
270 | 5.09k | continue; |
271 | | |
272 | 96.6k | unsigned index = 0; |
273 | 383k | for( unsigned j = 0; chan != chans_out[j]; j++ ) |
274 | 286k | if( mask & chans_out[j] ) |
275 | 207k | index++; |
276 | | |
277 | 96.6k | table[channels++] = index; |
278 | 96.6k | } |
279 | | |
280 | 90.2k | for( unsigned i = 0; i < channels; i++ ) |
281 | 75.9k | if( table[i] != i ) |
282 | 6.53k | return channels; |
283 | 14.3k | return 0; |
284 | 20.8k | } |
285 | | |
286 | | void aout_ChannelReorder( void *ptr, size_t bytes, uint8_t channels, |
287 | | const uint8_t *restrict chans_table, vlc_fourcc_t fourcc ) |
288 | 2.63k | { |
289 | 2.63k | if( unlikely(bytes == 0) ) |
290 | 0 | return; |
291 | | |
292 | 2.63k | assert( channels != 0 ); |
293 | | |
294 | | /* The audio formats supported in audio output are inlined. For other |
295 | | * formats (used in demuxers and muxers), memcpy() is used to avoid |
296 | | * breaking type punning. */ |
297 | 2.63k | #define REORDER_TYPE(type) \ |
298 | 1.80k | do { \ |
299 | 1.80k | const size_t frames = (bytes / sizeof (type)) / channels; \ |
300 | 1.80k | type *buf = ptr; \ |
301 | 1.80k | \ |
302 | 317k | for( size_t i = 0; i < frames; i++ ) \ |
303 | 315k | { \ |
304 | 315k | type tmp[AOUT_CHAN_MAX]; \ |
305 | 315k | \ |
306 | 2.87M | for( size_t j = 0; j < channels; j++ ) \ |
307 | 2.56M | tmp[chans_table[j]] = buf[j]; \ |
308 | 315k | memcpy( buf, tmp, sizeof (type) * channels ); \ |
309 | 315k | buf += channels; \ |
310 | 315k | } \ |
311 | 1.80k | } while(0) |
312 | | |
313 | 2.63k | if( likely(channels <= AOUT_CHAN_MAX) ) |
314 | 2.63k | { |
315 | 2.63k | switch( fourcc ) |
316 | 2.63k | { |
317 | 607 | case VLC_CODEC_U8: REORDER_TYPE(uint8_t); return; |
318 | 301 | case VLC_CODEC_S16N: REORDER_TYPE(int16_t); return; |
319 | 406 | case VLC_CODEC_FL32: REORDER_TYPE(float); return; |
320 | 266 | case VLC_CODEC_S32N: REORDER_TYPE(int32_t); return; |
321 | 223 | case VLC_CODEC_FL64: REORDER_TYPE(double); return; |
322 | 2.63k | } |
323 | 2.63k | } |
324 | | |
325 | 835 | unsigned size = aout_BitsPerSample( fourcc ) / 8; |
326 | 835 | assert( size != 0 && size <= 8 ); |
327 | | |
328 | 835 | const size_t frames = bytes / (size * channels); |
329 | 835 | unsigned char *buf = ptr; |
330 | | |
331 | 2.08k | for( size_t i = 0; i < frames; i++ ) |
332 | 1.24k | { |
333 | 1.24k | unsigned char tmp[256 * 8]; |
334 | | |
335 | 7.86k | for( size_t j = 0; j < channels; j++ ) |
336 | 6.61k | memcpy( tmp + size * chans_table[j], buf + size * j, size ); |
337 | 1.24k | memcpy( buf, tmp, size * channels ); |
338 | 1.24k | buf += size * channels; |
339 | 1.24k | } |
340 | 835 | } |
341 | | |
342 | | /** |
343 | | * Interleaves audio samples within a block of samples. |
344 | | * \param dst destination buffer for interleaved samples |
345 | | * \param srcv source buffers (one per plane) of uninterleaved samples |
346 | | * \param samples number of samples (per channel/per plane) |
347 | | * \param chans channels/planes count |
348 | | * \param fourcc sample format (must be a linear sample format) |
349 | | * \note The samples must be naturally aligned in memory. |
350 | | * \warning Destination and source buffers MUST NOT overlap. |
351 | | */ |
352 | | void aout_Interleave( void *restrict dst, const void *const *srcv, |
353 | | unsigned samples, unsigned chans, vlc_fourcc_t fourcc ) |
354 | 0 | { |
355 | 0 | #define INTERLEAVE_TYPE(type) \ |
356 | 0 | do { \ |
357 | 0 | type *d = dst; \ |
358 | 0 | for( size_t i = 0; i < chans; i++ ) { \ |
359 | 0 | const type *s = srcv[i]; \ |
360 | 0 | for( size_t j = 0, k = 0; j < samples; j++, k += chans ) \ |
361 | 0 | d[k] = *(s++); \ |
362 | 0 | d++; \ |
363 | 0 | } \ |
364 | 0 | } while(0) |
365 | |
|
366 | 0 | switch( fourcc ) |
367 | 0 | { |
368 | 0 | case VLC_CODEC_U8: INTERLEAVE_TYPE(uint8_t); break; |
369 | 0 | case VLC_CODEC_S16N: INTERLEAVE_TYPE(int16_t); break; |
370 | 0 | case VLC_CODEC_FL32: INTERLEAVE_TYPE(float); break; |
371 | 0 | case VLC_CODEC_S32N: INTERLEAVE_TYPE(int32_t); break; |
372 | 0 | case VLC_CODEC_FL64: INTERLEAVE_TYPE(double); break; |
373 | 0 | default: vlc_assert_unreachable(); |
374 | 0 | } |
375 | 0 | #undef INTERLEAVE_TYPE |
376 | 0 | } |
377 | | |
378 | | /** |
379 | | * Deinterleaves audio samples within a block of samples. |
380 | | * \param dst destination buffer for planar samples |
381 | | * \param src source buffer with interleaved samples |
382 | | * \param samples number of samples (per channel/per plane) |
383 | | * \param chans channels/planes count |
384 | | * \param fourcc sample format (must be a linear sample format) |
385 | | * \note The samples must be naturally aligned in memory. |
386 | | * \warning Destination and source buffers MUST NOT overlap. |
387 | | */ |
388 | | void aout_Deinterleave( void *restrict dst, const void *restrict src, |
389 | | unsigned samples, unsigned chans, vlc_fourcc_t fourcc ) |
390 | 0 | { |
391 | 0 | #define DEINTERLEAVE_TYPE(type) \ |
392 | 0 | do { \ |
393 | 0 | type *d = dst; \ |
394 | 0 | const type *s = src; \ |
395 | 0 | for( size_t i = 0; i < chans; i++ ) { \ |
396 | 0 | for( size_t j = 0, k = 0; j < samples; j++, k += chans ) \ |
397 | 0 | *(d++) = s[k]; \ |
398 | 0 | s++; \ |
399 | 0 | } \ |
400 | 0 | } while(0) |
401 | |
|
402 | 0 | switch( fourcc ) |
403 | 0 | { |
404 | 0 | case VLC_CODEC_U8: DEINTERLEAVE_TYPE(uint8_t); break; |
405 | 0 | case VLC_CODEC_S16N: DEINTERLEAVE_TYPE(int16_t); break; |
406 | 0 | case VLC_CODEC_FL32: DEINTERLEAVE_TYPE(float); break; |
407 | 0 | case VLC_CODEC_S32N: DEINTERLEAVE_TYPE(int32_t); break; |
408 | 0 | case VLC_CODEC_FL64: DEINTERLEAVE_TYPE(double); break; |
409 | 0 | default: vlc_assert_unreachable(); |
410 | 0 | } |
411 | 0 | #undef DEINTERLEAVE_TYPE |
412 | 0 | } |
413 | | |
414 | | /***************************************************************************** |
415 | | * aout_ChannelExtract: |
416 | | *****************************************************************************/ |
417 | | static inline void ExtractChannel( uint8_t *pi_dst, int i_dst_channels, |
418 | | const uint8_t *pi_src, int i_src_channels, |
419 | | int i_sample_count, |
420 | | const int *pi_selection, int i_bytes ) |
421 | 0 | { |
422 | 0 | for( int i = 0; i < i_sample_count; i++ ) |
423 | 0 | { |
424 | 0 | for( int j = 0; j < i_dst_channels; j++ ) |
425 | 0 | memcpy( &pi_dst[j * i_bytes], &pi_src[pi_selection[j] * i_bytes], i_bytes ); |
426 | 0 | pi_dst += i_dst_channels * i_bytes; |
427 | 0 | pi_src += i_src_channels * i_bytes; |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | void aout_ChannelExtract( void *p_dst, int i_dst_channels, |
432 | | const void *p_src, int i_src_channels, |
433 | | int i_sample_count, const int *pi_selection, int i_bits_per_sample ) |
434 | 0 | { |
435 | | /* It does not work in place */ |
436 | 0 | assert( p_dst != p_src ); |
437 | | |
438 | | /* Force the compiler to inline for the specific cases so it can optimize */ |
439 | 0 | if( i_bits_per_sample == 8 ) |
440 | 0 | ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 1 ); |
441 | 0 | else if( i_bits_per_sample == 16 ) |
442 | 0 | ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 2 ); |
443 | 0 | else if( i_bits_per_sample == 32 ) |
444 | 0 | ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 4 ); |
445 | 0 | else if( i_bits_per_sample == 64 ) |
446 | 0 | ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 8 ); |
447 | 0 | } |
448 | | |
449 | | bool aout_CheckChannelExtraction( int *pi_selection, |
450 | | uint32_t *pi_layout, int *pi_channels, |
451 | | const uint32_t pi_order_dst[AOUT_CHAN_MAX], |
452 | | const uint32_t *pi_order_src, int i_channels ) |
453 | 0 | { |
454 | 0 | static_assert(AOUT_CHAN_MAX <= (sizeof (*pi_order_dst) * CHAR_BIT), |
455 | 0 | "Missing bits"); |
456 | |
|
457 | 0 | uint32_t i_layout = 0; |
458 | 0 | int i_out = 0; |
459 | 0 | int pi_index[AOUT_CHAN_MAX]; |
460 | | |
461 | | /* */ |
462 | 0 | if( !pi_order_dst ) |
463 | 0 | pi_order_dst = pi_vlc_chan_order_wg4; |
464 | | |
465 | | /* */ |
466 | 0 | for( int i = 0; i < i_channels; i++ ) |
467 | 0 | { |
468 | | /* Ignore unknown or duplicated channels or not present in output */ |
469 | 0 | if( !pi_order_src[i] || (i_layout & pi_order_src[i]) ) |
470 | 0 | continue; |
471 | | |
472 | 0 | for( int j = 0; j < AOUT_CHAN_MAX; j++ ) |
473 | 0 | { |
474 | 0 | if( pi_order_dst[j] == pi_order_src[i] ) |
475 | 0 | { |
476 | 0 | assert( i_out < AOUT_CHAN_MAX ); |
477 | 0 | pi_index[i_out++] = i; |
478 | 0 | i_layout |= pi_order_src[i]; |
479 | 0 | break; |
480 | 0 | } |
481 | 0 | } |
482 | 0 | } |
483 | | |
484 | | /* */ |
485 | 0 | for( int i = 0, j = 0; i < AOUT_CHAN_MAX; i++ ) |
486 | 0 | { |
487 | 0 | for( int k = 0; k < i_out; k++ ) |
488 | 0 | { |
489 | 0 | if( pi_order_dst[i] == pi_order_src[pi_index[k]] ) |
490 | 0 | { |
491 | 0 | pi_selection[j++] = pi_index[k]; |
492 | 0 | break; |
493 | 0 | } |
494 | 0 | } |
495 | 0 | } |
496 | |
|
497 | 0 | *pi_layout = i_layout; |
498 | 0 | *pi_channels = i_out; |
499 | |
|
500 | 0 | for( int i = 0; i < i_out; i++ ) |
501 | 0 | { |
502 | 0 | if( pi_selection[i] != i ) |
503 | 0 | return true; |
504 | 0 | } |
505 | 0 | return i_out != i_channels; |
506 | 0 | } |
507 | | |
508 | | /* Return the order in which filters should be inserted */ |
509 | | static int FilterOrder( const char *psz_name ) |
510 | 0 | { |
511 | 0 | static const struct { |
512 | 0 | const char psz_name[10]; |
513 | 0 | int i_order; |
514 | 0 | } filter[] = { |
515 | 0 | { "equalizer", 0 }, |
516 | 0 | }; |
517 | 0 | for( unsigned i = 0; i < ARRAY_SIZE(filter); i++ ) |
518 | 0 | { |
519 | 0 | if( !strcmp( filter[i].psz_name, psz_name ) ) |
520 | 0 | return filter[i].i_order; |
521 | 0 | } |
522 | 0 | return INT_MAX; |
523 | 0 | } |
524 | | |
525 | | int aout_EnableFilter( audio_output_t *p_aout, const char *psz_name, bool b_add ) |
526 | 0 | { |
527 | 0 | if( *psz_name == '\0' ) |
528 | 0 | return VLC_EGENERIC; |
529 | | |
530 | 0 | const char *psz_variable = "audio-filter"; |
531 | 0 | char *psz_list; |
532 | 0 | psz_list = var_GetString( p_aout, psz_variable ); |
533 | | |
534 | | /* Split the string into an array of filters */ |
535 | 0 | int i_count = 1; |
536 | 0 | for( char *p = psz_list; p && *p; p++ ) |
537 | 0 | i_count += *p == ':'; |
538 | 0 | i_count += b_add; |
539 | |
|
540 | 0 | const char **ppsz_filter = calloc( i_count, sizeof(*ppsz_filter) ); |
541 | 0 | if( !ppsz_filter ) |
542 | 0 | { |
543 | 0 | free( psz_list ); |
544 | 0 | return VLC_ENOMEM; |
545 | 0 | } |
546 | 0 | bool b_present = false; |
547 | 0 | i_count = 0; |
548 | 0 | for( char *p = psz_list; p && *p; ) |
549 | 0 | { |
550 | 0 | char *psz_end = strchr(p, ':'); |
551 | 0 | if( psz_end ) |
552 | 0 | *psz_end++ = '\0'; |
553 | 0 | else |
554 | 0 | psz_end = p + strlen(p); |
555 | 0 | if( *p ) |
556 | 0 | { |
557 | 0 | b_present |= !strcmp( p, psz_name ); |
558 | 0 | ppsz_filter[i_count++] = p; |
559 | 0 | } |
560 | 0 | p = psz_end; |
561 | 0 | } |
562 | 0 | if( b_present == b_add ) |
563 | 0 | { |
564 | 0 | free( ppsz_filter ); |
565 | 0 | free( psz_list ); |
566 | 0 | return VLC_EGENERIC; |
567 | 0 | } |
568 | | |
569 | 0 | if( b_add ) |
570 | 0 | { |
571 | 0 | int i_order = FilterOrder( psz_name ); |
572 | 0 | int i; |
573 | 0 | for( i = 0; i < i_count; i++ ) |
574 | 0 | { |
575 | 0 | if( FilterOrder( ppsz_filter[i] ) > i_order ) |
576 | 0 | break; |
577 | 0 | } |
578 | 0 | if( i < i_count ) |
579 | 0 | memmove( &ppsz_filter[i+1], &ppsz_filter[i], (i_count - i) * sizeof(*ppsz_filter) ); |
580 | 0 | ppsz_filter[i] = psz_name; |
581 | 0 | i_count++; |
582 | 0 | } |
583 | 0 | else |
584 | 0 | { |
585 | 0 | for( int i = 0; i < i_count; i++ ) |
586 | 0 | { |
587 | 0 | if( !strcmp( ppsz_filter[i], psz_name ) ) |
588 | 0 | ppsz_filter[i] = ""; |
589 | 0 | } |
590 | 0 | } |
591 | 0 | size_t i_length = 0; |
592 | 0 | for( int i = 0; i < i_count; i++ ) |
593 | 0 | i_length += 1 + strlen( ppsz_filter[i] ); |
594 | |
|
595 | 0 | char *psz_new = malloc( i_length + 1 ); |
596 | |
|
597 | 0 | if( unlikely( !psz_new ) ) |
598 | 0 | { |
599 | 0 | free( ppsz_filter ); |
600 | 0 | free( psz_list ); |
601 | 0 | return VLC_ENOMEM; |
602 | 0 | } |
603 | | |
604 | 0 | *psz_new = '\0'; |
605 | 0 | for( int i = 0; i < i_count; i++ ) |
606 | 0 | { |
607 | 0 | if( *ppsz_filter[i] == '\0' ) |
608 | 0 | continue; |
609 | 0 | if( *psz_new ) |
610 | 0 | strcat( psz_new, ":" ); |
611 | 0 | strcat( psz_new, ppsz_filter[i] ); |
612 | 0 | } |
613 | 0 | free( ppsz_filter ); |
614 | 0 | free( psz_list ); |
615 | |
|
616 | 0 | var_SetString( p_aout, psz_variable, psz_new ); |
617 | 0 | free( psz_new ); |
618 | |
|
619 | 0 | return VLC_SUCCESS; |
620 | 0 | } |