diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | libaf/Makefile | 38 | ||||
-rw-r--r-- | libaf/af.c | 440 | ||||
-rw-r--r-- | libaf/af.h | 160 | ||||
-rw-r--r-- | libaf/af_channels.c | 171 | ||||
-rw-r--r-- | libaf/af_delay.c | 146 | ||||
-rw-r--r-- | libaf/af_dummy.c | 60 | ||||
-rw-r--r-- | libaf/af_format.c | 291 | ||||
-rw-r--r-- | libaf/af_resample.c | 340 | ||||
-rw-r--r-- | libaf/dsp.h | 22 | ||||
-rw-r--r-- | libaf/filter.c | 257 | ||||
-rw-r--r-- | libaf/filter.h | 65 | ||||
-rw-r--r-- | libaf/window.c | 203 | ||||
-rw-r--r-- | libaf/window.h | 33 | ||||
-rw-r--r-- | mp_msg.h | 2 |
15 files changed, 2234 insertions, 3 deletions
@@ -45,13 +45,13 @@ AO_LIBS = libao2/libao2.a 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 @@ ifeq ($(CSS_USE),yes) 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 @@ all: $(ALL_PRG) .c.o: $(CC) -c $(CFLAGS) -o $@ $< +libaf/libaf.a: + $(MAKE) -C libaf + libmpdvdkit2/libmpdvdkit.a: $(MAKE) -C libmpdvdkit2 diff --git a/libaf/Makefile b/libaf/Makefile new file mode 100644 index 0000000000..eec59367f8 --- /dev/null +++ b/libaf/Makefile @@ -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 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 <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; +} diff --git a/libaf/af.h b/libaf/af.h new file mode 100644 index 0000000000..b0ea7b710a --- /dev/null +++ b/libaf/af.h @@ -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 diff --git a/libaf/af_channels.c b/libaf/af_channels.c new file mode 100644 index 0000000000..9943e573aa --- /dev/null +++ b/libaf/af_channels.c @@ -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 +}; diff --git a/libaf/af_delay.c b/libaf/af_delay.c new file mode 100644 index 0000000000..d25e6ed7f2 --- /dev/null +++ b/libaf/af_delay.c @@ -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 +}; + + diff --git a/libaf/af_dummy.c b/libaf/af_dummy.c new file mode 100644 index 0000000000..8435b4a626 --- /dev/null +++ b/libaf/af_dummy.c @@ -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 |