/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-converter.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) 2005 Wim Taymans <wim at fluendo dot com> |
3 | | * (C) 2015 Wim Taymans <wim.taymans@gmail.com> |
4 | | * |
5 | | * audioconverter.c: Convert audio to different audio formats automatically |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Library General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Library General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Library General Public |
18 | | * License along with this library; if not, write to the |
19 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
20 | | * Boston, MA 02110-1301, USA. |
21 | | */ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | #include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <math.h> |
28 | | #include <string.h> |
29 | | |
30 | | #include "audio-converter.h" |
31 | | #include "gstaudiopack.h" |
32 | | |
33 | | /** |
34 | | * SECTION:gstaudioconverter |
35 | | * @title: GstAudioConverter |
36 | | * @short_description: Generic audio conversion |
37 | | * |
38 | | * This object is used to convert audio samples from one format to another. |
39 | | * The object can perform conversion of: |
40 | | * |
41 | | * * audio format with optional dithering and noise shaping |
42 | | * |
43 | | * * audio samplerate |
44 | | * |
45 | | * * audio channels and channel layout |
46 | | * |
47 | | */ |
48 | | |
49 | | #ifndef GST_DISABLE_GST_DEBUG |
50 | | #define GST_CAT_DEFAULT ensure_debug_category() |
51 | | static GstDebugCategory * |
52 | | ensure_debug_category (void) |
53 | 0 | { |
54 | 0 | static gsize cat_gonce = 0; |
55 | |
|
56 | 0 | if (g_once_init_enter (&cat_gonce)) { |
57 | 0 | gsize cat_done; |
58 | |
|
59 | 0 | cat_done = (gsize) _gst_debug_category_new ("audio-converter", 0, |
60 | 0 | "audio-converter object"); |
61 | |
|
62 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
63 | 0 | } |
64 | |
|
65 | 0 | return (GstDebugCategory *) cat_gonce; |
66 | 0 | } |
67 | | #else |
68 | | #define ensure_debug_category() /* NOOP */ |
69 | | #endif /* GST_DISABLE_GST_DEBUG */ |
70 | | |
71 | | typedef struct _AudioChain AudioChain; |
72 | | |
73 | | typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count); |
74 | | typedef gboolean (*AudioConvertSamplesFunc) (GstAudioConverter * convert, |
75 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
76 | | gpointer out[], gsize out_frames); |
77 | | typedef void (*AudioConvertEndianFunc) (gpointer dst, const gpointer src, |
78 | | gint count); |
79 | | |
80 | | /* int/int int/float float/int float/float |
81 | | * |
82 | | * unpack S32 S32 F64 F64 |
83 | | * convert S32->F64 |
84 | | * channel mix S32 F64 F64 F64 |
85 | | * convert F64->S32 |
86 | | * quantize S32 S32 |
87 | | * pack S32 F64 S32 F64 |
88 | | * |
89 | | * |
90 | | * interleave |
91 | | * deinterleave |
92 | | * resample |
93 | | */ |
94 | | struct _GstAudioConverter |
95 | | { |
96 | | GstAudioInfo in; |
97 | | GstAudioInfo out; |
98 | | |
99 | | GstStructure *config; |
100 | | |
101 | | GstAudioConverterFlags flags; |
102 | | GstAudioFormat current_format; |
103 | | GstAudioLayout current_layout; |
104 | | gint current_channels; |
105 | | |
106 | | gboolean in_writable; |
107 | | gpointer *in_data; |
108 | | gsize in_frames; |
109 | | gpointer *out_data; |
110 | | gsize out_frames; |
111 | | |
112 | | gboolean in_place; /* the conversion can be done in place; returned by gst_audio_converter_supports_inplace() */ |
113 | | |
114 | | gboolean passthrough; |
115 | | |
116 | | /* unpack */ |
117 | | gboolean in_default; |
118 | | gboolean unpack_ip; |
119 | | |
120 | | /* convert in */ |
121 | | AudioConvertFunc convert_in; |
122 | | |
123 | | /* channel mix */ |
124 | | gboolean mix_passthrough; |
125 | | GstAudioChannelMixer *mix; |
126 | | |
127 | | /* resample */ |
128 | | GstAudioResampler *resampler; |
129 | | |
130 | | /* convert out */ |
131 | | AudioConvertFunc convert_out; |
132 | | |
133 | | /* quant */ |
134 | | GstAudioQuantize *quant; |
135 | | |
136 | | /* change layout */ |
137 | | GstAudioFormat chlayout_format; |
138 | | GstAudioLayout chlayout_target; |
139 | | gint chlayout_channels; |
140 | | |
141 | | /* pack */ |
142 | | gboolean out_default; |
143 | | AudioChain *chain_end; /* NULL for empty chain or points to the last element in the chain */ |
144 | | |
145 | | /* endian swap */ |
146 | | AudioConvertEndianFunc swap_endian; |
147 | | |
148 | | AudioConvertSamplesFunc convert; |
149 | | }; |
150 | | |
151 | | static GstAudioConverter * |
152 | | gst_audio_converter_copy (GstAudioConverter * convert) |
153 | 0 | { |
154 | 0 | GstAudioConverter *res = |
155 | 0 | gst_audio_converter_new (convert->flags, &convert->in, &convert->out, |
156 | 0 | convert->config); |
157 | |
|
158 | 0 | return res; |
159 | 0 | } |
160 | | |
161 | 0 | G_DEFINE_BOXED_TYPE (GstAudioConverter, gst_audio_converter, |
162 | 0 | (GBoxedCopyFunc) gst_audio_converter_copy, |
163 | 0 | (GBoxedFreeFunc) gst_audio_converter_free); |
164 | 0 |
|
165 | 0 | typedef gboolean (*AudioChainFunc) (AudioChain * chain, gpointer user_data); |
166 | 0 | typedef gpointer *(*AudioChainAllocFunc) (AudioChain * chain, gsize num_samples, |
167 | 0 | gpointer user_data); |
168 | 0 |
|
169 | 0 | struct _AudioChain |
170 | 0 | { |
171 | 0 | AudioChain *prev; |
172 | 0 |
|
173 | 0 | AudioChainFunc make_func; |
174 | 0 | gpointer make_func_data; |
175 | 0 | GDestroyNotify make_func_notify; |
176 | 0 |
|
177 | 0 | const GstAudioFormatInfo *finfo; |
178 | 0 | gint stride; |
179 | 0 | gint inc; |
180 | 0 | gint blocks; |
181 | 0 |
|
182 | 0 | gboolean pass_alloc; |
183 | 0 | gboolean allow_ip; |
184 | 0 |
|
185 | 0 | AudioChainAllocFunc alloc_func; |
186 | 0 | gpointer alloc_data; |
187 | 0 |
|
188 | 0 | gpointer *tmp; |
189 | 0 | gsize allocated_samples; |
190 | 0 |
|
191 | 0 | gpointer *samples; |
192 | 0 | gsize num_samples; |
193 | 0 | }; |
194 | 0 |
|
195 | 0 | static AudioChain * |
196 | 0 | audio_chain_new (AudioChain * prev, GstAudioConverter * convert) |
197 | 0 | { |
198 | 0 | AudioChain *chain; |
199 | |
|
200 | 0 | chain = g_new0 (AudioChain, 1); |
201 | 0 | chain->prev = prev; |
202 | |
|
203 | 0 | if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
204 | 0 | chain->inc = 1; |
205 | 0 | chain->blocks = convert->current_channels; |
206 | 0 | } else { |
207 | 0 | chain->inc = convert->current_channels; |
208 | 0 | chain->blocks = 1; |
209 | 0 | } |
210 | 0 | chain->finfo = gst_audio_format_get_info (convert->current_format); |
211 | 0 | chain->stride = (chain->finfo->width * chain->inc) / 8; |
212 | |
|
213 | 0 | return chain; |
214 | 0 | } |
215 | | |
216 | | static void |
217 | | audio_chain_set_make_func (AudioChain * chain, |
218 | | AudioChainFunc make_func, gpointer user_data, GDestroyNotify notify) |
219 | 0 | { |
220 | 0 | chain->make_func = make_func; |
221 | 0 | chain->make_func_data = user_data; |
222 | 0 | chain->make_func_notify = notify; |
223 | 0 | } |
224 | | |
225 | | static void |
226 | | audio_chain_free (AudioChain * chain) |
227 | 0 | { |
228 | 0 | GST_LOG ("free chain %p", chain); |
229 | 0 | if (chain->make_func_notify) |
230 | 0 | chain->make_func_notify (chain->make_func_data); |
231 | 0 | g_free (chain->tmp); |
232 | 0 | g_free (chain); |
233 | 0 | } |
234 | | |
235 | | static gpointer * |
236 | | audio_chain_alloc_samples (AudioChain * chain, gsize num_samples) |
237 | 0 | { |
238 | 0 | return chain->alloc_func (chain, num_samples, chain->alloc_data); |
239 | 0 | } |
240 | | |
241 | | static void |
242 | | audio_chain_set_samples (AudioChain * chain, gpointer * samples, |
243 | | gsize num_samples) |
244 | 0 | { |
245 | 0 | GST_LOG ("set samples %p %" G_GSIZE_FORMAT, samples, num_samples); |
246 | |
|
247 | 0 | chain->samples = samples; |
248 | 0 | chain->num_samples = num_samples; |
249 | 0 | } |
250 | | |
251 | | static gpointer * |
252 | | audio_chain_get_samples (AudioChain * chain, gsize * avail) |
253 | 0 | { |
254 | 0 | gpointer *res; |
255 | |
|
256 | 0 | if (!chain->samples) |
257 | 0 | chain->make_func (chain, chain->make_func_data); |
258 | |
|
259 | 0 | res = chain->samples; |
260 | 0 | *avail = chain->num_samples; |
261 | 0 | chain->samples = NULL; |
262 | |
|
263 | 0 | return res; |
264 | 0 | } |
265 | | |
266 | | static guint |
267 | | get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def) |
268 | 0 | { |
269 | 0 | guint res; |
270 | 0 | if (!gst_structure_get_uint (convert->config, opt, &res)) |
271 | 0 | res = def; |
272 | 0 | return res; |
273 | 0 | } |
274 | | |
275 | | static gint |
276 | | get_opt_enum (GstAudioConverter * convert, const gchar * opt, GType type, |
277 | | gint def) |
278 | 0 | { |
279 | 0 | gint res; |
280 | 0 | if (!gst_structure_get_enum (convert->config, opt, type, &res)) |
281 | 0 | res = def; |
282 | 0 | return res; |
283 | 0 | } |
284 | | |
285 | | static const GValue * |
286 | | get_opt_value (GstAudioConverter * convert, const gchar * opt) |
287 | 0 | { |
288 | 0 | return gst_structure_get_value (convert->config, opt); |
289 | 0 | } |
290 | | |
291 | 0 | #define DEFAULT_OPT_RESAMPLER_METHOD GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL |
292 | 0 | #define DEFAULT_OPT_DITHER_METHOD GST_AUDIO_DITHER_NONE |
293 | 0 | #define DEFAULT_OPT_DITHER_THRESHOLD 20 |
294 | 0 | #define DEFAULT_OPT_NOISE_SHAPING_METHOD GST_AUDIO_NOISE_SHAPING_NONE |
295 | | #define DEFAULT_OPT_QUANTIZATION 1 |
296 | | |
297 | 0 | #define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \ |
298 | 0 | GST_AUDIO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_AUDIO_RESAMPLER_METHOD, \ |
299 | 0 | DEFAULT_OPT_RESAMPLER_METHOD) |
300 | 0 | #define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \ |
301 | 0 | GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD, \ |
302 | 0 | DEFAULT_OPT_DITHER_METHOD) |
303 | 0 | #define GET_OPT_DITHER_THRESHOLD(c) get_opt_uint(c, \ |
304 | 0 | GST_AUDIO_CONVERTER_OPT_DITHER_THRESHOLD, DEFAULT_OPT_DITHER_THRESHOLD) |
305 | 0 | #define GET_OPT_NOISE_SHAPING_METHOD(c) get_opt_enum(c, \ |
306 | 0 | GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, \ |
307 | 0 | DEFAULT_OPT_NOISE_SHAPING_METHOD) |
308 | | #define GET_OPT_QUANTIZATION(c) get_opt_uint(c, \ |
309 | | GST_AUDIO_CONVERTER_OPT_QUANTIZATION, DEFAULT_OPT_QUANTIZATION) |
310 | 0 | #define GET_OPT_MIX_MATRIX(c) get_opt_value(c, \ |
311 | 0 | GST_AUDIO_CONVERTER_OPT_MIX_MATRIX) |
312 | | |
313 | | static gboolean |
314 | | copy_config (const GstIdStr * fieldname, const GValue * value, |
315 | | gpointer user_data) |
316 | 0 | { |
317 | 0 | GstAudioConverter *convert = user_data; |
318 | |
|
319 | 0 | gst_structure_id_str_set_value (convert->config, fieldname, value); |
320 | |
|
321 | 0 | return TRUE; |
322 | 0 | } |
323 | | |
324 | | /** |
325 | | * gst_audio_converter_update_config: |
326 | | * @convert: a #GstAudioConverter |
327 | | * @in_rate: input rate |
328 | | * @out_rate: output rate |
329 | | * @config: (transfer full) (allow-none): a #GstStructure or %NULL |
330 | | * |
331 | | * Set @in_rate, @out_rate and @config as extra configuration for @convert. |
332 | | * |
333 | | * @in_rate and @out_rate specify the new sample rates of input and output |
334 | | * formats. A value of 0 leaves the sample rate unchanged. |
335 | | * |
336 | | * @config can be %NULL, in which case, the current configuration is not |
337 | | * changed. |
338 | | * |
339 | | * If the parameters in @config can not be set exactly, this function returns |
340 | | * %FALSE and will try to update as much state as possible. The new state can |
341 | | * then be retrieved and refined with gst_audio_converter_get_config(). |
342 | | * |
343 | | * Look at the `GST_AUDIO_CONVERTER_OPT_*` fields to check valid configuration |
344 | | * option and values. |
345 | | * |
346 | | * Returns: %TRUE when the new parameters could be set |
347 | | */ |
348 | | gboolean |
349 | | gst_audio_converter_update_config (GstAudioConverter * convert, |
350 | | gint in_rate, gint out_rate, GstStructure * config) |
351 | 0 | { |
352 | 0 | g_return_val_if_fail (convert != NULL, FALSE); |
353 | 0 | g_return_val_if_fail ((in_rate == 0 && out_rate == 0) || |
354 | 0 | convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, FALSE); |
355 | | |
356 | 0 | GST_LOG ("new rate %d -> %d", in_rate, out_rate); |
357 | |
|
358 | 0 | if (in_rate <= 0) |
359 | 0 | in_rate = convert->in.rate; |
360 | 0 | if (out_rate <= 0) |
361 | 0 | out_rate = convert->out.rate; |
362 | |
|
363 | 0 | convert->in.rate = in_rate; |
364 | 0 | convert->out.rate = out_rate; |
365 | |
|
366 | 0 | if (convert->resampler) |
367 | 0 | gst_audio_resampler_update (convert->resampler, in_rate, out_rate, config); |
368 | |
|
369 | 0 | if (config) { |
370 | 0 | gst_structure_foreach_id_str (config, copy_config, convert); |
371 | 0 | gst_structure_free (config); |
372 | 0 | } |
373 | |
|
374 | 0 | return TRUE; |
375 | 0 | } |
376 | | |
377 | | /** |
378 | | * gst_audio_converter_get_config: |
379 | | * @convert: a #GstAudioConverter |
380 | | * @in_rate: (out) (optional): result input rate |
381 | | * @out_rate: (out) (optional): result output rate |
382 | | * |
383 | | * Get the current configuration of @convert. |
384 | | * |
385 | | * Returns: (transfer none): |
386 | | * a #GstStructure that remains valid for as long as @convert is valid |
387 | | * or until gst_audio_converter_update_config() is called. |
388 | | */ |
389 | | const GstStructure * |
390 | | gst_audio_converter_get_config (GstAudioConverter * convert, |
391 | | gint * in_rate, gint * out_rate) |
392 | 0 | { |
393 | 0 | g_return_val_if_fail (convert != NULL, NULL); |
394 | | |
395 | 0 | if (in_rate) |
396 | 0 | *in_rate = convert->in.rate; |
397 | 0 | if (out_rate) |
398 | 0 | *out_rate = convert->out.rate; |
399 | |
|
400 | 0 | return convert->config; |
401 | 0 | } |
402 | | |
403 | | static gpointer * |
404 | | get_output_samples (AudioChain * chain, gsize num_samples, gpointer user_data) |
405 | 0 | { |
406 | 0 | GstAudioConverter *convert = user_data; |
407 | |
|
408 | 0 | GST_LOG ("output samples %p %" G_GSIZE_FORMAT, convert->out_data, |
409 | 0 | num_samples); |
410 | |
|
411 | 0 | return convert->out_data; |
412 | 0 | } |
413 | | |
414 | 0 | #define MEM_ALIGN(m,a) ((gint8 *)((guintptr)((gint8 *)(m) + ((a)-1)) & ~((a)-1))) |
415 | 0 | #define ALIGN 16 |
416 | | |
417 | | static gpointer * |
418 | | get_temp_samples (AudioChain * chain, gsize num_samples, gpointer user_data) |
419 | 0 | { |
420 | 0 | if (num_samples > chain->allocated_samples) { |
421 | 0 | gint i; |
422 | 0 | gint8 *s; |
423 | 0 | gsize stride = GST_ROUND_UP_N (num_samples * chain->stride, ALIGN); |
424 | | /* first part contains the pointers, second part the data, add some extra bytes |
425 | | * for alignment */ |
426 | 0 | gsize needed = (stride + sizeof (gpointer)) * chain->blocks + ALIGN - 1; |
427 | |
|
428 | 0 | GST_DEBUG ("alloc samples %d %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, |
429 | 0 | chain->stride, num_samples, needed); |
430 | 0 | chain->tmp = g_realloc (chain->tmp, needed); |
431 | 0 | chain->allocated_samples = num_samples; |
432 | | |
433 | | /* pointer to the data, make sure it's 16 bytes aligned */ |
434 | 0 | s = MEM_ALIGN (&chain->tmp[chain->blocks], ALIGN); |
435 | | |
436 | | /* set up the pointers */ |
437 | 0 | for (i = 0; i < chain->blocks; i++) |
438 | 0 | chain->tmp[i] = s + i * stride; |
439 | 0 | } |
440 | 0 | GST_LOG ("temp samples %p %" G_GSIZE_FORMAT, chain->tmp, num_samples); |
441 | |
|
442 | 0 | return chain->tmp; |
443 | 0 | } |
444 | | |
445 | | static gboolean |
446 | | do_unpack (AudioChain * chain, gpointer user_data) |
447 | 0 | { |
448 | 0 | GstAudioConverter *convert = user_data; |
449 | 0 | gsize num_samples; |
450 | 0 | gpointer *tmp; |
451 | 0 | gboolean in_writable; |
452 | |
|
453 | 0 | in_writable = convert->in_writable; |
454 | 0 | num_samples = convert->in_frames; |
455 | |
|
456 | 0 | if (!chain->allow_ip || !in_writable || !convert->in_default) { |
457 | 0 | gint i; |
458 | |
|
459 | 0 | if (in_writable && chain->allow_ip) { |
460 | 0 | tmp = convert->in_data; |
461 | 0 | GST_LOG ("unpack in-place %p, %" G_GSIZE_FORMAT, tmp, num_samples); |
462 | 0 | } else { |
463 | 0 | tmp = audio_chain_alloc_samples (chain, num_samples); |
464 | 0 | GST_LOG ("unpack to tmp %p, %" G_GSIZE_FORMAT, tmp, num_samples); |
465 | 0 | } |
466 | |
|
467 | 0 | if (convert->in_data) { |
468 | 0 | for (i = 0; i < chain->blocks; i++) { |
469 | 0 | if (convert->in_default) { |
470 | 0 | GST_LOG ("copy %p, %p, %" G_GSIZE_FORMAT, tmp[i], convert->in_data[i], |
471 | 0 | num_samples); |
472 | 0 | memcpy (tmp[i], convert->in_data[i], num_samples * chain->stride); |
473 | 0 | } else { |
474 | 0 | GST_LOG ("unpack %p, %p, %" G_GSIZE_FORMAT, tmp[i], |
475 | 0 | convert->in_data[i], num_samples); |
476 | 0 | convert->in.finfo->unpack_func (convert->in.finfo, |
477 | 0 | GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, tmp[i], convert->in_data[i], |
478 | 0 | num_samples * chain->inc); |
479 | 0 | } |
480 | 0 | } |
481 | 0 | } else { |
482 | 0 | for (i = 0; i < chain->blocks; i++) { |
483 | 0 | gst_audio_format_info_fill_silence (chain->finfo, tmp[i], |
484 | 0 | num_samples * chain->inc); |
485 | 0 | } |
486 | 0 | } |
487 | 0 | } else { |
488 | 0 | tmp = convert->in_data; |
489 | 0 | GST_LOG ("get in samples %p", tmp); |
490 | 0 | } |
491 | 0 | audio_chain_set_samples (chain, tmp, num_samples); |
492 | |
|
493 | 0 | return TRUE; |
494 | 0 | } |
495 | | |
496 | | static gboolean |
497 | | do_convert_in (AudioChain * chain, gpointer user_data) |
498 | 0 | { |
499 | 0 | gsize num_samples; |
500 | 0 | GstAudioConverter *convert = user_data; |
501 | 0 | gpointer *in, *out; |
502 | 0 | gint i; |
503 | |
|
504 | 0 | in = audio_chain_get_samples (chain->prev, &num_samples); |
505 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); |
506 | 0 | GST_LOG ("convert in %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples); |
507 | |
|
508 | 0 | for (i = 0; i < chain->blocks; i++) |
509 | 0 | convert->convert_in (out[i], in[i], num_samples * chain->inc); |
510 | |
|
511 | 0 | audio_chain_set_samples (chain, out, num_samples); |
512 | |
|
513 | 0 | return TRUE; |
514 | 0 | } |
515 | | |
516 | | static gboolean |
517 | | do_mix (AudioChain * chain, gpointer user_data) |
518 | 0 | { |
519 | 0 | gsize num_samples; |
520 | 0 | GstAudioConverter *convert = user_data; |
521 | 0 | gpointer *in, *out; |
522 | |
|
523 | 0 | in = audio_chain_get_samples (chain->prev, &num_samples); |
524 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); |
525 | 0 | GST_LOG ("mix %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples); |
526 | |
|
527 | 0 | gst_audio_channel_mixer_samples (convert->mix, in, out, num_samples); |
528 | |
|
529 | 0 | audio_chain_set_samples (chain, out, num_samples); |
530 | |
|
531 | 0 | return TRUE; |
532 | 0 | } |
533 | | |
534 | | static gboolean |
535 | | do_resample (AudioChain * chain, gpointer user_data) |
536 | 0 | { |
537 | 0 | GstAudioConverter *convert = user_data; |
538 | 0 | gpointer *in, *out; |
539 | 0 | gsize in_frames, out_frames; |
540 | |
|
541 | 0 | in = audio_chain_get_samples (chain->prev, &in_frames); |
542 | 0 | out_frames = convert->out_frames; |
543 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames)); |
544 | |
|
545 | 0 | GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, in, |
546 | 0 | out, in_frames, out_frames); |
547 | |
|
548 | 0 | gst_audio_resampler_resample (convert->resampler, in, in_frames, out, |
549 | 0 | out_frames); |
550 | |
|
551 | 0 | audio_chain_set_samples (chain, out, out_frames); |
552 | |
|
553 | 0 | return TRUE; |
554 | 0 | } |
555 | | |
556 | | static gboolean |
557 | | do_convert_out (AudioChain * chain, gpointer user_data) |
558 | 0 | { |
559 | 0 | GstAudioConverter *convert = user_data; |
560 | 0 | gsize num_samples; |
561 | 0 | gpointer *in, *out; |
562 | 0 | gint i; |
563 | |
|
564 | 0 | in = audio_chain_get_samples (chain->prev, &num_samples); |
565 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); |
566 | 0 | GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); |
567 | |
|
568 | 0 | for (i = 0; i < chain->blocks; i++) |
569 | 0 | convert->convert_out (out[i], in[i], num_samples * chain->inc); |
570 | |
|
571 | 0 | audio_chain_set_samples (chain, out, num_samples); |
572 | |
|
573 | 0 | return TRUE; |
574 | 0 | } |
575 | | |
576 | | static gboolean |
577 | | do_quantize (AudioChain * chain, gpointer user_data) |
578 | 0 | { |
579 | 0 | GstAudioConverter *convert = user_data; |
580 | 0 | gsize num_samples; |
581 | 0 | gpointer *in, *out; |
582 | |
|
583 | 0 | in = audio_chain_get_samples (chain->prev, &num_samples); |
584 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); |
585 | 0 | GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); |
586 | |
|
587 | 0 | if (in && out) |
588 | 0 | gst_audio_quantize_samples (convert->quant, in, out, num_samples); |
589 | |
|
590 | 0 | audio_chain_set_samples (chain, out, num_samples); |
591 | |
|
592 | 0 | return TRUE; |
593 | 0 | } |
594 | | |
595 | | #define MAKE_INTERLEAVE_FUNC(type) \ |
596 | | static inline void \ |
597 | | interleave_##type (const type * in[], type * out[], \ |
598 | 0 | gsize num_samples, gint channels) \ |
599 | 0 | { \ |
600 | 0 | gsize s; \ |
601 | 0 | gint c; \ |
602 | 0 | for (s = 0; s < num_samples; s++) { \ |
603 | 0 | for (c = 0; c < channels; c++) { \ |
604 | 0 | out[0][s * channels + c] = in[c][s]; \ |
605 | 0 | } \ |
606 | 0 | } \ |
607 | 0 | } Unexecuted instantiation: audio-converter.c:interleave_gint16 Unexecuted instantiation: audio-converter.c:interleave_gint32 Unexecuted instantiation: audio-converter.c:interleave_gfloat Unexecuted instantiation: audio-converter.c:interleave_gdouble |
608 | | |
609 | | #define MAKE_DEINTERLEAVE_FUNC(type) \ |
610 | | static inline void \ |
611 | | deinterleave_##type (const type * in[], type * out[], \ |
612 | 0 | gsize num_samples, gint channels) \ |
613 | 0 | { \ |
614 | 0 | gsize s; \ |
615 | 0 | gint c; \ |
616 | 0 | for (s = 0; s < num_samples; s++) { \ |
617 | 0 | for (c = 0; c < channels; c++) { \ |
618 | 0 | out[c][s] = in[0][s * channels + c]; \ |
619 | 0 | } \ |
620 | 0 | } \ |
621 | 0 | } Unexecuted instantiation: audio-converter.c:deinterleave_gint16 Unexecuted instantiation: audio-converter.c:deinterleave_gint32 Unexecuted instantiation: audio-converter.c:deinterleave_gfloat Unexecuted instantiation: audio-converter.c:deinterleave_gdouble |
622 | | |
623 | | MAKE_INTERLEAVE_FUNC (gint16); |
624 | | MAKE_INTERLEAVE_FUNC (gint32); |
625 | | MAKE_INTERLEAVE_FUNC (gfloat); |
626 | | MAKE_INTERLEAVE_FUNC (gdouble); |
627 | | MAKE_DEINTERLEAVE_FUNC (gint16); |
628 | | MAKE_DEINTERLEAVE_FUNC (gint32); |
629 | | MAKE_DEINTERLEAVE_FUNC (gfloat); |
630 | | MAKE_DEINTERLEAVE_FUNC (gdouble); |
631 | | |
632 | | static gboolean |
633 | | do_change_layout (AudioChain * chain, gpointer user_data) |
634 | 0 | { |
635 | 0 | GstAudioConverter *convert = user_data; |
636 | 0 | GstAudioFormat format = convert->chlayout_format; |
637 | 0 | GstAudioLayout out_layout = convert->chlayout_target; |
638 | 0 | gint channels = convert->chlayout_channels; |
639 | 0 | gsize num_samples; |
640 | 0 | gpointer *in, *out; |
641 | |
|
642 | 0 | in = audio_chain_get_samples (chain->prev, &num_samples); |
643 | 0 | out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); |
644 | |
|
645 | 0 | if (out_layout == GST_AUDIO_LAYOUT_INTERLEAVED) { |
646 | | /* interleave */ |
647 | 0 | GST_LOG ("interleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); |
648 | 0 | switch (format) { |
649 | 0 | case GST_AUDIO_FORMAT_S16: |
650 | 0 | interleave_gint16 ((const gint16 **) in, (gint16 **) out, |
651 | 0 | num_samples, channels); |
652 | 0 | break; |
653 | 0 | case GST_AUDIO_FORMAT_S32: |
654 | 0 | interleave_gint32 ((const gint32 **) in, (gint32 **) out, |
655 | 0 | num_samples, channels); |
656 | 0 | break; |
657 | 0 | case GST_AUDIO_FORMAT_F32: |
658 | 0 | interleave_gfloat ((const gfloat **) in, (gfloat **) out, |
659 | 0 | num_samples, channels); |
660 | 0 | break; |
661 | 0 | case GST_AUDIO_FORMAT_F64: |
662 | 0 | interleave_gdouble ((const gdouble **) in, (gdouble **) out, |
663 | 0 | num_samples, channels); |
664 | 0 | break; |
665 | 0 | default: |
666 | 0 | g_assert_not_reached (); |
667 | 0 | break; |
668 | 0 | } |
669 | 0 | } else { |
670 | | /* deinterleave */ |
671 | 0 | GST_LOG ("deinterleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); |
672 | 0 | switch (format) { |
673 | 0 | case GST_AUDIO_FORMAT_S16: |
674 | 0 | deinterleave_gint16 ((const gint16 **) in, (gint16 **) out, |
675 | 0 | num_samples, channels); |
676 | 0 | break; |
677 | 0 | case GST_AUDIO_FORMAT_S32: |
678 | 0 | deinterleave_gint32 ((const gint32 **) in, (gint32 **) out, |
679 | 0 | num_samples, channels); |
680 | 0 | break; |
681 | 0 | case GST_AUDIO_FORMAT_F32: |
682 | 0 | deinterleave_gfloat ((const gfloat **) in, (gfloat **) out, |
683 | 0 | num_samples, channels); |
684 | 0 | break; |
685 | 0 | case GST_AUDIO_FORMAT_F64: |
686 | 0 | deinterleave_gdouble ((const gdouble **) in, (gdouble **) out, |
687 | 0 | num_samples, channels); |
688 | 0 | break; |
689 | 0 | default: |
690 | 0 | g_assert_not_reached (); |
691 | 0 | break; |
692 | 0 | } |
693 | 0 | } |
694 | | |
695 | 0 | audio_chain_set_samples (chain, out, num_samples); |
696 | 0 | return TRUE; |
697 | 0 | } |
698 | | |
699 | | static gboolean |
700 | | is_intermediate_format (GstAudioFormat format) |
701 | 0 | { |
702 | 0 | return (format == GST_AUDIO_FORMAT_S16 || |
703 | 0 | format == GST_AUDIO_FORMAT_S32 || |
704 | 0 | format == GST_AUDIO_FORMAT_F32 || format == GST_AUDIO_FORMAT_F64); |
705 | 0 | } |
706 | | |
707 | | static AudioChain * |
708 | | chain_unpack (GstAudioConverter * convert) |
709 | 0 | { |
710 | 0 | AudioChain *prev; |
711 | 0 | GstAudioInfo *in = &convert->in; |
712 | 0 | GstAudioInfo *out = &convert->out; |
713 | 0 | gboolean same_format; |
714 | |
|
715 | 0 | same_format = in->finfo->format == out->finfo->format; |
716 | | |
717 | | /* do not unpack if we have the same input format as the output format |
718 | | * and it is a possible intermediate format */ |
719 | 0 | if (same_format && is_intermediate_format (in->finfo->format)) { |
720 | 0 | convert->current_format = in->finfo->format; |
721 | 0 | } else { |
722 | 0 | convert->current_format = in->finfo->unpack_format; |
723 | 0 | } |
724 | 0 | convert->current_layout = in->layout; |
725 | 0 | convert->current_channels = in->channels; |
726 | |
|
727 | 0 | convert->in_default = convert->current_format == in->finfo->format; |
728 | |
|
729 | 0 | GST_INFO ("unpack format %s to %s", |
730 | 0 | gst_audio_format_to_string (in->finfo->format), |
731 | 0 | gst_audio_format_to_string (convert->current_format)); |
732 | |
|
733 | 0 | prev = audio_chain_new (NULL, convert); |
734 | 0 | prev->allow_ip = prev->finfo->width <= in->finfo->width; |
735 | 0 | prev->pass_alloc = FALSE; |
736 | 0 | audio_chain_set_make_func (prev, do_unpack, convert, NULL); |
737 | |
|
738 | 0 | return prev; |
739 | 0 | } |
740 | | |
741 | | static AudioChain * |
742 | | chain_convert_in (GstAudioConverter * convert, AudioChain * prev) |
743 | 0 | { |
744 | 0 | gboolean in_int, out_int; |
745 | 0 | GstAudioInfo *in = &convert->in; |
746 | 0 | GstAudioInfo *out = &convert->out; |
747 | |
|
748 | 0 | in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo); |
749 | 0 | out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo); |
750 | |
|
751 | 0 | if (in_int && !out_int) { |
752 | 0 | GST_INFO ("convert S32 to F64"); |
753 | 0 | convert->convert_in = (AudioConvertFunc) audio_orc_s32_to_double; |
754 | 0 | convert->current_format = GST_AUDIO_FORMAT_F64; |
755 | |
|
756 | 0 | prev = audio_chain_new (prev, convert); |
757 | 0 | prev->allow_ip = FALSE; |
758 | 0 | prev->pass_alloc = FALSE; |
759 | 0 | audio_chain_set_make_func (prev, do_convert_in, convert, NULL); |
760 | 0 | } |
761 | 0 | return prev; |
762 | 0 | } |
763 | | |
764 | | static gboolean |
765 | | check_mix_matrix (guint in_channels, guint out_channels, const GValue * value) |
766 | 0 | { |
767 | 0 | guint i, j; |
768 | | |
769 | | /* audio-channel-mixer will generate an identity matrix */ |
770 | 0 | if (gst_value_array_get_size (value) == 0) |
771 | 0 | return TRUE; |
772 | | |
773 | 0 | if (gst_value_array_get_size (value) != out_channels) { |
774 | 0 | GST_ERROR ("Invalid mix matrix size, should be %d", out_channels); |
775 | 0 | goto fail; |
776 | 0 | } |
777 | | |
778 | 0 | for (j = 0; j < out_channels; j++) { |
779 | 0 | const GValue *row = gst_value_array_get_value (value, j); |
780 | |
|
781 | 0 | if (gst_value_array_get_size (row) != in_channels) { |
782 | 0 | GST_ERROR ("Invalid mix matrix row size, should be %d", in_channels); |
783 | 0 | goto fail; |
784 | 0 | } |
785 | | |
786 | 0 | for (i = 0; i < in_channels; i++) { |
787 | 0 | const GValue *itm; |
788 | |
|
789 | 0 | itm = gst_value_array_get_value (row, i); |
790 | 0 | if (!G_VALUE_HOLDS_FLOAT (itm) && |
791 | 0 | !G_VALUE_HOLDS_DOUBLE (itm) && |
792 | 0 | !G_VALUE_HOLDS_INT (itm) && |
793 | 0 | !G_VALUE_HOLDS_INT64 (itm) && |
794 | 0 | !G_VALUE_HOLDS_UINT (itm) && !G_VALUE_HOLDS_UINT64 (itm)) { |
795 | 0 | GST_ERROR |
796 | 0 | ("Invalid mix matrix element type, should be float or double or integer"); |
797 | 0 | goto fail; |
798 | 0 | } |
799 | 0 | } |
800 | 0 | } |
801 | | |
802 | 0 | return TRUE; |
803 | | |
804 | 0 | fail: |
805 | 0 | return FALSE; |
806 | 0 | } |
807 | | |
808 | | static gfloat ** |
809 | | mix_matrix_from_g_value (guint in_channels, guint out_channels, |
810 | | const GValue * value) |
811 | 0 | { |
812 | 0 | guint i, j; |
813 | 0 | gfloat **matrix = g_new (gfloat *, in_channels); |
814 | |
|
815 | 0 | for (i = 0; i < in_channels; i++) |
816 | 0 | matrix[i] = g_new (gfloat, out_channels); |
817 | |
|
818 | 0 | for (j = 0; j < out_channels; j++) { |
819 | 0 | const GValue *row = gst_value_array_get_value (value, j); |
820 | |
|
821 | 0 | for (i = 0; i < in_channels; i++) { |
822 | 0 | const GValue *itm; |
823 | 0 | gfloat coefficient; |
824 | |
|
825 | 0 | itm = gst_value_array_get_value (row, i); |
826 | 0 | if (G_VALUE_HOLDS_FLOAT (itm)) |
827 | 0 | coefficient = g_value_get_float (itm); |
828 | 0 | else if (G_VALUE_HOLDS_DOUBLE (itm)) |
829 | 0 | coefficient = g_value_get_double (itm); |
830 | 0 | else if (G_VALUE_HOLDS_INT (itm)) |
831 | 0 | coefficient = g_value_get_int (itm); |
832 | 0 | else if (G_VALUE_HOLDS_INT64 (itm)) |
833 | 0 | coefficient = g_value_get_int64 (itm); |
834 | 0 | else if (G_VALUE_HOLDS_UINT (itm)) |
835 | 0 | coefficient = g_value_get_uint (itm); |
836 | 0 | else if (G_VALUE_HOLDS_UINT64 (itm)) |
837 | 0 | coefficient = g_value_get_uint64 (itm); |
838 | 0 | else |
839 | 0 | g_assert_not_reached (); |
840 | | |
841 | 0 | matrix[i][j] = coefficient; |
842 | 0 | } |
843 | 0 | } |
844 | | |
845 | 0 | return matrix; |
846 | 0 | } |
847 | | |
848 | | static AudioChain * |
849 | | chain_mix (GstAudioConverter * convert, AudioChain * prev) |
850 | 0 | { |
851 | 0 | GstAudioInfo *in = &convert->in; |
852 | 0 | GstAudioInfo *out = &convert->out; |
853 | 0 | GstAudioFormat format = convert->current_format; |
854 | 0 | const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert); |
855 | 0 | GstAudioChannelMixerFlags flags = 0; |
856 | |
|
857 | 0 | convert->current_channels = out->channels; |
858 | | |
859 | | /* keep the input layout */ |
860 | 0 | if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
861 | 0 | flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN; |
862 | 0 | flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT; |
863 | 0 | } |
864 | |
|
865 | 0 | if (opt_matrix) { |
866 | 0 | gfloat **matrix = NULL; |
867 | |
|
868 | 0 | if (gst_value_array_get_size (opt_matrix)) |
869 | 0 | matrix = |
870 | 0 | mix_matrix_from_g_value (in->channels, out->channels, opt_matrix); |
871 | |
|
872 | 0 | convert->mix = |
873 | 0 | gst_audio_channel_mixer_new_with_matrix (flags, format, in->channels, |
874 | 0 | out->channels, matrix); |
875 | 0 | } else { |
876 | 0 | flags |= |
877 | 0 | GST_AUDIO_INFO_IS_UNPOSITIONED (in) ? |
878 | 0 | GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0; |
879 | 0 | flags |= |
880 | 0 | GST_AUDIO_INFO_IS_UNPOSITIONED (out) ? |
881 | 0 | GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_OUT : 0; |
882 | |
|
883 | 0 | convert->mix = |
884 | 0 | gst_audio_channel_mixer_new (flags, format, in->channels, in->position, |
885 | 0 | out->channels, out->position); |
886 | 0 | } |
887 | |
|
888 | 0 | convert->mix_passthrough = |
889 | 0 | gst_audio_channel_mixer_is_passthrough (convert->mix); |
890 | 0 | GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d", |
891 | 0 | gst_audio_format_to_string (format), convert->mix_passthrough, |
892 | 0 | in->channels, out->channels); |
893 | |
|
894 | 0 | if (!convert->mix_passthrough) { |
895 | 0 | prev = audio_chain_new (prev, convert); |
896 | 0 | prev->allow_ip = FALSE; |
897 | 0 | prev->pass_alloc = FALSE; |
898 | 0 | audio_chain_set_make_func (prev, do_mix, convert, NULL); |
899 | 0 | } |
900 | 0 | return prev; |
901 | 0 | } |
902 | | |
903 | | static AudioChain * |
904 | | chain_resample (GstAudioConverter * convert, AudioChain * prev) |
905 | 0 | { |
906 | 0 | GstAudioInfo *in = &convert->in; |
907 | 0 | GstAudioInfo *out = &convert->out; |
908 | 0 | GstAudioResamplerMethod method; |
909 | 0 | GstAudioResamplerFlags flags; |
910 | 0 | GstAudioFormat format = convert->current_format; |
911 | 0 | gint channels = convert->current_channels; |
912 | 0 | gboolean variable_rate; |
913 | |
|
914 | 0 | variable_rate = convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE; |
915 | |
|
916 | 0 | if (in->rate != out->rate || variable_rate) { |
917 | 0 | method = GET_OPT_RESAMPLER_METHOD (convert); |
918 | |
|
919 | 0 | flags = 0; |
920 | 0 | if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
921 | 0 | flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN; |
922 | 0 | } |
923 | | /* if the resampler is activated, it is optimal to change layout here */ |
924 | 0 | if (out->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
925 | 0 | flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT; |
926 | 0 | } |
927 | 0 | convert->current_layout = out->layout; |
928 | |
|
929 | 0 | if (variable_rate) |
930 | 0 | flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE; |
931 | |
|
932 | 0 | convert->resampler = |
933 | 0 | gst_audio_resampler_new (method, flags, format, channels, in->rate, |
934 | 0 | out->rate, convert->config); |
935 | |
|
936 | 0 | prev = audio_chain_new (prev, convert); |
937 | 0 | prev->allow_ip = FALSE; |
938 | 0 | prev->pass_alloc = FALSE; |
939 | 0 | audio_chain_set_make_func (prev, do_resample, convert, NULL); |
940 | 0 | } |
941 | 0 | return prev; |
942 | 0 | } |
943 | | |
944 | | static AudioChain * |
945 | | chain_convert_out (GstAudioConverter * convert, AudioChain * prev) |
946 | 0 | { |
947 | 0 | gboolean in_int, out_int; |
948 | 0 | GstAudioInfo *in = &convert->in; |
949 | 0 | GstAudioInfo *out = &convert->out; |
950 | |
|
951 | 0 | in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo); |
952 | 0 | out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo); |
953 | |
|
954 | 0 | if (!in_int && out_int) { |
955 | 0 | convert->convert_out = (AudioConvertFunc) audio_orc_double_to_s32; |
956 | 0 | convert->current_format = GST_AUDIO_FORMAT_S32; |
957 | |
|
958 | 0 | GST_INFO ("convert F64 to S32"); |
959 | 0 | prev = audio_chain_new (prev, convert); |
960 | 0 | prev->allow_ip = TRUE; |
961 | 0 | prev->pass_alloc = FALSE; |
962 | 0 | audio_chain_set_make_func (prev, do_convert_out, convert, NULL); |
963 | 0 | } |
964 | 0 | return prev; |
965 | 0 | } |
966 | | |
967 | | static AudioChain * |
968 | | chain_quantize (GstAudioConverter * convert, AudioChain * prev) |
969 | 0 | { |
970 | 0 | const GstAudioFormatInfo *cur_finfo; |
971 | 0 | GstAudioInfo *out = &convert->out; |
972 | 0 | gint in_depth, out_depth; |
973 | 0 | gboolean in_int, out_int; |
974 | 0 | GstAudioDitherMethod dither; |
975 | 0 | guint dither_threshold; |
976 | 0 | GstAudioNoiseShapingMethod ns; |
977 | |
|
978 | 0 | dither = GET_OPT_DITHER_METHOD (convert); |
979 | 0 | dither_threshold = GET_OPT_DITHER_THRESHOLD (convert); |
980 | 0 | ns = GET_OPT_NOISE_SHAPING_METHOD (convert); |
981 | |
|
982 | 0 | cur_finfo = gst_audio_format_get_info (convert->current_format); |
983 | |
|
984 | 0 | in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (cur_finfo); |
985 | 0 | out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo); |
986 | 0 | GST_INFO ("depth in %d, out %d", in_depth, out_depth); |
987 | |
|
988 | 0 | in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (cur_finfo); |
989 | 0 | out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo); |
990 | | |
991 | | /* Don't dither or apply noise shaping if target depth is bigger than 20 bits |
992 | | * as DA converters only can do a SNR up to 20 bits in reality. |
993 | | * Also don't dither or apply noise shaping if target depth is larger than |
994 | | * source depth. */ |
995 | 0 | if (out_depth > dither_threshold || (in_int && out_depth >= in_depth)) { |
996 | 0 | dither = GST_AUDIO_DITHER_NONE; |
997 | 0 | ns = GST_AUDIO_NOISE_SHAPING_NONE; |
998 | 0 | GST_INFO ("using no dither and noise shaping"); |
999 | 0 | } else { |
1000 | 0 | GST_INFO ("using dither %d and noise shaping %d", dither, ns); |
1001 | | /* Use simple error feedback when output sample rate is smaller than |
1002 | | * 32000 as the other methods might move the noise to audible ranges */ |
1003 | 0 | if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000) |
1004 | 0 | ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK; |
1005 | 0 | } |
1006 | | /* we still want to run the quantization step when reducing bits to get |
1007 | | * the rounding correct */ |
1008 | 0 | if (out_int && out_depth < 32 |
1009 | 0 | && convert->current_format == GST_AUDIO_FORMAT_S32) { |
1010 | 0 | GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns); |
1011 | 0 | convert->quant = |
1012 | 0 | gst_audio_quantize_new (dither, ns, 0, convert->current_format, |
1013 | 0 | out->channels, 1U << (32 - out_depth)); |
1014 | |
|
1015 | 0 | prev = audio_chain_new (prev, convert); |
1016 | 0 | prev->allow_ip = TRUE; |
1017 | 0 | prev->pass_alloc = TRUE; |
1018 | 0 | audio_chain_set_make_func (prev, do_quantize, convert, NULL); |
1019 | 0 | } |
1020 | 0 | return prev; |
1021 | 0 | } |
1022 | | |
1023 | | static AudioChain * |
1024 | | chain_change_layout (GstAudioConverter * convert, AudioChain * prev) |
1025 | 0 | { |
1026 | 0 | GstAudioInfo *out = &convert->out; |
1027 | |
|
1028 | 0 | if (convert->current_layout != out->layout) { |
1029 | 0 | convert->current_layout = out->layout; |
1030 | | |
1031 | | /* if there is only 1 channel, layouts are identical */ |
1032 | 0 | if (convert->current_channels > 1) { |
1033 | 0 | convert->chlayout_target = convert->current_layout; |
1034 | 0 | convert->chlayout_format = convert->current_format; |
1035 | 0 | convert->chlayout_channels = convert->current_channels; |
1036 | |
|
1037 | 0 | prev = audio_chain_new (prev, convert); |
1038 | 0 | prev->allow_ip = FALSE; |
1039 | 0 | prev->pass_alloc = FALSE; |
1040 | 0 | audio_chain_set_make_func (prev, do_change_layout, convert, NULL); |
1041 | 0 | } |
1042 | 0 | } |
1043 | 0 | return prev; |
1044 | 0 | } |
1045 | | |
1046 | | static AudioChain * |
1047 | | chain_pack (GstAudioConverter * convert, AudioChain * prev) |
1048 | 0 | { |
1049 | 0 | GstAudioInfo *out = &convert->out; |
1050 | 0 | GstAudioFormat format = convert->current_format; |
1051 | |
|
1052 | 0 | convert->current_format = out->finfo->format; |
1053 | |
|
1054 | 0 | convert->out_default = format == out->finfo->format; |
1055 | 0 | GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format), |
1056 | 0 | gst_audio_format_to_string (out->finfo->format)); |
1057 | |
|
1058 | 0 | return prev; |
1059 | 0 | } |
1060 | | |
1061 | | static void |
1062 | | setup_allocators (GstAudioConverter * convert) |
1063 | 0 | { |
1064 | 0 | AudioChain *chain; |
1065 | 0 | AudioChainAllocFunc alloc_func; |
1066 | 0 | gboolean allow_ip; |
1067 | | |
1068 | | /* start with using dest if we can directly write into it */ |
1069 | 0 | if (convert->out_default) { |
1070 | 0 | alloc_func = get_output_samples; |
1071 | 0 | allow_ip = FALSE; |
1072 | 0 | } else { |
1073 | 0 | alloc_func = get_temp_samples; |
1074 | 0 | allow_ip = TRUE; |
1075 | 0 | } |
1076 | | /* now walk backwards, we try to write into the dest samples directly |
1077 | | * and keep track if the source needs to be writable */ |
1078 | 0 | for (chain = convert->chain_end; chain; chain = chain->prev) { |
1079 | 0 | chain->alloc_func = alloc_func; |
1080 | 0 | chain->alloc_data = convert; |
1081 | 0 | chain->allow_ip = allow_ip && chain->allow_ip; |
1082 | 0 | GST_LOG ("chain %p: %d %d", chain, allow_ip, chain->allow_ip); |
1083 | |
|
1084 | 0 | if (!chain->pass_alloc) { |
1085 | | /* can't pass allocator, make new temp line allocator */ |
1086 | 0 | alloc_func = get_temp_samples; |
1087 | 0 | allow_ip = TRUE; |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | } |
1091 | | |
1092 | | static gboolean |
1093 | | converter_passthrough (GstAudioConverter * convert, |
1094 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
1095 | | gpointer out[], gsize out_frames) |
1096 | 0 | { |
1097 | 0 | gint i; |
1098 | 0 | AudioChain *chain; |
1099 | 0 | gsize samples; |
1100 | | |
1101 | | /* in-place passthrough -> do nothing */ |
1102 | 0 | if (in == out) { |
1103 | 0 | g_assert (convert->in_place); |
1104 | 0 | return TRUE; |
1105 | 0 | } |
1106 | | |
1107 | 0 | chain = convert->chain_end; |
1108 | |
|
1109 | 0 | samples = in_frames * chain->inc; |
1110 | |
|
1111 | 0 | GST_LOG ("passthrough: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples", |
1112 | 0 | in_frames, samples); |
1113 | |
|
1114 | 0 | if (in) { |
1115 | 0 | gsize bytes; |
1116 | |
|
1117 | 0 | bytes = samples * (convert->in.bpf / convert->in.channels); |
1118 | |
|
1119 | 0 | for (i = 0; i < chain->blocks; i++) { |
1120 | 0 | if (out[i] == in[i]) { |
1121 | 0 | g_assert (convert->in_place); |
1122 | 0 | continue; |
1123 | 0 | } |
1124 | | |
1125 | 0 | memcpy (out[i], in[i], bytes); |
1126 | 0 | } |
1127 | 0 | } else { |
1128 | 0 | for (i = 0; i < chain->blocks; i++) |
1129 | 0 | gst_audio_format_info_fill_silence (convert->in.finfo, out[i], samples); |
1130 | 0 | } |
1131 | 0 | return TRUE; |
1132 | 0 | } |
1133 | | |
1134 | | /* perform LE<->BE conversion on a block of @count 16-bit samples |
1135 | | * dst may equal src for in-place conversion |
1136 | | */ |
1137 | | static void |
1138 | | converter_swap_endian_16 (gpointer dst, const gpointer src, gint count) |
1139 | 0 | { |
1140 | 0 | guint16 *out = dst; |
1141 | 0 | const guint16 *in = src; |
1142 | 0 | gint i; |
1143 | |
|
1144 | 0 | for (i = 0; i < count; i++) |
1145 | 0 | out[i] = GUINT16_SWAP_LE_BE (in[i]); |
1146 | 0 | } |
1147 | | |
1148 | | /* perform LE<->BE conversion on a block of @count 24-bit samples |
1149 | | * dst may equal src for in-place conversion |
1150 | | * |
1151 | | * naive algorithm, which performs better with -O3 and worse with -O2 |
1152 | | * than the commented out optimized algorithm below |
1153 | | */ |
1154 | | static void |
1155 | | converter_swap_endian_24 (gpointer dst, const gpointer src, gint count) |
1156 | 0 | { |
1157 | 0 | guint8 *out = dst; |
1158 | 0 | const guint8 *in = src; |
1159 | 0 | gint i; |
1160 | |
|
1161 | 0 | count *= 3; |
1162 | |
|
1163 | 0 | for (i = 0; i < count; i += 3) { |
1164 | 0 | guint8 x = in[i + 0]; |
1165 | 0 | out[i + 0] = in[i + 2]; |
1166 | 0 | out[i + 1] = in[i + 1]; |
1167 | 0 | out[i + 2] = x; |
1168 | 0 | } |
1169 | 0 | } |
1170 | | |
1171 | | /* the below code performs better with -O2 but worse with -O3 */ |
1172 | | #if 0 |
1173 | | /* perform LE<->BE conversion on a block of @count 24-bit samples |
1174 | | * dst may equal src for in-place conversion |
1175 | | * |
1176 | | * assumes that dst and src are 32-bit aligned |
1177 | | */ |
1178 | | static void |
1179 | | converter_swap_endian_24 (gpointer dst, const gpointer src, gint count) |
1180 | | { |
1181 | | guint32 *out = dst; |
1182 | | const guint32 *in = src; |
1183 | | guint8 *out8; |
1184 | | const guint8 *in8; |
1185 | | gint i; |
1186 | | |
1187 | | /* first convert 24-bit samples in multiples of 4 reading 3x 32-bits in one cycle |
1188 | | * |
1189 | | * input: A1 B1 C1 A2 , B2 C2 A3 B3 , C3 A4 B4 C4 |
1190 | | * 32-bit endian swap: A2 C1 B1 A1 , B3 A3 C2 B2 , C4 B4 A4 C3 |
1191 | | * <-- x --> <-- y --> , <-- z --> |
1192 | | * |
1193 | | * desired output: C1 B1 A1 C2 , B2 A2 C3 B3 , A3 C4 B4 A4 |
1194 | | */ |
1195 | | for (i = 0; i < count / 4; i++, in += 3, out += 3) { |
1196 | | guint32 x, y, z; |
1197 | | |
1198 | | x = GUINT32_SWAP_LE_BE (in[0]); |
1199 | | y = GUINT32_SWAP_LE_BE (in[1]); |
1200 | | z = GUINT32_SWAP_LE_BE (in[2]); |
1201 | | |
1202 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
1203 | | out[0] = (x << 8) + ((y >> 8) & 0xff); |
1204 | | out[1] = (in[1] & 0xff0000ff) + ((x >> 8) & 0xff0000) + ((z << 8) & 0xff00); |
1205 | | out[2] = (z >> 8) + ((y << 8) & 0xff000000); |
1206 | | #else |
1207 | | out[0] = (x >> 8) + ((y << 8) & 0xff000000); |
1208 | | out[1] = (in[1] & 0xff0000ff) + ((x << 8) & 0xff00) + ((z >> 8) & 0xff0000); |
1209 | | out[2] = (z << 8) + ((y >> 8) & 0xff); |
1210 | | #endif |
1211 | | } |
1212 | | |
1213 | | /* convert the remainder less efficiently */ |
1214 | | for (out8 = (guint8 *) out, in8 = (const guint8 *) in, i = 0; i < (count & 3); |
1215 | | i++) { |
1216 | | guint8 x = in8[i + 0]; |
1217 | | out8[i + 0] = in8[i + 2]; |
1218 | | out8[i + 1] = in8[i + 1]; |
1219 | | out8[i + 2] = x; |
1220 | | } |
1221 | | } |
1222 | | #endif |
1223 | | |
1224 | | /* perform LE<->BE conversion on a block of @count 32-bit samples |
1225 | | * dst may equal src for in-place conversion |
1226 | | */ |
1227 | | static void |
1228 | | converter_swap_endian_32 (gpointer dst, const gpointer src, gint count) |
1229 | 0 | { |
1230 | 0 | guint32 *out = dst; |
1231 | 0 | const guint32 *in = src; |
1232 | 0 | gint i; |
1233 | |
|
1234 | 0 | for (i = 0; i < count; i++) |
1235 | 0 | out[i] = GUINT32_SWAP_LE_BE (in[i]); |
1236 | 0 | } |
1237 | | |
1238 | | /* perform LE<->BE conversion on a block of @count 64-bit samples |
1239 | | * dst may equal src for in-place conversion |
1240 | | */ |
1241 | | static void |
1242 | | converter_swap_endian_64 (gpointer dst, const gpointer src, gint count) |
1243 | 0 | { |
1244 | 0 | guint64 *out = dst; |
1245 | 0 | const guint64 *in = src; |
1246 | 0 | gint i; |
1247 | |
|
1248 | 0 | for (i = 0; i < count; i++) |
1249 | 0 | out[i] = GUINT64_SWAP_LE_BE (in[i]); |
1250 | 0 | } |
1251 | | |
1252 | | /* the worker function to perform endian-conversion only |
1253 | | * assuming finfo and foutinfo have the same depth |
1254 | | */ |
1255 | | static gboolean |
1256 | | converter_endian (GstAudioConverter * convert, |
1257 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
1258 | | gpointer out[], gsize out_frames) |
1259 | 0 | { |
1260 | 0 | gint i; |
1261 | 0 | AudioChain *chain; |
1262 | 0 | gsize samples; |
1263 | |
|
1264 | 0 | chain = convert->chain_end; |
1265 | 0 | samples = in_frames * chain->inc; |
1266 | |
|
1267 | 0 | GST_LOG ("convert endian: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples", |
1268 | 0 | in_frames, samples); |
1269 | |
|
1270 | 0 | if (in) { |
1271 | 0 | for (i = 0; i < chain->blocks; i++) |
1272 | 0 | convert->swap_endian (out[i], in[i], samples); |
1273 | 0 | } else { |
1274 | 0 | for (i = 0; i < chain->blocks; i++) |
1275 | 0 | gst_audio_format_info_fill_silence (convert->in.finfo, out[i], samples); |
1276 | 0 | } |
1277 | 0 | return TRUE; |
1278 | 0 | } |
1279 | | |
1280 | | static gboolean |
1281 | | converter_generic (GstAudioConverter * convert, |
1282 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
1283 | | gpointer out[], gsize out_frames) |
1284 | 0 | { |
1285 | 0 | AudioChain *chain; |
1286 | 0 | gpointer *tmp; |
1287 | 0 | gint i; |
1288 | 0 | gsize produced; |
1289 | |
|
1290 | 0 | chain = convert->chain_end; |
1291 | |
|
1292 | 0 | convert->in_writable = flags & GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE; |
1293 | 0 | convert->in_data = in; |
1294 | 0 | convert->in_frames = in_frames; |
1295 | 0 | convert->out_data = out; |
1296 | 0 | convert->out_frames = out_frames; |
1297 | | |
1298 | | /* get frames to pack */ |
1299 | 0 | tmp = audio_chain_get_samples (chain, &produced); |
1300 | |
|
1301 | 0 | if (!convert->out_default && tmp && out) { |
1302 | 0 | GST_LOG ("pack %p, %p %" G_GSIZE_FORMAT, tmp, out, produced); |
1303 | | /* and pack if needed */ |
1304 | 0 | for (i = 0; i < chain->blocks; i++) |
1305 | 0 | convert->out.finfo->pack_func (convert->out.finfo, 0, tmp[i], out[i], |
1306 | 0 | produced * chain->inc); |
1307 | 0 | } |
1308 | 0 | return TRUE; |
1309 | 0 | } |
1310 | | |
1311 | | static gboolean |
1312 | | converter_resample (GstAudioConverter * convert, |
1313 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
1314 | | gpointer out[], gsize out_frames) |
1315 | 0 | { |
1316 | 0 | gst_audio_resampler_resample (convert->resampler, in, in_frames, out, |
1317 | 0 | out_frames); |
1318 | |
|
1319 | 0 | return TRUE; |
1320 | 0 | } |
1321 | | |
1322 | | #define GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION(info1, info2) \ |
1323 | 0 | ( \ |
1324 | 0 | !(((info1)->flags ^ (info2)->flags) & (~GST_AUDIO_FORMAT_FLAG_UNPACK)) && \ |
1325 | 0 | (info1)->endianness != (info2)->endianness && \ |
1326 | 0 | (info1)->width == (info2)->width && \ |
1327 | 0 | (info1)->depth == (info2)->depth \ |
1328 | 0 | ) |
1329 | | |
1330 | | /** |
1331 | | * gst_audio_converter_new: |
1332 | | * @flags: extra #GstAudioConverterFlags |
1333 | | * @in_info: a source #GstAudioInfo |
1334 | | * @out_info: a destination #GstAudioInfo |
1335 | | * @config: (transfer full) (nullable): a #GstStructure with configuration options |
1336 | | * |
1337 | | * Create a new #GstAudioConverter that is able to convert between @in and @out |
1338 | | * audio formats. |
1339 | | * |
1340 | | * @config contains extra configuration options, see `GST_AUDIO_CONVERTER_OPT_*` |
1341 | | * parameters for details about the options and values. |
1342 | | * |
1343 | | * Returns: (nullable): a #GstAudioConverter or %NULL if conversion is not possible. |
1344 | | */ |
1345 | | GstAudioConverter * |
1346 | | gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, |
1347 | | GstAudioInfo * out_info, GstStructure * config) |
1348 | 0 | { |
1349 | 0 | GstAudioConverter *convert; |
1350 | 0 | AudioChain *prev; |
1351 | 0 | const GValue *opt_matrix = NULL; |
1352 | |
|
1353 | 0 | g_return_val_if_fail (in_info != NULL, FALSE); |
1354 | 0 | g_return_val_if_fail (out_info != NULL, FALSE); |
1355 | | |
1356 | 0 | if (config) |
1357 | 0 | opt_matrix = |
1358 | 0 | gst_structure_get_value (config, GST_AUDIO_CONVERTER_OPT_MIX_MATRIX); |
1359 | |
|
1360 | 0 | if (opt_matrix |
1361 | 0 | && !check_mix_matrix (in_info->channels, out_info->channels, opt_matrix)) |
1362 | 0 | goto invalid_mix_matrix; |
1363 | | |
1364 | 0 | if ((GST_AUDIO_INFO_CHANNELS (in_info) != GST_AUDIO_INFO_CHANNELS (out_info)) |
1365 | 0 | && (GST_AUDIO_INFO_IS_UNPOSITIONED (in_info) |
1366 | 0 | || GST_AUDIO_INFO_IS_UNPOSITIONED (out_info)) |
1367 | 0 | && !opt_matrix) |
1368 | 0 | goto unpositioned; |
1369 | | |
1370 | 0 | convert = g_new0 (GstAudioConverter, 1); |
1371 | |
|
1372 | 0 | convert->flags = flags; |
1373 | 0 | convert->in = *in_info; |
1374 | 0 | convert->out = *out_info; |
1375 | | |
1376 | | /* default config */ |
1377 | 0 | convert->config = gst_structure_new_static_str_empty ("GstAudioConverter"); |
1378 | 0 | if (config) |
1379 | 0 | gst_audio_converter_update_config (convert, 0, 0, config); |
1380 | |
|
1381 | 0 | GST_INFO ("unitsizes: %d -> %d", in_info->bpf, out_info->bpf); |
1382 | | |
1383 | | /* step 1, unpack */ |
1384 | 0 | prev = chain_unpack (convert); |
1385 | | /* step 2, optional convert from S32 to F64 for channel mix */ |
1386 | 0 | prev = chain_convert_in (convert, prev); |
1387 | | /* step 3, channel mix */ |
1388 | 0 | prev = chain_mix (convert, prev); |
1389 | | /* step 4, resample */ |
1390 | 0 | prev = chain_resample (convert, prev); |
1391 | | /* step 5, optional convert for quantize */ |
1392 | 0 | prev = chain_convert_out (convert, prev); |
1393 | | /* step 6, optional quantize */ |
1394 | 0 | prev = chain_quantize (convert, prev); |
1395 | | /* step 7, change layout */ |
1396 | 0 | prev = chain_change_layout (convert, prev); |
1397 | | /* step 8, pack */ |
1398 | 0 | convert->chain_end = chain_pack (convert, prev); |
1399 | |
|
1400 | 0 | convert->convert = converter_generic; |
1401 | 0 | convert->in_place = FALSE; |
1402 | 0 | convert->passthrough = FALSE; |
1403 | | |
1404 | | /* optimize */ |
1405 | 0 | if (convert->mix_passthrough) { |
1406 | 0 | if (out_info->finfo->format == in_info->finfo->format) { |
1407 | 0 | if (convert->resampler == NULL) { |
1408 | 0 | if (out_info->layout == in_info->layout) { |
1409 | 0 | GST_INFO ("same formats, same layout, no resampler and " |
1410 | 0 | "passthrough mixing -> passthrough"); |
1411 | 0 | convert->convert = converter_passthrough; |
1412 | 0 | convert->in_place = TRUE; |
1413 | 0 | convert->passthrough = TRUE; |
1414 | 0 | } |
1415 | 0 | } else { |
1416 | 0 | if (is_intermediate_format (in_info->finfo->format)) { |
1417 | 0 | GST_INFO ("same formats, and passthrough mixing -> only resampling"); |
1418 | 0 | convert->convert = converter_resample; |
1419 | 0 | } |
1420 | 0 | } |
1421 | 0 | } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo, |
1422 | 0 | in_info->finfo)) { |
1423 | 0 | if (convert->resampler == NULL && out_info->layout == in_info->layout) { |
1424 | 0 | GST_INFO ("no resampler, passthrough mixing -> only endian conversion"); |
1425 | 0 | convert->convert = converter_endian; |
1426 | 0 | convert->in_place = TRUE; |
1427 | |
|
1428 | 0 | switch (GST_AUDIO_INFO_WIDTH (in_info)) { |
1429 | 0 | case 16: |
1430 | 0 | GST_DEBUG ("initializing 16-bit endian conversion"); |
1431 | 0 | convert->swap_endian = converter_swap_endian_16; |
1432 | 0 | break; |
1433 | 0 | case 24: |
1434 | 0 | GST_DEBUG ("initializing 24-bit endian conversion"); |
1435 | 0 | convert->swap_endian = converter_swap_endian_24; |
1436 | 0 | break; |
1437 | 0 | case 32: |
1438 | 0 | GST_DEBUG ("initializing 32-bit endian conversion"); |
1439 | 0 | convert->swap_endian = converter_swap_endian_32; |
1440 | 0 | break; |
1441 | 0 | case 64: |
1442 | 0 | GST_DEBUG ("initializing 64-bit endian conversion"); |
1443 | 0 | convert->swap_endian = converter_swap_endian_64; |
1444 | 0 | break; |
1445 | 0 | default: |
1446 | 0 | GST_ERROR ("unsupported sample width for endian conversion"); |
1447 | 0 | g_assert_not_reached (); |
1448 | 0 | } |
1449 | 0 | } |
1450 | 0 | } |
1451 | 0 | } |
1452 | | |
1453 | 0 | setup_allocators (convert); |
1454 | |
|
1455 | 0 | return convert; |
1456 | | |
1457 | | /* ERRORS */ |
1458 | 0 | unpositioned: |
1459 | 0 | { |
1460 | 0 | GST_WARNING ("unpositioned channels"); |
1461 | 0 | g_clear_pointer (&config, gst_structure_free); |
1462 | 0 | return NULL; |
1463 | 0 | } |
1464 | | |
1465 | 0 | invalid_mix_matrix: |
1466 | 0 | { |
1467 | 0 | GST_WARNING ("Invalid mix matrix"); |
1468 | 0 | g_clear_pointer (&config, gst_structure_free); |
1469 | 0 | return NULL; |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | | /** |
1474 | | * gst_audio_converter_free: |
1475 | | * @convert: a #GstAudioConverter |
1476 | | * |
1477 | | * Free a previously allocated @convert instance. |
1478 | | */ |
1479 | | void |
1480 | | gst_audio_converter_free (GstAudioConverter * convert) |
1481 | 0 | { |
1482 | 0 | AudioChain *chain; |
1483 | |
|
1484 | 0 | g_return_if_fail (convert != NULL); |
1485 | | |
1486 | | /* walk the chain backwards and free all elements */ |
1487 | 0 | for (chain = convert->chain_end; chain;) { |
1488 | 0 | AudioChain *prev = chain->prev; |
1489 | 0 | audio_chain_free (chain); |
1490 | 0 | chain = prev; |
1491 | 0 | } |
1492 | |
|
1493 | 0 | if (convert->quant) |
1494 | 0 | gst_audio_quantize_free (convert->quant); |
1495 | 0 | if (convert->mix) |
1496 | 0 | gst_audio_channel_mixer_free (convert->mix); |
1497 | 0 | if (convert->resampler) |
1498 | 0 | gst_audio_resampler_free (convert->resampler); |
1499 | 0 | gst_audio_info_init (&convert->in); |
1500 | 0 | gst_audio_info_init (&convert->out); |
1501 | |
|
1502 | 0 | gst_structure_free (convert->config); |
1503 | |
|
1504 | 0 | g_free (convert); |
1505 | 0 | } |
1506 | | |
1507 | | /** |
1508 | | * gst_audio_converter_get_out_frames: |
1509 | | * @convert: a #GstAudioConverter |
1510 | | * @in_frames: number of input frames |
1511 | | * |
1512 | | * Calculate how many output frames can be produced when @in_frames input |
1513 | | * frames are given to @convert. |
1514 | | * |
1515 | | * Returns: the number of output frames |
1516 | | */ |
1517 | | gsize |
1518 | | gst_audio_converter_get_out_frames (GstAudioConverter * convert, |
1519 | | gsize in_frames) |
1520 | 0 | { |
1521 | 0 | if (convert->resampler) |
1522 | 0 | return gst_audio_resampler_get_out_frames (convert->resampler, in_frames); |
1523 | 0 | else |
1524 | 0 | return in_frames; |
1525 | 0 | } |
1526 | | |
1527 | | /** |
1528 | | * gst_audio_converter_get_in_frames: |
1529 | | * @convert: a #GstAudioConverter |
1530 | | * @out_frames: number of output frames |
1531 | | * |
1532 | | * Calculate how many input frames are currently needed by @convert to produce |
1533 | | * @out_frames of output frames. |
1534 | | * |
1535 | | * Returns: the number of input frames |
1536 | | */ |
1537 | | gsize |
1538 | | gst_audio_converter_get_in_frames (GstAudioConverter * convert, |
1539 | | gsize out_frames) |
1540 | 0 | { |
1541 | 0 | if (convert->resampler) |
1542 | 0 | return gst_audio_resampler_get_in_frames (convert->resampler, out_frames); |
1543 | 0 | else |
1544 | 0 | return out_frames; |
1545 | 0 | } |
1546 | | |
1547 | | /** |
1548 | | * gst_audio_converter_get_max_latency: |
1549 | | * @convert: a #GstAudioConverter |
1550 | | * |
1551 | | * Get the maximum number of input frames that the converter would |
1552 | | * need before producing output. |
1553 | | * |
1554 | | * Returns: the latency of @convert as expressed in the number of |
1555 | | * frames. |
1556 | | */ |
1557 | | gsize |
1558 | | gst_audio_converter_get_max_latency (GstAudioConverter * convert) |
1559 | 0 | { |
1560 | 0 | if (convert->resampler) |
1561 | 0 | return gst_audio_resampler_get_max_latency (convert->resampler); |
1562 | 0 | else |
1563 | 0 | return 0; |
1564 | 0 | } |
1565 | | |
1566 | | /** |
1567 | | * gst_audio_converter_reset: |
1568 | | * @convert: a #GstAudioConverter |
1569 | | * |
1570 | | * Reset @convert to the state it was when it was first created, clearing |
1571 | | * any history it might currently have. |
1572 | | */ |
1573 | | void |
1574 | | gst_audio_converter_reset (GstAudioConverter * convert) |
1575 | 0 | { |
1576 | 0 | if (convert->resampler) |
1577 | 0 | gst_audio_resampler_reset (convert->resampler); |
1578 | 0 | if (convert->quant) |
1579 | 0 | gst_audio_quantize_reset (convert->quant); |
1580 | 0 | } |
1581 | | |
1582 | | /** |
1583 | | * gst_audio_converter_samples: |
1584 | | * @convert: a #GstAudioConverter |
1585 | | * @flags: extra #GstAudioConverterFlags |
1586 | | * @in: (array) (element-type gpointer): input frames |
1587 | | * @in_frames: number of input frames |
1588 | | * @out: (array) (element-type gpointer): output frames |
1589 | | * @out_frames: number of output frames |
1590 | | * |
1591 | | * Perform the conversion with @in_frames in @in to @out_frames in @out |
1592 | | * using @convert. |
1593 | | * |
1594 | | * In case the samples are interleaved, @in and @out must point to an |
1595 | | * array with a single element pointing to a block of interleaved samples. |
1596 | | * |
1597 | | * If non-interleaved samples are used, @in and @out must point to an |
1598 | | * array with pointers to memory blocks, one for each channel. |
1599 | | * |
1600 | | * @in may be %NULL, in which case @in_frames of silence samples are processed |
1601 | | * by the converter. |
1602 | | * |
1603 | | * This function always produces @out_frames of output and consumes @in_frames of |
1604 | | * input. Use gst_audio_converter_get_out_frames() and |
1605 | | * gst_audio_converter_get_in_frames() to make sure @in_frames and @out_frames |
1606 | | * are matching and @in and @out point to enough memory. |
1607 | | * |
1608 | | * Returns: %TRUE is the conversion could be performed. |
1609 | | */ |
1610 | | gboolean |
1611 | | gst_audio_converter_samples (GstAudioConverter * convert, |
1612 | | GstAudioConverterFlags flags, gpointer in[], gsize in_frames, |
1613 | | gpointer out[], gsize out_frames) |
1614 | 0 | { |
1615 | 0 | g_return_val_if_fail (convert != NULL, FALSE); |
1616 | 0 | g_return_val_if_fail (out != NULL, FALSE); |
1617 | | |
1618 | 0 | if (in_frames == 0) { |
1619 | 0 | GST_LOG ("skipping empty buffer"); |
1620 | 0 | return TRUE; |
1621 | 0 | } |
1622 | 0 | return convert->convert (convert, flags, in, in_frames, out, out_frames); |
1623 | 0 | } |
1624 | | |
1625 | | /** |
1626 | | * gst_audio_converter_convert: |
1627 | | * @convert: a #GstAudioConverter |
1628 | | * @flags: extra #GstAudioConverterFlags |
1629 | | * @in: (array length=in_size) (element-type guint8): input data |
1630 | | * @in_size: size of @in |
1631 | | * @out: (out) (array length=out_size) (element-type guint8): a pointer where |
1632 | | * the output data will be written |
1633 | | * @out_size: (out): a pointer where the size of @out will be written |
1634 | | * |
1635 | | * Convenience wrapper around gst_audio_converter_samples(), which will |
1636 | | * perform allocation of the output buffer based on the result from |
1637 | | * gst_audio_converter_get_out_frames(). |
1638 | | * |
1639 | | * Returns: %TRUE is the conversion could be performed. |
1640 | | * |
1641 | | * Since: 1.14 |
1642 | | */ |
1643 | | gboolean |
1644 | | gst_audio_converter_convert (GstAudioConverter * convert, |
1645 | | GstAudioConverterFlags flags, gpointer in, gsize in_size, |
1646 | | gpointer * out, gsize * out_size) |
1647 | 0 | { |
1648 | 0 | gsize in_frames; |
1649 | 0 | gsize out_frames; |
1650 | |
|
1651 | 0 | g_return_val_if_fail (convert != NULL, FALSE); |
1652 | 0 | g_return_val_if_fail (flags ^ GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE, FALSE); |
1653 | | |
1654 | 0 | in_frames = in_size / convert->in.bpf; |
1655 | 0 | out_frames = gst_audio_converter_get_out_frames (convert, in_frames); |
1656 | |
|
1657 | 0 | *out_size = out_frames * convert->out.bpf; |
1658 | 0 | *out = g_malloc0 (*out_size); |
1659 | |
|
1660 | 0 | return gst_audio_converter_samples (convert, flags, &in, in_frames, out, |
1661 | 0 | out_frames); |
1662 | 0 | } |
1663 | | |
1664 | | /** |
1665 | | * gst_audio_converter_supports_inplace: |
1666 | | * @convert: a #GstAudioConverter |
1667 | | * |
1668 | | * Returns whether the audio converter can perform the conversion in-place. |
1669 | | * The return value would be typically input to gst_base_transform_set_in_place() |
1670 | | * |
1671 | | * Returns: %TRUE when the conversion can be done in place. |
1672 | | * |
1673 | | * Since: 1.12 |
1674 | | */ |
1675 | | gboolean |
1676 | | gst_audio_converter_supports_inplace (GstAudioConverter * convert) |
1677 | 0 | { |
1678 | 0 | return convert->in_place; |
1679 | 0 | } |
1680 | | |
1681 | | /** |
1682 | | * gst_audio_converter_is_passthrough: |
1683 | | * |
1684 | | * Returns whether the audio converter will operate in passthrough mode. |
1685 | | * The return value would be typically input to gst_base_transform_set_passthrough() |
1686 | | * |
1687 | | * Returns: %TRUE when no conversion will actually occur. |
1688 | | * |
1689 | | * Since: 1.16 |
1690 | | */ |
1691 | | gboolean |
1692 | | gst_audio_converter_is_passthrough (GstAudioConverter * convert) |
1693 | 0 | { |
1694 | 0 | return convert->passthrough; |
1695 | 0 | } |