mirror of
https://github.com/mpv-player/mpv
synced 2024-12-19 21:31:52 +00:00
Adding new audio output filter layer libaf
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@7569 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
parent
c0091278d8
commit
1f6c494641
9
Makefile
9
Makefile
@ -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
|
||||
|
||||
|
38
libaf/Makefile
Normal file
38
libaf/Makefile
Normal file
@ -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
|
440
libaf/af.c
Normal file
440
libaf/af.c
Normal file
@ -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;
|
||||
}
|
160
libaf/af.h
Normal file
160
libaf/af.h
Normal file
@ -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
|
171
libaf/af_channels.c
Normal file
171
libaf/af_channels.c
Normal file
@ -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
|
||||
};
|
146
libaf/af_delay.c
Normal file
146
libaf/af_delay.c
Normal file
@ -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
|
||||
};
|
||||
|
||||
|
60
libaf/af_dummy.c
Normal file
60
libaf/af_dummy.c
Normal file
@ -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
|
||||
};
|
291
libaf/af_format.c
Normal file
291
libaf/af_format.c
Normal file
@ -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
|
||||
};
|
340
libaf/af_resample.c
Normal file
340
libaf/af_resample.c
Normal file
@ -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
|
||||
};
|
||||
|
22
libaf/dsp.h
Normal file
22
libaf/dsp.h
Normal file
@ -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
|
257
libaf/filter.c
Normal file
257
libaf/filter.c
Normal file
@ -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;
|
||||
}
|
65
libaf/filter.h
Normal file
65
libaf/filter.h
Normal file
@ -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
|
203
libaf/window.c
Normal file
203
libaf/window.c
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
|
33
libaf/window.h
Normal file
33
libaf/window.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user