Commit 5bf84f02 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

trivial_mixer: rewrite, fix dual-mode (fixes #12673)

(cherry picked from commit a6c5f999ea28070c73f3c7db47414f666fdd6265)
(cherry picked from commit 1047340ebc8f59895d7ad8747f7ec48528a31f64)
(cherry picked from commit 81765639fdae8b0c9532c31e90e49eb6aecd5d72)
(cherry picked from commit d6388f26bb6efef29397ba91b02befc427b3423e)
(cherry picked from commit 4e63463ac0022f7eca3c4b9decb6ad5501b7ce38)
(cherry picked from commit 4d6a9eaa413c73277648d4f98c5da1840f997b33)
parent a7336c2a
/*****************************************************************************
* trivial.c : trivial channel mixer plug-in (drops unwanted channels)
*****************************************************************************
* Copyright (C) 2002, 2006 VLC authors and VideoLAN
* $Id$
* Copyright (C) 2002, 2006, 2014 VLC authors and VideoLAN
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
......@@ -29,21 +28,15 @@
# include "config.h"
#endif
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
#include <vlc_filter.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Create ( vlc_object_t * );
static int Create( vlc_object_t * );
static block_t *DoWork( filter_t *, block_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin ()
set_description( N_("Audio filter for trivial channel mixing") )
set_capability( "audio converter", 1 )
......@@ -52,136 +45,204 @@ vlc_module_begin ()
set_callbacks( Create, NULL )
vlc_module_end ()
/*****************************************************************************
* Create: allocate trivial channel mixer
*****************************************************************************/
static int Create( vlc_object_t *p_this )
/**
* Trivially upmixes
*/
static block_t *Upmix( filter_t *p_filter, block_t *p_in_buf )
{
filter_t * p_filter = (filter_t *)p_this;
if ( (p_filter->fmt_in.audio.i_physical_channels
== p_filter->fmt_out.audio.i_physical_channels
&& p_filter->fmt_in.audio.i_original_channels
== p_filter->fmt_out.audio.i_original_channels)
|| p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format
|| p_filter->fmt_in.audio.i_rate != p_filter->fmt_out.audio.i_rate
|| p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 )
unsigned i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
unsigned i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
assert( i_input_nb < i_output_nb );
block_t *p_out_buf = block_Alloc(
p_in_buf->i_buffer * i_output_nb / i_input_nb );
if( unlikely(p_out_buf == NULL) )
{
return VLC_EGENERIC;
block_Release( p_in_buf );
return NULL;
}
p_filter->pf_audio_filter = DoWork;
return VLC_SUCCESS;
p_out_buf->i_nb_samples = p_in_buf->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;
float *p_dest = (float *)p_out_buf->p_buffer;
const float *p_src = (float *)p_in_buf->p_buffer;
for( size_t i = 0; i < p_in_buf->i_nb_samples; i++ )
{
for( unsigned j = 0; j < i_output_nb; j++ )
p_dest[j] = p_src[j % i_input_nb];
p_src += i_input_nb;
p_dest += i_output_nb;
}
block_Release( p_in_buf );
return p_out_buf;
}
/*****************************************************************************
* SparseCopy: trivially downmix or upmix a buffer
*****************************************************************************/
static void SparseCopy( float * p_dest, const float * p_src, size_t i_len,
int i_output_stride, int i_input_stride )
/**
* Trivially downmixes (i.e. drop extra channels)
*/
static block_t *Downmix( filter_t *p_filter, block_t *p_buf )
{
int i;
for ( i = i_len; i--; )
unsigned i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
unsigned i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
assert( i_input_nb >= i_output_nb );
float *p_dest = (float *)p_buf->p_buffer;
const float *p_src = p_dest;
for( size_t i = 0; i < p_buf->i_nb_samples; i++ )
{
int j;
for ( j = 0; j < i_output_stride; j++ )
{
p_dest[j] = p_src[j % i_input_stride];
}
p_src += i_input_stride;
p_dest += i_output_stride;
for( unsigned j = 0; j < i_output_nb; j++ )
p_dest[j] = p_src[j];
p_src += i_input_nb;
p_dest += i_output_nb;
}
return p_buf;
}
/*****************************************************************************
* DoWork: convert a buffer
*****************************************************************************/
static block_t *DoWork( filter_t * p_filter, block_t * p_in_buf )
static block_t *CopyLeft( filter_t *p_filter, block_t *p_buf )
{
int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
int i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
float *p = (float *)p_buf->p_buffer;
block_t *p_out_buf;
if( i_input_nb >= i_output_nb )
for( unsigned i = 0; i < p_buf->i_nb_samples; i++ )
{
p_out_buf = p_in_buf; /* mix in place */
p_out_buf->i_buffer = p_in_buf->i_buffer * i_output_nb / i_input_nb;
p[1] = p[0];
p += 2;
}
else
(void) p_filter;
return p_buf;
}
static block_t *CopyRight( filter_t *p_filter, block_t *p_buf )
{
float *p = (float *)p_buf->p_buffer;
for( unsigned i = 0; i < p_buf->i_nb_samples; i++ )
{
p_out_buf = block_Alloc(
p_in_buf->i_buffer * i_output_nb / i_input_nb );
if( !p_out_buf )
goto out;
/* on upmixing case, zero out buffer */
memset( p_out_buf->p_buffer, 0, p_out_buf->i_buffer );
p_out_buf->i_nb_samples = p_in_buf->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;
p[0] = p[1];
p += 2;
}
(void) p_filter;
return p_buf;
}
float * p_dest = (float *)p_out_buf->p_buffer;
const float * p_src = (float *)p_in_buf->p_buffer;
const bool b_reverse_stereo = p_filter->fmt_out.audio.i_original_channels & AOUT_CHAN_REVERSESTEREO;
bool b_dualmono2stereo = (p_filter->fmt_in.audio.i_original_channels & AOUT_CHAN_DUALMONO );
b_dualmono2stereo &= (p_filter->fmt_out.audio.i_physical_channels & ( AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT ));
b_dualmono2stereo &= ((p_filter->fmt_out.audio.i_physical_channels & AOUT_CHAN_PHYSMASK) != (p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_PHYSMASK));
static block_t *ExtractLeft( filter_t *p_filter, block_t *p_buf )
{
float *p_dest = (float *)p_buf->p_buffer;
const float *p_src = p_dest;
if( likely( !b_reverse_stereo && ! b_dualmono2stereo ) )
for( unsigned i = 0; i < p_buf->i_nb_samples; i++ )
{
SparseCopy( p_dest, p_src, p_in_buf->i_nb_samples, i_output_nb,
i_input_nb );
*(p_dest++) = *p_src;
p_src += 2;
}
/* Special case from dual mono to stereo */
else if ( b_dualmono2stereo )
(void) p_filter;
return p_buf;
}
static block_t *ExtractRight( filter_t *p_filter, block_t *p_buf )
{
float *p_dest = (float *)p_buf->p_buffer;
const float *p_src = p_dest;
for( unsigned i = 0; i < p_buf->i_nb_samples; i++ )
{
int i;
/* This is a bit special. */
if ( !(p_filter->fmt_out.audio.i_original_channels & AOUT_CHAN_LEFT) )
p_src++;
*(p_dest++) = *(p_src++);
}
(void) p_filter;
return p_buf;
}
static block_t *ReverseStereo( filter_t *p_filter, block_t *p_buf )
{
float *p = (float *)p_buf->p_buffer;
/* Reverse-stereo mode */
for( unsigned i = 0; i < p_buf->i_nb_samples; i++ )
{
float f = p[0];
p[0] = p[1];
p[1] = f;
p += 2;
}
(void) p_filter;
return p_buf;
}
/**
* Probes the trivial channel mixer
*/
static int Create( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *)p_this;
const audio_format_t *infmt = &p_filter->fmt_in.audio;
const audio_format_t *outfmt = &p_filter->fmt_out.audio;
if( infmt->i_format != outfmt->i_format
|| infmt->i_rate != outfmt->i_rate
|| infmt->i_format != VLC_CODEC_FL32 )
return VLC_EGENERIC;
if( infmt->i_physical_channels == outfmt->i_physical_channels
&& infmt->i_original_channels == outfmt->i_original_channels )
return VLC_EGENERIC;
if( outfmt->i_physical_channels == AOUT_CHANS_STEREO )
{
bool swap = (outfmt->i_original_channels & AOUT_CHAN_REVERSESTEREO)
!= (infmt->i_original_channels & AOUT_CHAN_REVERSESTEREO);
if( (outfmt->i_original_channels & AOUT_CHAN_PHYSMASK)
== AOUT_CHAN_LEFT )
{
p_src++;
p_filter->pf_audio_filter = swap ? CopyRight : CopyLeft;
return VLC_SUCCESS;
}
if ( p_filter->fmt_out.audio.i_physical_channels == AOUT_CHAN_CENTER )
if( (outfmt->i_original_channels & AOUT_CHAN_PHYSMASK)
== AOUT_CHAN_RIGHT )
{
/* Mono mode */
for ( i = p_in_buf->i_nb_samples; i--; )
{
*p_dest = *p_src;
p_dest++;
p_src += 2;
}
p_filter->pf_audio_filter = swap ? CopyLeft : CopyRight;
return VLC_SUCCESS;
}
else
if( swap )
{
/* Fake-stereo mode */
for ( i = p_in_buf->i_nb_samples; i--; )
{
*p_dest = *p_src;
p_dest++;
*p_dest = *p_src;
p_dest++;
p_src += 2;
}
p_filter->pf_audio_filter = ReverseStereo;
return VLC_SUCCESS;
}
}
else if ( b_reverse_stereo )
if ( aout_FormatNbChannels( outfmt ) == 1 )
{
/* Reverse-stereo mode */
int i;
for ( i = p_in_buf->i_nb_samples; i--; )
bool mono = !!(infmt->i_original_channels & AOUT_CHAN_DUALMONO);
if( mono && (infmt->i_original_channels & AOUT_CHAN_LEFT) )
{
float i_tmp = p_src[0];
p_dest[0] = p_src[1];
p_dest[1] = i_tmp;
p_filter->pf_audio_filter = ExtractLeft;
return VLC_SUCCESS;
}
p_dest += 2;
p_src += 2;
if( mono && (infmt->i_original_channels & AOUT_CHAN_RIGHT) )
{
p_filter->pf_audio_filter = ExtractRight;
return VLC_SUCCESS;
}
}
out:
if( p_in_buf != p_out_buf )
block_Release( p_in_buf );
return p_out_buf;
}
if( aout_FormatNbChannels( outfmt ) > aout_FormatNbChannels( infmt ) )
p_filter->pf_audio_filter = Upmix;
else
p_filter->pf_audio_filter = Downmix;
return VLC_SUCCESS;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment