libs: audio-converter: complete code to support non-interleaved audio buffers

https://bugzilla.gnome.org/show_bug.cgi?id=705986
This commit is contained in:
George Kiagiadakis 2018-02-01 17:00:06 +02:00
parent eefdf32d96
commit 060ecd16cd

View file

@ -131,6 +131,11 @@ struct _GstAudioConverter
/* quant */
GstAudioQuantize *quant;
/* change layout */
GstAudioFormat chlayout_format;
GstAudioLayout chlayout_target;
gint chlayout_channels;
/* pack */
gboolean out_default;
AudioChain *chain_end; /* NULL for empty chain or points to the last element in the chain */
@ -582,6 +587,110 @@ do_quantize (AudioChain * chain, gpointer user_data)
return TRUE;
}
#define MAKE_INTERLEAVE_FUNC(type) \
static inline void \
interleave_##type (const type * in[], type * out[], \
gsize num_samples, gint channels) \
{ \
gsize s; \
gint c; \
for (s = 0; s < num_samples; s++) { \
for (c = 0; c < channels; c++) { \
out[0][s * channels + c] = in[c][s]; \
} \
} \
}
#define MAKE_DEINTERLEAVE_FUNC(type) \
static inline void \
deinterleave_##type (const type * in[], type * out[], \
gsize num_samples, gint channels) \
{ \
gsize s; \
gint c; \
for (s = 0; s < num_samples; s++) { \
for (c = 0; c < channels; c++) { \
out[c][s] = in[0][s * channels + c]; \
} \
} \
}
MAKE_INTERLEAVE_FUNC (gint16);
MAKE_INTERLEAVE_FUNC (gint32);
MAKE_INTERLEAVE_FUNC (gfloat);
MAKE_INTERLEAVE_FUNC (gdouble);
MAKE_DEINTERLEAVE_FUNC (gint16);
MAKE_DEINTERLEAVE_FUNC (gint32);
MAKE_DEINTERLEAVE_FUNC (gfloat);
MAKE_DEINTERLEAVE_FUNC (gdouble);
static gboolean
do_change_layout (AudioChain * chain, gpointer user_data)
{
GstAudioConverter *convert = user_data;
GstAudioFormat format = convert->chlayout_format;
GstAudioLayout out_layout = convert->chlayout_target;
gint channels = convert->chlayout_channels;
gsize num_samples;
gpointer *in, *out;
in = audio_chain_get_samples (chain->prev, &num_samples);
out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
if (out_layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
/* interleave */
GST_LOG ("interleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
switch (format) {
case GST_AUDIO_FORMAT_S16:
interleave_gint16 ((const gint16 **) in, (gint16 **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_S32:
interleave_gint32 ((const gint32 **) in, (gint32 **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_F32:
interleave_gfloat ((const gfloat **) in, (gfloat **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_F64:
interleave_gdouble ((const gdouble **) in, (gdouble **) out,
num_samples, channels);
break;
default:
g_assert_not_reached ();
break;
}
} else {
/* deinterleave */
GST_LOG ("deinterleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
switch (format) {
case GST_AUDIO_FORMAT_S16:
deinterleave_gint16 ((const gint16 **) in, (gint16 **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_S32:
deinterleave_gint32 ((const gint32 **) in, (gint32 **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_F32:
deinterleave_gfloat ((const gfloat **) in, (gfloat **) out,
num_samples, channels);
break;
case GST_AUDIO_FORMAT_F64:
deinterleave_gdouble ((const gdouble **) in, (gdouble **) out,
num_samples, channels);
break;
default:
g_assert_not_reached ();
break;
}
}
audio_chain_set_samples (chain, out, num_samples);
return TRUE;
}
static gboolean
is_intermediate_format (GstAudioFormat format)
{
@ -719,9 +828,16 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
GstAudioInfo *out = &convert->out;
GstAudioFormat format = convert->current_format;
const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
GstAudioChannelMixerFlags flags = 0;
convert->current_channels = out->channels;
/* keep the input layout */
if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN;
flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT;
}
if (opt_matrix) {
gfloat **matrix = NULL;
@ -730,12 +846,10 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
convert->mix =
gst_audio_channel_mixer_new_with_matrix (0, format, in->channels,
gst_audio_channel_mixer_new_with_matrix (flags, format, in->channels,
out->channels, matrix);
} else {
GstAudioChannelMixerFlags flags;
flags =
flags |=
GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
flags |=
@ -781,8 +895,13 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev)
flags = 0;
if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN;
}
/* if the resampler is activated, it is optimal to change layout here */
if (out->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
}
convert->current_layout = out->layout;
if (variable_rate)
flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
@ -875,6 +994,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev)
return prev;
}
static AudioChain *
chain_change_layout (GstAudioConverter * convert, AudioChain * prev)
{
GstAudioInfo *out = &convert->out;
if (convert->current_layout != out->layout) {
convert->current_layout = out->layout;
/* if there is only 1 channel, layouts are identical */
if (convert->current_channels > 1) {
convert->chlayout_target = convert->current_layout;
convert->chlayout_format = convert->current_format;
convert->chlayout_channels = convert->current_channels;
prev = audio_chain_new (prev, convert);
prev->allow_ip = FALSE;
prev->pass_alloc = FALSE;
audio_chain_set_make_func (prev, do_change_layout, convert, NULL);
}
}
return prev;
}
static AudioChain *
chain_pack (GstAudioConverter * convert, AudioChain * prev)
{
@ -1184,8 +1326,6 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
g_return_val_if_fail (in_info != NULL, FALSE);
g_return_val_if_fail (out_info != NULL, FALSE);
g_return_val_if_fail (in_info->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
g_return_val_if_fail (in_info->layout == out_info->layout, FALSE);
if (config)
opt_matrix =
@ -1226,7 +1366,9 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
prev = chain_convert_out (convert, prev);
/* step 6, optional quantize */
prev = chain_quantize (convert, prev);
/* step 7, pack */
/* step 7, change layout */
prev = chain_change_layout (convert, prev);
/* step 8, pack */
convert->chain_end = chain_pack (convert, prev);
convert->convert = converter_generic;
@ -1236,10 +1378,12 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
if (convert->mix_passthrough) {
if (out_info->finfo->format == in_info->finfo->format) {
if (convert->resampler == NULL) {
GST_INFO
("same formats, no resampler and passthrough mixing -> passthrough");
convert->convert = converter_passthrough;
convert->in_place = TRUE;
if (out_info->layout == in_info->layout) {
GST_INFO ("same formats, same layout, no resampler and "
"passthrough mixing -> passthrough");
convert->convert = converter_passthrough;
convert->in_place = TRUE;
}
} else {
if (is_intermediate_format (in_info->finfo->format)) {
GST_INFO ("same formats, and passthrough mixing -> only resampling");
@ -1248,7 +1392,7 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
}
} else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
in_info->finfo)) {
if (convert->resampler == NULL) {
if (convert->resampler == NULL && out_info->layout == in_info->layout) {
GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
convert->convert = converter_endian;
convert->in_place = TRUE;