From 1f6c494641c4ca99eec7a9f47818526c72789439 Mon Sep 17 00:00:00 2001 From: anders Date: Tue, 1 Oct 2002 06:45:08 +0000 Subject: Adding new audio output filter layer libaf git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@7569 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libaf/af.c | 440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 libaf/af.c (limited to 'libaf/af.c') diff --git a/libaf/af.c b/libaf/af.c new file mode 100644 index 0000000000..39b34d8ab5 --- /dev/null +++ b/libaf/af.c @@ -0,0 +1,440 @@ +#include +#include +#include + +#ifdef HAVE_MALLOC_H +#include +#endif + +#include "../config.h" +#include "../mp_msg.h" + +#include "af.h" + +// Static list of filters +extern af_info_t af_info_dummy; +extern af_info_t af_info_delay; +extern af_info_t af_info_channels; +extern af_info_t af_info_format; +extern af_info_t af_info_resample; + +static af_info_t* filter_list[]={ \ + &af_info_dummy,\ + &af_info_delay,\ + &af_info_channels,\ + &af_info_format,\ + &af_info_resample,\ + NULL \ +}; + +// Command line config switches +af_cfg_t af_cfg={\ + 0,\ + 0,\ + 0,\ + 0,\ + NULL,\ +}; + + +// Initialization types +#define SLOW 1 +#define FAST 2 +#define FORCE 3 + +// The first and last filter in the list +static af_instance_t* first=NULL; +static af_instance_t* last=NULL; +// Storage for input and output data formats (set by init) +static af_data_t input; +static af_data_t output; + +/* Find a filter in the static list of filters using it's name. This + function is used internally */ +af_info_t* af_find(char*name) +{ + int i=0; + while(filter_list[i]){ + if(!strcmp(filter_list[i]->name,name)) + return filter_list[i]; + i++; + } + mp_msg(MSGT_AFILTER,MSGL_ERR,"Couldn't find audio filter '%s'\n",name); + return NULL; +} + +// Function for creating a new filter of type name +af_instance_t* af_create(char* name) +{ + // Allocate space for the new filter and reset all pointers + af_instance_t* new=malloc(sizeof(af_instance_t)); + if(!new){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"Could not allocate memory\n"); + return NULL; + } + memset(new,0,sizeof(af_instance_t)); + + // Find filter from name + new->info=af_find(name); + + // Initialize the new filter + if(new->info && (AF_OK==new->info->open(new))) + return new; + + free(new); + mp_msg(MSGT_AFILTER,MSGL_ERR,"Couldn't create audio filter '%s'\n",name); + return NULL; +} + +/* Create and insert a new filter of type name before the filter in the + argument. This function can be called during runtime, the return + value is the new filter */ +af_instance_t* af_prepend(af_instance_t* af, char* name) +{ + // Create the new filter and make sure it is ok + af_instance_t* new=af_create(name); + if(!new) + return NULL; + // Update pointers + new->next=af; + if(af){ + new->prev=af->prev; + af->prev=new; + } + else + last=new; + if(new->prev) + new->prev->next=new; + else + first=new; + return new; +} + +/* Create and insert a new filter of type name after the filter in the + argument. This function can be called during runtime, the return + value is the new filter */ +af_instance_t* af_append(af_instance_t* af, char* name) +{ + // Create the new filter and make sure it is OK + af_instance_t* new=af_create(name); + if(!new) + return NULL; + // Update pointers + new->prev=af; + if(af){ + new->next=af->next; + af->next=new; + } + else + first=new; + if(new->next) + new->next->prev=new; + else + last=new; + return new; +} + +// Uninit and remove the filter "af" +void af_remove(af_instance_t* af) +{ + if(!af) return; + + // Detach pointers + if(af->prev) + af->prev->next=af->next; + else + first=af->next; + if(af->next) + af->next->prev=af->prev; + else + last=af->prev; + + // Uninitialize af and free memory + af->uninit(af); + free(af); +} + +/* Reinitializes all filters downstream from the filter given in the argument */ +int af_reinit(af_instance_t* af) +{ + if(!af) + return AF_ERROR; + + do{ + af_data_t in; // Format of the input to current filter + int rv=0; // Return value + + // Check if this is the first filter + if(!af->prev) + memcpy(&in,&input,sizeof(af_data_t)); + else + memcpy(&in,af->prev->data,sizeof(af_data_t)); + // Reset just in case... + in.audio=NULL; + in.len=0; + + rv = af->control(af,AF_CONTROL_REINIT,&in); + switch(rv){ + case AF_OK: + break; + case AF_FALSE:{ // Configuration filter is needed + af_instance_t* new = NULL; + // Insert channels filter + if((af->prev?af->prev->data->nch:input.nch) != in.nch){ + // Create channels filter + if(NULL == (new = af_prepend(af,"channels"))) + return AF_ERROR; + // Set number of output channels + if(AF_OK != (rv = new->control(new,AF_CONTROL_CHANNELS,&in.nch))) + return rv; + // Initialize channels filter + if(!new->prev) + memcpy(&in,&input,sizeof(af_data_t)); + else + memcpy(&in,new->prev->data,sizeof(af_data_t)); + if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) + return rv; + } + // Insert format filter + if(((af->prev?af->prev->data->format:input.format) != in.format) || + ((af->prev?af->prev->data->bps:input.bps) != in.bps)){ + // Create format filter + if(NULL == (new = af_prepend(af,"format"))) + return AF_ERROR; + // Set output format + if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT,&in))) + return rv; + // Initialize format filter + if(!new->prev) + memcpy(&in,&input,sizeof(af_data_t)); + else + memcpy(&in,new->prev->data,sizeof(af_data_t)); + if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) + return rv; + } + if(!new) // Should _never_ happen + return AF_ERROR; + af=new; + break; + } + case AF_DETACH:{ // Filter is redundant and wants to be unloaded + af_instance_t* aft=af->prev; + af_remove(af); + if(aft) + af=aft; + else + af=first; // Restart configuration + break; + } + default: + mp_msg(MSGT_AFILTER,MSGL_ERR,"Reinit did not work, audio filter '%s' returned error code %i\n",af->info->name,rv); + return AF_ERROR; + } + af=af->next; + }while(af); + return AF_OK; +} + +/* Find filter in the dynamic filter list using it's name This + function is used for finding already initialized filters */ +af_instance_t* af_get(char* name) +{ + af_instance_t* af=first; + while(af->next != NULL){ + if(!strcmp(af->info->name,name)) + return af; + af=af->next; + } + return NULL; +} + +// Uninit and remove all filters +void af_uninit() +{ + while(first) + af_remove(first); +} + +/* Init read configuration and create filter list accordingly. In and + out contains the format of the current movie and the formate of the + preferred output respectively */ +int af_init(af_data_t* in, af_data_t* out) +{ + int cfg=SLOW; // configuration type + int i=0; + + // Precaution in case caller is misbehaving + in->audio = out->audio = NULL; + in->len = out->len = 0; + + // Figure out how fast the machine is + if(af_cfg.force) + cfg=af_cfg.force; + else{ +# if defined(HAVE_SSE) || defined(HAVE_3DNOWEX) + cfg=FAST; +# else + cfg=SLOW; +# endif + } + + // Input and output configuration + memcpy(&input,in,sizeof(af_data_t)); + memcpy(&output,out,sizeof(af_data_t)); + + // Check if this is the first call + if(!first){ + // Add all filters in the list (if there are any) + if(!af_cfg.list){ + if(!af_append(first,"dummy")) // To make automatic format conversion work + return -1; + } + else{ + while(af_cfg.list[i]){ + if(!af_append(last,af_cfg.list[i++])) + return -1; + } + } + } + + // Init filters + if(AF_OK != af_reinit(first)) + return -1; + + // Check output format + if(cfg!=FORCE){ + af_instance_t* af = NULL; // New filter + // Check output frequency if not OK fix with resample + if(last->data->rate!=output.rate){ + if(NULL==(af=af_get("resample"))){ + if(cfg==SLOW){ + if(!strcmp(first->info->name,"format")) + af = af_append(first,"resample"); + else + af = af_prepend(first,"resample"); + } + else{ + if(!strcmp(last->info->name,"format")) + af = af_prepend(last,"resample"); + else + af = af_append(last,"resample"); + } + } + // Init the new filter + if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE,&output.rate))) + return -1; + if(AF_OK != af_reinit(af)) + return -1; + } + + // Check number of output channels fix if not OK + // If needed always inserted last -> easy to screw up other filters + if(last->data->nch!=output.nch){ + if(!strcmp(last->info->name,"format")) + af = af_prepend(last,"channels"); + else + af = af_append(last,"channels"); + // Init the new filter + if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&output.nch))) + return -1; + if(AF_OK != af_reinit(af)) + return -1; + } + + // Check output format fix if not OK + if((last->data->format != output.format) || (last->data->bps != output.bps)){ + if(strcmp(last->info->name,"format")) + af = af_append(last,"format"); + else + af = last; + // Init the new filter + if(!af ||(AF_OK != af->control(af,AF_CONTROL_FORMAT,&output))) + return -1; + if(AF_OK != af_reinit(af)) + return -1; + } + + // Re init again just in case + if(AF_OK != af_reinit(first)) + return -1; + + if((last->data->format != output.format) || (last->data->bps != output.bps) || + (last->data->nch!=output.nch) || (last->data->rate!=output.rate)){ + // Something is stuffed audio out will not work + mp_msg(MSGT_AFILTER,MSGL_ERR,"Unable to setup filter system can not meet sound-card demands, please report this error on MPlayer development mailing list. \n"); + af_uninit(); + return -1; + } + } + return 0; +} + +// Filter data chunk through the filters in the list +af_data_t* af_play(af_data_t* data) +{ + af_instance_t* af=first; + // Iterate through all filters + do{ + data=af->play(af,data); + af=af->next; + }while(af); + return data; +} + +/* Helper function used to calculate the exact buffer length needed + when buffers are resized */ +inline int af_lencalc(frac_t mul, int len){ + register int q = len*mul.n; + return q/mul.d + q%mul.d; +} + +/* Calculate how long the output from the filters will be given the + input length "len" */ +int af_outputlen(int len) +{ + af_instance_t* af=first; + frac_t mul = {1,1}; + // Iterate through all filters + do{ + mul.n *= af->mul.n; + mul.d *= af->mul.d; + af=af->next; + }while(af); + return af_lencalc(mul,len); +} + +/* Calculate how long the input to the filters should be to produce a + certain output length, i.e. the return value of this function is + the input length required to produce the output length "len". */ +int af_inputlen(int len) +{ + af_instance_t* af=first; + frac_t mul = {1,1}; + // Iterate through all filters + do{ + mul.d *= af->mul.n; + mul.n *= af->mul.d; + af=af->next; + }while(af); + return af_lencalc(mul,len); +} + +/* Helper function called by the macro with the same name this + function should not be called directly */ +inline int af_resize_local_buffer(af_instance_t* af, af_data_t* data) +{ + // Calculate new length + register int len = af_lencalc(af->mul,data->len); + mp_msg(MSGT_AFILTER,MSGL_V,"Reallocating memory in module %s, old len = %i, new len = %i\n",af->info->name,af->data->len,len); + // If there is a buffer free it + if(af->data->audio) + free(af->data->audio); + // Create new buffer and check that it is OK + af->data->audio = malloc(len); + if(!af->data->audio){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"Could not allocate memory \n"); + return AF_ERROR; + } + af->data->len=len; + return AF_OK; +} -- cgit v1.2.3