#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; }