Mercurial > mplayer.hg
changeset 7568:d08513b9fed6
Adding new audio output filter layer libaf
author | anders |
---|---|
date | Tue, 01 Oct 2002 06:45:08 +0000 |
parents | 85e9956a6727 |
children | 5e956ff37e91 |
files | Makefile libaf/Makefile libaf/af.c libaf/af.h libaf/af_channels.c libaf/af_delay.c libaf/af_dummy.c libaf/af_format.c libaf/af_resample.c libaf/dsp.h libaf/filter.c libaf/filter.h libaf/window.c libaf/window.h mp_msg.h |
diffstat | 15 files changed, 2234 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon Sep 30 21:46:54 2002 +0000 +++ b/Makefile Tue Oct 01 06:45:08 2002 +0000 @@ -45,13 +45,13 @@ A_LIBS = $(ALSA_LIB) $(ARTS_LIB) $(NAS_LIB) $(MAD_LIB) $(VORBIS_LIB) $(FAAD_LIB) $(SGIAUDIO_LIB) CODEC_LIBS = libmpcodecs/libmpcodecs.a mp3lib/libMP3.a liba52/liba52.a libmpeg2/libmpeg2.a $(AV_LIB) $(FAME_LIB) -COMMON_LIBS = $(CODEC_LIBS) libmpdemux/libmpdemux.a input/libinput.a postproc/libpostproc.a linux/libosdep.a $(LIB_LOADER) $(FREETYPE_LIB) $(A_LIBS) $(CSS_LIB) $(XVID_LIB) $(DECORE_LIB) $(TERMCAP_LIB) $(STREAMING_LIB) $(Z_LIB) $(GTK_LIBS) $(PNG_LIB) $(JPEG_LIB) $(GIF_LIB) $(CDPARANOIA_LIB) $(ARCH_LIB) -lm +COMMON_LIBS = $(CODEC_LIBS) libmpdemux/libmpdemux.a input/libinput.a postproc/libpostproc.a linux/libosdep.a $(LIB_LOADER) $(FREETYPE_LIB) $(A_LIBS) $(CSS_LIB) $(XVID_LIB) $(DECORE_LIB) $(TERMCAP_LIB) $(STREAMING_LIB) $(Z_LIB) $(GTK_LIBS) $(PNG_LIB) $(JPEG_LIB) $(GIF_LIB) $(CDPARANOIA_LIB) $(ARCH_LIB) -lm -Llibaf -laf ifeq ($(VIDIX),yes) MISC_LIBS += -Llibdha -ldha vidix/libvidix.a endif CFLAGS = $(OPTFLAGS) -Ilibmpdemux -Iloader $(VO_INC) $(EXTRA_INC) $(CDPARANOIA_INC) $(FREETYPE_INC) $(SDL_INC) # -Wall -PARTS = libmpdemux libmpcodecs mp3lib liba52 libmpeg2 libavcodec libao2 drivers linux postproc input libvo +PARTS = libmpdemux libmpcodecs mp3lib liba52 libmpeg2 libavcodec libao2 drivers linux postproc input libvo libaf ifeq ($(VIDIX),yes) PARTS += libdha vidix endif @@ -84,7 +84,7 @@ ALL_PRG += $(PRG_FIBMAP) endif -COMMON_DEPS = $(LOADER_DEP) $(MP1E_DEP) $(AV_DEP) libmpdemux/libmpdemux.a libmpcodecs/libmpcodecs.a libao2/libao2.a liba52/liba52.a mp3lib/libMP3.a libmpeg2/libmpeg2.a linux/libosdep.a postproc/libpostproc.a input/libinput.a libvo/libvo.a +COMMON_DEPS = $(LOADER_DEP) $(MP1E_DEP) $(AV_DEP) libmpdemux/libmpdemux.a libmpcodecs/libmpcodecs.a libao2/libao2.a liba52/liba52.a mp3lib/libMP3.a libmpeg2/libmpeg2.a linux/libosdep.a postproc/libpostproc.a input/libinput.a libvo/libvo.a libaf/libaf.a ifeq ($(VIDIX),yes) COMMON_DEPS += libdha/libdha.so vidix/libvidix.a @@ -115,6 +115,9 @@ .c.o: $(CC) -c $(CFLAGS) -o $@ $< +libaf/libaf.a: + $(MAKE) -C libaf + libmpdvdkit2/libmpdvdkit.a: $(MAKE) -C libmpdvdkit2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/Makefile Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,38 @@ +include ../config.mak + +LIBNAME = libaf.a + +SRCS=af.c af_dummy.c af_delay.c af_channels.c af_format.c af_resample.c window.c filter.c + +OBJS=$(SRCS:.c=.o) + +CFLAGS = $(OPTFLAGS) -I. -Wall +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +$(LIBNAME): $(OBJS) Makefile + $(AR) r $(LIBNAME) $(OBJS) + +$(OBJS):af.h dsp.h filter.h window.h + +all: $(LIBNAME) + +clean: + rm -f *.o *.a *~ + +distclean: + rm -f *.o *.a *~ .depend + +dep: depend + +depend: + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +# +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,440 @@ +#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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af.h Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,160 @@ +#ifndef __aop_h__ +#define __aop_h__ + +struct af_instance_s; + +// Audio data chunk +typedef struct af_data_s +{ + void* audio; // data buffer + int len; // buffer length + int rate; // sample rate + int nch; // number of channels + int format; // format + int bps; // bytes per sample +} af_data_t; + +// Fraction, used to calculate buffer lengths +typedef struct frac_s +{ + int n; // Numerator + int d; // Denominator +} frac_t; + +/* Audio filter information not specific for current instance, but for + a specific filter */ +typedef struct af_info_s +{ + const char *info; + const char *name; + const char *author; + const char *comment; + int (*open)(struct af_instance_s* vf); +} af_info_t; + +// Linked list of audio filters +typedef struct af_instance_s +{ + af_info_t* info; + int (*control)(struct af_instance_s* af, int cmd, void* arg); + void (*uninit)(struct af_instance_s* af); + af_data_t* (*play)(struct af_instance_s* af, af_data_t* data); + void* setup; // setup data for this specific instance and filter + af_data_t* data; // configuration for outgoing data stream + struct af_instance_s* next; + struct af_instance_s* prev; + frac_t mul; /* length multiplier: how much does this instance change + the length of the buffer. */ +}af_instance_t; + +/********************************************* +// Control parameters +*/ + +/* The control system is divided into 3 levels + mandatory calls - all filters must answer to all of these + optional calls - are optional + filter specific calls - applies only to some filters +*/ + +#define AF_CONTROL_MANDATORY_BASE 0 +#define AF_CONTROL_OPTIONAL_BASE 100 +#define AF_CONTROL_FILTER_SPECIFIC_BASE 200 + +// MANDATORY CALLS + +/* Reinitialize filter. The optional argument contains the new + configuration in form of a af_data_t struct. If the filter does not + support the new format the struct should be changed and AF_FALSE + should be returned. If the incoming and outgoing data streams are + identical the filter can return AF_DETACH. This will remove the + filter. */ +#define AF_CONTROL_REINIT 1 + AF_CONTROL_MANDATORY_BASE + +// OPTIONAL CALLS + + +// FILTER SPECIFIC CALLS + +// Set output rate in resample +#define AF_CONTROL_RESAMPLE 1 + AF_CONTROL_FILTER_SPECIFIC_BASE +// Set output format in format +#define AF_CONTROL_FORMAT 2 + AF_CONTROL_FILTER_SPECIFIC_BASE +// Set number of output channels in channels +#define AF_CONTROL_CHANNELS 3 + AF_CONTROL_FILTER_SPECIFIC_BASE +// Set delay length in delay +#define AF_CONTROL_SET_DELAY_LEN 4 + AF_CONTROL_FILTER_SPECIFIC_BASE +/********************************************* +// Return values +*/ + +#define AF_DETACH 2 +#define AF_OK 1 +#define AF_TRUE 1 +#define AF_FALSE 0 +#define AF_UNKNOWN -1 +#define AF_ERROR -2 +#define AF_NA -3 + + +/********************************************* +// Command line configuration switches +*/ +typedef struct af_cfg_s{ + int rate; + int format; + int bps; + int force; + char** list; +}af_cfg_t; + + +// Export functions + +/* Init read configuration and create filter list accordingly. In and + out contains the format of the current movie and the formate of the + prefered output respectively */ +int af_init(af_data_t* in, af_data_t* out); +// Uninit and remove all filters +void af_uninit(); +// Filter data chunk through the filters in the list +af_data_t* af_play(af_data_t* data); +/* Calculate how long the output from the filters will be given the + input length "len" */ +int af_outputlen(int 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); + + + +// Helper functions and macros used inside the audio filters + +/* Helper function called by the macro with the same name only to be + called from inside filters */ +int af_resize_local_buffer(af_instance_t* af, af_data_t* data); + +/* Helper function used to calculate the exact buffer length needed + when buffers are resized */ +int af_lencalc(frac_t mul, int len); + +/* Memory reallocation macro: if a local buffer is used (i.e. if the + filter doesn't operate on the incoming buffer this macro must be + called to ensure the buffer is big enough. */ +#define RESIZE_LOCAL_BUFFER(a,d)\ +((af->data->len < af_lencalc(af->mul,data->len))?af_resize_local_buffer(af,data):AF_OK) + +#ifndef min +#define min(a,b)(((a)>(b))?(b):(a)) +#endif + +#ifndef max +#define max(a,b)(((a)>(b))?(a):(b)) +#endif + +#ifndef AUDIO_FILTER +extern af_cfg_t af_cfg; +#endif /* AUDIO_FILTER */ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_channels.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,171 @@ +/* Audio filter that adds and removes channels, according to the + command line parameter channels. It is stupid and can only add + silence or copy channels not mix or filter. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../config.h" +#include "../mp_msg.h" + +#include "af.h" + +// Local function for copying data +void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps) +{ + switch(bps){ + case 1:{ + int8_t* tin = (int8_t*)in; + int8_t* tout = (int8_t*)out; + tin += inos; + tout += outos; + len = len/ins; + while(len--){ + *tout=*tin; + tin +=ins; + tout+=outs; + } + break; + } + case 2:{ + int16_t* tin = (int16_t*)in; + int16_t* tout = (int16_t*)out; + tin += inos; + tout += outos; + len = len/(2*ins); + while(len--){ + *tout=*tin; + tin +=ins; + tout+=outs; + } + break; + } + case 4:{ + int32_t* tin = (int32_t*)in; + int32_t* tout = (int32_t*)out; + tin += inos; + tout += outos; + len = len/(4*ins); + while(len--){ + *tout=*tin; + tin +=ins; + tout+=outs; + } + break; + } + case 8:{ + int64_t* tin = (int64_t*)in; + int64_t* tout = (int64_t*)out; + tin += inos; + tout += outos; + len = len/(8*ins); + while(len--){ + *tout=*tin; + tin +=ins; + tout+=outs; + } + break; + } + default: + mp_msg(MSGT_AFILTER,MSGL_ERR,"[channels] Unsupported number of bytes/sample: %i please report this error on the MPlayer mailing list. \n",bps); + } +} + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT: + // Make sure this filter isn't redundant + if(af->data->nch == ((af_data_t*)arg)->nch) + return AF_DETACH; + + af->data->rate = ((af_data_t*)arg)->rate; + af->data->format = ((af_data_t*)arg)->format; + af->data->bps = ((af_data_t*)arg)->bps; + af->mul.n = af->data->nch; + af->mul.d = ((af_data_t*)arg)->nch; + return AF_OK; + case AF_CONTROL_CHANNELS: + // Reinit must be called after this function has been called + + // Sanity check + if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > 6){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"[channels] The number of output channels must be between 1 and 6. Current value is%i \n",((int*)arg)[0]); + return AF_ERROR; + } + + af->data->nch=((int*)arg)[0]; + mp_msg(MSGT_AFILTER,MSGL_V,"[channels] Changing number of channels to %i\n",af->data->nch); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance_s* af) +{ + if(af->data) + free(af->data); +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + af_data_t* c = data; // Current working data + af_data_t* l = af->data; // Local data + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + // Reset unused channels if nch in < nch out + if(af->mul.n > af->mul.d) + memset(l->audio,0,af_lencalc(af->mul, c->len)); + + // Special case always output L & R + if(c->nch == 1){ + copy(c->audio,l->audio,1,0,l->nch,0,c->len,c->bps); + copy(c->audio,l->audio,1,0,l->nch,1,c->len,c->bps); + } + else{ + int i; + if(l->nch < c->nch){ + for(i=0;i<l->nch;i++) // Truncates R if l->nch == 1 not good need mixing + copy(c->audio,l->audio,c->nch,i,l->nch,i,c->len,c->bps); + } + else{ + for(i=0;i<c->nch;i++) + copy(c->audio,l->audio,c->nch,i,l->nch,i,c->len,c->bps); + } + } + + // Set output data + c->audio = l->audio; + c->len = af_lencalc(af->mul, c->len); + c->nch = l->nch; + + return c; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul.n=1; + af->mul.d=1; + af->data=calloc(1,sizeof(af_data_t)); + if(af->data == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +af_info_t af_info_channels = { + "Insert or remove channels", + "channels", + "Anders", + "", + open +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_delay.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,146 @@ +/* This audio filter doesn't really do anything useful but serves an + example of how audio filters work. It delays the output signal by + the number of seconds set by delay=n where n is the number of + seconds. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../config.h" +#include "../mp_msg.h" + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_delay_s +{ + void* buf; // data block used for delaying audio signal + int len; // local buffer length + float tlen; // Delay in seconds +}af_delay_t; + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT:{ + af->data->rate = ((af_data_t*)arg)->rate; + af->data->nch = ((af_data_t*)arg)->nch; + af->data->format = ((af_data_t*)arg)->format; + af->data->bps = ((af_data_t*)arg)->bps; + + return af->control(af,AF_CONTROL_SET_DELAY_LEN,&((af_delay_t*)af->setup)->tlen); + } + case AF_CONTROL_SET_DELAY_LEN:{ + af_delay_t* s = (af_delay_t*)af->setup; + void* bt = s->buf; // Old buffer + int lt = s->len; // Old len + + if(*((float*)arg) > 30 || *((float*)arg) < 0){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"Error setting delay length in af_delay. Delay must be between 0s and 30s\n"); + s->len=0; + s->tlen=0.0; + return AF_ERROR; + } + + // Set new len and allocate new buffer + s->tlen = *((float*)arg); + s->len = af->data->rate*af->data->bps*af->data->nch*(int)s->tlen; + s->buf = malloc(s->len); + mp_msg(MSGT_AFILTER,MSGL_DBG2,"[delay] Delaying audio output by %0.2fs\n",s->tlen); + mp_msg(MSGT_AFILTER,MSGL_DBG3,"[delay] Delaying audio output by %i bytes\n",s->len); + + // Out of memory error + if(!s->buf){ + s->len = 0; + free(bt); + return AF_ERROR; + } + + // Clear the new buffer + memset(s->buf, 0, s->len); + + /* Copy old buffer to avoid click in output + sound (at least most of it) and release it */ + if(bt){ + memcpy(s->buf,bt,min(lt,s->len)); + free(bt); + } + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance_s* af) +{ + if(af->data->audio) + free(af->data->audio); + if(af->data) + free(af->data); + if(((af_delay_t*)(af->setup))->buf) + free(((af_delay_t*)(af->setup))->buf); + if(af->setup) + free(af->setup); +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + af_data_t* c = data; // Current working data + af_data_t* l = af->data; // Local data + af_delay_t* s = (af_delay_t*)af->setup; // Setup for this instance + + + if(AF_OK != RESIZE_LOCAL_BUFFER(af , data)) + return NULL; + + if(s->len > c->len){ // Delay bigger than buffer + // Copy beginning of buffer to beginning of output buffer + memcpy(l->audio,s->buf,c->len); + // Move buffer left + memcpy(s->buf,s->buf+c->len,s->len-c->len); + // Save away current audio to end of buffer + memcpy(s->buf+s->len-c->len,c->audio,c->len); + } + else{ + // Copy end of previous block to beginning of output buffer + memcpy(l->audio,s->buf,s->len); + // Copy current block except end + memcpy(l->audio+s->len,c->audio,c->len-s->len); + // Save away end of current block for next call + memcpy(s->buf,c->audio+c->len-s->len,s->len); + } + + // Set output data + c->audio=l->audio; + + return c; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul.n=1; + af->mul.d=1; + af->data=calloc(1,sizeof(af_data_t)); + af->setup=calloc(1,sizeof(af_delay_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +af_info_t af_info_delay = { + "Delay audio filter", + "delay", + "Anders", + "", + open +}; + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_dummy.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,60 @@ +/* The name speaks for itself this filter is a dummy and will not blow + up regardless of what you do with it. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../config.h" +#include "../mp_msg.h" + +#include "af.h" + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT: + memcpy(af->data,(af_data_t*)arg,sizeof(af_data_t)); + mp_msg(MSGT_AFILTER,MSGL_V,"[dummy] Was reinitialized, rate=%iHz, nch = %i, format = 0x%08X and bps = %i\n",af->data->rate,af->data->nch,af->data->format,af->data->bps); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance_s* af) +{ + if(af->data) + free(af->data); +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + // Do something necessary to get rid of annoying warning during compile + if(!af) + printf("EEEK: Argument af == NULL in af_dummy.c play()."); + return data; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul.d=1; + af->mul.n=1; + af->data=malloc(sizeof(af_data_t)); + if(af->data == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +af_info_t af_info_dummy = { + "dummy", + "dummy", + "Anders", + "", + open +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_format.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,291 @@ +/* This audio output filter changes the format of a data block. Valid + formats are: AFMT_U8, AFMT_S8, AFMT_S16_LE, AFMT_S16_BE + AFMT_U16_LE, AFMT_U16_BE, AFMT_S32_LE and AFMT_S32_BE. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <limits.h> + +#include "../config.h" +#include "../mp_msg.h" + +#include "../libao2/afmt.h" + +#include "af.h" + +// Number of bits +#define B08 (0<<0) +#define B16 (1<<0) +#define B32 (2<<0) +#define NBITS_MASK (3<<0) + +// Endianess +#define BE (0<<2) // Big Endian +#define LE (1<<2) // Little Endian +#define END_MASK (1<<2) + +// Signed +#define US (0<<3) // Un Signed +#define SI (1<<3) // SIgned +#define SIGN_MASK (1<<3) + +int decode(int format) +{ + // Check input format + switch(format){ + case(AFMT_U8): + return LE|B08|US; + case(AFMT_S8): + return LE|B08|SI; break; + case(AFMT_S16_LE): + return LE|B16|SI; break; + case(AFMT_S16_BE): + return BE|B16|SI; break; + case(AFMT_U16_LE): + return LE|B16|US; break; + case(AFMT_U16_BE): + return BE|B16|US; break; + case(AFMT_S32_LE): + return LE|B32|SI; break; + case(AFMT_S32_BE): + return BE|B32|SI; break; + case(AFMT_IMA_ADPCM): + case(AFMT_MU_LAW): + case(AFMT_A_LAW): + case(AFMT_MPEG): + case(AFMT_AC3): + mp_msg(MSGT_AFILTER,MSGL_ERR,"[af_format] Input audio format not yet supported \n"); + return 0; + default: + //This can not happen .... + mp_msg(MSGT_AFILTER,MSGL_ERR,"Unrecognized input audio format\n"); + return 0; + } + +} + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT: + // Make sure this filter isn't redundant + if(af->data->format == ((af_data_t*)arg)->format && af->data->bps == ((af_data_t*)arg)->bps) + return AF_DETACH; + + af->data->rate = ((af_data_t*)arg)->rate; + af->data->nch = ((af_data_t*)arg)->nch; + af->mul.n = af->data->bps; + af->mul.d = ((af_data_t*)arg)->bps; + return AF_OK; + case AF_CONTROL_FORMAT: + // Reinit must be called after this function has been called + + // Sanity check for sample format + if(0 == ((int)af->setup=decode(((af_data_t*)arg)->format))) + return AF_ERROR; + af->data->format = ((af_data_t*)arg)->format; + + // Sanity check for bytes per sample + if(((af_data_t*)arg)->bps != 4 && ((af_data_t*)arg)->bps != 2 && ((af_data_t*)arg)->bps != 1){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"[format] The number of output bytes per sample must be 1, 2 or 4. Current value is%i \n",((af_data_t*)arg)->bps); + return AF_ERROR; + } + af->data->bps=((af_data_t*)arg)->bps; + + mp_msg(MSGT_AFILTER,MSGL_STATUS,"[format] Changing number sample format to 0x%08X and/or bytes per sample to %i \n",af->data->format,af->data->bps); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance_s* af) +{ + if(af->data) + free(af->data); + (int)af->setup = 0; +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + af_data_t* l = af->data; // Local data + void* la = NULL; // Local audio + int lf = (int)af->setup; // Local format + af_data_t* c = data; // Current working data + void* ca = c->audio; // Current audio + int cf = decode(c->format); // Current format + register int i = 0; // Counter + int len = c->len>>(cf&NBITS_MASK); // Loop end + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + la = l->audio; + + // Change to little endian + if((cf&END_MASK)!=LE){ + switch(cf&NBITS_MASK){ + case(B16):{ + register uint16_t s; + for(i=1;i<len;i++){ + s=((uint16_t*)ca)[i]; + ((uint16_t*)ca)[i]=(uint16_t)(((s&0x00FF)<<8) | (s&0xFF00)>>8); + } + } + break; + case(B32):{ + register uint32_t s; + for(i=1;i<len;i++){ + s=((uint32_t*)ca)[i]; + ((uint32_t*)ca)[i]=(uint32_t)(((s&0x000000FF)<<24) | ((s&0x0000FF00)<<8) | + ((s&0x00FF0000)>>8) | ((s&0xFF000000)>>24)); + } + } + break; + } + } + // Change signed/unsigned + if((cf&SIGN_MASK) != (lf&SIGN_MASK)){ + switch((cf&NBITS_MASK)){ + case(B08): + switch(cf&SIGN_MASK){ + case(US): + for(i=0;i<len;i++) + ((int8_t*)ca)[i]=(int8_t)(SCHAR_MIN+((int)((uint8_t*)ca)[i])); + break; + case(SI): + for(i=0;i<len;i++) + ((uint8_t*)ca)[i]=(uint8_t)(SCHAR_MAX+((int)((int8_t*)ca)[i])); + break; + } + break; + case(B16): + switch(cf&SIGN_MASK){ + case(US): + for(i=0;i<len;i++) + ((int16_t*)ca)[i]=(int16_t)(SHRT_MIN+((int)((uint16_t*)ca)[i])); + break; + case(SI): + for(i=0;i<len;i++) + ((uint16_t*)ca)[i]=(uint16_t)(SHRT_MAX+((int)((int16_t*)ca)[i])); + break; + } + break; + case(B32): + switch(cf&SIGN_MASK){ + case(US): + for(i=0;i<len;i++) + ((int32_t*)ca)[i]=(int32_t)(INT_MIN+((uint32_t*)ca)[i]); + break; + case(SI): + for(i=0;i<len;i++) + ((uint32_t*)ca)[i]=(uint32_t)(INT_MAX+((int32_t*)ca)[i]); + break; + } + break; + } + } + // Change the number of bits + if((cf&NBITS_MASK) == (lf&NBITS_MASK)){ + memcpy(la,ca,c->len); + } else { + switch(cf&NBITS_MASK){ + case(B08): + switch(lf&NBITS_MASK){ + case(B16): + for(i=1;i<len;i++) + ((uint16_t*)la)[i]=((uint16_t)((uint8_t*)ca)[i])<<8; + break; + case(B32): + for(i=1;i<len;i++) + ((uint32_t*)la)[i]=((uint32_t)((uint8_t*)ca)[i])<<24; + break; + } + break; + case(B16): + switch(lf&NBITS_MASK){ + case(B08): + for(i=0;i<len;i++) + ((uint8_t*)la)[i]=(uint8_t)((((uint16_t*)ca)[i])>>8); + break; + case(B32): + for(i=1;i<len;i++) + ((uint32_t*)la)[i]=((uint32_t)((uint16_t*)ca)[i])<<16; + break; + } + break; + case(B32): + switch(lf&NBITS_MASK){ + case(B08): + for(i=0;i<len;i++) + ((uint8_t*)la)[i]=(uint8_t)((((uint32_t*)ca)[i])>>24); + break; + case(B16): + for(i=1;i<len;i++) + ((uint16_t*)la)[i]=(uint16_t)((((uint32_t*)ca)[i])>>16); + break; + } + break; + } + } + // Switch to the correct endainess (again the problem with sun?) + if((lf&END_MASK)!=LE){ + switch(cf&NBITS_MASK){ + case(B16):{ + register uint16_t s; + for(i=1;i<len;i++){ + s=((uint16_t*)la)[i]; + ((uint16_t*)la)[i]=(uint16_t)(((s&0x00FF)<<8) | (s&0xFF00)>>8); + } + } + break; + case(B32):{ + register uint32_t s; + for(i=1;i<len;i++){ + s=((uint32_t*)la)[i]; + ((uint32_t*)la)[i]=(uint32_t)(((s&0x000000FF)<<24) | ((s&0x0000FF00)<<8) | + ((s&0x00FF0000)>>8) | ((s&0xFF000000)>>24)); + } + } + break; + } + } + + // Set output data + + // Make sure no samples are lost + c->len = (c->len*l->bps)/c->bps; + c->audio = l->audio; + c->bps = l->bps; + c->format = l->format; + return c; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul.n=1; + af->mul.d=1; + af->data=calloc(1,sizeof(af_data_t)); + if(af->data == NULL) + return AF_ERROR; + (int)af->setup = 0; + return AF_OK; +} + +// Description of this filter +af_info_t af_info_format = { + "Sample format conversion", + "format", + "Anders", + "", + open +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_resample.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,340 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +/* This audio filter changes the sample rate. */ + +#define PLUGIN + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "../config.h" +#include "../mp_msg.h" +#include "../libao2/afmt.h" + +#include "af.h" +#include "dsp.h" + +/* Below definition selects the length of each poly phase component. + Valid definitions are L8 and L16, where the number denotes the + length of the filter. This definition affects the computational + complexity (see play()), the performance (see filter.h) and the + memory usage. The filterlenght is choosen to 8 if the machine is + slow and to 16 if the machine is fast and has MMX. +*/ + +#if defined(HAVE_SSE) && !defined(HAVE_3DNOW) // This machine is slow + +#define L 8 // Filter length +// Unrolled loop to speed up execution +#define FIR(x,w,y){ \ + int16_t a = (w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+w[3]*x[3]) >> 16; \ + int16_t b = (w[4]*x[4]+w[5]*x[5]+w[6]*x[6]+w[7]*x[7]) >> 16; \ + (y[0]) = a+b; \ +} + +#else /* Fast machine */ + +#define L 16 +// Unrolled loop to speed up execution +#define FIR(x,w,y){ \ + int16_t a = (w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] ) >> 16; \ + int16_t b = (w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] ) >> 16; \ + int16_t c = (w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11]) >> 16; \ + int16_t d = (w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15]) >> 16; \ + y[0] = (a+b+c+d) >> 1; \ +} + +#endif /* Fast machine */ + +// Macro to add data to circular que +#define ADDQUE(xi,xq,in)\ + xq[xi]=xq[xi+L]=(*in);\ + xi=(--xi)&(L-1); + + + +// local data +typedef struct af_resample_s +{ + int16_t* w; // Current filter weights + int16_t** xq; // Circular buffers + int16_t xi; // Index for circular buffers + int16_t wi; // Index for w + uint16_t i; // Number of new samples to put in x queue + uint16_t dn; // Down sampling factor + uint16_t up; // Up sampling factor +} af_resample_t; + +// Euclids algorithm for calculating Greatest Common Divisor GCD(a,b) +inline int gcd(register int a, register int b) +{ + register int r = min(a,b); + a=max(a,b); + b=r; + + r=a%b; + while(r!=0){ + a=b; + b=r; + r=a%b; + } + return b; +} + +static int upsample(af_data_t* c,af_data_t* l, af_resample_t* s) +{ + uint16_t ci = l->nch; // Index for channels + uint16_t len = 0; // Number of input samples + uint16_t nch = l->nch; // Number of channels + uint16_t inc = s->up/s->dn; + uint16_t level = s->up%s->dn; + uint16_t up = s->up; + uint16_t dn = s->dn; + + register int16_t* w = s->w; + register uint16_t wi = 0; + register uint16_t xi = 0; + + // Index current channel + while(ci--){ + // Temporary pointers + register int16_t* x = s->xq[ci]; + register int16_t* in = ((int16_t*)c->audio)+ci; + register int16_t* out = ((int16_t*)l->audio)+ci; + int16_t* end = in+c->len/2; // Block loop end + wi = s->wi; xi = s->xi; + + while(in < end){ + register uint16_t i = inc; + if(wi<level) i++; + + ADDQUE(xi,x,in); + in+=nch; + while(i--){ + // Run the FIR filter + FIR((&x[xi]),(&w[wi*L]),out); + len++; out+=nch; + // Update wi to point at the correct polyphase component + wi=(wi+dn)%up; + } + } + } + // Save values that needs to be kept for next time + s->wi = wi; + s->xi = xi; + return len; +} + +static int downsample(af_data_t* c,af_data_t* l, af_resample_t* s) +{ + uint16_t ci = l->nch; // Index for channels + uint16_t len = 0; // Number of output samples + uint16_t nch = l->nch; // Number of channels + uint16_t inc = s->dn/s->up; + uint16_t level = s->dn%s->up; + uint16_t up = s->up; + uint16_t dn = s->dn; + + register uint16_t i = 0; + register uint16_t wi = 0; + register uint16_t xi = 0; + + // Index current channel + while(ci--){ + // Temporary pointers + register int16_t* x = s->xq[ci]; + register int16_t* in = ((int16_t*)c->audio)+ci; + register int16_t* out = ((int16_t*)l->audio)+ci; + register int16_t* end = in+c->len/2; // Block loop end + i = s->i; wi = s->wi; xi = s->xi; + + while(in < end){ + + ADDQUE(xi,x,in); + in+=nch; + if(!--i){ + // Run the FIR filter + FIR((&x[xi]),(&s->w[wi*L]),out); + len++; out+=nch; + + // Update wi to point at the correct polyphase component + wi=(wi+dn)%up; + + // Insert i number of new samples in queue + i = inc; + if(wi<level) i++; + } + } + } + // Save values that needs to be kept for next time + s->wi = wi; + s->xi = xi; + s->i = i; + + return len; +} + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT:{ + af_resample_t* s = (af_resample_t*)af->setup; + af_data_t* n = (af_data_t*)arg; // New configureation + int i,d = 0; + int rv = AF_OK; + + // Make sure this filter isn't redundant + if(af->data->rate == n->rate) + return AF_DETACH; + + // Create space for circular bufers (if nesessary) + if(af->data->nch != n->nch){ + // First free the old ones + if(s->xq){ + for(i=1;i<af->data->nch;i++) + if(s->xq[i]) + free(s->xq[i]); + free(s->xq); + } + // ... then create new + s->xq = malloc(n->nch*sizeof(int16_t*)); + for(i=0;i<n->nch;i++) + s->xq[i] = malloc(2*L*sizeof(int16_t)); + s->xi = 0; + } + + // Set parameters + af->data->nch = n->nch; + af->data->format = AFMT_S16_LE; + af->data->bps = 2; + if(af->data->format != n->format || af->data->bps != n->bps) + rv = AF_FALSE; + n->format = AFMT_S16_LE; + n->bps = 2; + + // Calculate up and down sampling factors + d=gcd(af->data->rate,n->rate); + + // Check if the the design needs to be redone + if(s->up != af->data->rate/d || s->dn != n->rate/d){ + float* w; + float* wt; + float fc; + int j; + s->up = af->data->rate/d; + s->dn = n->rate/d; + + // Calculate cuttof frequency for filter + fc = 1/(float)(max(s->up,s->dn)); + // Allocate space for polyphase filter bank and protptype filter + w = malloc(sizeof(float) * s->up *L); + if(NULL != s->w) + free(s->w); + s->w = malloc(L*s->up*sizeof(int16_t)); + + // Design prototype filter type using Kaiser window with beta = 10 + if(NULL == w || NULL == s->w || + -1 == design_fir(s->up*L, w, &fc, LP|KAISER , 10.0)){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"[resample] Unable to design prototype filter.\n"); + return AF_ERROR; + } + // Copy data from prototype to polyphase filter + wt=w; + for(j=0;j<L;j++){//Columns + for(i=0;i<s->up;i++){//Rows + float t=(float)s->up*32767.0*(*wt); + s->w[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5)); + wt++; + } + } + free(w); + mp_msg(MSGT_AFILTER,MSGL_V,"[resample] New filter designed up: %i down: %i\n", s->up, s->dn); + } + + // Set multiplier + af->mul.n = s->up; + af->mul.d = s->dn; + return rv; + } + case AF_CONTROL_RESAMPLE: + // Reinit must be called after this function has been called + + // Sanity check + if(((int*)arg)[0] <= 8000 || ((int*)arg)[0] > 192000){ + mp_msg(MSGT_AFILTER,MSGL_ERR,"[resample] The output sample frequency must be between 8kHz and 192kHz. Current value is %i \n",((int*)arg)[0]); + return AF_ERROR; + } + + af->data->rate=((int*)arg)[0]; + mp_msg(MSGT_AFILTER,MSGL_STATUS,"[resample] Changing sample rate to %iHz\n",af->data->rate); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance_s* af) +{ + if(af->data) + free(af->data); +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + int len = 0; // Length of output data + af_data_t* c = data; // Current working data + af_data_t* l = af->data; // Local data + af_resample_t* s = (af_resample_t*)af->setup; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + // Run resampling + if(s->up>s->dn) + len = upsample(c,l,s); + else + len = downsample(c,l,s); + + // Set output data + c->audio = l->audio; + c->len = len*2; + c->rate = l->rate; + + return c; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul.n=1; + af->mul.d=1; + af->data=calloc(1,sizeof(af_data_t)); + af->setup=calloc(1,sizeof(af_resample_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this plugin +af_info_t af_info_resample = { + "Sample frequency conversion", + "resample", + "Anders", + "", + open +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/dsp.h Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,22 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +#ifndef _DSP_H +#define _DSP_H 1 + +/* Implementation of routines used for DSP */ + +/* Size of floating point type used in routines */ +#define _ftype_t float + +#include <window.h> +#include <filter.h> + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/filter.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,257 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +/* Design and implementation of different types of digital filters + +*/ +#include <math.h> +#include "dsp.h" + +/* C implementation of FIR filter y=w*x + + n number of filter taps, where mod(n,4)==0 + w filter taps + x input signal must be a circular buffer which is indexed backwards +*/ +inline _ftype_t fir(register unsigned int n, _ftype_t* w, _ftype_t* x) +{ + register _ftype_t y; // Output + y = 0.0; + do{ + n--; + y+=w[n]*x[n]; + }while(n != 0); + return y; +} + +/* C implementation of parallel FIR filter y(k)=w(k) * x(k) (where * denotes convolution) + + n number of filter taps, where mod(n,4)==0 + d number of filters + xi current index in xq + w filter taps k by n big + x input signal must be a circular buffers which are indexed backwards + y output buffer + s output buffer stride +*/ +inline _ftype_t* pfir(unsigned int n, unsigned int d, unsigned int xi, _ftype_t** w, _ftype_t** x, _ftype_t* y, unsigned int s) +{ + register _ftype_t* xt = *x + xi; + register _ftype_t* wt = *w; + register int nt = 2*n; + while(d-- > 0){ + *y = fir(n,wt,xt); + wt+=n; + xt+=nt; + y+=s; + } + return y; +} + +/* Add new data to circular queue designed to be used with a parallel + FIR filter, with d filters. xq is the circular queue, in pointing + at the new samples, xi current index in xq and n the length of the + filter. xq must be n*2 by k big, s is the index for in. +*/ +inline int updatepq(unsigned int n, unsigned int d, unsigned int xi, _ftype_t** xq, _ftype_t* in, unsigned int s) +{ + register _ftype_t* txq = *xq + xi; + register int nt = n*2; + + while(d-- >0){ + *txq= *(txq+n) = *in; + txq+=nt; + in+=s; + } + return (++xi)&(n-1); +} + + +/* Design FIR filter using the Window method + + n filter length must be odd for HP and BS filters + w buffer for the filter taps (must be n long) + fc cutoff frequencies (1 for LP and HP, 2 for BP and BS) + 0 < fc < 1 where 1 <=> Fs/2 + flags window and filter type as defined in filter.h + variables are ored together: i.e. LP|HAMMING will give a + low pass filter designed using a hamming window + opt beta constant used only when designing using kaiser windows + + returns 0 if OK, -1 if fail +*/ +int design_fir(unsigned int n, _ftype_t* w, _ftype_t* fc, unsigned int flags, _ftype_t opt) +{ + unsigned int o = n & 1; // Indicator for odd filter length + unsigned int end = ((n + 1) >> 1) - o; // Loop end + unsigned int i; // Loop index + + _ftype_t k1 = 2 * M_PI; // 2*pi*fc1 + _ftype_t k2 = 0.5 * (_ftype_t)(1 - o);// Constant used if the filter has even length + _ftype_t k3; // 2*pi*fc2 Constant used in BP and BS design + _ftype_t g = 0.0; // Gain + _ftype_t t1,t2,t3; // Temporary variables + _ftype_t fc1,fc2; // Cutoff frequencies + + // Sanity check + if(!w || (n == 0)) return -1; + + // Get window coefficients + switch(flags & WINDOW_MASK){ + case(BOXCAR): + boxcar(n,w); break; + case(TRIANG): + triang(n,w); break; + case(HAMMING): + hamming(n,w); break; + case(HANNING): + hanning(n,w); break; + case(BLACKMAN): + blackman(n,w); break; + case(FLATTOP): + flattop(n,w); break; + case(KAISER): + kaiser(n,w,opt); break; + default: + return -1; + } + + if(flags & (LP | HP)){ + fc1=*fc; + // Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2 + fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25; + k1 *= fc1; + + if(flags & LP){ // Low pass filter + + // If the filter length is odd, there is one point which is exactly + // in the middle. The value at this point is 2*fCutoff*sin(x)/x, + // where x is zero. To make sure nothing strange happens, we set this + // value separately. + if (o){ + w[end] = fc1 * w[end] * 2.0; + g=w[end]; + } + + // Create filter + for (i=0 ; i<end ; i++){ + t1 = (_ftype_t)(i+1) - k2; + w[end-i-1] = w[n-end+i] = w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc + g += 2*w[end-i-1]; // Total gain in filter + } + } + else{ // High pass filter + if (!o) // High pass filters must have odd length + return -1; + w[end] = 1.0 - (fc1 * w[end] * 2.0); + g= w[end]; + + // Create filter + for (i=0 ; i<end ; i++){ + t1 = (_ftype_t)(i+1); + w[end-i-1] = w[n-end+i] = -1 * w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc + g += ((i&1) ? (2*w[end-i-1]) : (-2*w[end-i-1])); // Total gain in filter + } + } + } + + if(flags & (BP | BS)){ + fc1=fc[0]; + fc2=fc[1]; + // Cutoff frequencies must be < 1.0 where 1.0 <=> Fs/2 + fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25; + fc2 = ((fc2 <= 1.0) && (fc2 > 0.0)) ? fc2/2 : 0.25; + k3 = k1 * fc2; // 2*pi*fc2 + k1 *= fc1; // 2*pi*fc1 + + if(flags & BP){ // Band pass + // Calculate center tap + if (o){ + g=w[end]*(fc1+fc2); + w[end] = (fc2 - fc1) * w[end] * 2.0; + } + + // Create filter + for (i=0 ; i<end ; i++){ + t1 = (_ftype_t)(i+1) - k2; + t2 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2 + t3 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1 + g += w[end-i-1] * (t3 + t2); // Total gain in filter + w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3); + } + } + else{ // Band stop + if (!o) // Band stop filters must have odd length + return -1; + w[end] = 1.0 - (fc2 - fc1) * w[end] * 2.0; + g= w[end]; + + // Create filter + for (i=0 ; i<end ; i++){ + t1 = (_ftype_t)(i+1); + t2 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1 + t3 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2 + w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3); + g += 2*w[end-i-1]; // Total gain in filter + } + } + } + + // Normalize gain + g=1/g; + for (i=0; i<n; i++) + w[i] *= g; + + return 0; +} + +/* Design polyphase FIR filter from prototype filter + + n length of prototype filter + k number of polyphase components + w prototype filter taps + pw Parallel FIR filter + g Filter gain + flags FWD forward indexing + REW reverse indexing + ODD multiply every 2nd filter tap by -1 => HP filter + + returns 0 if OK, -1 if fail +*/ +int design_pfir(unsigned int n, unsigned int k, _ftype_t* w, _ftype_t** pw, _ftype_t g, unsigned int flags) +{ + int l = (int)n/k; // Length of individual FIR filters + int i; // Counters + int j; + _ftype_t t; // g * w[i] + + // Sanity check + if(l<1 || k<1 || !w || !pw) + return -1; + + // Do the stuff + if(flags&REW){ + for(j=l-1;j>-1;j--){//Columns + for(i=0;i<(int)k;i++){//Rows + t=g * *w++; + pw[i][j]=t * ((flags & ODD) ? ((j & 1) ? -1 : 1) : 1); + } + } + } + else{ + for(j=0;j<l;j++){//Columns + for(i=0;i<(int)k;i++){//Rows + t=g * *w++; + pw[i][j]=t * ((flags & ODD) ? ((j & 1) ? 1 : -1) : 1); + } + } + } + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/filter.h Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,65 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +#if !defined _DSP_H +# error "Never use <filter.h> directly; include <dsp.h> instead" +#endif + +#ifndef _FILTER_H +#define _FILTER_H 1 + + +// Design and implementation of different types of digital filters + + +// Flags used for filter design + +// Filter characteristics +#define LP 0x00010000 // Low pass +#define HP 0x00020000 // High pass +#define BP 0x00040000 // Band pass +#define BS 0x00080000 // Band stop +#define TYPE_MASK 0x000F0000 + +// Window types +#define BOXCAR 0x00000001 +#define TRIANG 0x00000002 +#define HAMMING 0x00000004 +#define HANNING 0x00000008 +#define BLACKMAN 0x00000010 +#define FLATTOP 0x00000011 +#define KAISER 0x00000012 +#define WINDOW_MASK 0x0000001F + +// Parallel filter design +#define FWD 0x00000001 // Forward indexing of polyphase filter +#define REW 0x00000002 // Reverse indexing of polyphase filter +#define ODD 0x00000010 // Make filter HP + +// Exported functions +extern _ftype_t fir(unsigned int n, _ftype_t* w, _ftype_t* x); +extern _ftype_t* pfir(unsigned int n, unsigned int k, unsigned int xi, _ftype_t** w, _ftype_t** x, _ftype_t* y, unsigned int s); + +extern int updateq(unsigned int n, unsigned int xi, _ftype_t* xq, _ftype_t* in); +extern int updatepq(unsigned int n, unsigned int k, unsigned int xi, _ftype_t** xq, _ftype_t* in, unsigned int s); + +extern int design_fir(unsigned int n, _ftype_t* w, _ftype_t* fc, unsigned int flags, _ftype_t opt); +extern int design_pfir(unsigned int n, unsigned int k, _ftype_t* w, _ftype_t** pw, _ftype_t g, unsigned int flags); + +/* Add new data to circular queue designed to be used with a FIR + filter. xq is the circular queue, in pointing at the new sample, xi + current index for xq and n the length of the filter. xq must be n*2 + long. +*/ +#define updateq(n,xi,xq,in)\ + xq[xi]=xq[xi+n]=*in;\ + xi=(++xi)&(n-1); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/window.c Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,203 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +/* Calculates a number of window functions. The following window + functions are currently implemented: Boxcar, Triang, Hanning, + Hamming, Blackman, Flattop and Kaiser. In the function call n is + the number of filter taps and w the buffer in which the filter + coefficients will be stored. +*/ + +#include <math.h> +#include "dsp.h" + +/* +// Boxcar +// +// n window length +// w buffer for the window parameters +*/ +void boxcar(int n, _ftype_t* w) +{ + int i; + // Calculate window coefficients + for (i=0 ; i<n ; i++) + w[i] = 1.0; +} + + +/* +// Triang a.k.a Bartlett +// +// | (N-1)| +// 2 * |k - -----| +// | 2 | +// w = 1.0 - --------------- +// N+1 +// n window length +// w buffer for the window parameters +*/ +void triang(int n, _ftype_t* w) +{ + _ftype_t k1 = (_ftype_t)(n & 1); + _ftype_t k2 = 1/((_ftype_t)n + k1); + int end = (n + 1) >> 1; + int i; + + // Calculate window coefficients + for (i=0 ; i<end ; i++) + w[i] = w[n-i-1] = (2.0*((_ftype_t)(i+1))-(1.0-k1))*k2; +} + + +/* +// Hanning +// 2*pi*k +// w = 0.5 - 0.5*cos(------), where 0 < k <= N +// N+1 +// n window length +// w buffer for the window parameters +*/ +void hanning(int n, _ftype_t* w) +{ + int i; + _ftype_t k = 2*M_PI/((_ftype_t)(n+1)); // 2*pi/(N+1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.5*(1.0 - cos(k*(_ftype_t)(i+1))); +} + +/* +// Hamming +// 2*pi*k +// w(k) = 0.54 - 0.46*cos(------), where 0 <= k < N +// N-1 +// +// n window length +// w buffer for the window parameters +*/ +void hamming(int n,_ftype_t* w) +{ + int i; + _ftype_t k = 2*M_PI/((_ftype_t)(n-1)); // 2*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.54 - 0.46*cos(k*(_ftype_t)i); +} + +/* +// Blackman +// 2*pi*k 4*pi*k +// w(k) = 0.42 - 0.5*cos(------) + 0.08*cos(------), where 0 <= k < N +// N-1 N-1 +// +// n window length +// w buffer for the window parameters +*/ +void blackman(int n,_ftype_t* w) +{ + int i; + _ftype_t k1 = 2*M_PI/((_ftype_t)(n-1)); // 2*pi/(N-1) + _ftype_t k2 = 2*k1; // 4*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.42 - 0.50*cos(k1*(_ftype_t)i) + 0.08*cos(k2*(_ftype_t)i); +} + +/* +// Flattop +// 2*pi*k 4*pi*k +// w(k) = 0.2810638602 - 0.5208971735*cos(------) + 0.1980389663*cos(------), where 0 <= k < N +// N-1 N-1 +// +// n window length +// w buffer for the window parameters +*/ +void flattop(int n,_ftype_t* w) +{ + int i; + _ftype_t k1 = 2*M_PI/((_ftype_t)(n-1)); // 2*pi/(N-1) + _ftype_t k2 = 2*k1; // 4*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.2810638602 - 0.5208971735*cos(k1*(_ftype_t)i) + 0.1980389663*cos(k2*(_ftype_t)i); +} + +/* Computes the 0th order modified Bessel function of the first kind. +// (Needed to compute Kaiser window) +// +// y = sum( (x/(2*n))^2 ) +// n +*/ +#define BIZ_EPSILON 1E-21 // Max error acceptable + +_ftype_t besselizero(_ftype_t x) +{ + _ftype_t temp; + _ftype_t sum = 1.0; + _ftype_t u = 1.0; + _ftype_t halfx = x/2.0; + int n = 1; + + do { + temp = halfx/(_ftype_t)n; + u *=temp * temp; + sum += u; + n++; + } while (u >= BIZ_EPSILON * sum); + return(sum); +} + +/* +// Kaiser +// +// n window length +// w buffer for the window parameters +// b beta parameter of Kaiser window, Beta >= 1 +// +// Beta trades the rejection of the low pass filter against the +// transition width from passband to stop band. Larger Beta means a +// slower transition and greater stop band rejection. See Rabiner and +// Gold (Theory and Application of DSP) under Kaiser windows for more +// about Beta. The following table from Rabiner and Gold gives some +// feel for the effect of Beta: +// +// All ripples in dB, width of transition band = D*N where N = window +// length +// +// BETA D PB RIP SB RIP +// 2.120 1.50 +-0.27 -30 +// 3.384 2.23 0.0864 -40 +// 4.538 2.93 0.0274 -50 +// 5.658 3.62 0.00868 -60 +// 6.764 4.32 0.00275 -70 +// 7.865 5.0 0.000868 -80 +// 8.960 5.7 0.000275 -90 +// 10.056 6.4 0.000087 -100 +*/ +void kaiser(int n, _ftype_t* w, _ftype_t b) +{ + _ftype_t tmp; + _ftype_t k1 = 1.0/besselizero(b); + int k2 = 1 - (n & 1); + int end = (n + 1) >> 1; + int i; + + // Calculate window coefficients + for (i=0 ; i<end ; i++){ + tmp = (_ftype_t)(2*i + k2) / ((_ftype_t)n - 1.0); + w[end-(1&(!k2))+i] = w[end-1-i] = k1 * besselizero(b*sqrt(1.0 - tmp*tmp)); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/window.h Tue Oct 01 06:45:08 2002 +0000 @@ -0,0 +1,33 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au +// +//============================================================================= +*/ + +/* Calculates a number of window functions. The following window + functions are currently implemented: Boxcar, Triang, Hanning, + Hamming, Blackman, Flattop and Kaiser. In the function call n is + the number of filter taps and w the buffer in which the filter + coefficients will be stored. +*/ + +#if !defined _DSP_H +# error "Never use <window.h> directly; include <dsp.h> instead" +#endif + +#ifndef _WINDOW_H +#define _WINDOW_H 1 + +extern void boxcar(int n, _ftype_t* w); +extern void triang(int n, _ftype_t* w); +extern void hanning(int n, _ftype_t* w); +extern void hamming(int n,_ftype_t* w); +extern void blackman(int n,_ftype_t* w); +extern void flattop(int n,_ftype_t* w); +extern void kaiser(int n, _ftype_t* w,_ftype_t b); + +#endif