[vlc-devel] [PATCH] audio_filter:add matrix-based upmixing
Vedanta Nayak
vedantnayak2 at gmail.com
Mon Sep 7 12:15:13 CEST 2020
Hi,
I have updated the copyright to the correct year.
---
modules/audio_filter/Makefile.am | 4 +
.../audio_filter/channel_mixer/matrix_upmix.c | 571 ++++++++++++++++++
2 files changed, 575 insertions(+)
create mode 100644 modules/audio_filter/channel_mixer/matrix_upmix.c
diff --git a/modules/audio_filter/Makefile.am b/modules/audio_filter/Makefile.am
index 279be02040..b1b7c5e85d 100644
--- a/modules/audio_filter/Makefile.am
+++ b/modules/audio_filter/Makefile.am
@@ -54,6 +54,9 @@ audio_filter_LTLIBRARIES = \
# Channel mixers
libdolby_surround_decoder_plugin_la_SOURCES = \
audio_filter/channel_mixer/dolby.c
+libmatrix_upmixer_plugin_la_SOURCES = \
+ audio_filter/channel_mixer/matrix_upmix.c
+libmatrix_upmixer_plugin_la_LIBADD = $(LIBM)
libheadphone_channel_mixer_plugin_la_SOURCES = \
audio_filter/channel_mixer/headphone.c
libheadphone_channel_mixer_plugin_la_LIBADD = $(LIBM)
@@ -82,6 +85,7 @@ endif
audio_filter_LTLIBRARIES += \
libdolby_surround_decoder_plugin.la \
+ libmatrix_upmixer_plugin.la \
libheadphone_channel_mixer_plugin.la \
libmono_plugin.la \
libremap_plugin.la \
diff --git a/modules/audio_filter/channel_mixer/matrix_upmix.c b/modules/audio_filter/channel_mixer/matrix_upmix.c
new file mode 100644
index 0000000000..7449aa0056
--- /dev/null
+++ b/modules/audio_filter/channel_mixer/matrix_upmix.c
@@ -0,0 +1,571 @@
+/*****************************************************************************
+ * matrix_upmix.c : matrix-based upmixer
+ *****************************************************************************
+ * Copyright © 2020 VLC authors and VideoLAN
+ *
+ * Authors: Vedanta Nayak <vedantnayak2 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_aout.h>
+#include <vlc_filter.h>
+
+#define MATRIX_TEXT N_( "Decoding Matrix" )
+#define MATRIX_LONGTEXT N_( "Decoding matrix to use for upmixing." )
+
+#define DELAY_TEXT N_( "Surround delay" )
+#define DELAY_LONGTEXT N_( "Delay to be applied to the rear channels "\
+ "for precedence effect. Maximum is 100. Default is 0." )
+
+enum {
+ DOLBY_PROLOGIC,
+ DOLBY_PROLOGICII
+};
+
+static const int matrix_list[] = {
+ DOLBY_PROLOGIC ,
+ DOLBY_PROLOGICII
+};
+static const char *const matrix_list_text[] = {
+ N_("Dolby Prologic"), N_("Dolby Prologic II")
+};
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Create ( vlc_object_t * );
+static void Destroy ( vlc_object_t * );
+
+static block_t *DPL ( filter_t *, block_t * );
+static block_t *DPLii ( filter_t *, block_t * );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin ()
+ set_description( N_("Matrix-based upmixer that decodes stereo audio to multichannel audio") )
+ set_shortname( N_("Matrix-based upmixer") )
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_ACODEC )
+ set_capability( "audio converter", 6 )
+ add_integer( "upmixing-matrix", 0, MATRIX_TEXT,
+ MATRIX_LONGTEXT, false)
+ change_integer_list( matrix_list , matrix_list_text )
+ add_integer_with_range("upmix-surround-delay",0,0,100,
+ DELAY_TEXT, DELAY_LONGTEXT, false)
+ set_callbacks( Create, Destroy )
+vlc_module_end ()
+
+/*****************************************************************************
+ * Internal data structures
+ *****************************************************************************/
+typedef struct
+{
+ int i_left;
+ int i_center;
+ int i_right;
+ int i_rear_left;
+ int i_rear_center;
+ int i_rear_right;
+ int i_delay;
+ size_t i_nb_rear;
+ block_t *firstblock;
+ block_t **block_chain;
+}filter_sys_t;
+
+/*****************************************************************************
+ * Create: allocate filter resources
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+ int i = 0;
+ int i_offset = 0;
+ filter_t * p_filter = (filter_t *)p_this;
+ filter_sys_t *p_sys;
+
+ /* Validate audio filter format */
+ if ( p_filter->fmt_in.audio.i_physical_channels != (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
+ || ! ( p_filter->fmt_in.audio.i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO )
+ || p_filter->fmt_out.audio.i_channels <= 2
+ || ( p_filter->fmt_in.audio.i_chan_mode & ~AOUT_CHANMODE_DOLBYSTEREO )
+ != ( p_filter->fmt_out.audio.i_chan_mode & ~AOUT_CHANMODE_DOLBYSTEREO ) )
+ {
+ return VLC_EGENERIC;
+ }
+
+ if ( p_filter->fmt_in.audio.i_rate != p_filter->fmt_out.audio.i_rate )
+ {
+ return VLC_EGENERIC;
+ }
+
+ if ( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32
+ || p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
+ {
+ return VLC_EGENERIC;
+ }
+
+ /* Allocate the memory needed to store the module's structure */
+ p_sys = p_filter->p_sys = malloc( sizeof(*p_sys) );
+ if( p_sys == NULL )
+ return VLC_ENOMEM;
+ p_sys->i_left = -1;
+ p_sys->i_center = -1;
+ p_sys->i_right = -1;
+ p_sys->i_rear_left = -1;
+ p_sys->i_rear_center = -1;
+ p_sys->i_rear_right = -1;
+ p_sys->i_nb_rear = 0;
+ p_sys->i_delay = -1;
+ p_sys->firstblock = NULL;
+ p_sys->block_chain = &p_sys->firstblock;
+
+ while ( pi_vlc_chan_order_wg4[i] )
+ {
+ if ( p_filter->fmt_out.audio.i_physical_channels & pi_vlc_chan_order_wg4[i] )
+ {
+ switch ( pi_vlc_chan_order_wg4[i] )
+ {
+ case AOUT_CHAN_LEFT:
+ p_sys->i_left = i_offset;
+ break;
+ case AOUT_CHAN_CENTER:
+ p_sys->i_center = i_offset;
+ break;
+ case AOUT_CHAN_RIGHT:
+ p_sys->i_right = i_offset;
+ break;
+ case AOUT_CHAN_REARLEFT:
+ p_sys->i_rear_left = i_offset;
+ p_sys->i_nb_rear++;
+ break;
+ case AOUT_CHAN_REARCENTER:
+ p_sys->i_rear_center = i_offset;
+ p_sys->i_nb_rear++;
+ break;
+ case AOUT_CHAN_REARRIGHT:
+ p_sys->i_rear_right = i_offset;
+ p_sys->i_nb_rear++;
+ break;
+ }
+ ++i_offset;
+ }
+ ++i;
+ }
+
+ size_t matrix = var_InheritInteger( vlc_object_parent(p_filter),
+ "upmixing-matrix" );
+ size_t delay = var_InheritInteger( vlc_object_parent(p_filter),
+ "upmix-surround-delay");
+ if (delay)
+ {
+ /* Calculate the number of samples to delay. Done in two steps:
+ * 1) Find number of samples every millisecond by rounding off
+ * (adding 999) and dividing by 1000.
+ * 2) Multiply with the number of milliseconds to delay. */
+ p_sys->i_delay = delay *
+ ((p_filter->fmt_in.audio.i_rate+999)/1000);
+ }
+ switch(matrix)
+ {
+ case DOLBY_PROLOGIC:
+ p_filter->pf_audio_filter = DPL;
+ break;
+ case DOLBY_PROLOGICII:
+ p_filter->pf_audio_filter = DPLii;
+ break;
+ }
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: deallocate resources associated with filter
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+ filter_t * p_filter = (filter_t *)p_this;
+ filter_sys_t * p_sys= p_filter->p_sys;
+ block_ChainRelease(p_sys->firstblock);
+ free(p_sys);
+}
+
+/* The decoding matrix used for DPL is :
+ * __|_____Lt_____|_____Rt_____|
+ * L | 1 | 0 |
+ * R | 0 | 1 |
+ * C | 0.707107 | 0.707107 |
+ * S | 0.707107 | -0.707107 |
+ * ------------------------------ */
+static block_t *DPL ( filter_t * p_filter, block_t * p_in_buf)
+{
+ filter_sys_t * p_sys = p_filter->p_sys;
+ float * p_in = (float*) p_in_buf->p_buffer;
+ size_t i_nb_samples = p_in_buf->i_nb_samples;
+ size_t i_nb_channels = aout_FormatNbChannels( &p_filter->fmt_out.audio );
+ size_t i_nb_rear = p_sys->i_nb_rear;
+ int delay = p_sys->i_delay;
+ block_t *p_out_buf = block_Alloc(sizeof(float) *
+ i_nb_samples * i_nb_channels );
+ if( !p_out_buf )
+ goto out;
+
+ /*No of samples delayed in current block*/
+ size_t i_samples_delayed = 0;
+ block_t *head = p_sys->firstblock;
+
+ float * p_out = (float*) p_out_buf->p_buffer;
+ p_out_buf->i_nb_samples = i_nb_samples;
+ p_out_buf->i_dts = p_in_buf->i_dts;
+ p_out_buf->i_pts = p_in_buf->i_pts;
+ p_out_buf->i_length = p_in_buf->i_length;
+ memset( p_out, 0, p_out_buf->i_buffer );
+
+ if ( i_nb_rear > 0 && delay != -1 )
+ {
+ if ( delay < (int)i_nb_samples)
+ {
+ i_samples_delayed = delay;
+ delay = 0;
+ while ( head != NULL )
+ {
+ /* Calculate the size of delay block using i_buffer */
+ size_t buffer_size = head->i_buffer/(2 *sizeof(float));
+ /* Checks if using all samples in current delay block will
+ * exceed current block's size. If the size of current
+ * delay block is greater, the maximum, ie i_nb_samples
+ * is used, else we use the
+ * entire delay block ie buffer_size */
+ size_t nb_samples = (buffer_size + i_samples_delayed >
+ i_nb_samples)? i_nb_samples : buffer_size;
+ float *p_delay = (float *) head->p_buffer;
+ size_t j = 0;
+ for ( size_t i = i_samples_delayed ; i < nb_samples;
+ ++j, ++i )
+ {
+ float f_left = p_delay [ j * 2 ];
+ float f_right = p_delay [ j * 2 + 1 ];
+ float f_rear = (f_left - f_right) * 0.707107;
+ if ( p_sys->i_rear_left >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_left] = f_rear;
+ }
+ if (p_sys->i_rear_right >= 0)
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_right] = f_rear;
+ }
+ if (p_sys->i_rear_center >= 0)
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_center] = f_rear;
+ }
+ }
+ if ( nb_samples == i_nb_samples)
+ {
+ /* If the end of the current block is reached. */
+ i_samples_delayed = i_nb_samples;
+ head->p_buffer += 2 * j;
+ head->i_buffer -= 2 * j * sizeof(float);
+ break;
+ }
+ else
+ {
+ /* Update the number of samples delayed in
+ * current block and go to the next delay block. */
+ i_samples_delayed += nb_samples;
+ block_t *consumed = head;
+ head = head->p_next;
+ block_Release(consumed);
+ }
+ }
+ /* Update the head of the block_t chain. */
+ p_sys->firstblock = head;
+ }
+ else
+ {
+ delay -= i_nb_samples;
+ i_samples_delayed = i_nb_samples;
+ }
+ }
+ for( size_t i = 0; i < i_nb_samples; ++i )
+ {
+ float f_left = p_in[ i * 2 ];
+ float f_right = p_in[ i * 2 + 1 ];
+
+ if( p_sys->i_center >= 0 )
+ {
+ float f_center = (f_left + f_right) * 0.707107;
+ f_left -= f_center / 2;
+ f_right -= f_center / 2;
+ p_out[ i * i_nb_channels + p_sys->i_center ] = f_center;
+ }
+
+ if( p_sys->i_left >= 0 )
+ {
+ p_out[ i * i_nb_channels + p_sys->i_left ] = f_left;
+ }
+
+ if( p_sys->i_right >= 0 )
+ {
+ p_out[ i * i_nb_channels + p_sys->i_right ] = f_right;
+ }
+
+ if (i_nb_rear > 0)
+ {
+ float f_rear = ( f_left - f_right ) * 0.707107;
+ if (delay == -1)
+ {
+ if( p_sys->i_rear_left >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_left ] = f_rear;
+ }
+ if( p_sys->i_rear_center >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_center ] = f_rear;
+ }
+ if( p_sys->i_rear_right >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_right ] = f_rear;
+ }
+ }
+ else
+ {
+ if ( i < i_nb_samples - i_samples_delayed )
+ {
+ if( p_sys->i_rear_left >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_left ] = f_rear;
+ }
+ if( p_sys->i_rear_center >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_center ] = f_rear;
+ }
+ if( p_sys->i_rear_right >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_right ] = f_rear;
+ }
+ }
+ }
+ }
+ }
+ if ( i_nb_rear > 0 && delay != -1 )
+ {
+ if (i_samples_delayed < i_nb_samples)
+ {
+ p_in_buf->p_buffer += 2 * i_samples_delayed;
+ p_in_buf->i_buffer -= i_samples_delayed * sizeof(float) * 2;
+ }
+ p_sys->i_delay = delay;
+ block_ChainLastAppend(&(p_sys->block_chain),p_in_buf);
+ }
+out:
+ if (delay == -1 || i_nb_rear == 0)
+ {
+ block_Release( p_in_buf );
+ }
+ return p_out_buf;
+}
+
+/* The decoding matrix used for DPLii is :
+ * ___|_____Lt_____|_____Rt_____|
+ * L | 0.412956 | 0.073144 |
+ * R | 0.073144 | 0.412956 |
+ * C | 0.343724 | 0.343724 |
+ * LS | -0.294947 | 0.242277 |
+ * RS | -0.178698 | 0.205033 |
+ * ------------------------------ */
+
+static block_t *DPLii ( filter_t * p_filter, block_t * p_in_buf )
+{
+ filter_sys_t * p_sys = p_filter->p_sys;
+ float * p_in = (float*) p_in_buf->p_buffer;
+ size_t i_nb_samples = p_in_buf->i_nb_samples;
+ int i_nb_channels = aout_FormatNbChannels( &p_filter->fmt_out.audio );
+ size_t i_nb_rear = p_sys->i_nb_rear;
+ int delay = p_sys->i_delay;
+ block_t *p_out_buf = block_Alloc(sizeof(float) *
+ i_nb_samples * i_nb_channels );
+ if( !p_out_buf )
+ goto out;
+
+ float * p_out = (float*) p_out_buf->p_buffer;
+ /*No of samples delayed */
+ size_t i_samples_delayed = 0;
+ block_t *head = p_sys->firstblock;
+
+ p_out_buf->i_nb_samples = i_nb_samples;
+ p_out_buf->i_dts = p_in_buf->i_dts;
+ p_out_buf->i_pts = p_in_buf->i_pts;
+ p_out_buf->i_length = p_in_buf->i_length;
+ memset( p_out, 0, p_out_buf->i_buffer );
+
+ if ( i_nb_rear > 0 && delay != -1 )
+ {
+ if ( delay < (int)i_nb_samples)
+ {
+ i_samples_delayed = delay;
+ delay = 0;
+ while ( head != NULL )
+ {
+ /* Calculate the size of delay block using i_buffer */
+ size_t buffer_size = head->i_buffer/(2 *sizeof(float));
+ /* Checks if using all samples in current delay block will
+ * exceed current block's size. If the size of current
+ * delay block is greater, the maximum, ie i_nb_samples
+ * is used, else we use the
+ * entire delay block ie buffer_size */
+ size_t nb_samples = (buffer_size + i_samples_delayed >
+ i_nb_samples)? i_nb_samples :
+ buffer_size;
+ float *p_delay = (float *) head->p_buffer;
+ size_t j = 0;
+ for ( size_t i = i_samples_delayed ; i < nb_samples;
+ ++j, ++i )
+ {
+ float f_left = p_delay [ j * 2 ];
+ float f_right = p_delay [ j * 2 + 1 ];
+ float f_rearleft = f_right * 0.242277 -
+ f_left * 0.294947 ;
+ float f_rearright= f_right * 0.205033 -
+ f_left * 0.178698 ;
+ if ( p_sys->i_rear_left >= 0 )
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_left] = f_rearleft;
+ }
+ if (p_sys->i_rear_right >= 0)
+ {
+ p_out[ i * i_nb_channels +
+ p_sys->i_rear_right] = f_rearright;
+ }
+ }
+ if ( nb_samples == i_nb_samples)
+ {
+ /* If the end of the current block is reached. */
+ i_samples_delayed = i_nb_samples;
+ head->p_buffer += 2 * j;
+ head->i_buffer -= 2 * j * sizeof(float);
+ break;
+ }
+ else
+ {
+ /* Update the number of samples delayed in
+ * current block and go to the next delay block. */
+ i_samples_delayed += nb_samples;
+ block_t *consumed = head;
+ head = head->p_next;
+ block_Release(consumed);
+ }
+ }
+ /* Update the head of the block_t chain. */
+ p_sys->firstblock = head;
+ }
+ else
+ {
+ delay -= i_nb_samples;
+ i_samples_delayed = i_nb_samples;
+ }
+ }
+ for ( size_t i = 0 ; i < i_nb_samples ; i++ )
+ {
+ float f_left = p_in[ i * 2 ];
+ float f_right = p_in[ i * 2 + 1 ];
+
+ if (p_sys->i_left >= 0)
+ {
+ p_out [ i * i_nb_channels + p_sys->i_left ] =
+ f_left * 0.412956 + f_right * 0.073144;
+ }
+ if (p_sys->i_right >= 0)
+ {
+ p_out [ i * i_nb_channels + p_sys->i_right ] =
+ f_left * 0.073144 + f_right * 0.412956;
+ }
+ if (p_sys->i_center >= 0)
+ {
+ p_out [ i * i_nb_channels + p_sys->i_center ] =
+ (f_left + f_right) * 0.343724;
+ }
+ if (i_nb_rear > 0)
+ {
+ if ( delay == -1 )
+ {
+ /* If no delay is present */
+ if (p_sys->i_rear_left >= 0)
+ {
+ p_out [ i * i_nb_channels + p_sys->i_rear_left ] =
+ f_right * 0.242277 - f_left * 0.294947;
+ }
+ if (p_sys->i_rear_right >= 0)
+ {
+ p_out [ i * i_nb_channels + p_sys->i_rear_right ] =
+ f_right * 0.205033 - f_left * 0.178698;
+ }
+ }
+ else
+ {
+ /* If delay is present */
+ if ( i < i_nb_samples - i_samples_delayed )
+ {
+ if (p_sys->i_rear_left >= 0)
+ {
+ p_out [ (i + i_samples_delayed) * i_nb_channels
+ + p_sys->i_rear_left ] = f_right * 0.242277
+ - f_left * 0.294947;
+ }
+ if (p_sys->i_rear_right >= 0)
+ {
+ p_out [ (i + i_samples_delayed) * i_nb_channels +
+ p_sys->i_rear_right ] = f_right * 0.205033
+ - f_left * 0.178698;
+ }
+ }
+ }
+ }
+ }
+ if ( i_nb_rear > 0 && delay != -1 )
+ {
+ if (i_samples_delayed < i_nb_samples)
+ {
+ p_in_buf->p_buffer += 2 * i_samples_delayed;
+ p_in_buf->i_buffer -= i_samples_delayed * sizeof(float) * 2;
+ }
+ p_sys->i_delay = delay;
+ block_ChainLastAppend(&(p_sys->block_chain),p_in_buf);
+ }
+out:
+ /* Skip block_Release if there is surround delay */
+ if (delay == -1 || i_nb_rear == 0)
+ {
+ block_Release( p_in_buf );
+ }
+ return p_out_buf;
+}
--
More information about the vlc-devel
mailing list