committed by
Jean-Baptiste Kempf
4 changed files with 237 additions and 1 deletions
@ -0,0 +1,232 @@ |
|||
/*****************************************************************************
|
|||
* stereo_widen.c : simple stereo widening effect |
|||
***************************************************************************** |
|||
* Copyright (C) 2012 VLC authors and VideoLAN |
|||
* |
|||
* Author : Sukrit Sangwan < sukritsangwan at gmail dot 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. |
|||
*****************************************************************************/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
|
|||
#include <vlc_common.h> |
|||
#include <vlc_aout.h> |
|||
#include <vlc_filter.h> |
|||
#include <vlc_plugin.h> |
|||
|
|||
/*****************************************************************************
|
|||
* Local prototypes |
|||
*****************************************************************************/ |
|||
static int Open ( vlc_object_t * ); |
|||
static void Close( vlc_object_t * ); |
|||
|
|||
static block_t *Filter ( filter_t *, block_t * ); |
|||
static int paramCallback( vlc_object_t *, char const *, vlc_value_t , |
|||
vlc_value_t , void * ); |
|||
|
|||
struct filter_sys_t |
|||
{ |
|||
float *pf_begin; /* circular buffer to store samples */ |
|||
float *pf_write; /* where to write current sample */ |
|||
int i_len; /* delay in number of samples */ |
|||
float f_delay; /* delay in milliseconds */ |
|||
float f_feedback; |
|||
float f_crossfeed; |
|||
float f_dry_mix; |
|||
bool b_free_buf; /* used if callback to delay fails to *
|
|||
* allocate buffer, then dont free it twice */ |
|||
}; |
|||
|
|||
#define DESCRIPTION N_("This filter enhances stereo effect by \ |
|||
suppressing mono,i.e. signal common to both channels, \ |
|||
and by delaying the signal of left into right and vice versa \ |
|||
thereby widening stereo effect") |
|||
#define DELAY_TEXT N_("Delay time") |
|||
#define DELAY_LONGTEXT N_("Time in ms of the delay of left signal into right \ |
|||
and vice versa.") |
|||
#define FEEDBACK_TEXT N_("Feedback Gain") |
|||
#define FEEDBACK_LONGTEXT N_("Amount of gain in delayed left signal into \ |
|||
right and vice versa. Gives a delay effect of left signal in \ |
|||
right output and vice versa which gives widening effect") |
|||
#define CROSSFEED_TEXT N_("Crossfeed") |
|||
#define CROSSFEED_LONGTEXT N_("Cross feed of left into right with inverted \ |
|||
phase. This helps in suppression of mono. If its value is 1 it \ |
|||
cancels all the signal common to both channels") |
|||
#define DRYMIX_TEXT N_("Dry Mix") |
|||
#define DRYMIX_LONGTEXT N_("Level of input signal of original channel") |
|||
|
|||
/*****************************************************************************
|
|||
* Module descriptor |
|||
*****************************************************************************/ |
|||
vlc_module_begin () |
|||
set_shortname( N_("stereo_widen") ) |
|||
set_description( DESCRIPTION ) |
|||
set_category( CAT_AUDIO ) |
|||
set_subcategory( SUBCAT_AUDIO_AFILTER ) |
|||
set_capability( "audio filter", 0 ) |
|||
set_callbacks( Open, Close ) |
|||
|
|||
add_float( "delay", 20, DELAY_TEXT, DELAY_LONGTEXT, true ) |
|||
add_float_with_range( "feedback", 0.3, 0.0, 0.9, |
|||
FEEDBACK_TEXT, FEEDBACK_LONGTEXT, true ) |
|||
add_float_with_range( "crossfeed", 0.3, 0.0, 0.8, |
|||
CROSSFEED_TEXT, CROSSFEED_LONGTEXT, true ) |
|||
add_float_with_range( "dry-mix", 0.8, 0.0, 1.0, |
|||
DRYMIX_TEXT, DRYMIX_LONGTEXT, true ) |
|||
vlc_module_end () |
|||
|
|||
/*****************************************************************************
|
|||
* Open: Allocate buffer |
|||
*****************************************************************************/ |
|||
static int Open( vlc_object_t *obj ) |
|||
{ |
|||
filter_t *p_filter = (filter_t *)obj; |
|||
filter_sys_t *p_sys; |
|||
|
|||
if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 || |
|||
!AOUT_FMTS_IDENTICAL( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio) ) |
|||
return VLC_EGENERIC; |
|||
|
|||
if( p_filter->fmt_in.audio.i_channels != 2 ) |
|||
{ |
|||
msg_Err ( p_filter, "stereo enhance requires stereo" ); |
|||
return VLC_EGENERIC; |
|||
} |
|||
|
|||
p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) ); |
|||
if( unlikely(!p_sys) ) |
|||
return VLC_ENOMEM; |
|||
|
|||
#define CREATE_VAR( stor, var ) \ |
|||
p_sys->stor = var_CreateGetFloat( obj, var ); \ |
|||
var_AddCallback( p_filter, var, paramCallback, p_sys ); |
|||
|
|||
CREATE_VAR( f_delay, "delay" ) |
|||
CREATE_VAR( f_feedback, "feedback" ) |
|||
CREATE_VAR( f_crossfeed, "crossfeed" ) |
|||
CREATE_VAR( f_dry_mix, "dry-mix" ) |
|||
|
|||
/* Compute buffer length and allocate space */ |
|||
p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate / 1000; |
|||
p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) ); |
|||
if( unlikely(!p_sys->pf_begin) ) |
|||
{ |
|||
free( p_sys ); |
|||
return VLC_ENOMEM; |
|||
} |
|||
p_sys->b_free_buf = true; |
|||
p_sys->pf_write = p_sys->pf_begin; |
|||
p_filter->pf_audio_filter = Filter; |
|||
return VLC_SUCCESS; |
|||
} |
|||
|
|||
|
|||
/*****************************************************************************
|
|||
* Filter: process each sample |
|||
*****************************************************************************/ |
|||
static block_t *Filter( filter_t *p_filter, block_t *p_block ) |
|||
{ |
|||
filter_sys_t *p_sys = p_filter->p_sys; |
|||
float *p_out = (float *)p_block->p_buffer; |
|||
float *pf_read; |
|||
|
|||
for (unsigned i = p_block->i_nb_samples; i > 0; i--) |
|||
{ |
|||
pf_read = p_sys->pf_write + 2; |
|||
/* if at end of buffer put read ptr at begin */ |
|||
if( pf_read > p_sys->pf_begin + p_sys->i_len ) |
|||
pf_read = p_sys->pf_begin; |
|||
|
|||
float left = p_out[0]; |
|||
float right = p_out[1]; |
|||
|
|||
*(p_out++) = p_sys->f_dry_mix * left - p_sys->f_crossfeed * right |
|||
- p_sys->f_feedback * pf_read[1]; |
|||
*(p_out++) = p_sys->f_dry_mix * right - p_sys->f_crossfeed * left |
|||
- p_sys->f_feedback * pf_read[0]; |
|||
p_sys->pf_write[0] = left ; |
|||
p_sys->pf_write[1] = right; |
|||
|
|||
/* if at end of buffer place pf_write at begin */ |
|||
if( p_sys->pf_write == p_sys->pf_begin + p_sys->i_len ) |
|||
p_sys->pf_write = p_sys->pf_begin; |
|||
else |
|||
p_sys->pf_write += 2; |
|||
} |
|||
|
|||
return p_block; |
|||
} |
|||
|
|||
/*****************************************************************************
|
|||
* Close: close the plugin |
|||
*****************************************************************************/ |
|||
static void Close( vlc_object_t *obj ) |
|||
{ |
|||
filter_t *p_filter = (filter_t *)obj; |
|||
filter_sys_t *p_sys = p_filter->p_sys; |
|||
|
|||
#define DEL_VAR(var) \ |
|||
var_DelCallback( p_filter, var, paramCallback, p_sys ); \ |
|||
var_Destroy( p_filter, var ); |
|||
|
|||
DEL_VAR( "feedback" ); |
|||
DEL_VAR( "crossfeed" ); |
|||
DEL_VAR( "dry-mix" ); |
|||
var_Destroy( p_filter, "delay" ); |
|||
if( p_sys->b_free_buf ) |
|||
free( p_sys->pf_begin ); |
|||
free( p_sys ); |
|||
} |
|||
|
|||
|
|||
/**********************************************************************
|
|||
* Callback to update params on the fly |
|||
**********************************************************************/ |
|||
static int paramCallback( vlc_object_t *p_this, char const *psz_var, |
|||
vlc_value_t oldval, vlc_value_t newval, |
|||
void *p_data ) |
|||
{ |
|||
filter_t *p_filter = (filter_t *)p_this; |
|||
filter_sys_t *p_sys = (filter_sys_t *) p_data; |
|||
|
|||
VLC_UNUSED(oldval); |
|||
VLC_UNUSED(p_this); |
|||
|
|||
if( !strcmp( psz_var, "delay" ) ) |
|||
{ |
|||
p_sys->f_delay = newval.f_float; |
|||
/* Free previous buffer and allocate new circular buffer */ |
|||
free( p_sys->pf_begin ); |
|||
p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate /1000; |
|||
p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) ); |
|||
if( unlikely(!p_sys->pf_begin) ) |
|||
{ |
|||
p_sys->b_free_buf = false; |
|||
msg_Dbg( p_filter, "Couldnt allocate buffer for delay" ); |
|||
Close( p_this ); |
|||
} |
|||
} |
|||
else if( !strcmp( psz_var, "feedback" ) ) |
|||
p_sys->f_feedback = newval.f_float; |
|||
else if( !strcmp( psz_var, "crossfeed" ) ) |
|||
p_sys->f_feedback = newval.f_float; |
|||
else if( !strcmp( psz_var, "dry-mix" ) ) |
|||
p_sys->f_dry_mix = newval.f_float; |
|||
|
|||
return VLC_SUCCESS; |
|||
} |
|||
Loading…
Reference in new issue