[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