view libaf/af.c @ 7568:d08513b9fed6

Adding new audio output filter layer libaf
author anders
date Tue, 01 Oct 2002 06:45:08 +0000
parents
children 8819fdf88b5d
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#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;
}