Commit 036c9c52 authored by Boris Dorès's avatar Boris Dorès

- the creation of the audio filter pipeline when specifying user audio

  channel mixers was broken (again) in 0.8.1 and 0.8.2; hopefully this
  will fix it for good, by allowing audio filters to request a specific
  format rather than imposing them an arbitrary one
- various other small fixes in the audio output core
- option to force detection of Dolby Surround
- simple module to decode Dolby Surround
- increase spatialization with two center speakers and fix channel
  order for 7.1 streams in the headphone channel mixer
parent 880f95fd
......@@ -1015,7 +1015,7 @@ dnl VLC_ADD_PLUGINS([externrun])
VLC_ADD_PLUGINS([i420_yuy2 i422_yuy2 i420_ymga])
VLC_ADD_PLUGINS([aout_file linear_resampler bandlimited_resampler])
VLC_ADD_PLUGINS([float32_mixer spdif_mixer simple_channel_mixer])
VLC_ADD_PLUGINS([headphone_channel_mixer normvol equalizer])
VLC_ADD_PLUGINS([dolby_surround_decoder headphone_channel_mixer normvol equalizer])
VLC_ADD_PLUGINS([fixed32tofloat32 float32tos16 float32tos8 float32tou16 float32tou8 a52tospdif dtstospdif s16tofloat32 s16tofloat32swab s8tofloat32 u8tofloat32 audio_format])
fi
......
......@@ -109,6 +109,8 @@ $Id$
* dmo: a DirectMediaObject decoder that uses DirectMedia to decode video (WMV3)
* dolby_surround_decoder: simple decoder for dolby surround encoded streams
* dshow: DirectShow access plugin for encoding cards under Windows
* dts: DTS basic parser/packetizer
......
SOURCES_trivial_channel_mixer = trivial.c
SOURCES_simple_channel_mixer = simple.c
SOURCES_headphone_channel_mixer = headphone.c
SOURCES_dolby_surround_decoder = dolby.c
/*****************************************************************************
* dolby.c : simple decoder for dolby surround encoded streams
*****************************************************************************
* Copyright (C) 2005 the VideoLAN team
* $Id: headphone.c 11664 2005-07-09 06:17:09Z courmisch $
*
* Authors: Boris Dors <babal@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <vlc/vlc.h>
#include "audio_output.h"
#include "aout_internal.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * );
static void DoWork ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
aout_buffer_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( N_("Simple decoder for dolby surround encoded streams") );
set_shortname( _("Dolby surround decoder") );
set_category( CAT_INPUT );
set_subcategory( SUBCAT_INPUT_ACODEC );
set_capability( "audio filter", 5 );
set_callbacks( Create, Destroy );
vlc_module_end();
/*****************************************************************************
* Internal data structures
*****************************************************************************/
struct aout_filter_sys_t
{
int i_left;
int i_center;
int i_right;
int i_rear_left;
int i_rear_center;
int i_rear_right;
};
/* our internal channel order (WG-4 order) */
static const uint32_t pi_channels[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
/*****************************************************************************
* Create: allocate headphone downmixer
*****************************************************************************/
static int Create( vlc_object_t *p_this )
{
int i = 0;
int i_offset = 0;
aout_filter_t * p_filter = (aout_filter_t *)p_this;
/* Validate audio filter format */
if ( p_filter->input.i_original_channels
!= (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT|AOUT_CHAN_DOLBYSTEREO )
|| aout_FormatNbChannels( &p_filter->output ) <= 2
|| p_filter->output.i_physical_channels
!= ( p_filter->output.i_original_channels & AOUT_CHAN_PHYSMASK )
|| p_filter->input.i_physical_channels
!= ( p_filter->input.i_original_channels & AOUT_CHAN_PHYSMASK ))
{
return VLC_EGENERIC;
}
if ( p_filter->input.i_rate != p_filter->output.i_rate )
{
return VLC_EGENERIC;
}
if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
|| p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the module's structure */
p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
if ( p_filter->p_sys == NULL )
{
msg_Err( p_filter, "Out of memory" );
return VLC_EGENERIC;
}
p_filter->p_sys->i_left = -1;
p_filter->p_sys->i_center = -1;
p_filter->p_sys->i_right = -1;
p_filter->p_sys->i_rear_left = -1;
p_filter->p_sys->i_rear_center = -1;
p_filter->p_sys->i_rear_right = -1;
while ( pi_channels[i] )
{
if ( p_filter->output.i_physical_channels & pi_channels[i] )
{
switch ( pi_channels[i] )
{
case AOUT_CHAN_LEFT:
p_filter->p_sys->i_left = i_offset;
break;
case AOUT_CHAN_CENTER:
p_filter->p_sys->i_center = i_offset;
break;
case AOUT_CHAN_RIGHT:
p_filter->p_sys->i_right = i_offset;
break;
case AOUT_CHAN_REARLEFT:
p_filter->p_sys->i_rear_left = i_offset;
break;
case AOUT_CHAN_REARCENTER:
p_filter->p_sys->i_rear_center = i_offset;
break;
case AOUT_CHAN_REARRIGHT:
p_filter->p_sys->i_rear_right = i_offset;
break;
}
++i_offset;
}
++i;
}
p_filter->pf_do_work = DoWork;
p_filter->b_in_place = 0;
return VLC_SUCCESS;
}
/*****************************************************************************
* Destroy: deallocate resources associated with headphone downmixer
*****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
aout_filter_t * p_filter = (aout_filter_t *)p_this;
if ( p_filter->p_sys != NULL )
{
free ( p_filter->p_sys );
p_filter->p_sys = NULL;
}
}
/*****************************************************************************
* DoWork: convert a buffer
*****************************************************************************/
static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
float * p_in = (float*) p_in_buf->p_buffer;
float * p_out = (float*) p_out_buf->p_buffer;
size_t i_nb_samples = p_in_buf->i_nb_samples;
size_t i_nb_channels = aout_FormatNbChannels( &p_filter->output );
p_out_buf->i_nb_samples = i_nb_samples;
p_out_buf->i_nb_bytes = sizeof(float) * i_nb_samples
* aout_FormatNbChannels( &p_filter->output );
memset ( p_out , 0 , p_out_buf->i_nb_bytes );
if ( p_filter->p_sys != NULL )
{
struct aout_filter_sys_t * p_sys = p_filter->p_sys;
size_t i_nb_rear = 0;
size_t i;
if ( p_sys->i_rear_left >= 0 )
{
++i_nb_rear;
}
if ( p_sys->i_rear_center >= 0 )
{
++i_nb_rear;
}
if ( p_sys->i_rear_right >= 0 )
{
++i_nb_rear;
}
for ( i = 0; i < i_nb_samples; ++i )
{
float f_left = p_in[ i * 2 ];
float f_right = p_in[ i * 2 + 1 ];
float f_rear = ( f_left - f_right ) / i_nb_rear;
if ( p_sys->i_center >= 0 )
{
float f_center = f_left + f_right;
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 ( 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;
}
}
}
}
......@@ -2,7 +2,7 @@
* headphone.c : headphone virtual spatialization channel mixer module
* -> gives the feeling of a real room with a simple headphone
*****************************************************************************
* Copyright (C) 2002 the VideoLAN team
* Copyright (C) 2002-2005 the VideoLAN team
* $Id$
*
* Authors: Boris Dors <babal@via.ecp.fr>
......@@ -47,11 +47,11 @@ static void DoWork ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
*****************************************************************************/
#define MODULE_DESCRIPTION N_ ( \
"This effect gives you the feeling that you are standing in a room " \
"with a complete 5.1 speaker set when using only a headphone, " \
"with a complete 7.1 speaker set when using only a headphone, " \
"providing a more realistic sound experience. It should also be " \
"more comfortable and less tiring when listening to music for " \
"long periods of time.\nIt works with any source format from mono " \
"to 5.1.")
"to 7.1.")
#define HEADPHONE_DIM_TEXT N_("Characteristic dimension")
#define HEADPHONE_DIM_LONGTEXT N_( \
......@@ -184,6 +184,10 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
/* Number of elementary operations */
p_data->i_nb_atomic_operations = i_nb_channels * 2;
if ( i_physical_channels & AOUT_CHAN_CENTER )
{
p_data->i_nb_atomic_operations += 2;
}
p_data->p_atomic_operations = malloc ( sizeof(struct atomic_operation_t)
* p_data->i_nb_atomic_operations );
if ( p_data->p_atomic_operations == NULL )
......@@ -212,59 +216,64 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARLEFT )
if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x , d_z_rear , 1.5 / i_nb_channels );
, -d_x , 0 , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, d_x , d_z_rear , 1.5 / i_nb_channels );
, d_x , 0 , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARCENTER )
if ( i_physical_channels & AOUT_CHAN_REARLEFT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , -d_z , 1.5 / i_nb_channels );
, -d_x , d_z_rear , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_CENTER )
if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , d_z , 1.5 / i_nb_channels );
, d_x , d_z_rear , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_LFE )
if ( i_physical_channels & AOUT_CHAN_REARCENTER )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , d_z_rear , 5.0 / i_nb_channels );
, 0 , -d_z , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
if ( i_physical_channels & AOUT_CHAN_CENTER )
{
/* having two center channels increases the spatialization effect */
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x , 0 , 1.5 / i_nb_channels );
, d_x / 5.0 , d_z , 0.75 / i_nb_channels );
i_next_atomic_operation += 2;
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x / 5.0 , d_z , 0.75 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
if ( i_physical_channels & AOUT_CHAN_LFE )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, d_x , 0 , 1.5 / i_nb_channels );
, 0 , d_z_rear , 5.0 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
......@@ -301,23 +310,54 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
static int Create( vlc_object_t *p_this )
{
aout_filter_t * p_filter = (aout_filter_t *)p_this;
vlc_bool_t b_fit = VLC_TRUE;
if ( p_filter->output.i_physical_channels != ( AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT )
|| p_filter->input.i_format != p_filter->output.i_format
|| p_filter->input.i_rate != p_filter->output.i_rate
|| (p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
&& p_filter->input.i_format != VLC_FOURCC('f','i','3','2')) )
/* Activate this filter only with stereo devices */
if ( p_filter->output.i_physical_channels != (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
|| p_filter->output.i_physical_channels
!= ( p_filter->output.i_original_channels & AOUT_CHAN_PHYSMASK )
|| p_filter->input.i_physical_channels
!= ( p_filter->input.i_original_channels & AOUT_CHAN_PHYSMASK ) )
{
msg_Dbg( p_filter, "Filter discarded (invalid format)" );
return -1;
msg_Dbg( p_filter, "Filter discarded (incompatible format)" );
return VLC_EGENERIC;
}
/* Request a specific format if not already compatible */
if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
|| p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
}
if ( p_filter->input.i_rate != p_filter->output.i_rate )
{
b_fit = VLC_FALSE;
p_filter->input.i_rate = p_filter->output.i_rate;
}
if ( p_filter->input.i_physical_channels == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
&& ( p_filter->input.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) )
{
b_fit = VLC_FALSE;
p_filter->input.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
AOUT_CHAN_CENTER |
AOUT_CHAN_REARLEFT |
AOUT_CHAN_REARRIGHT;
p_filter->input.i_original_channels = p_filter->input.i_physical_channels;
}
if ( ! b_fit )
{
msg_Dbg( p_filter, "Requesting specific format" );
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the module's structure */
p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
if ( p_filter->p_sys == NULL )
{
msg_Err( p_filter, "out of memory" );
return -1;
msg_Err( p_filter, "Out of memory" );
return VLC_EGENERIC;
}
p_filter->p_sys->i_overflow_buffer_size = 0;
p_filter->p_sys->p_overflow_buffer = NULL;
......@@ -329,13 +369,13 @@ static int Create( vlc_object_t *p_this )
, p_filter->input.i_physical_channels
, p_filter->input.i_rate ) < 0 )
{
return -1;
return VLC_EGENERIC;
}
p_filter->pf_do_work = DoWork;
p_filter->b_in_place = 0;
return 0;
return VLC_SUCCESS;
}
/*****************************************************************************
......
......@@ -136,16 +136,26 @@ static int Open( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t *)p_this;
aout_filter_sys_t *p_sys;
vlc_bool_t b_fit = VLC_TRUE;
if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
msg_Warn( p_filter, "Bad input or output format" );
return VLC_EGENERIC;
}
if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
{
b_fit = VLC_FALSE;
memcpy( &p_filter->output, &p_filter->input,
sizeof(audio_sample_format_t) );
msg_Warn( p_filter, "input and output formats are not similar" );
}
if ( ! b_fit )
{
return VLC_EGENERIC;
}
......
......@@ -98,6 +98,7 @@ vlc_module_end();
static int Open( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t*)p_this;
vlc_bool_t b_fit = VLC_TRUE;
int i_channels;
aout_filter_sys_t *p_sys = p_filter->p_sys =
malloc( sizeof( aout_filter_sys_t ) );
......@@ -105,13 +106,22 @@ static int Open( vlc_object_t *p_this )
if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
msg_Warn( p_filter, "Bad input or output format" );
return VLC_EGENERIC;
}
if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
{
b_fit = VLC_FALSE;
memcpy( &p_filter->output, &p_filter->input,
sizeof(audio_sample_format_t) );
msg_Warn( p_filter, "input and output formats are not similar" );
}
if ( ! b_fit )
{
return VLC_EGENERIC;
}
......
......@@ -366,9 +366,9 @@ static VLCExtended *_o_sharedInstance = nil;
/* en-/disable headphone virtualisation */
if ([o_ckb_hdphnVirt state] == NSOnState)
{
[self changeAFiltersString: "headphone" onOrOff: VLC_TRUE ];
[self changeAFiltersString: "headphone_channel_mixer" onOrOff: VLC_TRUE ];
}else{
[self changeAFiltersString: "headphone" onOrOff: VLC_FALSE ];
[self changeAFiltersString: "headphone_channel_mixer" onOrOff: VLC_FALSE ];
}
}
......
......@@ -819,7 +819,7 @@ void ExtraPanel::OnHeadphone( wxCommandEvent &event )
{
aout_instance_t *p_aout= (aout_instance_t *)vlc_object_find(p_intf,
VLC_OBJECT_AOUT, FIND_ANYWHERE);
ChangeFiltersString( p_intf , p_aout, "headphone",
ChangeFiltersString( p_intf , p_aout, "headphone_channel_mixer",
event.IsChecked() ? VLC_TRUE : VLC_FALSE );
if( p_aout != NULL )
vlc_object_release( p_aout );
......
......@@ -116,36 +116,49 @@ static int SplitConversion( const audio_sample_format_t * p_input_format,
return 2;
}
static void ReleaseFilter( aout_filter_t * p_filter )
{
module_Unneed( p_filter, p_filter->p_module );
vlc_object_detach( p_filter );
vlc_object_destroy( p_filter );
}
/*****************************************************************************
* aout_FiltersCreatePipeline: create a filters pipeline to transform a sample
* format to another
*****************************************************************************
* TODO : allow the user to add/remove specific filters
* pi_nb_filters must be initialized before calling this function
*****************************************************************************/
int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
aout_filter_t ** pp_filters,
aout_filter_t ** pp_filters_start,
int * pi_nb_filters,
const audio_sample_format_t * p_input_format,
const audio_sample_format_t * p_output_format )
{
aout_filter_t** pp_filters = pp_filters_start + *pi_nb_filters;
audio_sample_format_t temp_format;
int i_nb_conversions;
if ( AOUT_FMTS_IDENTICAL( p_input_format, p_output_format ) )
{
msg_Dbg( p_aout, "no need for any filter" );
*pi_nb_filters = 0;
return 0;
}
aout_FormatsPrint( p_aout, "filter(s)", p_input_format, p_output_format );
if( *pi_nb_filters + 1 > AOUT_MAX_FILTERS )
{
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
/* Try to find a filter to do the whole conversion. */
pp_filters[0] = FindFilter( p_aout, p_input_format, p_output_format );
if ( pp_filters[0] != NULL )
{
msg_Dbg( p_aout, "found a filter for the whole conversion" );
*pi_nb_filters = 1;
++*pi_nb_filters;
return 0;
}
......@@ -177,6 +190,12 @@ int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
/* We have the first stage of the conversion. Find a filter for
* the rest. */
if( *pi_nb_filters + 2 > AOUT_MAX_FILTERS )
{
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
p_output_format );
if ( pp_filters[1] == NULL )
......@@ -186,10 +205,16 @@ int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
p_output_format, &temp_format );
if ( !i_nb_conversions )
{
vlc_object_detach( pp_filters[0] );
vlc_object_destroy( pp_filters[0] );
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout,
"couldn't find a filter for the second part of the conversion" );
return -1;
}
if( *pi_nb_filters + 3 > AOUT_MAX_FILTERS )
{
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
&temp_format );
......@@ -198,31 +223,28 @@ int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
if ( pp_filters[1] == NULL || pp_filters[2] == NULL )
{
vlc_object_detach( pp_filters[0] );
vlc_object_destroy( pp_filters[0] );
ReleaseFilter( pp_filters[0] );
if ( pp_filters[1] != NULL )
{
vlc_object_detach( pp_filters[1] );
vlc_object_destroy( pp_filters[1] );
ReleaseFilter( pp_filters[1] );
}
if ( pp_filters[2] != NULL )
{
vlc_object_detach( pp_filters[2] );
vlc_object_destroy( pp_filters[2] );
ReleaseFilter( pp_filters[2] );
}
msg_Err( p_aout,
"couldn't find filters for the second part of the conversion" );
return -1;
}
*pi_nb_filters = 3;
*pi_nb_filters += 3;
msg_Dbg( p_aout, "found 3 filters for the whole conversion" );
}
else
{
*pi_nb_filters = 2;
*pi_nb_filters += 2;
msg_Dbg( p_aout, "found 2 filters for the whole conversion" );
}
/* We have enough filters. */
msg_Dbg( p_aout, "found %d filters for the whole conversion",
*pi_nb_filters );
return 0;
}
......
......@@ -37,66 +37,37 @@
#include "audio_output.h"
#include "aout_internal.h"
static void inputFailure( aout_instance_t *, aout_input_t *, char * );
static int VisualizationCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int EqualizerCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static aout_filter_t * allocateUserChannelMixer( aout_instance_t *,
audio_sample_format_t *,
audio_sample_format_t * );
/*****************************************************************************
* aout_InputNew : allocate a new input and rework the filter pipeline
*****************************************************************************/
int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
{
audio_sample_format_t user_filter_format;
audio_sample_format_t intermediate_format;/* input of resampler */
audio_sample_format_t chain_input_format;
audio_sample_format_t chain_output_format;
vlc_value_t val, text;
char * psz_filters, *psz_visual;
aout_filter_t * p_user_channel_mixer;
aout_FormatPrint( p_aout, "input", &p_input->input );
p_input->i_nb_filters = 0;
/* Prepare FIFO. */
aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
p_input->p_first_byte_to_mix = NULL;
/* Prepare format structure */
memcpy( &intermediate_format, &p_aout->mixer.mixer,
sizeof(audio_sample_format_t) );
intermediate_format.i_rate = p_input->input.i_rate;
/* Try to use the channel mixer chosen by the user */
memcpy ( &user_filter_format, &intermediate_format,
memcpy( &chain_input_format, &p_input->input,
sizeof(audio_sample_format_t) );
user_filter_format.i_physical_channels = p_input->input.i_physical_channels;
user_filter_format.i_original_channels = p_input->input.i_original_channels;
user_filter_format.i_bytes_per_frame = user_filter_format.i_bytes_per_frame
* aout_FormatNbChannels( &user_filter_format )
/ aout_FormatNbChannels( &intermediate_format );
p_user_channel_mixer = allocateUserChannelMixer( p_aout, &user_filter_format,
&intermediate_format );
/* If it failed, let the main pipeline do channel mixing */
if ( ! p_user_channel_mixer )
{
memcpy ( &user_filter_format, &intermediate_format,
memcpy( &chain_output_format, &p_aout->mixer.mixer,
sizeof(audio_sample_format_t) );
}
/* Create filters. */
if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
&p_input->i_nb_filters,
&p_input->input,
&user_filter_format
) < 0 )
{
msg_Err( p_aout, "couldn't set an input pipeline" );
aout_FifoDestroy( p_aout, &p_input->fifo );
p_input->b_error = 1;
return -1;
}
chain_output_format.i_rate = p_input->input.i_rate;
aout_FormatPrepare( &chain_output_format );
/* Now add user filters */
if( var_Type( p_aout, "visual" ) == 0 )
......@@ -198,13 +169,14 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
psz_filters = strdup( psz_visual );
}
/* parse user filter list */
if( psz_filters && *psz_filters )
{
char *psz_parser = psz_filters;
char *psz_next;
while( psz_parser && *psz_parser )
{
aout_filter_t * p_filter;
aout_filter_t * p_filter = NULL;
if( p_input->i_nb_filters >= AOUT_MAX_FILTERS )
{
......@@ -225,8 +197,6 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
break;
}
msg_Dbg( p_aout, "user filter \"%s\"", psz_parser );
/* Create a VLC object */
p_filter = vlc_object_create( p_aout, sizeof(aout_filter_t) );
if( p_filter == NULL )
......@@ -238,33 +208,80 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
}
vlc_object_attach( p_filter , p_aout );
memcpy( &p_filter->input, &user_filter_format,
/* request format */
memcpy( &p_filter->input, &chain_input_format,
sizeof(audio_sample_format_t) );
memcpy( &p_filter->output, &user_filter_format,
memcpy( &p_filter->output, &chain_output_format,
sizeof(audio_sample_format_t) );
p_filter->p_module =
module_Need( p_filter,"audio filter", psz_parser, VLC_FALSE );
/* try to find the requested filter */
p_filter->p_module = module_Need( p_filter, "audio filter",
psz_parser, VLC_TRUE );
if( p_filter->p_module== NULL )
if ( p_filter->p_module == NULL )
{
p_filter->p_module =
module_Need( p_filter,"visualization", psz_parser,
VLC_FALSE );
if( p_filter->p_module == NULL )
/* if the filter requested a special format, retry */
if ( !( AOUT_FMTS_IDENTICAL( &p_filter->input,
&chain_input_format )
&& AOUT_FMTS_IDENTICAL( &p_filter->output,
&chain_output_format ) ) )
{
aout_FormatPrepare( &p_filter->input );
aout_FormatPrepare( &p_filter->output );
p_filter->p_module = module_Need( p_filter, "audio filter",
psz_parser, VLC_TRUE );
}
/* try visual filters */
else
{
memcpy( &p_filter->input, &chain_output_format,
sizeof(audio_sample_format_t) );
memcpy( &p_filter->output, &chain_output_format,
sizeof(audio_sample_format_t) );
p_filter->p_module = module_Need( p_filter, "visualization",
psz_parser, VLC_TRUE );
}
}
/* failure */
if ( p_filter->p_module == NULL )
{
msg_Err( p_aout, "cannot add user filter %s (skipped)",
psz_parser );
vlc_object_detach( p_filter );
vlc_object_destroy( p_filter );
psz_parser = psz_next;
continue;
}
/* complete the filter chain if necessary */
if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->input ) )
{
if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
&p_input->i_nb_filters,
&chain_input_format,
&p_filter->input ) < 0 )
{
msg_Err( p_aout, "cannot add user filter %s (skipped)",
psz_parser );
module_Unneed( p_filter, p_filter->p_module );
vlc_object_detach( p_filter );
vlc_object_destroy( p_filter );
psz_parser = psz_next;
continue;
}
}
p_filter->b_continuity = VLC_FALSE;
/* success */
p_filter->b_continuity = VLC_FALSE;
p_input->pp_filters[p_input->i_nb_filters++] = p_filter;
memcpy( &chain_input_format, &p_filter->output,
sizeof( audio_sample_format_t ) );
/* next filter if any */
psz_parser = psz_next;
......@@ -273,60 +290,62 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
if( psz_filters ) free( psz_filters );
if( psz_visual ) free( psz_visual );
/* Attach the user channel mixer */
if ( p_user_channel_mixer )
/* complete the filter chain if necessary */
if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) )
{
p_input->pp_filters[p_input->i_nb_filters++] = p_user_channel_mixer;
if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
&p_input->i_nb_filters,
&chain_input_format,
&chain_output_format ) < 0 )
{
inputFailure( p_aout, p_input, "couldn't set an input pipeline" );
return -1;
}
}
/* Prepare hints for the buffer allocator. */
p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
p_input->input_alloc.i_bytes_per_sec = -1;
/* Create resamplers. */
if ( AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) )
{
p_input->i_nb_resamplers = 0;
}
else
{
/* Create resamplers. */
intermediate_format.i_rate = (__MAX(p_input->input.i_rate,
chain_output_format.i_rate = (__MAX(p_input->input.i_rate,
p_aout->mixer.mixer.i_rate)
* (100 + AOUT_MAX_RESAMPLING)) / 100;
if ( intermediate_format.i_rate == p_aout->mixer.mixer.i_rate )
if ( chain_output_format.i_rate == p_aout->mixer.mixer.i_rate )
{
/* Just in case... */
intermediate_format.i_rate++;
chain_output_format.i_rate++;
}
p_input->i_nb_resamplers = 0;
if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers,
&p_input->i_nb_resamplers,
&intermediate_format,
&chain_output_format,
&p_aout->mixer.mixer ) < 0 )
{
msg_Err( p_aout, "couldn't set a resampler pipeline" );
aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
p_input->i_nb_filters );
aout_FifoDestroy( p_aout, &p_input->fifo );
var_Destroy( p_aout, "visual" );
p_input->b_error = 1;
inputFailure( p_aout, p_input, "couldn't set a resampler pipeline");
return -1;
}
aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers,
p_input->i_nb_resamplers,
&p_input->input_alloc );
p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
/* Setup the initial rate of the resampler */
p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
}
p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
p_input->i_nb_filters,
&p_input->input_alloc );
p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
/* i_bytes_per_sec is still == -1 if no filters */
p_input->input_alloc.i_bytes_per_sec = __MAX(
......@@ -334,9 +353,8 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
(int)(p_input->input.i_bytes_per_frame
* p_input->input.i_rate
/ p_input->input.i_frame_length) );
/* Allocate in the heap, it is more convenient for the decoder. */
p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
/* Success */
p_input->b_error = VLC_FALSE;
p_input->b_restart = VLC_FALSE;
......@@ -354,8 +372,10 @@ int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
p_input->i_nb_filters );
p_input->i_nb_filters = 0;
aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
p_input->i_nb_resamplers );
p_input->i_nb_resamplers = 0;
aout_FifoDestroy( p_aout, &p_input->fifo );
return 0;
......@@ -572,13 +592,38 @@ int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
return 0;
}
static int ChangeFiltersString( aout_instance_t * p_aout,
/*****************************************************************************
* static functions
*****************************************************************************/
static void inputFailure( aout_instance_t * p_aout, aout_input_t * p_input,
char * psz_error_message )
{
/* error message */
msg_Err( p_aout, "couldn't set an input pipeline" );
/* clean up */
aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
p_input->i_nb_filters );
aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
p_input->i_nb_resamplers );
aout_FifoDestroy( p_aout, &p_input->fifo );
var_Destroy( p_aout, "visual" );
var_Destroy( p_aout, "equalizer" );
var_Destroy( p_aout, "audio-filter" );
var_Destroy( p_aout, "audio-visual" );
/* error flag */
p_input->b_error = 1;
}
static int ChangeFiltersString( aout_instance_t * p_aout, char* psz_variable,
char *psz_name, vlc_bool_t b_add )
{
vlc_value_t val;
char *psz_parser;
var_Get( p_aout, "audio-filter", &val );
var_Get( p_aout, psz_variable, &val );
if( !val.psz_string ) val.psz_string = strdup("");
......@@ -613,7 +658,7 @@ static int ChangeFiltersString( aout_instance_t * p_aout,
}
}
var_Set( p_aout, "audio-filter", val );
var_Set( p_aout, psz_variable, val );
free( val.psz_string );
return 1;
}
......@@ -628,23 +673,23 @@ static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
if( !psz_mode || !*psz_mode )
{
ChangeFiltersString( p_aout, "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE );
}
else
{
if( !strcmp( "goom", psz_mode ) )
{
ChangeFiltersString( p_aout, "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "goom", VLC_TRUE );
ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_TRUE );
ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
}
else if( !strcmp( "galaktos", psz_mode ) )
{
ChangeFiltersString( p_aout, "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "galaktos", VLC_TRUE );
ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_TRUE );
}
else
{
......@@ -652,9 +697,9 @@ static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
var_Create( p_aout, "effect-list", VLC_VAR_STRING );
var_Set( p_aout, "effect-list", val );
ChangeFiltersString( p_aout, "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "visual", VLC_TRUE );
ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_TRUE );
ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
}
}
......@@ -678,14 +723,16 @@ static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
if( !psz_mode || !*psz_mode )
{
i_ret = ChangeFiltersString( p_aout, "equalizer", VLC_FALSE );
i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
VLC_FALSE );
}
else
{
val.psz_string = psz_mode;
var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING );
var_Set( p_aout, "equalizer-preset", val );
i_ret = ChangeFiltersString( p_aout, "equalizer", VLC_TRUE );
i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
VLC_TRUE );
}
......@@ -700,48 +747,3 @@ static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
return VLC_SUCCESS;
}
static aout_filter_t * allocateUserChannelMixer( aout_instance_t * p_aout,
audio_sample_format_t * p_input_format,
audio_sample_format_t * p_output_format )
{
aout_filter_t * p_channel_mixer;
/* Retreive user preferred channel mixer */
char * psz_name = config_GetPsz( p_aout, "audio-channel-mixer" );
/* Not specified => let the main pipeline do the mixing */
if ( ! psz_name ) return NULL;
/* Debug information */
aout_FormatsPrint( p_aout, "channel mixer", p_input_format,
p_output_format );
/* Create a VLC object */
p_channel_mixer = vlc_object_create( p_aout, sizeof(aout_filter_t) );
if( p_channel_mixer == NULL )
{
msg_Err( p_aout, "cannot add user channel mixer %s", psz_name );
return NULL;
}
vlc_object_attach( p_channel_mixer , p_aout );
/* Attach the suitable module */
memcpy( &p_channel_mixer->input, p_input_format,
sizeof(audio_sample_format_t) );
memcpy( &p_channel_mixer->output, p_output_format,
sizeof(audio_sample_format_t) );
p_channel_mixer->p_module =
module_Need( p_channel_mixer,"audio filter", psz_name, VLC_TRUE );
if( p_channel_mixer->p_module== NULL )
{
msg_Err( p_aout, "cannot add user channel mixer %s", psz_name );
vlc_object_detach( p_channel_mixer );
vlc_object_destroy( p_channel_mixer );
return NULL;
}
p_channel_mixer->b_continuity = VLC_FALSE;
/* Ok */
return p_channel_mixer;
}
......@@ -189,6 +189,7 @@ int aout_OutputNew( aout_instance_t * p_aout,
aout_FormatPrint( p_aout, "mixer", &p_aout->output.output );
/* Create filters. */
p_aout->output.i_nb_filters = 0;
if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters,
&p_aout->output.i_nb_filters,
&p_aout->mixer.mixer,
......
......@@ -853,10 +853,30 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
if( p_sys->p_aout_input == NULL )
{
audio_sample_format_t format;
int i_force_dolby = config_GetInt( p_dec, "force-dolby-surround" );
p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec;
p_sys->audio = p_dec->fmt_out.audio;
memcpy( &format, &p_sys->audio, sizeof( audio_sample_format_t ) );
if ( i_force_dolby && (format.i_original_channels&AOUT_CHAN_PHYSMASK)
== (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) )
{
if ( i_force_dolby > 0 )
{
format.i_original_channels = format.i_original_channels |
AOUT_CHAN_DOLBYSTEREO;
}
else
{
format.i_original_channels = format.i_original_channels &
~AOUT_CHAN_DOLBYSTEREO;
}
}
p_sys->p_aout_input =
aout_DecNew( p_dec, &p_sys->p_aout, &p_sys->audio );
aout_DecNew( p_dec, &p_sys->p_aout, &format );
if( p_sys->p_aout_input == NULL )
{
msg_Err( p_dec, "failed to create audio output" );
......
......@@ -159,6 +159,14 @@ static char *ppsz_snap_formats[] =
"This option allows you to use the S/PDIF audio output by default when " \
"your hardware supports it as well as the audio stream being played.")
#define FORCE_DOLBY_TEXT N_("Force detection of Dolby Surround")
#define FORCE_DOLBY_LONGTEXT N_( \
"Use this when you know your stream is or is not encoded with Dolby Surround " \
"but fails to be detected as such." )
static int pi_force_dolby_values[] = { 0, 1, -1 };
static char *ppsz_force_dolby_descriptions[] = { N_("Auto"), N_("On"), N_("Off") };
#define AUDIO_FILTER_TEXT N_("Audio filters")
#define AUDIO_FILTER_LONGTEXT N_( \
"This allows you to add audio post processing filters, to modify " \
......@@ -169,12 +177,6 @@ static char *ppsz_snap_formats[] =
"This allows you to add visualization modules " \
"(spectrum analyzer, etc.).")
#define AUDIO_CHANNEL_MIXER N_("Channel mixer")
#define AUDIO_CHANNEL_MIXER_LONGTEXT N_( \
"This allows you to choose a specific audio channel mixer. For " \
"instance, you can use the \"headphone\" mixer that gives 5.1 feeling " \
"with a headphone.")
#define VOUT_CAT_LONGTEXT N_( \
"These options allow you to modify the behavior of the video output " \
"subsystem. You can for example enable video filters (deinterlacing, " \
......@@ -960,6 +962,9 @@ vlc_module_begin();
AOUT_RESAMP_LONGTEXT, VLC_TRUE );
#endif
add_bool( "spdif", 0, NULL, SPDIF_TEXT, SPDIF_LONGTEXT, VLC_FALSE );
add_integer( "force-dolby-surround", 0, NULL, FORCE_DOLBY_TEXT,
FORCE_DOLBY_LONGTEXT, VLC_FALSE );
change_integer_list( pi_force_dolby_values, ppsz_force_dolby_descriptions, 0 );
add_integer( "audio-desync", 0, NULL, DESYNC_TEXT,
DESYNC_LONGTEXT, VLC_TRUE );
set_subcategory( SUBCAT_AUDIO_AOUT );
......@@ -1239,8 +1244,6 @@ vlc_module_begin();
add_category_hint( N_("Miscellaneous"), MISC_CAT_LONGTEXT, VLC_TRUE );
add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT,
MEMCPY_LONGTEXT, VLC_TRUE );
add_module( "audio-channel-mixer", "audio mixer", NULL, NULL,
AUDIO_CHANNEL_MIXER, AUDIO_CHANNEL_MIXER_LONGTEXT, VLC_TRUE );
change_short('A');
set_section( N_("Plugins" ), NULL );
......
......@@ -808,6 +808,7 @@ rm -fr %buildroot
%dir %_libdir/vlc/audio_filter
%_libdir/vlc/audio_filter/libbandlimited_resampler_plugin.so
%_libdir/vlc/audio_filter/libdolby_surround_decoder_plugin.so
%_libdir/vlc/audio_filter/libdtstospdif_plugin.so
%_libdir/vlc/audio_filter/libfixed32tofloat32_plugin.so
%_libdir/vlc/audio_filter/libfixed32tos16_plugin.so
......
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